Compare commits
No commits in common. "develop" and "master" have entirely different histories.
|
@ -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
|
|
@ -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
|
|
|
@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
130
CHANGELOG.md
130
CHANGELOG.md
|
@ -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))
|
|
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
193
src/client.ts
193
src/client.ts
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
663
src/go_wasm.ts
663
src/go_wasm.ts
|
@ -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 {};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
)
|
|
|
@ -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=
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -1,2 +1,2 @@
|
||||||
export * from "./client.js";
|
export * from "./client.js";
|
||||||
export * from "./verify.js";
|
export * from "./cid.js";
|
||||||
|
|
177
src/verify.ts
177
src/verify.ts
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
17
src/wasm.ts
17
src/wasm.ts
|
@ -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;
|
|
||||||
}
|
|
Loading…
Reference in New Issue