Merge branch 'develop' into i/6264/ames-snub-whitelist

This commit is contained in:
Josh Lehman 2023-03-16 08:30:57 -07:00 committed by GitHub
commit dbe6f7fd20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
101 changed files with 254 additions and 32970 deletions

View File

@ -1,3 +0,0 @@
module.exports = {
ignorePatterns: ["**/*"]
};

34
.github/workflows/develop.yml vendored Normal file
View 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

View File

@ -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
View 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

View File

@ -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
View 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

View File

@ -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
.nvmrc
View File

@ -1 +0,0 @@
16.14.0

View File

@ -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

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4066cd90ac7d33b3d96b1b4d611d846d941de6e538c8355047c7cafcadce0851
size 6017832
oid sha256:9d0ff563027d47436d12f00b7500352ec844a1db7d336b0e5b9cfd5c4c49c66c
size 6017943

View File

@ -1,9 +0,0 @@
{
"packages": [
"pkg/npm/*",
"pkg/btc-wallet",
"pkg/interface",
"pkg/grid"
],
"version": "independent"
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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"
}
}

View File

@ -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

View File

@ -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 &)]

View File

@ -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)

View File

@ -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=?]

View File

@ -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

View File

@ -1 +1 @@
[%zuse 415]
[%zuse 414]

View File

@ -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))))

View File

@ -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 ~]

View File

@ -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)

View File

@ -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

View File

@ -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)
::

View File

@ -4,7 +4,7 @@
=> ..lull
~% %zuse ..part ~
|%
++ zuse %415
++ zuse %414
:: :: ::
:::: :: :: (2) engines
:: :: ::

View File

@ -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

View File

@ -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

View File

@ -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])

View File

@ -1 +0,0 @@
Each one of the folders in this directory is published at `@urbit/{folder name}`

View File

@ -1,12 +0,0 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": "> 1%",
"useBuiltIns": "usage",
"corejs": "3.19.1"
}
]
]
}

View File

@ -1,3 +0,0 @@
module.exports = {
extends: '@urbit'
};

View File

@ -1 +0,0 @@
tmp

View File

@ -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).

View File

@ -1,2 +0,0 @@
export * from './types';
export * from './lib';

View File

@ -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}`
};
};

View File

@ -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;
};

View File

@ -1,8 +0,0 @@
declare module 'urbit-ob' {
/**
* Convert a @p-encoded string to a decimal-encoded string.
*/
function patp2dec(name: string): string
}

View File

@ -1,2 +0,0 @@
export * from './lib';
export * from './types';

View File

@ -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
}
};
}

View File

@ -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;
}

View File

@ -1,2 +0,0 @@
export * from './lib';
export * from './types';

View File

@ -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}`
});

View File

@ -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;
}
};

View File

@ -1,2 +0,0 @@
export * from './types';
export * from './lib';

View File

@ -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;
}

View File

@ -1,2 +0,0 @@
export * from './update';
export * from './view';

View File

@ -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'>;

View File

@ -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;
}

View File

@ -1,2 +0,0 @@
export * from './types';
export * from './lib';

View File

@ -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];
}

View File

@ -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[];

View File

@ -1,2 +0,0 @@
export * from './lib';
export * from './types';

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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';

View File

@ -1,2 +0,0 @@
export * from './types';
export * from './lib';

View File

@ -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
}
});

View File

@ -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;
}

View File

@ -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];
}
}

View File

@ -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];
}
}

View File

@ -1,2 +0,0 @@
export * from './lib';
export * from './types';

View File

@ -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;
}

View File

@ -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;
}

View File

@ -1,2 +0,0 @@
export * from './types';
export * from './lib';

View File

@ -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 };

View File

@ -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';

File diff suppressed because it is too large Load Diff

View File

@ -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"
}
}

View File

@ -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
}
]
}
];

View File

@ -1,2 +0,0 @@
export * from './lib';
export * from './types';

View File

@ -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
});

View File

@ -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;

View File

@ -1,2 +0,0 @@
export * from './types';
export * from './lib';

View File

@ -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';

View File

@ -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;

View File

@ -1,2 +0,0 @@
export * from './types';
export * from './lib';

View File

@ -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`
});

View File

@ -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

View File

@ -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"]
}

View File

@ -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"
}
}
]
};

File diff suppressed because it is too large Load Diff

View File

@ -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"
}
}

View File

@ -1,13 +0,0 @@
{
"presets": [
"@babel/preset-typescript", //needed for .ts jest tests
[
"@babel/preset-env",
{
"targets": "> 1%",
"useBuiltIns": "usage",
"corejs": "3.19.1"
}
]
]
}

View File

@ -1,4 +0,0 @@
dist
node_modules
coverage
tmp

View File

@ -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`.

View File

@ -1,3 +0,0 @@
// import Urbit from '../../dist/browser';
// window.Urbit = Urbit;

View File

@ -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>&lt; ~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>

View File

@ -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();

View File

@ -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,
};

File diff suppressed because it is too large Load Diff

View File

@ -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"
}
}

View File

@ -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,
},
],
},
];

View File

@ -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;

View File

@ -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;

View File

@ -1,3 +0,0 @@
export * from './types';
import { Urbit } from './Urbit';
export { Urbit as default, Urbit };

View File

@ -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 {}

View File

@ -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);
}

View File

@ -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);
}
});
});

View File

@ -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