Compare commits

..

No commits in common. "develop" and "master" have entirely different histories.

16 changed files with 31106 additions and 20881 deletions

45
.circleci/config.yml Normal file
View File

@ -0,0 +1,45 @@
version: 2.1
orbs:
node: circleci/node@5.1.0
ssh: credijusto/ssh@0.5.2
workflows:
release:
jobs:
- node/run:
name: build
npm-run: build
post-steps:
- persist_to_workspace:
root: .
paths:
- lib/
filters:
branches:
only:
- master
- develop
- /^develop-.*$/
- node/run:
name: release
npm-run: semantic-release
requires:
- build
filters:
branches:
only:
- master
- develop
- /^develop-.*$/
context:
- publish
setup:
- attach_workspace:
at: ./
- add_ssh_keys:
fingerprints:
- "47:cf:a1:17:d9:81:8e:c5:51:e5:53:c8:33:e4:33:b9"
- ssh/ssh-add-host:
host_url: GITEA_HOST

View File

@ -1,52 +0,0 @@
name: Build/Publish
on:
push:
branches:
- master
- develop
- develop-*
workflow_dispatch:
inputs:
debug_enabled:
description: debug_enabled
type: boolean
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'npm'
- name: Setup Golang
uses: actions/setup-go@v4
with:
go-version: 1.19
- name: Setup Golang
run: |
VERSION=0.28.1;
TINYGO="tinygo_${VERSION}_amd64.deb";
wget -q https://github.com/tinygo-org/tinygo/releases/download/v$VERSION/$TINYGO;
sudo dpkg -i $TINYGO && rm $TINYGO;
- name: Fetch Wasm Deps
run: cd src/golang && go get
- run: npm ci
- run: npm run build --if-present
- name: Install SSH key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.GITEA_SSH_KEY }}
known_hosts: ${{ secrets.GITEA_KNOWN_HOST }}
- name: Publish
run: npm run semantic-release
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled && failure() }}
with:
limit-access-to-actor: true

View File

@ -1,6 +1,22 @@
{ {
"preset": [ "preset": [
"presetter-preset-hybrid", "presetter-preset-essentials",
"@lumeweb/node-library-preset" "presetter-preset-hybrid"
] ],
"config": {
"tsconfig": {
"compilerOptions": {
"lib": [
"ES2020",
"dom"
]
}
},
"prettier": {
"singleQuote": false
}
},
"variable": {
"source": "src"
}
} }

32
.releaserc Normal file
View File

@ -0,0 +1,32 @@
{
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/changelog",
{
"changelogFile": "docs/CHANGELOG.md"
}
],
"@semantic-release/git",
{
"assets": [
"package.json",
"docs/CHANGELOG.md",
"npm-shrinkwrap.json"
]
},
"@semantic-release/npm"
],
"branches": [
"master",
{
name: "develop",
prerelease: true
},
{
name: "develop-*",
prerelease: true
},
]
}

View File

