mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Integrate ActivityPub into the Backend
This commit is contained in:
parent
ec2b1050bc
commit
f74a45379f
3
.babelrc
3
.babelrc
@ -8,5 +8,8 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-throw-expressions"
|
||||
]
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ install:
|
||||
script:
|
||||
- docker-compose exec backend yarn run lint
|
||||
- docker-compose exec backend yarn run test --ci
|
||||
- docker-compose exec backend yarn run test:cucumber
|
||||
- docker-compose exec backend yarn run test:coverage
|
||||
- docker-compose exec backend yarn run db:reset
|
||||
- docker-compose exec backend yarn run db:seed
|
||||
|
||||
11
package.json
11
package.json
@ -12,13 +12,15 @@
|
||||
"dev": "nodemon --exec babel-node src/",
|
||||
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/index.js",
|
||||
"lint": "eslint src --config .eslintrc.js",
|
||||
"test": "nyc --reporter=text-lcov yarn run test:jest",
|
||||
"test": "nyc --reporter=text-lcov yarn test:jest",
|
||||
"test:cypress": "run-p --race test:before:*",
|
||||
"test:before:server": "cross-env GRAPHQL_URI=http://localhost:4123 GRAPHQL_PORT=4123 babel-node src/ 2> /dev/null",
|
||||
"test:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 PERMISSIONS=disabled babel-node src/ 2> /dev/null",
|
||||
"test:jest:cmd": "wait-on tcp:4001 tcp:4123 && jest --forceExit --detectOpenHandles --runInBand",
|
||||
"test:cucumber:cmd": "wait-on tcp:4001 tcp:4123 && cucumber-js --require-module @babel/register --exit test/",
|
||||
"test:jest:cmd:debug": "wait-on tcp:4001 tcp:4123 && node --inspect-brk ./node_modules/.bin/jest -i --forceExit --detectOpenHandles --runInBand",
|
||||
"test:jest": "run-p --race test:before:* 'test:jest:cmd {@}' --",
|
||||
"test:cucumber": "ACTIVITYPUB_PORT=4123 run-p --race test:before:* 'test:cucumber:cmd {@}' --",
|
||||
"test:jest:debug": "run-p --race test:before:* 'test:jest:cmd:debug {@}' --",
|
||||
"test:coverage": "nyc report --reporter=text-lcov > coverage.lcov",
|
||||
"db:script:seed": "wait-on tcp:4001 && babel-node src/seed/seed-db.js",
|
||||
@ -34,15 +36,18 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"activitystrea.ms": "^2.1.3",
|
||||
"apollo-cache-inmemory": "~1.4.3",
|
||||
"apollo-client": "~2.4.13",
|
||||
"apollo-link-http": "~1.5.11",
|
||||
"apollo-server": "~2.4.2",
|
||||
"bcryptjs": "~2.4.3",
|
||||
"cheerio": "~1.0.0-rc.2",
|
||||
"cors": "^2.8.5",
|
||||
"cross-env": "~5.2.0",
|
||||
"date-fns": "2.0.0-alpha.26",
|
||||
"dotenv": "~6.2.0",
|
||||
"express": "^4.16.4",
|
||||
"faker": "~4.1.0",
|
||||
"graphql": "~14.1.1",
|
||||
"graphql-custom-directives": "~0.2.14",
|
||||
@ -61,6 +66,7 @@
|
||||
"npm-run-all": "~4.1.5",
|
||||
"passport": "~0.4.0",
|
||||
"passport-jwt": "~4.0.0",
|
||||
"request": "^2.88.0",
|
||||
"sanitize-html": "~1.20.0",
|
||||
"slug": "~1.0.0",
|
||||
"trunc-html": "~1.1.2",
|
||||
@ -70,6 +76,7 @@
|
||||
"@babel/cli": "~7.2.3",
|
||||
"@babel/core": "~7.3.3",
|
||||
"@babel/node": "~7.2.2",
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.2.0",
|
||||
"@babel/preset-env": "~7.3.1",
|
||||
"@babel/register": "~7.0.0",
|
||||
"apollo-server-testing": "~2.4.2",
|
||||
@ -77,6 +84,8 @@
|
||||
"babel-eslint": "~10.0.1",
|
||||
"babel-jest": "~24.1.0",
|
||||
"chai": "~4.2.0",
|
||||
"cucumber": "^5.1.0",
|
||||
"debug": "^4.1.1",
|
||||
"eslint": "~5.13.0",
|
||||
"eslint-config-standard": "~12.0.0",
|
||||
"eslint-plugin-import": "~2.16.0",
|
||||
|
||||
137
src/activitypub/ActivityPub.js
Normal file
137
src/activitypub/ActivityPub.js
Normal file
@ -0,0 +1,137 @@
|
||||
import { sendAcceptActivity, sendRejectActivity, extractNameFromId, extractDomainFromUrl } from './utils'
|
||||
import request from 'request'
|
||||
import as from 'activitystrea.ms'
|
||||
import NitroDatasource from './NitroDatasource'
|
||||
import router from './routes'
|
||||
import dotenv from 'dotenv'
|
||||
import { resolve } from 'path'
|
||||
const debug = require('debug')('ea')
|
||||
|
||||
let activityPub = null
|
||||
|
||||
export { activityPub }
|
||||
|
||||
export default class ActivityPub {
|
||||
constructor (domain, port) {
|
||||
if (domain === 'localhost') { this.domain = `${domain}:${port}` } else { this.domain = domain }
|
||||
this.port = port
|
||||
this.dataSource = new NitroDatasource(this.domain)
|
||||
}
|
||||
static init (server) {
|
||||
if (!activityPub) {
|
||||
dotenv.config({ path: resolve('src', 'activitypub', '.env') })
|
||||
// const app = express()
|
||||
activityPub = new ActivityPub(process.env.ACTIVITYPUB_DOMAIN || 'localhost', process.env.ACTIVITYPUB_PORT || 4100)
|
||||
server.express.set('ap', activityPub)
|
||||
server.express.use(router)
|
||||
debug('ActivityPub service added to graphql endpoint')
|
||||
} else {
|
||||
debug('ActivityPub service already added to graphql endpoint')
|
||||
}
|
||||
}
|
||||
|
||||
getFollowersCollection (actorId) {
|
||||
return this.dataSource.getFollowersCollection(actorId)
|
||||
}
|
||||
|
||||
getFollowersCollectionPage (actorId) {
|
||||
return this.dataSource.getFollowersCollectionPage(actorId)
|
||||
}
|
||||
|
||||
getFollowingCollection (actorId) {
|
||||
return this.dataSource.getFollowingCollection(actorId)
|
||||
}
|
||||
|
||||
getFollowingCollectionPage (actorId) {
|
||||
return this.dataSource.getFollowingCollectionPage(actorId)
|
||||
}
|
||||
|
||||
getOutboxCollection (actorId) {
|
||||
return this.dataSource.getOutboxCollection(actorId)
|
||||
}
|
||||
|
||||
getOutboxCollectionPage (actorId) {
|
||||
return this.dataSource.getOutboxCollectionPage(actorId)
|
||||
}
|
||||
|
||||
handleFollowActivity (activity) {
|
||||
debug(`inside FOLLOW ${activity.actor}`)
|
||||
let toActorName = extractNameFromId(activity.object)
|
||||
let fromDomain = extractDomainFromUrl(activity.actor)
|
||||
const dataSource = this.dataSource
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request({
|
||||
url: activity.actor,
|
||||
headers: {
|
||||
'Accept': 'application/activity+json'
|
||||
}
|
||||
}, async (err, response, toActorObject) => {
|
||||
if (err) return reject(err)
|
||||
debug(`name = ${toActorName}@${this.domain}`)
|
||||
|
||||
let followersCollectionPage = await this.dataSource.getFollowersCollectionPage(activity.object)
|
||||
|
||||
const followActivity = as.follow()
|
||||
.id(activity.id)
|
||||
.actor(activity.actor)
|
||||
.object(activity.object)
|
||||
|
||||
// add follower if not already in collection
|
||||
if (followersCollectionPage.orderedItems.includes(activity.actor)) {
|
||||
debug('follower already in collection!')
|
||||
debug(`inbox = ${toActorObject.inbox}`)
|
||||
resolve(sendRejectActivity(followActivity, toActorName, fromDomain, toActorObject.inbox))
|
||||
} else {
|
||||
followersCollectionPage.orderedItems.push(activity.actor)
|
||||
}
|
||||
debug(`toActorObject = ${toActorObject}`)
|
||||
toActorObject = typeof toActorObject !== 'object' ? JSON.parse(toActorObject) : toActorObject
|
||||
debug(`followers = ${JSON.stringify(followersCollectionPage.orderedItems, null, 2)}`)
|
||||
debug(`inbox = ${toActorObject.inbox}`)
|
||||
debug(`outbox = ${toActorObject.outbox}`)
|
||||
debug(`followers = ${toActorObject.followers}`)
|
||||
debug(`following = ${toActorObject.following}`)
|
||||
|
||||
// TODO save after accept activity for the corresponding follow is received
|
||||
try {
|
||||
await dataSource.saveFollowersCollectionPage(followersCollectionPage)
|
||||
debug('follow activity saved')
|
||||
resolve(sendAcceptActivity(followActivity, toActorName, fromDomain, toActorObject.inbox))
|
||||
} catch (e) {
|
||||
debug('followers update error!', e)
|
||||
resolve(sendRejectActivity(followActivity, toActorName, fromDomain, toActorObject.inbox))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
handleUndoActivity (activity) {
|
||||
debug('inside UNDO')
|
||||
switch (activity.object.type) {
|
||||
case 'Follow':
|
||||
const followActivity = activity.object
|
||||
return this.dataSource.undoFollowActivity(followActivity.actor, followActivity.object)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
handleCreateActivity (activity) {
|
||||
debug('inside create')
|
||||
switch (activity.object.type) {
|
||||
case 'Article':
|
||||
case 'Note':
|
||||
const articleObject = activity.object
|
||||
if (articleObject.inReplyTo) {
|
||||
return this.dataSource.createComment(articleObject)
|
||||
} else {
|
||||
return this.dataSource.createPost(articleObject)
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
handleDeleteActivity (activity) {
|
||||
debug('inside delete')
|
||||
}
|
||||
}
|
||||
377
src/activitypub/NitroDatasource.js
Normal file
377
src/activitypub/NitroDatasource.js
Normal file
@ -0,0 +1,377 @@
|
||||
import {
|
||||
throwErrorIfGraphQLErrorOccurred,
|
||||
extractIdFromActivityId,
|
||||
createOrderedCollection,
|
||||
createOrderedCollectionPage,
|
||||
extractNameFromId,
|
||||
createArticleActivity,
|
||||
constructIdFromName
|
||||
} from './utils'
|
||||
import crypto from 'crypto'
|
||||
import gql from 'graphql-tag'
|
||||
import { createHttpLink } from 'apollo-link-http'
|
||||
import { InMemoryCache } from 'apollo-cache-inmemory'
|
||||
import fetch from 'node-fetch'
|
||||
import { ApolloClient } from 'apollo-client'
|
||||
import dotenv from 'dotenv'
|
||||
const debug = require('debug')('ea:nitro-datasource')
|
||||
|
||||
dotenv.config()
|
||||
|
||||
export default class NitroDatasource {
|
||||
constructor (domain) {
|
||||
this.domain = domain
|
||||
const defaultOptions = {
|
||||
query: {
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'all'
|
||||
}
|
||||
}
|
||||
const link = createHttpLink({ uri: process.env.GRAPHQL_URI, fetch: fetch }) // eslint-disable-line
|
||||
const cache = new InMemoryCache()
|
||||
this.client = new ApolloClient({
|
||||
link: link,
|
||||
cache: cache,
|
||||
defaultOptions
|
||||
})
|
||||
}
|
||||
|
||||
async getFollowersCollection (actorId) {
|
||||
const slug = extractNameFromId(actorId)
|
||||
debug(`slug= ${slug}`)
|
||||
const result = await this.client.query({
|
||||
query: gql`
|
||||
query {
|
||||
User(slug: "${slug}") {
|
||||
followedByCount
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
debug('successfully fetched followers')
|
||||
debug(result.data)
|
||||
if (result.data) {
|
||||
const actor = result.data.User[0]
|
||||
const followersCount = actor.followedByCount
|
||||
|
||||
const followersCollection = createOrderedCollection(slug, 'followers')
|
||||
followersCollection.totalItems = followersCount
|
||||
|
||||
return followersCollection
|
||||
} else {
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
}
|
||||
}
|
||||
|
||||
async getFollowersCollectionPage (actorId) {
|
||||
const slug = extractNameFromId(actorId)
|
||||
debug(`getFollowersPage slug = ${slug}`)
|
||||
const result = await this.client.query({
|
||||
query: gql`
|
||||
query {
|
||||
User(slug:"${slug}") {
|
||||
followedBy {
|
||||
slug
|
||||
}
|
||||
followedByCount
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
debug(result.data)
|
||||
if (result.data) {
|
||||
const actor = result.data.User[0]
|
||||
const followers = actor.followedBy
|
||||
const followersCount = actor.followedByCount
|
||||
|
||||
const followersCollection = createOrderedCollectionPage(slug, 'followers')
|
||||
followersCollection.totalItems = followersCount
|
||||
debug(`followers = ${JSON.stringify(followers, null, 2)}`)
|
||||
await Promise.all(
|
||||
followers.map(async (follower) => {
|
||||
followersCollection.orderedItems.push(constructIdFromName(follower.slug))
|
||||
})
|
||||
)
|
||||
|
||||
return followersCollection
|
||||
} else {
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
}
|
||||
}
|
||||
|
||||
async getFollowingCollection (actorId) {
|
||||
const slug = extractNameFromId(actorId)
|
||||
const result = await this.client.query({
|
||||
query: gql`
|
||||
query {
|
||||
User(slug:"${slug}") {
|
||||
followingCount
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
debug(result.data)
|
||||
if (result.data) {
|
||||
const actor = result.data.User[0]
|
||||
const followingCount = actor.followingCount
|
||||
|
||||
const followingCollection = createOrderedCollection(slug, 'following')
|
||||
followingCollection.totalItems = followingCount
|
||||
|
||||
return followingCollection
|
||||
} else {
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
}
|
||||
}
|
||||
|
||||
async getFollowingCollectionPage (actorId) {
|
||||
const slug = extractNameFromId(actorId)
|
||||
const result = await this.client.query({
|
||||
query: gql`
|
||||
query {
|
||||
User(slug:"${slug}") {
|
||||
following {
|
||||
slug
|
||||
}
|
||||
followingCount
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
debug(result.data)
|
||||
if (result.data) {
|
||||
const actor = result.data.User[0]
|
||||
const following = actor.following
|
||||
const followingCount = actor.followingCount
|
||||
|
||||
const followingCollection = createOrderedCollectionPage(slug, 'following')
|
||||
followingCollection.totalItems = followingCount
|
||||
|
||||
await Promise.all(
|
||||
following.map(async (user) => {
|
||||
followingCollection.orderedItems.push(await constructIdFromName(user.slug))
|
||||
})
|
||||
)
|
||||
|
||||
return followingCollection
|
||||
} else {
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
}
|
||||
}
|
||||
|
||||
async getOutboxCollection (actorId) {
|
||||
const slug = extractNameFromId(actorId)
|
||||
const result = await this.client.query({
|
||||
query: gql`
|
||||
query {
|
||||
User(slug:"${slug}") {
|
||||
contributions {
|
||||
title
|
||||
slug
|
||||
content
|
||||
contentExcerpt
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
debug(result.data)
|
||||
if (result.data) {
|
||||
const actor = result.data.User[0]
|
||||
const posts = actor.contributions
|
||||
|
||||
const outboxCollection = createOrderedCollection(slug, 'outbox')
|
||||
outboxCollection.totalItems = posts.length
|
||||
|
||||
return outboxCollection
|
||||
} else {
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
}
|
||||
}
|
||||
|
||||
async getOutboxCollectionPage (actorId) {
|
||||
const slug = extractNameFromId(actorId)
|
||||
const result = await this.client.query({
|
||||
query: gql`
|
||||
query {
|
||||
User(slug:"${slug}") {
|
||||
contributions {
|
||||
id
|
||||
title
|
||||
slug
|
||||
content
|
||||
contentExcerpt
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
debug(result.data)
|
||||
if (result.data) {
|
||||
const actor = result.data.User[0]
|
||||
const posts = actor.contributions
|
||||
|
||||
const outboxCollection = createOrderedCollectionPage(slug, 'outbox')
|
||||
outboxCollection.totalItems = posts.length
|
||||
await Promise.all(
|
||||
posts.map((post) => {
|
||||
outboxCollection.orderedItems.push(createArticleActivity(post.content, slug, post.id, post.createdAt))
|
||||
})
|
||||
)
|
||||
|
||||
debug('after createNote')
|
||||
return outboxCollection
|
||||
} else {
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
}
|
||||
}
|
||||
|
||||
async undoFollowActivity (fromActorId, toActorId) {
|
||||
const fromUserId = await this.ensureUser(fromActorId)
|
||||
const toUserId = await this.ensureUser(toActorId)
|
||||
const result = await this.client.mutate({
|
||||
mutation: gql`
|
||||
mutation {
|
||||
RemoveUserFollowedBy(from: {id: "${fromUserId}"}, to: {id: "${toUserId}"}) {
|
||||
from { name }
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
debug(`undoFollowActivity result = ${JSON.stringify(result, null, 2)}`)
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
}
|
||||
|
||||
async saveFollowersCollectionPage (followersCollection, onlyNewestItem = true) {
|
||||
debug('inside saveFollowers')
|
||||
let orderedItems = followersCollection.orderedItems
|
||||
const toUserName = extractNameFromId(followersCollection.id)
|
||||
const toUserId = await this.ensureUser(constructIdFromName(toUserName))
|
||||
orderedItems = onlyNewestItem ? [orderedItems.pop()] : orderedItems
|
||||
|
||||
return Promise.all(
|
||||
await Promise.all(orderedItems.map(async (follower) => {
|
||||
debug(`follower = ${follower}`)
|
||||
const fromUserId = await this.ensureUser(follower)
|
||||
debug(`fromUserId = ${fromUserId}`)
|
||||
debug(`toUserId = ${toUserId}`)
|
||||
const result = await this.client.mutate({
|
||||
mutation: gql`
|
||||
mutation {
|
||||
AddUserFollowedBy(from: {id: "${fromUserId}"}, to: {id: "${toUserId}"}) {
|
||||
from { name }
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
debug(`addUserFollowedBy edge = ${JSON.stringify(result, null, 2)}`)
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
debug('saveFollowers: added follow edge successfully')
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
async createPost (postObject) {
|
||||
// TODO how to handle the to field? Now the post is just created, doesn't matter who is the recipient
|
||||
// createPost
|
||||
const title = postObject.summary ? postObject.summary : postObject.content.split(' ').slice(0, 5).join(' ')
|
||||
const id = extractIdFromActivityId(postObject.id)
|
||||
let result = await this.client.mutate({
|
||||
mutation: gql`
|
||||
mutation {
|
||||
CreatePost(content: "${postObject.content}", title: "${title}", id: "${id}") {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
|
||||
// ensure user and add author to post
|
||||
const userId = await this.ensureUser(postObject.attributedTo)
|
||||
result = await this.client.mutate({
|
||||
mutation: gql`
|
||||
mutation {
|
||||
AddPostAuthor(from: {id: "${userId}"}, to: {id: "${id}"})
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
}
|
||||
|
||||
async createComment (postObject) {
|
||||
let result = await this.client.mutate({
|
||||
mutation: gql`
|
||||
mutation {
|
||||
CreateComment(content: "${postObject.content}") {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
const postId = extractIdFromActivityId(postObject.inReplyTo)
|
||||
|
||||
result = await this.client.mutate({
|
||||
mutation: gql`
|
||||
mutation {
|
||||
AddCommentPost(from: { id: "${result.data.CreateComment.id}", to: { id: "${postId}" }}) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will search for user existence and will create a disabled user with a random 16 bytes password when no user is found.
|
||||
*
|
||||
* @param actorId
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
async ensureUser (actorId) {
|
||||
debug(`inside ensureUser = ${actorId}`)
|
||||
const queryResult = await this.client.query({
|
||||
query: gql`
|
||||
query {
|
||||
User(slug: "${extractNameFromId(actorId)}") {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
if (queryResult.data && Array.isArray(queryResult.data.User) && queryResult.data.User.length > 0) {
|
||||
debug('ensureUser: user exists.. return id')
|
||||
// user already exists.. return the id
|
||||
return queryResult.data.User[0].id
|
||||
} else {
|
||||
debug('ensureUser: user not exists.. createUser')
|
||||
// user does not exist.. create it
|
||||
const result = await this.client.mutate({
|
||||
mutation: gql`
|
||||
mutation {
|
||||
CreateUser(password: "${crypto.randomBytes(16).toString('hex')}", slug:"${extractNameFromId(actorId)}", actorId: "${actorId}", name: "${extractNameFromId(actorId)}") {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
throwErrorIfGraphQLErrorOccurred(result)
|
||||
|
||||
return result.data.CreateUser.id
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/activitypub/routes/inbox.js
Normal file
50
src/activitypub/routes/inbox.js
Normal file
@ -0,0 +1,50 @@
|
||||
import express from 'express'
|
||||
import { verify } from '../security'
|
||||
const debug = require('debug')('ea:inbox')
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
// Shared Inbox endpoint (federated Server)
|
||||
// For now its only able to handle Note Activities!!
|
||||
router.post('/', async function (req, res, next) {
|
||||
debug(`Content-Type = ${req.get('Content-Type')}`)
|
||||
debug(`body = ${JSON.stringify(req.body, null, 2)}`)
|
||||
debug(`Request headers = ${JSON.stringify(req.headers, null, 2)}`)
|
||||
debug(`verify = ${await verify(`${req.protocol}://${req.hostname}:${req.port}${req.originalUrl}`, req.headers)}`)
|
||||
switch (req.body.type) {
|
||||
case 'Create':
|
||||
await await req.app.get('activityPub').handleCreateActivity(req.body).catch(next)
|
||||
break
|
||||
case 'Undo':
|
||||
await await req.app.get('activityPub').handleUndoActivity(req.body).catch(next)
|
||||
break
|
||||
case 'Follow':
|
||||
debug('handleFollow')
|
||||
await req.app.get('activityPub').handleFollowActivity(req.body)
|
||||
debug('handledFollow')
|
||||
break
|
||||
case 'Delete':
|
||||
await await req.app.get('activityPub').handleDeleteActivity(req.body).catch(next)
|
||||
break
|
||||
/* eslint-disable */
|
||||
case 'Update':
|
||||
|
||||
case 'Accept':
|
||||
|
||||
case 'Reject':
|
||||
|
||||
case 'Add':
|
||||
|
||||
case 'Remove':
|
||||
|
||||
case 'Like':
|
||||
|
||||
case 'Announce':
|
||||
debug('else!!')
|
||||
debug(JSON.stringify(req.body, null, 2))
|
||||
}
|
||||
/* eslint-enable */
|
||||
res.status(200).end()
|
||||
})
|
||||
|
||||
export default router
|
||||
27
src/activitypub/routes/index.js
Normal file
27
src/activitypub/routes/index.js
Normal file
@ -0,0 +1,27 @@
|
||||
import user from './user'
|
||||
import inbox from './inbox'
|
||||
import webfinger from './webfinger'
|
||||
import express from 'express'
|
||||
import cors from 'cors'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.use('/.well-known/webfinger',
|
||||
cors(),
|
||||
express.urlencoded({ extended: true }),
|
||||
webfinger
|
||||
)
|
||||
router.use('/activitypub/users',
|
||||
cors(),
|
||||
express.json({ type: ['application/activity+json', 'application/ld+json', 'application/json'] }),
|
||||
express.urlencoded({ extended: true }),
|
||||
user
|
||||
)
|
||||
router.use('/activitypub/inbox',
|
||||
cors(),
|
||||
express.json({ type: ['application/activity+json', 'application/ld+json', 'application/json'] }),
|
||||
express.urlencoded({ extended: true }),
|
||||
inbox
|
||||
)
|
||||
|
||||
export default router
|
||||
88
src/activitypub/routes/user.js
Normal file
88
src/activitypub/routes/user.js
Normal file
@ -0,0 +1,88 @@
|
||||
import { sendCollection } from '../utils'
|
||||
import express from 'express'
|
||||
import { serveUser } from '../utils/serveUser'
|
||||
import { verify } from '../security'
|
||||
|
||||
const router = express.Router()
|
||||
const debug = require('debug')('ea:user')
|
||||
|
||||
router.get('/:name', async function (req, res, next) {
|
||||
debug('inside user.js -> serveUser')
|
||||
await serveUser(req, res, next)
|
||||
})
|
||||
|
||||
router.get('/:name/following', (req, res) => {
|
||||
debug('inside user.js -> serveFollowingCollection')
|
||||
const name = req.params.name
|
||||
if (!name) {
|
||||
res.status(400).send('Bad request! Please specify a name.')
|
||||
} else {
|
||||
const collectionName = req.query.page ? 'followingPage' : 'following'
|
||||
sendCollection(collectionName, req, res)
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/:name/followers', (req, res) => {
|
||||
debug('inside user.js -> serveFollowersCollection')
|
||||
const name = req.params.name
|
||||
if (!name) {
|
||||
return res.status(400).send('Bad request! Please specify a name.')
|
||||
} else {
|
||||
const collectionName = req.query.page ? 'followersPage' : 'followers'
|
||||
sendCollection(collectionName, req, res)
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/:name/outbox', (req, res) => {
|
||||
debug('inside user.js -> serveOutboxCollection')
|
||||
const name = req.params.name
|
||||
if (!name) {
|
||||
return res.status(400).send('Bad request! Please specify a name.')
|
||||
} else {
|
||||
const collectionName = req.query.page ? 'outboxPage' : 'outbox'
|
||||
sendCollection(collectionName, req, res)
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/:name/inbox', async function (req, res, next) {
|
||||
debug(`body = ${JSON.stringify(req.body, null, 2)}`)
|
||||
debug(`actorId = ${req.body.actor}`)
|
||||
debug(`verify = ${await verify(`${req.protocol}://${req.hostname}:${req.port}${req.originalUrl}`, req.headers)}`)
|
||||
// const result = await saveActorId(req.body.actor)
|
||||
switch (req.body.type) {
|
||||
case 'Create':
|
||||
await req.app.get('ap').handleCreateActivity(req.body).catch(next)
|
||||
break
|
||||
case 'Undo':
|
||||
await req.app.get('ap').handleUndoActivity(req.body).catch(next)
|
||||
break
|
||||
case 'Follow':
|
||||
debug('handleFollow')
|
||||
await req.app.get('ap').handleFollowActivity(req.body).catch(next)
|
||||
debug('handledFollow')
|
||||
break
|
||||
case 'Delete':
|
||||
req.app.get('ap').handleDeleteActivity(req.body).catch(next)
|
||||
break
|
||||
/* eslint-disable */
|
||||
case 'Update':
|
||||
|
||||
case 'Accept':
|
||||
|
||||
case 'Reject':
|
||||
|
||||
case 'Add':
|
||||
|
||||
case 'Remove':
|
||||
|
||||
case 'Like':
|
||||
|
||||
case 'Announce':
|
||||
debug('else!!')
|
||||
debug(JSON.stringify(req.body, null, 2))
|
||||
}
|
||||
/* eslint-enable */
|
||||
res.status(200).end()
|
||||
})
|
||||
|
||||
export default router
|
||||
34
src/activitypub/routes/webfinger.js
Normal file
34
src/activitypub/routes/webfinger.js
Normal file
@ -0,0 +1,34 @@
|
||||
import express from 'express'
|
||||
import { createWebFinger } from '../utils'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.get('/', async function (req, res) {
|
||||
const resource = req.query.resource
|
||||
if (!resource || !resource.includes('acct:')) {
|
||||
return res.status(400).send('Bad request. Please make sure "acct:USER@DOMAIN" is what you are sending as the "resource" query parameter.')
|
||||
} else {
|
||||
const nameAndDomain = resource.replace('acct:', '')
|
||||
const name = nameAndDomain.split('@')[0]
|
||||
|
||||
const result = await req.app.get('ap').dataSource.client.query({
|
||||
query: gql`
|
||||
query {
|
||||
User(slug: "${name}") {
|
||||
slug
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
if (result.data && result.data.User.length > 0) {
|
||||
const webFinger = createWebFinger(name)
|
||||
return res.contentType('application/jrd+json').json(webFinger)
|
||||
} else {
|
||||
return res.status(404).json({ error: `No record found for ${nameAndDomain}.` })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
227
src/activitypub/security/index.js
Normal file
227
src/activitypub/security/index.js
Normal file
@ -0,0 +1,227 @@
|
||||
import dotenv from 'dotenv'
|
||||
import { resolve } from 'path'
|
||||
import crypto from 'crypto'
|
||||
import { activityPub } from '../ActivityPub'
|
||||
import gql from 'graphql-tag'
|
||||
import request from 'request'
|
||||
const debug = require('debug')('ea:security')
|
||||
|
||||
dotenv.config({ path: resolve('src', 'activitypub', '.env') })
|
||||
|
||||
export function generateRsaKeyPair () {
|
||||
return crypto.generateKeyPairSync('rsa', {
|
||||
modulusLength: 4096,
|
||||
publicKeyEncoding: {
|
||||
type: 'spki',
|
||||
format: 'pem'
|
||||
},
|
||||
privateKeyEncoding: {
|
||||
type: 'pkcs8',
|
||||
format: 'pem',
|
||||
cipher: 'aes-256-cbc',
|
||||
passphrase: process.env.PRIVATE_KEY_PASSPHRASE
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function signAndSend (activity, fromName, targetDomain, url) {
|
||||
// fix for development: replace with http
|
||||
url = url.indexOf('localhost') > -1 ? url.replace('https', 'http') : url
|
||||
debug(`passhprase = ${process.env.PRIVATE_KEY_PASSPHRASE}`)
|
||||
return new Promise(async (resolve, reject) => {
|
||||
debug('inside signAndSend')
|
||||
// get the private key
|
||||
const result = await activityPub.dataSource.client.query({
|
||||
query: gql`
|
||||
query {
|
||||
User(slug: "${fromName}") {
|
||||
privateKey
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
if (result.error) {
|
||||
reject(result.error)
|
||||
} else {
|
||||
const parsedActivity = JSON.parse(activity)
|
||||
if (Array.isArray(parsedActivity['@context'])) {
|
||||
parsedActivity['@context'].push('https://w3id.org/security/v1')
|
||||
} else {
|
||||
const context = [parsedActivity['@context']]
|
||||
context.push('https://w3id.org/security/v1')
|
||||
parsedActivity['@context'] = context
|
||||
}
|
||||
|
||||
// deduplicate context strings
|
||||
parsedActivity['@context'] = [...new Set(parsedActivity['@context'])]
|
||||
const privateKey = result.data.User[0].privateKey
|
||||
const date = new Date().toUTCString()
|
||||
|
||||
debug(`url = ${url}`)
|
||||
request({
|
||||
url: url,
|
||||
headers: {
|
||||
'Host': targetDomain,
|
||||
'Date': date,
|
||||
'Signature': createSignature(privateKey, `http://${activityPub.domain}/activitypub/users/${fromName}#main-key`, url,
|
||||
{
|
||||
'Host': targetDomain,
|
||||
'Date': date,
|
||||
'Content-Type': 'application/activity+json'
|
||||
}),
|
||||
'Content-Type': 'application/activity+json'
|
||||
},
|
||||
method: 'POST',
|
||||
body: JSON.stringify(parsedActivity)
|
||||
}, (error, response) => {
|
||||
if (error) {
|
||||
debug(`Error = ${JSON.stringify(error, null, 2)}`)
|
||||
reject(error)
|
||||
} else {
|
||||
debug('Response Headers:', JSON.stringify(response.headers, null, 2))
|
||||
debug('Response Body:', JSON.stringify(response.body, null, 2))
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function verify (url, headers) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const signatureHeader = headers['signature'] ? headers['signature'] : headers['Signature']
|
||||
if (!signatureHeader) {
|
||||
debug('No Signature header present!')
|
||||
resolve(false)
|
||||
}
|
||||
debug(`Signature Header = ${signatureHeader}`)
|
||||
const signature = extractKeyValueFromSignatureHeader(signatureHeader, 'signature')
|
||||
const algorithm = extractKeyValueFromSignatureHeader(signatureHeader, 'algorithm')
|
||||
const headersString = extractKeyValueFromSignatureHeader(signatureHeader, 'headers')
|
||||
const keyId = extractKeyValueFromSignatureHeader(signatureHeader, 'keyId')
|
||||
|
||||
if (!SUPPORTED_HASH_ALGORITHMS.includes(algorithm)) {
|
||||
debug('Unsupported hash algorithm specified!')
|
||||
resolve(false)
|
||||
}
|
||||
|
||||
const usedHeaders = headersString.split(' ')
|
||||
const verifyHeaders = {}
|
||||
Object.keys(headers).forEach((key) => {
|
||||
if (usedHeaders.includes(key.toLowerCase())) {
|
||||
verifyHeaders[key.toLowerCase()] = headers[key]
|
||||
}
|
||||
})
|
||||
const signingString = constructSigningString(url, verifyHeaders)
|
||||
debug(`keyId= ${keyId}`)
|
||||
request({
|
||||
url: keyId,
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
}, (err, response, body) => {
|
||||
if (err) reject(err)
|
||||
debug(`body = ${body}`)
|
||||
const actor = JSON.parse(body)
|
||||
const publicKeyPem = actor.publicKey.publicKeyPem
|
||||
resolve(httpVerify(publicKeyPem, signature, signingString, algorithm))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// specify the public key owner object
|
||||
/* const testPublicKeyOwner = {
|
||||
"@context": jsig.SECURITY_CONTEXT_URL,
|
||||
'@id': 'https://example.com/i/alice',
|
||||
publicKey: [testPublicKey]
|
||||
} */
|
||||
|
||||
/* const publicKey = {
|
||||
'@context': jsig.SECURITY_CONTEXT_URL,
|
||||
type: 'RsaVerificationKey2018',
|
||||
id: `https://${ActivityPub.domain}/users/${fromName}#main-key`,
|
||||
controller: `https://${ActivityPub.domain}/users/${fromName}`,
|
||||
publicKeyPem
|
||||
} */
|
||||
|
||||
// Obtained from invoking crypto.getHashes()
|
||||
export const SUPPORTED_HASH_ALGORITHMS = [
|
||||
'rsa-md4',
|
||||
'rsa-md5',
|
||||
'rsa-mdC2',
|
||||
'rsa-ripemd160',
|
||||
'rsa-sha1',
|
||||
'rsa-sha1-2',
|
||||
'rsa-sha224',
|
||||
'rsa-sha256',
|
||||
'rsa-sha384',
|
||||
'rsa-sha512',
|
||||
'blake2b512',
|
||||
'blake2s256',
|
||||
'md4',
|
||||
'md4WithRSAEncryption',
|
||||
'md5',
|
||||
'md5-sha1',
|
||||
'md5WithRSAEncryption',
|
||||
'mdc2',
|
||||
'mdc2WithRSA',
|
||||
'ripemd',
|
||||
'ripemd160',
|
||||
'ripemd160WithRSA',
|
||||
'rmd160',
|
||||
'sha1',
|
||||
'sha1WithRSAEncryption',
|
||||
'sha224',
|
||||
'sha224WithRSAEncryption',
|
||||
'sha256',
|
||||
'sha256WithRSAEncryption',
|
||||
'sha384',
|
||||
'sha384WithRSAEncryption',
|
||||
'sha512',
|
||||
'sha512WithRSAEncryption',
|
||||
'ssl3-md5',
|
||||
'ssl3-sha1',
|
||||
'whirlpool']
|
||||
|
||||
// signing
|
||||
function createSignature (privKey, keyId, url, headers = {}, algorithm = 'rsa-sha256') {
|
||||
if (!SUPPORTED_HASH_ALGORITHMS.includes(algorithm)) { return throw Error(`SIGNING: Unsupported hashing algorithm = ${algorithm}`) }
|
||||
const signer = crypto.createSign(algorithm)
|
||||
const signingString = constructSigningString(url, headers)
|
||||
signer.update(signingString)
|
||||
const signatureB64 = signer.sign({ key: privKey, passphrase: process.env.PRIVATE_KEY_PASSPHRASE }, 'base64')
|
||||
const headersString = Object.keys(headers).reduce((result, key) => { return result + ' ' + key.toLowerCase() }, '')
|
||||
return `keyId="${keyId}",algorithm="${algorithm}",headers="(request-target)${headersString}",signature="${signatureB64}"`
|
||||
}
|
||||
|
||||
// signing
|
||||
function constructSigningString (url, headers) {
|
||||
const urlObj = new URL(url)
|
||||
let signingString = `(request-target): post ${urlObj.pathname}${urlObj.search !== '' ? urlObj.search : ''}`
|
||||
return Object.keys(headers).reduce((result, key) => {
|
||||
return result + `\n${key.toLowerCase()}: ${headers[key]}`
|
||||
}, signingString)
|
||||
}
|
||||
|
||||
// verifying
|
||||
function httpVerify (pubKey, signature, signingString, algorithm) {
|
||||
if (!SUPPORTED_HASH_ALGORITHMS.includes(algorithm)) { throw Error(`SIGNING: Unsupported hashing algorithm = ${algorithm}`) }
|
||||
const verifier = crypto.createVerify(algorithm)
|
||||
verifier.update(signingString)
|
||||
return verifier.verify(pubKey, signature, 'base64')
|
||||
}
|
||||
|
||||
// verifying
|
||||
// This function can be used to extract the signature,headers,algorithm etc. out of the Signature Header.
|
||||
// Just pass what you want as key
|
||||
function extractKeyValueFromSignatureHeader (signatureHeader, key) {
|
||||
const keyString = signatureHeader.split(',').filter((el) => {
|
||||
return !!el.startsWith(key)
|
||||
})[0]
|
||||
|
||||
let firstEqualIndex = keyString.search('=')
|
||||
// When headers are requested add 17 to the index to remove "(request-target) " from the string
|
||||
if (key === 'headers') { firstEqualIndex += 17 }
|
||||
return keyString.substring(firstEqualIndex + 2, keyString.length - 1)
|
||||
}
|
||||
215
src/activitypub/utils/index.js
Normal file
215
src/activitypub/utils/index.js
Normal file
@ -0,0 +1,215 @@
|
||||
import crypto from 'crypto'
|
||||
import { signAndSend } from '../security'
|
||||
import as from 'activitystrea.ms'
|
||||
import { activityPub } from '../ActivityPub'
|
||||
const debug = require('debug')('ea:utils')
|
||||
|
||||
export function extractNameFromId (uri) {
|
||||
const urlObject = new URL(uri)
|
||||
const pathname = urlObject.pathname
|
||||
const splitted = pathname.split('/')
|
||||
|
||||
return splitted[splitted.indexOf('users') + 1]
|
||||
}
|
||||
|
||||
export function extractIdFromActivityId (uri) {
|
||||
const urlObject = new URL(uri)
|
||||
const pathname = urlObject.pathname
|
||||
const splitted = pathname.split('/')
|
||||
|
||||
return splitted[splitted.indexOf('status') + 1]
|
||||
}
|
||||
|
||||
export function constructIdFromName (name, fromDomain = activityPub.domain) {
|
||||
return `https://${fromDomain}/activitypub/users/${name}`
|
||||
}
|
||||
|
||||
export function extractDomainFromUrl (url) {
|
||||
return new URL(url).hostname
|
||||
}
|
||||
|
||||
export async function getActorIdByName (name) {
|
||||
debug(`name = ${name}`)
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
export function sendCollection (collectionName, req, res) {
|
||||
const name = req.params.name
|
||||
const id = constructIdFromName(name)
|
||||
|
||||
switch (collectionName) {
|
||||
case 'followers':
|
||||
attachThenCatch(activityPub.getFollowersCollection(id), res)
|
||||
break
|
||||
|
||||
case 'followersPage':
|
||||
attachThenCatch(activityPub.getFollowersCollectionPage(id), res)
|
||||
break
|
||||
|
||||
case 'following':
|
||||
attachThenCatch(activityPub.getFollowingCollection(id), res)
|
||||
break
|
||||
|
||||
case 'followingPage':
|
||||
attachThenCatch(activityPub.getFollowingCollectionPage(id), res)
|
||||
break
|
||||
|
||||
case 'outbox':
|
||||
attachThenCatch(activityPub.getOutboxCollection(id), res)
|
||||
break
|
||||
|
||||
case 'outboxPage':
|
||||
attachThenCatch(activityPub.getOutboxCollectionPage(id), res)
|
||||
break
|
||||
|
||||
default:
|
||||
res.status(500).end()
|
||||
}
|
||||
}
|
||||
|
||||
function attachThenCatch (promise, res) {
|
||||
return promise
|
||||
.then((collection) => {
|
||||
res.status(200).contentType('application/activity+json').send(collection)
|
||||
})
|
||||
.catch((err) => {
|
||||
debug(`error getting a Collection: = ${err}`)
|
||||
res.status(500).end()
|
||||
})
|
||||
}
|
||||
|
||||
export function createActor (name, pubkey) {
|
||||
return {
|
||||
'@context': [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
'https://w3id.org/security/v1'
|
||||
],
|
||||
'id': `https://${activityPub.domain}/activitypub/users/${name}`,
|
||||
'type': 'Person',
|
||||
'preferredUsername': `${name}`,
|
||||
'name': `${name}`,
|
||||
'following': `https://${activityPub.domain}/activitypub/users/${name}/following`,
|
||||
'followers': `https://${activityPub.domain}/activitypub/users/${name}/followers`,
|
||||
'inbox': `https://${activityPub.domain}/activitypub/users/${name}/inbox`,
|
||||
'outbox': `https://${activityPub.domain}/activitypub/users/${name}/outbox`,
|
||||
'url': `https://${activityPub.domain}/activitypub/@${name}`,
|
||||
'endpoints': {
|
||||
'sharedInbox': `https://${activityPub.domain}/activitypub/inbox`
|
||||
},
|
||||
'publicKey': {
|
||||
'id': `https://${activityPub.domain}/activitypub/users/${name}#main-key`,
|
||||
'owner': `https://${activityPub.domain}/activitypub/users/${name}`,
|
||||
'publicKeyPem': pubkey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createWebFinger (name) {
|
||||
return {
|
||||
'subject': `acct:${name}@${activityPub.domain}`,
|
||||
'links': [
|
||||
{
|
||||
'rel': 'self',
|
||||
'type': 'application/activity+json',
|
||||
'href': `https://${activityPub.domain}/users/${name}`
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export function createOrderedCollection (name, collectionName) {
|
||||
return {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'id': `https://${activityPub.domain}/activitypub/users/${name}/${collectionName}`,
|
||||
'summary': `${name}s ${collectionName} collection`,
|
||||
'type': 'OrderedCollection',
|
||||
'first': `https://${activityPub.domain}/activitypub/users/${name}/${collectionName}?page=true`,
|
||||
'totalItems': 0
|
||||
}
|
||||
}
|
||||
|
||||
export function createOrderedCollectionPage (name, collectionName) {
|
||||
return {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'id': `https://${activityPub.domain}/activitypub/users/${name}/${collectionName}?page=true`,
|
||||
'summary': `${name}s ${collectionName} collection`,
|
||||
'type': 'OrderedCollectionPage',
|
||||
'totalItems': 0,
|
||||
'partOf': `https://${activityPub.domain}/activitypub/users/${name}/${collectionName}`,
|
||||
'orderedItems': []
|
||||
}
|
||||
}
|
||||
|
||||
export function createNoteActivity (text, name, id, published) {
|
||||
const createUuid = crypto.randomBytes(16).toString('hex')
|
||||
|
||||
return {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'id': `https://${activityPub.domain}/activitypub/users/${name}/status/${createUuid}`,
|
||||
'type': 'Create',
|
||||
'actor': `https://${activityPub.domain}/activitypub/users/${name}`,
|
||||
'object': {
|
||||
'id': `https://${activityPub.domain}/activitypub/users/${name}/status/${id}`,
|
||||
'type': 'Note',
|
||||
'published': published,
|
||||
'attributedTo': `https://${activityPub.domain}/activitypub/users/${name}`,
|
||||
'content': text,
|
||||
'to': 'https://www.w3.org/ns/activitystreams#Public'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createArticleActivity (text, name, id, published) {
|
||||
const createUuid = crypto.randomBytes(16).toString('hex')
|
||||
|
||||
return {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'id': `https://${activityPub.domain}/activitypub/users/${name}/status/${createUuid}`,
|
||||
'type': 'Create',
|
||||
'actor': `https://${activityPub.domain}/activitypub/users/${name}`,
|
||||
'object': {
|
||||
'id': `https://${activityPub.domain}/activitypub/users/${name}/status/${id}`,
|
||||
'type': 'Article',
|
||||
'published': published,
|
||||
'attributedTo': `https://${activityPub.domain}/activitypub/users/${name}`,
|
||||
'content': text,
|
||||
'to': 'https://www.w3.org/ns/activitystreams#Public'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function sendAcceptActivity (theBody, name, targetDomain, url) {
|
||||
as.accept()
|
||||
.id(`https://${activityPub.domain}/activitypub/users/${name}/status/` + crypto.randomBytes(16).toString('hex'))
|
||||
.actor(`https://${activityPub.domain}/activitypub/users/${name}`)
|
||||
.object(theBody)
|
||||
.prettyWrite((err, doc) => {
|
||||
if (!err) {
|
||||
return signAndSend(doc, name, targetDomain, url)
|
||||
} else {
|
||||
debug(`error serializing Accept object: ${err}`)
|
||||
throw new Error('error serializing Accept object')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function sendRejectActivity (theBody, name, targetDomain, url) {
|
||||
as.reject()
|
||||
.id(`https://${activityPub.domain}/activitypub/users/${name}/status/` + crypto.randomBytes(16).toString('hex'))
|
||||
.actor(`https://${activityPub.domain}/activitypub/users/${name}`)
|
||||
.object(theBody)
|
||||
.prettyWrite((err, doc) => {
|
||||
if (!err) {
|
||||
return signAndSend(doc, name, targetDomain, url)
|
||||
} else {
|
||||
debug(`error serializing Accept object: ${err}`)
|
||||
throw new Error('error serializing Accept object')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function throwErrorIfGraphQLErrorOccurred (result) {
|
||||
if (result.error && (result.error.message || result.error.errors)) {
|
||||
throw new Error(`${result.error.message ? result.error.message : result.error.errors[0].message}`)
|
||||
}
|
||||
}
|
||||
43
src/activitypub/utils/serveUser.js
Normal file
43
src/activitypub/utils/serveUser.js
Normal file
@ -0,0 +1,43 @@
|
||||
import { createActor } from '../utils'
|
||||
const gql = require('graphql-tag')
|
||||
const debug = require('debug')('ea:serveUser')
|
||||
|
||||
export async function serveUser (req, res, next) {
|
||||
let name = req.params.name
|
||||
|
||||
if (name.startsWith('@')) {
|
||||
name = name.slice(1)
|
||||
}
|
||||
|
||||
debug(`name = ${name}`)
|
||||
const result = await req.app.get('ap').dataSource.client.query({
|
||||
query: gql`
|
||||
query {
|
||||
User(slug: "${name}") {
|
||||
publicKey
|
||||
}
|
||||
}
|
||||
`
|
||||
}).catch(reason => { debug(`serveUser User fetch error: ${reason}`) })
|
||||
|
||||
if (result.data && Array.isArray(result.data.User) && result.data.User.length > 0) {
|
||||
const publicKey = result.data.User[0].publicKey
|
||||
const actor = createActor(name, publicKey)
|
||||
debug(`actor = ${JSON.stringify(actor, null, 2)}`)
|
||||
debug(`accepts json = ${req.accepts(['application/activity+json', 'application/ld+json', 'application/json'])}`)
|
||||
if (req.accepts(['application/activity+json', 'application/ld+json', 'application/json'])) {
|
||||
return res.json(actor)
|
||||
} else if (req.accepts('text/html')) {
|
||||
// TODO show user's profile page instead of the actor object
|
||||
/* const outbox = JSON.parse(result.outbox)
|
||||
const posts = outbox.orderedItems.filter((el) => { return el.object.type === 'Note'})
|
||||
const actor = result.actor
|
||||
debug(posts) */
|
||||
// res.render('user', { user: actor, posts: JSON.stringify(posts)})
|
||||
return res.json(actor)
|
||||
}
|
||||
} else {
|
||||
debug(`error getting publicKey for actor ${name}`)
|
||||
next()
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import createServer from './server'
|
||||
import ActiviyPub from './activitypub/ActivityPub'
|
||||
|
||||
const serverConfig = {
|
||||
port: process.env.GRAPHQL_PORT || 4000
|
||||
@ -12,4 +13,5 @@ const server = createServer()
|
||||
server.start(serverConfig, options => {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(`Server ready at ${process.env.GRAPHQL_URI} 🚀`)
|
||||
ActiviyPub.init(server)
|
||||
})
|
||||
|
||||
@ -1,8 +1,16 @@
|
||||
import createOrUpdateLocations from './nodes/locations'
|
||||
import { generateRsaKeyPair } from '../activitypub/security'
|
||||
import dotenv from 'dotenv'
|
||||
import { resolve } from 'path'
|
||||
|
||||
dotenv.config({ path: resolve('src', 'activitypub', '.env') })
|
||||
|
||||
export default {
|
||||
Mutation: {
|
||||
CreateUser: async (resolve, root, args, context, info) => {
|
||||
const keys = generateRsaKeyPair()
|
||||
Object.assign(args, keys)
|
||||
args.actorId = `${process.env.ACTIVITYPUB_URI}/activitypub/users/${args.slug}`
|
||||
const result = await resolve(root, args, context, info)
|
||||
await createOrUpdateLocations(args.id, args.locationName, context.driver)
|
||||
return result
|
||||
|
||||
@ -77,6 +77,7 @@ type Location {
|
||||
|
||||
type User {
|
||||
id: ID!
|
||||
actorId: String
|
||||
name: String
|
||||
email: String
|
||||
slug: String
|
||||
@ -85,6 +86,8 @@ type User {
|
||||
deleted: Boolean
|
||||
disabled: Boolean
|
||||
role: UserGroupEnum
|
||||
publicKey: String
|
||||
privateKey: String
|
||||
|
||||
location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
|
||||
locationName: String
|
||||
|
||||
30
test/features/activity-article.feature.disable
Normal file
30
test/features/activity-article.feature.disable
Normal file
@ -0,0 +1,30 @@
|
||||
Feature: Send and receive Articles
|
||||
I want to send and receive article's via ActivityPub
|
||||
|
||||
Background:
|
||||
Given our own server runs at "http://localhost:4100"
|
||||
And we have the following users in our database:
|
||||
| Slug |
|
||||
| marvin |
|
||||
| max |
|
||||
|
||||
Scenario: Send an article to a user inbox and make sure it's added to the inbox
|
||||
When I send a POST request with the following activity to "/activitypub/users/max/inbox":
|
||||
"""
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://aronda.org/users/marvin/status/lka7dfzkjn2398hsfd",
|
||||
"type": "Create",
|
||||
"actor": "https://aronda.org/users/marvin",
|
||||
"object": {
|
||||
"id": "https://aronda.org/users/marvin/status/kljsdfg9843jknsdf",
|
||||
"type": "Article",
|
||||
"published": "2019-02-07T19:37:55.002Z",
|
||||
"attributedTo": "https://aronda.org/users/marvin",
|
||||
"content": "Hi John, how are you?",
|
||||
"to": "https://localhost:4100/activitypub/users/max"
|
||||
}
|
||||
}
|
||||
"""
|
||||
Then I expect the status code to be 200
|
||||
And the post with id "kljsdfg9843jknsdf" to be created
|
||||
28
test/features/activity-delete.feature.disable
Normal file
28
test/features/activity-delete.feature.disable
Normal file
@ -0,0 +1,28 @@
|
||||
Feature: Delete an object
|
||||
I want to delete objects
|
||||
|
||||
Background:
|
||||
Given our own server runs at "http://localhost:4100"
|
||||
And we have the following users in our database:
|
||||
| Slug |
|
||||
| bernd-das-brot|
|
||||
|
||||
Scenario: Deleting a post (Article Object)
|
||||
When I send a POST request with the following activity to "/activitypub/users/bernd-das-brot/inbox":
|
||||
"""
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://localhost:4100/users/karl-heinz/status/a4DJ2afdg323v32641vna42lkj685kasd2",
|
||||
"type": "Delete",
|
||||
"object": {
|
||||
"id": "https://aronda.org/users/marvin/status/kljsdfg9843jknsdf",
|
||||
"type": "Article",
|
||||
"published": "2019-02-07T19:37:55.002Z",
|
||||
"attributedTo": "https://aronda.org/users/marvin",
|
||||
"content": "Hi John, how are you?",
|
||||
"to": "https://localhost:4100/activitypub/users/max"
|
||||
}
|
||||
}
|
||||
"""
|
||||
Then I expect the status code to be 200
|
||||
And the object is removed from the outbox collection of "karl-heinz"
|
||||
51
test/features/activity-follow.feature
Normal file
51
test/features/activity-follow.feature
Normal file
@ -0,0 +1,51 @@
|
||||
Feature: Follow a user
|
||||
I want to be able to follow a user on another instance.
|
||||
Also if I do not want to follow a previous followed user anymore,
|
||||
I want to undo the follow.
|
||||
|
||||
Background:
|
||||
Given our own server runs at "http://localhost:4123"
|
||||
And we have the following users in our database:
|
||||
| Slug |
|
||||
| karl-heinz |
|
||||
| peter-lustiger |
|
||||
|
||||
Scenario: Send a follow to a user inbox and make sure it's added to the right followers collection
|
||||
When I send a POST request with the following activity to "/activitypub/users/peter-lustiger/inbox":
|
||||
"""
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://localhost:4123/activitypub/users/bob-der-baumeister/status/83J23549sda1k72fsa4567na42312455kad83",
|
||||
"type": "Follow",
|
||||
"actor": "http://localhost:4123/activitypub/users/bob-der-baumeister",
|
||||
"object": "http://localhost:4123/activitypub/users/peter-lustiger"
|
||||
}
|
||||
"""
|
||||
Then I expect the status code to be 200
|
||||
And the follower is added to the followers collection of "peter-lustiger"
|
||||
"""
|
||||
https://localhost:4123/activitypub/users/bob-der-baumeister
|
||||
"""
|
||||
|
||||
Scenario: Send an undo activity to revert the previous follow activity
|
||||
When I send a POST request with the following activity to "/activitypub/users/bob-der-baumeister/inbox":
|
||||
"""
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://localhost:4123/activitypub/users/peter-lustiger/status/a4DJ2afdg323v32641vna42lkj685kasd2",
|
||||
"type": "Undo",
|
||||
"actor": "http://localhost:4123/activitypub/users/peter-lustiger",
|
||||
"object": {
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://localhost:4123/activitypub/users/bob-der-baumeister/status/83J23549sda1k72fsa4567na42312455kad83",
|
||||
"type": "Follow",
|
||||
"actor": "http://localhost:4123/activitypub/users/bob-der-baumeister",
|
||||
"object": "http://localhost:4123/activitypub/users/peter-lustiger"
|
||||
}
|
||||
}
|
||||
"""
|
||||
Then I expect the status code to be 200
|
||||
And the follower is removed from the followers collection of "peter-lustiger"
|
||||
"""
|
||||
https://localhost:4123/activitypub/users/bob-der-baumeister
|
||||
"""
|
||||
101
test/features/collection.feature
Normal file
101
test/features/collection.feature
Normal file
@ -0,0 +1,101 @@
|
||||
Feature: Receiving collections
|
||||
As a member of the Fediverse I want to be able of fetching collections
|
||||
|
||||
Background:
|
||||
Given our own server runs at "http://localhost:4123"
|
||||
And we have the following users in our database:
|
||||
| Slug |
|
||||
| renate-oberdorfer |
|
||||
|
||||
Scenario: Send a request to the outbox URI of peter-lustig and expect a ordered collection
|
||||
When I send a GET request to "/activitypub/users/renate-oberdorfer/outbox"
|
||||
Then I expect the status code to be 200
|
||||
And I receive the following json:
|
||||
"""
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://localhost:4123/activitypub/users/renate-oberdorfer/outbox",
|
||||
"summary": "renate-oberdorfers outbox collection",
|
||||
"type": "OrderedCollection",
|
||||
"first": "https://localhost:4123/activitypub/users/renate-oberdorfer/outbox?page=true",
|
||||
"totalItems": 0
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: Send a request to the following URI of peter-lustig and expect a ordered collection
|
||||
When I send a GET request to "/activitypub/users/renate-oberdorfer/following"
|
||||
Then I expect the status code to be 200
|
||||
And I receive the following json:
|
||||
"""
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://localhost:4123/activitypub/users/renate-oberdorfer/following",
|
||||
"summary": "renate-oberdorfers following collection",
|
||||
"type": "OrderedCollection",
|
||||
"first": "https://localhost:4123/activitypub/users/renate-oberdorfer/following?page=true",
|
||||
"totalItems": 0
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: Send a request to the followers URI of peter-lustig and expect a ordered collection
|
||||
When I send a GET request to "/activitypub/users/renate-oberdorfer/followers"
|
||||
Then I expect the status code to be 200
|
||||
And I receive the following json:
|
||||
"""
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://localhost:4123/activitypub/users/renate-oberdorfer/followers",
|
||||
"summary": "renate-oberdorfers followers collection",
|
||||
"type": "OrderedCollection",
|
||||
"first": "https://localhost:4123/activitypub/users/renate-oberdorfer/followers?page=true",
|
||||
"totalItems": 0
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: Send a request to the outbox URI of peter-lustig and expect a paginated outbox collection
|
||||
When I send a GET request to "/activitypub/users/renate-oberdorfer/outbox?page=true"
|
||||
Then I expect the status code to be 200
|
||||
And I receive the following json:
|
||||
"""
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://localhost:4123/activitypub/users/renate-oberdorfer/outbox?page=true",
|
||||
"summary": "renate-oberdorfers outbox collection",
|
||||
"type": "OrderedCollectionPage",
|
||||
"totalItems": 0,
|
||||
"partOf": "https://localhost:4123/activitypub/users/renate-oberdorfer/outbox",
|
||||
"orderedItems": []
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: Send a request to the following URI of peter-lustig and expect a paginated following collection
|
||||
When I send a GET request to "/activitypub/users/renate-oberdorfer/following?page=true"
|
||||
Then I expect the status code to be 200
|
||||
And I receive the following json:
|
||||
"""
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://localhost:4123/activitypub/users/renate-oberdorfer/following?page=true",
|
||||
"summary": "renate-oberdorfers following collection",
|
||||
"type": "OrderedCollectionPage",
|
||||
"totalItems": 0,
|
||||
"partOf": "https://localhost:4123/activitypub/users/renate-oberdorfer/following",
|
||||
"orderedItems": []
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: Send a request to the followers URI of peter-lustig and expect a paginated followers collection
|
||||
When I send a GET request to "/activitypub/users/renate-oberdorfer/followers?page=true"
|
||||
Then I expect the status code to be 200
|
||||
And I receive the following json:
|
||||
"""
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://localhost:4123/activitypub/users/renate-oberdorfer/followers?page=true",
|
||||
"summary": "renate-oberdorfers followers collection",
|
||||
"type": "OrderedCollectionPage",
|
||||
"totalItems": 0,
|
||||
"partOf": "https://localhost:4123/activitypub/users/renate-oberdorfer/followers",
|
||||
"orderedItems": []
|
||||
}
|
||||
"""
|
||||
125
test/features/support/steps.js
Normal file
125
test/features/support/steps.js
Normal file
@ -0,0 +1,125 @@
|
||||
// features/support/steps.js
|
||||
import { Given, When, Then, AfterAll } from 'cucumber'
|
||||
import { expect } from 'chai'
|
||||
// import { client } from '../../../src/activitypub/apollo-client'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import Factory from '../../../src/seed/factories'
|
||||
import { host } from '../../../src/jest/helpers'
|
||||
const debug = require('debug')('ea:test:steps')
|
||||
|
||||
const factory = Factory()
|
||||
const client = new GraphQLClient(host)
|
||||
|
||||
function createUser (slug) {
|
||||
debug(`creating user ${slug}`)
|
||||
return factory.create('User', {
|
||||
name: slug,
|
||||
email: 'example@test.org',
|
||||
password: '1234'
|
||||
})
|
||||
// await login({ email: 'example@test.org', password: '1234' })
|
||||
}
|
||||
|
||||
AfterAll('Clean up the test data', function () {
|
||||
debug('All the tests are done! Deleting test data')
|
||||
})
|
||||
|
||||
Given('our own server runs at {string}', function (string) {
|
||||
// just documenation
|
||||
})
|
||||
|
||||
Given('we have the following users in our database:', async function (dataTable) {
|
||||
await Promise.all(dataTable.hashes().map((user) => {
|
||||
return createUser(user.Slug)
|
||||
}))
|
||||
})
|
||||
|
||||
When('I send a GET request to {string}', async function (pathname) {
|
||||
const response = await this.get(pathname)
|
||||
this.lastContentType = response.lastContentType
|
||||
|
||||
this.lastResponses.push(response.lastResponse)
|
||||
this.statusCode = response.statusCode
|
||||
})
|
||||
|
||||
When('I send a POST request with the following activity to {string}:', async function (inboxUrl, activity) {
|
||||
debug(`inboxUrl = ${inboxUrl}`)
|
||||
debug(`activity = ${activity}`)
|
||||
this.lastInboxUrl = inboxUrl
|
||||
this.lastActivity = activity
|
||||
const response = await this.post(inboxUrl, activity)
|
||||
|
||||
this.lastResponses.push(response.lastResponse)
|
||||
this.lastResponse = response.lastResponse
|
||||
this.statusCode = response.statusCode
|
||||
})
|
||||
|
||||
Then('I receive the following json:', function (docString) {
|
||||
const parsedDocString = JSON.parse(docString)
|
||||
const parsedLastResponse = JSON.parse(this.lastResponses.shift())
|
||||
if (Array.isArray(parsedDocString.orderedItems)) {
|
||||
parsedDocString.orderedItems.forEach((el) => {
|
||||
delete el.id
|
||||
if (el.object) delete el.object.published
|
||||
})
|
||||
parsedLastResponse.orderedItems.forEach((el) => {
|
||||
delete el.id
|
||||
if (el.object) delete el.object.published
|
||||
})
|
||||
}
|
||||
if (parsedDocString.publicKey && parsedDocString.publicKey.publicKeyPem) {
|
||||
delete parsedDocString.publicKey.publicKeyPem
|
||||
delete parsedLastResponse.publicKey.publicKeyPem
|
||||
}
|
||||
expect(parsedDocString).to.eql(parsedLastResponse)
|
||||
})
|
||||
|
||||
Then('I expect the Content-Type to be {string}', function (contentType) {
|
||||
expect(this.lastContentType).to.equal(contentType)
|
||||
})
|
||||
|
||||
Then('I expect the status code to be {int}', function (statusCode) {
|
||||
expect(this.statusCode).to.equal(statusCode)
|
||||
})
|
||||
|
||||
Then('the activity is added to the {string} collection', async function (collectionName) {
|
||||
const response = await this.get(this.lastInboxUrl.replace('inbox', collectionName) + '?page=true')
|
||||
debug(`orderedItems = ${JSON.parse(response.lastResponse).orderedItems}`)
|
||||
expect(JSON.parse(response.lastResponse).orderedItems).to.include(JSON.parse(this.lastActivity).object)
|
||||
})
|
||||
|
||||
Then('the follower is added to the followers collection of {string}', async function (userName, follower) {
|
||||
const response = await this.get(`/activitypub/users/${userName}/followers?page=true`)
|
||||
const responseObject = JSON.parse(response.lastResponse)
|
||||
expect(responseObject.orderedItems).to.include(follower)
|
||||
})
|
||||
|
||||
Then('the follower is removed from the followers collection of {string}', async function (userName, follower) {
|
||||
const response = await this.get(`/activitypub/users/${userName}/followers?page=true`)
|
||||
const responseObject = JSON.parse(response.lastResponse)
|
||||
expect(responseObject.orderedItems).to.not.include(follower)
|
||||
})
|
||||
|
||||
Then('the activity is added to the users inbox collection', async function () {
|
||||
|
||||
})
|
||||
|
||||
Then('the post with id {string} to be created', async function (id) {
|
||||
const result = await client.request(`
|
||||
query {
|
||||
Post(id: "${id}") {
|
||||
name
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
expect(result.data.Post).to.be.an('array').that.is.not.empty // eslint-disable-line
|
||||
})
|
||||
|
||||
Then('the object is removed from the outbox collection of {string}', (name) => {
|
||||
|
||||
})
|
||||
|
||||
Then('I send a GET request to {string} and expect a ordered collection', () => {
|
||||
|
||||
})
|
||||
65
test/features/webfinger.feature
Normal file
65
test/features/webfinger.feature
Normal file
@ -0,0 +1,65 @@
|
||||
Feature: Webfinger discovery
|
||||
From an external server, e.g. Mastodon
|
||||
I want to search for an actor alias
|
||||
In order to follow the actor
|
||||
|
||||
Background:
|
||||
Given our own server runs at "http://localhost:4100"
|
||||
And we have the following users in our database:
|
||||
| Slug |
|
||||
| peter-lustiger |
|
||||
|
||||
Scenario: Search
|
||||
When I send a GET request to "/.well-known/webfinger?resource=acct:peter-lustiger@localhost"
|
||||
Then I receive the following json:
|
||||
"""
|
||||
{
|
||||
"subject": "acct:peter-lustiger@localhost:4123",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"type": "application/activity+json",
|
||||
"href": "https://localhost:4123/users/peter-lustiger"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
And I expect the Content-Type to be "application/jrd+json; charset=utf-8"
|
||||
|
||||
Scenario: User does not exist
|
||||
When I send a GET request to "/.well-known/webfinger?resource=acct:nonexisting@localhost"
|
||||
Then I receive the following json:
|
||||
"""
|
||||
{
|
||||
"error": "No record found for nonexisting@localhost."
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: Receiving an actor object
|
||||
When I send a GET request to "/activitypub/users/peter-lustiger"
|
||||
Then I receive the following json:
|
||||
"""
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1"
|
||||
],
|
||||
"id": "https://localhost:4123/activitypub/users/peter-lustiger",
|
||||
"type": "Person",
|
||||
"preferredUsername": "peter-lustiger",
|
||||
"name": "peter-lustiger",
|
||||
"following": "https://localhost:4123/activitypub/users/peter-lustiger/following",
|
||||
"followers": "https://localhost:4123/activitypub/users/peter-lustiger/followers",
|
||||
"inbox": "https://localhost:4123/activitypub/users/peter-lustiger/inbox",
|
||||
"outbox": "https://localhost:4123/activitypub/users/peter-lustiger/outbox",
|
||||
"url": "https://localhost:4123/activitypub/@peter-lustiger",
|
||||
"endpoints": {
|
||||
"sharedInbox": "https://localhost:4123/activitypub/inbox"
|
||||
},
|
||||
"publicKey": {
|
||||
"id": "https://localhost:4123/activitypub/users/peter-lustiger#main-key",
|
||||
"owner": "https://localhost:4123/activitypub/users/peter-lustiger",
|
||||
"publicKeyPem": "adglkjlk89235kjn8obn2384f89z5bv9..."
|
||||
}
|
||||
}
|
||||
"""
|
||||
58
test/features/world.js
Normal file
58
test/features/world.js
Normal file
@ -0,0 +1,58 @@
|
||||
// features/support/world.js
|
||||
import { setWorldConstructor } from 'cucumber'
|
||||
import request from 'request'
|
||||
const debug = require('debug')('ea:test:world')
|
||||
|
||||
class CustomWorld {
|
||||
constructor () {
|
||||
// webfinger.feature
|
||||
this.lastResponses = []
|
||||
this.lastContentType = null
|
||||
this.lastInboxUrl = null
|
||||
this.lastActivity = null
|
||||
// activity-article.feature.disable
|
||||
this.statusCode = null
|
||||
}
|
||||
get (pathname) {
|
||||
return new Promise((resolve, reject) => {
|
||||
request(`http://localhost:4123/${this.replaceSlashes(pathname)}`, {
|
||||
headers: {
|
||||
'Accept': 'application/activity+json'
|
||||
}}, function (error, response, body) {
|
||||
if (!error) {
|
||||
debug(`get response = ${response.headers['content-type']}`)
|
||||
debug(`body = ${body}`)
|
||||
resolve({ lastResponse: body, lastContentType: response.headers['content-type'], statusCode: response.statusCode })
|
||||
} else {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
replaceSlashes (pathname) {
|
||||
return pathname.replace(/^\/+/, '')
|
||||
}
|
||||
|
||||
post (pathname, activity) {
|
||||
return new Promise((resolve, reject) => {
|
||||
request({
|
||||
url: `http://localhost:4123/${this.replaceSlashes(pathname)}`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/activity+json'
|
||||
},
|
||||
body: activity
|
||||
}, function (error, response, body) {
|
||||
if (!error) {
|
||||
debug(`post response = ${response.headers['content-type']}`)
|
||||
resolve({ lastResponse: body, lastContentType: response.headers['content-type'], statusCode: response.statusCode })
|
||||
} else {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setWorldConstructor(CustomWorld)
|
||||
600
yarn.lock
600
yarn.lock
@ -291,6 +291,14 @@
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
"@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
|
||||
|
||||
"@babel/plugin-proposal-throw-expressions@^7.2.0":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-throw-expressions/-/plugin-proposal-throw-expressions-7.2.0.tgz#2d9e452d370f139000e51db65d0a85dc60c64739"
|
||||
integrity sha512-adsydM8DQF4i5DLNO4ySAU5VtHTPewOtNBV3u7F4lNMPADFF9bWQ+iDtUUe8+033cYCUz+bFlQdXQJmJOwoLpw==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
"@babel/plugin-syntax-throw-expressions" "^7.2.0"
|
||||
|
||||
"@babel/plugin-proposal-unicode-property-regex@^7.2.0":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.2.0.tgz#abe7281fe46c95ddc143a65e5358647792039520"
|
||||
@ -328,6 +336,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
|
||||
"@babel/plugin-syntax-throw-expressions@^7.2.0":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-throw-expressions/-/plugin-syntax-throw-expressions-7.2.0.tgz#79001ee2afe1b174b1733cdc2fc69c9a46a0f1f8"
|
||||
integrity sha512-ngwynuqu1Rx0JUS9zxSDuPgW1K8TyVZCi2hHehrL4vyjqE7RGoNHWlZsS7KQT2vw9Yjk4YLa0+KldBXTRdPLRg==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
|
||||
"@babel/plugin-transform-arrow-functions@^7.2.0":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550"
|
||||
@ -558,6 +573,14 @@
|
||||
core-js "^2.5.7"
|
||||
regenerator-runtime "^0.11.1"
|
||||
|
||||
"@babel/polyfill@^7.2.3":
|
||||
version "7.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.2.5.tgz#6c54b964f71ad27edddc567d065e57e87ed7fa7d"
|
||||
integrity sha512-8Y/t3MWThtMLYr0YNC/Q76tqN1w30+b0uQMeFUYauG2UGTR19zyUtFrAzT23zNtBxPp+LbE5E/nwV/q/r3y6ug==
|
||||
dependencies:
|
||||
core-js "^2.5.7"
|
||||
regenerator-runtime "^0.12.0"
|
||||
|
||||
"@babel/preset-env@~7.3.1":
|
||||
version "7.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.3.1.tgz#389e8ca6b17ae67aaf9a2111665030be923515db"
|
||||
@ -876,6 +899,33 @@ acorn@^6.0.1, acorn@^6.0.2:
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.4.tgz#77377e7353b72ec5104550aa2d2097a2fd40b754"
|
||||
integrity sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==
|
||||
|
||||
activitystrea.ms@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/activitystrea.ms/-/activitystrea.ms-2.1.3.tgz#553548733e367dc0b6a7badc25fa6f8996cd80c3"
|
||||
integrity sha512-iiG5g5fYgfdaaqqFPaFIZC/KX8/4mOWkvniK+BNwJY6XDDKdIu56wmc9r0x1INHVnbFOTGuM8mZEntaM3I+YXw==
|
||||
dependencies:
|
||||
activitystreams-context "^3.0.0"
|
||||
jsonld "^0.4.11"
|
||||
jsonld-signatures "^1.1.5"
|
||||
moment "^2.17.1"
|
||||
readable-stream "^2.2.3"
|
||||
reasoner "2.0.0"
|
||||
rfc5646 "^2.0.0"
|
||||
vocabs-as "^3.0.0"
|
||||
vocabs-asx "^0.11.1"
|
||||
vocabs-interval "^0.11.1"
|
||||
vocabs-ldp "^0.1.0"
|
||||
vocabs-owl "^0.11.1"
|
||||
vocabs-rdf "^0.11.1"
|
||||
vocabs-rdfs "^0.11.1"
|
||||
vocabs-social "^0.11.1"
|
||||
vocabs-xsd "^0.11.1"
|
||||
|
||||
activitystreams-context@>=3.0.0, activitystreams-context@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/activitystreams-context/-/activitystreams-context-3.1.0.tgz#28334e129f17cfb937e8c702c52c1bcb1d2830c7"
|
||||
integrity sha512-KBQ+igwf1tezMXGVw5MvRSEm0gp97JI1hTZ45I6MEkWv25lEgNoA9L6wqfaOiCX8wnMRWw9pwRsPZKypdtxAtg==
|
||||
|
||||
ajv@^6.5.3, ajv@^6.5.5, ajv@^6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.6.1.tgz#6360f5ed0d80f232cc2b294c362d5dc2e538dd61"
|
||||
@ -920,6 +970,11 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
any-promise@^1.0.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
|
||||
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
|
||||
|
||||
anymatch@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
|
||||
@ -1295,6 +1350,15 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
|
||||
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
|
||||
|
||||
assertion-error-formatter@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/assertion-error-formatter/-/assertion-error-formatter-2.0.1.tgz#6bbdffaec8e2fa9e2b0eb158bfe353132d7c0a9b"
|
||||
integrity sha512-cjC3jUCh9spkroKue5PDSKH5RFQ/KNuZJhk3GwHYmB/8qqETxLOmMdLH+ohi/VukNzxDlMvIe7zScvLoOdhb6Q==
|
||||
dependencies:
|
||||
diff "^3.0.0"
|
||||
pad-right "^0.2.2"
|
||||
repeat-string "^1.6.1"
|
||||
|
||||
assertion-error@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
|
||||
@ -1337,6 +1401,11 @@ async-retry@^1.2.1:
|
||||
dependencies:
|
||||
retry "0.12.0"
|
||||
|
||||
async@^1.5.2:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
|
||||
integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=
|
||||
|
||||
async@^2.5.0, async@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
|
||||
@ -1485,11 +1554,49 @@ bcryptjs@~2.4.3:
|
||||
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
|
||||
integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=
|
||||
|
||||
becke-ch--regex--s0-0-v1--base--pl--lib@^1.2.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/becke-ch--regex--s0-0-v1--base--pl--lib/-/becke-ch--regex--s0-0-v1--base--pl--lib-1.4.0.tgz#429ceebbfa5f7e936e78d73fbdc7da7162b20e20"
|
||||
integrity sha1-Qpzuu/pffpNueNc/vcfacWKyDiA=
|
||||
|
||||
binary-extensions@^1.0.0:
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14"
|
||||
integrity sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==
|
||||
|
||||
bitcore-lib@^0.13.7:
|
||||
version "0.13.19"
|
||||
resolved "https://registry.yarnpkg.com/bitcore-lib/-/bitcore-lib-0.13.19.tgz#48af1e9bda10067c1ab16263472b5add2000f3dc"
|
||||
integrity sha1-SK8em9oQBnwasWJjRyta3SAA89w=
|
||||
dependencies:
|
||||
bn.js "=2.0.4"
|
||||
bs58 "=2.0.0"
|
||||
buffer-compare "=1.0.0"
|
||||
elliptic "=3.0.3"
|
||||
inherits "=2.0.1"
|
||||
lodash "=3.10.1"
|
||||
|
||||
"bitcore-message@github:comakery/bitcore-message#dist":
|
||||
version "1.0.2"
|
||||
resolved "https://codeload.github.com/comakery/bitcore-message/tar.gz/8799cc327029c3d34fc725f05b2cf981363f6ebf"
|
||||
dependencies:
|
||||
bitcore-lib "^0.13.7"
|
||||
|
||||
bluebird@^3.4.1:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7"
|
||||
integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==
|
||||
|
||||
bn.js@=2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-2.0.4.tgz#220a7cd677f7f1bfa93627ff4193776fe7819480"
|
||||
integrity sha1-Igp81nf38b+pNif/QZN3b+eBlIA=
|
||||
|
||||
bn.js@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-2.2.0.tgz#12162bc2ae71fc40a5626c33438f3a875cd37625"
|
||||
integrity sha1-EhYrwq5x/EClYmwzQ486h1zTdiU=
|
||||
|
||||
body-parser-graphql@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/body-parser-graphql/-/body-parser-graphql-1.1.0.tgz#80a80353c7cb623562fd375750dfe018d75f0f7c"
|
||||
@ -1555,6 +1662,11 @@ braces@^2.3.1, braces@^2.3.2:
|
||||
split-string "^3.0.2"
|
||||
to-regex "^3.0.1"
|
||||
|
||||
brorand@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
||||
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
|
||||
|
||||
browser-process-hrtime@^0.1.2:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4"
|
||||
@ -1576,6 +1688,11 @@ browserslist@^4.3.4:
|
||||
electron-to-chromium "^1.3.86"
|
||||
node-releases "^1.0.5"
|
||||
|
||||
bs58@=2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bs58/-/bs58-2.0.0.tgz#72b713bed223a0ac518bbda0e3ce3f4817f39eb5"
|
||||
integrity sha1-crcTvtIjoKxRi72g484/SBfznrU=
|
||||
|
||||
bser@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
|
||||
@ -1583,6 +1700,11 @@ bser@^2.0.0:
|
||||
dependencies:
|
||||
node-int64 "^0.4.0"
|
||||
|
||||
buffer-compare@=1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-compare/-/buffer-compare-1.0.0.tgz#acaa7a966e98eee9fae14b31c39a5f158fb3c4a2"
|
||||
integrity sha1-rKp6lm6Y7un64Usxw5pfFY+zxKI=
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||
@ -1794,7 +1916,7 @@ cli-cursor@^2.1.0:
|
||||
dependencies:
|
||||
restore-cursor "^2.0.0"
|
||||
|
||||
cli-table3@^0.5.0:
|
||||
cli-table3@^0.5.0, cli-table3@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202"
|
||||
integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==
|
||||
@ -1860,7 +1982,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
commander@^2.5.0, commander@^2.8.1:
|
||||
commander@^2.5.0, commander@^2.8.1, commander@^2.9.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
|
||||
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
|
||||
@ -1870,6 +1992,13 @@ commander@~2.17.1:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
||||
|
||||
commander@~2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
|
||||
integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=
|
||||
dependencies:
|
||||
graceful-readlink ">= 1.0.0"
|
||||
|
||||
commondir@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||
@ -1973,7 +2102,7 @@ core-util-is@1.0.2, core-util-is@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||
|
||||
cors@^2.8.4:
|
||||
cors@^2.8.4, cors@^2.8.5:
|
||||
version "2.8.5"
|
||||
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
|
||||
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
|
||||
@ -2064,6 +2193,60 @@ cssstyle@^1.0.0:
|
||||
dependencies:
|
||||
cssom "0.3.x"
|
||||
|
||||
cucumber-expressions@^6.0.0:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cucumber-expressions/-/cucumber-expressions-6.0.1.tgz#47c9c573781c2ff721d7ad5b2cd1c97f4399ab8e"
|
||||
integrity sha1-R8nFc3gcL/ch161bLNHJf0OZq44=
|
||||
dependencies:
|
||||
becke-ch--regex--s0-0-v1--base--pl--lib "^1.2.0"
|
||||
|
||||
cucumber-tag-expressions@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/cucumber-tag-expressions/-/cucumber-tag-expressions-1.1.1.tgz#7f5c7b70009bc2b666591bfe64854578bedee85a"
|
||||
integrity sha1-f1x7cACbwrZmWRv+ZIVFeL7e6Fo=
|
||||
|
||||
cucumber@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cucumber/-/cucumber-5.1.0.tgz#7b166812c255bec7eac4b0df7007a40d089c895d"
|
||||
integrity sha512-zrl2VYTBRgvxucwV2GKAvLqcfA1Naeax8plPvWgPEzl3SCJiuPPv3WxBHIRHtPYcEdbHDR6oqLpZP4bJ8UIdmA==
|
||||
dependencies:
|
||||
"@babel/polyfill" "^7.2.3"
|
||||
assertion-error-formatter "^2.0.1"
|
||||
bluebird "^3.4.1"
|
||||
cli-table3 "^0.5.1"
|
||||
colors "^1.1.2"
|
||||
commander "^2.9.0"
|
||||
cross-spawn "^6.0.5"
|
||||
cucumber-expressions "^6.0.0"
|
||||
cucumber-tag-expressions "^1.1.1"
|
||||
duration "^0.2.1"
|
||||
escape-string-regexp "^1.0.5"
|
||||
figures "2.0.0"
|
||||
gherkin "^5.0.0"
|
||||
glob "^7.1.3"
|
||||
indent-string "^3.1.0"
|
||||
is-generator "^1.0.2"
|
||||
is-stream "^1.1.0"
|
||||
knuth-shuffle-seeded "^1.0.6"
|
||||
lodash "^4.17.10"
|
||||
mz "^2.4.0"
|
||||
progress "^2.0.0"
|
||||
resolve "^1.3.3"
|
||||
serialize-error "^3.0.0"
|
||||
stack-chain "^2.0.0"
|
||||
stacktrace-js "^2.0.0"
|
||||
string-argv "0.1.1"
|
||||
title-case "^2.1.1"
|
||||
util-arity "^1.0.2"
|
||||
verror "^1.9.0"
|
||||
|
||||
d@1:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f"
|
||||
integrity sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=
|
||||
dependencies:
|
||||
es5-ext "^0.10.9"
|
||||
|
||||
dashdash@^1.12.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
||||
@ -2217,6 +2400,11 @@ diff-sequences@^24.0.0:
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.0.0.tgz#cdf8e27ed20d8b8d3caccb4e0c0d8fe31a173013"
|
||||
integrity sha512-46OkIuVGBBnrC0soO/4LHu5LHGHx0uhP65OVz8XOrAJpqiCB2aVIuESvjI1F9oqebuvY8lekS1pt6TN7vt7qsw==
|
||||
|
||||
diff@^3.0.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
|
||||
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
|
||||
|
||||
doctrine@1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
|
||||
@ -2302,6 +2490,14 @@ duplexer3@^0.1.4:
|
||||
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
||||
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
|
||||
|
||||
duration@^0.2.1:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/duration/-/duration-0.2.2.tgz#ddf149bc3bc6901150fe9017111d016b3357f529"
|
||||
integrity sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "~0.10.46"
|
||||
|
||||
ecc-jsbn@~0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
|
||||
@ -2327,6 +2523,16 @@ electron-to-chromium@^1.3.86:
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.88.tgz#f36ab32634f49ef2b0fdc1e82e2d1cc17feb29e7"
|
||||
integrity sha512-UPV4NuQMKeUh1S0OWRvwg0PI8ASHN9kBC8yDTk1ROXLC85W5GnhTRu/MZu3Teqx3JjlQYuckuHYXSUSgtb3J+A==
|
||||
|
||||
elliptic@=3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-3.0.3.tgz#865c9b420bfbe55006b9f969f97a0d2c44966595"
|
||||
integrity sha1-hlybQgv75VAGuflp+XoNLESWZZU=
|
||||
dependencies:
|
||||
bn.js "^2.0.0"
|
||||
brorand "^1.0.1"
|
||||
hash.js "^1.0.0"
|
||||
inherits "^2.0.1"
|
||||
|
||||
encodeurl@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
@ -2351,6 +2557,13 @@ error-ex@^1.2.0, error-ex@^1.3.1:
|
||||
dependencies:
|
||||
is-arrayish "^0.2.1"
|
||||
|
||||
error-stack-parser@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.2.tgz#4ae8dbaa2bf90a8b450707b9149dcabca135520d"
|
||||
integrity sha512-E1fPutRDdIj/hohG0UpT5mayXNCxXP9d+snxFsPU9X0XgccOumKraa3juDMwTUyi7+Bu5+mCGagjg4IYeNbOdw==
|
||||
dependencies:
|
||||
stackframe "^1.0.4"
|
||||
|
||||
es-abstract@^1.4.3:
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165"
|
||||
@ -2383,11 +2596,52 @@ es-to-primitive@^1.1.1, es-to-primitive@^1.2.0:
|
||||
is-date-object "^1.0.1"
|
||||
is-symbol "^1.0.2"
|
||||
|
||||
es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14, es5-ext@~0.10.46:
|
||||
version "0.10.48"
|
||||
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.48.tgz#9a0b31eeded39e64453bcedf6f9d50bbbfb43850"
|
||||
integrity sha512-CdRvPlX/24Mj5L4NVxTs4804sxiS2CjVprgCmrgoDkdmjdY4D+ySHa7K3jJf8R40dFg0tIm3z/dk326LrnuSGw==
|
||||
dependencies:
|
||||
es6-iterator "~2.0.3"
|
||||
es6-symbol "~3.1.1"
|
||||
next-tick "1"
|
||||
|
||||
es6-error@^4.0.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
|
||||
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
|
||||
|
||||
es6-iterator@~2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
|
||||
integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "^0.10.35"
|
||||
es6-symbol "^3.1.1"
|
||||
|
||||
es6-promise@^2.0.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-2.3.0.tgz#96edb9f2fdb01995822b263dd8aadab6748181bc"
|
||||
integrity sha1-lu258v2wGZWCKyY92KratnSBgbw=
|
||||
|
||||
es6-promise@~2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-2.0.1.tgz#ccc4963e679f0ca9fb187c777b9e583d3c7573c2"
|
||||
integrity sha1-zMSWPmefDKn7GHx3e55YPTx1c8I=
|
||||
|
||||
es6-promise@~4.0.5:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.0.5.tgz#7882f30adde5b240ccfa7f7d78c548330951ae42"
|
||||
integrity sha1-eILzCt3lskDM+n99eMVIMwlRrkI=
|
||||
|
||||
es6-symbol@^3.1.1, es6-symbol@~3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
|
||||
integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "~0.10.14"
|
||||
|
||||
escape-html@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
@ -2670,7 +2924,7 @@ expect@^24.1.0:
|
||||
jest-message-util "^24.0.0"
|
||||
jest-regex-util "^24.0.0"
|
||||
|
||||
express@^4.0.0, express@^4.16.3:
|
||||
express@^4.0.0, express@^4.16.3, express@^4.16.4:
|
||||
version "4.16.4"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e"
|
||||
integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==
|
||||
@ -2786,7 +3040,7 @@ fb-watchman@^2.0.0:
|
||||
dependencies:
|
||||
bser "^2.0.0"
|
||||
|
||||
figures@^2.0.0:
|
||||
figures@2.0.0, figures@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
|
||||
integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
|
||||
@ -3024,6 +3278,11 @@ getpass@^0.1.1:
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
gherkin@^5.0.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/gherkin/-/gherkin-5.1.0.tgz#684bbb03add24eaf7bdf544f58033eb28fb3c6d5"
|
||||
integrity sha1-aEu7A63STq9731RPWAM+so+zxtU=
|
||||
|
||||
glob-parent@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
|
||||
@ -3078,6 +3337,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2:
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
||||
|
||||
"graceful-readlink@>= 1.0.0":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
|
||||
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
|
||||
|
||||
graphql-custom-directives@~0.2.14:
|
||||
version "0.2.14"
|
||||
resolved "https://registry.yarnpkg.com/graphql-custom-directives/-/graphql-custom-directives-0.2.14.tgz#88611b8cb074477020ad85af47bfe168c4c23992"
|
||||
@ -3230,14 +3494,7 @@ graphql-yoga@~1.17.4:
|
||||
graphql-tools "^4.0.0"
|
||||
subscriptions-transport-ws "^0.9.8"
|
||||
|
||||
"graphql@^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0", graphql@^14.0.2:
|
||||
version "14.0.2"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-14.0.2.tgz#7dded337a4c3fd2d075692323384034b357f5650"
|
||||
integrity sha512-gUC4YYsaiSJT1h40krG3J+USGlwhzNTXSb4IOZljn9ag5Tj+RkoXrWp+Kh7WyE3t1NCfab5kzCuxBIvOMERMXw==
|
||||
dependencies:
|
||||
iterall "^1.2.2"
|
||||
|
||||
graphql@~14.1.1:
|
||||
"graphql@^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0", graphql@^14.0.2, graphql@~14.1.1:
|
||||
version "14.1.1"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-14.1.1.tgz#d5d77df4b19ef41538d7215d1e7a28834619fac0"
|
||||
integrity sha512-C5zDzLqvfPAgTtP8AUPIt9keDabrdRAqSWjj2OPRKrKxI9Fb65I36s1uCs1UUBFnSWTdO7hyHi7z1ZbwKMKF6Q==
|
||||
@ -3326,6 +3583,14 @@ has@^1.0.1, has@^1.0.3:
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
hash.js@^1.0.0:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
|
||||
integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
minimalistic-assert "^1.0.1"
|
||||
|
||||
hasha@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hasha/-/hasha-3.0.0.tgz#52a32fab8569d41ca69a61ff1a214f8eb7c8bd39"
|
||||
@ -3491,6 +3756,11 @@ imurmurhash@^0.1.4:
|
||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
|
||||
|
||||
indent-string@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
|
||||
integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
@ -3504,6 +3774,11 @@ inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, i
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
inherits@=2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
||||
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
|
||||
|
||||
ini@^1.3.4, ini@~1.3.0:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
||||
@ -3681,6 +3956,11 @@ is-generator-fn@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.0.0.tgz#038c31b774709641bda678b1f06a4e3227c10b3e"
|
||||
integrity sha512-elzyIdM7iKoFHzcrndIqjYomImhxrFRnGP3galODoII4TB9gI7mZ+FnlLQmmjf27SxHS2gKEeyhX5/+YRS6H9g==
|
||||
|
||||
is-generator@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-generator/-/is-generator-1.0.3.tgz#c14c21057ed36e328db80347966c693f886389f3"
|
||||
integrity sha1-wUwhBX7TbjKNuANHlmxpP4hjifM=
|
||||
|
||||
is-glob@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
|
||||
@ -4348,6 +4628,38 @@ jsonify@~0.0.0:
|
||||
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
|
||||
integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
|
||||
|
||||
jsonld-signatures@^1.1.5:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonld-signatures/-/jsonld-signatures-1.2.1.tgz#493df5df9cd3a9f1b1cb296bbd3d081679f20ca8"
|
||||
integrity sha1-ST3135zTqfGxyylrvT0IFnnyDKg=
|
||||
dependencies:
|
||||
async "^1.5.2"
|
||||
bitcore-message "github:CoMakery/bitcore-message#dist"
|
||||
commander "~2.9.0"
|
||||
es6-promise "~4.0.5"
|
||||
jsonld "0.4.3"
|
||||
node-forge "~0.6.45"
|
||||
|
||||
jsonld@0.4.3:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-0.4.3.tgz#0bbc929190064d6650a5af5876e1bfdf0ed288f3"
|
||||
integrity sha1-C7ySkZAGTWZQpa9YduG/3w7SiPM=
|
||||
dependencies:
|
||||
es6-promise "~2.0.1"
|
||||
pkginfo "~0.3.0"
|
||||
request "^2.61.0"
|
||||
xmldom "0.1.19"
|
||||
|
||||
jsonld@^0.4.11:
|
||||
version "0.4.12"
|
||||
resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-0.4.12.tgz#a02f205d5341414df1b6d8414f1b967a712073e8"
|
||||
integrity sha1-oC8gXVNBQU3xtthBTxuWenEgc+g=
|
||||
dependencies:
|
||||
es6-promise "^2.0.0"
|
||||
pkginfo "~0.4.0"
|
||||
request "^2.61.0"
|
||||
xmldom "0.1.19"
|
||||
|
||||
jsonwebtoken@^8.2.0, jsonwebtoken@~8.5.0:
|
||||
version "8.5.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz#ebd0ca2a69797816e1c5af65b6c759787252947e"
|
||||
@ -4420,6 +4732,13 @@ kleur@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.1.tgz#4f5b313f5fa315432a400f19a24db78d451ede62"
|
||||
integrity sha512-P3kRv+B+Ra070ng2VKQqW4qW7gd/v3iD8sy/zOdcYRsfiD+QBokQNOps/AfP6Hr48cBhIIBFWckB9aO+IZhrWg==
|
||||
|
||||
knuth-shuffle-seeded@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz#01f1b65733aa7540ee08d8b0174164d22081e4e1"
|
||||
integrity sha1-AfG2VzOqdUDuCNiwF0Fk0iCB5OE=
|
||||
dependencies:
|
||||
seed-random "~2.2.0"
|
||||
|
||||
latest-version@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15"
|
||||
@ -4571,6 +4890,11 @@ lodash.sortby@^4.7.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
|
||||
|
||||
lodash@=3.10.1:
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
|
||||
integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=
|
||||
|
||||
lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.11:
|
||||
version "4.17.11"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
@ -4588,6 +4912,11 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
|
||||
dependencies:
|
||||
js-tokens "^3.0.0 || ^4.0.0"
|
||||
|
||||
lower-case@^1.1.1:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
|
||||
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
|
||||
|
||||
lowercase-keys@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
||||
@ -4735,6 +5064,11 @@ mimic-fn@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
|
||||
integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
|
||||
|
||||
minimalistic-assert@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
||||
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
|
||||
|
||||
minimatch@^3.0.3, minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
@ -4787,6 +5121,11 @@ mkdirp@^0.5.0, mkdirp@^0.5.1:
|
||||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
moment@^2.17.1:
|
||||
version "2.24.0"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
|
||||
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
|
||||
|
||||
moment@^2.22.2:
|
||||
version "2.22.2"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
|
||||
@ -4807,6 +5146,20 @@ mute-stream@0.0.7:
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
|
||||
|
||||
mz@^2.4.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
|
||||
integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
|
||||
dependencies:
|
||||
any-promise "^1.0.0"
|
||||
object-assign "^4.0.1"
|
||||
thenify-all "^1.0.0"
|
||||
|
||||
n3@^0.9.1:
|
||||
version "0.9.1"
|
||||
resolved "https://registry.yarnpkg.com/n3/-/n3-0.9.1.tgz#430b547d58dc7381408c45784dd8058171903932"
|
||||
integrity sha1-QwtUfVjcc4FAjEV4TdgFgXGQOTI=
|
||||
|
||||
nan@^2.9.2:
|
||||
version "2.11.1"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.1.tgz#90e22bccb8ca57ea4cd37cc83d3819b52eea6766"
|
||||
@ -4866,11 +5219,23 @@ neo4j-graphql-js@~2.3.1:
|
||||
lodash "^4.17.11"
|
||||
neo4j-driver "^1.7.2"
|
||||
|
||||
next-tick@1:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
|
||||
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
|
||||
|
||||
nice-try@^1.0.4:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
no-case@^2.2.0:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
|
||||
integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
|
||||
dependencies:
|
||||
lower-case "^1.1.1"
|
||||
|
||||
node-fetch@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5"
|
||||
@ -4881,6 +5246,11 @@ node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@~2.3.0:
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5"
|
||||
integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==
|
||||
|
||||
node-forge@~0.6.45:
|
||||
version "0.6.49"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.49.tgz#f1ee95d5d74623938fe19d698aa5a26d54d2f60f"
|
||||
integrity sha1-8e6V1ddGI5OP4Z1piqWibVTS9g8=
|
||||
|
||||
node-int64@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
|
||||
@ -5299,6 +5669,13 @@ package-json@^4.0.0:
|
||||
registry-url "^3.0.3"
|
||||
semver "^5.1.0"
|
||||
|
||||
pad-right@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/pad-right/-/pad-right-0.2.2.tgz#6fbc924045d244f2a2a244503060d3bfc6009774"
|
||||
integrity sha1-b7ySQEXSRPKiokRQMGDTv8YAl3Q=
|
||||
dependencies:
|
||||
repeat-string "^1.5.2"
|
||||
|
||||
parent-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.0.tgz#df250bdc5391f4a085fb589dad761f5ad6b865b5"
|
||||
@ -5469,6 +5846,16 @@ pkg-dir@^3.0.0:
|
||||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
pkginfo@~0.3.0:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.3.1.tgz#5b29f6a81f70717142e09e765bbeab97b4f81e21"
|
||||
integrity sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=
|
||||
|
||||
pkginfo@~0.4.0:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff"
|
||||
integrity sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=
|
||||
|
||||
pn@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
|
||||
@ -5716,7 +6103,7 @@ readable-stream@1.1.x:
|
||||
isarray "0.0.1"
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.3.5:
|
||||
readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.3, readable-stream@^2.3.5:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
|
||||
@ -5754,6 +6141,18 @@ realpath-native@^1.0.0, realpath-native@^1.0.2:
|
||||
dependencies:
|
||||
util.promisify "^1.0.0"
|
||||
|
||||
reasoner@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/reasoner/-/reasoner-2.0.0.tgz#6ccf76cb9baf96b82c45ab0bd60211c2aa1b701b"
|
||||
integrity sha1-bM92y5uvlrgsRasL1gIRwqobcBs=
|
||||
dependencies:
|
||||
n3 "^0.9.1"
|
||||
rfc5646 "^2.0.0"
|
||||
vocabs-asx "^0.11.1"
|
||||
vocabs-rdf "^0.11.1"
|
||||
vocabs-rdfs "^0.11.1"
|
||||
vocabs-xsd "^0.11.1"
|
||||
|
||||
regenerate-unicode-properties@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c"
|
||||
@ -5861,7 +6260,7 @@ repeat-element@^1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
|
||||
integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
|
||||
|
||||
repeat-string@^1.6.1:
|
||||
repeat-string@^1.5.2, repeat-string@^1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
||||
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
|
||||
@ -5882,7 +6281,7 @@ request-promise-native@^1.0.5:
|
||||
stealthy-require "^1.1.0"
|
||||
tough-cookie ">=2.3.3"
|
||||
|
||||
request@^2.87.0, request@^2.88.0:
|
||||
request@^2.61.0, request@^2.87.0, request@^2.88.0:
|
||||
version "2.88.0"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
|
||||
integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
|
||||
@ -5945,7 +6344,7 @@ resolve@1.1.7:
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
|
||||
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
|
||||
|
||||
resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1, resolve@^1.9.0:
|
||||
resolve@^1.3.2, resolve@^1.3.3, resolve@^1.5.0, resolve@^1.8.1, resolve@^1.9.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba"
|
||||
integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==
|
||||
@ -5970,6 +6369,11 @@ retry@0.12.0:
|
||||
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
|
||||
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
|
||||
|
||||
rfc5646@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rfc5646/-/rfc5646-2.0.0.tgz#ac0c67b6cd04411ef7c80751ba159d9371ce116c"
|
||||
integrity sha1-rAxnts0EQR73yAdRuhWdk3HOEWw=
|
||||
|
||||
rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@~2.6.2:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
||||
@ -6069,6 +6473,11 @@ scheduler@^0.11.2:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
seed-random@~2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54"
|
||||
integrity sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=
|
||||
|
||||
semver-compare@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
||||
@ -6105,6 +6514,11 @@ send@0.16.2:
|
||||
range-parser "~1.2.0"
|
||||
statuses "~1.4.0"
|
||||
|
||||
serialize-error@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-3.0.0.tgz#80100282b09be33c611536f50033481cb9cc87cf"
|
||||
integrity sha512-+y3nkkG/go1Vdw+2f/+XUXM1DXX1XcxTl99FfiD/OEPUNw4uo0i6FKABfTAN5ZcgGtjTRZcEbxcE/jtXbEY19A==
|
||||
|
||||
serve-static@1.13.2:
|
||||
version "1.13.2"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1"
|
||||
@ -6265,6 +6679,11 @@ source-map-url@^0.4.0:
|
||||
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
|
||||
integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
|
||||
|
||||
source-map@0.5.6:
|
||||
version "0.5.6"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
|
||||
integrity sha1-dc449SvwczxafwwRjYEzSiu19BI=
|
||||
|
||||
source-map@^0.5.0, source-map@^0.5.6:
|
||||
version "0.5.7"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||
@ -6348,11 +6767,45 @@ sshpk@^1.7.0:
|
||||
safer-buffer "^2.0.2"
|
||||
tweetnacl "~0.14.0"
|
||||
|
||||
stack-chain@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-2.0.0.tgz#d73d1172af89565f07438b5bcc086831b6689b2d"
|
||||
integrity sha512-GGrHXePi305aW7XQweYZZwiRwR7Js3MWoK/EHzzB9ROdc75nCnjSJVi21rdAGxFl+yCx2L2qdfl5y7NO4lTyqg==
|
||||
|
||||
stack-generator@^2.0.1:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.3.tgz#bb74385c67ffc4ccf3c4dee5831832d4e509c8a0"
|
||||
integrity sha512-kdzGoqrnqsMxOEuXsXyQTmvWXZmG0f3Ql2GDx5NtmZs59sT2Bt9Vdyq0XdtxUi58q/+nxtbF9KOQ9HkV1QznGg==
|
||||
dependencies:
|
||||
stackframe "^1.0.4"
|
||||
|
||||
stack-utils@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8"
|
||||
integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==
|
||||
|
||||
stackframe@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.0.4.tgz#357b24a992f9427cba6b545d96a14ed2cbca187b"
|
||||
integrity sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw==
|
||||
|
||||
stacktrace-gps@^3.0.1:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.0.2.tgz#33f8baa4467323ab2bd1816efa279942ba431ccc"
|
||||
integrity sha512-9o+nWhiz5wFnrB3hBHs2PTyYrS60M1vvpSzHxwxnIbtY2q9Nt51hZvhrG1+2AxD374ecwyS+IUwfkHRE/2zuGg==
|
||||
dependencies:
|
||||
source-map "0.5.6"
|
||||
stackframe "^1.0.4"
|
||||
|
||||
stacktrace-js@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.0.tgz#776ca646a95bc6c6b2b90776536a7fc72c6ddb58"
|
||||
integrity sha1-d2ymRqlbxsayuQd2U2p/xyxt21g=
|
||||
dependencies:
|
||||
error-stack-parser "^2.0.1"
|
||||
stack-generator "^2.0.1"
|
||||
stacktrace-gps "^3.0.1"
|
||||
|
||||
static-extend@^0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
|
||||
@ -6381,6 +6834,11 @@ streamsearch@0.1.2:
|
||||
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
|
||||
integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=
|
||||
|
||||
string-argv@0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.1.tgz#66bd5ae3823708eaa1916fa5412703150d4ddfaf"
|
||||
integrity sha512-El1Va5ehZ0XTj3Ekw4WFidXvTmt9SrC0+eigdojgtJMVtPkF0qbBe9fyNSl9eQf+kUHnTSQxdQYzuHfZy8V+DQ==
|
||||
|
||||
string-length@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
|
||||
@ -6584,6 +7042,20 @@ text-table@^0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
|
||||
|
||||
thenify-all@^1.0.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
|
||||
integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=
|
||||
dependencies:
|
||||
thenify ">= 3.1.0 < 4"
|
||||
|
||||
"thenify@>= 3.1.0 < 4":
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839"
|
||||
integrity sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=
|
||||
dependencies:
|
||||
any-promise "^1.0.0"
|
||||
|
||||
throat@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
|
||||
@ -6599,6 +7071,14 @@ timed-out@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
|
||||
integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=
|
||||
|
||||
title-case@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa"
|
||||
integrity sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
upper-case "^1.0.3"
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
@ -6843,6 +7323,11 @@ update-notifier@^2.5.0:
|
||||
semver-diff "^2.0.0"
|
||||
xdg-basedir "^3.0.0"
|
||||
|
||||
upper-case@^1.0.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
|
||||
integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
|
||||
|
||||
uri-js@^4.2.1, uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
@ -6875,6 +7360,11 @@ use@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
|
||||
|
||||
util-arity@^1.0.2:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/util-arity/-/util-arity-1.1.0.tgz#59d01af1fdb3fede0ac4e632b0ab5f6ce97c9330"
|
||||
integrity sha1-WdAa8f2z/t4KxOYysKtfbOl8kzA=
|
||||
|
||||
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
@ -6923,7 +7413,7 @@ vary@^1, vary@~1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
|
||||
|
||||
verror@1.10.0:
|
||||
verror@1.10.0, verror@^1.9.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
|
||||
integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
|
||||
@ -6932,6 +7422,75 @@ verror@1.10.0:
|
||||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
vocabs-as@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vocabs-as/-/vocabs-as-3.0.0.tgz#0dd0549cecb331ba4e917d2c5a4e83b146865c23"
|
||||
integrity sha512-Dfze+B0CYZzhSK12jWvbxaL8/vXPnlzhhqhQTrEVxkGht+qzU4MmSLXSomQrdiSNKokVVtt16tyKoJWBW9TdNQ==
|
||||
dependencies:
|
||||
activitystreams-context ">=3.0.0"
|
||||
vocabs ">=0.11.2"
|
||||
|
||||
vocabs-asx@^0.11.1:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/vocabs-asx/-/vocabs-asx-0.11.1.tgz#6667e4e174dc4556722b6cb1b9619fb16491519a"
|
||||
integrity sha1-Zmfk4XTcRVZyK2yxuWGfsWSRUZo=
|
||||
dependencies:
|
||||
vocabs ">=0.11.1"
|
||||
|
||||
vocabs-interval@^0.11.1:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/vocabs-interval/-/vocabs-interval-0.11.1.tgz#1c009421f3e88a307aafbb75bfa670ff0f4f6d3c"
|
||||
integrity sha1-HACUIfPoijB6r7t1v6Zw/w9PbTw=
|
||||
dependencies:
|
||||
vocabs ">=0.11.1"
|
||||
|
||||
vocabs-ldp@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/vocabs-ldp/-/vocabs-ldp-0.1.0.tgz#da1728df560471750dfc7050e7e2df1bab901ce6"
|
||||
integrity sha1-2hco31YEcXUN/HBQ5+LfG6uQHOY=
|
||||
dependencies:
|
||||
vocabs ">=0.11.1"
|
||||
|
||||
vocabs-owl@^0.11.1:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/vocabs-owl/-/vocabs-owl-0.11.1.tgz#2355bbd27bfc19c5992d98079bbab3d7d65459e9"
|
||||
integrity sha1-I1W70nv8GcWZLZgHm7qz19ZUWek=
|
||||
dependencies:
|
||||
vocabs ">=0.11.1"
|
||||
|
||||
vocabs-rdf@^0.11.1:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/vocabs-rdf/-/vocabs-rdf-0.11.1.tgz#c7fa91d83b050ffb7b98ce2c72ab25c6fbcd1194"
|
||||
integrity sha1-x/qR2DsFD/t7mM4scqslxvvNEZQ=
|
||||
dependencies:
|
||||
vocabs ">=0.11.1"
|
||||
|
||||
vocabs-rdfs@^0.11.1:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/vocabs-rdfs/-/vocabs-rdfs-0.11.1.tgz#2e2df56ae0de008585b21057570386018da455bf"
|
||||
integrity sha1-Li31auDeAIWFshBXVwOGAY2kVb8=
|
||||
dependencies:
|
||||
vocabs ">=0.11.1"
|
||||
|
||||
vocabs-social@^0.11.1:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/vocabs-social/-/vocabs-social-0.11.1.tgz#d28545868cce325ba0c88e394f3de6e03fad85b1"
|
||||
integrity sha1-0oVFhozOMlugyI45Tz3m4D+thbE=
|
||||
dependencies:
|
||||
vocabs ">=0.11.1"
|
||||
|
||||
vocabs-xsd@^0.11.1:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/vocabs-xsd/-/vocabs-xsd-0.11.1.tgz#20e201d8fd0fd330d6650d9061fda60baae6cd6c"
|
||||
integrity sha1-IOIB2P0P0zDWZQ2QYf2mC6rmzWw=
|
||||
dependencies:
|
||||
vocabs ">=0.11.1"
|
||||
|
||||
vocabs@>=0.11.1, vocabs@>=0.11.2:
|
||||
version "0.11.2"
|
||||
resolved "https://registry.yarnpkg.com/vocabs/-/vocabs-0.11.2.tgz#8944b40f11d415f07db6e259804024a1dbfaa4d4"
|
||||
integrity sha512-OIon2MWA21ZO42UBsTa5DuMsk5zv72DxMdQNvLsPN1M9GrjVTovn3LgWUZdPVnKBpdWhqWV7Mfbq/Sh0vkHIBw==
|
||||
|
||||
w3c-hr-time@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045"
|
||||
@ -7125,6 +7684,11 @@ xmlbuilder@~9.0.1:
|
||||
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
|
||||
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
|
||||
|
||||
xmldom@0.1.19:
|
||||
version "0.1.19"
|
||||
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.19.tgz#631fc07776efd84118bf25171b37ed4d075a0abc"
|
||||
integrity sha1-Yx/Ad3bv2EEYvyUXGzftTQdaCrw=
|
||||
|
||||
xtend@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user