Compare commits
220 Commits
Author | SHA1 | Date |
---|---|---|
semantic-release-bot | 2d3f0bab32 | |
Derrick Hammer | fa8b48f94f | |
Derrick Hammer | 8970a013e3 | |
semantic-release-bot | 7d78bbf220 | |
Derrick Hammer | 92feec768c | |
Derrick Hammer | 2bc233c6b4 | |
Derrick Hammer | 98a6b48776 | |
semantic-release-bot | 12c9602f46 | |
Derrick Hammer | 2345f81227 | |
semantic-release-bot | a269ac9c2a | |
Derrick Hammer | 19984d2d63 | |
Derrick Hammer | 0fa655bb93 | |
semantic-release-bot | 3efce0666d | |
Derrick Hammer | 4a90d7da6f | |
Derrick Hammer | 78c142a5c3 | |
semantic-release-bot | df86413ee2 | |
Derrick Hammer | e9372e4b92 | |
Derrick Hammer | 1d4c950ae5 | |
semantic-release-bot | 6515702372 | |
Derrick Hammer | 390a57376f | |
Derrick Hammer | ab1ad68fcb | |
semantic-release-bot | 878fe81c32 | |
Derrick Hammer | dbbd4c16ec | |
Derrick Hammer | c699db423c | |
semantic-release-bot | 8748b49f21 | |
Derrick Hammer | 50bcbc5775 | |
Derrick Hammer | bc70a981f4 | |
semantic-release-bot | 8a8cd83e79 | |
Derrick Hammer | fdea1bfdce | |
Derrick Hammer | 9418bd2f67 | |
semantic-release-bot | 73842ec263 | |
Derrick Hammer | ce62654b85 | |
Derrick Hammer | 060e092fb2 | |
Derrick Hammer | 794036c667 | |
Derrick Hammer | 9bc7fda440 | |
Derrick Hammer | 841e453a8c | |
Derrick Hammer | e30747945c | |
Derrick Hammer | d349e454e5 | |
Derrick Hammer | 2894e63aa4 | |
Derrick Hammer | cc247a350e | |
Derrick Hammer | aef5a40a43 | |
Derrick Hammer | 40eedf2817 | |
Derrick Hammer | 282c431021 | |
Derrick Hammer | 68cb638086 | |
Derrick Hammer | 6ef11e9a27 | |
Derrick Hammer | 3f1dc5304c | |
Derrick Hammer | db50583dc2 | |
Derrick Hammer | 2c6b8438e1 | |
Derrick Hammer | 623bbf3235 | |
Derrick Hammer | 84a34496cd | |
Derrick Hammer | c882bb341b | |
Derrick Hammer | aca55ba230 | |
Derrick Hammer | d83d4892cc | |
Derrick Hammer | 885aaadd4f | |
Derrick Hammer | a8f2eb666b | |
Derrick Hammer | 2300b07419 | |
Derrick Hammer | 6a285b54f1 | |
semantic-release-bot | c2b0bced6d | |
Derrick Hammer | 6da5ae59e3 | |
Derrick Hammer | 21bbb9f67c | |
semantic-release-bot | 823ecaf600 | |
Derrick Hammer | 49592463f6 | |
Derrick Hammer | 108ab86fcc | |
semantic-release-bot | a04d117ff5 | |
Derrick Hammer | d096b7d62f | |
Derrick Hammer | 705590c7c5 | |
semantic-release-bot | 343bc2eae0 | |
Derrick Hammer | 9820f7ac0f | |
Derrick Hammer | 880a70f153 | |
semantic-release-bot | af58582550 | |
Derrick Hammer | 1db8cb7427 | |
Derrick Hammer | 2c56658a6c | |
semantic-release-bot | 45738130e0 | |
Derrick Hammer | f74ee39b9a | |
Derrick Hammer | 34e418b47e | |
semantic-release-bot | cfe89058c3 | |
Derrick Hammer | c657964cba | |
Derrick Hammer | 8451974d6e | |
semantic-release-bot | d3b8e086ce | |
Derrick Hammer | f85a2443d3 | |
Derrick Hammer | 07a451ce5e | |
semantic-release-bot | d7a670c5ea | |
Derrick Hammer | 043f1657ab | |
Derrick Hammer | 7ce52cbff7 | |
Derrick Hammer | 7dc8067e95 | |
semantic-release-bot | ca002262c6 | |
Derrick Hammer | c1913e7ba5 | |
Derrick Hammer | 2ee2978f20 | |
semantic-release-bot | bb3eb60761 | |
Derrick Hammer | d00db820cd | |
Derrick Hammer | f1c4f761d8 | |
semantic-release-bot | 8e0adbfb0f | |
Derrick Hammer | 700289b71f | |
Derrick Hammer | 698df86f80 | |
semantic-release-bot | b64ce75a70 | |
Derrick Hammer | 46559e7cbd | |
Derrick Hammer | 22ccc91566 | |
semantic-release-bot | c3c9dc3e66 | |
Derrick Hammer | 2a4a246799 | |
Derrick Hammer | b2007a6c81 | |
semantic-release-bot | 42421458b2 | |
Derrick Hammer | 7e61534c7b | |
Derrick Hammer | 8351e27c98 | |
semantic-release-bot | da6f3dbf92 | |
Derrick Hammer | 150541e98a | |
Derrick Hammer | 03116bfab4 | |
semantic-release-bot | 0aacc426f7 | |
Derrick Hammer | eabfb4eb1d | |
Derrick Hammer | bb0c8de24e | |
semantic-release-bot | 66342b98c4 | |
Derrick Hammer | dd296e9e50 | |
Derrick Hammer | 8162949a8a | |
semantic-release-bot | 9b7c8f1e82 | |
Derrick Hammer | 63e73ced76 | |
Derrick Hammer | 7fefaf0818 | |
semantic-release-bot | 243422a36a | |
Derrick Hammer | 62b7b0629e | |
Derrick Hammer | a1b67e71cd | |
semantic-release-bot | 85cbf2afe5 | |
Derrick Hammer | 872fab5455 | |
Derrick Hammer | 747aeb7d2e | |
semantic-release-bot | b12dd9a3b8 | |
Derrick Hammer | 9d153cde40 | |
Derrick Hammer | 0ab6cf0ff0 | |
semantic-release-bot | e470b52c9c | |
Derrick Hammer | 842383a5db | |
Derrick Hammer | 76133523d7 | |
semantic-release-bot | cb72915e49 | |
Derrick Hammer | faac04a13f | |
Derrick Hammer | 7f9990c2ea | |
semantic-release-bot | a19f7dcf22 | |
Derrick Hammer | 71c7a05a85 | |
Derrick Hammer | d1c26f8617 | |
Derrick Hammer | 4fe84e8ab4 | |
Derrick Hammer | 1e22782223 | |
semantic-release-bot | 88a6444c7c | |
Derrick Hammer | d10abaf31d | |
Derrick Hammer | efb29124a8 | |
semantic-release-bot | eb31a67d8a | |
Derrick Hammer | fce742c991 | |
Derrick Hammer | 50d7465eb1 | |
semantic-release-bot | f341f0a173 | |
Derrick Hammer | b4b13c6db0 | |
Derrick Hammer | 361ffaa73c | |
semantic-release-bot | 00397d37c1 | |
Derrick Hammer | d61ee47ada | |
Derrick Hammer | 5bb0393d96 | |
semantic-release-bot | a82d22454a | |
Derrick Hammer | 57d8aca7f1 | |
Derrick Hammer | 3ea6fa8bb0 | |
Derrick Hammer | 0fead1c786 | |
Derrick Hammer | 903823702c | |
semantic-release-bot | 682eeb15f6 | |
Derrick Hammer | 173a6cfd95 | |
semantic-release-bot | 00ad1be917 | |
Derrick Hammer | 61c535d8cb | |
Derrick Hammer | 79273d263b | |
semantic-release-bot | e3d2605285 | |
Derrick Hammer | 2d9167fe33 | |
Derrick Hammer | b1892ae0ce | |
semantic-release-bot | 1c17234025 | |
Derrick Hammer | 381abb7486 | |
Derrick Hammer | 42d52444c8 | |
Derrick Hammer | a813ee35f3 | |
semantic-release-bot | 0c26f2cedc | |
Derrick Hammer | 7126203cd3 | |
Derrick Hammer | b406ea60bb | |
Derrick Hammer | c4919e50ef | |
Derrick Hammer | b772aad66f | |
semantic-release-bot | e2a533ccb0 | |
Derrick Hammer | 56a990fe07 | |
Derrick Hammer | 887e79f989 | |
Derrick Hammer | dda05f4cad | |
Derrick Hammer | c4ec2b8a38 | |
Derrick Hammer | d551ad424e | |
semantic-release-bot | c4b1909c2b | |
Derrick Hammer | e5aa234333 | |
Derrick Hammer | ac822fb939 | |
semantic-release-bot | a942a11f63 | |
Derrick Hammer | 0d042f7867 | |
Derrick Hammer | 9d7ab9f395 | |
Derrick Hammer | 6d2311b9da | |
semantic-release-bot | 5ed6169931 | |
Derrick Hammer | 530b159c96 | |
semantic-release-bot | 7e0a182131 | |
Derrick Hammer | c4a4532425 | |
Derrick Hammer | b6e30e1645 | |
semantic-release-bot | 893322c40a | |
Derrick Hammer | 5462cf345b | |
Derrick Hammer | 80be9e6431 | |
semantic-release-bot | 40493fbcc0 | |
Derrick Hammer | ac38feae92 | |
Derrick Hammer | 89d24393e5 | |
semantic-release-bot | 34699434e4 | |
Derrick Hammer | 5db121774e | |
Derrick Hammer | 97df324828 | |
Derrick Hammer | d8e2046ebd | |
semantic-release-bot | 5760148afc | |
Derrick Hammer | beb60c23b3 | |
Derrick Hammer | 8b7f7082e3 | |
Derrick Hammer | ca43e88300 | |
semantic-release-bot | 49773456f5 | |
Derrick Hammer | 2890e908d5 | |
Derrick Hammer | bd4f10ad61 | |
semantic-release-bot | acd9503898 | |
Derrick Hammer | 80c6a92923 | |
Derrick Hammer | 100a9f2d6c | |
Derrick Hammer | 7dd6f9f0b0 | |
semantic-release-bot | 2b9563425d | |
Derrick Hammer | 5bb6c99f4c | |
semantic-release-bot | ec221ce2a7 | |
Derrick Hammer | a27bb31336 | |
Derrick Hammer | e2bcb8cf3d | |
semantic-release-bot | 39cb79980a | |
Derrick Hammer | 4f2ec806e1 | |
semantic-release-bot | 7dcb741e46 | |
Derrick Hammer | 738c3f12cf | |
Derrick Hammer | 381a892fae | |
Derrick Hammer | 7c07211356 | |
Derrick Hammer | d340447aba |
|
@ -1,45 +0,0 @@
|
|||
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
|
|
@ -0,0 +1,13 @@
|
|||
name: Build/Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- develop-*
|
||||
|
||||
jobs:
|
||||
main:
|
||||
uses: lumeweb/github-node-deploy-workflow/.github/workflows/main.yml@master
|
||||
secrets: inherit
|
|
@ -1,18 +1,3 @@
|
|||
{
|
||||
"preset": "presetter-preset-strict",
|
||||
"config": {
|
||||
"tsconfig": {
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"ES2021"
|
||||
]
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"singleQuote": false
|
||||
}
|
||||
},
|
||||
"variable": {
|
||||
"source": "src"
|
||||
}
|
||||
"preset": "@lumeweb/node-library-preset"
|
||||
}
|
||||
|
|
22
.releaserc
22
.releaserc
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"plugins": [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
[
|
||||
"@semantic-release/changelog"
|
||||
],
|
||||
"@semantic-release/npm",
|
||||
"@semantic-release/git",
|
||||
],
|
||||
"branches": [
|
||||
"master",
|
||||
{
|
||||
name: "develop",
|
||||
prerelease: true
|
||||
},
|
||||
{
|
||||
name: "develop-*",
|
||||
prerelease: true
|
||||
},
|
||||
]
|
||||
}
|
321
CHANGELOG.md
321
CHANGELOG.md
|
@ -1,3 +1,324 @@
|
|||
# [0.2.0-develop.61](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.60...v0.2.0-develop.61) (2023-11-17)
|
||||
|
||||
# [0.2.0-develop.60](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.59...v0.2.0-develop.60) (2023-10-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add savePortalSessions function ([2bc233c](https://git.lumeweb.com/LumeWeb/libweb/commit/2bc233c6b4a8abf5895014a4b8bc819113d763b5))
|
||||
|
||||
# [0.2.0-develop.59](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.58...v0.2.0-develop.59) (2023-09-20)
|
||||
|
||||
# [0.2.0-develop.58](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.57...v0.2.0-develop.58) (2023-09-11)
|
||||
|
||||
# [0.2.0-develop.57](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.56...v0.2.0-develop.57) (2023-09-09)
|
||||
|
||||
# [0.2.0-develop.56](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.55...v0.2.0-develop.56) (2023-09-09)
|
||||
|
||||
# [0.2.0-develop.55](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.54...v0.2.0-develop.55) (2023-09-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* need to use CID.decode ([ab1ad68](https://git.lumeweb.com/LumeWeb/libweb/commit/ab1ad68fcb7b2d08842a04ed4c411970f93cfbb4))
|
||||
|
||||
# [0.2.0-develop.54](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.53...v0.2.0-develop.54) (2023-09-08)
|
||||
|
||||
# [0.2.0-develop.53](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.52...v0.2.0-develop.53) (2023-09-08)
|
||||
|
||||
# [0.2.0-develop.52](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.51...v0.2.0-develop.52) (2023-09-08)
|
||||
|
||||
# [0.2.0-develop.51](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.50...v0.2.0-develop.51) (2023-09-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* move NO_PORTALS_ERROR to types and re-import ([2c6b843](https://git.lumeweb.com/LumeWeb/libweb/commit/2c6b8438e1cf7b72296af90ddb1bdd767bfda231))
|
||||
* remove export ([9bc7fda](https://git.lumeweb.com/LumeWeb/libweb/commit/9bc7fda44049e129ade3c28fe92d61a290e2d630))
|
||||
* use toRegistryEntry of CID ([841e453](https://git.lumeweb.com/LumeWeb/libweb/commit/841e453a8cbc1ef809e0a2d9eccc64c3ee9aaa4e))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add deriveBlakeChildKeyInt ([282c431](https://git.lumeweb.com/LumeWeb/libweb/commit/282c43102196426834f81253511ae7083756380f))
|
||||
* implement appdb, known as hidden db in s5 ([2894e63](https://git.lumeweb.com/LumeWeb/libweb/commit/2894e63aa46bbc66d1b8cd3c59d85f083fc713f5))
|
||||
|
||||
# [0.2.0-develop.50](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.49...v0.2.0-develop.50) (2023-09-04)
|
||||
|
||||
# [0.2.0-develop.49](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.48...v0.2.0-develop.49) (2023-09-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use hashType not type ([108ab86](https://git.lumeweb.com/LumeWeb/libweb/commit/108ab86fcc82cd22d394939e00ec8047eaaab901))
|
||||
|
||||
# [0.2.0-develop.48](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.47...v0.2.0-develop.48) (2023-09-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* pass along cid.type, dont force CID_TYPES.RESOLVER ([705590c](https://git.lumeweb.com/LumeWeb/libweb/commit/705590c7c57b18155752acac4bd8cba79bada742))
|
||||
|
||||
# [0.2.0-develop.47](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.46...v0.2.0-develop.47) (2023-09-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* encodeRegistryValue needs to use type and hashType ([880a70f](https://git.lumeweb.com/LumeWeb/libweb/commit/880a70f153f5d989cae0385c1111aff37feaf0c5))
|
||||
|
||||
# [0.2.0-develop.46](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.45...v0.2.0-develop.46) (2023-09-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* if has is a CID, override arguments with the CID properties unless we have an argument set already ([2c56658](https://git.lumeweb.com/LumeWeb/libweb/commit/2c56658a6c06c51e6f079232c35cce05a6ca72c3))
|
||||
|
||||
# [0.2.0-develop.45](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.44...v0.2.0-develop.45) (2023-09-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* return ret not cid ([34e418b](https://git.lumeweb.com/LumeWeb/libweb/commit/34e418b47e4e856bac34f6e5e63ae7f31a733aa6))
|
||||
|
||||
# [0.2.0-develop.44](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.43...v0.2.0-develop.44) (2023-09-04)
|
||||
|
||||
# [0.2.0-develop.43](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.42...v0.2.0-develop.43) (2023-09-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* if we have a Uint8Array but invalid CID_HASH_TYPES, return error ([07a451c](https://git.lumeweb.com/LumeWeb/libweb/commit/07a451ce5ecff302bef201b5f40e916e4420c278))
|
||||
|
||||
# [0.2.0-develop.42](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.41...v0.2.0-develop.42) (2023-09-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update encodeCid overloads and return types ([7dc8067](https://git.lumeweb.com/LumeWeb/libweb/commit/7dc8067e958bb62efe856cdb234ac0010b6d2896))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add encodeRegistryCid, encodeRegistryValue, decodeRegistryValue, decodeRegistryCid ([7ce52cb](https://git.lumeweb.com/LumeWeb/libweb/commit/7ce52cbff7e622a9046e6a36d4e664d211043e1e))
|
||||
|
||||
# [0.2.0-develop.41](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.40...v0.2.0-develop.41) (2023-09-03)
|
||||
|
||||
# [0.2.0-develop.40](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.39...v0.2.0-develop.40) (2023-09-03)
|
||||
|
||||
# [0.2.0-develop.39](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.38...v0.2.0-develop.39) (2023-09-03)
|
||||
|
||||
# [0.2.0-develop.38](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.37...v0.2.0-develop.38) (2023-09-02)
|
||||
|
||||
# [0.2.0-develop.37](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.36...v0.2.0-develop.37) (2023-09-02)
|
||||
|
||||
# [0.2.0-develop.36](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.35...v0.2.0-develop.36) (2023-09-02)
|
||||
|
||||
# [0.2.0-develop.35](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.34...v0.2.0-develop.35) (2023-09-02)
|
||||
|
||||
# [0.2.0-develop.34](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.33...v0.2.0-develop.34) (2023-09-02)
|
||||
|
||||
# [0.2.0-develop.33](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.32...v0.2.0-develop.33) (2023-09-02)
|
||||
|
||||
# [0.2.0-develop.32](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.31...v0.2.0-develop.32) (2023-08-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* change deriveChildKey to hkdf sha256 and create deriveBlakeChildKey that hashes based on the initial blake3 route used by s5 ([7fefaf0](https://git.lumeweb.com/LumeWeb/libweb/commit/7fefaf0818417f1cc4ae0c9f360e5b08734d68f3))
|
||||
|
||||
# [0.2.0-develop.31](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.30...v0.2.0-develop.31) (2023-08-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add loginActivePortals function ([a1b67e7](https://git.lumeweb.com/LumeWeb/libweb/commit/a1b67e71cd51003cae5f51ce927f6f9b6fa46ec6))
|
||||
|
||||
# [0.2.0-develop.30](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.29...v0.2.0-develop.30) (2023-08-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add new downloadSmallObject function that only hashes the data and compares it and does not verify it in real time ([747aeb7](https://git.lumeweb.com/LumeWeb/libweb/commit/747aeb7d2e34b702b764eae367f90b062aabc1f3))
|
||||
|
||||
# [0.2.0-develop.29](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.28...v0.2.0-develop.29) (2023-08-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle localStorage being undefined ([0ab6cf0](https://git.lumeweb.com/LumeWeb/libweb/commit/0ab6cf0ff095209f0a4d83c4295ecd5558de8a89))
|
||||
|
||||
# [0.2.0-develop.28](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.27...v0.2.0-develop.28) (2023-08-10)
|
||||
|
||||
# [0.2.0-develop.27](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.26...v0.2.0-develop.27) (2023-07-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* change to export type ([7f9990c](https://git.lumeweb.com/LumeWeb/libweb/commit/7f9990c2ea4157f83eb87c7c63555bc81db4732a))
|
||||
|
||||
# [0.2.0-develop.26](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.25...v0.2.0-develop.26) (2023-07-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add support for loading and saving portal lists ([4fe84e8](https://git.lumeweb.com/LumeWeb/libweb/commit/4fe84e8ab47d7fadeca685e51182e67f91d4c10f))
|
||||
|
||||
# [0.2.0-develop.25](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.24...v0.2.0-develop.25) (2023-07-18)
|
||||
|
||||
# [0.2.0-develop.24](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.23...v0.2.0-develop.24) (2023-07-18)
|
||||
|
||||
# [0.2.0-develop.23](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.22...v0.2.0-develop.23) (2023-07-18)
|
||||
|
||||
# [0.2.0-develop.22](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.21...v0.2.0-develop.22) (2023-07-08)
|
||||
|
||||
# [0.2.0-develop.21](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.20...v0.2.0-develop.21) (2023-07-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* export bufToHex ([0fead1c](https://git.lumeweb.com/LumeWeb/libweb/commit/0fead1c78628a049ff25b69cd586fb3d03b0e29e))
|
||||
|
||||
# [0.2.0-develop.20](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.19...v0.2.0-develop.20) (2023-06-29)
|
||||
|
||||
# [0.2.0-develop.19](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.18...v0.2.0-develop.19) (2023-06-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* switch to using globalThis, and check if we have localStorage (if not, might be node) ([79273d2](https://git.lumeweb.com/LumeWeb/libweb/commit/79273d263b9f65dbe4faf369fa936e8d746d81d2))
|
||||
|
||||
# [0.2.0-develop.18](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.17...v0.2.0-develop.18) (2023-06-26)
|
||||
|
||||
# [0.2.0-develop.17](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.16...v0.2.0-develop.17) (2023-06-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* re-export most of noble utility functions ([a813ee3](https://git.lumeweb.com/LumeWeb/libweb/commit/a813ee35f359a6f019d53b3bad0d9585dcc0d23e))
|
||||
|
||||
# [0.2.0-develop.16](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.15...v0.2.0-develop.16) (2023-06-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add initial upload method ([7126203](https://git.lumeweb.com/LumeWeb/libweb/commit/7126203cd3c97485de422e8759442a04074a8f64))
|
||||
|
||||
# [0.2.0-develop.15](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.14...v0.2.0-develop.15) (2023-06-25)
|
||||
|
||||
# [0.2.0-develop.14](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.13...v0.2.0-develop.14) (2023-06-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* switch to using @lumeweb/community-portals ([ac822fb](https://git.lumeweb.com/LumeWeb/libweb/commit/ac822fb9390cfc863a959dc09269479d73a71b80))
|
||||
|
||||
# [0.2.0-develop.13](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.12...v0.2.0-develop.13) (2023-06-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update libportal ([6d2311b](https://git.lumeweb.com/LumeWeb/libweb/commit/6d2311b9dab8b04df3e468b5b9f3932b9b18d0c7))
|
||||
* update npm-npm-shrinkwrap.json ([0d042f7](https://git.lumeweb.com/LumeWeb/libweb/commit/0d042f78673a61585683cdfc8518daa474da158e))
|
||||
|
||||
# [0.2.0-develop.12](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.11...v0.2.0-develop.12) (2023-06-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't skip portal if we fail to register ([530b159](https://git.lumeweb.com/LumeWeb/libweb/commit/530b159c96b5426c7c5c891c8773f6e9afb03685))
|
||||
|
||||
# [0.2.0-develop.11](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.10...v0.2.0-develop.11) (2023-06-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update libportal ([b6e30e1](https://git.lumeweb.com/LumeWeb/libweb/commit/b6e30e164584dbd4fadd48a1362d3cbbeb04ece7))
|
||||
|
||||
# [0.2.0-develop.10](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.9...v0.2.0-develop.10) (2023-06-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update libportal ([80be9e6](https://git.lumeweb.com/LumeWeb/libweb/commit/80be9e64316782d6aeb4f9c3245352ca7b65907d))
|
||||
|
||||
# [0.2.0-develop.9](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.8...v0.2.0-develop.9) (2023-06-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* refactor initPortal to return the instance ([89d2439](https://git.lumeweb.com/LumeWeb/libweb/commit/89d24393e5ec8c3d0846b00bec2912dd177e34a8))
|
||||
|
||||
# [0.2.0-develop.8](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.7...v0.2.0-develop.8) (2023-06-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* make getActivePortals return an array, not a set ([d8e2046](https://git.lumeweb.com/LumeWeb/libweb/commit/d8e2046ebda8b428cfcd5470d63a9c09a6047819))
|
||||
* need to use length, not size ([5db1217](https://git.lumeweb.com/LumeWeb/libweb/commit/5db121774ebf7c4eec4360a299cfb54991091747))
|
||||
|
||||
# [0.2.0-develop.7](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.6...v0.2.0-develop.7) (2023-06-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add portal to exports ([8b7f708](https://git.lumeweb.com/LumeWeb/libweb/commit/8b7f7082e3af84d8963ec535804b8816a1a396dc))
|
||||
* improve portal api ([ca43e88](https://git.lumeweb.com/LumeWeb/libweb/commit/ca43e883006a58d92eec519925a007c0c82c55c6))
|
||||
|
||||
# [0.2.0-develop.6](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.5...v0.2.0-develop.6) (2023-06-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update libportal ([bd4f10a](https://git.lumeweb.com/LumeWeb/libweb/commit/bd4f10ad619b218f801fc5ce31be28adbf97e584))
|
||||
|
||||
# [0.2.0-develop.5](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.4...v0.2.0-develop.5) (2023-06-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ensure we are using the export for @noble/hashes utils ([7dd6f9f](https://git.lumeweb.com/LumeWeb/libweb/commit/7dd6f9f0b0524b86e851add7e825b9dd0ac3a7c8))
|
||||
* update libportal ([100a9f2](https://git.lumeweb.com/LumeWeb/libweb/commit/100a9f2d6c75370ee909dd4fe6e574b19166d1f2))
|
||||
|
||||
# [0.2.0-develop.4](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.3...v0.2.0-develop.4) (2023-06-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update libportal ([5bb6c99](https://git.lumeweb.com/LumeWeb/libweb/commit/5bb6c99f4c16c13872886b259c27484e7d2c7fca))
|
||||
|
||||
# [0.2.0-develop.3](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.2...v0.2.0-develop.3) (2023-06-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add download to exports ([a27bb31](https://git.lumeweb.com/LumeWeb/libweb/commit/a27bb31336c41002780975edc0168ad70bc0a32a))
|
||||
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "fix: add download to exports" ([e2bcb8c](https://git.lumeweb.com/LumeWeb/libweb/commit/e2bcb8cf3d366f5f8dd27b3dfff7fb91de59e822))
|
||||
|
||||
# [0.2.0-develop.2](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.2.0-develop.1...v0.2.0-develop.2) (2023-06-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add download to exports ([4f2ec80](https://git.lumeweb.com/LumeWeb/libweb/commit/4f2ec806e1c46b23aaf3269f75a9fe2958e233fc))
|
||||
|
||||
# [0.2.0-develop.1](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.1.3...v0.2.0-develop.1) (2023-06-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* need to add dom support to TS ([738c3f1](https://git.lumeweb.com/LumeWeb/libweb/commit/738c3f12cfb49456fbcdd433b3f4bd30daa031b7))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add deriveChildKey function ([d7cdaaf](https://git.lumeweb.com/LumeWeb/libweb/commit/d7cdaaf316d4d26ed44860701376d18030030708))
|
||||
* add portal management apis ([d340447](https://git.lumeweb.com/LumeWeb/libweb/commit/d340447aba098dbac6163bfbacca0429323e6e45))
|
||||
* implement initial download method ([7c07211](https://git.lumeweb.com/LumeWeb/libweb/commit/7c07211356497ed36119d32943c46cb2e268b30f))
|
||||
|
||||
## [0.1.3](https://git.lumeweb.com/LumeWeb/libweb/compare/v0.1.2...v0.1.3) (2023-06-21)
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@lumeweb/libweb",
|
||||
"version": "0.1.3",
|
||||
"version": "0.2.0-develop.61",
|
||||
"main": "lib/index.js",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
|
@ -21,9 +21,13 @@
|
|||
"semantic-release": "semantic-release"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lumeweb/libportal": "^0.1.0",
|
||||
"@lumeweb/community-portals": "^0.1.0-develop.6",
|
||||
"@lumeweb/libportal": "0.2.0-develop.41",
|
||||
"@lumeweb/node-library-preset": "0.2.7",
|
||||
"@noble/ciphers": "^0.3.0",
|
||||
"@noble/curves": "^1.1.0",
|
||||
"@noble/hashes": "^1.3.1"
|
||||
"@noble/hashes": "^1.3.1",
|
||||
"binconv": "^0.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
import {
|
||||
CID,
|
||||
createKeyPair,
|
||||
S5Node,
|
||||
encryptionKeyDerivationTweak,
|
||||
pathKeyDerivationTweak,
|
||||
writeKeyDerivationTweak,
|
||||
} from "@lumeweb/libs5";
|
||||
import { deriveBlakeChildKey, deriveBlakeChildKeyInt } from "#keys.js";
|
||||
import { uploadObject } from "#upload.js";
|
||||
import { signRegistryEntry } from "@lumeweb/libs5/lib/service/registry.js";
|
||||
import { downloadSmallObject } from "#download.js";
|
||||
import { readableStreamToUint8Array } from "binconv";
|
||||
import { utf8ToBytes } from "@noble/hashes/utils";
|
||||
import { decryptMutableBytes, encryptMutableBytes } from "#encryption.js";
|
||||
|
||||
export default class AppDb {
|
||||
private readonly _rootKey: Uint8Array;
|
||||
private readonly _node: S5Node;
|
||||
private readonly _cidMap: Map<string, CID> = new Map<string, CID>();
|
||||
|
||||
constructor(rootKey: Uint8Array, api: S5Node) {
|
||||
this._rootKey = rootKey;
|
||||
this._node = api;
|
||||
}
|
||||
|
||||
public async getRawData(path: string): Promise<AppDbRawDataResponse> {
|
||||
const pathKey = this._derivePathKeyForPath(path);
|
||||
const encryptionKey = deriveBlakeChildKeyInt(
|
||||
pathKey,
|
||||
encryptionKeyDerivationTweak,
|
||||
);
|
||||
|
||||
const writeKey = deriveBlakeChildKeyInt(pathKey, writeKeyDerivationTweak);
|
||||
const keyPair = await createKeyPair(writeKey);
|
||||
|
||||
const sre = await this._node.services.registry.get(keyPair.publicKey);
|
||||
if (!sre) {
|
||||
return new AppDbRawDataResponse();
|
||||
}
|
||||
|
||||
const cid = CID.fromBytes(sre.data.slice(1));
|
||||
|
||||
const bytes = await downloadSmallObject(cid.toString());
|
||||
|
||||
const plaintext = await decryptMutableBytes(
|
||||
await readableStreamToUint8Array(bytes),
|
||||
encryptionKey,
|
||||
);
|
||||
|
||||
this._cidMap.set(path, cid);
|
||||
|
||||
return new AppDbRawDataResponse({
|
||||
data: plaintext,
|
||||
cid,
|
||||
revision: sre.revision,
|
||||
});
|
||||
}
|
||||
|
||||
public async setRawData(
|
||||
path: string,
|
||||
data: Uint8Array,
|
||||
revision: number,
|
||||
): Promise<void> {
|
||||
const pathKey = this._derivePathKeyForPath(path);
|
||||
const encryptionKey = deriveBlakeChildKeyInt(
|
||||
pathKey,
|
||||
encryptionKeyDerivationTweak,
|
||||
);
|
||||
|
||||
const cipherText = await encryptMutableBytes(data, encryptionKey);
|
||||
|
||||
const cid = await uploadObject(cipherText);
|
||||
const writeKey = deriveBlakeChildKeyInt(pathKey, writeKeyDerivationTweak);
|
||||
const keyPair = createKeyPair(writeKey);
|
||||
|
||||
const sre = await signRegistryEntry({
|
||||
kp: keyPair,
|
||||
data: cid.toRegistryEntry(),
|
||||
revision,
|
||||
});
|
||||
|
||||
await this._node.services.registry.set(sre);
|
||||
|
||||
this._cidMap.set(path, cid);
|
||||
}
|
||||
|
||||
public async getJSON(path: string): Promise<AppDbJSONResponse> {
|
||||
const res = await this.getRawData(path);
|
||||
|
||||
if (res.data === null) {
|
||||
return new AppDbJSONResponse({ cid: res.cid });
|
||||
}
|
||||
|
||||
const decodedData = JSON.parse(new TextDecoder().decode(res.data));
|
||||
return new AppDbJSONResponse({
|
||||
data: decodedData,
|
||||
revision: res.revision,
|
||||
cid: res.cid,
|
||||
});
|
||||
}
|
||||
|
||||
public async setJSON(
|
||||
path: string,
|
||||
data: any,
|
||||
revision: number,
|
||||
): Promise<void> {
|
||||
const encodedData = utf8ToBytes(JSON.stringify(data));
|
||||
await this.setRawData(path, new Uint8Array(encodedData), revision);
|
||||
}
|
||||
|
||||
private _derivePathKeyForPath(path: string): Uint8Array {
|
||||
const pathSegments = path
|
||||
.split("/")
|
||||
.map((e) => e.trim())
|
||||
.filter((element) => element.length > 0);
|
||||
|
||||
const key = this._deriveKeyForPathSegments(pathSegments);
|
||||
return deriveBlakeChildKeyInt(key, pathKeyDerivationTweak);
|
||||
}
|
||||
|
||||
private _deriveKeyForPathSegments(pathSegments: string[]): Uint8Array {
|
||||
if (pathSegments.length === 0) {
|
||||
return this._rootKey;
|
||||
}
|
||||
|
||||
return deriveBlakeChildKey(
|
||||
this._deriveKeyForPathSegments(
|
||||
pathSegments.slice(0, pathSegments.length - 1),
|
||||
),
|
||||
pathSegments[pathSegments.length - 1],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppDbRawDataResponse {
|
||||
data: Uint8Array | undefined;
|
||||
revision: number;
|
||||
cid: CID | undefined;
|
||||
|
||||
constructor({
|
||||
data,
|
||||
revision = -1,
|
||||
cid,
|
||||
}: {
|
||||
data?: Uint8Array;
|
||||
revision?: number;
|
||||
cid?: CID;
|
||||
} = {}) {
|
||||
this.data = data;
|
||||
this.revision = revision || -1;
|
||||
this.cid = cid;
|
||||
}
|
||||
}
|
||||
|
||||
class AppDbJSONResponse {
|
||||
data: any;
|
||||
revision: number;
|
||||
cid: CID | undefined;
|
||||
|
||||
constructor({
|
||||
data,
|
||||
revision = -1,
|
||||
cid,
|
||||
}: {
|
||||
data?: any;
|
||||
revision?: number;
|
||||
cid?: CID;
|
||||
} = {}) {
|
||||
this.data = data;
|
||||
this.revision = revision || -1;
|
||||
this.cid = cid;
|
||||
}
|
||||
}
|
||||
|
||||
export { AppDbRawDataResponse, AppDbJSONResponse };
|
35
src/cid.ts
35
src/cid.ts
|
@ -1,35 +0,0 @@
|
|||
import { ErrTuple } from "#types.js";
|
||||
import {
|
||||
decodeCid as decodeCidPortal,
|
||||
encodeCid as encodeCidPortal,
|
||||
} from "@lumeweb/libportal";
|
||||
import { addContextToErr } from "#err.js";
|
||||
|
||||
export function encodeCid(hash: Uint8Array, size: bigint): any;
|
||||
export function encodeCid(hash: string, size: bigint): any;
|
||||
export function encodeCid(hash: any, size: bigint): ErrTuple {
|
||||
try {
|
||||
return [encodeCidPortal(hash, size), null];
|
||||
} catch (e) {
|
||||
return [null, addContextToErr(e as Error, "failed to encode cid")];
|
||||
}
|
||||
}
|
||||
|
||||
export function decodeCid(cid: string): ErrTuple {
|
||||
try {
|
||||
return [decodeCidPortal(cid), null];
|
||||
} catch (e) {
|
||||
return [null, addContextToErr(e as Error, "failed to decode cid")];
|
||||
}
|
||||
}
|
||||
|
||||
export function verifyCid(cid: string): boolean {
|
||||
try {
|
||||
decodeCidPortal(cid);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export { CID } from "@lumeweb/libportal";
|
|
@ -1 +1,83 @@
|
|||
export function downloadObject(cid: string) {}
|
||||
import { getActivePortals } from "#portal.js";
|
||||
import { getVerifiableStream } from "@lumeweb/libportal";
|
||||
import {
|
||||
readableStreamToUint8Array,
|
||||
uint8ArrayToReadableStream,
|
||||
} from "binconv";
|
||||
import { equalBytes } from "@noble/curves/abstract/utils";
|
||||
import { blake3 } from "@noble/hashes/blake3";
|
||||
import { NO_PORTALS_ERROR } from "#types.js";
|
||||
import { CID } from "@lumeweb/libs5";
|
||||
|
||||
export async function downloadObject(cid: string): Promise<ReadableStream> {
|
||||
const activePortals = getActivePortals();
|
||||
|
||||
if (!activePortals.length) {
|
||||
throw NO_PORTALS_ERROR;
|
||||
}
|
||||
|
||||
for (const portal of activePortals) {
|
||||
if (!(await portal.isLoggedIn())) {
|
||||
try {
|
||||
await portal.register();
|
||||
} catch {}
|
||||
|
||||
await portal.login();
|
||||
}
|
||||
|
||||
let stream, proof;
|
||||
|
||||
try {
|
||||
stream = await portal.downloadFile(cid);
|
||||
proof = await portal.downloadProof(cid);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
return await getVerifiableStream(
|
||||
CID.decode(cid).hash.hashBytes,
|
||||
proof,
|
||||
stream,
|
||||
);
|
||||
}
|
||||
|
||||
throw NO_PORTALS_ERROR;
|
||||
}
|
||||
|
||||
export async function downloadSmallObject(
|
||||
cid: string,
|
||||
): Promise<ReadableStream<Uint8Array>> {
|
||||
const activePortals = getActivePortals();
|
||||
|
||||
if (!activePortals.length) {
|
||||
throw NO_PORTALS_ERROR;
|
||||
}
|
||||
|
||||
for (const portal of activePortals) {
|
||||
if (!(await portal.isLoggedIn())) {
|
||||
try {
|
||||
await portal.register();
|
||||
} catch {}
|
||||
|
||||
await portal.login();
|
||||
}
|
||||
|
||||
let stream;
|
||||
|
||||
try {
|
||||
stream = await portal.downloadFile(cid);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
const data = await readableStreamToUint8Array(stream);
|
||||
|
||||
if (!equalBytes(blake3(data), CID.decode(cid).hash.hashBytes)) {
|
||||
throw new Error("cid verification failed");
|
||||
}
|
||||
|
||||
return uint8ArrayToReadableStream(data);
|
||||
}
|
||||
|
||||
throw NO_PORTALS_ERROR;
|
||||
}
|
||||
|
|
130
src/encoding.ts
130
src/encoding.ts
|
@ -1,130 +0,0 @@
|
|||
import { addContextToErr } from "./err.js";
|
||||
import { Err } from "./types.js";
|
||||
|
||||
const MAX_UINT_64 = 18446744073709551615n;
|
||||
|
||||
// b64ToBuf will take an untrusted base64 string and convert it into a
|
||||
// Uin8Array, returning an error if the input is not valid base64.
|
||||
const b64regex = /^[0-9a-zA-Z-_/+=]*$/;
|
||||
function b64ToBuf(b64: string): [Uint8Array, Err] {
|
||||
// Check that the final string is valid base64.
|
||||
if (!b64regex.test(b64)) {
|
||||
return [new Uint8Array(0), "provided string is not valid base64"];
|
||||
}
|
||||
|
||||
// Swap any '-' characters for '+', and swap any '_' characters for '/'
|
||||
// for use in the atob function.
|
||||
b64 = b64.replaceAll("-", "+").replaceAll("_", "/");
|
||||
|
||||
// Perform the conversion.
|
||||
const binStr = atob(b64);
|
||||
const len = binStr.length;
|
||||
const buf = new Uint8Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
buf[i] = binStr.charCodeAt(i);
|
||||
}
|
||||
return [buf, null];
|
||||
}
|
||||
|
||||
// bufToHex takes a Uint8Array as input and returns the hex encoding of those
|
||||
// bytes as a string.
|
||||
function bufToHex(buf: Uint8Array): string {
|
||||
return [...buf].map((x) => x.toString(16).padStart(2, "0")).join("");
|
||||
}
|
||||
|
||||
// bufToB64 will convert a Uint8Array to a base64 string with URL encoding and
|
||||
// no padding characters.
|
||||
function bufToB64(buf: Uint8Array): string {
|
||||
const b64Str = btoa(String.fromCharCode(...buf));
|
||||
return b64Str.replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
||||
}
|
||||
|
||||
// bufToStr takes an ArrayBuffer as input and returns a text string. bufToStr
|
||||
// will check for invalid characters.
|
||||
function bufToStr(buf: ArrayBuffer): [string, Err] {
|
||||
try {
|
||||
const text = new TextDecoder("utf-8", { fatal: true }).decode(buf);
|
||||
return [text, null];
|
||||
} catch (err: any) {
|
||||
return [
|
||||
"",
|
||||
addContextToErr(err.toString(), "unable to decode ArrayBuffer to string"),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// decodeU64 is the opposite of encodeU64, it takes a uint64 encoded as 8 bytes
|
||||
// and decodes them into a BigInt.
|
||||
function decodeU64(u8: Uint8Array): [bigint, Err] {
|
||||
// Check the input.
|
||||
if (u8.length !== 8) {
|
||||
return [0n, "input should be 8 bytes"];
|
||||
}
|
||||
|
||||
// Process the input.
|
||||
let num = 0n;
|
||||
for (let i = u8.length - 1; i >= 0; i--) {
|
||||
num *= 256n;
|
||||
num += BigInt(u8[i]);
|
||||
}
|
||||
return [num, null];
|
||||
}
|
||||
|
||||
// encodeU64 will encode a bigint in the range of a uint64 to an 8 byte
|
||||
// Uint8Array.
|
||||
function encodeU64(num: bigint): [Uint8Array, Err] {
|
||||
// Check the bounds on the bigint.
|
||||
if (num < 0) {
|
||||
return [new Uint8Array(0), "expected a positive integer"];
|
||||
}
|
||||
if (num > MAX_UINT_64) {
|
||||
return [new Uint8Array(0), "expected a number no larger than a uint64"];
|
||||
}
|
||||
|
||||
// Encode the bigint into a Uint8Array.
|
||||
const encoded = new Uint8Array(8);
|
||||
for (let i = 0; i < encoded.length; i++) {
|
||||
encoded[i] = Number(num & 0xffn);
|
||||
num = num >> 8n;
|
||||
}
|
||||
return [encoded, null];
|
||||
}
|
||||
|
||||
// hexToBuf takes an untrusted string as input, verifies that the string is
|
||||
// valid hex, and then converts the string to a Uint8Array.
|
||||
const allHex = /^[0-9a-f]+$/i;
|
||||
function hexToBuf(hex: string): [Uint8Array, Err] {
|
||||
// The rest of the code doesn't handle zero length input well, so we handle
|
||||
// that separately. It's not an error, we just return an empty array.
|
||||
if (hex.length === 0) {
|
||||
return [new Uint8Array(0), null];
|
||||
}
|
||||
|
||||
// Check that the length makes sense.
|
||||
if (hex.length % 2 !== 0) {
|
||||
return [new Uint8Array(0), "input has incorrect length"];
|
||||
}
|
||||
|
||||
// Check that all of the characters are legal.
|
||||
if (!allHex.test(hex)) {
|
||||
return [new Uint8Array(0), "input has invalid character"];
|
||||
}
|
||||
|
||||
// Create the buffer and fill it.
|
||||
const matches = hex.match(/.{2}/g);
|
||||
if (matches === null) {
|
||||
return [new Uint8Array(0), "input is incomplete"];
|
||||
}
|
||||
const u8 = new Uint8Array(matches.map((byte) => parseInt(byte, 16)));
|
||||
return [u8, null];
|
||||
}
|
||||
|
||||
export {
|
||||
b64ToBuf,
|
||||
bufToHex,
|
||||
bufToB64,
|
||||
bufToStr,
|
||||
decodeU64,
|
||||
encodeU64,
|
||||
hexToBuf,
|
||||
};
|
|
@ -0,0 +1,109 @@
|
|||
import {
|
||||
decodeEndian,
|
||||
encodeEndian,
|
||||
encryptionKeyLength,
|
||||
encryptionNonceLength,
|
||||
encryptionOverheadLength,
|
||||
} from "@lumeweb/libs5";
|
||||
|
||||
import { xchacha20poly1305 } from "@noble/ciphers/chacha";
|
||||
import { randomBytes } from "@noble/ciphers/webcrypto/utils";
|
||||
|
||||
function padFileSizeDefault(initialSize: number): number {
|
||||
const kib = 1 << 10;
|
||||
// Only iterate to 53 (the maximum safe power of 2).
|
||||
for (let n = 0; n < 53; n++) {
|
||||
if (initialSize <= (1 << n) * 80 * kib) {
|
||||
const paddingBlock = (1 << n) * 4 * kib;
|
||||
let finalSize = initialSize;
|
||||
if (finalSize % paddingBlock !== 0) {
|
||||
finalSize = initialSize - (initialSize % paddingBlock) + paddingBlock;
|
||||
}
|
||||
return finalSize;
|
||||
}
|
||||
}
|
||||
// Prevent overflow.
|
||||
throw new Error("Could not pad file size, overflow detected.");
|
||||
}
|
||||
|
||||
function checkPaddedBlock(size: number): boolean {
|
||||
const kib = 1024;
|
||||
// Only iterate to 53 (the maximum safe power of 2).
|
||||
for (let n = 0; n < 53; n++) {
|
||||
if (size <= (1 << n) * 80 * kib) {
|
||||
const paddingBlock = (1 << n) * 4 * kib;
|
||||
return size % paddingBlock === 0;
|
||||
}
|
||||
}
|
||||
throw new Error("Could not check padded file size, overflow detected.");
|
||||
}
|
||||
|
||||
async function encryptMutableBytes(
|
||||
data: Uint8Array,
|
||||
key: Uint8Array,
|
||||
): Promise<Uint8Array> {
|
||||
const lengthInBytes = encodeEndian(data.length, 4);
|
||||
|
||||
const totalOverhead =
|
||||
encryptionOverheadLength + 4 + encryptionNonceLength + 2;
|
||||
|
||||
const finalSize =
|
||||
padFileSizeDefault(data.length + totalOverhead) - totalOverhead;
|
||||
|
||||
data = new Uint8Array([
|
||||
...lengthInBytes,
|
||||
...data,
|
||||
...new Uint8Array(finalSize - data.length),
|
||||
]);
|
||||
|
||||
const nonce = randomBytes(encryptionNonceLength);
|
||||
|
||||
const header = new Uint8Array([0x8d, 0x01, ...nonce]);
|
||||
|
||||
const stream_x = xchacha20poly1305(key, nonce);
|
||||
|
||||
const encryptedBytes = stream_x.encrypt(data);
|
||||
|
||||
return new Uint8Array([...header, ...encryptedBytes]);
|
||||
}
|
||||
|
||||
async function decryptMutableBytes(
|
||||
data: Uint8Array,
|
||||
key: Uint8Array,
|
||||
): Promise<Uint8Array> {
|
||||
if (key.length !== encryptionKeyLength) {
|
||||
throw `wrong encryptionKeyLength (${key.length} != ${encryptionKeyLength})`;
|
||||
}
|
||||
|
||||
if (!checkPaddedBlock(data.length)) {
|
||||
throw `Expected parameter 'data' to be padded encrypted data, length was '${
|
||||
data.length
|
||||
}', nearest padded block is '${padFileSizeDefault(data.length)}'`;
|
||||
}
|
||||
|
||||
const version = data[1];
|
||||
if (version !== 0x01) {
|
||||
throw "Invalid version";
|
||||
}
|
||||
|
||||
const nonce = data.subarray(2, encryptionNonceLength + 2);
|
||||
|
||||
const stream_x = xchacha20poly1305(key, nonce);
|
||||
|
||||
const decryptedBytes = stream_x.decrypt(
|
||||
data.subarray(encryptionNonceLength + 2),
|
||||
);
|
||||
|
||||
const lengthInBytes = decryptedBytes.subarray(0, 4);
|
||||
|
||||
const length = decodeEndian(lengthInBytes);
|
||||
|
||||
return decryptedBytes.subarray(4, length + 4);
|
||||
}
|
||||
|
||||
export {
|
||||
padFileSizeDefault,
|
||||
checkPaddedBlock,
|
||||
decryptMutableBytes,
|
||||
encryptMutableBytes,
|
||||
};
|
16
src/err.ts
16
src/err.ts
|
@ -1,16 +0,0 @@
|
|||
import { objAsString } from "./objAsString.js";
|
||||
|
||||
// addContextToErr is a helper function that standardizes the formatting of
|
||||
// adding context to an error.
|
||||
//
|
||||
// NOTE: To protect against accidental situations where an Error type or some
|
||||
// other type is provided instead of a string, we wrap both of the inputs with
|
||||
// objAsString before returning them. This prevents runtime failures.
|
||||
function addContextToErr(err: any, context: string): string {
|
||||
if (err === null || err === undefined) {
|
||||
err = "[no error provided]";
|
||||
}
|
||||
return objAsString(context) + ": " + objAsString(err);
|
||||
}
|
||||
|
||||
export { addContextToErr };
|
|
@ -1,101 +0,0 @@
|
|||
// errTracker.ts defines an 'ErrTracker' type which keeps track of historical
|
||||
// errors. When the number of errors gets too large, it randomly starts pruning
|
||||
// errors. It always keeps 250 of the most recent errors, and then keeps up to
|
||||
// 500 historic errors, where the first few errors after runtime are always
|
||||
// kept, and the ones in the middle are increasingly likely to be omitted from
|
||||
// the history.
|
||||
|
||||
import { Err } from "./types.js";
|
||||
|
||||
// MAX_ERRORS defines the maximum number of errors that will be held in the
|
||||
// HistoricErr object.
|
||||
const MAX_ERRORS = 1000;
|
||||
|
||||
// HistoricErr is a wrapper that adds a date to the Err type.
|
||||
interface HistoricErr {
|
||||
err: Err;
|
||||
date: Date;
|
||||
}
|
||||
|
||||
// ErrTracker keeps track of errors that have happened, randomly dropping
|
||||
// errors to prevent the tracker from using too much memory if there happen to
|
||||
// be a large number of errors.
|
||||
interface ErrTracker {
|
||||
recentErrs: HistoricErr[];
|
||||
oldErrs: HistoricErr[];
|
||||
|
||||
addErr: (err: Err) => void;
|
||||
viewErrs: () => HistoricErr[];
|
||||
}
|
||||
|
||||
// newErrTracker returns an ErrTracker object that is ready to have errors
|
||||
// added to it.
|
||||
function newErrTracker(): ErrTracker {
|
||||
const et: ErrTracker = {
|
||||
recentErrs: [],
|
||||
oldErrs: [],
|
||||
|
||||
addErr: function (err: Err): void {
|
||||
addHistoricErr(et, err);
|
||||
},
|
||||
viewErrs: function (): HistoricErr[] {
|
||||
return viewErrs(et);
|
||||
},
|
||||
};
|
||||
return et;
|
||||
}
|
||||
|
||||
// addHistoricErr is a function that will add an error to a set of historic
|
||||
// errors. It uses randomness to prune errors once the error object is too
|
||||
// large.
|
||||
function addHistoricErr(et: ErrTracker, err: Err): void {
|
||||
// Add this error to the set of most recent errors.
|
||||
et.recentErrs.push({
|
||||
err,
|
||||
date: new Date(),
|
||||
});
|
||||
|
||||
// Determine whether some of the most recent errors need to be moved into
|
||||
// logTermErrs. If the length of the mostRecentErrs is not at least half of
|
||||
// the MAX_ERRORS, we don't need to do anything.
|
||||
if (et.recentErrs.length < MAX_ERRORS / 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate through the recentErrs. For the first half of the recentErrs, we
|
||||
// will use randomness to either toss them or move them to oldErrs. The
|
||||
// second half of the recentErrs will be kept as the new recentErrs array.
|
||||
const newRecentErrs : HistoricErr[] = [];
|
||||
for (let i = 0; i < et.recentErrs.length; i++) {
|
||||
// If we are in the second half of the array, add the element to
|
||||
// newRecentErrs.
|
||||
if (i > et.recentErrs.length / 2) {
|
||||
newRecentErrs.push(et.recentErrs[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We are in the first half of the array, use a random number to add the
|
||||
// error oldErrs probabilistically.
|
||||
const rand = Math.random();
|
||||
const target = et.oldErrs.length / (MAX_ERRORS / 2);
|
||||
if (rand > target || et.oldErrs.length < 25) {
|
||||
et.oldErrs.push(et.recentErrs[i]);
|
||||
}
|
||||
}
|
||||
et.recentErrs = newRecentErrs;
|
||||
}
|
||||
|
||||
// viewErrs returns the list of errors that have been retained by the
|
||||
// HistoricErr object.
|
||||
function viewErrs(et: ErrTracker): HistoricErr[] {
|
||||
const finalErrs: HistoricErr[] = [];
|
||||
for (let i = 0; i < et.oldErrs.length; i++) {
|
||||
finalErrs.push(et.oldErrs[i]);
|
||||
}
|
||||
for (let i = 0; i < et.recentErrs.length; i++) {
|
||||
finalErrs.push(et.recentErrs[i]);
|
||||
}
|
||||
return finalErrs;
|
||||
}
|
||||
|
||||
export { ErrTracker, HistoricErr, newErrTracker };
|
38
src/index.ts
38
src/index.ts
|
@ -1,13 +1,35 @@
|
|||
import { ed25519 } from "@noble/curves/ed25519";
|
||||
import { sha512 } from "@noble/hashes/sha512";
|
||||
|
||||
export * from "./err.js";
|
||||
export * from "./errTracker.js";
|
||||
export * from "./objAsString.js";
|
||||
export * from "./parse.js";
|
||||
export * from "./stringifyJSON.js";
|
||||
import AppDb from "#appDb.js";
|
||||
|
||||
export {
|
||||
bytesToHex,
|
||||
hexToBytes,
|
||||
toBytes,
|
||||
concatBytes,
|
||||
randomBytes,
|
||||
} from "@noble/hashes/utils";
|
||||
export {
|
||||
numberToHexUnpadded,
|
||||
hexToNumber,
|
||||
bytesToNumberBE,
|
||||
bytesToNumberLE,
|
||||
numberToBytesBE,
|
||||
numberToBytesLE,
|
||||
numberToVarBytesBE,
|
||||
ensureBytes,
|
||||
equalBytes,
|
||||
utf8ToBytes,
|
||||
bitLen,
|
||||
bitGet,
|
||||
bitSet,
|
||||
bitMask,
|
||||
} from "@noble/curves/abstract/utils";
|
||||
export * from "./types.js";
|
||||
export * from "./cid.js";
|
||||
export * from "./encoding.js";
|
||||
export * from "./keys.js";
|
||||
export { ed25519, sha512 };
|
||||
export * from "./download.js";
|
||||
export * from "./upload.js";
|
||||
export * from "./portal.js";
|
||||
export * from "./encryption.js";
|
||||
export { ed25519, sha512, AppDb };
|
||||
|
|
17
src/keys.ts
17
src/keys.ts
|
@ -1,11 +1,28 @@
|
|||
import { blake3 } from "@noble/hashes/blake3";
|
||||
import { concatBytes } from "@noble/hashes/utils";
|
||||
import { hkdf } from "@noble/hashes/hkdf";
|
||||
import { sha256 } from "@noble/hashes/sha256";
|
||||
import { encodeEndian } from "@lumeweb/libs5";
|
||||
|
||||
export function deriveChildKey(
|
||||
parentKey: Uint8Array,
|
||||
tweak: string,
|
||||
): Uint8Array {
|
||||
return hkdf(sha256, parentKey, undefined, tweak, 32);
|
||||
}
|
||||
|
||||
export function deriveBlakeChildKey(
|
||||
parentKey: Uint8Array,
|
||||
tweak: string,
|
||||
): Uint8Array {
|
||||
const tweakHash = blake3(tweak);
|
||||
|
||||
return blake3(concatBytes(parentKey, tweakHash));
|
||||
}
|
||||
|
||||
export function deriveBlakeChildKeyInt(
|
||||
parentKey: Uint8Array,
|
||||
tweak: number,
|
||||
): Uint8Array {
|
||||
return blake3(concatBytes(parentKey, encodeEndian(tweak, 32)));
|
||||
}
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
// objAsString will try to return the provided object as a string. If the
|
||||
// object is already a string, it will be returned without modification. If the
|
||||
// object is an 'Error', the message of the error will be returned. If the object
|
||||
// has a toString method, the toString method will be called and the result
|
||||
// will be returned. If the object is null or undefined, a special string will
|
||||
// be returned indicating that the undefined/null object cannot be converted to
|
||||
// a string. In all other cases, JSON.stringify is used. If JSON.stringify
|
||||
// throws an exception, a message "[could not provide object as string]" will
|
||||
// be returned.
|
||||
//
|
||||
// NOTE: objAsString is intended to produce human readable output. It is lossy,
|
||||
// and it is not intended to be used for serialization.
|
||||
function objAsString(obj: any): string {
|
||||
// Check for undefined input.
|
||||
if (obj === undefined) {
|
||||
return "[cannot convert undefined to string]";
|
||||
}
|
||||
if (obj === null) {
|
||||
return "[cannot convert null to string]";
|
||||
}
|
||||
|
||||
// Parse the error into a string.
|
||||
if (typeof obj === "string") {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Check if the object is an error, and return the message of the error if
|
||||
// so.
|
||||
if (obj instanceof Error) {
|
||||
return obj.message;
|
||||
}
|
||||
|
||||
// Check if the object has a 'toString' method defined on it. To ensure
|
||||
// that we don't crash or throw, check that the toString is a function, and
|
||||
// also that the return value of toString is a string.
|
||||
if (Object.prototype.hasOwnProperty.call(obj, "toString")) {
|
||||
if (typeof obj.toString === "function") {
|
||||
const str = obj.toString();
|
||||
if (typeof str === "string") {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the object does not have a custom toString, attempt to perform a
|
||||
// JSON.stringify. We use a lot of bigints in libskynet, and calling
|
||||
// JSON.stringify on an object with a bigint will cause a throw, so we add
|
||||
// some custom handling to allow bigint objects to still be encoded.
|
||||
try {
|
||||
return JSON.stringify(obj, (_, v) => {
|
||||
if (typeof v === "bigint") {
|
||||
return v.toString();
|
||||
}
|
||||
return v;
|
||||
});
|
||||
} catch (err: any) {
|
||||
if (err !== undefined && typeof err.message === "string") {
|
||||
return `[stringify failed]: ${err.message}`;
|
||||
}
|
||||
return "[stringify failed]";
|
||||
}
|
||||
}
|
||||
|
||||
export { objAsString };
|
376
src/parse.ts
376
src/parse.ts
|
@ -1,376 +0,0 @@
|
|||
// @ts-nocheck
|
||||
|
||||
import { objAsString } from "./objAsString.js";
|
||||
import { Err } from "./types.js";
|
||||
|
||||
// json_parse extracted from the json-bigint npm library
|
||||
// regexpxs extracted from
|
||||
// (c) BSD-3-Clause
|
||||
// https://github.com/fastify/secure-json-parse/graphs/contributors and https://github.com/hapijs/bourne/graphs/contributors
|
||||
const suspectProtoRx =
|
||||
/(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])/;
|
||||
const suspectConstructorRx =
|
||||
/(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)/;
|
||||
let json_parse = function (options) {
|
||||
"use strict";
|
||||
|
||||
// This is a function that can parse a JSON text, producing a JavaScript
|
||||
// data structure. It is a simple, recursive descent parser. It does not use
|
||||
// eval or regular expressions, so it can be used as a model for implementing
|
||||
// a JSON parser in other languages.
|
||||
|
||||
// We are defining the function inside of another function to avoid creating
|
||||
// global variables.
|
||||
|
||||
// Default options one can override by passing options to the parse()
|
||||
let _options = {
|
||||
strict: false, // not being strict means do not generate syntax errors for "duplicate key"
|
||||
storeAsString: false, // toggles whether the values should be stored as BigNumber (default) or a string
|
||||
alwaysParseAsBig: false, // toggles whether all numbers should be Big
|
||||
protoAction: "error",
|
||||
constructorAction: "error",
|
||||
};
|
||||
|
||||
// If there are options, then use them to override the default _options
|
||||
if (options !== undefined && options !== null) {
|
||||
if (options.strict === true) {
|
||||
_options.strict = true;
|
||||
}
|
||||
if (options.storeAsString === true) {
|
||||
_options.storeAsString = true;
|
||||
}
|
||||
_options.alwaysParseAsBig = options.alwaysParseAsBig === true ? options.alwaysParseAsBig : false;
|
||||
|
||||
if (typeof options.constructorAction !== "undefined") {
|
||||
if (
|
||||
options.constructorAction === "error" ||
|
||||
options.constructorAction === "ignore" ||
|
||||
options.constructorAction === "preserve"
|
||||
) {
|
||||
_options.constructorAction = options.constructorAction;
|
||||
} else {
|
||||
throw new Error(
|
||||
`Incorrect value for constructorAction option, must be "error", "ignore" or undefined but passed ${options.constructorAction}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof options.protoAction !== "undefined") {
|
||||
if (options.protoAction === "error" || options.protoAction === "ignore" || options.protoAction === "preserve") {
|
||||
_options.protoAction = options.protoAction;
|
||||
} else {
|
||||
throw new Error(
|
||||
`Incorrect value for protoAction option, must be "error", "ignore" or undefined but passed ${options.protoAction}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let at, // The index of the current character
|
||||
ch, // The current character
|
||||
escapee = {
|
||||
'"': '"',
|
||||
"\\": "\\",
|
||||
"/": "/",
|
||||
b: "\b",
|
||||
f: "\f",
|
||||
n: "\n",
|
||||
r: "\r",
|
||||
t: "\t",
|
||||
},
|
||||
text,
|
||||
error = function (m) {
|
||||
// Call error when something is wrong.
|
||||
|
||||
throw {
|
||||
name: "SyntaxError",
|
||||
message: m,
|
||||
at: at,
|
||||
text: text,
|
||||
};
|
||||
},
|
||||
next = function (c) {
|
||||
// If a c parameter is provided, verify that it matches the current character.
|
||||
|
||||
if (c && c !== ch) {
|
||||
error("Expected '" + c + "' instead of '" + ch + "'");
|
||||
}
|
||||
|
||||
// Get the next character. When there are no more characters,
|
||||
// return the empty string.
|
||||
|
||||
ch = text.charAt(at);
|
||||
at += 1;
|
||||
return ch;
|
||||
},
|
||||
number = function () {
|
||||
// Parse a number value.
|
||||
|
||||
let number,
|
||||
string = "";
|
||||
|
||||
if (ch === "-") {
|
||||
string = "-";
|
||||
next("-");
|
||||
}
|
||||
while (ch >= "0" && ch <= "9") {
|
||||
string += ch;
|
||||
next();
|
||||
}
|
||||
if (ch === ".") {
|
||||
string += ".";
|
||||
while (next() && ch >= "0" && ch <= "9") {
|
||||
string += ch;
|
||||
}
|
||||
}
|
||||
if (ch === "e" || ch === "E") {
|
||||
string += ch;
|
||||
next();
|
||||
if (ch === "-" || ch === "+") {
|
||||
string += ch;
|
||||
next();
|
||||
}
|
||||
while (ch >= "0" && ch <= "9") {
|
||||
string += ch;
|
||||
next();
|
||||
}
|
||||
}
|
||||
number = +string;
|
||||
if (!isFinite(number)) {
|
||||
error("Bad number");
|
||||
} else {
|
||||
if (Number.isSafeInteger(number)) return !_options.alwaysParseAsBig ? number : BigInt(number);
|
||||
// Number with fractional part should be treated as number(double) including big integers in scientific notation, i.e 1.79e+308
|
||||
else return _options.storeAsString ? string : /[.eE]/.test(string) ? number : BigInt(string);
|
||||
}
|
||||
},
|
||||
string = function () {
|
||||
// Parse a string value.
|
||||
|
||||
let hex,
|
||||
i,
|
||||
string = "",
|
||||
uffff;
|
||||
|
||||
// When parsing for string values, we must look for " and \ characters.
|
||||
|
||||
if (ch === '"') {
|
||||
let startAt = at;
|
||||
while (next()) {
|
||||
if (ch === '"') {
|
||||
if (at - 1 > startAt) string += text.substring(startAt, at - 1);
|
||||
next();
|
||||
return string;
|
||||
}
|
||||
if (ch === "\\") {
|
||||
if (at - 1 > startAt) string += text.substring(startAt, at - 1);
|
||||
next();
|
||||
if (ch === "u") {
|
||||
uffff = 0;
|
||||
for (i = 0; i < 4; i += 1) {
|
||||
hex = parseInt(next(), 16);
|
||||
if (!isFinite(hex)) {
|
||||
break;
|
||||
}
|
||||
uffff = uffff * 16 + hex;
|
||||
}
|
||||
string += String.fromCharCode(uffff);
|
||||
} else if (typeof escapee[ch] === "string") {
|
||||
string += escapee[ch];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
startAt = at;
|
||||
}
|
||||
}
|
||||
}
|
||||
error("Bad string");
|
||||
},
|
||||
white = function () {
|
||||
// Skip whitespace.
|
||||
|
||||
while (ch && ch <= " ") {
|
||||
next();
|
||||
}
|
||||
},
|
||||
word = function () {
|
||||
// true, false, or null.
|
||||
|
||||
switch (ch) {
|
||||
case "t":
|
||||
next("t");
|
||||
next("r");
|
||||
next("u");
|
||||
next("e");
|
||||
return true;
|
||||
case "f":
|
||||
next("f");
|
||||
next("a");
|
||||
next("l");
|
||||
next("s");
|
||||
next("e");
|
||||
return false;
|
||||
case "n":
|
||||
next("n");
|
||||
next("u");
|
||||
next("l");
|
||||
next("l");
|
||||
return null;
|
||||
}
|
||||
error("Unexpected '" + ch + "'");
|
||||
},
|
||||
value, // Place holder for the value function.
|
||||
array = function () {
|
||||
// Parse an array value.
|
||||
|
||||
let array = [];
|
||||
|
||||
if (ch === "[") {
|
||||
next("[");
|
||||
white();
|
||||
if (ch === "]") {
|
||||
next("]");
|
||||
return array; // empty array
|
||||
}
|
||||
while (ch) {
|
||||
array.push(value());
|
||||
white();
|
||||
if (ch === "]") {
|
||||
next("]");
|
||||
return array;
|
||||
}
|
||||
next(",");
|
||||
white();
|
||||
}
|
||||
}
|
||||
error("Bad array");
|
||||
},
|
||||
object = function () {
|
||||
// Parse an object value.
|
||||
|
||||
let key,
|
||||
object = Object.create(null);
|
||||
|
||||
if (ch === "{") {
|
||||
next("{");
|
||||
white();
|
||||
if (ch === "}") {
|
||||
next("}");
|
||||
return object; // empty object
|
||||
}
|
||||
while (ch) {
|
||||
key = string();
|
||||
white();
|
||||
next(":");
|
||||
if (_options.strict === true && Object.hasOwnProperty.call(object, key)) {
|
||||
error('Duplicate key "' + key + '"');
|
||||
}
|
||||
|
||||
if (suspectProtoRx.test(key) === true) {
|
||||
if (_options.protoAction === "error") {
|
||||
error("Object contains forbidden prototype property");
|
||||
} else if (_options.protoAction === "ignore") {
|
||||
value();
|
||||
} else {
|
||||
object[key] = value();
|
||||
}
|
||||
} else if (suspectConstructorRx.test(key) === true) {
|
||||
if (_options.constructorAction === "error") {
|
||||
error("Object contains forbidden constructor property");
|
||||
} else if (_options.constructorAction === "ignore") {
|
||||
value();
|
||||
} else {
|
||||
object[key] = value();
|
||||
}
|
||||
} else {
|
||||
object[key] = value();
|
||||
}
|
||||
|
||||
white();
|
||||
if (ch === "}") {
|
||||
next("}");
|
||||
return object;
|
||||
}
|
||||
next(",");
|
||||
white();
|
||||
}
|
||||
}
|
||||
error("Bad object");
|
||||
};
|
||||
|
||||
value = function () {
|
||||
// Parse a JSON value. It could be an object, an array, a string, a number,
|
||||
// or a word.
|
||||
|
||||
white();
|
||||
switch (ch) {
|
||||
case "{":
|
||||
return object();
|
||||
case "[":
|
||||
return array();
|
||||
case '"':
|
||||
return string();
|
||||
case "-":
|
||||
return number();
|
||||
default:
|
||||
return ch >= "0" && ch <= "9" ? number() : word();
|
||||
}
|
||||
};
|
||||
|
||||
// Return the json_parse function. It will have access to all of the above
|
||||
// functions and variables.
|
||||
|
||||
return function (source, reviver) {
|
||||
let result;
|
||||
|
||||
text = source + "";
|
||||
at = 0;
|
||||
ch = " ";
|
||||
result = value();
|
||||
white();
|
||||
if (ch) {
|
||||
error("Syntax error");
|
||||
}
|
||||
|
||||
// If there is a reviver function, we recursively walk the new structure,
|
||||
// passing each name/value pair to the reviver function for possible
|
||||
// transformation, starting with a temporary root object that holds the result
|
||||
// in an empty key. If there is not a reviver function, we simply return the
|
||||
// result.
|
||||
|
||||
return typeof reviver === "function"
|
||||
? (function walk(holder, key) {
|
||||
let v,
|
||||
value = holder[key];
|
||||
if (value && typeof value === "object") {
|
||||
Object.keys(value).forEach(function (k) {
|
||||
v = walk(value, k);
|
||||
if (v !== undefined) {
|
||||
value[k] = v;
|
||||
} else {
|
||||
delete value[k];
|
||||
}
|
||||
});
|
||||
}
|
||||
return reviver.call(holder, key, value);
|
||||
})({ "": result }, "")
|
||||
: result;
|
||||
};
|
||||
};
|
||||
|
||||
// parseJSON is a wrapper for JSONbig.parse that returns an error rather than
|
||||
// throwing an error. JSONbig is an alternative to JSON.parse that decodes
|
||||
// every number as a bigint. This is required when working with the skyd API
|
||||
// because the skyd API uses 64 bit precision for all of its numbers, and
|
||||
// therefore cannot be parsed losslessly by javascript. The skyd API is
|
||||
// cryptographic, therefore full precision is required.
|
||||
function parseJSON(json: string): [any, Err] {
|
||||
try {
|
||||
let obj = json_parse({ alwaysParseAsBig: true })(json);
|
||||
return [obj, null];
|
||||
} catch (err: any) {
|
||||
return [{}, objAsString(err)];
|
||||
}
|
||||
}
|
||||
|
||||
export { parseJSON };
|
|
@ -0,0 +1,172 @@
|
|||
import { Portal } from "#types.js";
|
||||
import { Client } from "@lumeweb/libportal";
|
||||
import { deriveChildKey } from "#keys.js";
|
||||
import { bytesToHex } from "@noble/hashes/utils";
|
||||
import COMMUNITY_PORTAL_LIST from "@lumeweb/community-portals";
|
||||
import { createKeyPair } from "@lumeweb/libs5";
|
||||
import type { KeyPairEd25519 } from "@lumeweb/libs5";
|
||||
|
||||
let activePortalMasterKey;
|
||||
|
||||
export const DEFAULT_PORTAL_LIST = COMMUNITY_PORTAL_LIST;
|
||||
|
||||
let ACTIVE_PORTALS = new Set<Client>();
|
||||
|
||||
type PortalSessionsStore = { [id: string]: string };
|
||||
|
||||
const PORTAL_ID = Symbol.for("PORTAL_ID");
|
||||
const PORTAL_NAME = Symbol.for("PORTAL_NAME");
|
||||
|
||||
export function maybeInitDefaultPortals() {
|
||||
if (!activePortalMasterKey) {
|
||||
throw new Error("activePortalMasterKey not set");
|
||||
}
|
||||
|
||||
let portalsToLoad = DEFAULT_PORTAL_LIST;
|
||||
|
||||
const savedPortals = loadSavedPortals();
|
||||
|
||||
if (savedPortals) {
|
||||
portalsToLoad = savedPortals;
|
||||
}
|
||||
|
||||
for (const portal of portalsToLoad) {
|
||||
addActivePortal(initPortal(portal));
|
||||
}
|
||||
}
|
||||
|
||||
export function setActivePortalMasterKey(key: Uint8Array) {
|
||||
activePortalMasterKey = key;
|
||||
}
|
||||
|
||||
export function generatePortalEmail(portal: Portal) {
|
||||
const keyPair = generatePortalKeyPair(portal);
|
||||
|
||||
const userId = bytesToHex(keyPair.publicKey.slice(0, 12));
|
||||
|
||||
return `${userId}@example.com`;
|
||||
}
|
||||
|
||||
export function generatePortalKeyPair(portal: Portal): KeyPairEd25519 {
|
||||
const privateKey = deriveChildKey(
|
||||
activePortalMasterKey,
|
||||
`portal-account:${portal.id}`,
|
||||
);
|
||||
|
||||
return createKeyPair(privateKey);
|
||||
}
|
||||
|
||||
export function getActivePortals(): Client[] {
|
||||
return [...ACTIVE_PORTALS];
|
||||
}
|
||||
|
||||
export function addActivePortal(portal: Client) {
|
||||
ACTIVE_PORTALS.add(portal);
|
||||
}
|
||||
|
||||
export function initPortal(portal: Portal): Client {
|
||||
const sessions = getPortalSessions();
|
||||
let jwt: string | null = null;
|
||||
if (sessions) {
|
||||
if (portal.id in sessions) {
|
||||
jwt = sessions[portal.id];
|
||||
}
|
||||
}
|
||||
|
||||
const client = new Client({
|
||||
email: generatePortalEmail(portal),
|
||||
portalUrl: portal.url,
|
||||
privateKey: generatePortalKeyPair(portal).extractBytes(),
|
||||
jwt: jwt as string,
|
||||
});
|
||||
|
||||
client[PORTAL_ID] = portal.id;
|
||||
client[PORTAL_NAME] = portal.name;
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
export function getPortalSessions() {
|
||||
if (typeof globalThis.localStorage === "undefined") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let portalSessionsData = globalThis.localStorage.getItem("portal_sessions");
|
||||
let portalSessions: PortalSessionsStore = {};
|
||||
if (portalSessions) {
|
||||
portalSessions = JSON.parse(
|
||||
portalSessionsData as string,
|
||||
) as PortalSessionsStore;
|
||||
|
||||
return portalSessions;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function setActivePortals(portals: Client[]) {
|
||||
ACTIVE_PORTALS = new Set<Client>(portals);
|
||||
}
|
||||
|
||||
export function savePortals() {
|
||||
const portals = [...ACTIVE_PORTALS.values()].map((item) => {
|
||||
return {
|
||||
id: item[PORTAL_ID],
|
||||
name: item[PORTAL_NAME],
|
||||
url: item.portalUrl,
|
||||
} as Portal;
|
||||
});
|
||||
if (typeof globalThis.localStorage !== "undefined") {
|
||||
globalThis.localStorage.setItem("portals", JSON.stringify(portals));
|
||||
}
|
||||
}
|
||||
|
||||
export function savePortalSessions() {
|
||||
const portalSessions = {};
|
||||
|
||||
for (const portal of ACTIVE_PORTALS) {
|
||||
const jwt = portal.jwt;
|
||||
if (jwt) {
|
||||
portalSessions[portal[PORTAL_ID]] = jwt;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof globalThis.localStorage !== "undefined") {
|
||||
globalThis.localStorage.setItem(
|
||||
"portal_sessions",
|
||||
JSON.stringify(portalSessions),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function loadSavedPortals(): Portal[] | null {
|
||||
let portals: string | null = null;
|
||||
|
||||
if (typeof globalThis.localStorage !== "undefined") {
|
||||
portals = globalThis.localStorage.getItem("portals");
|
||||
}
|
||||
|
||||
if (!portals) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(portals);
|
||||
}
|
||||
|
||||
export async function loginActivePortals() {
|
||||
const activePortals = getActivePortals();
|
||||
|
||||
if (!activePortals.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const portal of activePortals) {
|
||||
if (!(await portal.isLoggedIn())) {
|
||||
try {
|
||||
await portal.register();
|
||||
} catch {}
|
||||
|
||||
await portal.login();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
import { addContextToErr } from "./err.js";
|
||||
import { objAsString } from "./objAsString.js";
|
||||
import { Err } from "./types.js";
|
||||
|
||||
// jsonStringify is a replacement for JSON.stringify that returns an error
|
||||
// rather than throwing.
|
||||
function jsonStringify(obj: any): [string, Err] {
|
||||
try {
|
||||
const str = JSON.stringify(obj, (_, v) => {
|
||||
if (typeof v === "bigint") {
|
||||
return Number(v);
|
||||
}
|
||||
return v;
|
||||
});
|
||||
return [str, null];
|
||||
} catch (err) {
|
||||
return ["", addContextToErr(objAsString(err), "unable to stringify object")];
|
||||
}
|
||||
}
|
||||
|
||||
export { jsonStringify };
|
77
src/types.ts
77
src/types.ts
|
@ -1,82 +1,9 @@
|
|||
// DataFn can take any object as input and has no return value. The input is
|
||||
// allowed to be undefined.
|
||||
type DataFn = (data?: any) => void;
|
||||
|
||||
// Err is an error type that is either a string or a null. If the value is
|
||||
// null, that indicates that there was no error. If the value is a string, it
|
||||
// indicates that there was an error and the string contains the error message.
|
||||
//
|
||||
// The skynet libraries prefer this error type to the standard Error type
|
||||
// because many times skynet libraries need to pass errors over postMessage,
|
||||
// and the 'Error' type is not able to be sent over postMessage.
|
||||
type Err = string | null;
|
||||
|
||||
// ErrFn must take an error message as input. The input is not allowed to be
|
||||
// undefined or null, there must be an error.
|
||||
type ErrFn = (errMsg: string) => void;
|
||||
|
||||
// ErrTuple is a type that pairs a 'data' field with an 'err' field. Skynet
|
||||
// libraries typically prefer returning ErrTuples to throwing or rejecting,
|
||||
// because it allows upstream code to avoid the try/catch/throw pattern. Though
|
||||
// the pattern is much celebrated in javascript, it encourages relaxed error
|
||||
// handling, and often makes error handling much more difficult because the try
|
||||
// and the catch are in different scopes.
|
||||
//
|
||||
// Most of the Skynet core libraries do not have any `throws` anywhere in their
|
||||
// API.
|
||||
//
|
||||
// Typically, an ErrTuple will have only one field filled out. If data is
|
||||
// returned, the err should be 'null'. If an error is returned, the data field
|
||||
// should generally be empty. Callers are expected to check the error before
|
||||
// they access any part of the data field.
|
||||
type ErrTuple = [data: any, err: Err];
|
||||
|
||||
// KernelAuthStatus is the structure of a message that gets sent by the kernel
|
||||
// containing its auth status. Auth occurs in 5 stages.
|
||||
//
|
||||
// Stage 0; no auth updates
|
||||
// Stage 1: bootloader is loaded, user is not yet logged in
|
||||
// Stage 2: bootloader is loaded, user is logged in
|
||||
// Stage 3: kernel is loaded, user is logged in
|
||||
// Stage 4: kernel is loaded, user is logging out (refresh iminent)
|
||||
//
|
||||
// 'kernelLoaded' is initially set to "not yet" and will be updated when the
|
||||
// kernel has loaded. If it is set to "success", it means the kernel loaded
|
||||
// without issues. If it is set to anything else, it means that there was an
|
||||
// error, and the new value is the error.
|
||||
//
|
||||
// 'kernelLoaded' will not be changed until 'loginComplete' has been set to
|
||||
// true. 'loginComplete' can be set to true immediately if the user is already
|
||||
// logged in.
|
||||
//
|
||||
// 'logoutComplete' can be set to 'true' at any point, which indicates that the
|
||||
// auth cycle needs to reset.
|
||||
interface KernelAuthStatus {
|
||||
loginComplete: boolean;
|
||||
kernelLoaded: "not yet" | "success" | string;
|
||||
logoutComplete: boolean;
|
||||
}
|
||||
|
||||
interface Portal {
|
||||
id: string;
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
// RequestOverrideResponse defines the type that the kernel returns as a
|
||||
// response to a requestOverride call.
|
||||
interface RequestOverrideResponse {
|
||||
override: boolean;
|
||||
headers?: any; // TODO: I don't know how to do an array of types.
|
||||
body?: Uint8Array;
|
||||
}
|
||||
const NO_PORTALS_ERROR = new Error("no active portals");
|
||||
|
||||
export {
|
||||
DataFn,
|
||||
ErrFn,
|
||||
Err,
|
||||
ErrTuple,
|
||||
KernelAuthStatus,
|
||||
RequestOverrideResponse,
|
||||
Portal,
|
||||
};
|
||||
export { Portal, NO_PORTALS_ERROR };
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import { NO_PORTALS_ERROR } from "#types.js";
|
||||
import { getActivePortals } from "#portal.js";
|
||||
import { CID } from "@lumeweb/libs5";
|
||||
|
||||
export async function uploadObject(
|
||||
file:
|
||||
| ReadableStream<Uint8Array>
|
||||
| import("stream").Readable
|
||||
| Uint8Array
|
||||
| Blob,
|
||||
size?: bigint,
|
||||
): Promise<CID> {
|
||||
const activePortals = getActivePortals();
|
||||
|
||||
if (!activePortals.length) {
|
||||
throw NO_PORTALS_ERROR;
|
||||
}
|
||||
|
||||
for (const portal of activePortals) {
|
||||
if (!(await portal.isLoggedIn())) {
|
||||
try {
|
||||
await portal.register();
|
||||
} catch {}
|
||||
|
||||
await portal.login();
|
||||
}
|
||||
|
||||
let upload;
|
||||
try {
|
||||
upload = await portal.uploadFile(file as any, size);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
return upload;
|
||||
}
|
||||
|
||||
throw NO_PORTALS_ERROR;
|
||||
}
|
Loading…
Reference in New Issue