mirror of
https://github.com/logos-storage/logos-storage-js.git
synced 2026-01-07 07:53:10 +00:00
Compare commits
No commits in common. "master" and "v0.0.22" have entirely different histories.
55
.github/workflows/ci.yaml
vendored
55
.github/workflows/ci.yaml
vendored
@ -1,55 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
pull_request:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
node_version: 22.12.0
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
URL: "http://localhost:8081/api/codex/v1/debug/info"
|
|
||||||
TIMEOUT_SECONDS: 300
|
|
||||||
SLEEP_INTERVAL: 2
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: ${{ env.node_version }}
|
|
||||||
registry-url: "https://registry.npmjs.org"
|
|
||||||
|
|
||||||
- run: npm ci
|
|
||||||
|
|
||||||
# - name: Start codex-factory
|
|
||||||
# run: npx codex-factory start latest &
|
|
||||||
|
|
||||||
# - name: Wait for first SP to be started
|
|
||||||
# run: |
|
|
||||||
# MAX_RETRIES=$((TIMEOUT_SECONDS / SLEEP_INTERVAL))
|
|
||||||
# echo "Waiting for $URL (timeout: ${TIMEOUT_SECONDS}s)..."
|
|
||||||
|
|
||||||
# for i in $(seq 1 $MAX_RETRIES); do
|
|
||||||
# STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL" || echo "000")
|
|
||||||
# if [ "$STATUS" = "200" ]; then
|
|
||||||
# echo "Codex is ready"
|
|
||||||
# exit 0
|
|
||||||
# fi
|
|
||||||
|
|
||||||
# sleep $SLEEP_INTERVAL
|
|
||||||
# done
|
|
||||||
|
|
||||||
# echo "Timed out after ${TIMEOUT_SECONDS}s waiting for $URL"
|
|
||||||
# exit 1
|
|
||||||
|
|
||||||
# - run: npm test
|
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,3 @@
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
*.tgz
|
*.tgz
|
||||||
index.bundle.js
|
|
||||||
211
README.md
211
README.md
@ -6,23 +6,6 @@ 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.
|
The SDK is currently under early development and the API can change at any time.
|
||||||
|
|
||||||
[](https://opensource.org/licenses/Apache-2.0)
|
|
||||||
[](https://opensource.org/licenses/MIT)
|
|
||||||
[](#stability)
|
|
||||||
[](https://github.com/codex-storage/codex-js/actions/workflows/ci.yml?query=branch%3Amaster)
|
|
||||||
|
|
||||||
## Breaking changes
|
|
||||||
|
|
||||||
- Version 0.1.0 introduces [upload strategy](#upload) to support browser and Node JS.
|
|
||||||
|
|
||||||
## Types generation
|
|
||||||
|
|
||||||
The types are generated from the openapi.yaml using the commande:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx openapi-typescript ./openapi.yaml -o src/openapi.ts --default-non-nullable false
|
|
||||||
```
|
|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
|
||||||
### Sync api
|
### Sync api
|
||||||
@ -42,7 +25,7 @@ const { Codex } = require("@codex-storage/sdk-js");
|
|||||||
To create a Codex instance, provide the REST API url to interact with the Codex client:
|
To create a Codex instance, provide the REST API url to interact with the Codex client:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const codex = new Codex("http://localhost:8080");
|
const codex = new Codex("http://localhost:3000");
|
||||||
```
|
```
|
||||||
|
|
||||||
Then you can access any module like this:
|
Then you can access any module like this:
|
||||||
@ -66,27 +49,13 @@ const { Codex } = require("@codex-storage/sdk-js/async");
|
|||||||
To create a Codex instance, provide the REST API url to interact with the Codex client:
|
To create a Codex instance, provide the REST API url to interact with the Codex client:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const codex = new Codex("http://localhost:8080");
|
const codex = new Codex("http://localhost:3000");
|
||||||
```
|
```
|
||||||
|
|
||||||
To use a module, you need to use the await syntax. If the module is not loaded yet, it will be imported first and then cached in memory.
|
To use a module, you need to use the await syntax. If the module is not loaded yet, it will be imported first and then cached in memory.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const marketplace = await codex.marketplace();
|
const marketplace = await codex.marketplace;
|
||||||
```
|
|
||||||
|
|
||||||
### Authentication
|
|
||||||
|
|
||||||
You can use basic authentication when creating a new Codex object:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const codex = new Codex("http://localhost:8080", {
|
|
||||||
auth: {
|
|
||||||
basic: "MY BASIC AUTH SECRET"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
You can obtain your secret using the `btoa` method in the browser or `Buffer.from(string).toString('base64')` in Node.js. The secret is stored in memory only.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Error handling
|
### Error handling
|
||||||
@ -127,19 +96,15 @@ if (slots.error) {
|
|||||||
The following API assume that you have already a marketplace module loaded, example:
|
The following API assume that you have already a marketplace module loaded, example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const codex = new Codex("http://localhost:8080");
|
const codex = new Codex("http://localhost:3000");
|
||||||
// When using the async api
|
|
||||||
const marketplace = await codex.marketplace();
|
const marketplace = await codex.marketplace();
|
||||||
|
|
||||||
// When using the sync api
|
|
||||||
const marketplace = codex.marketplace;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### activeSlots()
|
#### activeSlots()
|
||||||
|
|
||||||
Returns active slots.
|
Returns active slots.
|
||||||
|
|
||||||
- returns Promise<[CodexSlot](./src/marketplace/types.ts#L7)[]>
|
- returns Promise<[CodexSlot](./src/marketplace/types.ts#L85)[]>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -152,7 +117,7 @@ const slots = await marketplace.activeSlots();
|
|||||||
Returns active slot with id {slotId} for the host.
|
Returns active slot with id {slotId} for the host.
|
||||||
|
|
||||||
- slotId (string, required)
|
- slotId (string, required)
|
||||||
- returns Promise<[CodexSlotAgent](./src/marketplace/types.ts#L12)[]>
|
- returns Promise<[CodexSlot](./src/marketplace/types.ts#L85)[]>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -165,7 +130,7 @@ const slot = await marketplace.activeSlot(slotId);
|
|||||||
|
|
||||||
Returns storage that is for sale.
|
Returns storage that is for sale.
|
||||||
|
|
||||||
- returns Promise<[CodexAvailability](./src/marketplace/types.ts#L20)>
|
- returns Promise<[CodexAvailability](./src/marketplace/types.ts#L99)>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -177,16 +142,16 @@ const availabilities = await marketplace.availabilities();
|
|||||||
|
|
||||||
Offers storage for sale.
|
Offers storage for sale.
|
||||||
|
|
||||||
- input ([CodexCreateAvailabilityInput](./src/marketplace/types.ts#L45), required)
|
- input ([CodexCreateAvailabilityInput](./src/marketplace/types.ts#L175), required)
|
||||||
- returns Promise<[CodexAvailability](./src/marketplace/types.ts#L20)[]>
|
- returns Promise<[CodexAvailabilityCreateResponse](./src/marketplace/types.ts#L186)[]>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const response = await marketplace.createAvailability({
|
const response = await marketplace.createAvailability({
|
||||||
totalCollateral: 1,
|
maxCollateral: 1,
|
||||||
totalSize: 3000,
|
totalSize: 3000,
|
||||||
minPricePerBytePerSecond: 100,
|
minPrice: 100,
|
||||||
duration: 100,
|
duration: 100,
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@ -195,7 +160,7 @@ const response = await marketplace.createAvailability({
|
|||||||
|
|
||||||
Updates availability.
|
Updates availability.
|
||||||
|
|
||||||
- input ([CodexAvailabilityPatchInput](./src/marketplace/types.ts#L66), required)
|
- input ([CodexUpdateAvailabilityInput](./src/marketplace/types.ts#L186), required)
|
||||||
- returns Promise<"">
|
- returns Promise<"">
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@ -203,9 +168,9 @@ Example:
|
|||||||
```js
|
```js
|
||||||
const response = await marketplace.updateAvailability({
|
const response = await marketplace.updateAvailability({
|
||||||
id: "0x.....................",
|
id: "0x.....................",
|
||||||
totalCollateral: 1,
|
maxCollateral: 1,
|
||||||
totalSize: 3000,
|
totalSize: 3000,
|
||||||
minPricePerBytePerSecond: 100,
|
minPrice: 100,
|
||||||
duration: 100,
|
duration: 100,
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@ -215,7 +180,7 @@ const response = await marketplace.updateAvailability({
|
|||||||
Return list of reservations for ongoing Storage Requests that the node hosts.
|
Return list of reservations for ongoing Storage Requests that the node hosts.
|
||||||
|
|
||||||
- availabilityId (string, required)
|
- availabilityId (string, required)
|
||||||
- returns Promise<[CodexReservation](./src/marketplace/types.ts#L83)[]>
|
- returns Promise<[CodexReservation](./src/marketplace/types.ts#L198)[]>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -227,7 +192,7 @@ const reservations = await marketplace.reservations("Ox...");
|
|||||||
|
|
||||||
Creates a new Request for storage
|
Creates a new Request for storage
|
||||||
|
|
||||||
- input ([CodexCreateStorageRequestInput](./src/marketplace/types.ts#L120), required)
|
- input ([CodexCreateStorageRequestInput](./src/marketplace/types.ts#L230), required)
|
||||||
- returns Promise<string>
|
- returns Promise<string>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@ -261,7 +226,7 @@ const ids = await marketplace.purchaseIds();
|
|||||||
Returns purchase details
|
Returns purchase details
|
||||||
|
|
||||||
- purchaseId (string, required)
|
- purchaseId (string, required)
|
||||||
- returns Promise<[CodexPurchase](./src/marketplace/types.ts#L103)[]>
|
- returns Promise<[CodexPurchase](./src/marketplace/types.ts#L214)[]>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -275,19 +240,15 @@ const purchase = await marketplace.purchaseDetail(purchaseId);
|
|||||||
The following API assume that you have already a data module loaded, example:
|
The following API assume that you have already a data module loaded, example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const codex = new Codex("http://localhost:8080");
|
const codex = new Codex("http://localhost:3000");
|
||||||
// When using the async api
|
const data = await codex.data;
|
||||||
const data = await codex.data();
|
|
||||||
|
|
||||||
// When using the sync api
|
|
||||||
const data = codex.data;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### cids
|
#### cids
|
||||||
|
|
||||||
Returns the manifest stored locally in node.
|
Returns the manifest stored locally in node.
|
||||||
|
|
||||||
- returns Promise<[CodexDataItem](./src/data/types.ts#L8)[]>
|
- returns Promise<[CodexDataResponse](./src/data/types.ts#L54)[]>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -299,7 +260,7 @@ const cids = await data.cids();
|
|||||||
|
|
||||||
Returns a summary of the storage space allocation of the node
|
Returns a summary of the storage space allocation of the node
|
||||||
|
|
||||||
- returns Promise<[CodexNodeSpace](./src/data/types.ts#L15)[]>
|
- returns Promise<[CodexNodeSpace](./src/data/types.ts#L58)[]>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -311,55 +272,24 @@ const space = await data.space();
|
|||||||
|
|
||||||
Upload a file in a streaming manner
|
Upload a file in a streaming manner
|
||||||
|
|
||||||
#### Browser
|
- file (File, required)
|
||||||
|
- onProgress (onProgress: (loaded: number, total: number) => void, optional)
|
||||||
- strategy [BrowserUploadStrategy](./src/data/browser-upload.ts#L5)
|
- metadata ({ filename?: string, mimetype?: string }, optional)
|
||||||
- returns [UploadResponse](./src/data/types.ts#L17)
|
- returns [UploadResponse](./src/data/types.ts#L80)
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const file = new File(["foo"], "foo.txt", { type: "text/plain" });
|
// Get file from previous event
|
||||||
|
const [file] = e.target.files
|
||||||
const onProgress = (loaded, total) => {
|
const metadata = {
|
||||||
console.info("Loaded", loaded, "total", total);
|
filename: file.name,
|
||||||
};
|
mimetype: file.type,
|
||||||
|
|
||||||
const metadata = { filename: "foo.xt", mimetype: "text/plain" };
|
|
||||||
|
|
||||||
const strategy = new BrowserUploadStrategy(file, onProgress, metadata);
|
|
||||||
|
|
||||||
const uploadResponse = data.upload(strategy);
|
|
||||||
|
|
||||||
const res = await uploadResponse.result;
|
|
||||||
|
|
||||||
if (res.error) {
|
|
||||||
console.error(res.data);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
const upload = data.upload(file, (loaded: number, total: number) => {
|
||||||
console.info("CID is", res.data);
|
// Use loaded and total so update a progress bar for example
|
||||||
```
|
}, metadata);
|
||||||
|
await upload.result();
|
||||||
#### Node
|
|
||||||
|
|
||||||
- strategy [NodeUploadStrategy](./src/data/node-upload.ts#L9)
|
|
||||||
- returns [UploadResponse](./src/data/types.ts#L17)
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const strategy = new NodeUploadStrategy("Hello World !");
|
|
||||||
const uploadResponse = data.upload(strategy);
|
|
||||||
|
|
||||||
const res = await uploadResponse.result;
|
|
||||||
|
|
||||||
if (res.error) {
|
|
||||||
console.error(res.data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.info("CID is", res.data);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### manifest
|
#### manifest
|
||||||
@ -367,7 +297,7 @@ console.info("CID is", res.data);
|
|||||||
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.
|
||||||
|
|
||||||
- cid (string, required)
|
- cid (string, required)
|
||||||
- returns [CodexManifest](./src/data/types.ts#L30)
|
- returns [CodexManifest](./src/data/types.ts#L3)
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -406,39 +336,20 @@ const cid = "QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N";
|
|||||||
const result = await data.localDownload(cid);
|
const result = await data.localDownload(cid);
|
||||||
```
|
```
|
||||||
|
|
||||||
#### delete
|
|
||||||
|
|
||||||
Deletes either a single block or an entire dataset from the local node.
|
|
||||||
Does nothing if the dataset is not locally available.
|
|
||||||
|
|
||||||
- cid (string, required)
|
|
||||||
- returns ""
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const cid = "QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N";
|
|
||||||
const result = await data.delete(cid);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Debug
|
### Debug
|
||||||
|
|
||||||
The following API assume that you have already a node module loaded, example:
|
The following API assume that you have already a node module loaded, example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const codex = new Codex("http://localhost:8080");
|
const codex = new Codex("http://localhost:3000");
|
||||||
// When using the async api
|
const data = await codex.debug;
|
||||||
const data = await codex.debug();
|
|
||||||
|
|
||||||
// When using the sync api
|
|
||||||
const data = codex.debug;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### setLogLevel
|
#### setLogLevel
|
||||||
|
|
||||||
Set log level at run time.
|
Set log level at run time.
|
||||||
|
|
||||||
- level ([CodexLogLevel](./src/debug/types.ts#L7), required)
|
- level ([CodexLogLevel](./src/debug/types.ts#L3), required)
|
||||||
- returns Promise<"">
|
- returns Promise<"">
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@ -451,7 +362,7 @@ await debug.setLogLevel("DEBUG");
|
|||||||
|
|
||||||
Gets node information
|
Gets node information
|
||||||
|
|
||||||
- returns Promise<[CodexDebugInfo](./src/debug/types.ts#L23)>
|
- returns Promise<[CodexDebugInfo](./src/debug/types.ts#L15)>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -464,56 +375,18 @@ const info = await debug.info();
|
|||||||
The following API assume that you have already a node module loaded, example:
|
The following API assume that you have already a node module loaded, example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const codex = new Codex("http://localhost:8080");
|
const codex = new Codex("http://localhost:3000");
|
||||||
// When using the async api
|
const node = await codex.node;
|
||||||
const node = await codex.node();
|
|
||||||
|
|
||||||
// When using the sync api
|
|
||||||
const node = codex.node;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### spr
|
#### spr
|
||||||
|
|
||||||
Get Node's SPR
|
Get Node's SPR
|
||||||
|
|
||||||
- returns Promise<[CodexSpr](./src/node/types.ts#L11)>
|
- returns Promise<[CodexSpr](./src/node/types.ts#L1)>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const spr = await node.spr();
|
const spr = await node.spr();
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, the response will be a json. You can use `text` option to get the string:
|
|
||||||
|
|
||||||
#### peeriD
|
|
||||||
|
|
||||||
Get Node's peer id
|
|
||||||
|
|
||||||
- returns Promise<[CodexPeerId](./src/node/types.ts#L25)>
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const peerId = await node.peerId();
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, the response will be a json. You can use `text` option to get the string:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const peerId = await node.peerId("text");
|
|
||||||
```
|
|
||||||
|
|
||||||
#### connect
|
|
||||||
|
|
||||||
Connect to a peer
|
|
||||||
|
|
||||||
- returns Promise<string>
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const peerId = "..."
|
|
||||||
const addrs = [...]
|
|
||||||
const spr = await node.connect(peerId, addrs);
|
|
||||||
```
|
|
||||||
|
|||||||
1
examples/basic-auth/.gitignore
vendored
1
examples/basic-auth/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
index.bundle.js
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
# 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_CID 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.
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
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));
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
<html>
|
|
||||||
<script src="./index.bundle.js">
|
|
||||||
</script>
|
|
||||||
</html>
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
import { Codex } from "@codex-storage/sdk-js";
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const codex = new Codex(process.env.CODEX_NODE_URL, {
|
|
||||||
auth: {
|
|
||||||
basic: btoa("admin:SuperSecret123"),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = codex.data;
|
|
||||||
|
|
||||||
const cid = process.env.CODEX_CID;
|
|
||||||
|
|
||||||
const result = await data.networkDownloadStream(cid);
|
|
||||||
|
|
||||||
console.info(await result.data.text());
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
531
examples/basic-auth/package-lock.json
generated
531
examples/basic-auth/package-lock.json
generated
@ -1,531 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@codex-storage/sdk-js-basic-auth-example",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "@codex-storage/sdk-js-basic-auth-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.1.0",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"undici": "^7.5.0",
|
|
||||||
"valibot": "^0.32.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@tsconfig/strictest": "^2.0.5",
|
|
||||||
"@types/node": "^22.13.13",
|
|
||||||
"oas-normalize": "^13.1.2",
|
|
||||||
"openapi-typescript": "^7.6.1",
|
|
||||||
"prettier": "^3.5.3",
|
|
||||||
"tsup": "^8.3.6",
|
|
||||||
"typescript": "^5.8.2",
|
|
||||||
"vitest": "^3.0.9"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.18.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@codex-storage/sdk-js-basic-auth-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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
examples/download-browser/.gitignore
vendored
1
examples/download-browser/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
index.bundle.js
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
# 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_CID 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.
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
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));
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
<html>
|
|
||||||
<script src="./index.bundle.js">
|
|
||||||
</script>
|
|
||||||
</html>
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import { Codex } from "@codex-storage/sdk-js";
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const codex = new Codex(process.env.CODEX_NODE_URL);
|
|
||||||
|
|
||||||
const data = codex.data;
|
|
||||||
|
|
||||||
const cid = process.env.CODEX_CID;
|
|
||||||
|
|
||||||
const result = await data.networkDownloadStream(cid);
|
|
||||||
|
|
||||||
console.info(await result.data.text());
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@codex-storage/sdk-js-download-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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
examples/download-node/.gitignore
vendored
1
examples/download-node/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
index.bundle.js
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
# Download example
|
|
||||||
|
|
||||||
Small example to show how to download a file in node with Codex.
|
|
||||||
|
|
||||||
## Install dependencies
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
## Run node
|
|
||||||
|
|
||||||
```bash
|
|
||||||
CODEX_CID=REPLACE_BY_YOUR_CID node index.js
|
|
||||||
```
|
|
||||||
|
|
||||||
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".
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import { Codex } from "@codex-storage/sdk-js";
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const codex = new Codex(
|
|
||||||
process.env.CODEX_NODE_URL || "http://localhost:8080"
|
|
||||||
);
|
|
||||||
|
|
||||||
const data = codex.data;
|
|
||||||
|
|
||||||
const cid = process.env.CODEX_CID;
|
|
||||||
|
|
||||||
const result = await data.networkDownloadStream(cid);
|
|
||||||
|
|
||||||
console.info(await result.data.text());
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
74
examples/download-node/package-lock.json
generated
74
examples/download-node/package-lock.json
generated
@ -1,74 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@codex-storage/sdk-js-download-node-example",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "@codex-storage/sdk-js-download-node-example",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"@codex-storage/sdk-js": "../..",
|
|
||||||
"undici": "^7.7.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"prettier": "^3.5.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"..": {
|
|
||||||
"extraneous": true
|
|
||||||
},
|
|
||||||
"../..": {
|
|
||||||
"name": "@codex-storage/sdk-js",
|
|
||||||
"version": "0.1.2",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"valibot": "^1.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@tsconfig/strictest": "^2.0.5",
|
|
||||||
"@types/node": "^22.13.17",
|
|
||||||
"oas-normalize": "^13.1.2",
|
|
||||||
"openapi-typescript": "^7.6.1",
|
|
||||||
"prettier": "^3.5.3",
|
|
||||||
"tsup": "^8.3.6",
|
|
||||||
"typescript": "^5.8.2",
|
|
||||||
"vitest": "^3.1.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.18.1"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"undici": "^7.7.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@codex-storage/sdk-js": {
|
|
||||||
"resolved": "../..",
|
|
||||||
"link": true
|
|
||||||
},
|
|
||||||
"node_modules/prettier": {
|
|
||||||
"version": "3.5.3",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"prettier": "bin/prettier.cjs"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/undici": {
|
|
||||||
"version": "7.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.10.0.tgz",
|
|
||||||
"integrity": "sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.18.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@codex-storage/sdk-js-download-node-example",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"build": "node esbuild.js"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"description": "",
|
|
||||||
"dependencies": {
|
|
||||||
"@codex-storage/sdk-js": "../..",
|
|
||||||
"undici": "^7.7.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"prettier": "^3.5.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
examples/upload-browser/.gitignore
vendored
1
examples/upload-browser/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
index.bundle.js
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
# 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_CID 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.
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
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));
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
<html>
|
|
||||||
<script src="./index.bundle.js">
|
|
||||||
</script>
|
|
||||||
</html>
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
import { Codex } from "@codex-storage/sdk-js";
|
|
||||||
import { BrowserUploadStrategy } 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 strategy = new BrowserUploadStrategy(file, onProgress, metadata);
|
|
||||||
|
|
||||||
const uploadResponse = data.upload(strategy);
|
|
||||||
|
|
||||||
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
531
examples/upload-browser/package-lock.json
generated
@ -1,531 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
# 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".
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
const { Codex } = require("@codex-storage/sdk-js");
|
|
||||||
const { NodeUploadStrategy } = 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 strategy = new NodeUploadStrategy("Hello World !");
|
|
||||||
const uploadResponse = data.upload(strategy);
|
|
||||||
|
|
||||||
const res = await uploadResponse.result;
|
|
||||||
|
|
||||||
if (res.error) {
|
|
||||||
console.error(res.data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.info("CID is", res.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
80
examples/upload-node/package-lock.json
generated
80
examples/upload-node/package-lock.json
generated
@ -1,80 +0,0 @@
|
|||||||
{
|
|
||||||
"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": "../..",
|
|
||||||
"undici": "^7.7.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"prettier": "^3.5.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"..": {
|
|
||||||
"extraneous": true
|
|
||||||
},
|
|
||||||
"../..": {
|
|
||||||
"name": "@codex-storage/sdk-js",
|
|
||||||
"version": "0.1.1",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"undici": "^7.7.0",
|
|
||||||
"valibot": "^1.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@tsconfig/strictest": "^2.0.5",
|
|
||||||
"@types/node": "^22.13.17",
|
|
||||||
"oas-normalize": "^13.1.2",
|
|
||||||
"openapi-typescript": "^7.6.1",
|
|
||||||
"prettier": "^3.5.3",
|
|
||||||
"tsup": "^8.3.6",
|
|
||||||
"typescript": "^5.8.2",
|
|
||||||
"vitest": "^3.1.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.18.1"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"undici": "^7.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"../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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/undici": {
|
|
||||||
"version": "7.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.7.0.tgz",
|
|
||||||
"integrity": "sha512-tZ6+5NBq4KH35rr46XJ2JPFKxfcBlYNaqLF/wyWIO9RMHqqU/gx/CLB1Y2qMcgB8lWw/bKHa7qzspqCN7mUHvA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.18.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@codex-storage/sdk-js-update-node-example",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"description": "",
|
|
||||||
"dependencies": {
|
|
||||||
"@codex-storage/sdk-js": "../..",
|
|
||||||
"undici": "^7.7.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"prettier": "^3.5.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1043
openapi.yaml
1043
openapi.yaml
File diff suppressed because it is too large
Load Diff
2264
package-lock.json
generated
2264
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
41
package.json
41
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@codex-storage/sdk-js",
|
"name": "@codex-storage/sdk-js",
|
||||||
"version": "0.1.3",
|
"version": "0.0.22",
|
||||||
"description": "Codex SDK to interact with the Codex decentralized storage network.",
|
"description": "Codex SDK to interact with the Codex decentralized storage network.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -9,7 +9,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"prepack": "npm run build",
|
"prepack": "npm run build",
|
||||||
"prebuild": "npm run compile && rm -Rf dist/*",
|
"prebuild": "npm run compile && rm -Rf dist/*",
|
||||||
"build": "tsup src/index.ts src/async.ts src/browser.ts src/node.ts --format esm,cjs --dts --sourcemap --treeshake",
|
"build": "tsup src/index.ts src/async.ts --format esm,cjs --dts --sourcemap --treeshake",
|
||||||
"compile": "tsc --noEmit",
|
"compile": "tsc --noEmit",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
@ -35,26 +35,6 @@
|
|||||||
"default": "./dist/index.js"
|
"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": {
|
"./async": {
|
||||||
"import": {
|
"import": {
|
||||||
"types": "./dist/async.d.ts",
|
"types": "./dist/async.d.ts",
|
||||||
@ -66,6 +46,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sideEffects": false,
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
@ -73,22 +54,16 @@
|
|||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.18.1"
|
"node": ">=20"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tsconfig/strictest": "^2.0.5",
|
"@tsconfig/strictest": "^2.0.5",
|
||||||
"@types/node": "^22.13.17",
|
"prettier": "^3.4.2",
|
||||||
"oas-normalize": "^14.0.0",
|
|
||||||
"openapi-typescript": "^7.6.1",
|
|
||||||
"prettier": "^3.5.3",
|
|
||||||
"tsup": "^8.3.6",
|
"tsup": "^8.3.6",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.7.3",
|
||||||
"vitest": "^3.1.1"
|
"vitest": "^3.0.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"valibot": "^1.0.0"
|
"valibot": "^0.32.0"
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"undici": "^7.7.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from "./data/browser-upload";
|
|
||||||
@ -1,81 +0,0 @@
|
|||||||
import { CodexError } from "../errors/errors";
|
|
||||||
import type { SafeValue } from "../values/values";
|
|
||||||
import type { UploadStrategy, UploadStrategyOptions } from "./types";
|
|
||||||
|
|
||||||
export class BrowserUploadStrategy implements UploadStrategy {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
upload(
|
|
||||||
url: string,
|
|
||||||
{ auth }: UploadStrategyOptions
|
|
||||||
): 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 (auth?.basic) {
|
|
||||||
xhr.setRequestHeader("Authorization", "Basic " + auth.basic);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,201 +0,0 @@
|
|||||||
import { assert, describe, it } from "vitest";
|
|
||||||
import { CodexData } from "./data";
|
|
||||||
import { NodeUploadStrategy } from "./node-upload";
|
|
||||||
import crypto from "crypto";
|
|
||||||
|
|
||||||
describe("data", () => {
|
|
||||||
const data = new CodexData(
|
|
||||||
process.env["CLIENT_URL"] || "http://localhost:8080"
|
|
||||||
);
|
|
||||||
const spData = new CodexData(
|
|
||||||
process.env["SP_URL"] || "http://localhost:8081"
|
|
||||||
);
|
|
||||||
|
|
||||||
it("uploads a file a download it locally", async () => {
|
|
||||||
const content = crypto.randomBytes(16).toString("hex");
|
|
||||||
|
|
||||||
const strategy = new NodeUploadStrategy(content);
|
|
||||||
const res = data.upload(strategy);
|
|
||||||
const cid = await res.result;
|
|
||||||
assert.ok(cid.error == false);
|
|
||||||
assert.ok(cid.data);
|
|
||||||
|
|
||||||
const cids = await data.cids();
|
|
||||||
assert.ok(cids.error == false);
|
|
||||||
assert.ok(cids.data.content.find((c) => c.cid == cid.data));
|
|
||||||
|
|
||||||
const localDownload = await data.localDownload(cid.data);
|
|
||||||
assert.ok(localDownload.error == false);
|
|
||||||
assert.strictEqual(await localDownload.data.text(), content);
|
|
||||||
|
|
||||||
const manifest = await data.fetchManifest(cid.data);
|
|
||||||
assert.ok(manifest.error == false);
|
|
||||||
assert.strictEqual(manifest.data.cid, cid.data);
|
|
||||||
|
|
||||||
const { blockSize, datasetSize, treeCid } = manifest.data.manifest;
|
|
||||||
assert.ok(blockSize);
|
|
||||||
assert.ok(datasetSize);
|
|
||||||
assert.ok(treeCid);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("saves the metadata uploads provided during the upload", async () => {
|
|
||||||
const content = crypto.randomBytes(16).toString("hex");
|
|
||||||
|
|
||||||
const strategy = new NodeUploadStrategy(content, {
|
|
||||||
filename: "hello.txt",
|
|
||||||
mimetype: "text/plain",
|
|
||||||
});
|
|
||||||
const res = data.upload(strategy);
|
|
||||||
const cid = await res.result;
|
|
||||||
|
|
||||||
assert.ok(cid.error == false);
|
|
||||||
assert.ok(cid.data);
|
|
||||||
|
|
||||||
const manifest = await data.fetchManifest(cid.data);
|
|
||||||
assert.ok(manifest.error == false);
|
|
||||||
assert.strictEqual(manifest.data.cid, cid.data);
|
|
||||||
|
|
||||||
const { filename, mimetype } = manifest.data.manifest;
|
|
||||||
assert.strictEqual(filename, "hello.txt");
|
|
||||||
assert.ok(mimetype, "text/plain");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("fails when providing wrong metadata", async () => {
|
|
||||||
const content = crypto.randomBytes(16).toString("hex");
|
|
||||||
|
|
||||||
const strategy = new NodeUploadStrategy(content, {
|
|
||||||
filename: "hello.txt",
|
|
||||||
mimetype: "plain/text",
|
|
||||||
});
|
|
||||||
const res = data.upload(strategy);
|
|
||||||
const cid = await res.result;
|
|
||||||
|
|
||||||
assert.ok(cid.error == true);
|
|
||||||
assert.ok(
|
|
||||||
cid.data.message.includes(" The MIME type 'plain/text' is not valid")
|
|
||||||
);
|
|
||||||
assert.equal(cid.data.code, 422);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("delete a file a locally", async () => {
|
|
||||||
const content = "b".repeat(131072);
|
|
||||||
const strategy = new NodeUploadStrategy(content);
|
|
||||||
const res = data.upload(strategy);
|
|
||||||
const cid = await res.result;
|
|
||||||
assert.ok(cid.error == false);
|
|
||||||
assert.ok(cid.data);
|
|
||||||
|
|
||||||
let cids = await data.cids();
|
|
||||||
assert.ok(cids.error == false);
|
|
||||||
assert.ok(cids.data.content.find((c) => c.cid == cid.data));
|
|
||||||
|
|
||||||
const del = await data.delete(cid.data);
|
|
||||||
assert.ok(del.error == false);
|
|
||||||
|
|
||||||
cids = await data.cids();
|
|
||||||
assert.ok(cids.error == false);
|
|
||||||
assert.notOk(cids.data.content.find((c) => c.cid == cid.data));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("doesn't do anything when trying to delete a non existing cid", async () => {
|
|
||||||
const content = crypto.randomBytes(16).toString("hex");
|
|
||||||
const strategy = new NodeUploadStrategy(content);
|
|
||||||
const res = spData.upload(strategy);
|
|
||||||
const cid = await res.result;
|
|
||||||
assert.ok(cid.error == false);
|
|
||||||
assert.ok(cid.data);
|
|
||||||
|
|
||||||
const del = await data.delete(cid.data);
|
|
||||||
assert.ok(del.error == false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns an error when providing an invalid cid", async () => {
|
|
||||||
const del = await data.delete("hello");
|
|
||||||
assert.ok(del.error);
|
|
||||||
assert.ok(del.data.message.includes("Incorrect Cid"));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates the space available when storing data", async () => {
|
|
||||||
const content = crypto.randomBytes(16).toString("hex");
|
|
||||||
|
|
||||||
let space = await data.space();
|
|
||||||
assert.ok(space.error == false);
|
|
||||||
assert.ok(space.data.quotaMaxBytes);
|
|
||||||
|
|
||||||
const usedBytes = space.data.quotaUsedBytes;
|
|
||||||
|
|
||||||
const strategy = new NodeUploadStrategy(content);
|
|
||||||
const res = data.upload(strategy);
|
|
||||||
const cid = await res.result;
|
|
||||||
assert.ok(cid.error == false);
|
|
||||||
assert.ok(cid.data);
|
|
||||||
|
|
||||||
space = await data.space();
|
|
||||||
assert.ok(space.error == false);
|
|
||||||
assert.ok(space.data.quotaMaxBytes);
|
|
||||||
assert.ok(space.data.quotaUsedBytes > usedBytes);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("stream downloads a file on the network", async () => {
|
|
||||||
const content = crypto.randomBytes(16).toString("hex");
|
|
||||||
|
|
||||||
const strategy = new NodeUploadStrategy(content);
|
|
||||||
const res = spData.upload(strategy);
|
|
||||||
const cid = await res.result;
|
|
||||||
assert.ok(cid.error == false);
|
|
||||||
assert.ok(cid.data);
|
|
||||||
|
|
||||||
const networkDownload = await data.networkDownloadStream(cid.data);
|
|
||||||
assert.ok(networkDownload.error == false);
|
|
||||||
assert.strictEqual(await networkDownload.data.text(), content);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("downloads a file on the network", async () => {
|
|
||||||
const content = crypto.randomBytes(16).toString("hex");
|
|
||||||
|
|
||||||
const strategy = new NodeUploadStrategy(content);
|
|
||||||
const res = spData.upload(strategy);
|
|
||||||
const cid = await res.result;
|
|
||||||
assert.ok(cid.error == false);
|
|
||||||
assert.ok(cid.data);
|
|
||||||
|
|
||||||
const networkDownload = await data.networkDownload(cid.data);
|
|
||||||
assert.ok(networkDownload.error == false);
|
|
||||||
|
|
||||||
const cids = await data.cids();
|
|
||||||
assert.ok(cids.error == false);
|
|
||||||
assert.ok(cids.data.content.find((c) => c.cid == cid.data));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns an error when trying to stream download a not existing file on the network", async () => {
|
|
||||||
const cid = crypto.randomBytes(16).toString("hex");
|
|
||||||
|
|
||||||
const networkDownload = await data.networkDownloadStream(cid);
|
|
||||||
assert.ok(networkDownload.error);
|
|
||||||
assert.strictEqual(networkDownload.data.message, "Incorrect Cid");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns an error when trying to download a not existing file on the network", async () => {
|
|
||||||
const cid = crypto.randomBytes(16).toString("hex");
|
|
||||||
|
|
||||||
const networkDownload = await data.networkDownload(cid);
|
|
||||||
assert.ok(networkDownload.error);
|
|
||||||
assert.strictEqual(networkDownload.data.message, "Incorrect Cid");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns an error when trying to download a not existing file locally", async () => {
|
|
||||||
const cid = crypto.randomBytes(16).toString("hex");
|
|
||||||
|
|
||||||
const networkDownload = await data.localDownload(cid);
|
|
||||||
assert.ok(networkDownload.error);
|
|
||||||
assert.strictEqual(networkDownload.data.message, "Incorrect Cid");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns an error when trying to fetch a not existing manifest", async () => {
|
|
||||||
const cid = crypto.randomBytes(16).toString("hex");
|
|
||||||
|
|
||||||
const fetchManifest = await data.fetchManifest(cid);
|
|
||||||
assert.ok(fetchManifest.error);
|
|
||||||
assert.strictEqual(fetchManifest.data.message, "Incorrect Cid");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
145
src/data/data.ts
145
src/data/data.ts
@ -1,66 +1,53 @@
|
|||||||
import { Api } from "../api/config";
|
import { Api } from "../api/config";
|
||||||
import {
|
import { CodexError } from "../errors/errors";
|
||||||
Fetch,
|
import { Fetch } from "../fetch-safe/fetch-safe";
|
||||||
FetchAuthBuilder,
|
|
||||||
type FetchAuth,
|
|
||||||
} from "../fetch-safe/fetch-safe";
|
|
||||||
import type { SafeValue } from "../values/values";
|
import type { SafeValue } from "../values/values";
|
||||||
import type {
|
import type {
|
||||||
CodexDataResponse,
|
CodexDataResponse,
|
||||||
UploadStrategy,
|
|
||||||
UploadResponse,
|
|
||||||
CodexSpaceResponse,
|
|
||||||
CodexNodeSpace,
|
|
||||||
CodexDataNetworkResponse,
|
|
||||||
CodexNetworkDownload,
|
|
||||||
CodexManifest,
|
CodexManifest,
|
||||||
CodexDataItems,
|
CodexNodeSpace,
|
||||||
|
NetworkDownloadResponse,
|
||||||
|
UploadResponse,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
type CodexDataOptions = {
|
|
||||||
auth?: FetchAuth;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class CodexData {
|
export class CodexData {
|
||||||
readonly url: string;
|
readonly url: string;
|
||||||
readonly auth: FetchAuth = {};
|
|
||||||
|
|
||||||
constructor(url: string, options?: CodexDataOptions) {
|
constructor(url: string) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
|
|
||||||
if (options?.auth) {
|
|
||||||
this.auth = options.auth;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists manifest CIDs stored locally in node.
|
* Lists manifest CIDs stored locally in node.
|
||||||
* TODO: remove the faker data part when the api is ready
|
* TODO: remove the faker data part when the api is ready
|
||||||
*/
|
*/
|
||||||
cids(): Promise<SafeValue<CodexDataItems>> {
|
cids(): Promise<SafeValue<CodexDataResponse>> {
|
||||||
const url = this.url + Api.config.prefix + "/data";
|
const url = this.url + Api.config.prefix + "/data";
|
||||||
|
|
||||||
return Fetch.safeJson<CodexDataResponse>(url, {
|
return Fetch.safeJson<CodexDataResponse>(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
}).then((data) => {
|
}).then((data) => {
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { error: false, data: { content: data.data.content } };
|
return {
|
||||||
|
error: false,
|
||||||
|
data: {
|
||||||
|
content: data.data.content,
|
||||||
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a summary of the storage space allocation of the node.
|
* Gets a summary of the storage space allocation of the node.
|
||||||
*/
|
*/
|
||||||
space(): Promise<SafeValue<CodexNodeSpace>> {
|
space() {
|
||||||
const url = this.url + Api.config.prefix + "/space";
|
const url = this.url + Api.config.prefix + "/space";
|
||||||
|
|
||||||
return Fetch.safeJson<CodexSpaceResponse>(url, {
|
return Fetch.safeJson<CodexNodeSpace>(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,13 +57,59 @@ export class CodexData {
|
|||||||
* XMLHttpRequest is used instead of fetch for this case, to obtain progress information.
|
* 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.
|
* A callback onProgress can be passed to receive upload progress data information.
|
||||||
*/
|
*/
|
||||||
upload(strategy: UploadStrategy): UploadResponse {
|
upload(
|
||||||
|
file: Document | XMLHttpRequestBodyInit,
|
||||||
|
onProgress?: (loaded: number, total: number) => void,
|
||||||
|
metadata: { filename?: string, mimetype?: string } = {},
|
||||||
|
): UploadResponse {
|
||||||
const url = this.url + Api.config.prefix + "/data";
|
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 {
|
return {
|
||||||
result: strategy.upload(url, { auth: this.auth }),
|
result: promise,
|
||||||
abort: () => {
|
abort: () => {
|
||||||
strategy.abort();
|
xhr.abort();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -90,20 +123,6 @@ export class CodexData {
|
|||||||
|
|
||||||
return Fetch.safe(url, {
|
return Fetch.safe(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Download a file from the network to the local node if it's not available locally.
|
|
||||||
* Note: Download is performed async. Call can return before download is completed.
|
|
||||||
*/
|
|
||||||
async networkDownload(cid: string): Promise<SafeValue<CodexNetworkDownload>> {
|
|
||||||
const url = this.url + Api.config.prefix + `/data/${cid}/network`;
|
|
||||||
|
|
||||||
return Fetch.safeJson<CodexDataNetworkResponse>(url, {
|
|
||||||
method: "POST",
|
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,39 +130,35 @@ export class CodexData {
|
|||||||
* 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.
|
* 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>> {
|
||||||
|
const url = this.url + Api.config.prefix + `/data/${cid}/network`;
|
||||||
|
|
||||||
|
return Fetch.safeJson(url, {
|
||||||
|
method: "POST"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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>> {
|
async networkDownloadStream(cid: string): Promise<SafeValue<Response>> {
|
||||||
const url = this.url + Api.config.prefix + `/data/${cid}/network/stream`;
|
const url = this.url + Api.config.prefix + `/data/${cid}/network/stream`;
|
||||||
|
|
||||||
return Fetch.safe(url, {
|
return Fetch.safe(url, {
|
||||||
method: "GET",
|
method: "GET"
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download only the dataset manifest from the network to the local node
|
* Download only the dataset manifest from the network to the local node
|
||||||
* if it's not available locally.
|
* if it's not available locally.
|
||||||
*/
|
*/
|
||||||
async fetchManifest(cid: string): Promise<SafeValue<CodexManifest>> {
|
async fetchManifest(cid: string) {
|
||||||
const url = this.url + Api.config.prefix + `/data/${cid}/network/manifest`;
|
const url = this.url + Api.config.prefix + `/data/${cid}/network/manifest`;
|
||||||
|
|
||||||
return Fetch.safeJson<CodexManifest>(url, {
|
return Fetch.safeJson<CodexManifest>(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes either a single block or an entire dataset
|
|
||||||
* from the local node. Does nothing
|
|
||||||
* if the dataset is not locally available.
|
|
||||||
*/
|
|
||||||
async delete(cid: string): Promise<SafeValue<string>> {
|
|
||||||
const url = this.url + Api.config.prefix + `/data/${cid}`;
|
|
||||||
|
|
||||||
return Fetch.safeText(url, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,73 +0,0 @@
|
|||||||
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 { UploadStrategy, UploadStrategyOptions } from "./types";
|
|
||||||
import { FetchAuthBuilder } from "../fetch-safe/fetch-safe";
|
|
||||||
|
|
||||||
export class NodeUploadStrategy implements UploadStrategy {
|
|
||||||
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 upload(
|
|
||||||
url: string,
|
|
||||||
{ auth }: UploadStrategyOptions
|
|
||||||
): Promise<SafeValue<string>> {
|
|
||||||
const headers: Record<string, string> = FetchAuthBuilder.build(auth);
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,47 +1,89 @@
|
|||||||
import type { components, paths } from "../openapi";
|
|
||||||
import type { FetchAuth } from "../fetch-safe/fetch-safe";
|
|
||||||
import type { SafeValue } from "../values/values";
|
import type { SafeValue } from "../values/values";
|
||||||
|
|
||||||
export type CodexDataResponse =
|
export type CodexManifest = {
|
||||||
paths["/data"]["get"]["responses"][200]["content"]["application/json"];
|
/**
|
||||||
|
* "Root hash of the content"
|
||||||
|
*/
|
||||||
|
// rootHash: string;
|
||||||
|
|
||||||
export type CodexDataItem = components["schemas"]["DataItem"];
|
/**
|
||||||
|
* Length of original content in bytes
|
||||||
|
*/
|
||||||
|
// originalBytes: number;
|
||||||
|
|
||||||
export type CodexDataItems = CodexDataResponse;
|
/**
|
||||||
|
* Total size of all blocks
|
||||||
|
*/
|
||||||
|
datasetSize: number;
|
||||||
|
|
||||||
export type CodexSpaceResponse =
|
/**
|
||||||
paths["/space"]["get"]["responses"][200]["content"]["application/json"];
|
* "Size of blocks"
|
||||||
|
*/
|
||||||
|
blockSize: number;
|
||||||
|
|
||||||
export type CodexNodeSpace = CodexSpaceResponse;
|
/**
|
||||||
|
* Indicates if content is protected by erasure-coding
|
||||||
|
*/
|
||||||
|
protected: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Root of the merkle tree
|
||||||
|
*/
|
||||||
|
treeCid: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the name
|
||||||
|
*/
|
||||||
|
filename: string | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mimetype
|
||||||
|
*/
|
||||||
|
mimetype: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CodexDataContent = {
|
||||||
|
/**
|
||||||
|
* Content Identifier as specified at https://github.com/multiformats/cid
|
||||||
|
*/
|
||||||
|
cid: string;
|
||||||
|
|
||||||
|
manifest: CodexManifest;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CodexDataResponse = {
|
||||||
|
content: CodexDataContent[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CodexNodeSpace = {
|
||||||
|
/**
|
||||||
|
* Number of blocks stored by the node
|
||||||
|
*/
|
||||||
|
totalBlocks: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum storage space used by the node
|
||||||
|
*/
|
||||||
|
quotaMaxBytes: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount of storage space currently in use
|
||||||
|
*/
|
||||||
|
quotaUsedBytes: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount of storage space reserved
|
||||||
|
*/
|
||||||
|
quotaReservedBytes: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type UploadResponse = {
|
export type UploadResponse = {
|
||||||
result: Promise<SafeValue<string>>;
|
result: Promise<SafeValue<string>>;
|
||||||
abort: () => void;
|
abort: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CodexDataNetworkResponse =
|
|
||||||
paths["/data/{cid}/network"]["post"]["responses"][200]["content"]["application/json"];
|
|
||||||
|
|
||||||
export type CodexNetworkDownload = components["schemas"]["DataItem"];
|
export type NetworkDownloadResponse = {
|
||||||
|
cid: string
|
||||||
export type CodexFetchManifestResponse =
|
manifest: CodexManifest
|
||||||
paths["/data/{cid}/network/manifest"]["get"]["responses"][200]["content"]["application/json"];
|
}
|
||||||
|
|
||||||
export type CodexManifest = CodexFetchManifestResponse;
|
|
||||||
|
|
||||||
export type UploadStrategyOptions = {
|
|
||||||
auth?: FetchAuth;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface UploadStrategy {
|
|
||||||
upload(
|
|
||||||
url: string,
|
|
||||||
options?: UploadStrategyOptions
|
|
||||||
): Promise<SafeValue<string>>;
|
|
||||||
abort(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// paths["/data/{cid}"]["delete"]["responses"][204]["content"];
|
|
||||||
export type CodexDeleteResponse = "";
|
|
||||||
|
|
||||||
export type CodexDelete = CodexDeleteResponse;
|
|
||||||
@ -1,26 +1,45 @@
|
|||||||
import { assert, describe, it } from "vitest";
|
import { afterEach, assert, describe, it, vi } from "vitest";
|
||||||
import { CodexDebug } from "./debug";
|
import { CodexDebug } from "./debug";
|
||||||
|
import type { CodexLogLevel } from "./types";
|
||||||
|
import { CodexError } from "../errors/errors";
|
||||||
|
|
||||||
describe("debug", () => {
|
describe("debug", () => {
|
||||||
const debug = new CodexDebug(
|
afterEach(() => {
|
||||||
process.env["CLIENT_URL"] || "http://localhost:8080"
|
vi.restoreAllMocks();
|
||||||
);
|
|
||||||
|
|
||||||
it("changes the log level", async () => {
|
|
||||||
const logLevel = await debug.setLogLevel("NOTICE");
|
|
||||||
assert.ok(logLevel.error == false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("gets the debug info", async () => {
|
const debug = new CodexDebug("http://localhost:3000");
|
||||||
const info = await debug.info();
|
|
||||||
assert.ok(info.error == false);
|
it("returns an error when trying to setup the log level with a bad value", async () => {
|
||||||
assert.ok(info.data.spr);
|
const response = await debug.setLogLevel("TEST" as CodexLogLevel);
|
||||||
assert.ok(info.data.announceAddresses.length > 0);
|
|
||||||
|
assert.deepStrictEqual(response, {
|
||||||
|
error: true,
|
||||||
|
data: new CodexError("Cannot validate the input", {
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
expected:
|
||||||
|
'"TRACE" | "DEBUG" | "INFO" | "NOTICE" | "WARN" | "ERROR" | "FATAL"',
|
||||||
|
message:
|
||||||
|
'Invalid type: Expected "TRACE" | "DEBUG" | "INFO" | "NOTICE" | "WARN" | "ERROR" | "FATAL" but received "TEST"',
|
||||||
|
path: undefined,
|
||||||
|
received: '"TEST"',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns error when changing the log level with wrong value", async () => {
|
it("returns a success when trying to setup the log level with a correct value", async () => {
|
||||||
const logLevel = await debug.setLogLevel("HELLO");
|
const mockResponse = {
|
||||||
assert.ok(logLevel.error);
|
ok: true,
|
||||||
assert.strictEqual(logLevel.data.message, "Cannot validate the input");
|
status: 200,
|
||||||
|
text: async () => "",
|
||||||
|
} as Response;
|
||||||
|
globalThis.fetch = vi.fn().mockResolvedValue(mockResponse);
|
||||||
|
|
||||||
|
const response = await debug.setLogLevel("ERROR");
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, { error: false, data: "" });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,40 +1,22 @@
|
|||||||
import { Api } from "../api/config";
|
import { Api } from "../api/config";
|
||||||
import { CodexError, CodexValibotIssuesMap } from "../errors/errors";
|
import { CodexError, CodexValibotIssuesMap } from "../errors/errors";
|
||||||
import {
|
import { Fetch } from "../fetch-safe/fetch-safe";
|
||||||
Fetch,
|
|
||||||
FetchAuthBuilder,
|
|
||||||
type FetchAuth,
|
|
||||||
} from "../fetch-safe/fetch-safe";
|
|
||||||
import type { SafeValue } from "../values/values";
|
import type { SafeValue } from "../values/values";
|
||||||
import {
|
import { CodexLogLevel, type CodexDebugInfo } from "./types";
|
||||||
CodexLogLevelInput,
|
|
||||||
type CodexDebugInfo,
|
|
||||||
type CodexInfoResponse,
|
|
||||||
type CodexLogLevel,
|
|
||||||
} from "./types";
|
|
||||||
import * as v from "valibot";
|
import * as v from "valibot";
|
||||||
|
|
||||||
type CodexDebugOptions = {
|
|
||||||
auth?: FetchAuth;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class CodexDebug {
|
export class CodexDebug {
|
||||||
readonly url: string;
|
readonly url: string;
|
||||||
readonly auth: FetchAuth = {};
|
|
||||||
|
|
||||||
constructor(url: string, options?: CodexDebugOptions) {
|
constructor(url: string) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
|
|
||||||
if (options?.auth) {
|
|
||||||
this.auth = options.auth;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set log level at run time
|
* Set log level at run time
|
||||||
*/
|
*/
|
||||||
async setLogLevel(level: CodexLogLevel): Promise<SafeValue<string>> {
|
async setLogLevel(level: CodexLogLevel): Promise<SafeValue<"">> {
|
||||||
const result = v.safeParse(CodexLogLevelInput, level);
|
const result = v.safeParse(CodexLogLevel, level);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
@ -51,22 +33,26 @@ export class CodexDebug {
|
|||||||
"/debug/chronicles/loglevel?level=" +
|
"/debug/chronicles/loglevel?level=" +
|
||||||
level;
|
level;
|
||||||
|
|
||||||
return Fetch.safeText(url, {
|
const res = await Fetch.safe(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
body: "",
|
body: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (res.error) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { error: false, data: "" };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets node information
|
* Gets node information
|
||||||
*/
|
*/
|
||||||
info(): Promise<SafeValue<CodexDebugInfo>> {
|
info() {
|
||||||
const url = this.url + Api.config.prefix + `/debug/info`;
|
const url = this.url + Api.config.prefix + `/debug/info`;
|
||||||
|
|
||||||
return Fetch.safeJson<CodexInfoResponse>(url, {
|
return Fetch.safeJson<CodexDebugInfo>(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,6 @@
|
|||||||
import * as v from "valibot";
|
import * as v from "valibot";
|
||||||
import type { paths } from "../openapi";
|
|
||||||
|
|
||||||
export type CodexLogLevelResponse =
|
export const CodexLogLevel = v.picklist([
|
||||||
paths["/debug/chronicles/loglevel"]["post"]["responses"][200]["content"];
|
|
||||||
|
|
||||||
export type CodexLogLevel =
|
|
||||||
paths["/debug/chronicles/loglevel"]["post"]["parameters"]["query"]["level"];
|
|
||||||
|
|
||||||
export const CodexLogLevelInput = v.picklist([
|
|
||||||
"TRACE",
|
"TRACE",
|
||||||
"DEBUG",
|
"DEBUG",
|
||||||
"INFO",
|
"INFO",
|
||||||
@ -17,7 +10,49 @@ export const CodexLogLevelInput = v.picklist([
|
|||||||
"FATAL",
|
"FATAL",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export type CodexInfoResponse =
|
export type CodexLogLevel = v.InferOutput<typeof CodexLogLevel>;
|
||||||
paths["/debug/info"]["get"]["responses"][200]["content"]["application/json"];
|
|
||||||
|
|
||||||
export type CodexDebugInfo = CodexInfoResponse;
|
export type CodexDebugInfo = {
|
||||||
|
/**
|
||||||
|
* Peer Identity reference as specified at https://docs.libp2p.io/concepts/fundamentals/peers/
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address of node as specified by the multi-address specification https://multiformats.io/multiaddr/
|
||||||
|
*/
|
||||||
|
addrs: string[];
|
||||||
|
|
||||||
|
announceAddresses: string[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path of the data repository where all nodes data are stored
|
||||||
|
*/
|
||||||
|
repo: string;
|
||||||
|
|
||||||
|
// Signed Peer Record (libp2p)
|
||||||
|
spr: string;
|
||||||
|
|
||||||
|
table: {
|
||||||
|
localNode: {
|
||||||
|
nodeId: string
|
||||||
|
peerId: string
|
||||||
|
record: string
|
||||||
|
address: string
|
||||||
|
seen: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes: {
|
||||||
|
nodeId: string
|
||||||
|
peerId: string
|
||||||
|
record: string
|
||||||
|
address: string
|
||||||
|
seen: boolean
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
codex: {
|
||||||
|
version: string
|
||||||
|
revision: string
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { afterEach, assert, describe, it, vi } from "vitest";
|
import { afterEach, assert, describe, it, vi } from "vitest";
|
||||||
import { Fetch } from "../fetch-safe/fetch-safe";
|
import { Fetch } from "../fetch-safe/fetch-safe";
|
||||||
import { CodexError } from "../errors/errors";
|
import { CodexError } from "../async";
|
||||||
|
|
||||||
describe("fetch", () => {
|
describe.only("fetch", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.restoreAllMocks();
|
vi.restoreAllMocks();
|
||||||
});
|
});
|
||||||
@ -26,7 +26,7 @@ describe("fetch", () => {
|
|||||||
assert.deepStrictEqual(result, { error: true, data: error });
|
assert.deepStrictEqual(result, { error: true, data: error });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns an error when the json parsing failed", async () => {
|
it.only("returns an error when the json parsing failed", async () => {
|
||||||
const mockResponse = {
|
const mockResponse = {
|
||||||
ok: true,
|
ok: true,
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@ -2,21 +2,6 @@ import { CodexError } from "../errors/errors";
|
|||||||
import { Promises } from "../promise-safe/promise-safe";
|
import { Promises } from "../promise-safe/promise-safe";
|
||||||
import { type SafeValue } from "../values/values";
|
import { type SafeValue } from "../values/values";
|
||||||
|
|
||||||
export type FetchAuth = {
|
|
||||||
basic?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const FetchAuthBuilder = {
|
|
||||||
build(auth: FetchAuth | undefined) {
|
|
||||||
if (auth?.basic) {
|
|
||||||
return {
|
|
||||||
Authorization: "Basic " + auth.basic,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Fetch = {
|
export const Fetch = {
|
||||||
async safe(url: string, init: RequestInit): Promise<SafeValue<Response>> {
|
async safe(url: string, init: RequestInit): Promise<SafeValue<Response>> {
|
||||||
const res = await Promises.safe(() => fetch(url, init));
|
const res = await Promises.safe(() => fetch(url, init));
|
||||||
@ -60,14 +45,4 @@ export const Fetch = {
|
|||||||
|
|
||||||
return Promises.safe(() => res.data.json());
|
return Promises.safe(() => res.data.json());
|
||||||
},
|
},
|
||||||
|
|
||||||
async safeText(url: string, init: RequestInit): Promise<SafeValue<string>> {
|
|
||||||
const res = await this.safe(url, init);
|
|
||||||
|
|
||||||
if (res.error) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promises.safe(() => res.data.text());
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|||||||
20
src/index.ts
20
src/index.ts
@ -2,7 +2,6 @@ import { CodexData } from "./data/data";
|
|||||||
import { CodexNode } from "./node/node";
|
import { CodexNode } from "./node/node";
|
||||||
import { CodexMarketplace } from "./marketplace/marketplace";
|
import { CodexMarketplace } from "./marketplace/marketplace";
|
||||||
import { CodexDebug } from "./debug/debug";
|
import { CodexDebug } from "./debug/debug";
|
||||||
import type { FetchAuth } from "./fetch-safe/fetch-safe";
|
|
||||||
|
|
||||||
export * from "./fetch-safe/fetch-safe";
|
export * from "./fetch-safe/fetch-safe";
|
||||||
export * from "./marketplace/types";
|
export * from "./marketplace/types";
|
||||||
@ -16,28 +15,19 @@ export { CodexData } from "./data/data";
|
|||||||
export { CodexNode } from "./node/node";
|
export { CodexNode } from "./node/node";
|
||||||
export { CodexMarketplace } from "./marketplace/marketplace";
|
export { CodexMarketplace } from "./marketplace/marketplace";
|
||||||
|
|
||||||
type CodexProps = {
|
|
||||||
auth?: FetchAuth;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class Codex {
|
export class Codex {
|
||||||
readonly url: string;
|
readonly url: string;
|
||||||
private _marketplace: CodexMarketplace | null;
|
private _marketplace: CodexMarketplace | null;
|
||||||
private _data: CodexData | null;
|
private _data: CodexData | null;
|
||||||
private _node: CodexNode | null;
|
private _node: CodexNode | null;
|
||||||
private _debug: CodexDebug | null;
|
private _debug: CodexDebug | null;
|
||||||
private readonly auth: FetchAuth = {};
|
|
||||||
|
|
||||||
constructor(url: string, options?: CodexProps) {
|
constructor(url: string) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this._marketplace = null;
|
this._marketplace = null;
|
||||||
this._data = null;
|
this._data = null;
|
||||||
this._node = null;
|
this._node = null;
|
||||||
this._debug = null;
|
this._debug = null;
|
||||||
|
|
||||||
if (options?.auth) {
|
|
||||||
this.auth = options?.auth;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get marketplace() {
|
get marketplace() {
|
||||||
@ -45,7 +35,7 @@ export class Codex {
|
|||||||
return this._marketplace;
|
return this._marketplace;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._marketplace = new CodexMarketplace(this.url, { auth: this.auth });
|
this._marketplace = new CodexMarketplace(this.url);
|
||||||
|
|
||||||
return this._marketplace;
|
return this._marketplace;
|
||||||
}
|
}
|
||||||
@ -55,7 +45,7 @@ export class Codex {
|
|||||||
return this._data;
|
return this._data;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._data = new CodexData(this.url, { auth: this.auth });
|
this._data = new CodexData(this.url);
|
||||||
|
|
||||||
return this._data;
|
return this._data;
|
||||||
}
|
}
|
||||||
@ -65,7 +55,7 @@ export class Codex {
|
|||||||
return this._node;
|
return this._node;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._node = new CodexNode(this.url, { auth: this.auth });
|
this._node = new CodexNode(this.url);
|
||||||
|
|
||||||
return this._node;
|
return this._node;
|
||||||
}
|
}
|
||||||
@ -75,7 +65,7 @@ export class Codex {
|
|||||||
return this._debug;
|
return this._debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._debug = new CodexDebug(this.url, { auth: this.auth });
|
this._debug = new CodexDebug(this.url);
|
||||||
|
|
||||||
return this._debug;
|
return this._debug;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,276 +1,379 @@
|
|||||||
import { assert, describe, it } from "vitest";
|
import { afterEach, assert, describe, it, vi } from "vitest";
|
||||||
|
import { Fetch } from "../fetch-safe/fetch-safe";
|
||||||
import { CodexMarketplace } from "./marketplace";
|
import { CodexMarketplace } from "./marketplace";
|
||||||
import { CodexData } from "../data/data";
|
import {
|
||||||
import { NodeUploadStrategy } from "../data/node-upload";
|
randomEthereumAddress,
|
||||||
import type {
|
randomInt,
|
||||||
CodexAvailabilityPatchInput,
|
randomString,
|
||||||
CodexCreateAvailabilityInput,
|
} from "../tests/tests.util";
|
||||||
CodexCreateStorageRequestInput,
|
import { CodexError } from "../errors/errors";
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
describe("marketplace", async () => {
|
function createStorageRequest() {
|
||||||
describe("availability", async () => {
|
return {
|
||||||
const spMarketplace = new CodexMarketplace(
|
cid: randomString(64),
|
||||||
process.env["SP_URL"] || "http://localhost:8081"
|
duration: randomInt(1, 64000),
|
||||||
|
pricePerBytePerSecond: randomInt(1, 100),
|
||||||
|
proofProbability: randomInt(1, 100),
|
||||||
|
nodes: randomInt(1, 5),
|
||||||
|
tolerance: randomInt(1, 100),
|
||||||
|
expiry: randomInt(1, 100),
|
||||||
|
collateralPerByte: randomInt(1, 100),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function missingNumberValidationError(field: string) {
|
||||||
|
return {
|
||||||
|
error: true as any,
|
||||||
|
data: new CodexError("Cannot validate the input", {
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
path: field,
|
||||||
|
expected: "number",
|
||||||
|
message: "Invalid type: Expected number but received undefined",
|
||||||
|
received: "undefined",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function extraValidationError(field: string, value: unknown) {
|
||||||
|
return {
|
||||||
|
error: true as any,
|
||||||
|
data: new CodexError("Cannot validate the input", {
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
path: field,
|
||||||
|
expected: "never",
|
||||||
|
message: `Invalid type: Expected never but received "${value}"`,
|
||||||
|
received: `"${value}"`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function missingStringValidationError(field: string) {
|
||||||
|
return {
|
||||||
|
error: true as any,
|
||||||
|
data: new CodexError("Cannot validate the input", {
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
path: field,
|
||||||
|
expected: "string",
|
||||||
|
message: "Invalid type: Expected string but received undefined",
|
||||||
|
received: "undefined",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mistypeNumberValidationError(field: string, value: string) {
|
||||||
|
return {
|
||||||
|
error: true as any,
|
||||||
|
data: new CodexError("Cannot validate the input", {
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
path: field,
|
||||||
|
expected: "number",
|
||||||
|
message: `Invalid type: Expected number but received "${value}"`,
|
||||||
|
received: `"${value}"`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function minNumberValidationError(field: string, min: number) {
|
||||||
|
return {
|
||||||
|
error: true as any,
|
||||||
|
data: new CodexError("Cannot validate the input", {
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
path: field,
|
||||||
|
expected: ">=" + min,
|
||||||
|
message: "Invalid value: Expected >=1 but received 0",
|
||||||
|
received: "0",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAvailability() {
|
||||||
|
return {
|
||||||
|
id: randomEthereumAddress(),
|
||||||
|
totalSize: randomInt(0, 9).toString(),
|
||||||
|
duration: randomInt(0, 9).toString(),
|
||||||
|
minPrice: randomInt(0, 9).toString(),
|
||||||
|
maxCollateral: randomInt(0, 9).toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("marketplace", () => {
|
||||||
|
const marketplace = new CodexMarketplace("http://localhost:3000");
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create an availability without total size", async () => {
|
||||||
|
const response = await marketplace.createAvailability({
|
||||||
|
duration: 3000,
|
||||||
|
maxCollateral: 1,
|
||||||
|
minPrice: 100,
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, missingNumberValidationError("totalSize"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create an availability with an invalid number valid", async () => {
|
||||||
|
const response = await marketplace.createAvailability({
|
||||||
|
duration: 3000,
|
||||||
|
maxCollateral: 1,
|
||||||
|
minPrice: 100,
|
||||||
|
totalSize: "abc",
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
response,
|
||||||
|
mistypeNumberValidationError("totalSize", "abc")
|
||||||
);
|
);
|
||||||
const totalSize = 1_000_000;
|
|
||||||
const duration = 3000;
|
|
||||||
const minPricePerBytePerSecond = 1000;
|
|
||||||
const totalCollateral = 1_000_000_000;
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
duration,
|
|
||||||
totalCollateral,
|
|
||||||
minPricePerBytePerSecond,
|
|
||||||
totalSize,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await spMarketplace.createAvailability(body);
|
|
||||||
assert.ok(result.error == false);
|
|
||||||
|
|
||||||
const availability = result.data;
|
|
||||||
|
|
||||||
describe("create", async () => {
|
|
||||||
it("verifies that the availability was created successfully", async () => {
|
|
||||||
assert.ok(availability.id);
|
|
||||||
assert.equal(availability.duration, duration);
|
|
||||||
assert.equal(availability.freeSize, totalSize);
|
|
||||||
assert.equal(
|
|
||||||
availability.minPricePerBytePerSecond,
|
|
||||||
BigInt(minPricePerBytePerSecond)
|
|
||||||
);
|
|
||||||
assert.equal(availability.totalCollateral, BigInt(totalCollateral));
|
|
||||||
assert.equal(
|
|
||||||
availability.totalRemainingCollateral,
|
|
||||||
BigInt(totalCollateral)
|
|
||||||
);
|
|
||||||
assert.strictEqual(availability.totalSize, totalSize);
|
|
||||||
assert.strictEqual(availability.until, 0);
|
|
||||||
assert.ok(availability.enabled);
|
|
||||||
});
|
|
||||||
|
|
||||||
const errors: Partial<CodexCreateAvailabilityInput>[] = [
|
|
||||||
{ duration: 0 },
|
|
||||||
{ totalSize: 0 },
|
|
||||||
{ totalCollateral: -1 },
|
|
||||||
{ minPricePerBytePerSecond: -1 },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const err of errors) {
|
|
||||||
const field = Object.keys(err)[0] as keyof typeof err;
|
|
||||||
assert.ok(field);
|
|
||||||
|
|
||||||
it(`fails to create availability with wrong ${field} = ${err[field]}`, async () => {
|
|
||||||
const response = await spMarketplace.createAvailability({
|
|
||||||
...body,
|
|
||||||
[field]: err[field],
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.ok(response.error);
|
|
||||||
assert.ok(response.data.errors?.length);
|
|
||||||
assert.equal(response.data.errors[0]?.path, field);
|
|
||||||
assert.equal(
|
|
||||||
response.data.errors[0]?.received,
|
|
||||||
err[field]?.toString()
|
|
||||||
);
|
|
||||||
assert.ok(response.data.errors[0]?.message.startsWith("Invalid"));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("update", async () => {
|
|
||||||
async function getUpdatedAvailability() {
|
|
||||||
const availabilities = await spMarketplace.availabilities();
|
|
||||||
assert.ok(availabilities.error == false);
|
|
||||||
return availabilities.data.find((a) => a.id == availability.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
const updates: Omit<CodexAvailabilityPatchInput, "id">[] = [
|
|
||||||
{ enabled: false },
|
|
||||||
{ duration: 3000 },
|
|
||||||
{ minPricePerBytePerSecond: BigInt(1) },
|
|
||||||
{ totalSize: 3000 },
|
|
||||||
{ totalCollateral: BigInt(3000) },
|
|
||||||
{ until: 5000 },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const usecase of updates) {
|
|
||||||
const field = Object.keys(usecase)[0] as keyof typeof usecase;
|
|
||||||
assert.ok(field);
|
|
||||||
|
|
||||||
it(`updates availability's ${field}`, async () => {
|
|
||||||
const response = await spMarketplace.updateAvailability({
|
|
||||||
id: availability.id,
|
|
||||||
...usecase,
|
|
||||||
});
|
|
||||||
assert.ok(response.error == false);
|
|
||||||
|
|
||||||
const updated = await getUpdatedAvailability();
|
|
||||||
assert.ok(updated?.[field] == usecase[field]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const errors: Omit<CodexAvailabilityPatchInput, "id">[] = [
|
|
||||||
{ duration: 0 },
|
|
||||||
{ totalSize: 0 },
|
|
||||||
{ totalCollateral: -1 },
|
|
||||||
{ minPricePerBytePerSecond: -1 },
|
|
||||||
{ until: -1 },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const err of errors) {
|
|
||||||
const field = Object.keys(err)[0] as keyof typeof err;
|
|
||||||
assert.ok(field);
|
|
||||||
|
|
||||||
it(`fails to update availability with wrong ${field}`, async () => {
|
|
||||||
const response = await spMarketplace.updateAvailability({
|
|
||||||
id: availability.id,
|
|
||||||
...err,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.ok(response.error);
|
|
||||||
assert.ok(response.data.errors?.length);
|
|
||||||
assert.equal(response.data.errors[0]?.path, field);
|
|
||||||
assert.equal(
|
|
||||||
response.data.errors[0]?.received,
|
|
||||||
err[field]?.toString()
|
|
||||||
);
|
|
||||||
assert.ok(response.data.errors[0]?.message.startsWith("Invalid"));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = new CodexData(
|
it("returns an error when trying to create an availability with zero total size", async () => {
|
||||||
process.env["CLIENT_URL"] || "http://localhost:8080"
|
const response = await marketplace.createAvailability({
|
||||||
);
|
duration: 3000,
|
||||||
const marketplace = new CodexMarketplace(
|
totalCollateral: 1,
|
||||||
process.env["CLIENT_URL"] || "http://localhost:8080"
|
minPricePerBytePerSecond: 100,
|
||||||
);
|
totalSize: 0,
|
||||||
|
|
||||||
async function uploadContent(sizeInBytes: number) {
|
|
||||||
const content = "a".repeat(sizeInBytes);
|
|
||||||
const strategy = new NodeUploadStrategy(content);
|
|
||||||
const res = data.upload(strategy);
|
|
||||||
const cid = await res.result;
|
|
||||||
assert.ok(cid.error == false);
|
|
||||||
assert.ok(cid.data);
|
|
||||||
return cid.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createStorageRequestBody(targetSizeInBytes = 131072) {
|
|
||||||
return {
|
|
||||||
cid: await uploadContent(targetSizeInBytes),
|
|
||||||
duration: 1000,
|
|
||||||
pricePerBytePerSecond: 1,
|
|
||||||
proofProbability: 1,
|
|
||||||
expiry: 900,
|
|
||||||
collateralPerByte: 1,
|
|
||||||
nodes: 3,
|
|
||||||
tolerance: 1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("storage request", async () => {
|
|
||||||
const body = await createStorageRequestBody();
|
|
||||||
|
|
||||||
it("creates successfully", async () => {
|
|
||||||
const request = await marketplace.createStorageRequest(body);
|
|
||||||
assert.ok(request.error == false);
|
|
||||||
assert.ok(request.data);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const errors: {
|
assert.deepStrictEqual(response, minNumberValidationError("totalSize", 1));
|
||||||
request: Partial<CodexCreateStorageRequestInput>;
|
|
||||||
message: string;
|
|
||||||
}[] = [
|
|
||||||
{ request: { cid: "" }, message: "Incorrect Cid" },
|
|
||||||
{
|
|
||||||
request: { duration: 0 },
|
|
||||||
message: "Cannot validate the input",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: { pricePerBytePerSecond: 0 },
|
|
||||||
message: "Cannot validate the input",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: { proofProbability: 0 },
|
|
||||||
message: "Cannot validate the input",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: { expiry: 0 },
|
|
||||||
message: "Cannot validate the input",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: { collateralPerByte: 0 },
|
|
||||||
message: "Cannot validate the input",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: { tolerance: 0 },
|
|
||||||
message: "Cannot validate the input",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: { cid: await uploadContent(1) },
|
|
||||||
message:
|
|
||||||
"Dataset too small for erasure parameters, need at least 131072 bytes",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: { duration: 3000, expiry: 4000 },
|
|
||||||
message:
|
|
||||||
"Expiry must be greater than zero and less than the request's duration",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: { nodes: 2, tolerance: 1 },
|
|
||||||
message:
|
|
||||||
"Invalid parameters: parameters must satify `1 < (nodes - tolerance) ≥ tolerance`",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const err of errors) {
|
|
||||||
it(`fails to create storage request with wrong ${JSON.stringify(err.request)}`, async () => {
|
|
||||||
const request = await marketplace.createStorageRequest({
|
|
||||||
...body,
|
|
||||||
...err.request,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.ok(request.error);
|
|
||||||
assert.ok(request.data.message.includes(err.message));
|
|
||||||
|
|
||||||
if (request.data.errors?.length) {
|
|
||||||
const keys = Object.keys(err.request);
|
|
||||||
for (const e of request.data.errors) {
|
|
||||||
assert.ok(e.path);
|
|
||||||
assert.ok(keys.includes(e.path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("purchases", async () => {
|
it("returns an error when trying to create an availability without duration", async () => {
|
||||||
const body = await createStorageRequestBody();
|
const response = await marketplace.createAvailability({
|
||||||
|
totalSize: 3000,
|
||||||
|
maxCollateral: 1,
|
||||||
|
minPrice: 100,
|
||||||
|
} as any);
|
||||||
|
|
||||||
const request = await marketplace.createStorageRequest(body);
|
assert.deepStrictEqual(response, missingNumberValidationError("duration"));
|
||||||
assert.ok(request.error == false);
|
});
|
||||||
assert.ok(request.data);
|
|
||||||
|
|
||||||
it("lists successfully", async () => {
|
it("returns an error when trying to create an availability with zero duration", async () => {
|
||||||
const ids = await marketplace.purchaseIds();
|
const response = await marketplace.createAvailability({
|
||||||
|
duration: 0,
|
||||||
assert.ok(ids.error == false);
|
totalCollateral: 1,
|
||||||
assert.ok(ids.data.length);
|
minPricePerBytePerSecond: 100,
|
||||||
assert.ok(ids.data[0]);
|
totalSize: 3000,
|
||||||
|
|
||||||
const purchase = await marketplace.purchaseDetail(ids.data[0]);
|
|
||||||
assert.ok(purchase.error == false);
|
|
||||||
assert.ok(purchase.data.requestId);
|
|
||||||
assert.ok(purchase.data.state);
|
|
||||||
|
|
||||||
const purchases = await marketplace.purchases();
|
|
||||||
assert.ok(purchases.error == false);
|
|
||||||
assert.ok(purchases.data.length);
|
|
||||||
assert.ok(purchases.data[0]?.requestId);
|
|
||||||
assert.ok(purchases.data[0]?.state);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, minNumberValidationError("duration", 1));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create an availability without min price", async () => {
|
||||||
|
const response = await marketplace.createAvailability({
|
||||||
|
totalSize: 3000,
|
||||||
|
maxCollateral: 1,
|
||||||
|
duration: 100,
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, missingNumberValidationError("minPrice"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create an availability without max collateral", async () => {
|
||||||
|
const response = await marketplace.createAvailability({
|
||||||
|
totalSize: 3000,
|
||||||
|
minPrice: 100,
|
||||||
|
duration: 100,
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
response,
|
||||||
|
missingNumberValidationError("maxCollateral")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create an availability with an extra field", async () => {
|
||||||
|
const response = await marketplace.createAvailability({
|
||||||
|
maxCollateral: 1,
|
||||||
|
totalSize: 3000,
|
||||||
|
minPrice: 100,
|
||||||
|
duration: 100,
|
||||||
|
hello: "world",
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, extraValidationError("hello", "world"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns a response when the request succeed", async () => {
|
||||||
|
const data = { ...createAvailability(), freeSize: "1000" };
|
||||||
|
|
||||||
|
const spy = vi.spyOn(Fetch, "safeJson");
|
||||||
|
spy.mockImplementationOnce(() => Promise.resolve({ error: false, data }));
|
||||||
|
|
||||||
|
const response = await marketplace.createAvailability({
|
||||||
|
totalCollateral: 1,
|
||||||
|
totalSize: 3000,
|
||||||
|
minPricePerBytePerSecond: 100,
|
||||||
|
duration: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(!response.error);
|
||||||
|
// @ts-ignore
|
||||||
|
assert.deepEqual(response.data, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns a response when the create availability succeed", async () => {
|
||||||
|
const data = { ...createAvailability(), freeSize: "1000" };
|
||||||
|
|
||||||
|
const spy = vi.spyOn(Fetch, "safeJson");
|
||||||
|
spy.mockImplementationOnce(() => Promise.resolve({ error: false, data }));
|
||||||
|
|
||||||
|
const response = await marketplace.createAvailability({
|
||||||
|
totalCollateral: 1,
|
||||||
|
totalSize: 3000,
|
||||||
|
minPricePerBytePerSecond: 100,
|
||||||
|
duration: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(!response.error);
|
||||||
|
// @ts-ignore
|
||||||
|
assert.deepEqual(response.data, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to update an availability without id", async () => {
|
||||||
|
const response = await marketplace.updateAvailability({
|
||||||
|
totalCollateral: 1,
|
||||||
|
totalSize: 3000,
|
||||||
|
minPricePerBytePerSecond: 100,
|
||||||
|
duration: 100,
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, missingStringValidationError("id"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to update an availability with zero total size", async () => {
|
||||||
|
const response = await marketplace.updateAvailability({
|
||||||
|
id: randomString(64),
|
||||||
|
totalSize: 0,
|
||||||
|
minPricePerBytePerSecond: 100,
|
||||||
|
duration: 100,
|
||||||
|
totalCollateral: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, minNumberValidationError("totalSize", 1));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to update an availability with zero duration", async () => {
|
||||||
|
const response = await marketplace.updateAvailability({
|
||||||
|
id: randomString(64),
|
||||||
|
totalSize: 100,
|
||||||
|
duration: 0,
|
||||||
|
minPricePerBytePerSecond: 100,
|
||||||
|
totalCollateral: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, minNumberValidationError("duration", 1));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns a response when the update availability succeed", async () => {
|
||||||
|
const mockResponse = {
|
||||||
|
ok: true,
|
||||||
|
status: 200,
|
||||||
|
} as any;
|
||||||
|
globalThis.fetch = vi.fn().mockResolvedValue(mockResponse);
|
||||||
|
|
||||||
|
const response = await marketplace.updateAvailability({
|
||||||
|
id: randomString(64),
|
||||||
|
totalSize: 3000,
|
||||||
|
duration: 10,
|
||||||
|
minPricePerBytePerSecond: 100,
|
||||||
|
totalCollateral: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(!response.error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create a storage request without cid", async () => {
|
||||||
|
const { cid, ...rest } = createStorageRequest();
|
||||||
|
|
||||||
|
const response = await marketplace.createStorageRequest(rest as any);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, missingStringValidationError("cid"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create a storage request without duration", async () => {
|
||||||
|
const { duration, ...rest } = createStorageRequest();
|
||||||
|
|
||||||
|
const response = await marketplace.createStorageRequest(rest as any);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, missingNumberValidationError("duration"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create a storage request with zero duration", async () => {
|
||||||
|
const { duration, ...rest } = createStorageRequest();
|
||||||
|
|
||||||
|
const response = await marketplace.createStorageRequest({
|
||||||
|
...rest,
|
||||||
|
duration: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, minNumberValidationError("duration", 1));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create a storage request without pricePerBytePerSecond", async () => {
|
||||||
|
const { pricePerBytePerSecond, ...rest } = createStorageRequest();
|
||||||
|
|
||||||
|
const response = await marketplace.createStorageRequest(rest as any);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
response,
|
||||||
|
missingNumberValidationError("pricePerBytePerSecond")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create a storage request without proof probability", async () => {
|
||||||
|
const { proofProbability, ...rest } = createStorageRequest();
|
||||||
|
|
||||||
|
const response = await marketplace.createStorageRequest(rest as any);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
response,
|
||||||
|
missingNumberValidationError("proofProbability")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create a storage request without expiry", async () => {
|
||||||
|
const { expiry, ...rest } = createStorageRequest();
|
||||||
|
|
||||||
|
const response = await marketplace.createStorageRequest(rest as any);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, missingNumberValidationError("expiry"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create a storage request with zero expiry", async () => {
|
||||||
|
const { expiry, ...rest } = createStorageRequest();
|
||||||
|
|
||||||
|
const response = await marketplace.createStorageRequest({
|
||||||
|
...rest,
|
||||||
|
expiry: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(response, minNumberValidationError("expiry", 1));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when trying to create a storage request without collateralPerByte", async () => {
|
||||||
|
const { collateralPerByte, ...rest } = createStorageRequest();
|
||||||
|
|
||||||
|
const response = await marketplace.createStorageRequest(rest as any);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
response,
|
||||||
|
missingNumberValidationError("collateralPerByte")
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,51 +1,26 @@
|
|||||||
import * as v from "valibot";
|
import * as v from "valibot";
|
||||||
import { Api } from "../api/config";
|
import { Api } from "../api/config";
|
||||||
import { CodexError, CodexValibotIssuesMap } from "../errors/errors";
|
import { CodexError, CodexValibotIssuesMap } from "../errors/errors";
|
||||||
import {
|
import { Fetch } from "../fetch-safe/fetch-safe";
|
||||||
Fetch,
|
|
||||||
FetchAuthBuilder,
|
|
||||||
type FetchAuth,
|
|
||||||
} from "../fetch-safe/fetch-safe";
|
|
||||||
import type { SafeValue } from "../values/values";
|
import type { SafeValue } from "../values/values";
|
||||||
import {
|
import {
|
||||||
type CodexAvailabilityResponse,
|
|
||||||
type CodexAvailability,
|
type CodexAvailability,
|
||||||
type CodexSlot,
|
|
||||||
type CodexSlotAgent,
|
|
||||||
type CodexSlotResponse,
|
|
||||||
type CodexSlotAgentResponse,
|
|
||||||
type CodexAvailabilityWithoutTypes,
|
|
||||||
type CodexAvailabilityCreateResponse,
|
type CodexAvailabilityCreateResponse,
|
||||||
type CodexAvailabilityCreateBody,
|
type CodexAvailabilityDto,
|
||||||
CodexAvailabilityPatchInput,
|
|
||||||
type CodexReservationsResponse,
|
|
||||||
type CodexPurchaseIdsResponse,
|
|
||||||
type CodexPurchaseResponse,
|
|
||||||
type CodexPurchase,
|
|
||||||
type CodexStorageRequestCreateBody,
|
|
||||||
type CodexReservation,
|
|
||||||
type CodexPurchaseWithoutTypes,
|
|
||||||
type CodexAvailabilityPatchBody,
|
|
||||||
} from "./types";
|
|
||||||
import {
|
|
||||||
CodexCreateAvailabilityInput,
|
CodexCreateAvailabilityInput,
|
||||||
CodexCreateStorageRequestInput,
|
CodexCreateStorageRequestInput,
|
||||||
|
type CodexPurchase,
|
||||||
|
type CodexReservation,
|
||||||
|
type CodexSlot,
|
||||||
|
type CodexStorageRequest,
|
||||||
|
CodexUpdateAvailabilityInput,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
type CodexMarketplaceOptions = {
|
|
||||||
auth?: FetchAuth;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class CodexMarketplace {
|
export class CodexMarketplace {
|
||||||
readonly url: string;
|
readonly url: string;
|
||||||
readonly auth: FetchAuth = {};
|
|
||||||
|
|
||||||
constructor(url: string, options?: CodexMarketplaceOptions) {
|
constructor(url: string) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
|
|
||||||
if (options?.auth) {
|
|
||||||
this.auth = options.auth;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,51 +29,30 @@ export class CodexMarketplace {
|
|||||||
async activeSlots(): Promise<SafeValue<CodexSlot[]>> {
|
async activeSlots(): Promise<SafeValue<CodexSlot[]>> {
|
||||||
const url = this.url + Api.config.prefix + "/sales/slots";
|
const url = this.url + Api.config.prefix + "/sales/slots";
|
||||||
|
|
||||||
return Fetch.safeJson<CodexSlotResponse[]>(url, {
|
return Fetch.safeJson<CodexSlot[]>(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns active slot with id {slotId} for the host
|
* Returns active slot with id {slotId} for the host
|
||||||
*/
|
*/
|
||||||
async activeSlot(slotId: string): Promise<SafeValue<CodexSlotAgent>> {
|
async activeSlot(slotId: string): Promise<SafeValue<CodexSlot>> {
|
||||||
const url = this.url + Api.config.prefix + "/sales/slots/" + slotId;
|
const url = this.url + Api.config.prefix + "/sales/slots/" + slotId;
|
||||||
|
|
||||||
return Fetch.safeJson<CodexSlotAgentResponse>(url, {
|
return Fetch.safeJson<CodexSlot>(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private transformAvailability({
|
|
||||||
freeSize,
|
|
||||||
...a
|
|
||||||
}: CodexAvailabilityWithoutTypes) {
|
|
||||||
const availability: CodexAvailability = {
|
|
||||||
...a,
|
|
||||||
minPricePerBytePerSecond: BigInt(a.minPricePerBytePerSecond),
|
|
||||||
totalCollateral: BigInt(a.totalCollateral),
|
|
||||||
totalRemainingCollateral: BigInt(a.totalRemainingCollateral),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (freeSize) {
|
|
||||||
availability.freeSize = freeSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
return availability;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns storage that is for sale
|
* Returns storage that is for sale
|
||||||
*/
|
*/
|
||||||
async availabilities(): Promise<SafeValue<CodexAvailability[]>> {
|
async availabilities(): Promise<SafeValue<CodexAvailability[]>> {
|
||||||
const url = this.url + Api.config.prefix + "/sales/availability";
|
const url = this.url + Api.config.prefix + "/sales/availability";
|
||||||
|
|
||||||
const res = await Fetch.safeJson<CodexAvailabilityResponse>(url, {
|
const res = await Fetch.safeJson<CodexAvailabilityDto[]>(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
@ -107,7 +61,15 @@ export class CodexMarketplace {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
error: false,
|
error: false,
|
||||||
data: res.data.map(this.transformAvailability),
|
data: res.data.map((a) => ({
|
||||||
|
id: a.id,
|
||||||
|
totalSize: parseInt(a.totalSize, 10),
|
||||||
|
freeSize: parseInt(a.freeSize, 10),
|
||||||
|
duration: parseInt(a.duration, 10),
|
||||||
|
minPricePerBytePerSecond: parseInt(a.minPricePerBytePerSecond, 10),
|
||||||
|
totalCollateral: parseInt(a.totalCollateral, 10),
|
||||||
|
totalRemainingCollateral: parseInt(a.totalRemainingCollateral, 10),
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +78,7 @@ export class CodexMarketplace {
|
|||||||
*/
|
*/
|
||||||
async createAvailability(
|
async createAvailability(
|
||||||
input: CodexCreateAvailabilityInput
|
input: CodexCreateAvailabilityInput
|
||||||
): Promise<SafeValue<CodexAvailability>> {
|
): Promise<SafeValue<CodexAvailabilityCreateResponse>> {
|
||||||
const result = v.safeParse(CodexCreateAvailabilityInput, input);
|
const result = v.safeParse(CodexCreateAvailabilityInput, input);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
@ -130,32 +92,16 @@ export class CodexMarketplace {
|
|||||||
|
|
||||||
const url = this.url + Api.config.prefix + "/sales/availability";
|
const url = this.url + Api.config.prefix + "/sales/availability";
|
||||||
|
|
||||||
const body: CodexAvailabilityCreateBody = {
|
const body = result.output;
|
||||||
totalSize: result.output.totalSize,
|
|
||||||
duration: result.output.duration,
|
|
||||||
minPricePerBytePerSecond:
|
|
||||||
result.output.minPricePerBytePerSecond.toString(),
|
|
||||||
totalCollateral: result.output.totalCollateral.toString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (result.output.enabled) {
|
|
||||||
body.enabled = result.output.enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.output.until) {
|
|
||||||
body.until = result.output.until;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Fetch.safeJson<CodexAvailabilityCreateResponse>(url, {
|
return Fetch.safeJson<CodexAvailabilityCreateResponse>(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
body: JSON.stringify({
|
||||||
body: JSON.stringify(body),
|
totalSize: body.totalSize.toString(),
|
||||||
}).then((result) => {
|
duration: body.duration.toString(),
|
||||||
if (result.error) {
|
minPricePerBytePerSecond: body.minPricePerBytePerSecond.toString(),
|
||||||
return result;
|
totalCollateral: body.totalCollateral.toString(),
|
||||||
}
|
}),
|
||||||
|
|
||||||
return { error: false, data: this.transformAvailability(result.data) };
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,9 +110,9 @@ export class CodexMarketplace {
|
|||||||
* Existing Requests linked to this Availability will continue as is.
|
* Existing Requests linked to this Availability will continue as is.
|
||||||
*/
|
*/
|
||||||
async updateAvailability(
|
async updateAvailability(
|
||||||
input: CodexAvailabilityPatchInput
|
input: CodexUpdateAvailabilityInput
|
||||||
): Promise<SafeValue<"">> {
|
): Promise<SafeValue<"">> {
|
||||||
const result = v.safeParse(CodexAvailabilityPatchInput, input);
|
const result = v.safeParse(CodexUpdateAvailabilityInput, input);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
return {
|
return {
|
||||||
@ -180,38 +126,16 @@ export class CodexMarketplace {
|
|||||||
const url =
|
const url =
|
||||||
this.url + Api.config.prefix + "/sales/availability/" + result.output.id;
|
this.url + Api.config.prefix + "/sales/availability/" + result.output.id;
|
||||||
|
|
||||||
const { totalSize, duration, minPricePerBytePerSecond, totalCollateral } =
|
const body = result.output;
|
||||||
result.output;
|
|
||||||
let body: CodexAvailabilityPatchBody = {};
|
|
||||||
|
|
||||||
if (totalSize) {
|
|
||||||
body.totalSize = totalSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (duration) {
|
|
||||||
body.duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minPricePerBytePerSecond) {
|
|
||||||
body.minPricePerBytePerSecond = minPricePerBytePerSecond.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalCollateral) {
|
|
||||||
body.totalCollateral = totalCollateral.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.output.enabled != undefined) {
|
|
||||||
body.enabled = result.output.enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.output.until) {
|
|
||||||
body.until = result.output.until;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await Fetch.safe(url, {
|
const res = await Fetch.safe(url, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
body: JSON.stringify({
|
||||||
body: JSON.stringify(body),
|
totalSize: body.totalSize.toString(),
|
||||||
|
duration: body.duration.toString(),
|
||||||
|
minPricePerBytePerSecond: body.minPricePerBytePerSecond.toString(),
|
||||||
|
totalCollateral: body.totalCollateral.toString(),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
@ -232,56 +156,28 @@ export class CodexMarketplace {
|
|||||||
Api.config.prefix +
|
Api.config.prefix +
|
||||||
`/sales/availability/${availabilityId}/reservations`;
|
`/sales/availability/${availabilityId}/reservations`;
|
||||||
|
|
||||||
return Fetch.safeJson<CodexReservationsResponse>(url, {
|
return Fetch.safeJson<CodexReservation[]>(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns list of purchase IDs
|
* Returns list of purchase IDs
|
||||||
*/
|
*/
|
||||||
async purchaseIds(): Promise<SafeValue<CodexPurchaseIdsResponse>> {
|
async purchaseIds(): Promise<SafeValue<string[]>> {
|
||||||
const url = this.url + Api.config.prefix + `/storage/purchases`;
|
const url = this.url + Api.config.prefix + `/storage/purchases`;
|
||||||
|
|
||||||
return Fetch.safeJson<CodexPurchaseIdsResponse>(url, {
|
return Fetch.safeJson<string[]>(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private transformPurchase(p: CodexPurchaseWithoutTypes): CodexPurchase {
|
|
||||||
const purchase: CodexPurchase = {
|
|
||||||
requestId: p.requestId,
|
|
||||||
state: p.state,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (p.error) {
|
|
||||||
purchase.error = p.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p.request) {
|
|
||||||
return purchase;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...purchase,
|
|
||||||
request: {
|
|
||||||
...p.request,
|
|
||||||
ask: {
|
|
||||||
...p.request.ask,
|
|
||||||
proofProbability: parseInt(p.request.ask.proofProbability, 10),
|
|
||||||
pricePerBytePerSecond: parseInt(
|
|
||||||
p.request.ask.pricePerBytePerSecond,
|
|
||||||
10
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async purchases(): Promise<SafeValue<CodexPurchase[]>> {
|
async purchases(): Promise<SafeValue<CodexPurchase[]>> {
|
||||||
const res = await this.purchaseIds();
|
const url = this.url + Api.config.prefix + `/storage/purchases`;
|
||||||
|
|
||||||
|
const res = await Fetch.safeJson<string[]>(url, {
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
return res;
|
return res;
|
||||||
@ -300,9 +196,10 @@ export class CodexMarketplace {
|
|||||||
data: purchases.map((p) =>
|
data: purchases.map((p) =>
|
||||||
p.error
|
p.error
|
||||||
? ({
|
? ({
|
||||||
state: "errored",
|
state: "error",
|
||||||
error: p.data.message,
|
error: p.data.message,
|
||||||
requestId: "",
|
requestId: "",
|
||||||
|
request: {} as CodexStorageRequest,
|
||||||
} satisfies CodexPurchase)
|
} satisfies CodexPurchase)
|
||||||
: p.data
|
: p.data
|
||||||
),
|
),
|
||||||
@ -316,15 +213,8 @@ export class CodexMarketplace {
|
|||||||
const url =
|
const url =
|
||||||
this.url + Api.config.prefix + `/storage/purchases/` + purchaseId;
|
this.url + Api.config.prefix + `/storage/purchases/` + purchaseId;
|
||||||
|
|
||||||
return Fetch.safeJson<CodexPurchaseResponse>(url, {
|
return Fetch.safeJson<CodexPurchase>(url, {
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
}).then((res) => {
|
|
||||||
if (res.error) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { error: false, data: this.transformPurchase(res.data) };
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,18 +247,23 @@ export class CodexMarketplace {
|
|||||||
} = result.output;
|
} = result.output;
|
||||||
const url = this.url + Api.config.prefix + "/storage/request/" + cid;
|
const url = this.url + Api.config.prefix + "/storage/request/" + cid;
|
||||||
|
|
||||||
return Fetch.safeText(url, {
|
const res = await Fetch.safe(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
duration,
|
duration: duration.toString(),
|
||||||
pricePerBytePerSecond: pricePerBytePerSecond.toString(),
|
pricePerBytePerSecond: pricePerBytePerSecond.toString(),
|
||||||
proofProbability: proofProbability.toString(),
|
proofProbability: proofProbability.toString(),
|
||||||
nodes,
|
nodes,
|
||||||
collateralPerByte: collateralPerByte.toString(),
|
collateral: collateralPerByte.toString(),
|
||||||
expiry,
|
expiry: expiry.toString(),
|
||||||
tolerance,
|
tolerance,
|
||||||
} satisfies CodexStorageRequestCreateBody),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (res.error) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { error: false, data: await res.data.text() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,169 +1,241 @@
|
|||||||
import type { components, paths } from "../openapi";
|
|
||||||
import * as v from "valibot";
|
import * as v from "valibot";
|
||||||
|
|
||||||
export type CodexSlotResponse =
|
export type CodexStorageRequest = {
|
||||||
paths["/sales/slots"]["get"]["responses"][200]["content"]["application/json"];
|
id: string;
|
||||||
|
|
||||||
export type CodexSlot = CodexSlotResponse;
|
/**
|
||||||
|
* Address of Ethereum address
|
||||||
|
*/
|
||||||
|
client: string;
|
||||||
|
|
||||||
export type CodexSlotAgentResponse =
|
ask: {
|
||||||
paths["/sales/slots/{slotId}"]["get"]["responses"][200]["content"]["application/json"];
|
/**
|
||||||
|
* Number of slots that the tequest want to have the content spread over.
|
||||||
|
*/
|
||||||
|
slots: number;
|
||||||
|
|
||||||
export type CodexSlotAgent = CodexSlotAgentResponse;
|
/**
|
||||||
|
* Amount of storage per slot (in bytes) as decimal string.
|
||||||
|
*/
|
||||||
|
slotSize: string;
|
||||||
|
|
||||||
export type CodexAvailabilityResponse =
|
/**
|
||||||
paths["/sales/availability"]["get"]["responses"][200]["content"]["application/json"];
|
* The duration of the storage request in seconds.
|
||||||
|
*/
|
||||||
|
duration: string;
|
||||||
|
|
||||||
export type CodexAvailabilityWithoutTypes =
|
/**
|
||||||
components["schemas"]["SalesAvailabilityREAD"];
|
* How often storage proofs are required as decimal string (in periods).
|
||||||
|
*/
|
||||||
|
proofProbability: string;
|
||||||
|
|
||||||
export type CodexAvailability = Omit<
|
/**
|
||||||
CodexAvailabilityWithoutTypes,
|
* The amount of tokens paid per byte per second per slot to hosts the client is willing to pay
|
||||||
| "freeSize"
|
*/
|
||||||
| "minPricePerBytePerSecond"
|
pricePerBytePerSecond: string;
|
||||||
| "totalCollateral"
|
|
||||||
| "totalRemainingCollateral"
|
/**
|
||||||
> & {
|
* Max slots that can be lost without data considered to be lost.
|
||||||
freeSize?: number;
|
*/
|
||||||
minPricePerBytePerSecond: BigInt;
|
maxSlotLoss: number;
|
||||||
totalCollateral: BigInt;
|
};
|
||||||
totalRemainingCollateral: BigInt;
|
|
||||||
|
content: {
|
||||||
|
/**
|
||||||
|
* Unique Content ID
|
||||||
|
*/
|
||||||
|
cid: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erasure code parameters
|
||||||
|
*/
|
||||||
|
// erasure: {
|
||||||
|
/**
|
||||||
|
* Total number of chunks generated by the erasure code process.
|
||||||
|
*/
|
||||||
|
// totalChunks: number;
|
||||||
|
// };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters for Proof of Retrievability
|
||||||
|
*/
|
||||||
|
// por: {
|
||||||
|
// u: string;
|
||||||
|
// publicKey: string;
|
||||||
|
// name: string;
|
||||||
|
// };
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Number as decimal string that represents expiry threshold in seconds from when the Request is submitted.
|
||||||
|
* When the threshold is reached and the Request does not find requested amount of nodes to host the data, the Request is voided.
|
||||||
|
* The number of seconds can not be higher then the Request's duration itself.
|
||||||
|
*/
|
||||||
|
expiry: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Random data
|
||||||
|
*/
|
||||||
|
nonce: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CodexAvailabilityCreateResponse =
|
/**
|
||||||
paths["/sales/availability"]["post"]["responses"][201]["content"]["application/json"];
|
* A storage slot is a portion of a storage contract that needs to be fulfilled
|
||||||
|
* by a host. To initiate a contract, all the slots must be filled.
|
||||||
|
*/
|
||||||
|
export type CodexSlot = {
|
||||||
|
id: string;
|
||||||
|
|
||||||
export type CodexAvailabilityCreateBody = Exclude<
|
request: CodexStorageRequest;
|
||||||
paths["/sales/availability"]["post"]["requestBody"],
|
|
||||||
undefined
|
/**
|
||||||
>["content"]["application/json"];
|
* The slot index as hexadecimal string
|
||||||
|
*/
|
||||||
|
slotIndex: "string";
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage availability for sell.
|
||||||
|
*/
|
||||||
|
export type CodexAvailability = {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size of available storage in bytes
|
||||||
|
*/
|
||||||
|
totalSize: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unused size of availability's storage in bytes as decimal string
|
||||||
|
*/
|
||||||
|
freeSize: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum time the storage should be sold for (in seconds)
|
||||||
|
*/
|
||||||
|
duration: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal price per byte per second paid (in amount of tokens) for the
|
||||||
|
* hosted request's slot for the request's duration as decimal string
|
||||||
|
*/
|
||||||
|
minPricePerBytePerSecond: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total collateral (in amount of tokens) that can be used for matching requests
|
||||||
|
*/
|
||||||
|
totalCollateral: number;
|
||||||
|
|
||||||
|
totalRemainingCollateral: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage availability received from the api.
|
||||||
|
*/
|
||||||
|
export type CodexAvailabilityDto = {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size of available storage in bytes
|
||||||
|
*/
|
||||||
|
totalSize: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unused size of availability's storage in bytes as decimal string
|
||||||
|
*/
|
||||||
|
freeSize: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum time the storage should be sold for (in seconds)
|
||||||
|
*/
|
||||||
|
duration: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal price per byte per second paid (in amount of tokens) for the
|
||||||
|
* hosted request's slot for the request's duration as decimal string
|
||||||
|
*/
|
||||||
|
minPricePerBytePerSecond: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total collateral (in amount of tokens) that can be used for matching requests
|
||||||
|
*/
|
||||||
|
totalCollateral: string;
|
||||||
|
|
||||||
|
totalRemainingCollateral: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CodexAvailabilityCreateResponse = CodexAvailability & {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unused size of availability's storage in bytes as decimal string
|
||||||
|
*/
|
||||||
|
freeSize: string;
|
||||||
|
};
|
||||||
|
|
||||||
export const CodexCreateAvailabilityInput = v.strictObject({
|
export const CodexCreateAvailabilityInput = v.strictObject({
|
||||||
totalSize: v.pipe(v.number(), v.minValue(1)),
|
totalSize: v.pipe(v.number(), v.minValue(1)),
|
||||||
duration: v.pipe(v.number(), v.minValue(1)),
|
duration: v.pipe(v.number(), v.minValue(1)),
|
||||||
minPricePerBytePerSecond: v.union([
|
minPricePerBytePerSecond: v.number(),
|
||||||
v.pipe(v.bigint(), v.minValue(BigInt(0))),
|
totalCollateral: v.number(),
|
||||||
v.pipe(
|
|
||||||
v.number(),
|
|
||||||
v.minValue(0),
|
|
||||||
v.transform((input) => BigInt(input))
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
totalCollateral: v.union([
|
|
||||||
v.pipe(v.bigint(), v.minValue(BigInt(0))),
|
|
||||||
v.pipe(
|
|
||||||
v.number(),
|
|
||||||
v.minValue(0),
|
|
||||||
v.transform((input) => BigInt(input))
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
enabled: v.optional(v.boolean()),
|
|
||||||
until: v.optional(v.pipe(v.number(), v.minValue(0))),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type CodexAvailabilityPatchResponse =
|
export type CodexCreateAvailabilityInput = v.InferOutput<
|
||||||
paths["/sales/availability/{id}"]["patch"]["responses"][204]["content"];
|
typeof CodexCreateAvailabilityInput
|
||||||
|
|
||||||
export type CodexAvailabilityPatchBody = Partial<
|
|
||||||
Exclude<
|
|
||||||
paths["/sales/availability"]["post"]["requestBody"],
|
|
||||||
undefined
|
|
||||||
>["content"]["application/json"]
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type CodexCreateAvailabilityInput = Omit<
|
export const CodexUpdateAvailabilityInput = v.strictObject({
|
||||||
v.InferOutput<typeof CodexCreateAvailabilityInput>,
|
|
||||||
"minPricePerBytePerSecond" | "totalCollateral"
|
|
||||||
> & {
|
|
||||||
minPricePerBytePerSecond?: number | BigInt;
|
|
||||||
totalCollateral?: number | BigInt;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CodexAvailabilityPatchInput = v.strictObject({
|
|
||||||
id: v.string(),
|
id: v.string(),
|
||||||
totalSize: v.optional(v.pipe(v.number(), v.minValue(1))),
|
totalSize: v.pipe(v.number(), v.minValue(1)),
|
||||||
duration: v.optional(v.pipe(v.number(), v.minValue(1))),
|
duration: v.pipe(v.number(), v.minValue(1)),
|
||||||
minPricePerBytePerSecond: v.optional(
|
minPricePerBytePerSecond: v.number(),
|
||||||
v.union([
|
totalCollateral: v.number(),
|
||||||
v.pipe(v.bigint(), v.minValue(BigInt(0))),
|
|
||||||
v.pipe(
|
|
||||||
v.number(),
|
|
||||||
v.minValue(0),
|
|
||||||
v.transform((input) => BigInt(input))
|
|
||||||
),
|
|
||||||
])
|
|
||||||
),
|
|
||||||
totalCollateral: v.optional(
|
|
||||||
v.union([
|
|
||||||
v.pipe(v.bigint(), v.minValue(BigInt(0))),
|
|
||||||
v.pipe(
|
|
||||||
v.number(),
|
|
||||||
v.minValue(0),
|
|
||||||
v.transform((input) => BigInt(input))
|
|
||||||
),
|
|
||||||
])
|
|
||||||
),
|
|
||||||
enabled: v.optional(v.boolean()),
|
|
||||||
until: v.optional(v.pipe(v.number(), v.minValue(0))),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type CodexAvailabilityPatchInput = Omit<
|
export type CodexUpdateAvailabilityInput = v.InferOutput<
|
||||||
v.InferOutput<typeof CodexAvailabilityPatchInput>,
|
typeof CodexUpdateAvailabilityInput
|
||||||
"minPricePerBytePerSecond" | "totalCollateral"
|
>;
|
||||||
> & {
|
|
||||||
minPricePerBytePerSecond?: number | BigInt;
|
export type CodexReservation = {
|
||||||
totalCollateral?: number | BigInt;
|
id: string;
|
||||||
|
availabilityId: string;
|
||||||
|
requestId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size in bytes
|
||||||
|
*/
|
||||||
|
size: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slot Index as hexadecimal string
|
||||||
|
*/
|
||||||
|
slotIndex: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CodexReservationsResponse =
|
export type CodexPurchase = {
|
||||||
paths["/sales/availability/{id}/reservations"]["get"]["responses"][200]["content"]["application/json"];
|
/**
|
||||||
|
* Description of the request's state
|
||||||
|
*/
|
||||||
|
state: string;
|
||||||
|
|
||||||
export type CodexReservation = components["schemas"]["Reservation"];
|
/**
|
||||||
|
* If request failed, then here is presented the error message
|
||||||
|
*/
|
||||||
|
error: string;
|
||||||
|
|
||||||
export type CodexPurchaseIdsResponse =
|
request: CodexStorageRequest;
|
||||||
paths["/storage/purchases"]["get"]["responses"][200]["content"]["application/json"];
|
|
||||||
|
|
||||||
export type CodexPurchaseResponse =
|
requestId: string;
|
||||||
paths["/storage/purchases/{id}"]["get"]["responses"][200]["content"]["application/json"];
|
|
||||||
|
|
||||||
export type CodexStorageAsk = Omit<
|
|
||||||
components["schemas"]["StorageAsk"],
|
|
||||||
"slotSize" | "duration" | "proofProbability" | "pricePerBytePerSecond"
|
|
||||||
> & {
|
|
||||||
slotSize: number;
|
|
||||||
duration: number;
|
|
||||||
proofProbability: number;
|
|
||||||
pricePerBytePerSecond: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CodexPurchaseWithoutTypes = components["schemas"]["Purchase"];
|
|
||||||
|
|
||||||
export type CodexPurchase = Omit<
|
|
||||||
components["schemas"]["Purchase"],
|
|
||||||
"request"
|
|
||||||
> & {
|
|
||||||
request?: Omit<components["schemas"]["StorageRequest"], "ask"> & {
|
|
||||||
ask: CodexStorageAsk;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CodexStorageRequestResponse =
|
|
||||||
paths["/storage/request/{cid}"]["post"]["responses"][200]["content"]["text/plain"];
|
|
||||||
|
|
||||||
export type CodexStorageRequestCreateBody = Exclude<
|
|
||||||
paths["/storage/request/{cid}"]["post"]["requestBody"],
|
|
||||||
undefined
|
|
||||||
>["content"]["application/json"];
|
|
||||||
|
|
||||||
export const CodexCreateStorageRequestInput = v.strictObject({
|
export const CodexCreateStorageRequestInput = v.strictObject({
|
||||||
cid: v.string(),
|
cid: v.string(),
|
||||||
duration: v.pipe(v.number(), v.minValue(1)),
|
duration: v.pipe(v.number(), v.minValue(1)),
|
||||||
pricePerBytePerSecond: v.pipe(v.number(), v.minValue(1)),
|
pricePerBytePerSecond: v.number(),
|
||||||
proofProbability: v.pipe(v.number(), v.minValue(1)),
|
proofProbability: v.number(),
|
||||||
nodes: v.optional(v.number(), 1),
|
nodes: v.optional(v.number(), 1),
|
||||||
tolerance: v.optional(v.pipe(v.number(), v.minValue(1)), 1),
|
tolerance: v.optional(v.number(), 0),
|
||||||
expiry: v.pipe(v.number(), v.minValue(1)),
|
expiry: v.pipe(v.number(), v.minValue(1)),
|
||||||
collateralPerByte: v.pipe(v.number(), v.minValue(1)),
|
collateralPerByte: v.number(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type CodexCreateStorageRequestInput = v.InferOutput<
|
export type CodexCreateStorageRequestInput = v.InferOutput<
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
export * from "./data/node-upload";
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
import { assert, describe, expect, it, vi } from "vitest";
|
|
||||||
import { CodexNode } from "./node";
|
|
||||||
import { Fetch } from "../fetch-safe/fetch-safe";
|
|
||||||
|
|
||||||
describe("node", () => {
|
|
||||||
const clientUrl = process.env["CLIENT_URL"] || "http://localhost:8080";
|
|
||||||
const node = new CodexNode(clientUrl);
|
|
||||||
|
|
||||||
it("gets the json spr", async () => {
|
|
||||||
const spr = await node.spr("json");
|
|
||||||
assert.ok(spr.error == false);
|
|
||||||
assert.ok(spr.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("gets the text spr", async () => {
|
|
||||||
const spr = await node.spr("text");
|
|
||||||
assert.ok(spr.error == false);
|
|
||||||
assert.ok(spr.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("connects to a peer", async () => {
|
|
||||||
const spy = vi.spyOn(Fetch, "safeText");
|
|
||||||
spy.mockImplementationOnce(() =>
|
|
||||||
Promise.resolve({ error: false, data: "" })
|
|
||||||
);
|
|
||||||
|
|
||||||
await node.connect("1234", ["5678"]);
|
|
||||||
expect(spy).toHaveBeenCalledWith(
|
|
||||||
clientUrl + "/api/codex/v1/connect/1234?addrs=5678",
|
|
||||||
{
|
|
||||||
headers: {},
|
|
||||||
method: "GET",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,39 +1,20 @@
|
|||||||
import { Api } from "../api/config";
|
import { Api } from "../api/config";
|
||||||
import {
|
import { Fetch } from "../fetch-safe/fetch-safe";
|
||||||
Fetch,
|
|
||||||
FetchAuthBuilder,
|
|
||||||
type FetchAuth,
|
|
||||||
} from "../fetch-safe/fetch-safe";
|
|
||||||
import type { SafeValue } from "../values/values";
|
import type { SafeValue } from "../values/values";
|
||||||
import type {
|
import type { CodexSpr } from "./types";
|
||||||
CodexPeerId,
|
|
||||||
CodexPeerIdContentType,
|
|
||||||
CodexPeerIdJsonResponse,
|
|
||||||
CodexSpr,
|
|
||||||
CodexSprContentType,
|
|
||||||
CodexSprJsonResponse,
|
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
type CodexNodeOptions = {
|
|
||||||
auth?: FetchAuth;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class CodexNode {
|
export class CodexNode {
|
||||||
readonly url: string;
|
readonly url: string;
|
||||||
readonly auth: FetchAuth = {};
|
|
||||||
|
|
||||||
constructor(url: string, options?: CodexNodeOptions) {
|
constructor(url: string) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
|
|
||||||
if (options?.auth) {
|
|
||||||
this.auth = options.auth;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to a peer
|
* Connect to a peer
|
||||||
|
* TODO check result
|
||||||
*/
|
*/
|
||||||
connect(peerId: string, addrs: string[] = []): Promise<SafeValue<string>> {
|
connect(peerId: string, addrs: string[] = []) {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
for (const addr of addrs) {
|
for (const addr of addrs) {
|
||||||
@ -43,63 +24,31 @@ export class CodexNode {
|
|||||||
const url =
|
const url =
|
||||||
this.url + Api.config.prefix + `/connect/${peerId}?` + params.toString();
|
this.url + Api.config.prefix + `/connect/${peerId}?` + params.toString();
|
||||||
|
|
||||||
return Fetch.safeText(url, {
|
return Fetch.safe(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: FetchAuthBuilder.build(this.auth),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Node's SPR
|
* Get Node's SPR
|
||||||
*/
|
*/
|
||||||
async spr(
|
async spr(): Promise<SafeValue<CodexSpr>> {
|
||||||
type: CodexSprContentType = "json"
|
|
||||||
): Promise<SafeValue<CodexSpr<CodexSprContentType>>> {
|
|
||||||
const url = this.url + Api.config.prefix + "/spr";
|
const url = this.url + Api.config.prefix + "/spr";
|
||||||
|
|
||||||
if (type == "json") {
|
return Fetch.safeJson(url, {
|
||||||
return Fetch.safeJson<CodexSprJsonResponse>(url, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
...FetchAuthBuilder.build(this.auth),
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Fetch.safeText(url, {
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
|
||||||
...FetchAuthBuilder.build(this.auth),
|
|
||||||
"Content-Type": "text/plain",
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Node's PeerID
|
* Get Node's PeerID
|
||||||
|
* TODO check result
|
||||||
*/
|
*/
|
||||||
peerId(
|
peerId() {
|
||||||
type: CodexPeerIdContentType = "json"
|
|
||||||
): Promise<SafeValue<CodexPeerId<CodexPeerIdContentType>>> {
|
|
||||||
const url = this.url + Api.config.prefix + "/node/peerid";
|
const url = this.url + Api.config.prefix + "/node/peerid";
|
||||||
|
|
||||||
if (type == "json") {
|
return Fetch.safe(url, {
|
||||||
return Fetch.safeJson<CodexPeerIdJsonResponse>(url, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
...FetchAuthBuilder.build(this.auth),
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Fetch.safeText(url, {
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
|
||||||
...FetchAuthBuilder.build(this.auth),
|
|
||||||
"Content-Type": "text/plain",
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,29 +1,3 @@
|
|||||||
import type { paths } from "../openapi";
|
export type CodexSpr = {
|
||||||
|
spr: string;
|
||||||
export type CodexSprTextResponse =
|
};
|
||||||
paths["/spr"]["get"]["responses"][200]["content"]["text/plain"];
|
|
||||||
|
|
||||||
export type CodexSprJsonResponse =
|
|
||||||
paths["/spr"]["get"]["responses"][200]["content"]["application/json"];
|
|
||||||
|
|
||||||
export type CodexSprContentType = "json" | "text";
|
|
||||||
|
|
||||||
export type CodexSpr<T extends CodexSprContentType> = T extends "json"
|
|
||||||
? CodexSprJsonResponse
|
|
||||||
: T extends "text"
|
|
||||||
? CodexSprTextResponse
|
|
||||||
: never;
|
|
||||||
|
|
||||||
export type CodexPeerIdTextResponse =
|
|
||||||
paths["/peerid"]["get"]["responses"][200]["content"]["text/plain"];
|
|
||||||
|
|
||||||
export type CodexPeerIdJsonResponse =
|
|
||||||
paths["/peerid"]["get"]["responses"][200]["content"]["application/json"];
|
|
||||||
|
|
||||||
export type CodexPeerIdContentType = "json" | "text";
|
|
||||||
|
|
||||||
export type CodexPeerId<T extends CodexPeerIdContentType> = T extends "json"
|
|
||||||
? CodexPeerIdJsonResponse
|
|
||||||
: T extends "text"
|
|
||||||
? CodexPeerIdTextResponse
|
|
||||||
: never;
|
|
||||||
|
|||||||
1496
src/openapi.ts
1496
src/openapi.ts
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
import { assert, describe, it } from "vitest";
|
import { assert, describe, it } from "vitest";
|
||||||
import { Promises } from "./promise-safe";
|
import { Promises } from "./promise-safe";
|
||||||
import { CodexError } from "../errors/errors";
|
import { CodexError } from "../async";
|
||||||
|
|
||||||
describe("promise safe", () => {
|
describe("promise safe", () => {
|
||||||
it("returns an error when the promise failed", async () => {
|
it("returns an error when the promise failed", async () => {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { CodexError } from "../errors/errors";
|
import { CodexError } from "../async";
|
||||||
import type { SafeValue } from "../values/values";
|
import type { SafeValue } from "../values/values";
|
||||||
|
|
||||||
export const Promises = {
|
export const Promises = {
|
||||||
|
|||||||
@ -14,7 +14,6 @@
|
|||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Bundler",
|
"moduleResolution": "Bundler",
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true
|
||||||
"noUncheckedIndexedAccess": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import { defineConfig } from "vitest/config";
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
test: {
|
|
||||||
// run tests sequentially, not in parallel
|
|
||||||
// number of workers set to 1 disables parallelism
|
|
||||||
maxThreads: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Loading…
x
Reference in New Issue
Block a user