Merge pull request #27 from bluesky-social/ts-server
Convert server to Typescript
This commit is contained in:
commit
a70dc03ccc
@ -120,7 +120,7 @@ Binary [CAR file](https://github.com/ipld/go-car) representing a valid user stor
|
||||
A valid Ucan with attenuation for the following resource:
|
||||
```
|
||||
{
|
||||
'twitter': '${USERNAME}'
|
||||
'bluesky': '${USERNAME}'
|
||||
'cap': 'POST'
|
||||
}
|
||||
```
|
||||
|
@ -7,7 +7,7 @@
|
||||
"@ipld/car": "^3.2.3",
|
||||
"@ipld/dag-cbor": "^7.0.0",
|
||||
"axios": "^0.24.0",
|
||||
"multiformats": "^9.5.6",
|
||||
"multiformats": "^9.6.2",
|
||||
"ucans": "^0.9.0-alpha3"
|
||||
}
|
||||
}
|
||||
|
29
common/src/bluesky-capability.ts
Normal file
29
common/src/bluesky-capability.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { capabilities, Capability, CapabilitySemantics, CapabilityEscalation, Chained } from "ucans"
|
||||
|
||||
export interface BlueskyCapability {
|
||||
bluesky: string
|
||||
cap: "POST"
|
||||
}
|
||||
|
||||
export const blueskySemantics: CapabilitySemantics<BlueskyCapability> = {
|
||||
|
||||
tryParsing(cap: Capability): BlueskyCapability | null {
|
||||
if (typeof cap.bluesky === "string" && cap.cap === "POST") {
|
||||
return {
|
||||
bluesky: cap.bluesky,
|
||||
cap: cap.cap,
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
tryDelegating<T extends BlueskyCapability>(parentCap: T, childCap: T): T | null | CapabilityEscalation<BlueskyCapability> {
|
||||
// potency is always "POST" for now, so doesn't need to be checked
|
||||
return childCap.bluesky === parentCap.bluesky ? childCap : null
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
export function blueskyCapabilities(ucan: Chained) {
|
||||
return capabilities(ucan, blueskySemantics)
|
||||
}
|
@ -5,3 +5,4 @@ export * from './types'
|
||||
export * as check from './type-check'
|
||||
export * as service from './service'
|
||||
export * as util from './util'
|
||||
export * as ucanCheck from './ucan-checks'
|
||||
|
@ -1,9 +1,9 @@
|
||||
import * as car from '@ipld/car'
|
||||
import { CarWriter } from '@ipld/car'
|
||||
import { BlockWriter } from '@ipld/car/lib/writer-browser'
|
||||
import { CID } from 'multiformats/cid'
|
||||
import { flattenUint8Arrays } from './util'
|
||||
import { streamToArray } from './util'
|
||||
|
||||
let globalDB: MemoryDB | null = null
|
||||
export class MemoryDB {
|
||||
|
||||
map: Map<string, any>
|
||||
@ -12,11 +12,18 @@ export class MemoryDB {
|
||||
this.map = new Map()
|
||||
}
|
||||
|
||||
async get(k: CID) {
|
||||
static getGlobal(): MemoryDB {
|
||||
if (globalDB === null) {
|
||||
globalDB = new MemoryDB()
|
||||
}
|
||||
return globalDB
|
||||
}
|
||||
|
||||
async get(k: CID): Promise<Uint8Array> {
|
||||
return this.map.get(k.toString())
|
||||
}
|
||||
|
||||
async put(k: CID, v: Uint8Array) {
|
||||
async put(k: CID, v: Uint8Array): Promise<void> {
|
||||
this.map.set(k.toString(), v)
|
||||
}
|
||||
|
||||
@ -34,11 +41,7 @@ export class MemoryDB {
|
||||
}
|
||||
|
||||
async getCarFile(root: CID): Promise<Uint8Array> {
|
||||
const arrays = []
|
||||
for await (const chunk of this.getCarStream(root)) {
|
||||
arrays.push(chunk)
|
||||
}
|
||||
return flattenUint8Arrays(arrays)
|
||||
return streamToArray(this.getCarStream(root))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ const SERVER_URL = 'http://localhost:2583'
|
||||
const THIRD_PARTY_URL = 'http://localhost:2584'
|
||||
|
||||
export const register = async (car: Uint8Array, authToken: string): Promise<void> => {
|
||||
await axios.post(`${SERVER_URL}/register`, car, {
|
||||
await axios.post(`${SERVER_URL}/user/register`, car, {
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Authorization': `Bearer ${authToken}`
|
||||
@ -13,7 +13,7 @@ export const register = async (car: Uint8Array, authToken: string): Promise<void
|
||||
}
|
||||
|
||||
export const updateUser = async (car: Uint8Array, authToken: string): Promise<void> => {
|
||||
await axios.post(`${SERVER_URL}/update`, car, {
|
||||
await axios.post(`${SERVER_URL}/user/update`, car, {
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Authorization': `Bearer ${authToken}`
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Request } from "express"
|
||||
import * as ucan from "ucans"
|
||||
import { Chained, isCapabilityEscalation, Ucan } from "ucans"
|
||||
import { twitterCapabilities }from './twitter-capability'
|
||||
import { Chained, isCapabilityEscalation } from "ucans"
|
||||
import { blueskyCapabilities }from './bluesky-capability'
|
||||
|
||||
|
||||
type Check = (ucan: Chained) => Error | null
|
||||
@ -25,6 +25,13 @@ export const checkUcan = async (req: Request, ...checks: Check[]): Promise<Chain
|
||||
return decoded
|
||||
}
|
||||
|
||||
export const isRoot = () => (token: Chained): Error | null => {
|
||||
if (token.proofs && token.proofs.length > 0) {
|
||||
throw new Error("Ucan is an attenuation and not the root")
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const hasAudience = (did: string) => (token: Chained): Error | null => {
|
||||
if(token.audience() !== did) {
|
||||
return new Error("Ucan audience does not match server Did")
|
||||
@ -33,9 +40,9 @@ export const hasAudience = (did: string) => (token: Chained): Error | null => {
|
||||
}
|
||||
|
||||
export const hasPostingPermission = (username: string, rootDid: string) => (token: Chained): Error | null => {
|
||||
for(const cap of twitterCapabilities(token)) {
|
||||
// has a properly formatted capability for posting to user's twitter account
|
||||
if(!isCapabilityEscalation(cap) && cap.capability.twitter === username && cap.capability.cap === 'POST') {
|
||||
for(const cap of blueskyCapabilities(token)) {
|
||||
// has a properly formatted capability for posting to user's bluesky account
|
||||
if(!isCapabilityEscalation(cap) && cap.capability.bluesky === username && cap.capability.cap === 'POST') {
|
||||
if(cap.info.originator !== rootDid) {
|
||||
return new Error(`Posting permission does not come from the user's root DID: ${rootDid}`)
|
||||
} else if (cap.info.expiresAt < Date.now() / 1000) {
|
@ -3,13 +3,16 @@ import IpldStore from "./ipld-store"
|
||||
|
||||
import { CID } from 'multiformats/cid'
|
||||
import { sha256 as blockHasher } from 'multiformats/hashes/sha2'
|
||||
|
||||
import * as blockCodec from '@ipld/dag-cbor'
|
||||
import { CarReader, CarWriter } from '@ipld/car'
|
||||
import { BlockWriter } from '@ipld/car/lib/writer-browser'
|
||||
import * as hashmap from 'ipld-hashmap'
|
||||
|
||||
import { Didable, Keypair } from "ucans"
|
||||
|
||||
import * as hashmap from 'ipld-hashmap'
|
||||
import { User, Post } from "./types"
|
||||
import { CarReader } from '@ipld/car'
|
||||
import { streamToArray } from './util'
|
||||
|
||||
export class UserStore {
|
||||
|
||||
@ -61,7 +64,7 @@ export class UserStore {
|
||||
return new UserStore(db, ipldStore, postMap, root, posts, keypair)
|
||||
}
|
||||
|
||||
static async fromCarFile(buf: Uint8Array, keypair: Keypair) {
|
||||
static async fromCarFile(buf: Uint8Array, db: MemoryDB, keypair: Keypair) {
|
||||
const car = await CarReader.fromBytes(buf)
|
||||
|
||||
const roots = await car.getRoots()
|
||||
@ -70,7 +73,6 @@ export class UserStore {
|
||||
}
|
||||
const root = roots[0]
|
||||
|
||||
const db = new MemoryDB()
|
||||
for await (const block of car.blocks()) {
|
||||
await db.put(block.cid, block.bytes)
|
||||
}
|
||||
@ -118,10 +120,28 @@ export class UserStore {
|
||||
return posts
|
||||
}
|
||||
|
||||
async getCarFile(): Promise<Uint8Array> {
|
||||
return this.db.getCarFile(this.root)
|
||||
getCarStream(): AsyncIterable<Uint8Array> {
|
||||
const writeDB = async (car: BlockWriter) => {
|
||||
const addCid = async (cid: CID) => {
|
||||
car.put({ cid, bytes: await this.db.get(cid) })
|
||||
}
|
||||
await addCid(this.root)
|
||||
const rootObj = await this.ipldStore.getSignedRoot(this.root)
|
||||
await addCid(rootObj.user)
|
||||
for await (const cid of this.postMap.cids()) {
|
||||
await addCid(cid)
|
||||
}
|
||||
car.close()
|
||||
}
|
||||
|
||||
const { writer, out } = CarWriter.create([this.root])
|
||||
writeDB(writer)
|
||||
return out
|
||||
}
|
||||
|
||||
async getCarFile(): Promise<Uint8Array> {
|
||||
return streamToArray(this.getCarStream())
|
||||
}
|
||||
}
|
||||
|
||||
export default UserStore
|
||||
|
@ -10,3 +10,11 @@ export const flattenUint8Arrays = (arrs: Uint8Array[]): Uint8Array => {
|
||||
})
|
||||
return flattened
|
||||
}
|
||||
|
||||
export const streamToArray = async (stream: AsyncIterable<Uint8Array>): Promise<Uint8Array> => {
|
||||
const arrays = []
|
||||
for await (const chunk of stream) {
|
||||
arrays.push(chunk)
|
||||
}
|
||||
return flattenUint8Arrays(arrays)
|
||||
}
|
||||
|
@ -21,9 +21,9 @@ function Register(props: Props) {
|
||||
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault()
|
||||
const keypair = await ucan.EdKeypair.create({ exportable: true })
|
||||
const twitterDid = await service.getServerDid()
|
||||
const blueskyDid = await service.getServerDid()
|
||||
const token = await ucan.build({
|
||||
audience: twitterDid,
|
||||
audience: blueskyDid,
|
||||
issuer: keypair
|
||||
})
|
||||
const userStore = await UserStore.create(username, keypair)
|
||||
|
@ -2,7 +2,7 @@ import styles from "@components/App.module.scss";
|
||||
|
||||
import * as React from "react";
|
||||
|
||||
import { service, UserStore, LocalUser, Post } from '@bluesky-demo/common'
|
||||
import { service, UserStore, MemoryDB, LocalUser, Post } from '@bluesky-demo/common'
|
||||
import * as ucan from 'ucans'
|
||||
|
||||
import App from "@components/App"
|
||||
@ -20,12 +20,12 @@ function Home(props: {}) {
|
||||
const addPost = async (post: Post) => {
|
||||
await store.addPost(post)
|
||||
const car = await store.getCarFile()
|
||||
const twitterDid = await service.getServerDid()
|
||||
const blueskyDid = await service.getServerDid()
|
||||
const token = await ucan.build({
|
||||
audience: twitterDid,
|
||||
audience: blueskyDid,
|
||||
issuer: localUser.keypair,
|
||||
capabilities: [{
|
||||
'twitter': localUser.username,
|
||||
'bluesky': localUser.username,
|
||||
'cap': 'POST'
|
||||
}]
|
||||
})
|
||||
@ -38,8 +38,9 @@ function Home(props: {}) {
|
||||
let userStore: UserStore
|
||||
try {
|
||||
const car = await service.fetchUser(localUser.keypair.did())
|
||||
userStore = await UserStore.fromCarFile(car, localUser.keypair)
|
||||
userStore = await UserStore.fromCarFile(car, MemoryDB.getGlobal(), localUser.keypair)
|
||||
} catch (_err) {
|
||||
// @TODO: show error instead of an empty store
|
||||
userStore = await createNewStore()
|
||||
}
|
||||
setStore(userStore)
|
||||
@ -66,7 +67,7 @@ function Home(props: {}) {
|
||||
audience,
|
||||
issuer: localUser.keypair,
|
||||
capabilities: [{
|
||||
'twitter': localUser.username,
|
||||
'bluesky': localUser.username,
|
||||
'cap': 'POST'
|
||||
}]
|
||||
})
|
||||
@ -102,9 +103,7 @@ function Home(props: {}) {
|
||||
|
||||
// Going to display all users on a server to follow
|
||||
const loadAllUsers = async () => {
|
||||
console.log("In loadAllUsers")
|
||||
let users = await service.fetchUsers()
|
||||
console.log("All users fetched from server:", users)
|
||||
setAllUsers(users)
|
||||
}
|
||||
|
||||
|
@ -246,7 +246,7 @@ var listUsersCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("Error: Non-200 status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
@ -321,7 +321,7 @@ var registerCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", r.Account.Server+"/register", buf)
|
||||
req, err := http.NewRequest("POST", r.Account.Server+"/user/register", buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -431,7 +431,7 @@ func PushUpdate(ctx context.Context, acc *Account, oldroot cid.Cid, bs blockstor
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", acc.Server+"/update", buf)
|
||||
req, err := http.NewRequest("POST", acc.Server+"/user/update", buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
"workspaces": [
|
||||
"common",
|
||||
"frontend",
|
||||
"third-party"
|
||||
"third-party",
|
||||
"server"
|
||||
]
|
||||
}
|
||||
|
22
server/package.json
Normal file
22
server/package.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@bluesky-demo/server",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "esr src/index.ts",
|
||||
"build": "esbuild src/index.ts --outfile=dist/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@bluesky-demo/common": "*",
|
||||
"esbuild": "^0.14.10",
|
||||
"esbuild-runner": "^2.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.2",
|
||||
"multiformats": "^9.6.2",
|
||||
"ucans": "0.9.0-alpha3"
|
||||
}
|
||||
}
|
14
server/src/index.ts
Normal file
14
server/src/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import express from 'express'
|
||||
import cors from 'cors'
|
||||
import Routes from './routes'
|
||||
|
||||
const app = express()
|
||||
app.use(express.json())
|
||||
app.use(cors())
|
||||
|
||||
app.use('/', Routes)
|
||||
|
||||
const PORT = 2583
|
||||
app.listen(PORT, () => {
|
||||
console.log(`🐦 Bluesky server is running at http://localhost:${PORT}`)
|
||||
})
|
12
server/src/routes/index.ts
Normal file
12
server/src/routes/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import express from 'express'
|
||||
import WellKnown from './well-known'
|
||||
import User from './user'
|
||||
import Users from './users'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.use('/.well-known', WellKnown)
|
||||
router.use('/user', User)
|
||||
router.use('/users', Users)
|
||||
|
||||
export default router
|
89
server/src/routes/user.ts
Normal file
89
server/src/routes/user.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import express from 'express'
|
||||
import * as ucan from 'ucans'
|
||||
import { ucanCheck, UserStore, MemoryDB } from '@bluesky-demo/common'
|
||||
import * as UserDids from '../user-dids'
|
||||
import * as UserRoots from '../user-roots'
|
||||
import { readReqBytes } from '../util'
|
||||
import { SERVER_KEYPAIR, SERVER_DID } from '../server-identity'
|
||||
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.post('/register', async (req, res) => {
|
||||
// We look for a simple Ucan with no capabilities as proof the user is in possession of the given key
|
||||
let u: ucan.Chained
|
||||
try {
|
||||
u = await ucanCheck.checkUcan(
|
||||
req,
|
||||
ucanCheck.hasAudience(SERVER_DID),
|
||||
ucanCheck.isRoot()
|
||||
)
|
||||
} catch(err) {
|
||||
return res.status(401).send(`Invalid Ucan: ${err.toString()}`)
|
||||
}
|
||||
|
||||
let userStore: UserStore
|
||||
try {
|
||||
const bytes = await readReqBytes(req)
|
||||
// @TODO: make a read-only vesion of user store that doesn't require keypair
|
||||
userStore = await UserStore.fromCarFile(bytes, MemoryDB.getGlobal(), SERVER_KEYPAIR)
|
||||
}catch(err) {
|
||||
return res.status(400).send("Could not parse UserStore from CAR File")
|
||||
}
|
||||
|
||||
const user = await userStore.getUser()
|
||||
const currDid = await UserDids.get(user.name)
|
||||
if (currDid !== null) {
|
||||
return res.status(409).send(`Username ${user.name} already taken.`)
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
UserDids.set(user.name, u.issuer()),
|
||||
UserRoots.set(u.issuer(), userStore.root)
|
||||
])
|
||||
|
||||
return res.sendStatus(200)
|
||||
})
|
||||
|
||||
router.post('/update', async (req, res) => {
|
||||
let userStore: UserStore
|
||||
try {
|
||||
const bytes = await readReqBytes(req)
|
||||
userStore = await UserStore.fromCarFile(bytes, MemoryDB.getGlobal(), SERVER_KEYPAIR)
|
||||
}catch(err) {
|
||||
return res.status(400).send("Could not parse UserStore from CAR File")
|
||||
}
|
||||
|
||||
const user = await userStore.getUser()
|
||||
const userDid = await UserDids.get(user.name)
|
||||
|
||||
let u: ucan.Chained
|
||||
try {
|
||||
u = await ucanCheck.checkUcan(
|
||||
req,
|
||||
ucanCheck.hasAudience(SERVER_DID),
|
||||
ucanCheck.hasPostingPermission(user.name, userDid)
|
||||
)
|
||||
} catch(err) {
|
||||
return res.status(401).send(err)
|
||||
}
|
||||
|
||||
// @TODO: verify signature on data structure
|
||||
|
||||
await UserRoots.set(userDid, userStore.root)
|
||||
|
||||
return res.sendStatus(200)
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const userRoot = await UserRoots.get(id)
|
||||
|
||||
const userStore = await UserStore.get(userRoot, MemoryDB.getGlobal(), SERVER_KEYPAIR)
|
||||
|
||||
const bytes = await userStore.getCarFile()
|
||||
return res.status(200).send(Buffer.from(bytes))
|
||||
})
|
||||
|
||||
export default router
|
11
server/src/routes/users.ts
Normal file
11
server/src/routes/users.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import express from 'express'
|
||||
import * as UserDids from '../user-dids'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.get('/', async (req, res) => {
|
||||
const dids = await UserDids.listDids()
|
||||
res.status(200).send(dids)
|
||||
})
|
||||
|
||||
export default router
|
28
server/src/routes/well-known.ts
Normal file
28
server/src/routes/well-known.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import express from 'express'
|
||||
import { SERVER_DID } from '../server-identity'
|
||||
import * as UserDids from '../user-dids'
|
||||
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
// Return the server's did
|
||||
router.get('/did.json', (_req, res) => {
|
||||
return res.send({ id: SERVER_DID })}
|
||||
)
|
||||
|
||||
// Retrieve a user's DID by their username
|
||||
router.get('/webfinger', async (req, res) => {
|
||||
const { resource } = req.query
|
||||
if(typeof resource !== 'string') {
|
||||
return res.status(400).send("Bad param: expected `resource` to be a string")
|
||||
}
|
||||
|
||||
const did = await UserDids.get(resource)
|
||||
if (!did) {
|
||||
return res.status(404).send("User DID not found")
|
||||
}
|
||||
// @TODO: sketch out more of a webfinger doc
|
||||
return res.status(200).send({ id: did })
|
||||
})
|
||||
|
||||
export default router
|
6
server/src/server-identity.ts
Normal file
6
server/src/server-identity.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import * as ucan from 'ucans'
|
||||
|
||||
// @TODO: For demo only, do not actually store secret keys in plaintext.
|
||||
const SECRET_KEY = 'JjiTSPfawSBQrxSTEakN8GPvNxwb30MF+R3guCzu78hrzKHjNcFqkF6lTyYuLVpbMCVpPKFJTyju27mw1TA1aQ=='
|
||||
export const SERVER_KEYPAIR = ucan.EdKeypair.fromSecretKey(SECRET_KEY)
|
||||
export const SERVER_DID = SERVER_KEYPAIR.did()
|
14
server/src/user-dids.ts
Normal file
14
server/src/user-dids.ts
Normal file
@ -0,0 +1,14 @@
|
||||
const userDids: {[username: string]: string} = {}
|
||||
|
||||
// these fns will be async in the future
|
||||
export const set = async (username: string, did: string): Promise<void> => {
|
||||
userDids[username] = did
|
||||
}
|
||||
|
||||
export const get = async (username: string): Promise<string | null> => {
|
||||
return userDids[username] || null
|
||||
}
|
||||
|
||||
export const listDids = async (): Promise<string[]> => {
|
||||
return Object.values(userDids)
|
||||
}
|
12
server/src/user-roots.ts
Normal file
12
server/src/user-roots.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { CID } from 'multiformats/cid'
|
||||
|
||||
const userRoots: {[did: string]: CID} = {}
|
||||
|
||||
// these fns will be async in the future
|
||||
export const set = async (did: string, cid: CID): Promise<void> => {
|
||||
userRoots[did] = cid
|
||||
}
|
||||
|
||||
export const get = async (did: string): Promise<CID | null> => {
|
||||
return userRoots[did] || null
|
||||
}
|
17
server/src/util.ts
Normal file
17
server/src/util.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Request } from "express";
|
||||
|
||||
export const readReqBytes = async (req: Request): Promise<Uint8Array> => {
|
||||
return new Promise((resolve) => {
|
||||
const chunks: Buffer[] = []
|
||||
|
||||
req.on('data', (chunk) => {
|
||||
chunks.push(chunk)
|
||||
})
|
||||
|
||||
req.on('end', () => {
|
||||
resolve(
|
||||
new Uint8Array(Buffer.concat(chunks))
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
25
third-party/src/index.ts
vendored
25
third-party/src/index.ts
vendored
@ -1,13 +1,12 @@
|
||||
import express from 'express'
|
||||
import cors from 'cors'
|
||||
import * as ucan from 'ucans'
|
||||
import * as check from './ucan-checks'
|
||||
import { service, UserStore } from '@bluesky-demo/common'
|
||||
import { service, UserStore, MemoryDB, ucanCheck } from '@bluesky-demo/common'
|
||||
|
||||
// WARNING: For demo only, do not actually store secret keys in plaintext.
|
||||
const SECRET_KEY = 'I0HyDksQcCRdJBGVuE78Ts34SzyF7+xNprEQw/IRa51OuFZQc5ugqfgjeWRMehyfr7A1vXICRoUD5kqVadsRHA=='
|
||||
const SERVER_KEY = ucan.EdKeypair.fromSecretKey(SECRET_KEY)
|
||||
const SERVER_DID = SERVER_KEY.did()
|
||||
const SERVER_KEYPAIR = ucan.EdKeypair.fromSecretKey(SECRET_KEY)
|
||||
const SERVER_DID = SERVER_KEYPAIR.did()
|
||||
|
||||
const app = express()
|
||||
app.use(express.json())
|
||||
@ -25,7 +24,7 @@ app.post('/post', async (req, res) => {
|
||||
return res.status(400).send("Bad param: 'username' should be a string")
|
||||
}
|
||||
|
||||
// Get the user's root DID from twitter
|
||||
// Get the user's root DID from bluesky
|
||||
const userDid = await service.fetchUserDid(username)
|
||||
if (!userDid) {
|
||||
return res.status(401).send("User DID not found")
|
||||
@ -34,23 +33,27 @@ app.post('/post', async (req, res) => {
|
||||
// Check that it's a valid ucan, that it's meant for this server, and that it has permission to post for the given username
|
||||
let u: ucan.Chained
|
||||
try {
|
||||
u = await check.checkUcan(req, check.hasAudience(SERVER_DID), check.hasPostingPermission(username, userDid))
|
||||
u = await ucanCheck.checkUcan(
|
||||
req,
|
||||
ucanCheck.hasAudience(SERVER_DID),
|
||||
ucanCheck.hasPostingPermission(username, userDid)
|
||||
)
|
||||
} catch (err) {
|
||||
return res.status(401).send(err)
|
||||
}
|
||||
|
||||
// Create a new Ucan to send to twitter using the user's Ucan as proof that we have permission to post on their behalf
|
||||
const twitterDid = await service.getServerDid()
|
||||
// Create a new Ucan to send to bluesky using the user's Ucan as proof that we have permission to post on their behalf
|
||||
const blueskyDid = await service.getServerDid()
|
||||
const extendUcan = await ucan.build({
|
||||
audience: twitterDid,
|
||||
issuer: SERVER_KEY,
|
||||
audience: blueskyDid,
|
||||
issuer: SERVER_KEYPAIR,
|
||||
capabilities: u.attenuation(),
|
||||
proofs: [u.encoded()]
|
||||
})
|
||||
const encoded = ucan.encode(extendUcan)
|
||||
|
||||
const car = await service.fetchUser(userDid)
|
||||
const userStore = await UserStore.fromCarFile(car, SERVER_KEY)
|
||||
const userStore = await UserStore.fromCarFile(car, MemoryDB.getGlobal(), SERVER_KEYPAIR)
|
||||
await userStore.addPost({
|
||||
user: username,
|
||||
text: `Hey there! I'm posting on ${username}'s behalf`
|
||||
|
29
third-party/src/twitter-capability.ts
vendored
29
third-party/src/twitter-capability.ts
vendored
@ -1,29 +0,0 @@
|
||||
import { capabilities, Capability, CapabilitySemantics, CapabilityEscalation, Chained } from "ucans"
|
||||
|
||||
export interface TwitterCapability {
|
||||
twitter: string
|
||||
cap: "POST"
|
||||
}
|
||||
|
||||
export const twitterSemantics: CapabilitySemantics<TwitterCapability> = {
|
||||
|
||||
tryParsing(cap: Capability): TwitterCapability | null {
|
||||
if (typeof cap.twitter === "string" && cap.cap === "POST") {
|
||||
return {
|
||||
twitter: cap.twitter,
|
||||
cap: cap.cap,
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
tryDelegating<T extends TwitterCapability>(parentCap: T, childCap: T): T | null | CapabilityEscalation<TwitterCapability> {
|
||||
// potency is always "POST" for now, so doesn't need to be checked
|
||||
return childCap.twitter === parentCap.twitter ? childCap : null
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
export function twitterCapabilities(ucan: Chained) {
|
||||
return capabilities(ucan, twitterSemantics)
|
||||
}
|
254
yarn.lock
254
yarn.lock
@ -20,9 +20,9 @@
|
||||
integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
|
||||
|
||||
"@babel/highlight@^7.10.4":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b"
|
||||
integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==
|
||||
version "7.16.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88"
|
||||
integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.16.7"
|
||||
chalk "^2.0.0"
|
||||
@ -233,9 +233,9 @@
|
||||
integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==
|
||||
|
||||
"@types/express-serve-static-core@^4.17.18":
|
||||
version "4.17.27"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.27.tgz#7a776191e47295d2a05962ecbb3a4ce97e38b401"
|
||||
integrity sha512-e/sVallzUTPdyOTiqi8O8pMdBBphscvI6E4JYaKlja4Lm+zh7UFSSdW5VMkRbhDtmrONqOUHOXRguPsDckzxNA==
|
||||
version "4.17.28"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8"
|
||||
integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
"@types/qs" "*"
|
||||
@ -257,9 +257,9 @@
|
||||
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
|
||||
|
||||
"@types/node@*":
|
||||
version "17.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.8.tgz#50d680c8a8a78fe30abe6906453b21ad8ab0ad7b"
|
||||
integrity sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==
|
||||
version "17.0.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.12.tgz#f7aa331b27f08244888c47b7df126184bc2339c5"
|
||||
integrity sha512-4YpbAsnJXWYK/fpTVFlMIcUIho2AYCi4wg5aNPrG1ng7fn/1/RZfCIpRCiBX+12RVa34RluilnvCqD+g3KiSiA==
|
||||
|
||||
"@types/prop-types@*":
|
||||
version "15.7.4"
|
||||
@ -641,14 +641,14 @@ camelcase@^6.2.0:
|
||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||
|
||||
caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228:
|
||||
version "1.0.30001296"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001296.tgz#d99f0f3bee66544800b93d261c4be55a35f1cec8"
|
||||
integrity sha512-WfrtPEoNSoeATDlf4y3QvkwiELl9GyPLISV5GejTbbQRtQx4LhsXmc9IQ6XCL2d7UxCyEzToEZNMeqR79OUw8Q==
|
||||
version "1.0.30001302"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001302.tgz#da57ce61c51177ef3661eeed7faef392d3790aaa"
|
||||
integrity sha512-YYTMO+tfwvgUN+1ZnRViE53Ma1S/oETg+J2lISsqi/ZTNThj3ZYBOKP2rHwJc37oCsPqAzJ3w2puZHn0xlLPPw==
|
||||
|
||||
cborg@^1.5.4, cborg@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/cborg/-/cborg-1.6.0.tgz#07975681efb2a1ee33c696c5b7bf62092fddc542"
|
||||
integrity sha512-zNgXCO0jlGDKh8EQO34PziChBZhOoLf3qjkS0VtKy4jEKjEX/PbrKYQ1QP+960rxmC3fUuN1yOeq4Frf2E9RTw==
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/cborg/-/cborg-1.6.1.tgz#d5c6f6cfa8539588918c3936814caddc65e8e5ec"
|
||||
integrity sha512-dOGlTG610S6t3j7EYFxPBH7KiF1OlSAdWtMI4Iv1dabcId/L/nUvkfOEPge+vDp9YoPerEMiDoy5+Vm2oEqmQw==
|
||||
|
||||
chalk@2.4.2, chalk@^2.0.0:
|
||||
version "2.4.2"
|
||||
@ -691,9 +691,9 @@ chokidar@3.5.1:
|
||||
fsevents "~2.3.1"
|
||||
|
||||
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.2:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
|
||||
integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
||||
dependencies:
|
||||
anymatch "~3.1.2"
|
||||
braces "~3.0.2"
|
||||
@ -1032,9 +1032,9 @@ ee-first@1.1.1:
|
||||
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
|
||||
|
||||
electron-to-chromium@^1.3.723:
|
||||
version "1.4.35"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.35.tgz#69aabb73d7030733e71c1e970ec16f5ceefbaea4"
|
||||
integrity sha512-wzTOMh6HGFWeALMI3bif0mzgRrVGyP1BdFRx7IvWukFrSC5QVQELENuy+Fm2dCrAdQH9T3nuqr07n94nPDFBWA==
|
||||
version "1.4.53"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.53.tgz#5d80a91c399b44952ef485857fb5b9d4387d2e60"
|
||||
integrity sha512-rFveSKQczlcav+H3zkKqykU6ANseFwXwkl855jOIap5/0gnEcuIhv2ecz6aoTrXavF6I/CEBeRnBnkB51k06ew==
|
||||
|
||||
elliptic@^6.5.3:
|
||||
version "6.5.4"
|
||||
@ -1126,75 +1126,75 @@ es6-object-assign@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
||||
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
|
||||
|
||||
esbuild-android-arm64@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.10.tgz#c854db57dc2d4df6f4f62185ca812f26a132bf1e"
|
||||
integrity sha512-vzkTafHKoiMX4uIN1kBnE/HXYLpNT95EgGanVk6DHGeYgDolU0NBxjO7yZpq4ZGFPOx8384eAdDrBYhO11TAlQ==
|
||||
esbuild-android-arm64@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.14.tgz#3705f32f209deeb11c275af47c298c8783dd5f0c"
|
||||
integrity sha512-be/Uw6DdpQiPfula1J4bdmA+wtZ6T3BRCZsDMFB5X+k0Gp8TIh9UvmAcqvKNnbRAafSaXG3jPCeXxDKqnc8hFQ==
|
||||
|
||||
esbuild-darwin-64@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.10.tgz#c44fab6b8bfc83e5d083f513e4acbff14fb82eac"
|
||||
integrity sha512-DJwzFVB95ZV7C3PQbf052WqaUuuMFXJeZJ0LKdnP1w+QOU0rlbKfX0tzuhoS//rOXUj1TFIwRuRsd0FX6skR7A==
|
||||
esbuild-darwin-64@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.14.tgz#c07e4eae6d938300a2d330ea82494c55bcea84e5"
|
||||
integrity sha512-BEexYmjWafcISK8cT6O98E3TfcLuZL8DKuubry6G54n2+bD4GkoRD6HYUOnCkfl2p7jodA+s4369IjSFSWjtHg==
|
||||
|
||||
esbuild-darwin-arm64@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.10.tgz#9454b3763b36407dc395c4c3529fb5ddd4a6225f"
|
||||
integrity sha512-RNaaoZDg3nsqs5z56vYCjk/VJ76npf752W0rOaCl5lO5TsgV9zecfdYgt7dtUrIx8b7APhVaNYud+tGsDOVC9g==
|
||||
esbuild-darwin-arm64@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.14.tgz#a8631e13a51a6f784fb0906e2a64c6ab53988755"
|
||||
integrity sha512-tnBKm41pDOB1GtZ8q/w26gZlLLRzVmP8fdsduYjvM+yFD7E2DLG4KbPAqFMWm4Md9B+DitBglP57FY7AznxbTg==
|
||||
|
||||
esbuild-freebsd-64@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.10.tgz#04eef46d5d5e4152c6b5a6a12f432db0fe7c89de"
|
||||
integrity sha512-10B3AzW894u6bGZZhWiJOHw1uEHb4AFbUuBdyml1Ht0vIqd+KqWW+iY/yMwQAzILr2WJZqEhbOXRkJtY8aRqOw==
|
||||
esbuild-freebsd-64@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.14.tgz#c280c2b944746b27ee6c6487c2691865c90bed2e"
|
||||
integrity sha512-Q9Rx6sgArOHalQtNwAaIzJ6dnQ8A+I7f/RsQsdkS3JrdzmnlFo8JEVofTmwVQLoIop7OKUqIVOGP4PoQcwfVMA==
|
||||
|
||||
esbuild-freebsd-arm64@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.10.tgz#67ca88529543ada948737c95819253ead16494a7"
|
||||
integrity sha512-mSQrKB7UaWvuryBTCo9leOfY2uEUSimAvcKIaUWbk5Hth9Sg+Try+qNA/NibPgs/vHkX0KFo/Rce6RPea+P15g==
|
||||
esbuild-freebsd-arm64@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.14.tgz#aa4e21276efcf20e5ab2487e91ca1d789573189b"
|
||||
integrity sha512-TJvq0OpLM7BkTczlyPIphcvnwrQwQDG1HqxzoYePWn26SMUAlt6wrLnEvxdbXAvNvDLVzG83kA+JimjK7aRNBA==
|
||||
|
||||
esbuild-linux-32@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.10.tgz#8f3d5fb0b9b616d6b604da781d71767d7679f64f"
|
||||
integrity sha512-lktF09JgJLZ63ANYHIPdYe339PDuVn19Q/FcGKkXWf+jSPkn5xkYzAabboNGZNUgNqSJ/vY7VrOn6UrBbJjgYA==
|
||||
esbuild-linux-32@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.14.tgz#3db4d929239203ce38a9060d5419ac6a6d28846c"
|
||||
integrity sha512-h/CrK9Baimt5VRbu8gqibWV7e1P9l+mkanQgyOgv0Ng3jHT1NVFC9e6rb1zbDdaJVmuhWX5xVliUA5bDDCcJeg==
|
||||
|
||||
esbuild-linux-64@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.10.tgz#c1c60a079c4709164bdd89fbb007a2edeea7c34a"
|
||||
integrity sha512-K+gCQz2oLIIBI8ZM77e9sYD5/DwEpeYCrOQ2SYXx+R4OU2CT9QjJDi4/OpE7ko4AcYMlMW7qrOCuLSgAlEj4Wg==
|
||||
esbuild-linux-64@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.14.tgz#f880026254c1f565a7a10fdebb7cff9b083a127d"
|
||||
integrity sha512-IC+wAiIg/egp5OhQp4W44D9PcBOH1b621iRn1OXmlLzij9a/6BGr9NMIL4CRwz4j2kp3WNZu5sT473tYdynOuQ==
|
||||
|
||||
esbuild-linux-arm64@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.10.tgz#d8f1f89190f6d8b6e06a1214aafba454e5daa990"
|
||||
integrity sha512-+qocQuQvcp5wo/V+OLXxqHPc+gxHttJEvbU/xrCGE03vIMqraL4wMua8JQx0SWEnJCWP+Nhf//v8OSwz1Xr5kA==
|
||||
esbuild-linux-arm64@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.14.tgz#a34bc3076e50b109c3b8c8bad9c146e35942322b"
|
||||
integrity sha512-6QVul3RI4M5/VxVIRF/I5F+7BaxzR3DfNGoqEVSCZqUbgzHExPn+LXr5ly1C7af2Kw4AHpo+wDqx8A4ziP9avw==
|
||||
|
||||
esbuild-linux-arm@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.10.tgz#43192a00019a4553fb44e67f628fff0f560f16c2"
|
||||
integrity sha512-BYa60dZ/KPmNKYxtHa3LSEdfKWHcm/RzP0MjB4AeBPhjS0D6/okhaBesZIY9kVIGDyeenKsJNOmnVt4+dhNnvQ==
|
||||
esbuild-linux-arm@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.14.tgz#231ffd12fef69ee06365d4c94b69850e4830e927"
|
||||
integrity sha512-gxpOaHOPwp7zSmcKYsHrtxabScMqaTzfSQioAMUaB047YiMuDBzqVcKBG8OuESrYkGrL9DDljXr/mQNg7pbdaQ==
|
||||
|
||||
esbuild-linux-mips64le@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.10.tgz#f57bb8b2f1a3063cc91cfd787c8a9130cf863c16"
|
||||
integrity sha512-nmUd2xoBXpGo4NJCEWoaBj+n4EtDoLEvEYc8Z3aSJrY0Oa6s04czD1flmhd0I/d6QEU8b7GQ9U0g/rtBfhtxBg==
|
||||
esbuild-linux-mips64le@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.14.tgz#bd00570e3a30422224b732c7a5f262146c357403"
|
||||
integrity sha512-4Jl5/+xoINKbA4cesH3f4R+q0vltAztZ6Jm8YycS8lNhN1pgZJBDxWfI6HUMIAdkKlIpR1PIkA9aXQgZ8sxFAg==
|
||||
|
||||
esbuild-linux-ppc64le@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.10.tgz#becd965bfe3425d41e026f1c4678b393127fecbd"
|
||||
integrity sha512-vsOWZjm0rZix7HSmqwPph9arRVCyPtUpcURdayQDuIhMG2/UxJxpbdRaa//w4zYqcJzAWwuyH2PAlyy0ZNuxqQ==
|
||||
esbuild-linux-ppc64le@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.14.tgz#430609413fd9e04d9def4e3f06726b031b23d825"
|
||||
integrity sha512-BitW37GxeebKxqYNl4SVuSdnIJAzH830Lr6Mkq3pBHXtzQay0vK+IeOR/Ele1GtNVJ+/f8wYM53tcThkv5SC5w==
|
||||
|
||||
esbuild-linux-s390x@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.10.tgz#cc4228ac842febc48b84757814bed964a619be62"
|
||||
integrity sha512-knArKKZm0ypIYWOWyOT7+accVwbVV1LZnl2FWWy05u9Tyv5oqJ2F5+X2Vqe/gqd61enJXQWqoufXopvG3zULOg==
|
||||
esbuild-linux-s390x@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.14.tgz#2f0d8cbfe53cf3cb97f6372549a41a8051dbd689"
|
||||
integrity sha512-vLj6p76HOZG3wfuTr5MyO3qW5iu8YdhUNxuY+tx846rPo7GcKtYSPMusQjeVEfZlJpSYoR+yrNBBxq+qVF9zrw==
|
||||
|
||||
esbuild-netbsd-64@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.10.tgz#6ec50d9e4547a7579f447307b19f66bbedfd868b"
|
||||
integrity sha512-6Gg8neVcLeyq0yt9bZpReb8ntZ8LBEjthxrcYWVrBElcltnDjIy1hrzsujt0+sC2rL+TlSsE9dzgyuvlDdPp2w==
|
||||
esbuild-netbsd-64@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.14.tgz#3e44de35e1add7e9582f3c0d2558d86aafbc813b"
|
||||
integrity sha512-fn8looXPQhpVqUyCBWUuPjesH+yGIyfbIQrLKG05rr1Kgm3rZD/gaYrd3Wpmf5syVZx70pKZPvdHp8OTA+y7cQ==
|
||||
|
||||
esbuild-openbsd-64@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.10.tgz#925ac3d2326cc219d514e1ca806e80e5143aa043"
|
||||
integrity sha512-9rkHZzp10zI90CfKbFrwmQjqZaeDmyQ6s9/hvCwRkbOCHuto6RvMYH9ghQpcr5cUxD5OQIA+sHXi0zokRNXjcg==
|
||||
esbuild-openbsd-64@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.14.tgz#04710ef1d01cd9f15d54f50d20b5a3778f8306a2"
|
||||
integrity sha512-HdAnJ399pPff3SKbd8g+P4o5znseni5u5n5rJ6Z7ouqOdgbOwHe2ofZbMow17WMdNtz1IyOZk2Wo9Ve6/lZ4Rg==
|
||||
|
||||
esbuild-runner@^2.2.1:
|
||||
version "2.2.1"
|
||||
@ -1204,49 +1204,49 @@ esbuild-runner@^2.2.1:
|
||||
source-map-support "0.5.19"
|
||||
tslib "2.3.1"
|
||||
|
||||
esbuild-sunos-64@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.10.tgz#8d3576d8cac5c4f9f2a84be81b9078d424dbc739"
|
||||
integrity sha512-mEU+pqkhkhbwpJj5DiN3vL0GUFR/yrL3qj8ER1amIVyRibKbj02VM1QaIuk1sy5DRVIKiFXXgCaHvH3RNWCHIw==
|
||||
esbuild-sunos-64@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.14.tgz#8e583dd92c5c7ac4303ddc37f588e44211e04e19"
|
||||
integrity sha512-bmDHa99ulsGnYlh/xjBEfxoGuC8CEG5OWvlgD+pF7bKKiVTbtxqVCvOGEZeoDXB+ja6AvHIbPxrEE32J+m5nqQ==
|
||||
|
||||
esbuild-windows-32@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.10.tgz#8a67fca4cb594a340566d66eef3f568f65057a48"
|
||||
integrity sha512-Z5DieUL1N6s78dOSdL95KWf8Y89RtPGxIoMF+LEy8ChDsX+pZpz6uAVCn+YaWpqQXO+2TnrcbgBIoprq2Mco1g==
|
||||
esbuild-windows-32@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.14.tgz#6d293ddfb71229f21cc13d85d5d2f43e8131693b"
|
||||
integrity sha512-6tVooQcxJCNenPp5GHZBs/RLu31q4B+BuF4MEoRxswT+Eq2JGF0ZWDRQwNKB8QVIo3t6Svc5wNGez+CwKNQjBg==
|
||||
|
||||
esbuild-windows-64@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.10.tgz#5e6d7c475ff6a71ad0aa4046894364e6c40a9249"
|
||||
integrity sha512-LE5Mm62y0Bilu7RDryBhHIX8rK3at5VwJ6IGM3BsASidCfOBTzqcs7Yy0/Vkq39VKeTmy9/66BAfVoZRNznoDw==
|
||||
esbuild-windows-64@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.14.tgz#08a36844b69542f8ec1cb33a5ddcea02b9d0b2e8"
|
||||
integrity sha512-kl3BdPXh0/RD/dad41dtzj2itMUR4C6nQbXQCyYHHo4zoUoeIXhpCrSl7BAW1nv5EFL8stT1V+TQVXGZca5A2A==
|
||||
|
||||
esbuild-windows-arm64@0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.10.tgz#50ab9a83f6ccf71c272e58489ecc4d7375075f32"
|
||||
integrity sha512-OJOyxDtabvcUYTc+O4dR0JMzLBz6G9+gXIHA7Oc5d5Fv1xiYa0nUeo8+W5s2e6ZkPRdIwOseYoL70rZz80S5BA==
|
||||
esbuild-windows-arm64@0.14.14:
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.14.tgz#ca747ce4066d5b8a79dbe48fe6ecd92d202e5366"
|
||||
integrity sha512-dCm1wTOm6HIisLanmybvRKvaXZZo4yEVrHh1dY0v582GThXJOzuXGja1HIQgV09RpSHYRL3m4KoUBL00l6SWEg==
|
||||
|
||||
esbuild@^0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.10.tgz#10268d2b576b25ed6f8554553413988628a7767b"
|
||||
integrity sha512-ibZb+NwFqBwHHJlpnFMtg4aNmVK+LUtYMFC9CuKs6lDCBEvCHpqCFZFEirpqt1jOugwKGx8gALNGvX56lQyfew==
|
||||
version "0.14.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.14.tgz#3b99f20d628013c3e2ae90e67687e03f1d6eb071"
|
||||
integrity sha512-aiK4ddv+uui0k52OqSHu4xxu+SzOim7Rlz4i25pMEiC8rlnGU0HJ9r+ZMfdWL5bzifg+nhnn7x4NSWTeehYblg==
|
||||
optionalDependencies:
|
||||
esbuild-android-arm64 "0.14.10"
|
||||
esbuild-darwin-64 "0.14.10"
|
||||
esbuild-darwin-arm64 "0.14.10"
|
||||
esbuild-freebsd-64 "0.14.10"
|
||||
esbuild-freebsd-arm64 "0.14.10"
|
||||
esbuild-linux-32 "0.14.10"
|
||||
esbuild-linux-64 "0.14.10"
|
||||
esbuild-linux-arm "0.14.10"
|
||||
esbuild-linux-arm64 "0.14.10"
|
||||
esbuild-linux-mips64le "0.14.10"
|
||||
esbuild-linux-ppc64le "0.14.10"
|
||||
esbuild-linux-s390x "0.14.10"
|
||||
esbuild-netbsd-64 "0.14.10"
|
||||
esbuild-openbsd-64 "0.14.10"
|
||||
esbuild-sunos-64 "0.14.10"
|
||||
esbuild-windows-32 "0.14.10"
|
||||
esbuild-windows-64 "0.14.10"
|
||||
esbuild-windows-arm64 "0.14.10"
|
||||
esbuild-android-arm64 "0.14.14"
|
||||
esbuild-darwin-64 "0.14.14"
|
||||
esbuild-darwin-arm64 "0.14.14"
|
||||
esbuild-freebsd-64 "0.14.14"
|
||||
esbuild-freebsd-arm64 "0.14.14"
|
||||
esbuild-linux-32 "0.14.14"
|
||||
esbuild-linux-64 "0.14.14"
|
||||
esbuild-linux-arm "0.14.14"
|
||||
esbuild-linux-arm64 "0.14.14"
|
||||
esbuild-linux-mips64le "0.14.14"
|
||||
esbuild-linux-ppc64le "0.14.14"
|
||||
esbuild-linux-s390x "0.14.14"
|
||||
esbuild-netbsd-64 "0.14.14"
|
||||
esbuild-openbsd-64 "0.14.14"
|
||||
esbuild-sunos-64 "0.14.14"
|
||||
esbuild-windows-32 "0.14.14"
|
||||
esbuild-windows-64 "0.14.14"
|
||||
esbuild-windows-arm64 "0.14.14"
|
||||
|
||||
escalade@^3.1.1:
|
||||
version "3.1.1"
|
||||
@ -1360,9 +1360,9 @@ find-up@^4.0.0:
|
||||
path-exists "^4.0.0"
|
||||
|
||||
follow-redirects@^1.14.4:
|
||||
version "1.14.6"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd"
|
||||
integrity sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==
|
||||
version "1.14.7"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
|
||||
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
|
||||
|
||||
foreach@^2.0.5:
|
||||
version "2.0.5"
|
||||
@ -2061,15 +2061,15 @@ ms@2.1.3, ms@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
multiformats@^9.1.2, multiformats@^9.4.2, multiformats@^9.5.4, multiformats@^9.5.6:
|
||||
version "9.5.6"
|
||||
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.5.6.tgz#316c702117b21f5f53ed0f2857a117b361ae5af2"
|
||||
integrity sha512-FAM5VaBdBl9Ya+DssXsWgZ+Nao07RtFUc+w0IgEuyHrg1b1ms+QoMUw8h7rtKcLuaJwfYJdf3Ms1205nq+j+Zg==
|
||||
multiformats@^9.1.2, multiformats@^9.4.2, multiformats@^9.5.4, multiformats@^9.6.2:
|
||||
version "9.6.2"
|
||||
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.6.2.tgz#3dd8f696171a367fa826b7c432851da850eb115e"
|
||||
integrity sha512-1dKng7RkBelbEZQQD2zvdzYKgUmtggpWl+GXQBYhnEGGkV6VIYfWgV3VSeyhcUFFEelI5q4D0etCJZ7fbuiamQ==
|
||||
|
||||
nanoid@^3.1.23:
|
||||
version "3.1.30"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362"
|
||||
integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c"
|
||||
integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==
|
||||
|
||||
native-url@0.3.4:
|
||||
version "0.3.4"
|
||||
@ -3109,9 +3109,9 @@ typedarray-to-buffer@^3.1.5:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
typescript@^4.4.2:
|
||||
version "4.5.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8"
|
||||
integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==
|
||||
version "4.5.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
||||
|
||||
ucans@0.9.0-alpha3, ucans@^0.9.0-alpha3:
|
||||
version "0.9.0-alpha3"
|
||||
|
Loading…
x
Reference in New Issue
Block a user