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