@ -1,130 +0,0 @@
# [0.2.0-develop.41](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.40...v0.2.0-develop.41) (2023-11-17)
# [0.2.0-develop.40](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.39...v0.2.0-develop.40) (2023-10-19)
### Features
* add getter for jwtSessionKey ([9954616](https://git.lumeweb.com/LumeWeb/libportal/commit/9954616f5a80b6f04e7b14aa67cf106733e9d75c))
# [0.2.0-develop.39](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.38...v0.2.0-develop.39) (2023-09-20)
# [0.2.0-develop.38](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.37...v0.2.0-develop.38) (2023-09-11)
# [0.2.0-develop.37](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.36...v0.2.0-develop.37) (2023-09-11)
# [0.2.0-develop.36](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.35...v0.2.0-develop.36) (2023-09-09)
# [0.2.0-develop.35](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.34...v0.2.0-develop.35) (2023-09-09)
# [0.2.0-develop.34](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.33...v0.2.0-develop.34) (2023-09-08)
# [0.2.0-develop.33](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.32...v0.2.0-develop.33) (2023-09-08)
# [0.2.0-develop.32](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.31...v0.2.0-develop.32) (2023-09-08)
# [0.2.0-develop.31](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.30...v0.2.0-develop.31) (2023-09-08)
# [0.2.0-develop.30](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.29...v0.2.0-develop.30) (2023-09-08)
# [0.2.0-develop.29](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.28...v0.2.0-develop.29) (2023-09-08)
# [0.2.0-develop.28](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.27...v0.2.0-develop.28) (2023-09-07)
# [0.2.0-develop.27](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.26...v0.2.0-develop.27) (2023-09-07)
# [0.2.0-develop.26](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.25...v0.2.0-develop.26) (2023-09-07)
# [0.2.0-develop.25](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.24...v0.2.0-develop.25) (2023-09-04)
# [0.2.0-develop.24](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.23...v0.2.0-develop.24) (2023-09-04)
# [0.2.0-develop.23](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.22...v0.2.0-develop.23) (2023-09-03)
# [0.2.0-develop.22](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.21...v0.2.0-develop.22) (2023-09-03)
# [0.2.0-develop.21](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.20...v0.2.0-develop.21) (2023-09-03)
### Bug Fixes
* use switch to module in package.json ([b6722cf](https://git.lumeweb.com/LumeWeb/libportal/commit/b6722cf98d347095815532b3923eefb42deb2f0a))
# [0.2.0-develop.20](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.19...v0.2.0-develop.20) (2023-09-02)
### Bug Fixes
* check for only undefined or null on the size ([bd10837](https://git.lumeweb.com/LumeWeb/libportal/commit/bd108376ba33bb3c6b5c25606c5ed032e292e911))
# [0.2.0-develop.19](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.18...v0.2.0-develop.19) (2023-09-02)
### Bug Fixes
* fix encodeCid overload typings ([2eb5810](https://git.lumeweb.com/LumeWeb/libportal/commit/2eb5810dec17413ef68f282e9d884bcd867f520d))
# [0.2.0-develop.18](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.17...v0.2.0-develop.18) (2023-09-02)
# [0.2.0-develop.17](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.16...v0.2.0-develop.17) (2023-08-10)
### Bug Fixes
* refactor how we process a nodejs stream, as the current approach is extremely slow and wasteful. We need to do a bit of macgyvering and convert it via pipe to a passthrough so it passes a typeof check for Stream, then import it to form-data Response, and request a blob ([ae35797](https://git.lumeweb.com/LumeWeb/libportal/commit/ae35797a2525d23ac9a552d076a9904e68a7a142))
# [0.2.0-develop.16](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.15...v0.2.0-develop.16) (2023-07-18)
### Features
* add portalUrl getter ([0d0b2d4](https://git.lumeweb.com/LumeWeb/libportal/commit/0d0b2d4799a277c25f39673a10e4351c1991536c))
# [0.2.0-develop.15](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.14...v0.2.0-develop.15) (2023-07-18)
### Bug Fixes
* further wasm loading fixes ([d7d146b](https://git.lumeweb.com/LumeWeb/libportal/commit/d7d146b78d3737b17baf45bb4dd2dcf8fc7cbe8d))
# [0.2.0-develop.14](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.13...v0.2.0-develop.14) (2023-07-18)
### Bug Fixes
* async loading quirk in firefox js engine ([ea90488](https://git.lumeweb.com/LumeWeb/libportal/commit/ea9048868a4323da810bf139a083daf3ed5d79f7))
# [0.2.0-develop.13](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.12...v0.2.0-develop.13) (2023-07-18)
### Bug Fixes
* switch to using utf8ToBytes ([37fd754](https://git.lumeweb.com/LumeWeb/libportal/commit/37fd7543afe5f06e3193e24cb2c3390c848faadb))
# [0.2.0-develop.12](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.11...v0.2.0-develop.12) (2023-07-08)
### Bug Fixes
* add ?init query string for vite bundler ([04bd963](https://git.lumeweb.com/LumeWeb/libportal/commit/04bd9636a3fc70f5d23b5e61add7fb3d18604d27))
# [0.2.0-develop.11](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.10...v0.2.0-develop.11) (2023-06-26)
### Bug Fixes
* update uploadFile return type ([848f3df](https://git.lumeweb.com/LumeWeb/libportal/commit/848f3dff9d55e6c08779ae3696c6053d406d2f32))
# [0.2.0-develop.10](https://git.lumeweb.com/LumeWeb/libportal/compare/v0.2.0-develop.9...v0.2.0-develop.10) (2023-06-26)
### Bug Fixes
* add missing controller.enqueue ([2aa53fa](https://git.lumeweb.com/LumeWeb/libportal/commit/2aa53faf00cc7024a24dc97fffaeb855faa4e650))
* add properties and methods to go wasm middleware, accessed via reflection ([53dd352](https://git.lumeweb.com/LumeWeb/libportal/commit/53dd352c95fec8ec266a53c03f19cecbecf8821b))
* ensure root and proof are Uint8Array's ([0c320f9](https://git.lumeweb.com/LumeWeb/libportal/commit/0c320f992bdf269614716b51818ed7063086c01c))
* exit not properly exported in wasm ([23a55f7](https://git.lumeweb.com/LumeWeb/libportal/commit/23a55f772b7dde7712742ee5f47a5fda5bb8afd2))
* fix wasmDone logic error ([68fec66](https://git.lumeweb.com/LumeWeb/libportal/commit/68fec66069721a6dc94027419ddd2cafbc877cbc))
* need to refactor verification stream logic further and check if the stream is done but wasm isn't ([051f4b2](https://git.lumeweb.com/LumeWeb/libportal/commit/051f4b2da75ab2287c99a3514af5d0d4f28017bf))
* rename exit to kill to fix symbol conflict ([50a7c80](https://git.lumeweb.com/LumeWeb/libportal/commit/50a7c803584b57e4e294aca117fc1e8b9a2a09c7))
* update uploadFile overload types ([45fbc1b](https://git.lumeweb.com/LumeWeb/libportal/commit/45fbc1b63d2c19e186d6f21b022fee62be61866a))

50426
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,9 @@
{ {
"name": "@lumeweb/libportal", "name": "@lumeweb/libportal",
"version": "0.2.0-develop.41", "version": "0.1.0",
"main": "lib/index.js", "main": "lib/index.js",
"type": "module", "module": "lib/index.mjs",
"types": "lib/index.d.ts",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "gitea@git.lumeweb.com:LumeWeb/libportal.git" "url": "gitea@git.lumeweb.com:LumeWeb/libportal.git"
@ -15,26 +16,24 @@
"./package.json": "./package.json" "./package.json": "./package.json"
}, },
"devDependencies": { "devDependencies": {
"@lumeweb/node-library-preset": "^0.2.7",
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
"@semantic-release/commit-analyzer": "^10.0.1", "@semantic-release/commit-analyzer": "^10.0.1",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"@semantic-release/npm": "^10.0.4", "@semantic-release/npm": "^10.0.4",
"@semantic-release/release-notes-generator": "^11.0.3", "@semantic-release/release-notes-generator": "^11.0.3",
"presetter": "^4.0.1", "presetter": "^3.5.5",
"presetter-preset-hybrid": "4.0.1", "presetter-preset-essentials": "^3.5.5",
"presetter-preset-strict": "^4.0.1", "presetter-preset-hybrid": "^3.5.5",
"semantic-release": "^21.0.5" "semantic-release": "^21.0.5"
}, },
"readme": "ERROR: No README data found!", "readme": "ERROR: No README data found!",
"_id": "@lumeweb/libportal@0.1.0",
"scripts": { "scripts": {
"prepare": "presetter bootstrap", "prepare": "presetter bootstrap",
"build": "run build", "build": "presetter run build",
"build:wasm": "bash -c \"source ~/.gvm/scripts/gvm; mkdir ./lib/wasm && cd src/golang; tinygo build -o ../../lib/wasm/bao.wasm -target wasm ./main.go\"",
"semantic-release": "semantic-release" "semantic-release": "semantic-release"
}, },
"dependencies": { "dependencies": {
"@lumeweb/libs5": "^0.1.0-develop.61",
"@noble/curves": "^1.1.0", "@noble/curves": "^1.1.0",
"@noble/hashes": "^1.3.1", "@noble/hashes": "^1.3.1",
"detect-node": "^2.1.0", "detect-node": "^2.1.0",

74
src/cid.ts Normal file
View File

@ -0,0 +1,74 @@
import { base58btc } from "multiformats/bases/base58";
import * as edUtils from "@noble/curves/abstract/utils";
export const MAGIC_BYTES = new Uint8Array([0x26, 0x1f]);
export interface CID {
hash: Uint8Array;
size: bigint;
}
export function encodeCid(hash: Uint8Array, size: bigint);
export function encodeCid(hash: string, size: bigint);
export function encodeCid(hash: any, size: bigint) {
if (typeof hash === "string") {
hash = edUtils.hexToBytes(hash);
}
if (!(hash instanceof Uint8Array)) {
throw new Error();
}
if (!size) {
throw new Error("size required");
}
size = BigInt(size);
const sizeBytes = new Uint8Array(8);
const sizeView = new DataView(sizeBytes.buffer);
sizeView.setBigInt64(0, size, true);
const prefixedHash = Uint8Array.from([...MAGIC_BYTES, ...hash, ...sizeBytes]);
return base58btc.encode(prefixedHash).toString();
}
export function decodeCid(cid: string): CID {
let bytes = base58btc.decode(cid);
if (!arrayBufferEqual(bytes.slice(0, 2).buffer, bytes.buffer)) {
throw new Error("Invalid cid");
}
bytes = bytes.slice(2);
let cidHash = bytes.slice(0, 32);
let size = bytes.slice(32);
const sizeView = new DataView(size.buffer);
return {
hash: cidHash,
size: sizeView.getBigInt64(0, true),
};
}
function arrayBufferEqual(buf1, buf2) {
if (buf1 === buf2) {
return true;
}
if (buf1.byteLength !== buf2.byteLength) {
return false;
}
var view1 = new DataView(buf1);
var view2 = new DataView(buf2);
var i = buf1.byteLength;
while (i--) {
if (view1.getUint8(i) !== view2.getUint8(i)) {
return false;
}
}
return true;
}

View File

@ -2,6 +2,13 @@ import { ed25519 as ed } from "@noble/curves/ed25519";
import * as edUtils from "@noble/curves/abstract/utils"; import * as edUtils from "@noble/curves/abstract/utils";
import { RegisterRequest } from "./requests/account.js"; import { RegisterRequest } from "./requests/account.js";
import fetch, {
FormData,
Blob,
RequestInit,
Response,
HeadersInit,
} from "node-fetch";
import { import {
LoginRequest, LoginRequest,
LogoutRequest, LogoutRequest,
@ -14,29 +21,24 @@ import {
UploadStatusResponse, UploadStatusResponse,
} from "./responses/files.js"; } from "./responses/files.js";
import * as TUS from "tus-js-client"; import TUS from "tus-js-client";
import streamToBlob from "stream-to-blob"; import streamToBlob from "stream-to-blob";
import defer from "p-defer"; import defer from "p-defer";
import { blake3 } from "@noble/hashes/blake3"; import { blake3 } from "@noble/hashes/blake3";
import { encodeCid } from "./cid.js";
import { Readable as NodeReadableStream } from "stream";
import { import {
AuthStatusResponse, AuthStatusResponse,
LoginResponse, LoginResponse,
PubkeyChallengeResponse, PubkeyChallengeResponse,
} from "./responses/auth.js"; } from "./responses/auth.js";
import isNode from "detect-node";
import { utf8ToBytes } from "@noble/curves/abstract/utils";
import { CID, CID_TYPES } from "@lumeweb/libs5";
type NodeReadableStreamType = typeof import("stream").Readable;
type NodePassThroughStreamType = typeof import("stream").PassThrough;
export interface ClientOptions { export interface ClientOptions {
portalUrl: string; portalUrl: string;
email?: string; email?: string;
password?: string; password?: string;
privateKey?: Uint8Array; privateKey?: Uint8Array;
jwt?: string;
} }
interface FetchOptions { interface FetchOptions {
@ -60,10 +62,6 @@ export class Client {
throw new Error("Portal url is required"); throw new Error("Portal url is required");
} }
if (options.jwt) {
this.jwtSessionKey = options.jwt;
}
this._options = options; this._options = options;
} }
@ -71,10 +69,6 @@ export class Client {
return this._options.email as string; return this._options.email as string;
} }
get portalUrl(): string {
return this._options.portalUrl as string;
}
set email(email: string) { set email(email: string) {
this._options.email = email; this._options.email = email;
} }
@ -83,10 +77,6 @@ export class Client {
return this._options.password as string; return this._options.password as string;
} }
get jwt(): string | undefined {
return this.jwtSessionKey;
}
set password(password: string) { set password(password: string) {
this._options.email = password; this._options.email = password;
} }
@ -115,10 +105,6 @@ export class Client {
} }
async login(): Promise<LoginResponse> { async login(): Promise<LoginResponse> {
if (this._options.privateKey) {
return this.loginPubkey();
}
return this.post<LoginResponse>("/api/v1/auth/login", { return this.post<LoginResponse>("/api/v1/auth/login", {
email: this._options.email, email: this._options.email,
password: this._options.password, password: this._options.password,
@ -145,7 +131,7 @@ export class Client {
return json.status as boolean; return json.status as boolean;
} }
async loginPubkey(): Promise<LoginResponse> { async loginPubkey(): Promise<void> {
if (!this._options.privateKey) { if (!this._options.privateKey) {
throw new Error("Private key is required"); throw new Error("Private key is required");
} }
@ -158,7 +144,7 @@ export class Client {
); );
const signature = ed.sign( const signature = ed.sign(
utf8ToBytes(challenge.challenge), new TextEncoder().encode(challenge.challenge),
this._options.privateKey, this._options.privateKey,
); );
@ -172,8 +158,6 @@ export class Client {
); );
this.jwtSessionKey = loginRet.token; this.jwtSessionKey = loginRet.token;
return { token: loginRet.token };
} }
logout(request: LogoutRequest): Promise<void> { logout(request: LogoutRequest): Promise<void> {
@ -202,7 +186,6 @@ export class Client {
} }
async downloadProof(cid: string): Promise<ArrayBuffer> { async downloadProof(cid: string): Promise<ArrayBuffer> {
const Response = await this.getFetchResponseObject();
return await new Response( return await new Response(
await this.get<any>(`/api/v1/files/proof/${cid}`, { await this.get<any>(`/api/v1/files/proof/${cid}`, {
auth: true, auth: true,
@ -223,27 +206,13 @@ export class Client {
if (options.data) { if (options.data) {
fetchOptions.body = options.data; fetchOptions.body = options.data;
const _FormData = await this.getFormDataObject(); if (!(fetchOptions.body instanceof FormData)) {
if (!(fetchOptions.body instanceof _FormData)) {
fetchOptions.headers["Content-Type"] = "application/json"; fetchOptions.headers["Content-Type"] = "application/json";
fetchOptions.body = JSON.stringify(fetchOptions.body); fetchOptions.body = JSON.stringify(fetchOptions.body);
} else {
if (isNode) {
const formDataToBlob = (
await import("formdata-polyfill/formdata-to-blob.js")
).formDataToBlob;
const Blob = (await import("node-fetch")).Blob;
const blob = formDataToBlob(fetchOptions.body, Blob);
// @ts-ignore
fetchOptions.body = Buffer.from(await blob.arrayBuffer());
fetchOptions.headers["Content-Type"] = blob.type;
}
} }
} }
const fetch = await this.getFetchObject(); const response = await fetch(this.getEndpoint(path), fetchOptions);
const response = await fetch(this.getEndpoint(path), fetchOptions as any);
if (!options.fullResponse) { if (!options.fullResponse) {
if (!response.ok) { if (!response.ok) {
@ -292,21 +261,23 @@ export class Client {
); );
} }
async uploadFile(stream: Blob, size?: bigint): Promise<CID>; async uploadFile(stream: Blob, size?: bigint);
async uploadFile( async uploadFile(
stream: ReadableStream, stream: ReadableStream,
hashStream: ReadableStream, hashStream: ReadableStream,
size: bigint, size: bigint,
): Promise<CID>; );
async uploadFile(stream: Uint8Array, size?: bigint): Promise<CID>; async uploadFile(stream: Uint8Array, size?: bigint);
async uploadFile( async uploadFile(
stream: NodeJS.ReadableStream, stream: NodeJS.ReadableStream,
hashStream: NodeJS.ReadableStream, hashStream: NodeJS.ReadableStream,
size?: bigint, size?: bigint,
): Promise<CID>; );
async uploadFile(stream: any, hashStream?: any, size?: bigint): Promise<CID> { async uploadFile(
const Blob = await this.getBlobObject(); stream: any,
hashStream?: any,
size?: bigint,
): Promise<string> {
if (stream instanceof Uint8Array || stream instanceof Blob) { if (stream instanceof Uint8Array || stream instanceof Blob) {
size = BigInt(stream.length); size = BigInt(stream.length);
} }
@ -324,47 +295,36 @@ export class Client {
return this.uploadFileTus(stream, hashStream, size); return this.uploadFileTus(stream, hashStream, size);
} }
private async uploadFileSmall(stream: Blob): Promise<CID>; private async uploadFileSmall(stream: Blob): Promise<string>;
private async uploadFileSmall( private async uploadFileSmall(
stream: ReadableStream, stream: ReadableStream,
hashStream: ReadableStream, hashStream: ReadableStream,
): Promise<CID>; ): Promise<string>;
private async uploadFileSmall(stream: Uint8Array): Promise<CID>; private async uploadFileSmall(stream: Uint8Array): Promise<string>;
private async uploadFileSmall(stream: NodeJS.ReadableStream): Promise<CID>; private async uploadFileSmall(stream: NodeJS.ReadableStream): Promise<string>;
private async uploadFileSmall(stream: any): Promise<CID> { private async uploadFileSmall(stream: any): Promise<string> {
const Blob = await this.getBlobObject();
if (stream instanceof ReadableStream) { if (stream instanceof ReadableStream) {
stream = await streamToBlob(stream); stream = await streamToBlob(stream);
} }
let NodeReadableStream = if (stream instanceof NodeReadableStream) {
(await this.getNodeReadableObject()) as NodeReadableStreamType; let data = new Uint8Array();
for await (const chunk of stream) {
data = Uint8Array.from([...data, ...chunk]);
}
let NodePassThroughStream = stream = data;
(await this.getNodePassThroughObject()) as NodePassThroughStreamType;
if (NodeReadableStream && stream instanceof NodeReadableStream) {
const Response = await this.getFetchResponseObject();
stream = await new Response(
stream.pipe(new NodePassThroughStream()) as any,
).blob();
} }
if (stream instanceof Uint8Array) { if (stream instanceof Uint8Array) {
stream = new Blob([Buffer.from(stream)]); stream = new Blob([Buffer.from(stream)]);
} }
if ( if (!(stream instanceof Blob) && !(stream instanceof NodeReadableStream)) {
!(stream instanceof Blob) &&
!(NodeReadableStream && stream instanceof NodeReadableStream)
) {
throw new Error("Invalid stream"); throw new Error("Invalid stream");
} }
const _FormData = await this.getFormDataObject(); const formData = new FormData();
const formData = new _FormData();
formData.set("file", stream as Blob); formData.set("file", stream as Blob);
const response = await this.post<UploadResponse>( const response = await this.post<UploadResponse>(
@ -373,26 +333,29 @@ export class Client {
{ auth: true }, { auth: true },
); );
return CID.decode(response.cid); return response.cid;
} }
private async uploadFileTus(stream: Blob, size?: bigint): Promise<CID>; private async uploadFileTus(stream: Blob, size?: bigint): Promise<string>;
private async uploadFileTus( private async uploadFileTus(
stream: ReadableStream, stream: ReadableStream,
hashStream: ReadableStream, hashStream: ReadableStream,
size?: bigint, size?: bigint,
): Promise<CID>; ): Promise<string>;
private async uploadFileTus(stream: Uint8Array, size?: bigint): Promise<CID>; private async uploadFileTus(
stream: Uint8Array,
size?: bigint,
): Promise<string>;
private async uploadFileTus( private async uploadFileTus(
stream: NodeJS.ReadableStream, stream: NodeJS.ReadableStream,
hashStream: ReadableStream, hashStream: ReadableStream,
size?: bigint, size?: bigint,
): Promise<CID>; ): Promise<string>;
private async uploadFileTus( private async uploadFileTus(
stream: any, stream: any,
hashStream?: any, hashStream?: any,
size?: bigint, size?: bigint,
): Promise<CID> { ): Promise<string> {
if (["bigint", "number"].includes(typeof hashStream)) { if (["bigint", "number"].includes(typeof hashStream)) {
size = BigInt(hashStream); size = BigInt(hashStream);
hashStream = undefined; hashStream = undefined;
@ -405,10 +368,7 @@ export class Client {
hash = await this.computeHash(hashStream); hash = await this.computeHash(hashStream);
} }
let NodeReadableStream = if (stream instanceof NodeReadableStream) {
(await this.getNodeReadableObject()) as NodeReadableStreamType;
if (NodeReadableStream && stream instanceof NodeReadableStream) {
hash = await this.computeHash(hashStream); hash = await this.computeHash(hashStream);
} }
@ -421,7 +381,7 @@ export class Client {
if ( if (
!(stream instanceof ReadableStreamDefaultReader) && !(stream instanceof ReadableStreamDefaultReader) &&
!(stream instanceof Blob) && !(stream instanceof Blob) &&
!(NodeReadableStream && stream instanceof NodeReadableStream) !(stream instanceof NodeReadableStream)
) { ) {
throw new Error("Invalid stream"); throw new Error("Invalid stream");
} }
@ -466,10 +426,10 @@ export class Client {
await ret.promise; await ret.promise;
const cid = CID.fromHash(hash, Number(size)); const cid = encodeCid(hash, size as bigint);
while (true) { while (true) {
const status = await this.getUploadStatus(cid.toString()); const status = await this.getUploadStatus(cid as string);
if (status.status === "uploaded") { if (status.status === "uploaded") {
break; break;
@ -480,7 +440,7 @@ export class Client {
}); });
} }
return cid; return cid as string;
} }
async getUploadStatus(cid: string) { async getUploadStatus(cid: string) {
@ -511,10 +471,7 @@ export class Client {
return edUtils.bytesToHex(hasher.digest()); return edUtils.bytesToHex(hasher.digest());
} }
let NodeReadableStream = if (stream instanceof NodeReadableStream) {
(await this.getNodeReadableObject()) as NodeReadableStreamType;
if (NodeReadableStream && stream instanceof NodeReadableStream) {
const hasher = blake3.create({}); const hasher = blake3.create({});
for await (const chunk of stream) { for await (const chunk of stream) {
@ -532,52 +489,4 @@ export class Client {
throw new Error("Invalid stream"); throw new Error("Invalid stream");
} }
private async getFormDataObject() {
if (isNode) {
return (await import("node-fetch")).FormData;
}
return FormData;
}
private async getBlobObject() {
if (isNode) {
return (await import("node-fetch")).Blob;
}
return Blob;
}
private async getNodeReadableObject() {
if (isNode) {
return (await import("stream")).Readable;
}
return undefined;
}
private async getNodePassThroughObject() {
if (isNode) {
return (await import("stream")).PassThrough;
}
return undefined;
}
private async getFetchObject() {
if (isNode) {
return (await import("node-fetch")).default;
}
return fetch;
}
private async getFetchResponseObject() {
if (isNode) {
return (await import("node-fetch")).Response;
}
return Response;
}
} }

View File

@ -1,663 +0,0 @@
const encoder = new TextEncoder();
const decoder = new TextDecoder();
type ImportObject = {
wasi_snapshot_preview1: {
proc_exit: (code) => void;
random_get: (bufPtr, bufLen) => number;
fd_seek: () => number;
fd_write: (fd, iovs_ptr, iovs_len, nwritten_ptr) => number;
fd_close: () => number;
fd_fdstat_get: () => number;
};
env: {
"syscall/js.valueInvoke": (
ret_addr,
v_addr,
args_ptr,
args_len,
args_cap,
) => void;
"syscall/js.valueDelete": (v_addr, p_ptr, p_len) => void;
"syscall/js.copyBytesToGo": (
ret_addr,
dest_addr,
dest_len,
dest_cap,
source_addr,
) => void;
"syscall/js.valueSet": (v_addr, p_ptr, p_len, x_addr) => void;
"syscall/js.copyBytesToJS": (
ret_addr,
dest_addr,
source_addr,
source_len,
source_cap,
) => void;
"syscall/js.valueNew": (
ret_addr,
v_addr,
args_ptr,
args_len,
args_cap,
) => void;
"syscall/js.valueInstanceOf": (v_addr, t_addr) => boolean;
"runtime.ticks": () => number;
"runtime.sleepTicks": (timeout) => void;
"syscall/js.valueLoadString": (
v_addr,
slice_ptr,
slice_len,
slice_cap,
) => void;
"syscall/js.stringVal": (ret_ptr, value_ptr, value_len) => void;
"syscall/js.valueIndex": (ret_addr, v_addr, i) => void;
"syscall/js.valueLength": (v_addr) => any;
"syscall/js.valueCall": (
ret_addr,
v_addr,
m_ptr,
m_len,
args_ptr,
args_len,
args_cap,
) => void;
"syscall/js.finalizeRef": (sp) => void;
"syscall/js.valueGet": (retval, v_addr, p_ptr, p_len) => void;
"syscall/js.valuePrepareString": (ret_addr, v_addr) => void;
"syscall/js.valueSetIndex": (v_addr, i, x_addr) => void;
};
};
let logLine: any = [];
export default class Go {
private _callbackTimeouts: Map<any, any>;
private _nextCallbackTimeoutID: number;
private _inst?: any;
private _values: any[] = [NaN, 0, null, true, false, global, this];
private _ids: Map<any, any> = new Map<any, any>();
private _idPool: any[] = [];
private _goRefCounts: any[] = [];
private exited = false;
private _resolveCallbackPromise?: () => void;
importObject: ImportObject;
private _pendingEvent?: any;
constructor() {
this._callbackTimeouts = new Map();
this._nextCallbackTimeoutID = 1;
const mem = () => {
// The buffer may change when requesting more memory.
return new DataView(this._inst.exports.memory.buffer);
};
const setInt64 = (addr, v) => {
mem().setUint32(addr + 0, v, true);
mem().setUint32(addr + 4, Math.floor(v / 4294967296), true);
};
const getInt64 = (addr) => {
const low = mem().getUint32(addr + 0, true);
const high = mem().getInt32(addr + 4, true);
return low + high * 4294967296;
};
const loadValue = (addr) => {
const f = mem().getFloat64(addr, true);
if (f === 0) {
return undefined;
}
if (!isNaN(f)) {
return f;
}
const id = mem().getUint32(addr, true);
return this._values[id];
};
const storeValue = (addr, v) => {
const nanHead = 0x7ff80000;
if (typeof v === "number") {
if (isNaN(v)) {
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 0, true);
return;
}
if (v === 0) {
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 1, true);
return;
}
mem().setFloat64(addr, v, true);
return;
}
switch (v) {
case undefined:
mem().setFloat64(addr, 0, true);
return;
case null:
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 2, true);
return;
case true:
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 3, true);
return;
case false:
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 4, true);
return;
}
let id = this._ids.get(v);
if (id === undefined) {
id = this._idPool.pop();
if (id === undefined) {
id = this._values.length;
}
this._values[id] = v;
this._goRefCounts[id] = 0;
this._ids.set(v, id);
}
this._goRefCounts[id]++;
let typeFlag = 1;
switch (typeof v) {
case "string":
typeFlag = 2;
break;
case "symbol":
typeFlag = 3;
break;
case "function":
typeFlag = 4;
break;
}
mem().setUint32(addr + 4, nanHead | typeFlag, true);
mem().setUint32(addr, id, true);
};
const loadSlice = (array, len, cap?) => {
return new Uint8Array(this._inst.exports.memory.buffer, array, len);
};
const loadSliceOfValues = (array, len, cap) => {
const a = new Array(len);
for (let i = 0; i < len; i++) {
a[i] = loadValue(array + i * 8);
}
return a;
};
const loadString = (ptr, len) => {
return decoder.decode(
new DataView(this._inst.exports.memory.buffer, ptr, len),
);
};
const timeOrigin = Date.now() - performance.now();
this.importObject = {
wasi_snapshot_preview1: {
fd_write: (fd, iovs_ptr, iovs_len, nwritten_ptr) => {
let nwritten = 0;
if (fd == 1) {
for (let iovs_i = 0; iovs_i < iovs_len; iovs_i++) {
let iov_ptr = iovs_ptr + iovs_i * 8;
let ptr = mem().getUint32(iov_ptr + 0, true);
let len = mem().getUint32(iov_ptr + 4, true);
nwritten += len;
for (let i = 0; i < len; i++) {
let c = mem().getUint8(ptr + i);
if (c == 13) {
// CR
// ignore
} else if (c == 10) {
// LF
// write line
let line = decoder.decode(new Uint8Array(logLine));
logLine = [];
console.log(line);
} else {
logLine.push(c);
}
}
}
} else {
console.error("Invalid file descriptor:", fd);
}
mem().setUint32(nwritten_ptr, nwritten, true);
return 0;
},
fd_close: () => 0, // dummy
fd_fdstat_get: () => 0, // dummy
fd_seek: () => 0, // dummy
proc_exit: (code) => {
if (global.process) {
// Node.js
process.exit(code);
} else {
// Can't exit in a browser.
throw "Trying to exit with code " + code;
}
},
random_get: (bufPtr, bufLen) => {
crypto.getRandomValues(loadSlice(bufPtr, bufLen));
return 0;
},
},
env: {
"runtime.ticks": () => {
return timeOrigin + performance.now();
},
"runtime.sleepTicks": (timeout) => {
setTimeout(this._inst.exports.go_scheduler, timeout);
},
"syscall/js.finalizeRef": (sp) => {
console.error("syscall/js.finalizeRef not implemented");
},
"syscall/js.stringVal": (ret_ptr, value_ptr, value_len) => {
const s = loadString(value_ptr, value_len);
storeValue(ret_ptr, s);
},
"syscall/js.valueGet": (retval, v_addr, p_ptr, p_len) => {
let prop = loadString(p_ptr, p_len);
let value = loadValue(v_addr);
let result = Reflect.get(value, prop);
storeValue(retval, result);
},
"syscall/js.valueSet": (v_addr, p_ptr, p_len, x_addr) => {
const v = loadValue(v_addr);
const p = loadString(p_ptr, p_len);
const x = loadValue(x_addr);
Reflect.set(v, p, x);
},
"syscall/js.valueDelete": (v_addr, p_ptr, p_len) => {
const v = loadValue(v_addr);
const p = loadString(p_ptr, p_len);
Reflect.deleteProperty(v, p);
},
"syscall/js.valueIndex": (ret_addr, v_addr, i) => {
storeValue(ret_addr, Reflect.get(loadValue(v_addr), i));
},
"syscall/js.valueSetIndex": (v_addr, i, x_addr) => {
Reflect.set(loadValue(v_addr), i, loadValue(x_addr));
},
"syscall/js.valueCall": (
ret_addr,
v_addr,
m_ptr,
m_len,
args_ptr,
args_len,
args_cap,
) => {
const v = loadValue(v_addr);
const name = loadString(m_ptr, m_len);
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
try {
const m = Reflect.get(v, name);
storeValue(ret_addr, Reflect.apply(m, v, args));
mem().setUint8(ret_addr + 8, 1);
} catch (err) {
storeValue(ret_addr, err);
mem().setUint8(ret_addr + 8, 0);
}
},
"syscall/js.valueInvoke": (
ret_addr,
v_addr,
args_ptr,
args_len,
args_cap,
) => {
try {
const v = loadValue(v_addr);
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
storeValue(ret_addr, Reflect.apply(v, undefined, args));
mem().setUint8(ret_addr + 8, 1);
} catch (err) {
storeValue(ret_addr, err);
mem().setUint8(ret_addr + 8, 0);
}
},
"syscall/js.valueNew": (
ret_addr,
v_addr,
args_ptr,
args_len,
args_cap,
) => {
const v = loadValue(v_addr);
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
try {
storeValue(ret_addr, Reflect.construct(v, args));
mem().setUint8(ret_addr + 8, 1);
} catch (err) {
storeValue(ret_addr, err);
mem().setUint8(ret_addr + 8, 0);
}
},
"syscall/js.valueLength": (v_addr) => {
return loadValue(v_addr).length;
},
"syscall/js.valuePrepareString": (ret_addr, v_addr) => {
const s = String(loadValue(v_addr));
const str = encoder.encode(s);
storeValue(ret_addr, str);
setInt64(ret_addr + 8, str.length);
},
"syscall/js.valueLoadString": (
v_addr,
slice_ptr,
slice_len,
slice_cap,
) => {
const str = loadValue(v_addr);
loadSlice(slice_ptr, slice_len, slice_cap).set(str);
},
"syscall/js.valueInstanceOf": (v_addr, t_addr) => {
return loadValue(v_addr) instanceof loadValue(t_addr);
},
"syscall/js.copyBytesToGo": (
ret_addr,
dest_addr,
dest_len,
dest_cap,
source_addr,
) => {
let num_bytes_copied_addr = ret_addr;
let returned_status_addr = ret_addr + 4;
const dst = loadSlice(dest_addr, dest_len, dest_cap);
const src = loadValue(source_addr);
if (
!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)
) {
mem().setUint8(returned_status_addr, 0);
return;
}
const toCopy = src.subarray(0, dst.length);
dst.set(toCopy);
setInt64(num_bytes_copied_addr, toCopy.length);
mem().setUint8(returned_status_addr, 1);
},
"syscall/js.copyBytesToJS": (
ret_addr,
dest_addr,
source_addr,
source_len,
source_cap,
) => {
let num_bytes_copied_addr = ret_addr;
let returned_status_addr = ret_addr + 4;
const dst = loadValue(dest_addr);
const src = loadSlice(source_addr, source_len, source_cap);
if (
!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)
) {
mem().setUint8(returned_status_addr, 0);
return;
}
const toCopy = src.subarray(0, dst.length);
dst.set(toCopy);
setInt64(num_bytes_copied_addr, toCopy.length);
mem().setUint8(returned_status_addr, 1);
},
},
};
}
async run(instance) {
this._inst = instance;
const mem = new DataView(this._inst.exports.memory.buffer);
while (true) {
const callbackPromise = new Promise((resolve) => {
this._resolveCallbackPromise = () => {
if (this.exited) {
throw new Error("Bad callback: Go program has already exited");
}
setTimeout(resolve, 0);
};
});
this._inst.exports._start();
if (this.exited) {
break;
}
await callbackPromise;
}
}
_resume() {
if (this.exited) {
throw new Error("Go program has already exited");
}
this._inst.exports.resume();
}
_makeFuncWrapper(id) {
const go = this;
return function () {
const event = { id: id, this: this, args: arguments };
go._pendingEvent = event;
go._resume();
// @ts-ignore
return event.result;
};
}
}
if (
typeof global !== "undefined" ||
typeof window !== "undefined" ||
typeof self !== "undefined"
) {
if (typeof global !== "undefined") {
// global already exists
} else if (typeof window !== "undefined") {
window.global = window;
} else if (typeof self !== "undefined") {
self.global = self;
} else {
throw new Error(
"Cannot export Go (neither global, window nor self is defined)",
);
}
if (!global.require && typeof require !== "undefined") {
global.require = require;
}
// @ts-ignore
if (!global.fs && global.require) {
global.fs = require("fs");
}
const enosys = () => {
const err = new Error("not implemented");
// @ts-ignore
err.code = "ENOSYS";
return err;
};
if (!global.fs) {
let outputBuf = "";
global.fs = {
constants: {
O_WRONLY: -1,
O_RDWR: -1,
O_CREAT: -1,
O_TRUNC: -1,
O_APPEND: -1,
O_EXCL: -1,
},
writeSync: (fd, buf) => {
outputBuf += decoder.decode(buf);
const nl = outputBuf.lastIndexOf("\n");
if (nl != -1) {
console.log(outputBuf.substr(0, nl));
outputBuf = outputBuf.substr(nl + 1);
}
return buf.length;
},
write: (fd, buf, offset, length, position, callback) => {
if (offset !== 0 || length !== buf.length || position !== null) {
callback(enosys());
return;
}
const n = global.fs.writeSync(fd, buf);
callback(null, n);
},
open: (path, flags, mode, callback) => {
callback(enosys());
},
fsync: (fd, callback) => {
callback(null);
},
fdatasync: (fd, callback) => {
callback(null);
},
close: (fd, callback) => {
callback(null);
},
createReadStream: enosys,
createWriteStream: enosys,
ftruncate: (fd, length, callback) => {
callback(enosys());
},
readFile: (path, callback) => {
callback(enosys());
},
writeFile: (path, data, callback) => {
callback(enosys());
},
truncate: (path, length, callback) => {
callback(enosys());
},
readdir: (path, callback) => {
callback(enosys());
},
unlink: (path, callback) => {
callback(enosys());
},
rmdir: (path, callback) => {
callback(enosys());
},
mkdir: (path, perm, callback) => {
callback(enosys());
},
stat: (path, callback) => {
callback(enosys());
},
lstat: (path, callback) => {
callback(enosys());
},
fstat: (fd, callback) => {
callback(enosys());
},
rename: (oldPath, newPath, callback) => {
callback(enosys());
},
symlink: (target, path, callback) => {
callback(enosys());
},
link: (existingPath, newPath, callback) => {
callback(enosys());
},
readlink: (path, callback) => {
callback(enosys());
},
chmod: (path, mode, callback) => {
callback(enosys());
},
lchmod: (path, mode, callback) => {
callback(enosys());
},
fchmod: (fd, mode, callback) => {
callback(enosys());
},
chown: (path, uid, gid, callback) => {
callback(enosys());
},
lchown: (path, uid, gid, callback) => {
callback(enosys());
},
fchown: (fd, uid, gid, callback) => {
callback(enosys());
},
utimes: (path, atime, mtime, callback) => {
callback(enosys());
},
futimes: (fd, atime, mtime, callback) => {
callback(enosys());
},
realpath: (path, callback) => {
callback(enosys());
},
fallocate: (fd, mode, offset, length, callback) => {
callback(enosys());
},
copyFile: (src, dest, flags, callback) => {
callback(enosys());
},
};
}
if (!global.crypto) {
global.crypto = {
// @ts-ignore
getRandomValues: (arr: number[]) => {
for (let i = 0; i < arr.length; i++) {
arr[i] = Math.floor(Math.random() * 256);
}
},
};
}
if (!global.performance) {
// @ts-ignore
global.performance = {
now: () => Date.now(),
};
}
if (!global.TextEncoder) {
global.TextEncoder = TextEncoder;
}
if (!global.TextDecoder) {
// @ts-ignore
global.TextDecoder = TextDecoder;
}
if (!global.Buffer) {
global.Buffer = {
// @ts-ignore
isBuffer: (x) => false,
};
}
if (!global.process) {
// @ts-ignore
global.process = {
getuid: () => -1,
getgid: () => -1,
geteuid: () => -1,
getegid: () => -1,
getgroups: () => [],
pid: -1,
ppid: -1,
umask: () => 0,
cwd: () => "/",
chdir: (dir) => {},
};
}
if (!global.Error) {
// @ts-ignore
global.Error = class extends Error {};
}
}

View File

@ -1,8 +0,0 @@
module main
go 1.19
require (
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
)

View File

@ -1,4 +0,0 @@
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=

View File

@ -1,125 +0,0 @@
package main
import (
"bytes"
"io"
"lukechampine.com/blake3"
"math/rand"
"strconv"
"syscall/js"
)
var activeReader *reader
var pipe chan []byte
var callbackId int
var nextBytes chan int
var killChan chan bool
func main() {
pipe = make(chan []byte)
nextBytes = make(chan int)
killChan = make(chan bool)
callbackId = rand.Int()
<-killChan
}
type reader struct {
}
func (r *reader) Read(p []byte) (n int, err error) {
nextBytes <- len(p)
data := <-pipe
copy(p[:], data[:])
resetWritePromise()
return len(data), nil
}
//export start
func start() int {
rootChan := make(chan []byte)
proofChan := make(chan []byte)
resetWritePromise()
setGlobalObject(getGlobalPrefix()+"_set_root", js.FuncOf(func(this js.Value, args []js.Value) any {
jsroot := args[0]
root := make([]byte, jsroot.Get("length").Int())
js.CopyBytesToGo(root, jsroot)
rootChan <- root
return nil
}))
setGlobalObject(getGlobalPrefix()+"_set_proof", js.FuncOf(func(this js.Value, args []js.Value) any {
jsproof := args[0]
proofSlice := make([]byte, jsproof.Get("length").Int())
js.CopyBytesToGo(proofSlice, jsproof)
proofChan <- proofSlice
return nil
}))
setGlobalObject(getGlobalPrefix()+"_write", js.FuncOf(func(this js.Value, args []js.Value) any {
d := args[0]
data := make([]byte, d.Get("length").Int())
js.CopyBytesToGo(data, d)
pipe <- data
return nil
}))
go func() {
rootSlice := <-rootChan
proof := <-proofChan
var root [32]byte
copy(root[:], rootSlice)
ret, err := blake3.BaoDecode(io.Discard, activeReader, bytes.NewReader(proof), root)
setGlobalObject(getGlobalPrefix()+"_result", ret)
setGlobalObject(getGlobalPrefix()+"_error", err)
}()
return callbackId
}
//export kill
func kill() {
killChan <- true
}
func createWritePromiseHandler() js.Value {
return createPromiseHandler(func(this js.Value, args []js.Value) {
bytesToRead := <-nextBytes
args[0].Invoke(bytesToRead)
})
}
func createPromiseHandler(cb func(this js.Value, args []js.Value)) js.Value {
return js.Global().Get("Promise").New(js.FuncOf(func(this js.Value, args []js.Value) any {
cb(this, args)
return nil
}))
}
func getGlobalPrefix() string {
return "bao_" + strconv.FormatInt(int64(callbackId), 10)
}
func getWritePromiseName() string {
return getGlobalPrefix() + "_write_promise"
}
func resetWritePromise() {
setGlobalObject(getWritePromiseName(), createWritePromiseHandler())
}
func setGlobalObject(name string, p any) {
js.Global().Set(name, p)
}

View File

@ -1,2 +1,2 @@
export * from "./client.js"; export * from "./client.js";
export * from "./verify.js"; export * from "./cid.js";

View File

@ -1,177 +0,0 @@
// @ts-ignore
import baoWasm from "./wasm.js";
import Go from "./go_wasm.js";
export async function getVerifiableStream(
root: Uint8Array,
proof: Uint8Array,
data: ReadableStream,
) {
const wasm = await getWasmInstance();
// @ts-ignore
const reader = new VariableChunkStream(data);
let bytesToRead;
if (root instanceof ArrayBuffer) {
root = new Uint8Array(root);
}
if (proof instanceof ArrayBuffer) {
proof = new Uint8Array(proof);
}
const getNextBytes = async () => {
bytesToRead = getWasmProperty(wasmId, "write_promise");
bytesToRead = await bytesToRead;
};
const callExports = (name: string) => {
// @ts-ignore
return wasm.exports[name]();
};
// @ts-ignore
const exit = () => {
callExports("kill");
cleanup();
};
const done = (controller: ReadableStreamDefaultController) => {
controller.close();
exit();
};
const cleanup = () => {
const win = getWin();
const props = Object.getOwnPropertyNames(win);
props
.filter((item) => item.startsWith(`bao_${wasmId}`))
.forEach((item) => {
delete win[item];
});
};
// @ts-ignore
const wasmId = callExports("start");
getWasmProperty(wasmId, "set_root")(root);
getWasmProperty(wasmId, "set_proof")(proof);
await getNextBytes();
return new ReadableStream({
async pull(controller) {
let chunk;
try {
chunk = await reader.read(bytesToRead);
if (chunk.value) {
getWasmProperty(wasmId, "write")(chunk.value);
}
} catch (e) {
// @ts-ignore
exit();
controller.error(e);
}
const result = getWasmProperty(wasmId, "result");
const wasmDone = result !== undefined;
if (!wasmDone) {
await getNextBytes();
}
if (chunk.done || wasmDone) {
if (wasmDone) {
if (result) {
controller.enqueue(chunk.value);
done(controller);
} else {
controller.error(getWasmProperty(wasmId, "error"));
exit();
}
} else {
if (!wasmDone) {
controller.error("stream is ended but verification not complete");
exit();
return;
}
controller.enqueue(chunk.value);
done(controller);
}
} else {
controller.enqueue(chunk.value);
}
},
async cancel(reason: any) {
await reader.cancel(reason);
exit();
},
});
}
function getWin() {
return globalThis || self || window;
}
function getWasmProperty(id: number, prop: string) {
return getWin()[`bao_${id}_${prop}`];
}
async function getWasmInstance() {
const go = new Go();
let wasm = (await baoWasm(
go.importObject,
)) as WebAssembly.WebAssemblyInstantiatedSource;
go.run(wasm);
return wasm;
}
class VariableChunkStream {
private reader: ReadableStreamDefaultReader;
private currentChunk: Uint8Array = new Uint8Array();
private currentChunkSize = 0;
private readerDone = false;
constructor(stream: ReadableStream) {
this.reader = stream.getReader();
}
async read(bytes: number) {
if (this.currentChunk.length === 0 && !this.readerDone) {
const { done, value } = await this.reader.read();
if (done) {
return { done: true };
}
this.currentChunk = value;
this.currentChunkSize = this.currentChunk.length;
}
if (this.currentChunkSize > bytes) {
const chunk = this.currentChunk.slice(0, bytes);
this.currentChunk = this.currentChunk.slice(bytes);
this.currentChunkSize -= bytes;
return { value: chunk, done: false };
}
if (this.currentChunkSize < bytes && !this.readerDone) {
const { done, value } = await this.reader.read();
if (done) {
this.readerDone = true;
}
this.currentChunk = new Uint8Array([...this.currentChunk, ...value]);
this.currentChunkSize += value.length;
return this.read(bytes);
}
const chunk = this.currentChunk;
this.currentChunk = new Uint8Array();
this.currentChunkSize = 0;
return { value: chunk, done: this.readerDone };
}
async cancel(reason: any) {
await this.reader.cancel(reason);
this.currentChunk = new Uint8Array();
this.currentChunkSize = 0;
}
}

View File

@ -1,17 +0,0 @@
import isNode from "detect-node";
export default async function (imports) {
if (isNode) {
const fs = await import("fs/promises");
// @ts-ignore
const wasmPath = new URL("wasm/bao.wasm", import.meta.url);
const wasm = await fs.readFile(wasmPath);
return (await WebAssembly.instantiate(wasm, imports)).instance;
}
// @ts-ignore
let wasm = await import("./wasm/bao.wasm?init");
wasm = wasm.default || wasm;
wasm = await wasm(imports);
return wasm;
}