mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-11-28 03:00:15 +03:00
Merge branch 'develop' into i/6264/ames-snub-whitelist
This commit is contained in:
commit
dbe6f7fd20
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
ignorePatterns: ["**/*"]
|
||||
};
|
34
.github/workflows/develop.yml
vendored
Normal file
34
.github/workflows/develop.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
name: Push to develop
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'develop'
|
||||
paths:
|
||||
- '.github/workflows/feature.yml'
|
||||
- '.github/workflows/develop.yml'
|
||||
- '.github/workflows/release.yml'
|
||||
- '.github/workflows/master.yml'
|
||||
- '.github/workflows/vere.yml'
|
||||
- 'pkg/arvo/**'
|
||||
- 'pkg/docker-image/**'
|
||||
- 'pkg/ent/**'
|
||||
- 'pkg/ge-additions/**'
|
||||
- 'pkg/libaes_siv/**'
|
||||
- 'pkg/urbit/**'
|
||||
- 'pkg/urcrypt/**'
|
||||
- 'tests/**'
|
||||
- 'bin/**'
|
||||
- 'nix/**'
|
||||
- 'default.nix'
|
||||
|
||||
jobs:
|
||||
call-vere:
|
||||
uses: ./.github/workflows/vere.yml
|
||||
with:
|
||||
pace: 'edge'
|
||||
upload: >-
|
||||
${{
|
||||
(github.ref_name == 'next/vere' && github.ref_type == 'branch')
|
||||
}}
|
||||
secrets: inherit
|
@ -1,25 +1,12 @@
|
||||
name: build
|
||||
name: Feature pull request
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/build.yml'
|
||||
- '.github/workflows/vere.yml'
|
||||
- 'pkg/arvo/**'
|
||||
- 'pkg/docker-image/**'
|
||||
- 'pkg/ent/**'
|
||||
- 'pkg/ge-additions/**'
|
||||
- 'pkg/libaes_siv/**'
|
||||
- 'pkg/urbit/**'
|
||||
- 'pkg/urcrypt/**'
|
||||
- 'tests/**'
|
||||
- 'bin/**'
|
||||
- 'nix/**'
|
||||
- 'default.nix'
|
||||
- 'vere-version'
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/build.yml'
|
||||
- '.github/workflows/feature.yml'
|
||||
- '.github/workflows/develop.yml'
|
||||
- '.github/workflows/release.yml'
|
||||
- '.github/workflows/master.yml'
|
||||
- '.github/workflows/vere.yml'
|
||||
- 'pkg/arvo/**'
|
||||
- 'pkg/docker-image/**'
|
||||
@ -32,13 +19,12 @@ on:
|
||||
- 'bin/**'
|
||||
- 'nix/**'
|
||||
- 'default.nix'
|
||||
- 'vere-version'
|
||||
|
||||
jobs:
|
||||
call-vere:
|
||||
uses: ./.github/workflows/vere.yml
|
||||
with:
|
||||
pace: 'edge' # XX s/b once?
|
||||
pace: 'edge'
|
||||
upload: >-
|
||||
${{
|
||||
(github.ref_name == 'next/vere' && github.ref_type == 'branch')
|
34
.github/workflows/master.yml
vendored
Normal file
34
.github/workflows/master.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
name: Push to master
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
paths:
|
||||
- '.github/workflows/feature.yml'
|
||||
- '.github/workflows/develop.yml'
|
||||
- '.github/workflows/release.yml'
|
||||
- '.github/workflows/master.yml'
|
||||
- '.github/workflows/vere.yml'
|
||||
- 'pkg/arvo/**'
|
||||
- 'pkg/docker-image/**'
|
||||
- 'pkg/ent/**'
|
||||
- 'pkg/ge-additions/**'
|
||||
- 'pkg/libaes_siv/**'
|
||||
- 'pkg/urbit/**'
|
||||
- 'pkg/urcrypt/**'
|
||||
- 'tests/**'
|
||||
- 'bin/**'
|
||||
- 'nix/**'
|
||||
- 'default.nix'
|
||||
|
||||
jobs:
|
||||
call-vere:
|
||||
uses: ./.github/workflows/vere.yml
|
||||
with:
|
||||
pace: 'live'
|
||||
upload: >-
|
||||
${{
|
||||
(github.ref_name == 'next/vere' && github.ref_type == 'branch')
|
||||
}}
|
||||
secrets: inherit
|
62
.github/workflows/publish-npm-packages.yml
vendored
62
.github/workflows/publish-npm-packages.yml
vendored
@ -1,62 +0,0 @@
|
||||
name: publish-npm-packages
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
paths:
|
||||
- 'pkg/npm/**'
|
||||
jobs:
|
||||
publish-api:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Publish '@urbit/api' if a new version is available"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
lfs: true
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
- run: 'npm install'
|
||||
working-directory: 'pkg/npm/api'
|
||||
- uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
check-version: true
|
||||
package: './pkg/npm/api/package.json'
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
publish-http-api:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Publish '@urbit/http-api' if a new version is available"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
lfs: true
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
- run: 'npm install'
|
||||
working-directory: 'pkg/npm/http-api'
|
||||
- uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
check-version: true
|
||||
package: './pkg/npm/http-api/package.json'
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
publish-eslint-config:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Publish '@urbit/eslint-config' if a new version is available"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
lfs: true
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
- run: 'npm install'
|
||||
working-directory: 'pkg/npm/eslint-config'
|
||||
- uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
check-version: true
|
||||
package: './pkg/npm/eslint-config/package.json'
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
|
34
.github/workflows/release.yml
vendored
Normal file
34
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
name: Push to release branch
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'release/*'
|
||||
paths:
|
||||
- '.github/workflows/feature.yml'
|
||||
- '.github/workflows/develop.yml'
|
||||
- '.github/workflows/release.yml'
|
||||
- '.github/workflows/master.yml'
|
||||
- '.github/workflows/vere.yml'
|
||||
- 'pkg/arvo/**'
|
||||
- 'pkg/docker-image/**'
|
||||
- 'pkg/ent/**'
|
||||
- 'pkg/ge-additions/**'
|
||||
- 'pkg/libaes_siv/**'
|
||||
- 'pkg/urbit/**'
|
||||
- 'pkg/urcrypt/**'
|
||||
- 'tests/**'
|
||||
- 'bin/**'
|
||||
- 'nix/**'
|
||||
- 'default.nix'
|
||||
|
||||
jobs:
|
||||
call-vere:
|
||||
uses: ./.github/workflows/vere.yml
|
||||
with:
|
||||
pace: 'soon'
|
||||
upload: >-
|
||||
${{
|
||||
(github.ref_name == 'next/vere' && github.ref_type == 'branch')
|
||||
}}
|
||||
secrets: inherit
|
11
.github/workflows/vere.yml
vendored
11
.github/workflows/vere.yml
vendored
@ -39,11 +39,6 @@ on:
|
||||
- soon
|
||||
- live
|
||||
|
||||
env:
|
||||
UPLOAD_BASE: bootstrap.urbit.org/vere
|
||||
VERE_PACE: ${{ inputs.pace }}
|
||||
VERSION_TYPE: ${{ (inputs.pace == 'soon' || inputs.pace == 'live') && 'real' || 'hash' }}
|
||||
|
||||
jobs:
|
||||
urbit:
|
||||
strategy:
|
||||
@ -78,8 +73,10 @@ jobs:
|
||||
name: run urbit-tests
|
||||
run: |
|
||||
cp -RL tests pkg/arvo/tests
|
||||
vere="$(cat ./vere-version | sed -e 's/\([^ ]*\) \([^ ]*\)/\1\/\2\/vere-\2/g' | tr -d '\n')"
|
||||
url="$(echo https://bootstrap.urbit.org/vere/${vere}-linux-x86_64)"
|
||||
vere=$(curl https://bootstrap.urbit.org/vere/${{ inputs.pace }}/last)
|
||||
echo $vere
|
||||
url="$(echo https://bootstrap.urbit.org/vere/${{ inputs.pace }}/v${vere}/vere-v${vere}-linux-x86_64)"
|
||||
echo $url
|
||||
# put in .jam so it doesn't crash when it gets -A'd in
|
||||
curl -Lo pkg/arvo/vere.jam "$url"
|
||||
chmod +x pkg/arvo/vere.jam
|
||||
|
@ -1,16 +0,0 @@
|
||||
bin
|
||||
doc
|
||||
extras
|
||||
nix
|
||||
pkg/arvo
|
||||
pkg/base-dev
|
||||
pkg/docker-image
|
||||
pkg/ent
|
||||
pkg/garden
|
||||
pkg/garden-dev
|
||||
pkg/ge-additions
|
||||
pkg/herb
|
||||
pkg/hs
|
||||
pkg/libaes_siv
|
||||
pkg/urbit
|
||||
sh
|
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4066cd90ac7d33b3d96b1b4d611d846d941de6e538c8355047c7cafcadce0851
|
||||
size 6017832
|
||||
oid sha256:9d0ff563027d47436d12f00b7500352ec844a1db7d336b0e5b9cfd5c4c49c66c
|
||||
size 6017943
|
||||
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"packages": [
|
||||
"pkg/npm/*",
|
||||
"pkg/btc-wallet",
|
||||
"pkg/interface",
|
||||
"pkg/grid"
|
||||
],
|
||||
"version": "independent"
|
||||
}
|
@ -17,7 +17,7 @@ in stdenvNoCC.mkDerivation {
|
||||
buildPhase = ''
|
||||
set -xeuo pipefail
|
||||
|
||||
${arvo}/vere.jam ${lib.concatStringsSep " " args} ./pier
|
||||
${arvo}/vere.jam ${lib.concatStringsSep " " args} -c ./pier
|
||||
|
||||
cleanup () {
|
||||
if [ -f ./pier/.vere.lock ]; then
|
||||
|
15716
package-lock.json
generated
15716
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "root",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "16.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.29.0",
|
||||
"husky": "^6.0.0",
|
||||
"lerna": "^4.0.0",
|
||||
"lint-staged": "^11.1.2",
|
||||
"prettier": "^2.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
"watch-libs": "lerna run watch --no-private --parallel",
|
||||
"build-libs": "lerna run build --no-private",
|
||||
"test": "lerna run test",
|
||||
"bootstrap": "lerna bootstrap",
|
||||
"build:prod": "lerna run build:prod"
|
||||
}
|
||||
}
|
@ -801,8 +801,8 @@
|
||||
=/ heads=(list [tako desk])
|
||||
%+ turn ~(tap in desks)
|
||||
|= =desk
|
||||
=+ .^(=dome %cv /(scot %p our.bowl)/[desk]/(scot %da now.bowl))
|
||||
=/ =tako (~(got by hit.dome) let.dome)
|
||||
=+ .^(=domo %cv /(scot %p our.bowl)/[desk]/(scot %da now.bowl))
|
||||
=/ =tako (~(got by hit.domo) let.domo)
|
||||
[tako desk]
|
||||
=/ yakis=(set yaki)
|
||||
%- silt
|
||||
|
@ -16,6 +16,6 @@
|
||||
?> ((sane %tas) +<.arg)
|
||||
[-.arg +<.arg]
|
||||
=+ .^(=cone:clay %cx /(scot %p p.bec)//(scot %da now)/domes)
|
||||
=/ =foam:clay (~(gut by cone) [p.bec des] *foam:clay)
|
||||
=+ ((slog ?:(=(%live liv.foam) ~ ['kiln: desk not live' ~])) ~)
|
||||
[%kiln-rein des (~(put by ren.foam) dap &)]
|
||||
=/ =dome:clay (~(gut by cone) [p.bec des] *dome:clay)
|
||||
=+ ((slog ?:(=(%live liv.dome) ~ ['kiln: desk not live' ~])) ~)
|
||||
[%kiln-rein des (~(put by ren.dome) dap &)]
|
||||
|
@ -13,7 +13,7 @@
|
||||
=/ =lobe u.fil.arch
|
||||
=+ .^(=rang %cx /(scot %p p.bec)//(scot %da now)/rang)
|
||||
=+ .^(=cone %cx /(scot %p p.bec)//(scot %da now)/domes)
|
||||
=/ domes=(list [[=ship =desk] foam]) ~(tap by cone)
|
||||
=/ domes=(list [[=ship =desk] dome]) ~(tap by cone)
|
||||
=/ norms
|
||||
|^
|
||||
|- ^- (set [ship desk tako norm path])
|
||||
@ -30,7 +30,7 @@
|
||||
(~(uni in paths) $(aeon +(aeon)))
|
||||
::
|
||||
++ draw-tako
|
||||
|= [=ship =desk foam =tako]
|
||||
|= [=ship =desk dome =tako]
|
||||
^- (set [^ship ^desk ^tako norm path])
|
||||
~+
|
||||
=/ =yaki (~(got by hut.rang) tako)
|
||||
|
@ -8,7 +8,7 @@
|
||||
%- flop ^- tang
|
||||
%- zing
|
||||
%+ turn ~(tap by cone)
|
||||
|= [[=ship =desk] foam]
|
||||
|= [[=ship =desk] dome]
|
||||
:- leaf+"{<ship>}/{<desk>}:"
|
||||
%+ turn ~(tap of nor)
|
||||
|= [=path keep=?]
|
||||
|
@ -807,10 +807,16 @@
|
||||
::
|
||||
++ poke-uninstall
|
||||
|= loc=desk
|
||||
?~ got=(~(get by sources) loc)
|
||||
=+ .^(=rock:tire %cx /(scot %p our)//(scot %da now)/tire)
|
||||
?~ got=(~(get by rock) loc)
|
||||
abet:(spam leaf+"desk does not exist: {<loc>}" ~)
|
||||
?: =(+<:got %dead)
|
||||
abet:(spam leaf+"desk not installed: {<loc>}" ~)
|
||||
~> %slog.(fmt "uninstalling {<loc>}")
|
||||
=. ..on-init (emit %pass /kiln/uninstall %arvo %c %zest loc %dead)
|
||||
(poke-unsync loc u.got)
|
||||
?~ sync=(~(get by sources) loc)
|
||||
abet
|
||||
(poke-unsync loc u.sync)
|
||||
::
|
||||
++ poke-unmount
|
||||
|= mon=kiln-unmount
|
||||
|
@ -1 +1 @@
|
||||
[%zuse 415]
|
||||
[%zuse 414]
|
||||
|
@ -4217,12 +4217,19 @@
|
||||
$(inx +(inx))
|
||||
==
|
||||
|- ^- ?
|
||||
?: =(0 b) &
|
||||
=+ cur=(end 3 b)
|
||||
?: =(inx len) &
|
||||
=+ cur=(cut 3 [inx 1] b)
|
||||
?: &((lth cur 32) !=(10 cur)) |
|
||||
=+ len=(teff cur)
|
||||
?& |(=(1 len) =+(i=1 |-(|(=(i len) &((gte (cut 3 [i 1] b) 128) $(i +(i)))))))
|
||||
$(b (rsh [3 len] b))
|
||||
=+ tef=(teff cur)
|
||||
?& ?| =(1 tef)
|
||||
=+ i=1
|
||||
|- ^- ?
|
||||
?|
|
||||
=(i tef)
|
||||
?& (gte (cut 3 [(add i inx) 1] b) 128)
|
||||
$(i +(i))
|
||||
== == ==
|
||||
$(inx +(inx))
|
||||
==
|
||||
::
|
||||
++ ruth :: biblical sanity
|
||||
@ -5312,7 +5319,7 @@
|
||||
--
|
||||
++ ag
|
||||
|%
|
||||
++ ape |*(fel=rule ;~(pose (cold 0 (just '0')) fel))
|
||||
++ ape |*(fel=rule ;~(pose (cold `@`0 (just '0')) fel))
|
||||
++ bay (ape (bass 16 ;~(plug qeb:ab (star ;~(pfix dog qib:ab)))))
|
||||
++ bip =+ tod=(ape qex:ab)
|
||||
(bass 0x1.0000 ;~(plug tod (stun [7 7] ;~(pfix dog tod))))
|
||||
|
@ -3,7 +3,7 @@
|
||||
!:
|
||||
=> ..part
|
||||
|%
|
||||
++ lull %326
|
||||
++ lull %325
|
||||
:: :: ::
|
||||
:::: :: :: (1) models
|
||||
:: :: ::
|
||||
@ -834,17 +834,34 @@
|
||||
[%worn =ship =desk =tako =norm] :: set commit norm
|
||||
[%seek =ship =desk =cash] :: fetch source blobs
|
||||
== ::
|
||||
+$ cone (map [ship desk] foam) :: domes
|
||||
+$ foam ::
|
||||
$: dome ::
|
||||
tom=(map tako norm) ::
|
||||
nor=norm ::
|
||||
liv=zest ::
|
||||
ren=(map dude:gall ?) ::
|
||||
+$ cone (map [ship desk] dome) :: domes
|
||||
::
|
||||
:: Desk state.
|
||||
::
|
||||
:: Includes a checked-out ankh with current content, most recent version, map
|
||||
:: of all version numbers to commit hashes (commits are in hut.rang), and map
|
||||
:: of labels to version numbers.
|
||||
::
|
||||
:: `mim` is a cache of the content in the directories that are mounted
|
||||
:: to unix. Often, we convert to/from mime without anything really
|
||||
:: having changed; this lets us short-circuit that in some cases.
|
||||
:: Whenever you give an `%ergo`, you must update this.
|
||||
::
|
||||
+$ dome
|
||||
$: let=aeon :: top id
|
||||
hit=(map aeon tako) :: versions by id
|
||||
lab=(map @tas aeon) :: labels
|
||||
tom=(map tako norm) :: tomb policies
|
||||
nor=norm :: default policy
|
||||
mim=(map path mime) :: mime cache
|
||||
fod=flue :: ford cache
|
||||
wic=(map weft yoki) :: commit-in-waiting
|
||||
liv=zest :: running agents
|
||||
ren=rein :: force agents on/off
|
||||
== ::
|
||||
+$ crew (set ship) :: permissions group
|
||||
+$ dict [src=path rul=real] :: effective permission
|
||||
+$ dome :: project state
|
||||
+$ domo :: project state
|
||||
$: let=@ud :: top id
|
||||
hit=(map @ud tako) :: changes by id
|
||||
lab=(map @tas @ud) :: labels
|
||||
@ -1107,6 +1124,30 @@
|
||||
::
|
||||
+$ flow (map leak [refs=@ud =soak])
|
||||
::
|
||||
:: Per-desk ford cache
|
||||
::
|
||||
:: Spill is the set of "roots" we have into the global ford cache.
|
||||
:: We add a root for everything referenced directly or indirectly on
|
||||
:: a desk, then invalidate them on commit only if their dependencies
|
||||
:: change.
|
||||
::
|
||||
:: Sprig is a fast-lookup index over the global ford cache. The only
|
||||
:: goal is to make cache hits fast.
|
||||
::
|
||||
+$ flue [spill=(set leak) sprig=(map mist [=leak =soak])]
|
||||
::
|
||||
:: Ford build without content.
|
||||
::
|
||||
+$ mist
|
||||
$% [%file =path]
|
||||
[%nave =mark]
|
||||
[%dais =mark]
|
||||
[%cast =mars]
|
||||
[%tube =mars]
|
||||
[%vale =path]
|
||||
[%arch =path]
|
||||
==
|
||||
::
|
||||
:: $pile: preprocessed hoon source file
|
||||
::
|
||||
:: /- sur-file :: surface imports from /sur
|
||||
@ -1564,9 +1605,8 @@
|
||||
::
|
||||
[%scry ~]
|
||||
:: respond with the @p the requester is authenticated as
|
||||
:: TODO: put this back in when we burn the next kelvin
|
||||
::
|
||||
:: [%name ~]
|
||||
[%name ~]
|
||||
:: respond with the default file not found page
|
||||
::
|
||||
[%four-oh-four ~]
|
||||
|
@ -2992,9 +2992,12 @@
|
||||
:: clear all packets from this message from the packet pump
|
||||
::
|
||||
=. message-pump (run-packet-pump:message-pump %done message-num *@dr)
|
||||
=/ =wire (make-pump-timer-wire her.channel bone)
|
||||
?: (~(has in corked.peer-state) bone)
|
||||
%- %+ trace odd.veb
|
||||
|.("trying to delete a corked bone={<bone>}")
|
||||
peer-core
|
||||
=/ nack-bone=^bone (mix 0b10 bone)
|
||||
=? rcv.peer-state (~(has by rcv.peer-state) nack-bone)
|
||||
=? rcv.peer-state (~(has by rcv.peer-state) nack-bone)
|
||||
:: if the publisher was behind we remove nacks received on that bone
|
||||
::
|
||||
(~(del by rcv.peer-state) nack-bone)
|
||||
|
@ -138,7 +138,7 @@
|
||||
:: `sto` stores the data needed to merge, and `bas` is the base
|
||||
:: beak for the merge.
|
||||
::
|
||||
+$ melt [bas=beak con=(list [beak germ]) sto=(map beak (unit dome:clay))]
|
||||
+$ melt [bas=beak con=(list [beak germ]) sto=(map beak (unit domo))]
|
||||
::
|
||||
:: Domestic desk state.
|
||||
::
|
||||
@ -153,30 +153,6 @@
|
||||
fiz=melt :: state for mega merges
|
||||
==
|
||||
::
|
||||
:: Desk state.
|
||||
::
|
||||
:: Includes a checked-out ankh with current content, most recent version, map
|
||||
:: of all version numbers to commit hashes (commits are in hut.rang), and map
|
||||
:: of labels to version numbers.
|
||||
::
|
||||
:: `mim` is a cache of the content in the directories that are mounted
|
||||
:: to unix. Often, we convert to/from mime without anything really
|
||||
:: having changed; this lets us short-circuit that in some cases.
|
||||
:: Whenever you give an `%ergo`, you must update this.
|
||||
::
|
||||
+$ dome
|
||||
$: let=aeon :: top id
|
||||
hit=(map aeon tako) :: versions by id
|
||||
lab=(map @tas aeon) :: labels
|
||||
tom=(map tako norm) :: tomb policies
|
||||
nor=norm :: default policy
|
||||
mim=(map path mime) :: mime cache
|
||||
fod=flue :: ford cache
|
||||
wic=(map weft yoki) :: commit-in-waiting
|
||||
liv=zest :: running agents
|
||||
ren=rein :: force agents on/off
|
||||
== ::
|
||||
::
|
||||
:: Over-the-wire backfill request/response
|
||||
::
|
||||
+$ fill
|
||||
@ -191,30 +167,6 @@
|
||||
[%1 peg=(unit page)]
|
||||
==
|
||||
::
|
||||
:: Per-desk ford cache
|
||||
::
|
||||
:: Spill is the set of "roots" we have into the global ford cache.
|
||||
:: We add a root for everything referenced directly or indirectly on
|
||||
:: a desk, then invalidate them on commit only if their dependencies
|
||||
:: change.
|
||||
::
|
||||
:: Sprig is a fast-lookup index over the global ford cache. The only
|
||||
:: goal is to make cache hits fast.
|
||||
::
|
||||
+$ flue [spill=(set leak) sprig=(map mist [=leak =soak])]
|
||||
::
|
||||
:: Ford build without content.
|
||||
::
|
||||
+$ mist
|
||||
$% [%file =path]
|
||||
[%nave =mark]
|
||||
[%dais =mark]
|
||||
[%cast =mars]
|
||||
[%tube =mars]
|
||||
[%vale =path]
|
||||
[%arch =path]
|
||||
==
|
||||
::
|
||||
:: New desk data.
|
||||
::
|
||||
:: Sent to other ships to update them about a particular desk.
|
||||
@ -2289,9 +2241,9 @@
|
||||
|= [bas=beak con=(list [beak germ])]
|
||||
^- melt
|
||||
:+ bas con
|
||||
%- ~(gas by *(map beak (unit dome:clay)))
|
||||
:- [bas *(unit dome:clay)]
|
||||
(turn con |=(a=[beak germ] [-.a *(unit dome:clay)]))
|
||||
%- ~(gas by *(map beak (unit domo)))
|
||||
:- [bas *(unit domo)]
|
||||
(turn con |=(a=[beak germ] [-.a *(unit domo)]))
|
||||
::
|
||||
++ start-fuse
|
||||
|= [bas=beak con=(list [beak germ])]
|
||||
@ -2314,7 +2266,7 @@
|
||||
=/ discarded=tang
|
||||
%+ turn
|
||||
~(tap in sto.fiz)
|
||||
|= [k=beak v=(unit dome:clay)]
|
||||
|= [k=beak v=(unit domo)]
|
||||
^- tank
|
||||
=/ received=tape ?~(v "missing" "received")
|
||||
leaf+"{<(en-beam k ~)>} {received}"
|
||||
@ -2341,17 +2293,17 @@
|
||||
((slog [leaf+"clay: got strange fuse response {<msg>}"]~) ..take-fuse)
|
||||
=. fiz
|
||||
:+ bas.fiz con.fiz
|
||||
(~(put by sto.fiz) bec `!<(dome:clay q.r.u.riot))
|
||||
(~(put by sto.fiz) bec `!<(domo q.r.u.riot))
|
||||
=/ all-done=flag
|
||||
%- ~(all by sto.fiz)
|
||||
|= res=(unit dome:clay)
|
||||
|= res=(unit domo)
|
||||
^- flag
|
||||
!=(res ~)
|
||||
?. all-done
|
||||
..take-fuse
|
||||
=| rag=rang
|
||||
=/ clean-state ..take-fuse
|
||||
=/ initial-dome=dome:clay (need (~(got by sto.fiz) bas.fiz))
|
||||
=/ initial-dome=domo (need (~(got by sto.fiz) bas.fiz))
|
||||
=/ next-yaki=yaki
|
||||
(~(got by hut.ran) (~(got by hit.initial-dome) let.initial-dome))
|
||||
=/ parents=(list tako) ~[(~(got by hit.initial-dome) let.initial-dome)]
|
||||
@ -2362,7 +2314,7 @@
|
||||
=. ..take-fuse (done-fuse clean-state %& ~)
|
||||
(park | & [%| next-yaki(p (flop parents))] rag)
|
||||
=/ [bec=beak g=germ] i.merges
|
||||
=/ ali-dom=dome:clay (need (~(got by sto.fiz) bec))
|
||||
=/ ali-dom=domo (need (~(got by sto.fiz) bec))
|
||||
=/ result (merge-helper p.bec q.bec g ali-dom `next-yaki)
|
||||
?- -.result
|
||||
%|
|
||||
@ -2428,10 +2380,10 @@
|
||||
^+ ..merge
|
||||
?~ riot
|
||||
(done %| %ali-unavailable ~[>[ali-ship ali-desk germ]<])
|
||||
=/ ali-dome=dome:clay
|
||||
=/ ali-dome=domo
|
||||
?: &(?=(@ -.q.q.r.u.riot) !=(~ -.q.q.r.u.riot))
|
||||
!<(dome:clay q.r.u.riot)
|
||||
+:!<([* dome:clay] q.r.u.riot)
|
||||
!<(domo q.r.u.riot)
|
||||
+:!<([* domo] q.r.u.riot)
|
||||
=/ result=(each (unit merge-result) (pair term tang))
|
||||
(merge-helper ali-ship ali-desk germ ali-dome ~)
|
||||
?- -.result
|
||||
@ -2447,7 +2399,7 @@
|
||||
+$ merge-result [conflicts=(set path) new=yoki lat=(map lobe page)]
|
||||
::
|
||||
++ merge-helper
|
||||
|= [=ali=ship =ali=desk =germ ali-dome=dome:clay next-yaki=(unit yaki)]
|
||||
|= [=ali=ship =ali=desk =germ ali-dome=domo next-yaki=(unit yaki)]
|
||||
^- (each (unit merge-result) [term tang])
|
||||
|^
|
||||
^- (each (unit merge-result) [term tang])
|
||||
@ -4324,17 +4276,17 @@
|
||||
::
|
||||
++ read-v
|
||||
|= [yon=aeon pax=path]
|
||||
^- (unit (unit [%dome (hypo dome:clay)]))
|
||||
^- (unit (unit [%dome (hypo domo)]))
|
||||
?: (lth yon let.dom)
|
||||
:* ~ ~ %dome -:!>(*dome:clay)
|
||||
^- dome:clay
|
||||
:* ~ ~ %dome -:!>(*domo)
|
||||
^- domo
|
||||
:* let=yon
|
||||
hit=(molt (skim ~(tap by hit.dom) |=([p=@ud *] (lte p yon))))
|
||||
lab=(molt (skim ~(tap by lab.dom) |=([* p=@ud] (lte p yon))))
|
||||
== ==
|
||||
?: (gth yon let.dom)
|
||||
~
|
||||
``[%dome -:!>(*dome:clay) [let hit lab]:dom]
|
||||
``[%dome -:!>(*domo) [let hit lab]:dom]
|
||||
::
|
||||
:: Gets all cases refering to the same revision as the given case.
|
||||
::
|
||||
@ -5654,19 +5606,19 @@
|
||||
%- ~(gas by *cone)
|
||||
%+ turn ~(tap by dos.rom.ruf)
|
||||
|= [=desk =dojo]
|
||||
[[our desk] [[let hit lab] tom nor liv ren]:dom.dojo]
|
||||
[[our desk] dom.dojo]
|
||||
=. domes
|
||||
%- ~(uni by domes)
|
||||
%- ~(gas by *cone)
|
||||
^- (list [[ship desk] foam])
|
||||
^- (list [[ship desk] dome])
|
||||
%- zing
|
||||
^- (list (list [[ship desk] foam]))
|
||||
^- (list (list [[ship desk] dome]))
|
||||
%+ turn ~(tap by hoy.ruf)
|
||||
|= [=ship =rung]
|
||||
^- (list [[^ship desk] foam])
|
||||
^- (list [[^ship desk] dome])
|
||||
%+ turn ~(tap by rus.rung)
|
||||
|= [=desk =rede]
|
||||
[[ship desk] [[let hit lab] tom nor liv ren]:dom.rede]
|
||||
[[ship desk] dom.rede]
|
||||
``[%domes !>(`cone`domes)]
|
||||
::
|
||||
++ cult
|
||||
|
@ -75,32 +75,6 @@
|
||||
::
|
||||
=server-state
|
||||
==
|
||||
::
|
||||
:: +outstanding-connection-new: intermediate type to enable
|
||||
:: /~/name endpoint without
|
||||
:: breaking type change in lull
|
||||
::
|
||||
+$ outstanding-connection-new
|
||||
$: :: action: the action that had matched
|
||||
::
|
||||
action=action-new
|
||||
:: inbound-request: the original request which caused this connection
|
||||
::
|
||||
=inbound-request
|
||||
:: response-header: set when we get our first %start
|
||||
::
|
||||
response-header=(unit response-header:http)
|
||||
:: bytes-sent: the total bytes sent in response
|
||||
::
|
||||
bytes-sent=@ud
|
||||
==
|
||||
:: +action-new: intermediate type to enable /~/name endpoint
|
||||
:: without breaking type change in lull
|
||||
::
|
||||
+$ action-new
|
||||
$% action
|
||||
[%name ~]
|
||||
==
|
||||
:: +server-state: state relating to open inbound HTTP connections
|
||||
::
|
||||
+$ server-state
|
||||
@ -113,13 +87,13 @@
|
||||
:: TODO: It would be nice if we had a path trie. We could decompose
|
||||
:: the :binding into a (map (unit @t) (trie knot =action)).
|
||||
::
|
||||
bindings=(list [=binding =duct action=action-new])
|
||||
bindings=(list [=binding =duct =action])
|
||||
:: cors-registry: state used and managed by the +cors core
|
||||
::
|
||||
=cors-registry
|
||||
:: connections: open http connections not fully complete
|
||||
::
|
||||
connections=(map duct outstanding-connection-new)
|
||||
connections=(map duct outstanding-connection)
|
||||
:: authentication-state: state managed by the +authentication core
|
||||
::
|
||||
=authentication-state
|
||||
@ -606,7 +580,7 @@
|
||||
::
|
||||
=/ act [%app app=%lens]
|
||||
::
|
||||
=/ connection=outstanding-connection-new
|
||||
=/ connection=outstanding-connection
|
||||
[act [& secure address request] ~ 0]
|
||||
::
|
||||
=. connections.state
|
||||
@ -630,13 +604,13 @@
|
||||
(fall (forwarded-for u.forwards) address)
|
||||
::
|
||||
=/ host (get-header:http 'host' headers)
|
||||
=/ [action=action-new suburl=@t]
|
||||
=/ [=action suburl=@t]
|
||||
(get-action-for-binding host url.request)
|
||||
::
|
||||
=/ authenticated (request-is-logged-in:authentication request)
|
||||
:: record that we started an asynchronous response
|
||||
::
|
||||
=/ connection=outstanding-connection-new
|
||||
=/ connection=outstanding-connection
|
||||
[action [authenticated secure address request] ~ 0]
|
||||
=. connections.state
|
||||
(~(put by connections.state) duct connection)
|
||||
@ -1962,7 +1936,7 @@
|
||||
(session-cookie-string u.session-id &)
|
||||
headers.response-header.http-event
|
||||
::
|
||||
=/ connection=outstanding-connection-new
|
||||
=/ connection=outstanding-connection
|
||||
(~(got by connections.state) duct)
|
||||
:: if the request was a simple cors request from an approved origin
|
||||
:: append the necessary cors headers to the response
|
||||
@ -1998,7 +1972,7 @@
|
||||
::
|
||||
=. connections.state
|
||||
%+ ~(jab by connections.state) duct
|
||||
|= connection=outstanding-connection-new
|
||||
|= connection=outstanding-connection
|
||||
=+ size=?~(data.http-event 0 p.u.data.http-event)
|
||||
connection(bytes-sent (add bytes-sent.connection size))
|
||||
::
|
||||
@ -2051,7 +2025,7 @@
|
||||
:: Adds =binding =action if there is no conflicting bindings.
|
||||
::
|
||||
++ add-binding
|
||||
|= [=binding action=action-new]
|
||||
|= [=binding =action]
|
||||
^- [(list move) server-state]
|
||||
=^ success bindings.state
|
||||
:: prevent binding in reserved namespaces
|
||||
@ -2072,7 +2046,7 @@
|
||||
%_ state
|
||||
bindings
|
||||
%+ skip bindings.state
|
||||
|= [item-binding=^binding item-duct=^duct action=action-new]
|
||||
|= [item-binding=^binding item-duct=^duct =action]
|
||||
^- ?
|
||||
&(=(item-binding binding) =(item-duct duct))
|
||||
==
|
||||
@ -2080,7 +2054,7 @@
|
||||
::
|
||||
++ get-action-for-binding
|
||||
|= [raw-host=(unit @t) url=@t]
|
||||
^- [action=action-new suburl=@t]
|
||||
^- [=action suburl=@t]
|
||||
:: process :raw-host
|
||||
::
|
||||
:: If we are missing a 'Host:' header, if that header is a raw IP
|
||||
@ -2193,8 +2167,8 @@
|
||||
:: +insert-binding: add a new binding, replacing any existing at its path
|
||||
::
|
||||
++ insert-binding
|
||||
|= $: new=[=binding =duct action=action-new]
|
||||
bindings=(list [=binding =duct action=action-new])
|
||||
|= $: new=[=binding =duct =action]
|
||||
bindings=(list [=binding =duct =action])
|
||||
==
|
||||
^+ bindings
|
||||
?~ bindings [new]~
|
||||
@ -2257,7 +2231,7 @@
|
||||
::
|
||||
=. bindings.server-state.ax
|
||||
=- (roll - insert-binding)
|
||||
^- (list [binding ^duct action-new])
|
||||
^- (list [binding ^duct action])
|
||||
:~ [[~ /~/login] duct [%authentication ~]]
|
||||
[[~ /~/logout] duct [%logout ~]]
|
||||
[[~ /~/channel] duct [%channel ~]]
|
||||
@ -2635,31 +2609,6 @@
|
||||
--
|
||||
::
|
||||
++ http-server-gate ..$
|
||||
:: +bindings-old: filter /~/name endpoint from bindings
|
||||
::
|
||||
++ bindings-old
|
||||
|= new=(list [b=binding d=duct a=action-new])
|
||||
=| old=(list [binding duct action])
|
||||
|- ^+ old
|
||||
?~ new old
|
||||
=/ l
|
||||
?: ?=([%name ~] a.i.new)
|
||||
old
|
||||
(snoc old [b.i.new d.i.new a.i.new])
|
||||
$(new t.new, old l)
|
||||
:: +connections-old: filter /~/name endpoint from connections
|
||||
::
|
||||
++ connections-old
|
||||
|= new=(map duct outstanding-connection-new)
|
||||
=| old=(map duct outstanding-connection)
|
||||
=/ l=(list [d=duct o=outstanding-connection-new]) ~(tap by new)
|
||||
|- ^+ old
|
||||
?~ l old
|
||||
=/ x
|
||||
?: ?=([%name ~] -.o.i.l)
|
||||
old
|
||||
(~(put by old) d.i.l o.i.l)
|
||||
$(l t.l, old x)
|
||||
:: +load: migrate old state to new state (called on vane reload)
|
||||
::
|
||||
++ load
|
||||
@ -2714,9 +2663,9 @@
|
||||
=* who p.why
|
||||
?: =(tyl /whey)
|
||||
=/ maz=(list mass)
|
||||
:~ bindings+&+(bindings-old bindings.server-state.ax)
|
||||
:~ bindings+&+bindings.server-state.ax
|
||||
auth+&+authentication-state.server-state.ax
|
||||
connections+&+(connections-old connections.server-state.ax)
|
||||
connections+&+connections.server-state.ax
|
||||
channels+&+channel-state.server-state.ax
|
||||
axle+&+ax
|
||||
==
|
||||
@ -2756,8 +2705,8 @@
|
||||
?. ?=(%$ ren)
|
||||
[~ ~]
|
||||
?+ syd [~ ~]
|
||||
%bindings ``noun+!>((bindings-old bindings.server-state.ax))
|
||||
%connections ``noun+!>((connections-old connections.server-state.ax))
|
||||
%bindings ``noun+!>(bindings.server-state.ax)
|
||||
%connections ``noun+!>(connections.server-state.ax)
|
||||
%authentication-state ``noun+!>(authentication-state.server-state.ax)
|
||||
%channel-state ``noun+!>(channel-state.server-state.ax)
|
||||
::
|
||||
|
@ -4,7 +4,7 @@
|
||||
=> ..lull
|
||||
~% %zuse ..part ~
|
||||
|%
|
||||
++ zuse %415
|
||||
++ zuse %414
|
||||
:: :: ::
|
||||
:::: :: :: (2) engines
|
||||
:: :: ::
|
||||
|
@ -14,8 +14,8 @@
|
||||
=/ [b-path=path shallow=flag] ?:(?=([^ *] b) b [`path`b |])
|
||||
=/ a-beam (need (de-beam a-path))
|
||||
=/ b-beam (need (de-beam b-path))
|
||||
;< a-dome=dome bind:m (get-from-clay a-beam dome %v)
|
||||
;< b-dome=dome bind:m (get-from-clay b-beam dome %v)
|
||||
;< a-domo=domo bind:m (get-from-clay a-beam domo %v)
|
||||
;< b-domo=domo bind:m (get-from-clay b-beam domo %v)
|
||||
;< diffs=(list diff-type) bind:m (diff-beams a-beam b-beam)
|
||||
%- pure:m
|
||||
!> ^- tang
|
||||
|
@ -733,7 +733,7 @@
|
||||
::
|
||||
%eth-get-block-by-number
|
||||
:- 'eth_getBlockByNumber'
|
||||
:~ (tape (num-to-hex bon.req))
|
||||
:~ (tape (num-to-hex-minimal bon.req))
|
||||
b+txs.req
|
||||
==
|
||||
::
|
||||
@ -942,6 +942,12 @@
|
||||
%- render-hex-bytes
|
||||
(as-octs:mimes:html n)
|
||||
::
|
||||
++ num-to-hex-minimal
|
||||
|= n=@
|
||||
^- tape
|
||||
%- prefix-hex
|
||||
((x-co:co 1) n)
|
||||
::
|
||||
++ address-to-hex
|
||||
|= a=address
|
||||
^- tape
|
||||
|
@ -73,9 +73,9 @@
|
||||
?~ sink [hash]~
|
||||
(mergebase-hashes our syd now her.u.sink sud.u.sink)
|
||||
=/ dek (~(got by tyr) syd)
|
||||
=/ =foam (~(got by cone) our syd)
|
||||
=/ =dome (~(got by cone) our syd)
|
||||
=/ [on=(list [@tas ?]) of=(list [@tas ?])]
|
||||
(skid ~(tap by ren.foam) |=([* ?] +<+))
|
||||
(skid ~(tap by ren.dome) |=([* ?] +<+))
|
||||
=/ sat
|
||||
?- zest.dek
|
||||
%live "running"
|
||||
@ -130,7 +130,7 @@
|
||||
=/ syd (scot %tas desk)
|
||||
=/ yon (scot %ud aeon)
|
||||
::
|
||||
=/ dom .^(dome cv/~[her syd yon])
|
||||
=/ dom .^(domo cv/~[her syd yon])
|
||||
=/ tak ~| aeons=~(key by hit.dom)
|
||||
(scot %uv (~(got by hit.dom) aeon))
|
||||
=/ yak .^(yaki cs/~[her syd yon %yaki tak])
|
||||
|
@ -1 +0,0 @@
|
||||
Each one of the folders in this directory is published at `@urbit/{folder name}`
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": "> 1%",
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.19.1"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
extends: '@urbit'
|
||||
};
|
1
pkg/npm/api/.gitignore
vendored
1
pkg/npm/api/.gitignore
vendored
@ -1 +0,0 @@
|
||||
tmp
|
@ -1,41 +0,0 @@
|
||||
# Urbit API in JavaScript
|
||||
|
||||
This package simplifies the process of working with Urbit's APIs into fluent, typed functions organized by app. Pairs well with `@urbit/http-api`. Compare:
|
||||
|
||||
Without:
|
||||
```ts
|
||||
import UrbitInterface from '@urbit/http-api';
|
||||
const api: UrbitInterface = useApi();
|
||||
api.poke({
|
||||
app: 'settings-store',
|
||||
mark: 'settings-event',
|
||||
json: {
|
||||
'put-entry': {
|
||||
'bucket-key': bucket,
|
||||
'entry-key': key,
|
||||
'value': value
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
With:
|
||||
```ts
|
||||
import UrbitInterface from '@urbit/http-api';
|
||||
import { settings } from '@urbit/api';
|
||||
const api: UrbitInterface = useApi();
|
||||
api.poke(settings.putEntry(bucket, key, value));
|
||||
```
|
||||
|
||||
You may import single functions
|
||||
```ts
|
||||
import { putEntry } from '@urbit/api';
|
||||
```
|
||||
or whole apps:
|
||||
```ts
|
||||
import { settings } from '@urbit/api';
|
||||
```
|
||||
|
||||
This package also provides types and utilities for working with Urbit's internal data structures, such as Nouns, Das, Tas, and so forth.
|
||||
|
||||
This package was originally developed as part of Tlon's Landscape client and therefore the best reference material exists [there](https://github.com/urbit/urbit/tree/master/pkg/interface/src).
|
@ -1,2 +0,0 @@
|
||||
export * from './types';
|
||||
export * from './lib';
|
@ -1,109 +0,0 @@
|
||||
|
||||
import { Patp, Poke, Scry } from '../lib';
|
||||
import {
|
||||
Contact,
|
||||
ContactUpdateAdd,
|
||||
ContactUpdateEdit,
|
||||
ContactUpdateRemove,
|
||||
ContactEditField,
|
||||
ContactShare,
|
||||
ContactUpdate,
|
||||
ContactUpdateAllowShips,
|
||||
ContactUpdateAllowGroup,
|
||||
ContactUpdateSetPublic
|
||||
} from './types';
|
||||
|
||||
export const CONTACT_UPDATE_VERSION = 0;
|
||||
|
||||
const storeAction = <T extends ContactUpdate>(data: T, version: number = CONTACT_UPDATE_VERSION): Poke<T> => ({
|
||||
app: 'contact-store',
|
||||
mark: `contact-update-${version}`,
|
||||
json: data
|
||||
});
|
||||
|
||||
export { storeAction as contactStoreAction };
|
||||
|
||||
export const addContact = (ship: Patp, contact: Contact): Poke<ContactUpdateAdd> => {
|
||||
contact['last-updated'] = Date.now();
|
||||
|
||||
return storeAction({
|
||||
add: { ship, contact }
|
||||
});
|
||||
};
|
||||
|
||||
export const removeContact = (ship: Patp): Poke<ContactUpdateRemove> =>
|
||||
storeAction({
|
||||
remove: { ship }
|
||||
});
|
||||
|
||||
export const share = (recipient: Patp, version: number = CONTACT_UPDATE_VERSION): Poke<ContactShare> => ({
|
||||
app: 'contact-push-hook',
|
||||
mark: 'contact-share',
|
||||
json: { share: recipient }
|
||||
});
|
||||
|
||||
export const editContact = (
|
||||
ship: Patp,
|
||||
editField: ContactEditField
|
||||
): Poke<ContactUpdateEdit> =>
|
||||
storeAction({
|
||||
edit: {
|
||||
ship,
|
||||
'edit-field': editField,
|
||||
timestamp: Date.now()
|
||||
}
|
||||
});
|
||||
|
||||
export const allowShips = (
|
||||
ships: Patp[]
|
||||
): Poke<ContactUpdateAllowShips> => storeAction({
|
||||
allow: {
|
||||
ships
|
||||
}
|
||||
});
|
||||
|
||||
export const allowGroup = (
|
||||
ship: string,
|
||||
name: string
|
||||
): Poke<ContactUpdateAllowGroup> => storeAction({
|
||||
allow: {
|
||||
group: { ship, name }
|
||||
}
|
||||
});
|
||||
|
||||
export const setPublic = (
|
||||
setPublic: any
|
||||
): Poke<ContactUpdateSetPublic> => {
|
||||
return storeAction({
|
||||
'set-public': setPublic
|
||||
});
|
||||
};
|
||||
|
||||
export const retrieve = (
|
||||
ship: string
|
||||
) => {
|
||||
const resource = { ship, name: '' };
|
||||
return {
|
||||
app: 'contact-pull-hook',
|
||||
mark: 'pull-hook-action',
|
||||
json: {
|
||||
add: {
|
||||
resource,
|
||||
ship
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchIsAllowed = (
|
||||
entity: string,
|
||||
name: string,
|
||||
ship: string,
|
||||
personal: boolean
|
||||
): Scry => {
|
||||
const isPersonal = personal ? 'true' : 'false';
|
||||
return {
|
||||
app: 'contact-store',
|
||||
path: `/is-allowed/${entity}/${name}/${ship}/${isPersonal}`
|
||||
};
|
||||
};
|
@ -1,82 +0,0 @@
|
||||
import { Path, Patp } from '../lib';
|
||||
import { Resource } from '../groups';
|
||||
|
||||
export type ContactUpdate =
|
||||
| ContactUpdateAdd
|
||||
| ContactUpdateRemove
|
||||
| ContactUpdateEdit
|
||||
| ContactUpdateInitial
|
||||
| ContactUpdateAllowGroup
|
||||
| ContactUpdateAllowShips
|
||||
| ContactUpdateSetPublic;
|
||||
|
||||
export interface ContactUpdateAdd {
|
||||
add: {
|
||||
ship: Patp;
|
||||
contact: Contact;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ContactUpdateRemove {
|
||||
remove: {
|
||||
ship: Patp;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ContactUpdateEdit {
|
||||
edit: {
|
||||
ship: Patp;
|
||||
'edit-field': ContactEditField;
|
||||
timestamp: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ContactUpdateAllowShips {
|
||||
allow: {
|
||||
ships: Patp[];
|
||||
}
|
||||
}
|
||||
|
||||
export interface ContactUpdateAllowGroup {
|
||||
allow: {
|
||||
group: Resource;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ContactUpdateSetPublic {
|
||||
'set-public': boolean;
|
||||
}
|
||||
|
||||
export interface ContactShare {
|
||||
share: Patp;
|
||||
}
|
||||
|
||||
export interface ContactUpdateInitial {
|
||||
initial: Rolodex;
|
||||
}
|
||||
|
||||
export type Rolodex = {
|
||||
[p in Patp]: Contact;
|
||||
};
|
||||
|
||||
export type Contacts = Rolodex;
|
||||
|
||||
export interface Contact {
|
||||
nickname: string;
|
||||
bio: string;
|
||||
status: string;
|
||||
color: string;
|
||||
avatar: string | null;
|
||||
cover: string | null;
|
||||
groups: Path[];
|
||||
'last-updated': number;
|
||||
}
|
||||
|
||||
type ContactKeys = keyof Contact;
|
||||
|
||||
export type ContactEditFieldPrim = Exclude<ContactKeys, 'groups' | 'last-updated'>;
|
||||
|
||||
export type ContactEditField = Partial<Pick<Contact, ContactEditFieldPrim>> & {
|
||||
'add-group'?: Resource;
|
||||
'remove-group'?: Resource;
|
||||
};
|
8
pkg/npm/api/deps.d.ts
vendored
8
pkg/npm/api/deps.d.ts
vendored
@ -1,8 +0,0 @@
|
||||
|
||||
declare module 'urbit-ob' {
|
||||
|
||||
/**
|
||||
* Convert a @p-encoded string to a decimal-encoded string.
|
||||
*/
|
||||
function patp2dec(name: string): string
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
export * from './lib';
|
||||
export * from './types';
|
@ -1,65 +0,0 @@
|
||||
import { Poke, Scry } from '../lib';
|
||||
import { Chad } from './types';
|
||||
|
||||
export function chadIsRunning(chad: Chad) {
|
||||
return 'glob' in chad || 'site' in chad;
|
||||
}
|
||||
|
||||
export const scryCharges: Scry = {
|
||||
app: 'docket',
|
||||
path: '/charges'
|
||||
};
|
||||
|
||||
export const scryDockets: Scry = {
|
||||
app: 'docket',
|
||||
path: '/dockets'
|
||||
};
|
||||
|
||||
export const scryTreaties: Scry = {
|
||||
app: 'treaty',
|
||||
path: '/treaties'
|
||||
};
|
||||
|
||||
export const scryDefaultAlly: Scry = {
|
||||
app: 'treaty',
|
||||
path: '/default-ally'
|
||||
};
|
||||
|
||||
export const scryAllies: Scry = {
|
||||
app: 'treaty',
|
||||
path: '/allies'
|
||||
};
|
||||
|
||||
export const scryAllyTreaties = (ship: string): Scry => ({
|
||||
app: 'treaty',
|
||||
path: `/treaties/${ship}`
|
||||
});
|
||||
|
||||
/**
|
||||
* Uninstall a desk, and remove docket
|
||||
*/
|
||||
export function docketUninstall(desk: string): Poke<string> {
|
||||
return {
|
||||
app: 'docket',
|
||||
mark: 'docket-uninstall',
|
||||
json: desk
|
||||
};
|
||||
}
|
||||
|
||||
export function docketInstall(ship: string, desk: string): Poke<any> {
|
||||
return {
|
||||
app: 'docket',
|
||||
mark: 'docket-install',
|
||||
json: `${ship}/${desk}`
|
||||
};
|
||||
}
|
||||
|
||||
export function allyShip(ship: string): Poke<any> {
|
||||
return {
|
||||
app: 'treaty',
|
||||
mark: 'ally-update-0',
|
||||
json: {
|
||||
add: ship
|
||||
}
|
||||
};
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
import { Cass } from '../hood';
|
||||
export type DeskStatus = 'active' | 'suspended';
|
||||
|
||||
export type DocketHref = DocketHrefSite | DocketHrefGlob;
|
||||
|
||||
export interface DocketHrefGlob {
|
||||
glob: {
|
||||
base: string;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export interface DocketHrefSite {
|
||||
site: string;
|
||||
}
|
||||
|
||||
export interface Docket {
|
||||
title: string;
|
||||
info?: string;
|
||||
color: string;
|
||||
href: DocketHref;
|
||||
website: string;
|
||||
license: string;
|
||||
version: string;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
export interface Charge extends Docket {
|
||||
chad: Chad;
|
||||
}
|
||||
|
||||
export type Chad = HungChad | GlobChad | SiteChad | InstallChad | SuspendChad;
|
||||
|
||||
export interface HungChad {
|
||||
hung: string;
|
||||
}
|
||||
|
||||
export interface GlobChad {
|
||||
glob: null;
|
||||
}
|
||||
export interface SiteChad {
|
||||
site: null;
|
||||
}
|
||||
export interface InstallChad {
|
||||
install: null;
|
||||
|
||||
}
|
||||
export interface SuspendChad {
|
||||
suspend: null;
|
||||
}
|
||||
|
||||
export interface Treaty extends Docket {
|
||||
ship: string;
|
||||
desk: string;
|
||||
cass: Cass;
|
||||
hash: string;
|
||||
}
|
||||
|
||||
export interface Charges {
|
||||
[desk: string]: Charge;
|
||||
}
|
||||
|
||||
export interface Treaties {
|
||||
[ref: string]: Treaty;
|
||||
}
|
||||
|
||||
export type Charter = string[];
|
||||
|
||||
export interface Allies {
|
||||
[ship: string]: Charter;
|
||||
}
|
||||
|
||||
export interface Provider {
|
||||
shipName: string;
|
||||
nickname?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export type ChargeUpdate = ChargeUpdateInitial | ChargeUpdateAdd | ChargeUpdateDel;
|
||||
|
||||
export interface ChargeUpdateInitial {
|
||||
initial: {
|
||||
[desk: string]: Charge;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChargeUpdateAdd {
|
||||
'add-charge': {
|
||||
desk: string;
|
||||
charge: Charge;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChargeUpdateDel {
|
||||
'del-charge': string;
|
||||
}
|
||||
|
||||
export type AllyUpdate = AllyUpdateIni | AllyUpdateAdd | AllyUpdateDel | AllyUpdateNew;
|
||||
|
||||
export interface AllyUpdateIni {
|
||||
ini: {
|
||||
[ship: string]: string[];
|
||||
}
|
||||
}
|
||||
|
||||
export interface AllyUpdateAdd {
|
||||
add: string;
|
||||
}
|
||||
|
||||
export interface AllyUpdateDel {
|
||||
del: string;
|
||||
}
|
||||
|
||||
export interface AllyUpdateNew {
|
||||
new: {
|
||||
ship: string;
|
||||
alliance: string[];
|
||||
}
|
||||
}
|
||||
|
||||
export type TreatyUpdate = TreatyUpdateIni | TreatyUpdateAdd | TreatyUpdateDel;
|
||||
|
||||
export interface TreatyUpdateIni {
|
||||
ini: {
|
||||
[foreignDesk: string]: Treaty;
|
||||
}
|
||||
}
|
||||
|
||||
export interface TreatyUpdateAdd {
|
||||
add: Treaty;
|
||||
}
|
||||
|
||||
export interface TreatyUpdateDel {
|
||||
del: string;
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
export * from './lib';
|
||||
export * from './types';
|
@ -1,534 +0,0 @@
|
||||
import { GroupPolicy, makeResource, Resource, resourceFromPath } from '../groups';
|
||||
|
||||
import { decToUd, deSig, unixToDa, Scry } from '../lib';
|
||||
import { Enc, Path, Patp, PatpNoSig, Poke, Thread } from '../lib/types';
|
||||
import { Content, GraphChildrenPoke, GraphNode, GraphNodePoke, Post } from './types';
|
||||
import { patp2dec } from 'urbit-ob';
|
||||
|
||||
export const GRAPH_UPDATE_VERSION = 3;
|
||||
|
||||
export const createBlankNodeWithChildPost = (
|
||||
ship: PatpNoSig,
|
||||
parentIndex = '',
|
||||
childIndex = '',
|
||||
contents: Content[]
|
||||
): GraphNodePoke => {
|
||||
const date = unixToDa(Date.now()).toString();
|
||||
const nodeIndex = parentIndex + '/' + date;
|
||||
|
||||
const childGraph: GraphChildrenPoke = {};
|
||||
childGraph[childIndex] = {
|
||||
post: {
|
||||
author: `~${ship}`,
|
||||
index: nodeIndex + '/' + childIndex,
|
||||
'time-sent': Date.now(),
|
||||
contents,
|
||||
hash: null,
|
||||
signatures: []
|
||||
},
|
||||
children: null
|
||||
};
|
||||
|
||||
return {
|
||||
post: {
|
||||
author: `~${ship}`,
|
||||
index: nodeIndex,
|
||||
'time-sent': Date.now(),
|
||||
contents: [],
|
||||
hash: null,
|
||||
signatures: []
|
||||
},
|
||||
children: childGraph
|
||||
};
|
||||
};
|
||||
|
||||
export const markPending = (nodes: any): any => {
|
||||
Object.keys(nodes).forEach((key) => {
|
||||
nodes[key].post.author = deSig(nodes[key].post.author);
|
||||
nodes[key].post.pending = true;
|
||||
if (nodes[key].children) {
|
||||
nodes[key].children = markPending(nodes[key].children);
|
||||
}
|
||||
});
|
||||
return nodes;
|
||||
};
|
||||
|
||||
export const createPost = (
|
||||
ship: PatpNoSig,
|
||||
contents: Content[],
|
||||
parentIndex = '',
|
||||
childIndex = 'DATE_PLACEHOLDER'
|
||||
): Post => {
|
||||
if (childIndex === 'DATE_PLACEHOLDER') {
|
||||
childIndex = unixToDa(Date.now()).toString();
|
||||
}
|
||||
return {
|
||||
author: `~${ship}`,
|
||||
index: parentIndex + '/' + childIndex,
|
||||
'time-sent': Date.now(),
|
||||
contents,
|
||||
hash: null,
|
||||
signatures: []
|
||||
};
|
||||
};
|
||||
|
||||
function moduleToMark(mod: string): string | undefined {
|
||||
if(mod === 'link') {
|
||||
return 'graph-validator-link';
|
||||
}
|
||||
if(mod === 'publish') {
|
||||
return 'graph-validator-publish';
|
||||
}
|
||||
if(mod === 'chat') {
|
||||
return 'graph-validator-chat';
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const storeAction = <T>(data: T, version: number = GRAPH_UPDATE_VERSION): Poke<T> => ({
|
||||
app: 'graph-store',
|
||||
mark: `graph-update-${version}`,
|
||||
json: data
|
||||
});
|
||||
|
||||
export { storeAction as graphStoreAction };
|
||||
|
||||
const viewAction = <T>(threadName: string, action: T): Thread<T> => ({
|
||||
inputMark: 'graph-view-action',
|
||||
outputMark: 'json',
|
||||
threadName,
|
||||
body: action
|
||||
});
|
||||
|
||||
export { viewAction as graphViewAction };
|
||||
|
||||
const hookAction = <T>(data: T, version: number = GRAPH_UPDATE_VERSION): Poke<T> => ({
|
||||
app: 'graph-push-hook',
|
||||
mark: `graph-update-${version}`,
|
||||
json: data
|
||||
});
|
||||
|
||||
const dmAction = <T>(data: T): Poke<T> => ({
|
||||
app: 'dm-hook',
|
||||
mark: 'dm-hook-action',
|
||||
json: data
|
||||
});
|
||||
|
||||
export { hookAction as graphHookAction };
|
||||
|
||||
export const createManagedGraph = (
|
||||
ship: PatpNoSig,
|
||||
name: string,
|
||||
title: string,
|
||||
description: string,
|
||||
group: Path,
|
||||
mod: string
|
||||
): Thread<any> => {
|
||||
const associated = { group: resourceFromPath(group) };
|
||||
const resource = makeResource(`~${ship}`, name);
|
||||
|
||||
return viewAction('graph-create', {
|
||||
create: {
|
||||
resource,
|
||||
title,
|
||||
description,
|
||||
associated,
|
||||
module: mod,
|
||||
mark: moduleToMark(mod)
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const createUnmanagedGraph = (
|
||||
ship: PatpNoSig,
|
||||
name: string,
|
||||
title: string,
|
||||
description: string,
|
||||
policy: Enc<GroupPolicy>,
|
||||
mod: string
|
||||
): Thread<any> => viewAction('graph-create', {
|
||||
create: {
|
||||
resource: makeResource(`~${ship}`, name),
|
||||
title,
|
||||
description,
|
||||
associated: { policy },
|
||||
module: mod,
|
||||
mark: moduleToMark(mod)
|
||||
}
|
||||
});
|
||||
|
||||
export const joinGraph = (
|
||||
ship: Patp,
|
||||
name: string
|
||||
): Thread<any> => viewAction('graph-join', {
|
||||
join: {
|
||||
resource: makeResource(ship, name),
|
||||
ship
|
||||
}
|
||||
});
|
||||
|
||||
export const deleteGraph = (
|
||||
ship: PatpNoSig,
|
||||
name: string
|
||||
): Thread<any> => viewAction('graph-delete', {
|
||||
'delete': {
|
||||
resource: makeResource(`~${ship}`, name)
|
||||
}
|
||||
});
|
||||
|
||||
export const leaveGraph = (
|
||||
ship: Patp,
|
||||
name: string
|
||||
): Thread<any> => viewAction('graph-leave', {
|
||||
'leave': {
|
||||
resource: makeResource(ship, name)
|
||||
}
|
||||
});
|
||||
|
||||
export const groupifyGraph = (
|
||||
ship: Patp,
|
||||
name: string,
|
||||
toPath?: string
|
||||
): Thread<any> => {
|
||||
const resource = makeResource(ship, name);
|
||||
const to = toPath && resourceFromPath(toPath);
|
||||
|
||||
return viewAction('graph-groupify', {
|
||||
groupify: {
|
||||
resource,
|
||||
to
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const evalCord = (
|
||||
cord: string
|
||||
): Thread<any> => {
|
||||
return ({
|
||||
inputMark: 'graph-view-action',
|
||||
outputMark: 'tang',
|
||||
threadName: 'graph-eval',
|
||||
body: {
|
||||
eval: cord
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const addGraph = (
|
||||
ship: Patp,
|
||||
name: string,
|
||||
graph: any,
|
||||
mark: any
|
||||
): Poke<any> => {
|
||||
return storeAction({
|
||||
'add-graph': {
|
||||
resource: { ship, name },
|
||||
graph,
|
||||
mark
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const addNodes = (
|
||||
ship: Patp,
|
||||
name: string,
|
||||
nodes: Object
|
||||
): Thread<any> => ({
|
||||
inputMark: `graph-update-${GRAPH_UPDATE_VERSION}`,
|
||||
outputMark: 'graph-view-action',
|
||||
threadName: 'graph-add-nodes',
|
||||
body: {
|
||||
'add-nodes': {
|
||||
resource: { ship, name },
|
||||
nodes
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const addPost = (
|
||||
ship: Patp,
|
||||
name: string,
|
||||
post: Post
|
||||
): Thread<any> => {
|
||||
const nodes: Record<string, GraphNode> = {};
|
||||
nodes[post.index] = {
|
||||
post,
|
||||
children: null
|
||||
};
|
||||
return addNodes(ship, name, nodes);
|
||||
};
|
||||
|
||||
export const addNode = (
|
||||
ship: Patp,
|
||||
name: string,
|
||||
node: GraphNodePoke
|
||||
): Thread<any> => {
|
||||
const nodes: Record<string, GraphNodePoke> = {};
|
||||
nodes[node.post.index] = node;
|
||||
|
||||
return addNodes(ship, name, nodes);
|
||||
};
|
||||
|
||||
export const createGroupFeed = (
|
||||
group: Resource,
|
||||
vip: any = ''
|
||||
): Thread<any> => ({
|
||||
inputMark: 'graph-view-action',
|
||||
outputMark: 'resource',
|
||||
threadName: 'graph-create-group-feed',
|
||||
body: {
|
||||
'create-group-feed': {
|
||||
resource: group,
|
||||
vip
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const disableGroupFeed = (
|
||||
group: Resource
|
||||
): Thread<any> => ({
|
||||
inputMark: 'graph-view-action',
|
||||
outputMark: 'json',
|
||||
threadName: 'graph-disable-group-feed',
|
||||
body: {
|
||||
'disable-group-feed': {
|
||||
resource: group
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Set dm-hook to screen new DMs or not
|
||||
*
|
||||
*/
|
||||
export const setScreen = (screen: boolean): Poke<any> => dmAction({ screen });
|
||||
|
||||
/**
|
||||
* Accept a pending DM request
|
||||
*
|
||||
* @param ship the ship to accept
|
||||
*/
|
||||
export const acceptDm = (ship: string) => dmAction({
|
||||
accept: ship
|
||||
});
|
||||
|
||||
/**
|
||||
* Decline a pending DM request
|
||||
*
|
||||
* @param ship the ship to accept
|
||||
*/
|
||||
export const declineDm = (ship: string) => dmAction({
|
||||
decline: ship
|
||||
});
|
||||
|
||||
/**
|
||||
* Remove posts from a set of indices
|
||||
*
|
||||
*/
|
||||
export const removePosts = (
|
||||
ship: Patp,
|
||||
name: string,
|
||||
indices: string[]
|
||||
): Poke<any> => hookAction({
|
||||
'remove-posts': {
|
||||
resource: { ship, name },
|
||||
indices
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Remove a DM message from our inbox
|
||||
*
|
||||
* @remarks
|
||||
* This does not remove the message from the recipients inbox
|
||||
*/
|
||||
export const removeDmMessage = (
|
||||
our: Patp,
|
||||
index: string
|
||||
): Poke<any> => ({
|
||||
app: 'graph-store',
|
||||
mark: `graph-update-${GRAPH_UPDATE_VERSION}`,
|
||||
json: {
|
||||
'remove-posts': {
|
||||
resource: { ship: our, name: 'dm-inbox' },
|
||||
indices: [index]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Send a DM to a ship
|
||||
*
|
||||
* @param our sender
|
||||
* @param ship recipient
|
||||
* @param contents contents of message
|
||||
*/
|
||||
export const addDmMessage = (our: PatpNoSig, ship: Patp, contents: Content[]): Poke<any> => {
|
||||
const post = createPost(our, contents, `/${patp2dec(ship)}`);
|
||||
const node: GraphNode = {
|
||||
post,
|
||||
children: null
|
||||
};
|
||||
return {
|
||||
app: 'dm-hook',
|
||||
mark: `graph-update-${GRAPH_UPDATE_VERSION}`,
|
||||
json: {
|
||||
'add-nodes': {
|
||||
resource: { ship: `~${our}`, name: 'dm-inbox' },
|
||||
nodes: {
|
||||
[post.index]: node
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const encodeIndex = (idx: string) => idx.split('/').map(decToUd).join('/');
|
||||
|
||||
/**
|
||||
* Fetch all graph keys
|
||||
*/
|
||||
export const getKeys = (): Scry => ({
|
||||
app: 'graph-store',
|
||||
path: '/keys'
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch newest (larger keys) nodes in a graph under some index
|
||||
*
|
||||
* @param ship ship of graph
|
||||
* @param name name of graph
|
||||
* @param count number of nodes to load
|
||||
* @param index index to query
|
||||
*/
|
||||
export const getNewest = (
|
||||
ship: string,
|
||||
name: string,
|
||||
count: number,
|
||||
index = ''
|
||||
): Scry => ({
|
||||
app: 'graph-store',
|
||||
path: `/graph/${ship}/${name}/node/siblings` +
|
||||
`/newest/lone/${count}${encodeIndex(index)}`
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch nodes in a graph that are older (key is smaller) and direct
|
||||
* siblings of some index
|
||||
*
|
||||
* @param ship ship of graph
|
||||
* @param name name of graph
|
||||
* @param count number of nodes to load
|
||||
* @param index index to query
|
||||
*/
|
||||
export const getOlderSiblings = (
|
||||
ship: string,
|
||||
name: string,
|
||||
count: number,
|
||||
index: string
|
||||
): Scry => ({
|
||||
app: 'graph-store',
|
||||
path: `/graph/${ship}/${name}/node/siblings/older/lone/${count}${encodeIndex(index)}`
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch nodes in a graph that are younger (key is larger) and direct
|
||||
* siblings of some index
|
||||
*
|
||||
* @param ship ship of graph
|
||||
* @param name name of graph
|
||||
* @param count number of nodes to load
|
||||
* @param index index to query
|
||||
*/
|
||||
export const getYoungerSiblings = (
|
||||
ship: string,
|
||||
name: string,
|
||||
count: number,
|
||||
index: string
|
||||
): Scry => ({
|
||||
app: 'graph-store',
|
||||
path: `/graph/${ship}/${name}/node/siblings/newer/lone/${count}${encodeIndex(index)}`
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch all nodes in a graph under some index, without loading children
|
||||
*
|
||||
* @param ship ship of graph
|
||||
* @param name name of graph
|
||||
* @param index index to query
|
||||
*/
|
||||
export const getShallowChildren = (ship: string, name: string, index = '') => ({
|
||||
app: 'graph-store',
|
||||
path: `/graph/${ship}/${name}/node/children/lone/~/~${encodeIndex(index)}`
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch newest nodes in a graph as a flat map, including children,
|
||||
* optionally starting at a specified key
|
||||
*
|
||||
* @param ship ship of graph
|
||||
* @param name name of graph
|
||||
* @param count number of nodes to load
|
||||
* @param start key to start at
|
||||
*
|
||||
*/
|
||||
export const getDeepOlderThan = (
|
||||
ship: string,
|
||||
name: string,
|
||||
count: number,
|
||||
start = ''
|
||||
) => ({
|
||||
app: 'graph-store',
|
||||
path: `/graph/${ship}/${name}/node/siblings` +
|
||||
`/${start.length > 0 ? 'older' : 'newest'}` +
|
||||
`/kith/${count}${encodeIndex(start)}`
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch a flat map of a nodes ancestors and firstborn children
|
||||
*
|
||||
* @param ship ship of graph
|
||||
* @param name name of graph
|
||||
* @param index index to query
|
||||
*
|
||||
*/
|
||||
export const getFirstborn = (
|
||||
ship: string,
|
||||
name: string,
|
||||
index: string
|
||||
): Scry => ({
|
||||
app: 'graph-store',
|
||||
path: `/graph/${ship}/${name}/node/firstborn${encodeIndex(index)}`
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch a single node, and all it's children
|
||||
*
|
||||
* @param ship ship of graph
|
||||
* @param name name of graph
|
||||
* @param index index to query
|
||||
*
|
||||
*/
|
||||
export const getNode = (
|
||||
ship: string,
|
||||
name: string,
|
||||
index: string
|
||||
): Scry => ({
|
||||
app: 'graph-store',
|
||||
path: `/graph/${ship}/${name}/node/index/kith${encodeIndex(index)}`
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch entire graph
|
||||
*
|
||||
* @param ship ship of graph
|
||||
* @param name name of graph
|
||||
*
|
||||
*/
|
||||
export const getGraph = (
|
||||
ship: string,
|
||||
name: string
|
||||
): Scry => ({
|
||||
app: 'graph-store',
|
||||
path: `/graph/${ship}/${name}`
|
||||
});
|
@ -1,94 +0,0 @@
|
||||
import { Patp } from '../lib';
|
||||
import { BigIntOrderedMap } from '../lib/BigIntOrderedMap';
|
||||
import { BigIntArrayOrderedMap } from '../lib/BigIntArrayOrderedMap';
|
||||
|
||||
export interface TextContent {
|
||||
text: string;
|
||||
}
|
||||
export interface UrlContent {
|
||||
url: string;
|
||||
}
|
||||
export interface CodeContent {
|
||||
code: {
|
||||
expression: string;
|
||||
output: string[] | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ReferenceContent {
|
||||
reference: AppReference | GraphReference | GroupReference;
|
||||
}
|
||||
|
||||
export interface GraphReference {
|
||||
graph: {
|
||||
graph: string;
|
||||
group: string;
|
||||
index: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface GroupReference {
|
||||
group: string;
|
||||
}
|
||||
|
||||
export interface AppReference {
|
||||
app: {
|
||||
ship: string;
|
||||
desk: string;
|
||||
path: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface MentionContent {
|
||||
mention: string;
|
||||
emphasis?: 'bold' | 'italic';
|
||||
}
|
||||
export type Content =
|
||||
| TextContent
|
||||
| UrlContent
|
||||
| CodeContent
|
||||
| ReferenceContent
|
||||
| MentionContent;
|
||||
|
||||
export interface Post {
|
||||
author: Patp;
|
||||
contents: Content[];
|
||||
hash: string | null;
|
||||
index: string;
|
||||
pending?: boolean;
|
||||
signatures: string[];
|
||||
'time-sent': number;
|
||||
}
|
||||
|
||||
export interface GraphNodePoke {
|
||||
post: Post;
|
||||
children: GraphChildrenPoke | null;
|
||||
}
|
||||
|
||||
export interface GraphChildrenPoke {
|
||||
[k: string]: GraphNodePoke;
|
||||
}
|
||||
|
||||
export interface GraphNode {
|
||||
children: Graph | null;
|
||||
post: Post;
|
||||
}
|
||||
|
||||
export interface FlatGraphNode {
|
||||
children: null;
|
||||
post: Post;
|
||||
}
|
||||
|
||||
export type Graph = BigIntOrderedMap<GraphNode>;
|
||||
|
||||
export type Graphs = { [rid: string]: Graph };
|
||||
|
||||
export type FlatGraph = BigIntArrayOrderedMap<FlatGraphNode>;
|
||||
|
||||
export type FlatGraphs = { [rid: string]: FlatGraph };
|
||||
|
||||
export type ThreadGraphs = {
|
||||
[rid: string]: {
|
||||
[index: string]: FlatGraph;
|
||||
}
|
||||
};
|
@ -1,2 +0,0 @@
|
||||
export * from './types';
|
||||
export * from './lib';
|
@ -1,226 +0,0 @@
|
||||
import { deSig } from '../index';
|
||||
import { Enc, Path, Patp, PatpNoSig, Poke, Thread } from '../lib/types';
|
||||
import { Group, GroupPolicy, GroupPolicyDiff, GroupUpdateAddMembers, GroupUpdateAddTag, GroupUpdateChangePolicy, GroupUpdateRemoveGroup, GroupUpdateRemoveMembers, GroupUpdateRemoveTag, Resource, RoleTags, Tag } from './types';
|
||||
import { GroupUpdate } from './update';
|
||||
|
||||
export const GROUP_UPDATE_VERSION = 0;
|
||||
|
||||
export const proxyAction = <T>(data: T, version: number = GROUP_UPDATE_VERSION): Poke<T> => ({
|
||||
app: 'group-push-hook',
|
||||
mark: `group-update-${version}`,
|
||||
json: data
|
||||
});
|
||||
|
||||
const storeAction = <T extends GroupUpdate>(data: T, version: number = GROUP_UPDATE_VERSION): Poke<T> => ({
|
||||
app: 'group-store',
|
||||
mark: `group-update-${version}`,
|
||||
json: data
|
||||
});
|
||||
|
||||
export { storeAction as groupStoreAction };
|
||||
|
||||
const viewAction = <T>(data: T): Poke<T> => ({
|
||||
app: 'group-view',
|
||||
mark: 'group-view-action',
|
||||
json: data
|
||||
});
|
||||
|
||||
export { viewAction as groupViewAction };
|
||||
|
||||
export const viewThread = <T>(thread: string, action: T): Thread<T> => ({
|
||||
inputMark: 'group-view-action',
|
||||
outputMark: 'json',
|
||||
threadName: thread,
|
||||
body: action
|
||||
});
|
||||
|
||||
export const removeMembers = (
|
||||
resource: Resource,
|
||||
ships: PatpNoSig[]
|
||||
): Poke<GroupUpdateRemoveMembers> => proxyAction({
|
||||
removeMembers: {
|
||||
resource,
|
||||
ships
|
||||
}
|
||||
});
|
||||
|
||||
export const addTag = (
|
||||
resource: Resource,
|
||||
tag: Tag,
|
||||
ships: Patp[]
|
||||
): Poke<GroupUpdateAddTag> => proxyAction({
|
||||
addTag: {
|
||||
resource,
|
||||
tag,
|
||||
ships
|
||||
}
|
||||
});
|
||||
|
||||
export const removeTag = (
|
||||
tag: Tag,
|
||||
resource: Resource,
|
||||
ships: PatpNoSig[]
|
||||
): Poke<GroupUpdateRemoveTag> => proxyAction({
|
||||
removeTag: {
|
||||
tag,
|
||||
resource,
|
||||
ships
|
||||
}
|
||||
});
|
||||
|
||||
export const addMembers = (
|
||||
resource: Resource,
|
||||
ships: PatpNoSig[]
|
||||
): Poke<GroupUpdateAddMembers> => proxyAction({
|
||||
addMembers: {
|
||||
resource,
|
||||
ships
|
||||
}
|
||||
});
|
||||
|
||||
export const removeGroup = (
|
||||
resource: Resource
|
||||
): Poke<GroupUpdateRemoveGroup> => storeAction({
|
||||
removeGroup: {
|
||||
resource
|
||||
}
|
||||
});
|
||||
|
||||
export const changePolicy = (
|
||||
resource: Resource,
|
||||
diff: Enc<GroupPolicyDiff>
|
||||
): Poke<Enc<GroupUpdateChangePolicy>> => proxyAction({
|
||||
changePolicy: {
|
||||
resource,
|
||||
diff
|
||||
}
|
||||
});
|
||||
|
||||
export const join = (
|
||||
ship: string,
|
||||
name: string,
|
||||
app: "groups" | "graph",
|
||||
autojoin: boolean,
|
||||
share: boolean
|
||||
): Poke<any> => viewAction({
|
||||
join: {
|
||||
resource: makeResource(ship, name),
|
||||
ship,
|
||||
shareContact: share || false,
|
||||
app,
|
||||
autojoin
|
||||
}
|
||||
});
|
||||
|
||||
export const createGroup = (
|
||||
name: string,
|
||||
policy: Enc<GroupPolicy>,
|
||||
title: string,
|
||||
description: string
|
||||
): Thread<any> => viewThread('group-create', {
|
||||
create: {
|
||||
name,
|
||||
policy,
|
||||
title,
|
||||
description
|
||||
}
|
||||
});
|
||||
|
||||
export const deleteGroup = (
|
||||
ship: string,
|
||||
name: string
|
||||
): Thread<any> => viewThread('group-delete', {
|
||||
remove: makeResource(ship, name)
|
||||
});
|
||||
|
||||
export const leaveGroup = (
|
||||
ship: string,
|
||||
name: string
|
||||
): Thread<any> => viewThread('group-leave', {
|
||||
leave: makeResource(ship, name)
|
||||
});
|
||||
|
||||
export const invite = (
|
||||
ship: string,
|
||||
name: string,
|
||||
ships: Patp[],
|
||||
description: string
|
||||
): Thread<any> => viewThread('group-invite', {
|
||||
invite: {
|
||||
resource: makeResource(ship, name),
|
||||
ships,
|
||||
description
|
||||
}
|
||||
});
|
||||
|
||||
export const abortJoin = (
|
||||
resource: string
|
||||
): Poke<any> => viewAction({
|
||||
abort: resource
|
||||
});
|
||||
|
||||
export const roleTags = ['janitor', 'moderator', 'admin'];
|
||||
// TODO make this type better?
|
||||
|
||||
export const groupBunts = {
|
||||
group: (): Group => ({ members: [], tags: { role: {} }, hidden: false, policy: groupBunts.policy() }),
|
||||
policy: (): GroupPolicy => ({ open: { banned: [], banRanks: [] } })
|
||||
};
|
||||
|
||||
export const joinError = ['no-perms', 'strange', 'abort'] as const;
|
||||
export const joinResult = ['done', ...joinError] as const;
|
||||
export const joinLoad = ['start', 'added', 'metadata'] as const;
|
||||
export const joinProgress = [...joinLoad, ...joinResult] as const;
|
||||
|
||||
export function roleForShip(
|
||||
group: Group,
|
||||
ship: PatpNoSig
|
||||
): RoleTags | undefined {
|
||||
return roleTags.reduce((currRole, role) => {
|
||||
const roleShips = group?.tags?.role?.[role];
|
||||
return roleShips && roleShips.includes(ship) ? role : currRole;
|
||||
}, undefined as RoleTags | undefined);
|
||||
};
|
||||
|
||||
export function resourceFromPath(path: Path): Resource {
|
||||
const [, , ship, name] = path.split('/');
|
||||
return { ship, name };
|
||||
}
|
||||
|
||||
export function makeResource(ship: string, name: string) {
|
||||
return { ship, name };
|
||||
}
|
||||
|
||||
export const isWriter = (group: Group, resource: string, ship: string) => {
|
||||
const graph = group?.tags?.graph;
|
||||
const writers: string[] | undefined = graph && (graph[resource] as any)?.writers;
|
||||
const admins = group?.tags?.role?.admin ?? [];
|
||||
if (typeof writers === 'undefined') {
|
||||
return true;
|
||||
} else {
|
||||
return [...writers].includes(ship) || admins.includes(ship);
|
||||
}
|
||||
};
|
||||
|
||||
export function isChannelAdmin(
|
||||
group: Group,
|
||||
resource: string,
|
||||
ship: string
|
||||
): boolean {
|
||||
const role = roleForShip(group, deSig(ship));
|
||||
|
||||
return (
|
||||
isHost(resource, ship) ||
|
||||
role === 'admin' ||
|
||||
role === 'moderator'
|
||||
);
|
||||
}
|
||||
|
||||
export function isHost(
|
||||
resource: string,
|
||||
ship: string
|
||||
): boolean {
|
||||
const [, , host] = resource.split('/');
|
||||
|
||||
return ship === host;
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
export * from './update';
|
||||
export * from './view';
|
@ -1,175 +0,0 @@
|
||||
import { PatpNoSig, Path, ShipRank, Enc } from '../lib';
|
||||
import { roleTags } from './index';
|
||||
|
||||
export type RoleTags = typeof roleTags[number];
|
||||
interface RoleTag {
|
||||
tag: 'admin' | 'moderator' | 'janitor';
|
||||
}
|
||||
|
||||
interface AppTag {
|
||||
app: string;
|
||||
resource: string;
|
||||
tag: string;
|
||||
}
|
||||
|
||||
export type Tag = AppTag | RoleTag;
|
||||
|
||||
export interface InvitePolicy {
|
||||
invite: {
|
||||
pending: PatpNoSig[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface OpenPolicy {
|
||||
open: {
|
||||
banned: PatpNoSig[];
|
||||
banRanks: ShipRank[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface Resource {
|
||||
name: string;
|
||||
ship: PatpNoSig;
|
||||
}
|
||||
|
||||
export type OpenPolicyDiff =
|
||||
| AllowRanksDiff
|
||||
| BanRanksDiff
|
||||
| AllowShipsDiff
|
||||
| BanShipsDiff;
|
||||
|
||||
export interface AllowRanksDiff {
|
||||
allowRanks: ShipRank[];
|
||||
}
|
||||
|
||||
export interface BanRanksDiff {
|
||||
banRanks: ShipRank[];
|
||||
}
|
||||
|
||||
export interface AllowShipsDiff {
|
||||
allowShips: PatpNoSig[];
|
||||
}
|
||||
|
||||
export interface BanShipsDiff {
|
||||
banShips: PatpNoSig[];
|
||||
}
|
||||
|
||||
export type InvitePolicyDiff = AddInvitesDiff | RemoveInvitesDiff;
|
||||
|
||||
export interface AddInvitesDiff {
|
||||
addInvites: PatpNoSig[];
|
||||
}
|
||||
|
||||
export interface RemoveInvitesDiff {
|
||||
removeInvites: PatpNoSig[];
|
||||
}
|
||||
|
||||
export interface ReplacePolicyDiff {
|
||||
replace: GroupPolicy;
|
||||
}
|
||||
|
||||
export type GroupPolicyDiff =
|
||||
| { open: OpenPolicyDiff }
|
||||
| { invite: InvitePolicyDiff }
|
||||
| ReplacePolicyDiff;
|
||||
|
||||
export type GroupPolicy = OpenPolicy | InvitePolicy;
|
||||
|
||||
export interface TaggedShips {
|
||||
[tag: string]: PatpNoSig[];
|
||||
}
|
||||
|
||||
export interface Tags {
|
||||
role: TaggedShips;
|
||||
[app: string]: TaggedShips;
|
||||
}
|
||||
|
||||
export interface Group {
|
||||
members: PatpNoSig[];
|
||||
tags: Tags;
|
||||
policy: GroupPolicy;
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
export type Groups = {
|
||||
[p in Path]: Group;
|
||||
};
|
||||
|
||||
export interface GroupUpdateInitial {
|
||||
initial: Enc<Groups>;
|
||||
}
|
||||
|
||||
export interface GroupUpdateAddGroup {
|
||||
addGroup: {
|
||||
resource: Resource;
|
||||
policy: Enc<GroupPolicy>;
|
||||
hidden: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface GroupUpdateAddMembers {
|
||||
addMembers: {
|
||||
ships: PatpNoSig[];
|
||||
resource: Resource;
|
||||
};
|
||||
}
|
||||
|
||||
export interface GroupUpdateRemoveMembers {
|
||||
removeMembers: {
|
||||
ships: PatpNoSig[];
|
||||
resource: Resource;
|
||||
};
|
||||
}
|
||||
|
||||
export interface GroupUpdateAddTag {
|
||||
addTag: {
|
||||
tag: Tag;
|
||||
resource: Resource;
|
||||
ships: PatpNoSig[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface GroupUpdateRemoveTag {
|
||||
removeTag: {
|
||||
tag: Tag;
|
||||
resource: Resource;
|
||||
ships: PatpNoSig[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface GroupUpdateChangePolicy {
|
||||
changePolicy: { resource: Resource; diff: GroupPolicyDiff };
|
||||
}
|
||||
|
||||
export interface GroupUpdateRemoveGroup {
|
||||
removeGroup: {
|
||||
resource: Resource;
|
||||
};
|
||||
}
|
||||
|
||||
export interface GroupUpdateExpose {
|
||||
expose: {
|
||||
resource: Resource;
|
||||
};
|
||||
}
|
||||
|
||||
export interface GroupUpdateInitialGroup {
|
||||
initialGroup: {
|
||||
resource: Resource;
|
||||
group: Enc<Group>;
|
||||
};
|
||||
}
|
||||
|
||||
export type GroupUpdate =
|
||||
| GroupUpdateInitial
|
||||
| GroupUpdateAddGroup
|
||||
| GroupUpdateAddMembers
|
||||
| GroupUpdateRemoveMembers
|
||||
| GroupUpdateAddTag
|
||||
| GroupUpdateRemoveTag
|
||||
| GroupUpdateChangePolicy
|
||||
| GroupUpdateRemoveGroup
|
||||
| GroupUpdateExpose
|
||||
| GroupUpdateInitialGroup;
|
||||
|
||||
export type GroupAction = Omit<GroupUpdate, 'initialGroup' | 'initial'>;
|
@ -1,30 +0,0 @@
|
||||
import { joinError, joinProgress, joinResult } from ".";
|
||||
import {Patp} from "../lib";
|
||||
|
||||
export type JoinError = typeof joinError[number];
|
||||
|
||||
export type JoinResult = typeof joinResult[number];
|
||||
|
||||
|
||||
export type JoinProgress = typeof joinProgress[number];
|
||||
|
||||
export interface JoinRequest {
|
||||
/**
|
||||
* Whether to display the join request or not
|
||||
*/
|
||||
hidden: boolean;
|
||||
/**
|
||||
* Timestamp of when the request started
|
||||
*/
|
||||
started: number;
|
||||
ship: Patp;
|
||||
progress: JoinProgress;
|
||||
shareContact: boolean;
|
||||
autojoin: boolean;
|
||||
app: 'graph' | 'groups';
|
||||
invite: string[];
|
||||
}
|
||||
|
||||
export interface JoinRequests {
|
||||
[rid: string]: JoinRequest;
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
export * from './types';
|
||||
export * from './lib';
|
@ -1,163 +0,0 @@
|
||||
import { BigInteger } from 'big-integer';
|
||||
|
||||
import { Poke } from '../lib/types';
|
||||
import {
|
||||
HarkBin,
|
||||
HarkBinId,
|
||||
HarkBody,
|
||||
HarkLid,
|
||||
HarkPlace
|
||||
} from './types';
|
||||
import { decToUd } from '../lib';
|
||||
|
||||
export const harkAction = <T>(data: T): Poke<T> => ({
|
||||
app: 'hark-store',
|
||||
mark: 'hark-action',
|
||||
json: data
|
||||
});
|
||||
|
||||
const graphHookAction = <T>(data: T): Poke<T> => ({
|
||||
app: 'hark-graph-hook',
|
||||
mark: 'hark-graph-hook-action',
|
||||
json: data
|
||||
});
|
||||
|
||||
export { graphHookAction as harkGraphHookAction };
|
||||
|
||||
const groupHookAction = <T>(data: T): Poke<T> => ({
|
||||
app: 'hark-group-hook',
|
||||
mark: 'hark-group-hook-action',
|
||||
json: data
|
||||
});
|
||||
|
||||
export { groupHookAction as harkGroupHookAction };
|
||||
|
||||
export const actOnNotification = (
|
||||
frond: string,
|
||||
intTime: BigInteger,
|
||||
bin: HarkBin
|
||||
): Poke<unknown> =>
|
||||
harkAction({
|
||||
[frond]: {
|
||||
time: decToUd(intTime.toString()),
|
||||
bin
|
||||
}
|
||||
});
|
||||
|
||||
export const setMentions = (mentions: boolean): Poke<unknown> =>
|
||||
graphHookAction({
|
||||
'set-mentions': mentions
|
||||
});
|
||||
|
||||
export const setWatchOnSelf = (watchSelf: boolean): Poke<unknown> =>
|
||||
graphHookAction({
|
||||
'set-watch-on-self': watchSelf
|
||||
});
|
||||
|
||||
export const setDoNotDisturb = (dnd: boolean): Poke<unknown> =>
|
||||
harkAction({
|
||||
'set-dnd': dnd
|
||||
});
|
||||
|
||||
export const addNote = (bin: HarkBin, body: HarkBody) =>
|
||||
harkAction({
|
||||
'add-note': {
|
||||
bin,
|
||||
body
|
||||
}
|
||||
});
|
||||
|
||||
export const archive = (bin: HarkBin, lid: HarkLid): Poke<unknown> =>
|
||||
harkAction({
|
||||
archive: {
|
||||
lid,
|
||||
bin
|
||||
}
|
||||
});
|
||||
|
||||
export const opened = harkAction({
|
||||
opened: null
|
||||
});
|
||||
|
||||
export const markCountAsRead = (place: HarkPlace): Poke<unknown> =>
|
||||
harkAction({
|
||||
'read-count': place
|
||||
});
|
||||
|
||||
export const markEachAsRead = (
|
||||
place: HarkPlace,
|
||||
path: string
|
||||
): Poke<unknown> =>
|
||||
harkAction({
|
||||
'read-each': {
|
||||
place,
|
||||
path
|
||||
}
|
||||
});
|
||||
|
||||
export const seen = () => harkAction({ seen: null });
|
||||
|
||||
export const readAll = harkAction({ 'read-all': null });
|
||||
export const archiveAll = harkAction({ 'archive-all': null });
|
||||
|
||||
export const ignoreGroup = (group: string): Poke<unknown> =>
|
||||
groupHookAction({
|
||||
ignore: group
|
||||
});
|
||||
|
||||
export const ignoreGraph = (graph: string, index: string): Poke<unknown> =>
|
||||
graphHookAction({
|
||||
ignore: {
|
||||
graph,
|
||||
index
|
||||
}
|
||||
});
|
||||
|
||||
export const listenGroup = (group: string): Poke<unknown> =>
|
||||
groupHookAction({
|
||||
listen: group
|
||||
});
|
||||
|
||||
export const listenGraph = (graph: string, index: string): Poke<unknown> =>
|
||||
graphHookAction({
|
||||
listen: {
|
||||
graph,
|
||||
index
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Read all graphs belonging to a particular group
|
||||
*/
|
||||
export const readGroup = (group: string) =>
|
||||
harkAction({
|
||||
'read-group': group
|
||||
});
|
||||
|
||||
/**
|
||||
* Read all unreads in a graph
|
||||
*/
|
||||
export const readGraph = (graph: string) =>
|
||||
harkAction({
|
||||
'read-graph': graph
|
||||
});
|
||||
|
||||
export function harkBinToId(bin: HarkBin): HarkBinId {
|
||||
const { place, path } = bin;
|
||||
return `${place.desk}${place.path}${path}`;
|
||||
}
|
||||
|
||||
export function harkBinEq(a: HarkBin, b: HarkBin): boolean {
|
||||
return (
|
||||
a.place.path === b.place.path &&
|
||||
a.place.desk === b.place.desk &&
|
||||
a.path === b.path
|
||||
);
|
||||
}
|
||||
|
||||
export function harkLidToId(lid: HarkLid): string {
|
||||
if('time' in lid) {
|
||||
return `archive-${lid.time}`;
|
||||
}
|
||||
return Object.keys(lid)[0];
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
|
||||
export interface HarkStats {
|
||||
count: number;
|
||||
each: string[];
|
||||
last: number;
|
||||
}
|
||||
|
||||
export interface Timebox {
|
||||
[binId: string]: Notification;
|
||||
}
|
||||
|
||||
export type HarkContent = { ship: string; } | { text: string; };
|
||||
|
||||
export interface HarkBody {
|
||||
title: HarkContent[];
|
||||
time: number;
|
||||
content: HarkContent[];
|
||||
link: string;
|
||||
binned: string;
|
||||
}
|
||||
|
||||
export interface HarkPlace {
|
||||
desk: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface HarkBin {
|
||||
path: string;
|
||||
place: HarkPlace;
|
||||
}
|
||||
|
||||
export type HarkLid =
|
||||
{ unseen: null; }
|
||||
| { seen: null; }
|
||||
| { time: string; };
|
||||
|
||||
export type HarkBinId = string;
|
||||
export interface Notification {
|
||||
bin: HarkBin;
|
||||
time: number;
|
||||
body: HarkBody[];
|
||||
}
|
||||
|
||||
export interface NotificationGraphConfig {
|
||||
watchOnSelf: boolean;
|
||||
mentions: boolean;
|
||||
watching: WatchedIndex[]
|
||||
}
|
||||
|
||||
export interface Unreads {
|
||||
[path: string]: HarkStats;
|
||||
}
|
||||
|
||||
interface WatchedIndex {
|
||||
graph: string;
|
||||
index: string;
|
||||
}
|
||||
export type GroupNotificationsConfig = string[];
|
@ -1,2 +0,0 @@
|
||||
export * from './lib';
|
||||
export * from './types';
|
@ -1,127 +0,0 @@
|
||||
import { Poke, Scry } from '../lib';
|
||||
import { Pike } from './types';
|
||||
|
||||
export const getPikes: Scry = {
|
||||
app: 'hood',
|
||||
path: '/kiln/pikes'
|
||||
};
|
||||
|
||||
/**
|
||||
* Install a foreign desk
|
||||
*/
|
||||
export function kilnInstall(
|
||||
ship: string,
|
||||
desk: string,
|
||||
local?: string
|
||||
): Poke<any> {
|
||||
return {
|
||||
app: 'hood',
|
||||
mark: 'kiln-install',
|
||||
json: {
|
||||
ship,
|
||||
desk,
|
||||
local: local || desk
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync with a foreign desk
|
||||
*/
|
||||
export function kilnSync(
|
||||
ship: string,
|
||||
desk: string,
|
||||
local?: string
|
||||
): Poke<any> {
|
||||
return {
|
||||
app: 'hood',
|
||||
mark: 'kiln-sync',
|
||||
json: {
|
||||
ship,
|
||||
desk,
|
||||
local: local || desk
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsync with a foreign desk
|
||||
*/
|
||||
export function kilnUnsync(
|
||||
ship: string,
|
||||
desk: string,
|
||||
local?: string
|
||||
): Poke<any> {
|
||||
return {
|
||||
app: 'hood',
|
||||
mark: 'kiln-unsync',
|
||||
json: {
|
||||
ship,
|
||||
desk,
|
||||
local: local || desk
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall a desk
|
||||
*/
|
||||
export function kilnUninstall(
|
||||
desk: string
|
||||
): Poke<any> {
|
||||
return {
|
||||
app: 'hood',
|
||||
mark: 'kiln-uninstall',
|
||||
json: desk
|
||||
};
|
||||
}
|
||||
|
||||
export function kilnSuspend(
|
||||
desk: string
|
||||
): Poke<any> {
|
||||
return {
|
||||
app: 'hood',
|
||||
mark: 'kiln-suspend',
|
||||
json: desk
|
||||
};
|
||||
}
|
||||
|
||||
export function kilnRevive(
|
||||
desk: string
|
||||
): Poke<any> {
|
||||
return {
|
||||
app: 'hood',
|
||||
mark: 'kiln-revive',
|
||||
json: desk
|
||||
};
|
||||
}
|
||||
|
||||
export function kilnBump(): Poke<any> {
|
||||
return {
|
||||
app: 'hood',
|
||||
mark: 'kiln-bump',
|
||||
json: null,
|
||||
};
|
||||
}
|
||||
|
||||
export function kilnPause(desk: string) {
|
||||
return {
|
||||
app: 'hood',
|
||||
mark: 'kiln-pause',
|
||||
json: desk
|
||||
};
|
||||
}
|
||||
|
||||
export function kilnResume(desk: string) {
|
||||
return {
|
||||
app: 'hood',
|
||||
mark: 'kiln-resume',
|
||||
json: desk
|
||||
};
|
||||
}
|
||||
|
||||
export const scryLag: Scry = ({ app: 'hood', path: '/kiln/lag' });
|
||||
|
||||
export function getPikePublisher(pike: Pike) {
|
||||
return pike.sync?.ship;
|
||||
}
|
@ -1,208 +0,0 @@
|
||||
|
||||
/**
|
||||
* A pending commit, awaiting a future kelvin version
|
||||
*/
|
||||
interface Woof {
|
||||
aeon: number;
|
||||
weft: Weft;
|
||||
}
|
||||
|
||||
interface Rein {
|
||||
/**
|
||||
* Agents not in manifest that should be running
|
||||
*/
|
||||
add: string[];
|
||||
/**
|
||||
* Agents in manifest that should not be running
|
||||
*/
|
||||
sub: string[];
|
||||
}
|
||||
|
||||
export interface Rail {
|
||||
/**
|
||||
* Original publisher of desk, if available
|
||||
*/
|
||||
publisher: string | null;
|
||||
/**
|
||||
* Ship of foreign vat
|
||||
*/
|
||||
ship: string;
|
||||
/**
|
||||
* Desk of foreign vat
|
||||
*/
|
||||
desk: string;
|
||||
/**
|
||||
* Aeon (version number) that we currently have synced
|
||||
*/
|
||||
aeon: number;
|
||||
next: Woof[];
|
||||
paused: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tracker of a foreign {@link Vat}
|
||||
*
|
||||
*/
|
||||
export interface Arak {
|
||||
rein: Rein;
|
||||
rail: Rail | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A component's kelvin version
|
||||
*/
|
||||
export interface Weft {
|
||||
/**
|
||||
* Name of the component
|
||||
*
|
||||
* @remarks
|
||||
* Usually %zuse, %hoon, or %lull
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Kelvin version
|
||||
*
|
||||
*/
|
||||
kelvin: number;
|
||||
}
|
||||
|
||||
export interface KilnDiffBlock {
|
||||
block: {
|
||||
desk: string;
|
||||
arak: Arak;
|
||||
weft: Weft;
|
||||
blockers: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface KilnDiffReset {
|
||||
reset: {
|
||||
desk: string;
|
||||
arak: Arak;
|
||||
};
|
||||
}
|
||||
|
||||
export interface KilnDiffMerge {
|
||||
merge: {
|
||||
desk: string;
|
||||
arak: Arak;
|
||||
};
|
||||
}
|
||||
|
||||
export interface KilnDiffMergeSunk {
|
||||
'merge-sunk': {
|
||||
desk: string;
|
||||
arak: Arak;
|
||||
tang: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface KilnDiffMergeFail {
|
||||
'merge-fail': {
|
||||
desk: string;
|
||||
arak: Arak;
|
||||
tang: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type KilnDiff =
|
||||
| KilnDiffBlock
|
||||
| KilnDiffReset
|
||||
| KilnDiffMerge
|
||||
| KilnDiffMergeSunk
|
||||
| KilnDiffMergeFail;
|
||||
|
||||
/**
|
||||
* Cases for revision
|
||||
*
|
||||
*/
|
||||
export interface Cass {
|
||||
/**
|
||||
* Revision number
|
||||
*/
|
||||
ud: number;
|
||||
/**
|
||||
* Timestamp of revision, as stringifed `@da`
|
||||
*
|
||||
* @remarks
|
||||
* If \@da is outside valid positive unix timestamp, value will be zero
|
||||
*/
|
||||
da: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A local desk installation
|
||||
*/
|
||||
export interface Vat {
|
||||
/**
|
||||
* Desk that this Vat describes
|
||||
*/
|
||||
desk: string;
|
||||
/**
|
||||
* Hash of the desk, rendered as `@uv`
|
||||
*
|
||||
* @remarks
|
||||
* Equivalent to
|
||||
* ```hoon
|
||||
* .^(@uv %cz /=desk=)
|
||||
* ```
|
||||
*/
|
||||
hash: string;
|
||||
/**
|
||||
* Current revision
|
||||
*/
|
||||
cass: Cass;
|
||||
/**
|
||||
* Foreign sync
|
||||
*/
|
||||
arak: Arak;
|
||||
}
|
||||
|
||||
export interface Vats {
|
||||
[desk: string]: Vat;
|
||||
}
|
||||
/**
|
||||
* TODO: crisp one-liner describing a Pike
|
||||
*/
|
||||
export interface Pike {
|
||||
/**
|
||||
* Hash of the desk, rendered as `@uv`
|
||||
*
|
||||
* @remarks
|
||||
* Equivalent to
|
||||
* ```hoon
|
||||
* .^(@uv %cz /=desk=)
|
||||
* ```
|
||||
*/
|
||||
hash: string;
|
||||
sync: {
|
||||
/**
|
||||
* Source desk for this Pike
|
||||
*/
|
||||
desk: string;
|
||||
/**
|
||||
* Source ship for this Pike
|
||||
*/
|
||||
ship: string;
|
||||
} | null;
|
||||
/**
|
||||
* {@link Weft}s associated with this Pike
|
||||
*/
|
||||
wefts: Weft[];
|
||||
/**
|
||||
* how live is this pike?
|
||||
*
|
||||
* live - app is running
|
||||
* held - app is not running, but is trying to run. this state can be entered
|
||||
* in two main ways:
|
||||
* - when installing an app but it hasn't finished downloading (or it did
|
||||
* but failed to install for some reason)
|
||||
* - when user forced a kelvin upgrade by suspending desks.
|
||||
* dead - app is not running
|
||||
*/
|
||||
zest: "live" | "dead" | "held";
|
||||
}
|
||||
|
||||
export interface Pikes {
|
||||
[desk: string]: Pike;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
export * from './contacts';
|
||||
export * as contacts from './contacts';
|
||||
export * from './graph';
|
||||
export * as graph from './graph';
|
||||
export * from './groups';
|
||||
export * as groups from './groups';
|
||||
export * from './hark';
|
||||
export * as hark from './hark';
|
||||
export * from './invite';
|
||||
// this conflicts with /groups/lib invite
|
||||
// export * as invite from './invite';
|
||||
export * from './metadata';
|
||||
export * as metadata from './metadata';
|
||||
export * from './settings';
|
||||
export * as settings from './settings';
|
||||
export * from './s3';
|
||||
export * as s3 from './s3';
|
||||
export * from './lib';
|
||||
export * from './lib/BigIntOrderedMap';
|
||||
export * from './lib/BigIntArrayOrderedMap';
|
||||
export * as hood from './hood';
|
||||
export * from './hood';
|
||||
export * as docket from './docket';
|
||||
export * from './docket';
|
||||
export * as term from './term';
|
||||
export * from './term';
|
@ -1,2 +0,0 @@
|
||||
export * from './types';
|
||||
export * from './lib';
|
@ -1,28 +0,0 @@
|
||||
import { Poke, Serial } from "../lib";
|
||||
import { InviteUpdate, InviteUpdateAccept, InviteUpdateDecline } from "./types";
|
||||
|
||||
export const inviteAction = <T extends InviteUpdate>(data: T): Poke<T> => ({
|
||||
app: 'invite-store',
|
||||
mark: 'invite-action',
|
||||
json: data
|
||||
});
|
||||
|
||||
export const accept = (
|
||||
app: string,
|
||||
uid: Serial
|
||||
): Poke<InviteUpdateAccept> => inviteAction({
|
||||
accept: {
|
||||
term: app,
|
||||
uid
|
||||
}
|
||||
});
|
||||
|
||||
export const decline = (
|
||||
app: string,
|
||||
uid: Serial
|
||||
): Poke<InviteUpdateDecline> => inviteAction({
|
||||
decline: {
|
||||
term: app,
|
||||
uid
|
||||
}
|
||||
});
|
@ -1,75 +0,0 @@
|
||||
import { Serial, PatpNoSig, Path } from '../lib';
|
||||
import { Resource } from "../groups";
|
||||
|
||||
export type InviteUpdate =
|
||||
InviteUpdateInitial
|
||||
| InviteUpdateCreate
|
||||
| InviteUpdateDelete
|
||||
| InviteUpdateInvite
|
||||
| InviteUpdateAccept
|
||||
| InviteUpdateAccepted
|
||||
| InviteUpdateDecline;
|
||||
|
||||
export interface InviteUpdateAccept {
|
||||
accept: {
|
||||
term: string;
|
||||
uid: Serial;
|
||||
}
|
||||
}
|
||||
|
||||
export interface InviteUpdateInitial {
|
||||
initial: Invites;
|
||||
}
|
||||
|
||||
export interface InviteUpdateCreate {
|
||||
create: {
|
||||
term: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface InviteUpdateDelete {
|
||||
delete: {
|
||||
term: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface InviteUpdateInvite {
|
||||
invite: {
|
||||
term: string;
|
||||
uid: Serial;
|
||||
invite: Invite;
|
||||
};
|
||||
}
|
||||
|
||||
export interface InviteUpdateAccepted {
|
||||
accepted: {
|
||||
term: string;
|
||||
uid: Serial;
|
||||
};
|
||||
}
|
||||
|
||||
export interface InviteUpdateDecline {
|
||||
decline: {
|
||||
term: string;
|
||||
uid: Serial;
|
||||
};
|
||||
}
|
||||
|
||||
// actual datastructures
|
||||
|
||||
|
||||
export type Invites = {
|
||||
[p in Path]: AppInvites;
|
||||
};
|
||||
|
||||
export type AppInvites = {
|
||||
[s in Serial]: Invite;
|
||||
};
|
||||
|
||||
export interface Invite {
|
||||
app: string;
|
||||
recipient: PatpNoSig;
|
||||
resource: Resource;
|
||||
ship: PatpNoSig;
|
||||
text: string;
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
import produce, { immerable, castDraft, setAutoFreeze, enablePatches } from 'immer';
|
||||
import bigInt, { BigInteger } from 'big-integer';
|
||||
|
||||
setAutoFreeze(false);
|
||||
|
||||
enablePatches();
|
||||
|
||||
export function stringToArr(str: string) {
|
||||
return str.split('/').slice(1).map((ind) => {
|
||||
return bigInt(ind);
|
||||
});
|
||||
}
|
||||
|
||||
export function arrToString(arr: BigInteger[]) {
|
||||
let string = '';
|
||||
arr.forEach((key) => {
|
||||
string = string + `/${key.toString()}`;
|
||||
});
|
||||
return string;
|
||||
}
|
||||
|
||||
function sorted(a: BigInteger[], b: BigInteger[], reversed = false) {
|
||||
const getSort = sortBigIntArr(a, b);
|
||||
if (reversed) {
|
||||
return getSort * -1;
|
||||
} else {
|
||||
return getSort;
|
||||
}
|
||||
}
|
||||
|
||||
export function sortBigIntArr(a: BigInteger[], b: BigInteger[]) {
|
||||
const aLen = a.length;
|
||||
const bLen = b.length;
|
||||
|
||||
const aCop = a.slice(0);
|
||||
const bCop = b.slice(0);
|
||||
aCop.reverse();
|
||||
bCop.reverse();
|
||||
|
||||
let i = 0;
|
||||
while (i < aLen && i < bLen) {
|
||||
if (aCop[i].lt(bCop[i])) {
|
||||
return 1;
|
||||
} else if (aCop[i].gt(bCop[i])) {
|
||||
return -1;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return bLen - aLen;
|
||||
}
|
||||
|
||||
export class BigIntArrayOrderedMap<V> implements Iterable<[BigInteger[], V]> {
|
||||
root: Record<string, V> = {}
|
||||
cachedIter: [BigInteger[], V][] | null = null;
|
||||
[immerable] = true;
|
||||
reversed = false;
|
||||
|
||||
constructor(items: [BigInteger[], V][] = [], reversed = false) {
|
||||
items.forEach(([key, val]) => {
|
||||
this.set(key, val);
|
||||
});
|
||||
this.reversed = reversed;
|
||||
}
|
||||
|
||||
get size() {
|
||||
return Object.keys(this.root).length;
|
||||
}
|
||||
|
||||
get(key: BigInteger[]) {
|
||||
return this.root[arrToString(key)] ?? null;
|
||||
}
|
||||
|
||||
gas(items: [BigInteger[], V][]) {
|
||||
return produce(this, (draft) => {
|
||||
items.forEach(([key, value]) => {
|
||||
draft.root[arrToString(key)] = castDraft(value);
|
||||
});
|
||||
draft.generateCachedIter();
|
||||
},
|
||||
(patches) => {
|
||||
// console.log(`gassed with ${JSON.stringify(patches, null, 2)}`);
|
||||
});
|
||||
}
|
||||
|
||||
set(key: BigInteger[], value: V) {
|
||||
return produce(this, (draft) => {
|
||||
draft.root[arrToString(key)] = castDraft(value);
|
||||
draft.cachedIter = null;
|
||||
});
|
||||
}
|
||||
|
||||
clear() {
|
||||
return produce(this, (draft) => {
|
||||
draft.cachedIter = [];
|
||||
draft.root = {};
|
||||
});
|
||||
}
|
||||
|
||||
has(key: BigInteger[]) {
|
||||
return arrToString(key) in this.root;
|
||||
}
|
||||
|
||||
delete(key: BigInteger[]) {
|
||||
const result = produce(this, (draft) => {
|
||||
delete draft.root[arrToString(key)];
|
||||
draft.cachedIter = null;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
[Symbol.iterator](): IterableIterator<[BigInteger[], V]> {
|
||||
let idx = 0;
|
||||
const result = this.generateCachedIter();
|
||||
return {
|
||||
[Symbol.iterator]: this[Symbol.iterator],
|
||||
next: (): IteratorResult<[BigInteger[], V]> => {
|
||||
if (idx < result.length) {
|
||||
return { value: result[idx++], done: false };
|
||||
}
|
||||
return { done: true, value: null };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
peekLargest() {
|
||||
const sorted = Array.from(this);
|
||||
return sorted[0] as [BigInteger[], V] | null;
|
||||
}
|
||||
|
||||
peekSmallest() {
|
||||
const sorted = Array.from(this);
|
||||
return sorted[sorted.length - 1] as [BigInteger[], V] | null;
|
||||
}
|
||||
|
||||
keys() {
|
||||
return Array.from(this).map(([k,v]) => k);
|
||||
}
|
||||
|
||||
generateCachedIter() {
|
||||
if(this.cachedIter) {
|
||||
return [...this.cachedIter];
|
||||
}
|
||||
const result = Object.keys(this.root).map((key) => {
|
||||
return [stringToArr(key), this.root[key]] as [BigInteger[], V];
|
||||
}).sort(([a], [b]) => sorted(a, b, this.reversed));
|
||||
this.cachedIter = result;
|
||||
return [...result];
|
||||
}
|
||||
}
|
||||
|
@ -1,117 +0,0 @@
|
||||
import produce, { immerable, castDraft, setAutoFreeze, enablePatches } from 'immer';
|
||||
import bigInt, { BigInteger } from 'big-integer';
|
||||
|
||||
setAutoFreeze(false);
|
||||
|
||||
enablePatches();
|
||||
|
||||
function sortBigInt(a: BigInteger, b: BigInteger) {
|
||||
if (a.lt(b)) {
|
||||
return 1;
|
||||
} else if (a.eq(b)) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
export class BigIntOrderedMap<V> implements Iterable<[BigInteger, V]> {
|
||||
root: Record<string, V> = {}
|
||||
cachedIter: [BigInteger, V][] | null = null;
|
||||
[immerable] = true;
|
||||
|
||||
constructor(items: [BigInteger, V][] = []) {
|
||||
items.forEach(([key, val]) => {
|
||||
this.set(key, val);
|
||||
});
|
||||
}
|
||||
|
||||
get size() {
|
||||
if(this.cachedIter) {
|
||||
return this.cachedIter.length;
|
||||
}
|
||||
return this.generateCachedIter().length;
|
||||
}
|
||||
|
||||
get(key: BigInteger) {
|
||||
return this.root[key.toString()] ?? null;
|
||||
}
|
||||
|
||||
gas(items: [BigInteger, V][]) {
|
||||
return produce(this, (draft) => {
|
||||
items.forEach(([key, value]) => {
|
||||
draft.root[key.toString()] = castDraft(value);
|
||||
});
|
||||
draft.cachedIter = null;
|
||||
},
|
||||
(patches) => {
|
||||
// console.log(`gassed with ${JSON.stringify(patches, null, 2)}`);
|
||||
});
|
||||
}
|
||||
|
||||
set(key: BigInteger, value: V) {
|
||||
return produce(this, (draft) => {
|
||||
draft.root[key.toString()] = castDraft(value);
|
||||
draft.cachedIter = null;
|
||||
});
|
||||
}
|
||||
|
||||
clear() {
|
||||
return produce(this, (draft) => {
|
||||
draft.cachedIter = [];
|
||||
draft.root = {};
|
||||
});
|
||||
}
|
||||
|
||||
has(key: BigInteger) {
|
||||
return key.toString() in this.root;
|
||||
}
|
||||
|
||||
delete(key: BigInteger) {
|
||||
const result = produce(this, (draft) => {
|
||||
delete draft.root[key.toString()];
|
||||
draft.cachedIter = null;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
[Symbol.iterator](): IterableIterator<[BigInteger, V]> {
|
||||
let idx = 0;
|
||||
const result = this.generateCachedIter();
|
||||
return {
|
||||
[Symbol.iterator]: this[Symbol.iterator],
|
||||
next: (): IteratorResult<[BigInteger, V]> => {
|
||||
if (idx < result.length) {
|
||||
return { value: result[idx++], done: false };
|
||||
}
|
||||
return { done: true, value: null };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
peekLargest() {
|
||||
const sorted = Array.from(this);
|
||||
return sorted[0] as [BigInteger, V] | null;
|
||||
}
|
||||
|
||||
peekSmallest() {
|
||||
const sorted = Array.from(this);
|
||||
return sorted[sorted.length - 1] as [BigInteger, V] | null;
|
||||
}
|
||||
|
||||
keys() {
|
||||
return Array.from(this).map(([k,v]) => k);
|
||||
}
|
||||
|
||||
generateCachedIter() {
|
||||
if(this.cachedIter) {
|
||||
return [...this.cachedIter];
|
||||
}
|
||||
const result = Object.keys(this.root).map((key) => {
|
||||
const num = bigInt(key);
|
||||
return [num, this.root[key]] as [BigInteger, V];
|
||||
}).sort(([a], [b]) => sortBigInt(a,b));
|
||||
this.cachedIter = result;
|
||||
return [...result];
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
export * from './lib';
|
||||
export * from './types';
|
@ -1,259 +0,0 @@
|
||||
import bigInt, { BigInteger } from "big-integer";
|
||||
|
||||
import { Resource } from "../groups/types";
|
||||
import { Post, GraphNode } from "../graph/types";
|
||||
|
||||
const DA_UNIX_EPOCH = bigInt("170141184475152167957503069145530368000"); // `@ud` ~1970.1.1
|
||||
|
||||
const DA_SECOND = bigInt("18446744073709551616"); // `@ud` ~s1
|
||||
|
||||
function chunk<T>(arr: T[], size: number): T[][] {
|
||||
let chunk: T[] = [];
|
||||
let newArray = [chunk];
|
||||
|
||||
for (let i = 0;i < arr.length;i++) {
|
||||
if (chunk.length < size) {
|
||||
chunk.push(arr[i])
|
||||
} else {
|
||||
chunk = [arr[i]]
|
||||
newArray.push(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
function dropWhile<T>(arr: T[], pred: (x: T) => boolean): T[] {
|
||||
const newArray = arr.slice();
|
||||
|
||||
for (const item of arr) {
|
||||
if (pred(item)) {
|
||||
newArray.shift();
|
||||
} else {
|
||||
return newArray;
|
||||
}
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a bigint representing an urbit date, returns a unix timestamp.
|
||||
*
|
||||
* @param {BigInteger} da The urbit date
|
||||
*
|
||||
* @return {number} The unix timestamp
|
||||
*/
|
||||
export function daToUnix(da: BigInteger): number {
|
||||
// ported from +time:enjs:format in hoon.hoon
|
||||
const offset = DA_SECOND.divide(bigInt(2000));
|
||||
const epochAdjusted = offset.add(da.subtract(DA_UNIX_EPOCH));
|
||||
|
||||
return Math.round(
|
||||
epochAdjusted.multiply(bigInt(1000)).divide(DA_SECOND).toJSNumber()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a unix timestamp, returns a bigint representing an urbit date
|
||||
*
|
||||
* @param {number} unix The unix timestamp
|
||||
*
|
||||
* @return {BigInteger} The urbit date
|
||||
*/
|
||||
export function unixToDa(unix: number): BigInteger {
|
||||
const timeSinceEpoch = bigInt(unix).multiply(DA_SECOND).divide(bigInt(1000));
|
||||
return DA_UNIX_EPOCH.add(timeSinceEpoch);
|
||||
}
|
||||
|
||||
|
||||
export function makePatDa(patda: string): BigInteger {
|
||||
return bigInt(udToDec(patda));
|
||||
}
|
||||
|
||||
export function udToDec(ud: string): string {
|
||||
return ud.replace(/\./g, "");
|
||||
}
|
||||
|
||||
export function decToUd(str: string): string {
|
||||
const transform = chunk(str.split('').reverse(), 3)
|
||||
.map(group => group.reverse().join(''))
|
||||
.reverse()
|
||||
.join('.')
|
||||
return transform.replace(/^[0\.]+/g, '');
|
||||
}
|
||||
|
||||
export function resourceAsPath(resource: Resource): string {
|
||||
const { name, ship } = resource;
|
||||
return `/ship/~${ship}/${name}`;
|
||||
}
|
||||
|
||||
export function uuid(): string {
|
||||
let str = "0v";
|
||||
str += Math.ceil(Math.random() * 8) + ".";
|
||||
for (let i = 0; i < 5; i++) {
|
||||
let _str = Math.ceil(Math.random() * 10000000).toString(32);
|
||||
_str = ("00000" + _str).substr(-5, 5);
|
||||
str += _str + ".";
|
||||
}
|
||||
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
Goes from:
|
||||
~2018.7.17..23.15.09..5be5 // urbit @da
|
||||
To:
|
||||
(javascript Date object)
|
||||
*/
|
||||
export function daToDate(st: string): Date {
|
||||
const dub = function (n: string) {
|
||||
return parseInt(n) < 10 ? "0" + parseInt(n) : n.toString();
|
||||
};
|
||||
const da = st.split("..");
|
||||
const bigEnd = da[0].split(".");
|
||||
const lilEnd = da[1].split(".");
|
||||
const ds = `${bigEnd[0].slice(1)}-${dub(bigEnd[1])}-${dub(bigEnd[2])}T${dub(
|
||||
lilEnd[0]
|
||||
)}:${dub(lilEnd[1])}:${dub(lilEnd[2])}Z`;
|
||||
return new Date(ds);
|
||||
}
|
||||
|
||||
/*
|
||||
Goes from:
|
||||
(javascript Date object)
|
||||
To:
|
||||
~2018.7.17..23.15.09..5be5 // urbit @da
|
||||
*/
|
||||
|
||||
export function dateToDa(d: Date, mil: boolean = false): string {
|
||||
const fil = function (n: number) {
|
||||
return n >= 10 ? n : "0" + n;
|
||||
};
|
||||
return (
|
||||
`~${d.getUTCFullYear()}.` +
|
||||
`${d.getUTCMonth() + 1}.` +
|
||||
`${fil(d.getUTCDate())}..` +
|
||||
`${fil(d.getUTCHours())}.` +
|
||||
`${fil(d.getUTCMinutes())}.` +
|
||||
`${fil(d.getUTCSeconds())}` +
|
||||
`${mil ? "..0000" : ""}`
|
||||
);
|
||||
}
|
||||
|
||||
export function preSig(ship: string): string {
|
||||
if (!ship) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (ship.trim().startsWith('~')) {
|
||||
return ship.trim();
|
||||
}
|
||||
|
||||
return '~'.concat(ship.trim());
|
||||
}
|
||||
|
||||
export function deSig(ship: string): string | null {
|
||||
if (!ship) {
|
||||
return null;
|
||||
}
|
||||
return ship.replace("~", "");
|
||||
}
|
||||
|
||||
// trim patps to match dojo, chat-cli
|
||||
export function cite(ship: string) {
|
||||
let patp = ship,
|
||||
shortened = '';
|
||||
if (patp === null || patp === '') {
|
||||
return null;
|
||||
}
|
||||
if (patp.startsWith('~')) {
|
||||
patp = patp.substr(1);
|
||||
}
|
||||
// comet
|
||||
if (patp.length === 56) {
|
||||
shortened = '~' + patp.slice(0, 6) + '_' + patp.slice(50, 56);
|
||||
return shortened;
|
||||
}
|
||||
// moon
|
||||
if (patp.length === 27) {
|
||||
shortened = '~' + patp.slice(14, 20) + '^' + patp.slice(21, 27);
|
||||
return shortened;
|
||||
}
|
||||
return `~${patp}`;
|
||||
}
|
||||
|
||||
|
||||
export function uxToHex(ux: string) {
|
||||
if (ux.length > 2 && ux.substr(0, 2) === '0x') {
|
||||
const value = ux.substr(2).replace('.', '').padStart(6, '0');
|
||||
return value;
|
||||
}
|
||||
|
||||
const value = ux.replace('.', '').padStart(6, '0');
|
||||
return value;
|
||||
}
|
||||
|
||||
export const hexToUx = (hex: string): string => {
|
||||
const nonZeroChars = dropWhile(hex.split(''), y => y === '0');
|
||||
const ux = chunk(nonZeroChars.reverse(), 4).map(x => {
|
||||
return x.reverse().join('');
|
||||
}).reverse().join('.') || '0';
|
||||
|
||||
return `0x${ux}`;
|
||||
};
|
||||
|
||||
|
||||
// encode the string into @ta-safe format, using logic from +wood.
|
||||
// for example, 'some Chars!' becomes '~.some.~43.hars~21.'
|
||||
//
|
||||
export function stringToTa(str: string): string {
|
||||
let out = "";
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str[i];
|
||||
let add = "";
|
||||
switch (char) {
|
||||
case " ":
|
||||
add = ".";
|
||||
break;
|
||||
case ".":
|
||||
add = "~.";
|
||||
break;
|
||||
case "~":
|
||||
add = "~~";
|
||||
break;
|
||||
default:
|
||||
const charCode = str.charCodeAt(i);
|
||||
if (
|
||||
(charCode >= 97 && charCode <= 122) || // a-z
|
||||
(charCode >= 48 && charCode <= 57) || // 0-9
|
||||
char === "-"
|
||||
) {
|
||||
add = char;
|
||||
} else {
|
||||
// TODO behavior for unicode doesn't match +wood's,
|
||||
// but we can probably get away with that for now.
|
||||
add = "~" + charCode.toString(16) + ".";
|
||||
}
|
||||
}
|
||||
out = out + add;
|
||||
}
|
||||
return "~." + out;
|
||||
}
|
||||
|
||||
export const buntPost = (): Post => ({
|
||||
author: '',
|
||||
contents: [],
|
||||
hash: null,
|
||||
index: '',
|
||||
signatures: [],
|
||||
'time-sent': 0
|
||||
});
|
||||
|
||||
export function makeNodeMap(posts: Post[]): Record<string, GraphNode> {
|
||||
const nodes: Record<string, GraphNode> = {};
|
||||
posts.forEach((p: Post) => {
|
||||
nodes[String(p.index)] = { children: null, post: p };
|
||||
});
|
||||
return nodes;
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
/**
|
||||
* Martian embassy
|
||||
*/
|
||||
|
||||
import { BigIntOrderedMap } from "./BigIntOrderedMap";
|
||||
|
||||
// an urbit style path rendered as string
|
||||
export type Path = string;
|
||||
|
||||
// patp including leading sig
|
||||
export type Patp = string;
|
||||
|
||||
// patp excluding leading sig
|
||||
export type PatpNoSig = string;
|
||||
|
||||
// @uvH encoded string
|
||||
export type Serial = string;
|
||||
|
||||
// jug from hoon
|
||||
export type Jug<K,V> = Map<K,Set<V>>;
|
||||
|
||||
// name of app
|
||||
export type AppName = 'chat' | 'link' | 'contacts' | 'publish' | 'graph' | 'groups';
|
||||
|
||||
export type ShipRank = 'czar' | 'king' | 'duke' | 'earl' | 'pawn';
|
||||
|
||||
export type Action = 'poke' | 'subscribe' | 'ack' | 'unsubscribe' | 'delete';
|
||||
|
||||
|
||||
export type SetElement<S> = S extends Set<(infer T)> ? T : never;
|
||||
export type MapKey<M> = M extends Map<(infer K), any> ? K : never;
|
||||
export type MapValue<M> = M extends Map<any, (infer V)> ? V : never;
|
||||
|
||||
/**
|
||||
* Turns sets into arrays and maps into objects so we can send them over the wire
|
||||
*/
|
||||
export type Enc<S> =
|
||||
S extends Set<any> ?
|
||||
Enc<SetElement<S>>[] :
|
||||
S extends Map<string, any> ?
|
||||
{ [s: string]: Enc<MapValue<S>> } :
|
||||
S extends object ?
|
||||
{ [K in keyof S]: Enc<S[K]> } :
|
||||
S extends BigIntOrderedMap<infer T> ?
|
||||
{ [index: string]: T } :
|
||||
S;
|
||||
|
||||
export type Mark = string;
|
||||
|
||||
export interface Poke<Action> {
|
||||
ship?: string; // This should be handled by the http library, but is part of the spec
|
||||
app: string;
|
||||
mark: Mark;
|
||||
json: Action;
|
||||
}
|
||||
|
||||
export interface Scry {
|
||||
app: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface Thread<Action> {
|
||||
inputMark: string;
|
||||
outputMark: string;
|
||||
threadName: string;
|
||||
body: Action;
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
export * from './types';
|
||||
export * from './lib';
|
@ -1,99 +0,0 @@
|
||||
import { Path, Poke, uxToHex, PatpNoSig } from '../lib';
|
||||
import { MdAppName, Association, Metadata, MetadataUpdate, MetadataUpdateAdd, MetadataUpdateRemove, MetadataEditField, MetadataUpdateEdit } from './types';
|
||||
|
||||
export const METADATA_UPDATE_VERSION = 2;
|
||||
|
||||
export const metadataAction = <T extends MetadataUpdate>(data: T, version: number = METADATA_UPDATE_VERSION): Poke<T> => ({
|
||||
app: 'metadata-push-hook',
|
||||
mark: `metadata-update-${version}`,
|
||||
json: data
|
||||
});
|
||||
|
||||
export const add = (
|
||||
ship: PatpNoSig,
|
||||
appName: MdAppName,
|
||||
resource: Path,
|
||||
group: Path,
|
||||
title: string,
|
||||
description: string,
|
||||
dateCreated: string,
|
||||
color: string,
|
||||
moduleName: string
|
||||
): Poke<MetadataUpdateAdd> => metadataAction({
|
||||
add: {
|
||||
group,
|
||||
resource: {
|
||||
resource,
|
||||
'app-name': appName
|
||||
},
|
||||
metadata: {
|
||||
title,
|
||||
description,
|
||||
color,
|
||||
'date-created': dateCreated,
|
||||
creator: `~${ship}`,
|
||||
config: { graph: moduleName },
|
||||
picture: '',
|
||||
hidden: false,
|
||||
preview: false,
|
||||
vip: ''
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export { add as metadataAdd };
|
||||
|
||||
export const remove = (
|
||||
appName: MdAppName,
|
||||
resource: string,
|
||||
group: string
|
||||
): Poke<MetadataUpdateRemove> => metadataAction<MetadataUpdateRemove>({
|
||||
remove: {
|
||||
group,
|
||||
resource: {
|
||||
resource,
|
||||
'app-name': appName
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export { remove as metadataRemove };
|
||||
|
||||
export const edit = (
|
||||
association: Association,
|
||||
edit: MetadataEditField
|
||||
): Poke<MetadataUpdateEdit> => metadataAction<MetadataUpdateEdit>({
|
||||
edit: {
|
||||
group: association.group,
|
||||
resource: {
|
||||
resource: association.resource,
|
||||
'app-name': association['app-name']
|
||||
},
|
||||
edit
|
||||
}
|
||||
});
|
||||
|
||||
export { edit as metadataEdit };
|
||||
|
||||
/**
|
||||
* @deprecated use {@link edit} instead
|
||||
*/
|
||||
export const update = (
|
||||
association: Association,
|
||||
newMetadata: Partial<Metadata>
|
||||
): Poke<MetadataUpdateAdd> => {
|
||||
const metadata = { ...association.metadata, ...newMetadata };
|
||||
metadata.color = uxToHex(metadata.color);
|
||||
return metadataAction<MetadataUpdateAdd>({
|
||||
add: {
|
||||
group: association.group,
|
||||
resource: {
|
||||
resource: association.resource,
|
||||
'app-name': association['app-name']
|
||||
},
|
||||
metadata
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export { update as metadataUpdate };
|
@ -1,101 +0,0 @@
|
||||
import { Path, Patp } from '../lib';
|
||||
|
||||
export type MdAppName = 'groups' | 'graph';
|
||||
|
||||
export type MetadataUpdate =
|
||||
MetadataUpdateInitial
|
||||
| MetadataUpdateAdd
|
||||
| MetadataUpdateUpdate
|
||||
| MetadataUpdateRemove
|
||||
| MetadataUpdateEdit;
|
||||
|
||||
export interface MetadataUpdateInitial {
|
||||
associations: ResourceAssociations;
|
||||
}
|
||||
|
||||
export type ResourceAssociations = {
|
||||
[p in Path]: Association;
|
||||
}
|
||||
|
||||
export type MetadataUpdateAdd = {
|
||||
add: AssociationPoke;
|
||||
}
|
||||
|
||||
export type MetadataUpdateUpdate = {
|
||||
update: AssociationPoke;
|
||||
}
|
||||
|
||||
export interface MetadataUpdateEdit {
|
||||
edit: {
|
||||
resource: MdResource;
|
||||
group: string;
|
||||
edit: MetadataEditField;
|
||||
}
|
||||
}
|
||||
|
||||
export type MetadataEditField = Partial<Omit<Metadata, 'config' | 'creator' | 'date-created'>>;
|
||||
|
||||
export type MetadataUpdateRemove = {
|
||||
remove: {
|
||||
resource: MdResource;
|
||||
group: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface MdResource {
|
||||
resource: string;
|
||||
'app-name': MdAppName;
|
||||
}
|
||||
|
||||
export interface MetadataUpdatePreview {
|
||||
group: string;
|
||||
channels: Associations;
|
||||
'channel-count': number;
|
||||
members: number;
|
||||
metadata: Metadata;
|
||||
}
|
||||
|
||||
export type Associations = {
|
||||
groups: AppAssociations<GroupConfig>
|
||||
graph: AppAssociations<GraphConfig>;
|
||||
}
|
||||
|
||||
export type AppAssociations<C = MetadataConfig> = {
|
||||
[p in Path]: Association<C>;
|
||||
}
|
||||
|
||||
export type Association<C = MetadataConfig> = MdResource & {
|
||||
group: Path;
|
||||
metadata: Metadata<C>;
|
||||
};
|
||||
|
||||
export interface AssociationPoke {
|
||||
group: Path;
|
||||
resource: MdResource;
|
||||
metadata: Metadata;
|
||||
}
|
||||
|
||||
export interface Metadata<C = MetadataConfig> {
|
||||
color: string;
|
||||
creator: Patp;
|
||||
'date-created': string;
|
||||
description: string;
|
||||
title: string;
|
||||
config: C;
|
||||
hidden: boolean;
|
||||
picture: string;
|
||||
preview: boolean;
|
||||
vip: PermVariation;
|
||||
}
|
||||
|
||||
export type MetadataConfig = GroupConfig | GraphConfig;
|
||||
|
||||
export interface GraphConfig {
|
||||
graph: string;
|
||||
}
|
||||
|
||||
export interface GroupConfig {
|
||||
group: undefined | {} | MdResource;
|
||||
}
|
||||
|
||||
export type PermVariation = '' | ' ' | 'reader-comments' | 'member-metadata' | 'host-feed' | 'admin-feed';
|
1891
pkg/npm/api/package-lock.json
generated
1891
pkg/npm/api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,56 +0,0 @@
|
||||
{
|
||||
"name": "@urbit/api",
|
||||
"version": "2.2.0",
|
||||
"description": "A library that provides bindings and types for Urbit's various userspace desks",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@github.com/urbit/urbit.git",
|
||||
"directory": "pkg/npm/api"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "dist/cjs/index.cjs",
|
||||
"module": "dist/esm/index.js",
|
||||
"exports": {
|
||||
"require": "./dist/cjs/index.cjs",
|
||||
"import": "./dist/esm/index.js"
|
||||
},
|
||||
"jsdelivr": "dist/urbit-api.min.js",
|
||||
"unpkg": "dist/urbit-api.min.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist/**"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "echo \"No test specified\" && exit 0",
|
||||
"build": "npm run clean && rollup -c && npx tsc -p tsconfig.json",
|
||||
"prepare": "npm run build",
|
||||
"watch": "rollup -c -w",
|
||||
"clean": "rm -rf dist/* types/*"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.0",
|
||||
"big-integer": "^1.6.48",
|
||||
"core-js": "^3.19.1",
|
||||
"immer": "^9.0.1",
|
||||
"urbit-ob": "^5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.16.0",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^21.0.1",
|
||||
"@rollup/plugin-node-resolve": "^13.0.6",
|
||||
"@types/node": "^15.12.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.2",
|
||||
"@typescript-eslint/parser": "^4.28.2",
|
||||
"@urbit/eslint-config": "^1.0.3",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint-plugin-react": "^7.24.0",
|
||||
"rollup": "^2.59.0",
|
||||
"rollup-plugin-analyzer": "^4.0.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-typescript2": "^0.30.0",
|
||||
"typescript": "^4.3.2"
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
||||
import commonJS from '@rollup/plugin-commonjs';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import babel from '@rollup/plugin-babel';
|
||||
import typescript from 'rollup-plugin-typescript2';
|
||||
import analyze from 'rollup-plugin-analyzer'
|
||||
|
||||
const input = ['./index.ts'];
|
||||
|
||||
// Skip certain warnings
|
||||
function onwarn(warning) {
|
||||
if (warning.code === 'THIS_IS_UNDEFINED') {
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(warning.message);
|
||||
}
|
||||
|
||||
export default [
|
||||
{
|
||||
input,
|
||||
onwarn,
|
||||
plugins: [
|
||||
nodeResolve({
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx']
|
||||
}),
|
||||
commonJS(),
|
||||
typescript(),
|
||||
babel({
|
||||
babelHelpers: 'bundled',
|
||||
exclude: ['node_modules/**']
|
||||
}),
|
||||
terser({
|
||||
ecma: 2017,
|
||||
compress: true,
|
||||
mangle: true
|
||||
})
|
||||
],
|
||||
output: {
|
||||
file: 'dist/urbit-api.min.js',
|
||||
format: 'umd',
|
||||
name: 'UrbitAPI', // this is the name of the global object
|
||||
esModule: false,
|
||||
exports: 'named',
|
||||
sourcemap: true
|
||||
}
|
||||
},
|
||||
{
|
||||
input,
|
||||
onwarn,
|
||||
plugins: [
|
||||
nodeResolve({
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx']
|
||||
}),
|
||||
commonJS(),
|
||||
typescript(),
|
||||
analyze({
|
||||
limit: 10
|
||||
})
|
||||
],
|
||||
output: [
|
||||
{
|
||||
file: 'dist/esm/index.js',
|
||||
format: 'esm',
|
||||
exports: 'named',
|
||||
sourcemap: true,
|
||||
},
|
||||
{
|
||||
file: 'dist/cjs/index.cjs',
|
||||
format: 'cjs',
|
||||
exports: 'named',
|
||||
sourcemap: true
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
@ -1,2 +0,0 @@
|
||||
export * from './lib';
|
||||
export * from './types';
|
@ -1,47 +0,0 @@
|
||||
import { Poke } from '../lib/types';
|
||||
import { S3Update, S3UpdateAccessKeyId, S3UpdateAddBucket, S3UpdateCurrentBucket, S3UpdateEndpoint, S3UpdateRemoveBucket, S3UpdateSecretAccessKey } from './types';
|
||||
|
||||
const s3Action = <T extends S3Update>(
|
||||
data: any
|
||||
): Poke<T> => ({
|
||||
app: 's3-store',
|
||||
mark: 's3-action',
|
||||
json: data
|
||||
});
|
||||
|
||||
export const setCurrentBucket = (
|
||||
bucket: string
|
||||
): Poke<S3UpdateCurrentBucket> => s3Action({
|
||||
'set-current-bucket': bucket
|
||||
});
|
||||
|
||||
export const addBucket = (
|
||||
bucket: string
|
||||
): Poke<S3UpdateAddBucket> => s3Action({
|
||||
'add-bucket': bucket
|
||||
});
|
||||
|
||||
export const removeBucket = (
|
||||
bucket: string
|
||||
): Poke<S3UpdateRemoveBucket> => s3Action({
|
||||
'remove-bucket': bucket
|
||||
});
|
||||
|
||||
export const setEndpoint = (
|
||||
endpoint: string
|
||||
): Poke<S3UpdateEndpoint> => s3Action({
|
||||
'set-endpoint': endpoint
|
||||
});
|
||||
|
||||
export const setAccessKeyId = (
|
||||
accessKeyId: string
|
||||
): Poke<S3UpdateAccessKeyId> => s3Action({
|
||||
'set-access-key-id': accessKeyId
|
||||
});
|
||||
|
||||
export const setSecretAccessKey = (
|
||||
secretAccessKey: string
|
||||
): Poke<S3UpdateSecretAccessKey> => s3Action({
|
||||
'set-secret-access-key': secretAccessKey
|
||||
});
|
||||
|
@ -1,60 +0,0 @@
|
||||
export interface S3Credentials {
|
||||
endpoint: string;
|
||||
accessKeyId: string;
|
||||
secretAccessKey: string;
|
||||
}
|
||||
|
||||
export interface S3Configuration {
|
||||
buckets: Set<string>;
|
||||
currentBucket: string;
|
||||
}
|
||||
|
||||
export interface S3State {
|
||||
configuration: S3Configuration;
|
||||
credentials: S3Credentials | null;
|
||||
}
|
||||
|
||||
export interface S3UpdateCredentials {
|
||||
credentials: S3Credentials;
|
||||
}
|
||||
|
||||
export interface S3UpdateConfiguration {
|
||||
configuration: {
|
||||
buckets: string[];
|
||||
currentBucket: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface S3UpdateCurrentBucket {
|
||||
setCurrentBucket: string;
|
||||
}
|
||||
|
||||
export interface S3UpdateAddBucket {
|
||||
addBucket: string;
|
||||
}
|
||||
|
||||
export interface S3UpdateRemoveBucket {
|
||||
removeBucket: string;
|
||||
}
|
||||
|
||||
export interface S3UpdateEndpoint {
|
||||
setEndpoint: string;
|
||||
}
|
||||
|
||||
export interface S3UpdateAccessKeyId {
|
||||
setAccessKeyId: string;
|
||||
}
|
||||
|
||||
export interface S3UpdateSecretAccessKey {
|
||||
setSecretAccessKey: string;
|
||||
}
|
||||
|
||||
export type S3Update =
|
||||
S3UpdateCredentials
|
||||
| S3UpdateConfiguration
|
||||
| S3UpdateCurrentBucket
|
||||
| S3UpdateAddBucket
|
||||
| S3UpdateRemoveBucket
|
||||
| S3UpdateEndpoint
|
||||
| S3UpdateAccessKeyId
|
||||
| S3UpdateSecretAccessKey;
|
@ -1,2 +0,0 @@
|
||||
export * from './types';
|
||||
export * from './lib';
|
@ -1,78 +0,0 @@
|
||||
import { Poke, Scry } from '../lib';
|
||||
import { PutBucket, Key, Bucket, DelBucket, Value, PutEntry, DelEntry, SettingsUpdate } from './types';
|
||||
|
||||
export const action = <T extends SettingsUpdate>(data: T): Poke<T> => ({
|
||||
app: 'settings-store',
|
||||
mark: 'settings-event',
|
||||
json: data
|
||||
});
|
||||
|
||||
export const putBucket = (
|
||||
desk: string,
|
||||
key: Key,
|
||||
bucket: Bucket
|
||||
): Poke<PutBucket> => action({
|
||||
'put-bucket': {
|
||||
desk,
|
||||
'bucket-key': key,
|
||||
'bucket': bucket
|
||||
}
|
||||
});
|
||||
|
||||
export const delBucket = (
|
||||
desk: string,
|
||||
key: Key
|
||||
): Poke<DelBucket> => action({
|
||||
'del-bucket': {
|
||||
desk,
|
||||
'bucket-key': key
|
||||
}
|
||||
});
|
||||
|
||||
export const putEntry = (
|
||||
desk: string,
|
||||
bucket: Key,
|
||||
key: Key,
|
||||
value: Value
|
||||
): Poke<PutEntry> => action({
|
||||
'put-entry': {
|
||||
desk,
|
||||
'bucket-key': bucket,
|
||||
'entry-key': key,
|
||||
value: value
|
||||
}
|
||||
});
|
||||
|
||||
export const delEntry = (
|
||||
desk: string,
|
||||
bucket: Key,
|
||||
key: Key
|
||||
): Poke<DelEntry> => action({
|
||||
'del-entry': {
|
||||
desk,
|
||||
'bucket-key': bucket,
|
||||
'entry-key': key
|
||||
}
|
||||
});
|
||||
|
||||
export const getAll: Scry = {
|
||||
app: 'settings-store',
|
||||
path: '/all'
|
||||
};
|
||||
|
||||
export const getBucket = (desk: string, bucket: string) => ({
|
||||
app: 'settings-store',
|
||||
path: `/bucket/${bucket}`
|
||||
});
|
||||
|
||||
export const getEntry = (desk: string, bucket: string, entry: string) => ({
|
||||
app: 'settings-store',
|
||||
path: `/entry/${desk}/${bucket}/${entry}`
|
||||
});
|
||||
|
||||
export const getDeskSettings = (desk: string) => ({
|
||||
app: 'settings-store',
|
||||
path: `/desk/${desk}`
|
||||
});
|
||||
|
||||
export * from './types';
|
@ -1,64 +0,0 @@
|
||||
export type Key = string;
|
||||
export type Value = string | string[] | boolean | number;
|
||||
export type Bucket = { [key: string]: Value; };
|
||||
export type DeskSettings = { [bucket: string]: Bucket; };
|
||||
export type Settings = { [desk: string]: Settings; }
|
||||
|
||||
export interface PutBucket {
|
||||
'put-bucket': {
|
||||
desk: string;
|
||||
'bucket-key': Key;
|
||||
'bucket': Bucket;
|
||||
};
|
||||
}
|
||||
|
||||
export interface DelBucket {
|
||||
'del-bucket': {
|
||||
desk: string;
|
||||
'bucket-key': Key;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PutEntry {
|
||||
'put-entry': {
|
||||
'bucket-key': Key;
|
||||
'entry-key': Key;
|
||||
'value'?: Value;
|
||||
};
|
||||
}
|
||||
|
||||
export interface DelEntry {
|
||||
'del-entry': {
|
||||
desk: string;
|
||||
'bucket-key': Key;
|
||||
'entry-key': Key;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AllData {
|
||||
'all': Settings;
|
||||
}
|
||||
|
||||
export interface DeskData {
|
||||
desk: DeskSettings;
|
||||
}
|
||||
|
||||
export interface BucketData {
|
||||
'bucket': Bucket;
|
||||
}
|
||||
|
||||
export interface EntryData {
|
||||
'entry': Value;
|
||||
}
|
||||
|
||||
export type SettingsUpdate =
|
||||
| PutBucket
|
||||
| DelBucket
|
||||
| PutEntry
|
||||
| DelEntry;
|
||||
|
||||
export type SettingsData =
|
||||
| AllData
|
||||
| BucketData
|
||||
| EntryData
|
||||
| DeskData;
|
@ -1,2 +0,0 @@
|
||||
export * from './types';
|
||||
export * from './lib';
|
@ -1,20 +0,0 @@
|
||||
import { Scry } from '../../http-api/src'
|
||||
import { Poke } from '../../http-api/src/types';
|
||||
import { Belt, Task, SessionTask } from './types';
|
||||
|
||||
export const pokeTask = (session: string, task: Task): Poke<SessionTask> => ({
|
||||
app: 'herm',
|
||||
mark: 'herm-task',
|
||||
json: { session, ...task }
|
||||
});
|
||||
|
||||
export const pokeBelt = (
|
||||
session: string,
|
||||
belt: Belt
|
||||
): Poke<SessionTask> => pokeTask(session, { belt });
|
||||
|
||||
//NOTE scry will return string[]
|
||||
export const scrySessions = (): Scry => ({
|
||||
app: 'herm',
|
||||
path: `/sessions`
|
||||
});
|
@ -1,61 +0,0 @@
|
||||
// outputs
|
||||
//
|
||||
|
||||
export type TermUpdate =
|
||||
| Blit;
|
||||
|
||||
export type Tint =
|
||||
| null
|
||||
| 'r' | 'g' | 'b' | 'c' | 'm' | 'y' | 'k' | 'w'
|
||||
| { r: number, g: number, b: number };
|
||||
|
||||
export type Deco = null | 'br' | 'un' | 'bl';
|
||||
|
||||
export type Stye = {
|
||||
deco: Deco[],
|
||||
back: Tint,
|
||||
fore: Tint
|
||||
};
|
||||
|
||||
export type Stub = {
|
||||
stye: Stye,
|
||||
text: string[]
|
||||
}
|
||||
|
||||
export type Blit =
|
||||
| { bel: null } // make a noise
|
||||
| { clr: null } // clear the screen
|
||||
| { hop: number | { x: number, y: number } } // set cursor col/pos
|
||||
| { klr: Stub[] } // put styled
|
||||
| { mor: Blit[] } // multiple blits
|
||||
| { nel: null } // newline
|
||||
| { put: string[] } // put text at cursor
|
||||
| { sag: { path: string, file: string } } // save to jamfile
|
||||
| { sav: { path: string, file: string } } // save to file
|
||||
| { url: string } // activate url
|
||||
| { wyp: null } // wipe cursor line
|
||||
|
||||
// inputs
|
||||
//
|
||||
|
||||
export type Bolt =
|
||||
| string
|
||||
| { aro: 'd' | 'l' | 'r' | 'u' }
|
||||
| { bac: null }
|
||||
| { del: null }
|
||||
| { hit: { x: number, y: number } }
|
||||
| { ret: null }
|
||||
|
||||
export type Belt =
|
||||
| Bolt
|
||||
| { mod: { mod: 'ctl' | 'met' | 'hyp', key: Bolt } }
|
||||
| { txt: Array<string> }
|
||||
|
||||
export type Task =
|
||||
| { belt: Belt }
|
||||
| { blew: { w: number, h: number } }
|
||||
| { hail: null }
|
||||
| { open: { term: string, apps: Array<{ who: string, app: string }> } }
|
||||
| { shut: null }
|
||||
|
||||
export type SessionTask = { session: string } & Task
|
@ -1,24 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "./tmp",
|
||||
"module": "ESNext",
|
||||
"noImplicitAny": true,
|
||||
"target": "ESNext",
|
||||
"pretty": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"strict": false,
|
||||
"noErrorTruncation": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"./dist/**/*",
|
||||
"./tmp/**/*",
|
||||
"rollup.config.ts"
|
||||
],
|
||||
"include": ["./*.ts"]
|
||||
}
|
@ -1,204 +0,0 @@
|
||||
const env = {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
};
|
||||
|
||||
const rules = {
|
||||
"array-bracket-spacing": ["error", "never"],
|
||||
"arrow-parens": [
|
||||
"error",
|
||||
"as-needed",
|
||||
{
|
||||
"requireForBlockBody": true
|
||||
}
|
||||
],
|
||||
"arrow-spacing": "error",
|
||||
"@typescript-eslint/ban-ts-comment": [2,
|
||||
{
|
||||
'ts-expect-error': 'allow-with-description',
|
||||
'ts-ignore': 'allow-with-description',
|
||||
'ts-nocheck': 'allow-with-description',
|
||||
'ts-check': 'allow-with-description',
|
||||
minimumDescriptionLength: 3,
|
||||
}],
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
"block-spacing": ["error", "always"],
|
||||
"brace-style": ["error", "1tbs"],
|
||||
"camelcase": [
|
||||
"error",
|
||||
{
|
||||
"properties": "never"
|
||||
}
|
||||
],
|
||||
"comma-dangle": ["error", "never"],
|
||||
"eol-last": ["error", "always"],
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"func-name-matching": "error",
|
||||
"indent": [
|
||||
"off",
|
||||
2,
|
||||
{
|
||||
"ArrayExpression": "off",
|
||||
"SwitchCase": 1,
|
||||
"CallExpression": {
|
||||
"arguments": "off"
|
||||
},
|
||||
"FunctionDeclaration": {
|
||||
"parameters": "off"
|
||||
},
|
||||
"FunctionExpression": {
|
||||
"parameters": "off"
|
||||
},
|
||||
"MemberExpression": "off",
|
||||
"ObjectExpression": "off",
|
||||
"ImportDeclaration": "off"
|
||||
}
|
||||
],
|
||||
"handle-callback-err": "off",
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"max-lines": [
|
||||
"warn",
|
||||
{
|
||||
"max": 300,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
],
|
||||
"max-lines-per-function": [
|
||||
"warn",
|
||||
{
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
],
|
||||
"max-statements-per-line": [
|
||||
"error",
|
||||
{
|
||||
"max": 1
|
||||
}
|
||||
],
|
||||
"new-cap": [
|
||||
"error",
|
||||
{
|
||||
"newIsCap": true,
|
||||
"capIsNew": false
|
||||
}
|
||||
],
|
||||
"new-parens": "error",
|
||||
"no-buffer-constructor": "error",
|
||||
"no-console": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"no-extra-semi": "off",
|
||||
"no-fallthrough": "off",
|
||||
"no-func-assign": "off",
|
||||
"no-implicit-coercion": "error",
|
||||
"no-multi-assign": "error",
|
||||
"no-multiple-empty-lines": [
|
||||
"error",
|
||||
{
|
||||
"max": 1
|
||||
}
|
||||
],
|
||||
"no-nested-ternary": "warn",
|
||||
"no-param-reassign": "off",
|
||||
"no-return-assign": "error",
|
||||
"no-return-await": "off",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"no-tabs": "error",
|
||||
"no-trailing-spaces": "error",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"vars": "all",
|
||||
"args": "none",
|
||||
"ignoreRestSiblings": false
|
||||
}
|
||||
],
|
||||
"no-use-before-define": "off",
|
||||
"@typescript-eslint/no-use-before-define": [
|
||||
"error",
|
||||
{
|
||||
"functions": false,
|
||||
"classes": false
|
||||
}
|
||||
],
|
||||
"no-useless-escape": "off",
|
||||
"no-var": "error",
|
||||
"nonblock-statement-body-position": ["error", "below"],
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"padded-blocks": ["error", "never"],
|
||||
"prefer-arrow-callback": "error",
|
||||
"prefer-const": [
|
||||
"error",
|
||||
{
|
||||
"destructuring": "all",
|
||||
"ignoreReadBeforeAssign": true
|
||||
}
|
||||
],
|
||||
"prefer-template": "off",
|
||||
"quotes": ["error", "single"],
|
||||
"react/display-name": "off",
|
||||
"semi": ["error", "always"],
|
||||
"spaced-comment": [
|
||||
"error",
|
||||
"always",
|
||||
{
|
||||
"exceptions": ["!"]
|
||||
}
|
||||
],
|
||||
"space-before-blocks": "error",
|
||||
"unicode-bom": ["error", "never"],
|
||||
"valid-jsdoc": "error",
|
||||
"wrap-iife": ["error", "inside"],
|
||||
"react/jsx-closing-bracket-location": 1,
|
||||
"react/jsx-tag-spacing": 1,
|
||||
"react/jsx-max-props-per-line": ["error", { "maximum": 2, "when": "multiline" }],
|
||||
"react/prop-types": 0
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
"env": env,
|
||||
"extends": [
|
||||
"plugin:react/recommended",
|
||||
"eslint:recommended",
|
||||
],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "^16.5.2"
|
||||
}
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 10,
|
||||
"requireConfigFile": false,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"root": true,
|
||||
"rules": rules,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.ts", "**/*.tsx"],
|
||||
"env": env,
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": { "jsx": true },
|
||||
"ecmaVersion": 10,
|
||||
"requireConfigFile": false,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"rules": {
|
||||
...rules,
|
||||
"valid-jsdoc": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
1873
pkg/npm/eslint-config/package-lock.json
generated
1873
pkg/npm/eslint-config/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "@urbit/eslint-config",
|
||||
"version": "1.0.3",
|
||||
"description": "",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@github.com/urbit/urbit.git",
|
||||
"directory": "pkg/npm/eslint-config"
|
||||
},
|
||||
"main": "index.js",
|
||||
"scripts": {},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^4.15.0",
|
||||
"@typescript-eslint/parser": "^4.15.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^7.26.0",
|
||||
"eslint-plugin-react": "^7.22.0",
|
||||
"typescript": "^4.1.5"
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
"@babel/preset-typescript", //needed for .ts jest tests
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": "> 1%",
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.19.1"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
4
pkg/npm/http-api/.gitignore
vendored
4
pkg/npm/http-api/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
dist
|
||||
node_modules
|
||||
coverage
|
||||
tmp
|
@ -1,21 +0,0 @@
|
||||
# Urbit Connector
|
||||
|
||||
This project allows you to connect to an [Urbit](https://urbit.org) ship via a JavaScript application.
|
||||
|
||||
## Example
|
||||
|
||||
Check out the `example` directory for examples of how to use this code.
|
||||
|
||||
1. Open `example/index.html` in your browser and follow the instructions there, or
|
||||
2. With a ship running in the same fashion as indicated in the file above, run `node example/index.js`
|
||||
|
||||
The code for either of these can be found in `src/example/browser.js` or `src/example/node.js`, depending on your context.
|
||||
|
||||
## Design
|
||||
|
||||
This library is designed to be useful for node applications that communicate with an urbit running either on the local computer or on a remote one.
|
||||
|
||||
The majority of its methods are asynchronous and return Promises. This is due to the non-blocking nature of JavaScript. If used in a React app, response handlers should be bound with `this` to `setState` after a message is received.
|
||||
|
||||
## NOTE
|
||||
You must enable CORS requests on your urbit for this library to work in browser context. Use `+cors-registry` to see domains which have made requests to your urbit, and then approve the needed one, e.g. `|cors-approve http://zod.arvo.network`.
|
@ -1,3 +0,0 @@
|
||||
// import Urbit from '../../dist/browser';
|
||||
|
||||
// window.Urbit = Urbit;
|
@ -1,122 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Demo</title>
|
||||
<script src="../dist/urbit-http-api.min.js"></script>
|
||||
<style>
|
||||
@import url('https://rsms.me/inter/inter.css');
|
||||
@font-face {
|
||||
font-family: 'Source Code Pro';
|
||||
src: url('https://storage.googleapis.com/media.urbit.org/fonts/scp-regular.woff');
|
||||
font-weight: 400;
|
||||
}
|
||||
body {
|
||||
margin: 0 auto;
|
||||
max-width: 70ch;
|
||||
padding: 2ch;
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
#mylog {
|
||||
white-space: pre-wrap;
|
||||
padding: 2ch;
|
||||
background: black;
|
||||
color: white;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
}
|
||||
#mylog div {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.chunk {
|
||||
border-bottom: 1px dashed currentColor;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<details>
|
||||
<summary>Show instructions</summary>
|
||||
<p>Assuming you are running a fakezod on port 8080, run</p>
|
||||
<code id="instructions">|cors-approve '{window.location.origin}'</code>
|
||||
<p>in its dojo.</p>
|
||||
<p>
|
||||
Press the button to run the code below. Output will be logged. You
|
||||
should see <code>< ~zod: opening airlock</code> in your dojo. Create
|
||||
a chat and send a message to see the events logged.
|
||||
</p>
|
||||
<pre>
|
||||
window.airlock = await UrbitHttpApi.Urbit.authenticate({
|
||||
ship: 'zod',
|
||||
url: 'localhost:8080',
|
||||
code: 'lidlut-tabwed-pillex-ridrup',
|
||||
verbose: true
|
||||
});
|
||||
window.airlock.subscribe({
|
||||
app: 'graph-store',
|
||||
path: '/updates',
|
||||
event: console.log
|
||||
});</pre
|
||||
>
|
||||
</details>
|
||||
|
||||
<button id="blastoff" onclick="blastOff()">Blast Off</button>
|
||||
<pre id="mylog"></pre>
|
||||
</body>
|
||||
<script>
|
||||
var baseLogFunction = console.log;
|
||||
console.log = function () {
|
||||
baseLogFunction.apply(console, arguments);
|
||||
var chunk = document.createElement('div');
|
||||
chunk.className = 'chunk';
|
||||
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
const val =
|
||||
typeof args[i] === 'string' ? args[i] : JSON.stringify(args[i]);
|
||||
var node = createLogNode(val);
|
||||
chunk.appendChild(node);
|
||||
}
|
||||
document
|
||||
.querySelector('#mylog')
|
||||
.insertBefore(chunk, document.querySelector('#mylog').firstChild);
|
||||
};
|
||||
|
||||
function createLogNode(message) {
|
||||
var node = document.createElement('div');
|
||||
node.className = 'message';
|
||||
var textNode = document.createTextNode(message);
|
||||
node.appendChild(textNode);
|
||||
return node;
|
||||
}
|
||||
|
||||
window.onerror = function (message, url, linenumber) {
|
||||
console.log(
|
||||
'JavaScript error: ' +
|
||||
message +
|
||||
' on line ' +
|
||||
linenumber +
|
||||
' for ' +
|
||||
url
|
||||
);
|
||||
};
|
||||
const instructions = document.getElementById('instructions');
|
||||
instructions.innerText = instructions.innerText.replace(
|
||||
'{window.location.origin}',
|
||||
window.location.origin
|
||||
);
|
||||
async function blastOff() {
|
||||
window.airlock = await UrbitHttpApi.Urbit.authenticate({
|
||||
ship: 'zod',
|
||||
url: 'localhost',
|
||||
code: 'lidlut-tabwed-pillex-ridrup',
|
||||
verbose: true,
|
||||
});
|
||||
window.airlock.subscribe({
|
||||
app: 'graph-store',
|
||||
path: '/updates',
|
||||
event: console.log,
|
||||
});
|
||||
document.body.removeChild(document.getElementById('blastoff'));
|
||||
}
|
||||
</script>
|
||||
</html>
|
@ -1,14 +0,0 @@
|
||||
// import Urbit from '../../dist/index';
|
||||
|
||||
// async function blastOff() {
|
||||
// const airlock = await Urbit.authenticate({
|
||||
// ship: 'zod',
|
||||
// url: 'localhost:8080',
|
||||
// code: 'lidlut-tabwed-pillex-ridrup',
|
||||
// verbose: true
|
||||
// });
|
||||
|
||||
// airlock.subscribe('chat-view', '/primary');
|
||||
// }
|
||||
|
||||
// blastOff();
|
@ -1,194 +0,0 @@
|
||||
/*
|
||||
* For a detailed explanation regarding each configuration property and type check, visit:
|
||||
* https://jestjs.io/docs/configuration
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
automock: false,
|
||||
|
||||
// Stop running tests after `n` failures
|
||||
// bail: 0,
|
||||
|
||||
// The directory where Jest should store its cached dependency information
|
||||
// cacheDirectory: "/private/var/folders/7w/hvrpvq7978bbb9kwkbhsn6rr0000gn/T/jest_dx",
|
||||
|
||||
// Automatically clear mock calls and instances between every test
|
||||
clearMocks: true,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
collectCoverage: true,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
// collectCoverageFrom: undefined,
|
||||
|
||||
// The directory where Jest should output its coverage files
|
||||
coverageDirectory: 'coverage',
|
||||
|
||||
// An array of regexp pattern strings used to skip coverage collection
|
||||
// coveragePathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// Indicates which provider should be used to instrument code for coverage
|
||||
// coverageProvider: "babel",
|
||||
|
||||
// A list of reporter names that Jest uses when writing coverage reports
|
||||
// coverageReporters: [
|
||||
// "json",
|
||||
// "text",
|
||||
// "lcov",
|
||||
// "clover"
|
||||
// ],
|
||||
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
// coverageThreshold: undefined,
|
||||
|
||||
// A path to a custom dependency extractor
|
||||
// dependencyExtractor: undefined,
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
// errorOnDeprecated: false,
|
||||
|
||||
// Force coverage collection from ignored files using an array of glob patterns
|
||||
// forceCoverageMatch: [],
|
||||
|
||||
// A path to a module which exports an async function that is triggered once before all test suites
|
||||
// globalSetup: undefined,
|
||||
|
||||
// A path to a module which exports an async function that is triggered once after all test suites
|
||||
// globalTeardown: undefined,
|
||||
|
||||
// A set of global variables that need to be available in all test environments
|
||||
// globals: {},
|
||||
|
||||
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
||||
maxWorkers: 1,
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
// moduleDirectories: [
|
||||
// "node_modules"
|
||||
// ],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
// moduleFileExtensions: [
|
||||
// "js",
|
||||
// "jsx",
|
||||
// "ts",
|
||||
// "tsx",
|
||||
// "json",
|
||||
// "node"
|
||||
// ],
|
||||
|
||||
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||
// moduleNameMapper: {},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
|
||||
// An enum that specifies notification mode. Requires { notify: true }
|
||||
// notifyMode: "failure-change",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
// preset: undefined,
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: undefined,
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
// reporters: undefined,
|
||||
|
||||
// Automatically reset mock state between every test
|
||||
// resetMocks: false,
|
||||
|
||||
// Reset the module registry before running each individual test
|
||||
// resetModules: false,
|
||||
|
||||
// A path to a custom resolver
|
||||
// resolver: undefined,
|
||||
|
||||
// Automatically restore mock state between every test
|
||||
// restoreMocks: false,
|
||||
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
// rootDir: undefined,
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
// roots: [
|
||||
// "<rootDir>"
|
||||
// ],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
// runner: "jest-runner",
|
||||
|
||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||
setupFiles: ['./setupEnv.js'],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
// setupFilesAfterEnv: [],
|
||||
|
||||
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
||||
// slowTestThreshold: 5,
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
testEnvironment: 'jsdom',
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
// testMatch: [
|
||||
// "**/__tests__/**/*.[jt]s?(x)",
|
||||
// "**/?(*.)+(spec|test).[tj]s?(x)"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||
// testRegex: [],
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: undefined,
|
||||
|
||||
// This option allows use of a custom test runner
|
||||
// testRunner: "jest-circus/runner",
|
||||
|
||||
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
|
||||
testURL: 'http://localhost',
|
||||
|
||||
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
|
||||
// timers: "real",
|
||||
|
||||
// A map from regular expressions to paths to transformers
|
||||
// transform: undefined,
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
// transformIgnorePatterns: [
|
||||
// "/node_modules/",
|
||||
// "\\.pnp\\.[^\\/]+$"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: undefined,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
// watchman: true,
|
||||
};
|
7854
pkg/npm/http-api/package-lock.json
generated
7854
pkg/npm/http-api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,70 +0,0 @@
|
||||
{
|
||||
"name": "@urbit/http-api",
|
||||
"version": "2.3.0",
|
||||
"license": "MIT",
|
||||
"description": "Library to interact with an Urbit ship over HTTP",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@github.com/urbit/urbit.git",
|
||||
"directory": "pkg/npm/http-api"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "dist/cjs/index.cjs",
|
||||
"module": "dist/esm/index.js",
|
||||
"exports": {
|
||||
"require": "./dist/cjs/index.cjs",
|
||||
"import": "./dist/esm/index.js"
|
||||
},
|
||||
"jsdelivr": "dist/urbit-http-api.min.js",
|
||||
"unpkg": "dist/urbit-http-api.min.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist/**"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"build": "npm run clean && rollup -c && npx tsc -p tsconfig.json",
|
||||
"prepare": "npm run build",
|
||||
"watch": "rollup -c -w",
|
||||
"clean": "rm -rf dist/* types/*"
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 80,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5"
|
||||
},
|
||||
"author": "",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.8",
|
||||
"@babel/preset-env": "^7.15.8",
|
||||
"@babel/preset-typescript": "^7.16.0",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^21.0.1",
|
||||
"@rollup/plugin-node-resolve": "^13.0.6",
|
||||
"@types/browser-or-node": "^1.2.0",
|
||||
"@types/eventsource": "^1.1.5",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@types/react": "^16.9.56",
|
||||
"@typescript-eslint/eslint-plugin": "^4.7.0",
|
||||
"@typescript-eslint/parser": "^4.7.0",
|
||||
"babel-jest": "^27.0.6",
|
||||
"cross-fetch": "^3.1.4",
|
||||
"event-target-polyfill": "0.0.3",
|
||||
"fast-text-encoding": "^1.0.3",
|
||||
"jest": "^27.0.6",
|
||||
"rollup": "^2.59.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-typescript2": "^0.30.0",
|
||||
"typescript": "^3.9.7",
|
||||
"util": "^0.12.3",
|
||||
"web-streams-polyfill": "^3.0.3",
|
||||
"yet-another-abortcontroller-polyfill": "0.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@microsoft/fetch-event-source": "^2.0.0",
|
||||
"browser-or-node": "^1.3.0",
|
||||
"core-js": "^3.19.1"
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
||||
import commonJS from '@rollup/plugin-commonjs';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import babel from '@rollup/plugin-babel';
|
||||
import typescript from 'rollup-plugin-typescript2';
|
||||
|
||||
const input = ['src/index.ts'];
|
||||
|
||||
// Skip certain warnings
|
||||
function onwarn(warning) {
|
||||
if (warning.code === 'THIS_IS_UNDEFINED') {
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(warning.message);
|
||||
}
|
||||
|
||||
export default [
|
||||
{
|
||||
input,
|
||||
onwarn,
|
||||
plugins: [
|
||||
nodeResolve({
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
}),
|
||||
commonJS(),
|
||||
typescript(),
|
||||
babel({
|
||||
babelHelpers: 'bundled',
|
||||
exclude: ['node_modules/**'],
|
||||
}),
|
||||
terser({
|
||||
ecma: 2017,
|
||||
compress: true,
|
||||
mangle: true,
|
||||
}),
|
||||
],
|
||||
output: {
|
||||
file: `dist/urbit-http-api.min.js`,
|
||||
format: 'umd',
|
||||
name: 'UrbitHttpApi', // this is the name of the global object
|
||||
esModule: false,
|
||||
exports: 'named',
|
||||
sourcemap: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
input,
|
||||
onwarn,
|
||||
plugins: [
|
||||
nodeResolve({
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
}),
|
||||
commonJS(),
|
||||
typescript(),
|
||||
],
|
||||
output: [
|
||||
{
|
||||
file: 'dist/esm/index.js',
|
||||
format: 'esm',
|
||||
exports: 'named',
|
||||
sourcemap: true,
|
||||
},
|
||||
{
|
||||
file: 'dist/cjs/index.cjs',
|
||||
format: 'cjs',
|
||||
exports: 'named',
|
||||
sourcemap: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
@ -1,8 +0,0 @@
|
||||
require('event-target-polyfill');
|
||||
require('yet-another-abortcontroller-polyfill');
|
||||
require('cross-fetch/polyfill');
|
||||
require('fast-text-encoding');
|
||||
require('web-streams-polyfill');
|
||||
|
||||
global.ReadableStream = require('web-streams-polyfill').ReadableStream;
|
||||
|
@ -1,602 +0,0 @@
|
||||
import { isBrowser, isNode } from 'browser-or-node';
|
||||
import {
|
||||
fetchEventSource,
|
||||
EventSourceMessage,
|
||||
} from '@microsoft/fetch-event-source';
|
||||
|
||||
import {
|
||||
Scry,
|
||||
Thread,
|
||||
AuthenticationInterface,
|
||||
PokeInterface,
|
||||
SubscriptionRequestInterface,
|
||||
headers,
|
||||
SSEOptions,
|
||||
PokeHandlers,
|
||||
Message,
|
||||
FatalError,
|
||||
} from './types';
|
||||
import { hexString } from './utils';
|
||||
|
||||
/**
|
||||
* A class for interacting with an urbit ship, given its URL and code
|
||||
*/
|
||||
export class Urbit {
|
||||
/**
|
||||
* UID will be used for the channel: The current unix time plus a random hex string
|
||||
*/
|
||||
private uid: string = `${Math.floor(Date.now() / 1000)}-${hexString(6)}`;
|
||||
|
||||
/**
|
||||
* lastEventId is an auto-updated index of which events have been *sent* over this channel.
|
||||
* lastHeardEventId is the latest event we have heard back about.
|
||||
* lastAcknowledgedEventId is the latest event we have sent an ack for.
|
||||
*/
|
||||
private lastEventId: number = 0;
|
||||
private lastHeardEventId: number = -1;
|
||||
private lastAcknowledgedEventId: number = -1;
|
||||
|
||||
/**
|
||||
* SSE Client is null for now; we don't want to start polling until it the channel exists
|
||||
*/
|
||||
private sseClientInitialized: boolean = false;
|
||||
|
||||
/**
|
||||
* Cookie gets set when we log in.
|
||||
*/
|
||||
cookie?: string | undefined;
|
||||
|
||||
/**
|
||||
* A registry of requestId to successFunc/failureFunc
|
||||
*
|
||||
* These functions are registered during a +poke and are executed
|
||||
* in the onServerEvent()/onServerError() callbacks. Only one of
|
||||
* the functions will be called, and the outstanding poke will be
|
||||
* removed after calling the success or failure function.
|
||||
*/
|
||||
|
||||
private outstandingPokes: Map<number, PokeHandlers> = new Map();
|
||||
|
||||
/**
|
||||
* A registry of requestId to subscription functions.
|
||||
*
|
||||
* These functions are registered during a +subscribe and are
|
||||
* executed in the onServerEvent()/onServerError() callbacks. The
|
||||
* event function will be called whenever a new piece of data on this
|
||||
* subscription is available, which may be 0, 1, or many times. The
|
||||
* disconnect function may be called exactly once.
|
||||
*/
|
||||
private outstandingSubscriptions: Map<number, SubscriptionRequestInterface> =
|
||||
new Map();
|
||||
|
||||
/**
|
||||
* Our abort controller, used to close the connection
|
||||
*/
|
||||
private abort = new AbortController();
|
||||
|
||||
/**
|
||||
* Ship can be set, in which case we can do some magic stuff like send chats
|
||||
*/
|
||||
ship?: string | null;
|
||||
|
||||
/**
|
||||
* If verbose, logs output eagerly.
|
||||
*/
|
||||
verbose?: boolean;
|
||||
|
||||
/**
|
||||
* number of consecutive errors in connecting to the eventsource
|
||||
*/
|
||||
private errorCount = 0;
|
||||
|
||||
onError?: (error: any) => void = null;
|
||||
|
||||
onRetry?: () => void = null;
|
||||
|
||||
onOpen?: () => void = null;
|
||||
|
||||
/** This is basic interpolation to get the channel URL of an instantiated Urbit connection. */
|
||||
private get channelUrl(): string {
|
||||
return `${this.url}/~/channel/${this.uid}`;
|
||||
}
|
||||
|
||||
private get fetchOptions(): any {
|
||||
const headers: headers = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
if (!isBrowser) {
|
||||
headers.Cookie = this.cookie;
|
||||
}
|
||||
return {
|
||||
credentials: 'include',
|
||||
accept: '*',
|
||||
headers,
|
||||
signal: this.abort.signal,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new Urbit connection.
|
||||
*
|
||||
* @param url The URL (with protocol and port) of the ship to be accessed. If
|
||||
* the airlock is running in a webpage served by the ship, this should just
|
||||
* be the empty string.
|
||||
* @param code The access code for the ship at that address
|
||||
*/
|
||||
constructor(public url: string, public code?: string, public desk?: string) {
|
||||
if (isBrowser) {
|
||||
window.addEventListener('beforeunload', this.delete);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* All-in-one hook-me-up.
|
||||
*
|
||||
* Given a ship, url, and code, this returns an airlock connection
|
||||
* that is ready to go. It `|hi`s itself to create the channel,
|
||||
* then opens the channel via EventSource.
|
||||
*
|
||||
*/
|
||||
static async authenticate({
|
||||
ship,
|
||||
url,
|
||||
code,
|
||||
verbose = false,
|
||||
}: AuthenticationInterface) {
|
||||
const airlock = new Urbit(url.startsWith('http') ? url : `http://${url}`, code);
|
||||
airlock.verbose = verbose;
|
||||
airlock.ship = ship;
|
||||
await airlock.connect();
|
||||
await airlock.poke({
|
||||
app: 'hood',
|
||||
mark: 'helm-hi',
|
||||
json: 'opening airlock',
|
||||
});
|
||||
await airlock.eventSource();
|
||||
return airlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the Urbit ship. Nothing can be done until this is called.
|
||||
* That's why we roll it into this.authenticate
|
||||
*/
|
||||
async connect(): Promise<void> {
|
||||
if (this.verbose) {
|
||||
console.log(
|
||||
`password=${this.code} `,
|
||||
isBrowser
|
||||
? 'Connecting in browser context at ' + `${this.url}/~/login`
|
||||
: 'Connecting from node context'
|
||||
);
|
||||
}
|
||||
return fetch(`${this.url}/~/login`, {
|
||||
method: 'post',
|
||||
body: `password=${this.code}`,
|
||||
credentials: 'include',
|
||||
}).then((response) => {
|
||||
if (this.verbose) {
|
||||
console.log('Received authentication response', response);
|
||||
}
|
||||
const cookie = response.headers.get('set-cookie');
|
||||
if (!this.ship) {
|
||||
this.ship = new RegExp(/urbauth-~([\w-]+)/).exec(cookie)[1];
|
||||
}
|
||||
if (!isBrowser) {
|
||||
this.cookie = cookie;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the SSE pipe for the appropriate channel.
|
||||
*/
|
||||
async eventSource(): Promise<void> {
|
||||
if (this.sseClientInitialized) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (this.lastEventId === 0) {
|
||||
// Can't receive events until the channel is open,
|
||||
// so poke and open then
|
||||
await this.poke({
|
||||
app: 'hood',
|
||||
mark: 'helm-hi',
|
||||
json: 'Opening API channel',
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.sseClientInitialized = true;
|
||||
return new Promise((resolve, reject) => {
|
||||
const sseOptions: SSEOptions = {
|
||||
headers: {},
|
||||
};
|
||||
if (isBrowser) {
|
||||
sseOptions.withCredentials = true;
|
||||
} else if (isNode) {
|
||||
sseOptions.headers.Cookie = this.cookie;
|
||||
}
|
||||
fetchEventSource(this.channelUrl, {
|
||||
...this.fetchOptions,
|
||||
openWhenHidden: true,
|
||||
onopen: async (response) => {
|
||||
if (this.verbose) {
|
||||
console.log('Opened eventsource', response);
|
||||
}
|
||||
if (response.ok) {
|
||||
this.errorCount = 0;
|
||||
this.onOpen && this.onOpen();
|
||||
resolve();
|
||||
return; // everything's good
|
||||
} else {
|
||||
const err = new Error('failed to open eventsource');
|
||||
reject(err);
|
||||
}
|
||||
},
|
||||
onmessage: (event: EventSourceMessage) => {
|
||||
if (this.verbose) {
|
||||
console.log('Received SSE: ', event);
|
||||
}
|
||||
if (!event.id) return;
|
||||
const eventId = parseInt(event.id, 10);
|
||||
if (eventId <= this.lastHeardEventId) {
|
||||
console.log('dropping old or out-of-order event', {
|
||||
eventId, lastHeard: this.lastHeardEventId
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.lastHeardEventId = eventId;
|
||||
if (eventId - this.lastAcknowledgedEventId > 20) {
|
||||
this.ack(eventId);
|
||||
}
|
||||
|
||||
if (event.data && JSON.parse(event.data)) {
|
||||
const data: any = JSON.parse(event.data);
|
||||
|
||||
if (
|
||||
data.response === 'poke' &&
|
||||
this.outstandingPokes.has(data.id)
|
||||
) {
|
||||
const funcs = this.outstandingPokes.get(data.id);
|
||||
if (data.hasOwnProperty('ok')) {
|
||||
funcs.onSuccess();
|
||||
} else if (data.hasOwnProperty('err')) {
|
||||
console.error(data.err);
|
||||
funcs.onError(data.err);
|
||||
} else {
|
||||
console.error('Invalid poke response', data);
|
||||
}
|
||||
this.outstandingPokes.delete(data.id);
|
||||
} else if (
|
||||
data.response === 'subscribe' &&
|
||||
this.outstandingSubscriptions.has(data.id)
|
||||
) {
|
||||
const funcs = this.outstandingSubscriptions.get(data.id);
|
||||
if (data.hasOwnProperty('err')) {
|
||||
console.error(data.err);
|
||||
funcs.err(data.err, data.id);
|
||||
this.outstandingSubscriptions.delete(data.id);
|
||||
}
|
||||
} else if (
|
||||
data.response === 'diff' &&
|
||||
this.outstandingSubscriptions.has(data.id)
|
||||
) {
|
||||
const funcs = this.outstandingSubscriptions.get(data.id);
|
||||
try {
|
||||
funcs.event(data.json, data.mark ?? 'json');
|
||||
} catch (e) {
|
||||
console.error('Failed to call subscription event callback', e);
|
||||
}
|
||||
} else if (
|
||||
data.response === 'quit' &&
|
||||
this.outstandingSubscriptions.has(data.id)
|
||||
) {
|
||||
const funcs = this.outstandingSubscriptions.get(data.id);
|
||||
funcs.quit(data);
|
||||
this.outstandingSubscriptions.delete(data.id);
|
||||
} else {
|
||||
console.log([...this.outstandingSubscriptions.keys()]);
|
||||
console.log('Unrecognized response', data);
|
||||
}
|
||||
}
|
||||
},
|
||||
onerror: (error) => {
|
||||
console.warn(error);
|
||||
if (!(error instanceof FatalError) && this.errorCount++ < 4) {
|
||||
this.onRetry && this.onRetry();
|
||||
return Math.pow(2, this.errorCount - 1) * 750;
|
||||
}
|
||||
this.onError && this.onError(error);
|
||||
throw error;
|
||||
},
|
||||
onclose: () => {
|
||||
console.log('e');
|
||||
throw new Error('Ship unexpectedly closed the connection');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset airlock, abandoning current subscriptions and wiping state
|
||||
*
|
||||
*/
|
||||
reset() {
|
||||
if (this.verbose) {
|
||||
console.log('resetting');
|
||||
}
|
||||
this.delete();
|
||||
this.abort.abort();
|
||||
this.abort = new AbortController();
|
||||
this.uid = `${Math.floor(Date.now() / 1000)}-${hexString(6)}`;
|
||||
this.lastEventId = 0;
|
||||
this.lastHeardEventId = -1;
|
||||
this.lastAcknowledgedEventId = -1;
|
||||
this.outstandingSubscriptions = new Map();
|
||||
this.outstandingPokes = new Map();
|
||||
this.sseClientInitialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoincrements the next event ID for the appropriate channel.
|
||||
*/
|
||||
private getEventId(): number {
|
||||
return ++this.lastEventId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges an event.
|
||||
*
|
||||
* @param eventId The event to acknowledge.
|
||||
*/
|
||||
private async ack(eventId: number): Promise<number | void> {
|
||||
this.lastAcknowledgedEventId = eventId;
|
||||
const message: Message = {
|
||||
action: 'ack',
|
||||
'event-id': eventId,
|
||||
};
|
||||
await this.sendJSONtoChannel(message);
|
||||
return eventId;
|
||||
}
|
||||
|
||||
private async sendJSONtoChannel(...json: Message[]): Promise<void> {
|
||||
const response = await fetch(this.channelUrl, {
|
||||
...this.fetchOptions,
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(json),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to PUT channel');
|
||||
}
|
||||
if (!this.sseClientInitialized) {
|
||||
await this.eventSource();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a subscription, waits for a fact and then unsubscribes
|
||||
*
|
||||
* @param app Name of gall agent to subscribe to
|
||||
* @param path Path to subscribe to
|
||||
* @param timeout Optional timeout before ending subscription
|
||||
*
|
||||
* @returns The first fact on the subcription
|
||||
*/
|
||||
async subscribeOnce<T = any>(app: string, path: string, timeout?: number) {
|
||||
return new Promise<T>(async (resolve, reject) => {
|
||||
let done = false;
|
||||
let id: number | null = null;
|
||||
const quit = () => {
|
||||
if (!done) {
|
||||
reject('quit');
|
||||
}
|
||||
};
|
||||
const event = (e: T) => {
|
||||
if (!done) {
|
||||
resolve(e);
|
||||
this.unsubscribe(id);
|
||||
}
|
||||
};
|
||||
const request = { app, path, event, err: reject, quit };
|
||||
|
||||
id = await this.subscribe(request);
|
||||
|
||||
if (timeout) {
|
||||
setTimeout(() => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
reject('timeout');
|
||||
this.unsubscribe(id);
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pokes a ship with data.
|
||||
*
|
||||
* @param app The app to poke
|
||||
* @param mark The mark of the data being sent
|
||||
* @param json The data to send
|
||||
*/
|
||||
async poke<T>(params: PokeInterface<T>): Promise<number> {
|
||||
const { app, mark, json, ship, onSuccess, onError } = {
|
||||
onSuccess: () => {},
|
||||
onError: () => {},
|
||||
ship: this.ship,
|
||||
...params,
|
||||
};
|
||||
const message: Message = {
|
||||
id: this.getEventId(),
|
||||
action: 'poke',
|
||||
ship,
|
||||
app,
|
||||
mark,
|
||||
json,
|
||||
};
|
||||
const [send, result] = await Promise.all([
|
||||
this.sendJSONtoChannel(message),
|
||||
new Promise<number>((resolve, reject) => {
|
||||
this.outstandingPokes.set(message.id, {
|
||||
onSuccess: () => {
|
||||
onSuccess();
|
||||
resolve(message.id);
|
||||
},
|
||||
onError: (event) => {
|
||||
onError(event);
|
||||
reject(event.err);
|
||||
},
|
||||
});
|
||||
}),
|
||||
]);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to a path on an app on a ship.
|
||||
*
|
||||
*
|
||||
* @param app The app to subsribe to
|
||||
* @param path The path to which to subscribe
|
||||
* @param handlers Handlers to deal with various events of the subscription
|
||||
*/
|
||||
async subscribe(params: SubscriptionRequestInterface): Promise<number> {
|
||||
const { app, path, ship, err, event, quit } = {
|
||||
err: () => {},
|
||||
event: () => {},
|
||||
quit: () => {},
|
||||
ship: this.ship,
|
||||
...params,
|
||||
};
|
||||
|
||||
const message: Message = {
|
||||
id: this.getEventId(),
|
||||
action: 'subscribe',
|
||||
ship,
|
||||
app,
|
||||
path,
|
||||
};
|
||||
|
||||
this.outstandingSubscriptions.set(message.id, {
|
||||
app,
|
||||
path,
|
||||
err,
|
||||
event,
|
||||
quit,
|
||||
});
|
||||
|
||||
await this.sendJSONtoChannel(message);
|
||||
|
||||
return message.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes to a given subscription.
|
||||
*
|
||||
* @param subscription
|
||||
*/
|
||||
async unsubscribe(subscription: number) {
|
||||
return this.sendJSONtoChannel({
|
||||
id: this.getEventId(),
|
||||
action: 'unsubscribe',
|
||||
subscription,
|
||||
}).then(() => {
|
||||
this.outstandingSubscriptions.delete(subscription);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the connection to a channel.
|
||||
*/
|
||||
delete() {
|
||||
if (isBrowser) {
|
||||
navigator.sendBeacon(
|
||||
this.channelUrl,
|
||||
JSON.stringify([
|
||||
{
|
||||
action: 'delete',
|
||||
},
|
||||
])
|
||||
);
|
||||
} else {
|
||||
// TODO
|
||||
// this.sendMessage('delete');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scry into an gall agent at a path
|
||||
*
|
||||
* @typeParam T - Type of the scry result
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Equivalent to
|
||||
* ```hoon
|
||||
* .^(T %gx /(scot %p our)/[app]/(scot %da now)/[path]/json)
|
||||
* ```
|
||||
* The returned cage must have a conversion to JSON for the scry to succeed
|
||||
*
|
||||
* @param params The scry request
|
||||
* @returns The scry result
|
||||
*/
|
||||
async scry<T = any>(params: Scry): Promise<T> {
|
||||
const { app, path } = params;
|
||||
const response = await fetch(
|
||||
`${this.url}/~/scry/${app}${path}.json`,
|
||||
this.fetchOptions
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
return Promise.reject(response);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a thread
|
||||
*
|
||||
*
|
||||
* @param inputMark The mark of the data being sent
|
||||
* @param outputMark The mark of the data being returned
|
||||
* @param threadName The thread to run
|
||||
* @param body The data to send to the thread
|
||||
* @returns The return value of the thread
|
||||
*/
|
||||
async thread<R, T = any>(params: Thread<T>): Promise<R> {
|
||||
const {
|
||||
inputMark,
|
||||
outputMark,
|
||||
threadName,
|
||||
body,
|
||||
desk = this.desk,
|
||||
} = params;
|
||||
if (!desk) {
|
||||
throw new Error('Must supply desk to run thread from');
|
||||
}
|
||||
const res = await fetch(
|
||||
`${this.url}/spider/${desk}/${inputMark}/${threadName}/${outputMark}.json`,
|
||||
{
|
||||
...this.fetchOptions,
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
}
|
||||
);
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to connect to a ship that has its *.arvo.network domain configured.
|
||||
*
|
||||
* @param name Name of the ship e.g. zod
|
||||
* @param code Code to log in
|
||||
*/
|
||||
static async onArvoNetwork(ship: string, code: string): Promise<Urbit> {
|
||||
const url = `https://${ship}.arvo.network`;
|
||||
return await Urbit.authenticate({ ship, url, code });
|
||||
}
|
||||
}
|
||||
|
||||
export default Urbit;
|
@ -1,3 +0,0 @@
|
||||
export * from './types';
|
||||
import { Urbit } from './Urbit';
|
||||
export { Urbit as default, Urbit };
|
@ -1,198 +0,0 @@
|
||||
/**
|
||||
* An urbit style path, rendered as a Javascript string
|
||||
* @example
|
||||
* `"/updates"`
|
||||
*/
|
||||
export type Path = string;
|
||||
|
||||
/**
|
||||
* @p including leading sig, rendered as a string
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* "~sampel-palnet"
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
export type Patp = string;
|
||||
|
||||
/**
|
||||
* @p not including leading sig, rendered as a string
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* "sampel-palnet"
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
export type PatpNoSig = string;
|
||||
|
||||
/**
|
||||
* The name of a clay mark, as a string
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* "graph-update"
|
||||
* ```
|
||||
*/
|
||||
export type Mark = string;
|
||||
|
||||
/**
|
||||
* The name of a gall agent, as a string
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```typescript
|
||||
* "graph-store"
|
||||
* ```
|
||||
*/
|
||||
export type GallAgent = string;
|
||||
|
||||
/**
|
||||
* Description of an outgoing poke
|
||||
*
|
||||
* @typeParam Action - Typescript type of the data being poked
|
||||
*/
|
||||
export interface Poke<Action> {
|
||||
/**
|
||||
* Ship to poke. If left empty, the api lib will populate it with the ship that it is connected to.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This should always be the ship that you are connected to
|
||||
*
|
||||
*/
|
||||
ship?: PatpNoSig;
|
||||
/**
|
||||
*/
|
||||
app: GallAgent;
|
||||
/**
|
||||
* Mark of the cage to be poked
|
||||
*
|
||||
*/
|
||||
mark: Mark;
|
||||
/**
|
||||
* Vase of the cage of to be poked, as JSON
|
||||
*/
|
||||
json: Action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of a scry request
|
||||
*/
|
||||
export interface Scry {
|
||||
/** {@inheritDoc GallAgent} */
|
||||
app: GallAgent;
|
||||
/** {@inheritDoc Path} */
|
||||
path: Path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of a thread request
|
||||
*
|
||||
* @typeParam Action - Typescript type of the data being poked
|
||||
*/
|
||||
export interface Thread<Action> {
|
||||
/**
|
||||
* The mark of the input vase
|
||||
*/
|
||||
inputMark: Mark;
|
||||
/**
|
||||
* The mark of the output vase
|
||||
*/
|
||||
outputMark: Mark;
|
||||
/**
|
||||
* Name of the thread
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* "graph-add-nodes"
|
||||
* ```
|
||||
*/
|
||||
threadName: string;
|
||||
/**
|
||||
* Desk of thread
|
||||
*/
|
||||
desk?: string;
|
||||
/**
|
||||
* Data of the input vase
|
||||
*/
|
||||
body: Action;
|
||||
}
|
||||
|
||||
export type Action = 'poke' | 'subscribe' | 'ack' | 'unsubscribe' | 'delete';
|
||||
|
||||
export interface PokeHandlers {
|
||||
onSuccess?: () => void;
|
||||
onError?: (e: any) => void;
|
||||
}
|
||||
|
||||
export type PokeInterface<T> = PokeHandlers & Poke<T>;
|
||||
|
||||
export interface AuthenticationInterface {
|
||||
ship: string;
|
||||
url: string;
|
||||
code: string;
|
||||
verbose?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscription event handlers
|
||||
*
|
||||
*/
|
||||
export interface SubscriptionInterface {
|
||||
/**
|
||||
* Handle negative %watch-ack
|
||||
*/
|
||||
err?(error: any, id: string): void;
|
||||
/**
|
||||
* Handle %fact
|
||||
*/
|
||||
event?(data: any, mark: string): void;
|
||||
/**
|
||||
* Handle %kick
|
||||
*/
|
||||
quit?(data: any): void;
|
||||
}
|
||||
|
||||
export type OnceSubscriptionErr = 'quit' | 'nack' | 'timeout';
|
||||
|
||||
export interface SubscriptionRequestInterface extends SubscriptionInterface {
|
||||
/**
|
||||
* The app to subscribe to
|
||||
* @example
|
||||
* `"graph-store"`
|
||||
*/
|
||||
app: GallAgent;
|
||||
/**
|
||||
* The path to which to subscribe
|
||||
* @example
|
||||
* `"/keys"`
|
||||
*/
|
||||
path: Path;
|
||||
}
|
||||
|
||||
export interface headers {
|
||||
'Content-Type': string;
|
||||
Cookie?: string;
|
||||
}
|
||||
|
||||
export interface CustomEventHandler {
|
||||
(data: any, response: string): void;
|
||||
}
|
||||
|
||||
export interface SSEOptions {
|
||||
headers?: {
|
||||
Cookie?: string;
|
||||
};
|
||||
withCredentials?: boolean;
|
||||
}
|
||||
|
||||
export interface Message extends Record<string, any> {
|
||||
action: Action;
|
||||
id?: number;
|
||||
}
|
||||
|
||||
export class ResumableError extends Error {}
|
||||
|
||||
export class FatalError extends Error {}
|
@ -1,53 +0,0 @@
|
||||
export function camelize(str: string) {
|
||||
return str
|
||||
.replace(/\s(.)/g, function ($1: string) {
|
||||
return $1.toUpperCase();
|
||||
})
|
||||
.replace(/\s/g, '')
|
||||
.replace(/^(.)/, function ($1: string) {
|
||||
return $1.toLowerCase();
|
||||
});
|
||||
}
|
||||
|
||||
export function uncamelize(str: string, separator = '-') {
|
||||
// Replace all capital letters by separator followed by lowercase one
|
||||
var str = str.replace(/[A-Z]/g, function (letter: string) {
|
||||
return separator + letter.toLowerCase();
|
||||
});
|
||||
return str.replace(new RegExp('^' + separator), '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hex string of given length.
|
||||
*
|
||||
* Poached from StackOverflow.
|
||||
*
|
||||
* @param len Length of hex string to return.
|
||||
*/
|
||||
export function hexString(len: number): string {
|
||||
const maxlen = 8;
|
||||
const min = Math.pow(16, Math.min(len, maxlen) - 1);
|
||||
const max = Math.pow(16, Math.min(len, maxlen)) - 1;
|
||||
const n = Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
let r = n.toString(16);
|
||||
while (r.length < len) {
|
||||
r = r + hexString(len - maxlen);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random UID.
|
||||
*
|
||||
* Copied from https://github.com/urbit/urbit/blob/137e4428f617c13f28ed31e520eff98d251ed3e9/pkg/interface/src/lib/util.js#L3
|
||||
*/
|
||||
export function uid(): string {
|
||||
let str = '0v';
|
||||
str += Math.ceil(Math.random() * 8) + '.';
|
||||
for (let i = 0; i < 5; i++) {
|
||||
let _str = Math.ceil(Math.random() * 10000000).toString(32);
|
||||
_str = ('00000' + _str).substr(-5, 5);
|
||||
str += _str + '.';
|
||||
}
|
||||
return str.slice(0, -1);
|
||||
}
|
@ -1,180 +0,0 @@
|
||||
import Urbit from '../src';
|
||||
import 'jest';
|
||||
|
||||
function fakeSSE(messages = [], timeout = 0) {
|
||||
const ourMessages = [...messages];
|
||||
const enc = new TextEncoder();
|
||||
return new ReadableStream({
|
||||
start(controller) {
|
||||
const interval = setInterval(() => {
|
||||
let message = ':\n';
|
||||
if (ourMessages.length > 0) {
|
||||
message = ourMessages.shift();
|
||||
}
|
||||
|
||||
controller.enqueue(enc.encode(message));
|
||||
}, 50);
|
||||
|
||||
if (timeout > 0) {
|
||||
setTimeout(() => {
|
||||
controller.close();
|
||||
clearInterval(interval);
|
||||
interval;
|
||||
}, timeout);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const ship = '~sampel-palnet';
|
||||
let eventId = 0;
|
||||
function event(data: any) {
|
||||
return `id:${eventId++}\ndata:${JSON.stringify(data)}\n\n`;
|
||||
}
|
||||
|
||||
function fact(id: number, data: any) {
|
||||
return event({
|
||||
response: 'diff',
|
||||
id,
|
||||
json: data,
|
||||
});
|
||||
}
|
||||
|
||||
function ack(id: number, err = false) {
|
||||
const res = err ? { err: 'Error' } : { ok: true };
|
||||
return event({ id, response: 'poke', ...res });
|
||||
}
|
||||
const fakeFetch = (body) => () =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
body: body(),
|
||||
});
|
||||
|
||||
const wait = (ms: number) => new Promise((res) => setTimeout(res, ms));
|
||||
|
||||
process.on('unhandledRejection', (error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
describe('Initialisation', () => {
|
||||
let airlock: Urbit;
|
||||
let fetchSpy;
|
||||
beforeEach(() => {
|
||||
airlock = new Urbit('', '+code');
|
||||
});
|
||||
afterEach(() => {
|
||||
fetchSpy.mockReset();
|
||||
});
|
||||
it('should poke & connect upon a 200', async () => {
|
||||
airlock.onOpen = jest.fn();
|
||||
fetchSpy = jest.spyOn(window, 'fetch');
|
||||
fetchSpy
|
||||
.mockImplementationOnce(() =>
|
||||
Promise.resolve({ ok: true, body: fakeSSE() })
|
||||
)
|
||||
.mockImplementationOnce(() =>
|
||||
Promise.resolve({ ok: true, body: fakeSSE([ack(1)]) })
|
||||
);
|
||||
await airlock.eventSource();
|
||||
|
||||
expect(airlock.onOpen).toHaveBeenCalled();
|
||||
}, 500);
|
||||
it('should handle failures', async () => {
|
||||
fetchSpy = jest.spyOn(window, 'fetch');
|
||||
airlock.onRetry = jest.fn();
|
||||
airlock.onOpen = jest.fn();
|
||||
fetchSpy
|
||||
.mockImplementationOnce(() =>
|
||||
Promise.resolve({ ok: true, body: fakeSSE() })
|
||||
)
|
||||
.mockImplementationOnce(() =>
|
||||
Promise.resolve({ ok: true, body: fakeSSE([], 100) })
|
||||
);
|
||||
|
||||
airlock.onError = jest.fn();
|
||||
try {
|
||||
airlock.eventSource();
|
||||
await wait(200);
|
||||
} catch (e) {
|
||||
expect(airlock.onRetry).toHaveBeenCalled();
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
|
||||
describe('subscription', () => {
|
||||
let airlock: Urbit;
|
||||
let fetchSpy: jest.SpyInstance;
|
||||
beforeEach(() => {
|
||||
eventId = 1;
|
||||
});
|
||||
afterEach(() => {
|
||||
fetchSpy.mockReset();
|
||||
});
|
||||
|
||||
it('should subscribe', async () => {
|
||||
fetchSpy = jest.spyOn(window, 'fetch');
|
||||
airlock = new Urbit('', '+code');
|
||||
airlock.onOpen = jest.fn();
|
||||
const params = {
|
||||
app: 'app',
|
||||
path: '/path',
|
||||
err: jest.fn(),
|
||||
event: jest.fn(),
|
||||
quit: jest.fn(),
|
||||
};
|
||||
const firstEv = 'one';
|
||||
const secondEv = 'two';
|
||||
const events = (id) => [fact(id, firstEv), fact(id, secondEv)];
|
||||
fetchSpy.mockImplementation(fakeFetch(() => fakeSSE(events(1))));
|
||||
|
||||
await airlock.subscribe(params);
|
||||
await wait(600);
|
||||
|
||||
expect(airlock.onOpen).toBeCalled();
|
||||
expect(params.event).toHaveBeenNthCalledWith(1, firstEv, 'json');
|
||||
expect(params.event).toHaveBeenNthCalledWith(2, secondEv, 'json');
|
||||
}, 800);
|
||||
it('should poke', async () => {
|
||||
fetchSpy = jest.spyOn(window, 'fetch');
|
||||
airlock = new Urbit('', '+code');
|
||||
airlock.onOpen = jest.fn();
|
||||
fetchSpy.mockImplementation(fakeFetch(() => fakeSSE([ack(1)])));
|
||||
const params = {
|
||||
app: 'app',
|
||||
mark: 'mark',
|
||||
json: { poke: 1 },
|
||||
onSuccess: jest.fn(),
|
||||
onError: jest.fn(),
|
||||
};
|
||||
await airlock.poke(params);
|
||||
await wait(300);
|
||||
expect(params.onSuccess).toHaveBeenCalled();
|
||||
}, 800);
|
||||
|
||||
it('should nack poke', async () => {
|
||||
fetchSpy = jest.spyOn(window, 'fetch');
|
||||
airlock = new Urbit('', '+code');
|
||||
airlock.onOpen = jest.fn();
|
||||
fetchSpy
|
||||
.mockImplementationOnce(() =>
|
||||
Promise.resolve({ ok: true, body: fakeSSE() })
|
||||
)
|
||||
.mockImplementationOnce(() =>
|
||||
Promise.resolve({ ok: false, body: fakeSSE([ack(1, true)]) })
|
||||
);
|
||||
|
||||
const params = {
|
||||
app: 'app',
|
||||
mark: 'mark',
|
||||
json: { poke: 1 },
|
||||
onSuccess: jest.fn(),
|
||||
onError: jest.fn(),
|
||||
};
|
||||
try {
|
||||
await airlock.poke(params);
|
||||
await wait(300);
|
||||
} catch (e) {
|
||||
expect(true).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
@ -1,24 +0,0 @@
|
||||
{
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist", "tmp"],
|
||||
"compilerOptions": {
|
||||
"outDir": "./tmp",
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"strict": false,
|
||||
"pretty": true,
|
||||
"noImplicitAny": true,
|
||||
"noErrorTruncation": true,
|
||||
"allowJs": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": ["./node_modules/@types/*", "*"]
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user