kopia lustrzana https://github.com/cloudflare/wildebeest
Merge branch 'main' into docs
commit
6e519832f4
|
@ -16,6 +16,8 @@ module.exports = {
|
|||
'no-var': 'error',
|
||||
'@typescript-eslint/no-unsafe-return': 'error',
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'no-console': 'off',
|
||||
'no-constant-condition': 'off',
|
||||
/*
|
||||
Note: the following rules have been set to off so that linting
|
||||
can pass with the current code, but we need to gradually
|
||||
|
@ -26,13 +28,11 @@ module.exports = {
|
|||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/restrict-plus-operands': 'off',
|
||||
'no-constant-condition': 'off',
|
||||
'@typescript-eslint/await-thenable': 'off',
|
||||
'@typescript-eslint/require-await': 'off',
|
||||
'@typescript-eslint/restrict-template-expressions': 'off',
|
||||
'@typescript-eslint/no-misused-promises': 'off',
|
||||
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
|
||||
'no-console': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-inferrable-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
|
|
|
@ -23,7 +23,11 @@ jobs:
|
|||
- name: Build
|
||||
run: yarn build
|
||||
- name: Check formatting
|
||||
run: yarn pretty
|
||||
run: yarn pretty
|
||||
- name: Check backend linting
|
||||
run: yarn lint:backend
|
||||
- name: Check functions linting
|
||||
run: yarn lint:functions
|
||||
- name: Run API tests
|
||||
run: yarn test
|
||||
|
||||
|
@ -40,5 +44,7 @@ jobs:
|
|||
run: yarn && yarn --cwd frontend
|
||||
- name: Initialize local database
|
||||
run: yarn database:create-mock
|
||||
- name: Check frontend linting
|
||||
run: yarn lint:frontend
|
||||
- name: Run UI tests
|
||||
run: yarn test:ui
|
||||
|
|
|
@ -22,8 +22,8 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
env:
|
||||
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
|
||||
CF_DEPLOY_DOMAIN: ${{ secrets.CF_DEPLOY_DOMAIN }}
|
||||
CF_ZONE_ID: ${{ vars.CF_ZONE_ID }}
|
||||
CF_DEPLOY_DOMAIN: ${{ vars.CF_DEPLOY_DOMAIN }}
|
||||
|
||||
# this is needed to get the lowercase version of the repository_owner name
|
||||
# TODO: switch to some lowercase function in the future when Actions supports it
|
||||
|
@ -109,6 +109,30 @@ jobs:
|
|||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
if: ${{ env.tfstate_kv != '' }}
|
||||
|
||||
- name: download VAPID keys
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
with:
|
||||
command: kv:key get --namespace-id=${{ env.tfstate_kv }} vapid_jwk | jq . > ./tf/vapid_jwk
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
preCommands: |
|
||||
echo "*** pre commands ***"
|
||||
apt-get update && apt-get -y install jq
|
||||
echo "******"
|
||||
env:
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
continue-on-error: true
|
||||
if: ${{ env.tfstate_kv != '' }}
|
||||
|
||||
- name: generate VAPID keys if needed
|
||||
run: |
|
||||
sudo chmod 777 ./tf/vapid_jwk || true
|
||||
|
||||
if [ ! -s ./tf/vapid_jwk ]
|
||||
then
|
||||
node ./scripts/generate-vapid-keys.mjs > ./tf/vapid_jwk
|
||||
echo "VAPID keys generated"
|
||||
fi
|
||||
|
||||
- name: Configure
|
||||
run: terraform plan && terraform apply -auto-approve
|
||||
continue-on-error: true
|
||||
|
@ -116,11 +140,14 @@ jobs:
|
|||
env:
|
||||
TF_VAR_cloudflare_account_id: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
TF_VAR_cloudflare_api_token: ${{ secrets.CF_API_TOKEN }}
|
||||
TF_VAR_cloudflare_zone_id: ${{ secrets.CF_ZONE_ID }}
|
||||
TF_VAR_cloudflare_deploy_domain: ${{ secrets.CF_DEPLOY_DOMAIN }}
|
||||
TF_VAR_cloudflare_zone_id: ${{ vars.CF_ZONE_ID }}
|
||||
TF_VAR_cloudflare_deploy_domain: ${{ vars.CF_DEPLOY_DOMAIN }}
|
||||
TF_VAR_gh_username: ${{ env.OWNER_LOWER }}
|
||||
TF_VAR_d1_id: ${{ env.d1_id }}
|
||||
TF_VAR_access_auth_domain: ${{ env.auth_domain }}
|
||||
TF_VAR_wd_instance_title: ${{ vars.INSTANCE_TITLE }}
|
||||
TF_VAR_wd_admin_email: ${{ vars.ADMIN_EMAIL }}
|
||||
TF_VAR_wd_instance_description: ${{ vars.INSTANCE_DESCR }}
|
||||
|
||||
- name: retrieve Terraform state KV namespace
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
|
@ -134,6 +161,14 @@ jobs:
|
|||
env:
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
if: ${{ env.tfstate_kv == '' }}
|
||||
|
||||
- name: store VAPID keys state
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
with:
|
||||
command: kv:key put --namespace-id=${{ env.tfstate_kv }} vapid_jwk --path=./tf/vapid_jwk
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
env:
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
- name: store Terraform state
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
|
|
|
@ -70,7 +70,7 @@ Wildebeest uses [Deploy to Workers](https://deploy.workers.cloudflare.com/) to a
|
|||
|
||||
**Click here to start the installation.**
|
||||
|
||||
[<img src="https://deploy.workers.cloudflare.com/button"/>](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/wildebeest&authed=true&fields=%7B%22name%22:%22Zone%20tag%22,%22secret%22:%22CF_ZONE_ID%22,%22descr%22:%22Zone%20tag%22%7D&fields=%7B%22name%22:%22Domain%22,%22secret%22:%22CF_DEPLOY_DOMAIN%22,%22descr%22:%22Domain%20on%20which%20your%20instance%20will%20be%20running%22%7D&fields=%7B%22name%22:%22Instance%20title%22,%22secret%22:%22INSTANCE_TITLE%22,%22descr%22:%22Title%20of%20your%20instance%22%7D&fields=%7B%22name%22:%22Administrator%20email%22,%22secret%22:%22ADMIN_EMAIL%22,%22descr%22:%22An%20email%20address%20that%20can%20be%20messaged%20regarding%20inquiries%20or%20issues%22%7D&fields=%7B%22name%22:%22Instance%20description%22,%22secret%22:%22INSTANCE_DESCR%22,%22descr%22:%22A%20short,%20plain-text%20description%20of%20your%20instance%22%7D)
|
||||
[<img src="https://deploy.workers.cloudflare.com/button"/>](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/wildebeest&authed=true&fields={%22name%22:%22Zone%20ID%22,%22secret%22:%22CF_ZONE_ID%22,%22descr%22:%22Get%20your%20Zone%20ID%20from%20the%20Cloudflare%20Dashboard%22}&fields={%22name%22:%22Domain%22,%22secret%22:%22CF_DEPLOY_DOMAIN%22,%22descr%22:%22Domain%20on%20which%20your%20instance%20will%20be%20running%22}&fields={%22name%22:%22Instance%20title%22,%22secret%22:%22INSTANCE_TITLE%22,%22descr%22:%22Title%20of%20your%20instance%22}&fields={%22name%22:%22Administrator%20Email%22,%22secret%22:%22ADMIN_EMAIL%22,%22descr%22:%22An%20Email%20address%20that%20can%20be%20messaged%20regarding%20inquiries%20or%20issues%22}&fields={%22name%22:%22Instance%20description%22,%22secret%22:%22INSTANCE_DESCR%22,%22descr%22:%22A%20short,%20plain-text%20description%20of%20your%20instance%22})
|
||||
|
||||
Please pay attention to all the steps involved in the installation process.
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as actors from 'wildebeest/backend/src/activitypub/actors'
|
||||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
import { addObjectInOutbox } from 'wildebeest/backend/src/activitypub/actors/outbox'
|
||||
import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import * as objects from 'wildebeest/backend/src/activitypub/objects'
|
||||
|
@ -79,7 +80,14 @@ export function makeGetActorAsId(activity: Activity): Function {
|
|||
}
|
||||
}
|
||||
|
||||
export async function handle(domain: string, activity: Activity, db: D1Database, userKEK: string) {
|
||||
export async function handle(
|
||||
domain: string,
|
||||
activity: Activity,
|
||||
db: D1Database,
|
||||
userKEK: string,
|
||||
adminEmail: string,
|
||||
vapidKeys: JWK
|
||||
) {
|
||||
// The `object` field of the activity is required to be an object, with an
|
||||
// `id` and a `type` field.
|
||||
const requireComplexObject = () => {
|
||||
|
@ -183,7 +191,7 @@ export async function handle(domain: string, activity: Activity, db: D1Database,
|
|||
const notifId = await createNotification(db, 'mention', person, fromActor, obj)
|
||||
await Promise.all([
|
||||
await addObjectInInbox(db, person, obj),
|
||||
await sendMentionNotification(db, fromActor, person, notifId),
|
||||
await sendMentionNotification(db, fromActor, person, notifId, adminEmail, vapidKeys),
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -228,7 +236,7 @@ export async function handle(domain: string, activity: Activity, db: D1Database,
|
|||
|
||||
// Notify the user
|
||||
const notifId = await insertFollowNotification(db, receiver, originalActor)
|
||||
await sendFollowNotification(db, originalActor, receiver, notifId)
|
||||
await sendFollowNotification(db, originalActor, receiver, notifId, adminEmail, vapidKeys)
|
||||
} else {
|
||||
console.warn(`actor ${objectId} not found`)
|
||||
}
|
||||
|
@ -282,7 +290,7 @@ export async function handle(domain: string, activity: Activity, db: D1Database,
|
|||
// Store the reblog for counting
|
||||
insertReblog(db, fromActor, obj),
|
||||
|
||||
sendReblogNotification(db, fromActor, targetActor, notifId),
|
||||
sendReblogNotification(db, fromActor, targetActor, notifId, adminEmail, vapidKeys),
|
||||
])
|
||||
break
|
||||
}
|
||||
|
@ -312,7 +320,7 @@ export async function handle(domain: string, activity: Activity, db: D1Database,
|
|||
insertLike(db, fromActor, obj),
|
||||
])
|
||||
|
||||
await sendLikeNotification(db, fromActor, targetActor, notifId)
|
||||
await sendLikeNotification(db, fromActor, targetActor, notifId, adminEmail, vapidKeys)
|
||||
break
|
||||
}
|
||||
|
||||
|
|
|
@ -1,53 +1,10 @@
|
|||
export type InstanceConfig = {
|
||||
title?: string
|
||||
email?: string
|
||||
description?: string
|
||||
thumbnail?: string
|
||||
}
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
|
||||
const DEFAULT_THUMBNAIL =
|
||||
export const DEFAULT_THUMBNAIL =
|
||||
'https://imagedelivery.net/NkfPDviynOyTAOI79ar_GQ/b24caf12-5230-48c4-0bf7-2f40063bd400/thumbnail'
|
||||
|
||||
export async function configure(db: D1Database, data: InstanceConfig) {
|
||||
const sql = `
|
||||
INSERT INTO instance_config
|
||||
VALUES ('title', ?),
|
||||
('email', ?),
|
||||
('thumbnail', ?),
|
||||
('description', ?);
|
||||
`
|
||||
|
||||
const { success, error } = await db
|
||||
.prepare(sql)
|
||||
.bind(data.title, data.email, DEFAULT_THUMBNAIL, data.description)
|
||||
.run()
|
||||
if (!success) {
|
||||
throw new Error('SQL error: ' + error)
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateVAPIDKeys(db: D1Database) {
|
||||
const keyPair = (await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, [
|
||||
'sign',
|
||||
'verify',
|
||||
])) as CryptoKeyPair
|
||||
const jwk = await crypto.subtle.exportKey('jwk', keyPair.privateKey)
|
||||
|
||||
const sql = `
|
||||
INSERT INTO instance_config
|
||||
VALUES ('vapid_jwk', ?);
|
||||
`
|
||||
|
||||
const { success, error } = await db.prepare(sql).bind(JSON.stringify(jwk)).run()
|
||||
if (!success) {
|
||||
throw new Error('SQL error: ' + error)
|
||||
}
|
||||
}
|
||||
|
||||
export async function get(db: D1Database, name: string): Promise<string> {
|
||||
const row: { value: string } = await db.prepare('SELECT value FROM instance_config WHERE key = ?').bind(name).first()
|
||||
if (!row) {
|
||||
throw new Error(`configuration not found: ${name}`)
|
||||
}
|
||||
return row.value
|
||||
export function getVAPIDKeys(env: Env): JWK {
|
||||
const value: JWK = JSON.parse(env.VAPID_JWK)
|
||||
return value
|
||||
}
|
||||
|
|
|
@ -29,10 +29,6 @@ export function userConflict(): Response {
|
|||
return generateErrorResponse(`User already exists or conflicts`, 403)
|
||||
}
|
||||
|
||||
export function timelineMissing(): Response {
|
||||
return generateErrorResponse(`The timeline is invalid or being regenerated`, 404)
|
||||
}
|
||||
|
||||
export function clientUnknown(): Response {
|
||||
return generateErrorResponse(`The client is unknown or invalid`, 403)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { Object } from 'wildebeest/backend/src/activitypub/objects'
|
||||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
import * as actors from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { urlToHandle } from 'wildebeest/backend/src/utils/handle'
|
||||
import { loadExternalMastodonAccount } from 'wildebeest/backend/src/mastodon/account'
|
||||
|
@ -9,8 +10,6 @@ import { WebPushResult } from 'wildebeest/backend/src/webpush/webpushinfos'
|
|||
import type { Actor } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import type { NotificationType, Notification } from 'wildebeest/backend/src/types/notification'
|
||||
import { getSubscriptionForAllClients } from 'wildebeest/backend/src/mastodon/subscription'
|
||||
import { getVAPIDKeys } from 'wildebeest/backend/src/mastodon/subscription'
|
||||
import * as config from 'wildebeest/backend/src/config'
|
||||
|
||||
export async function createNotification(
|
||||
db: D1Database,
|
||||
|
@ -43,9 +42,14 @@ export async function insertFollowNotification(db: D1Database, actor: Actor, fro
|
|||
return row.id
|
||||
}
|
||||
|
||||
export async function sendFollowNotification(db: D1Database, follower: Actor, actor: Actor, notificationId: string) {
|
||||
const sub = await config.get(db, 'email')
|
||||
|
||||
export async function sendFollowNotification(
|
||||
db: D1Database,
|
||||
follower: Actor,
|
||||
actor: Actor,
|
||||
notificationId: string,
|
||||
adminEmail: string,
|
||||
vapidKeys: JWK
|
||||
) {
|
||||
const data = {
|
||||
preferred_locale: 'en',
|
||||
notification_type: 'follow',
|
||||
|
@ -58,16 +62,21 @@ export async function sendFollowNotification(db: D1Database, follower: Actor, ac
|
|||
const message: WebPushMessage = {
|
||||
data: JSON.stringify(data),
|
||||
urgency: 'normal',
|
||||
sub,
|
||||
sub: adminEmail,
|
||||
ttl: 60 * 24 * 7,
|
||||
}
|
||||
|
||||
return sendNotification(db, actor, message)
|
||||
return sendNotification(db, actor, message, vapidKeys)
|
||||
}
|
||||
|
||||
export async function sendLikeNotification(db: D1Database, fromActor: Actor, actor: Actor, notificationId: string) {
|
||||
const sub = await config.get(db, 'email')
|
||||
|
||||
export async function sendLikeNotification(
|
||||
db: D1Database,
|
||||
fromActor: Actor,
|
||||
actor: Actor,
|
||||
notificationId: string,
|
||||
adminEmail: string,
|
||||
vapidKeys: JWK
|
||||
) {
|
||||
const data = {
|
||||
preferred_locale: 'en',
|
||||
notification_type: 'favourite',
|
||||
|
@ -80,16 +89,21 @@ export async function sendLikeNotification(db: D1Database, fromActor: Actor, act
|
|||
const message: WebPushMessage = {
|
||||
data: JSON.stringify(data),
|
||||
urgency: 'normal',
|
||||
sub,
|
||||
sub: adminEmail,
|
||||
ttl: 60 * 24 * 7,
|
||||
}
|
||||
|
||||
return sendNotification(db, actor, message)
|
||||
return sendNotification(db, actor, message, vapidKeys)
|
||||
}
|
||||
|
||||
export async function sendMentionNotification(db: D1Database, fromActor: Actor, actor: Actor, notificationId: string) {
|
||||
const sub = await config.get(db, 'email')
|
||||
|
||||
export async function sendMentionNotification(
|
||||
db: D1Database,
|
||||
fromActor: Actor,
|
||||
actor: Actor,
|
||||
notificationId: string,
|
||||
adminEmail: string,
|
||||
vapidKeys: JWK
|
||||
) {
|
||||
const data = {
|
||||
preferred_locale: 'en',
|
||||
notification_type: 'mention',
|
||||
|
@ -102,16 +116,21 @@ export async function sendMentionNotification(db: D1Database, fromActor: Actor,
|
|||
const message: WebPushMessage = {
|
||||
data: JSON.stringify(data),
|
||||
urgency: 'normal',
|
||||
sub,
|
||||
sub: adminEmail,
|
||||
ttl: 60 * 24 * 7,
|
||||
}
|
||||
|
||||
return sendNotification(db, actor, message)
|
||||
return sendNotification(db, actor, message, vapidKeys)
|
||||
}
|
||||
|
||||
export async function sendReblogNotification(db: D1Database, fromActor: Actor, actor: Actor, notificationId: string) {
|
||||
const sub = await config.get(db, 'email')
|
||||
|
||||
export async function sendReblogNotification(
|
||||
db: D1Database,
|
||||
fromActor: Actor,
|
||||
actor: Actor,
|
||||
notificationId: string,
|
||||
adminEmail: string,
|
||||
vapidKeys: JWK
|
||||
) {
|
||||
const data = {
|
||||
preferred_locale: 'en',
|
||||
notification_type: 'reblog',
|
||||
|
@ -124,15 +143,14 @@ export async function sendReblogNotification(db: D1Database, fromActor: Actor, a
|
|||
const message: WebPushMessage = {
|
||||
data: JSON.stringify(data),
|
||||
urgency: 'normal',
|
||||
sub,
|
||||
sub: adminEmail,
|
||||
ttl: 60 * 24 * 7,
|
||||
}
|
||||
|
||||
return sendNotification(db, actor, message)
|
||||
return sendNotification(db, actor, message, vapidKeys)
|
||||
}
|
||||
|
||||
async function sendNotification(db: D1Database, actor: Actor, message: WebPushMessage) {
|
||||
const vapidKeys = await getVAPIDKeys(db)
|
||||
async function sendNotification(db: D1Database, actor: Actor, message: WebPushMessage, vapidKeys: JWK) {
|
||||
const subscriptions = await getSubscriptionForAllClients(db, actor)
|
||||
|
||||
const promises = subscriptions.map(async (subscription) => {
|
||||
|
|
|
@ -9,8 +9,6 @@ import * as media from 'wildebeest/backend/src/media/'
|
|||
import type { MastodonStatus } from 'wildebeest/backend/src/types'
|
||||
import { parseHandle } from '../utils/parse'
|
||||
import { urlToHandle } from '../utils/handle'
|
||||
import { getLikes } from './like'
|
||||
import { getReblogs } from './reblog'
|
||||
|
||||
export function getMentions(input: string): Array<Handle> {
|
||||
const mentions: Array<Handle> = []
|
||||
|
|
|
@ -124,15 +124,6 @@ function subscriptionFromRow(row: any): Subscription {
|
|||
}
|
||||
}
|
||||
|
||||
export async function getVAPIDKeys(db: D1Database): Promise<JWK> {
|
||||
const row: any = await db.prepare("SELECT value FROM instance_config WHERE key = 'vapid_jwk'").first()
|
||||
if (!row) {
|
||||
throw new Error('missing VAPID keys')
|
||||
}
|
||||
const value: JWK = JSON.parse(row.value)
|
||||
return value
|
||||
}
|
||||
|
||||
export function VAPIDPublicKey(keys: JWK): string {
|
||||
return b64ToUrlEncoded(exportPublicKeyPair(keys))
|
||||
}
|
||||
|
|
|
@ -56,8 +56,6 @@ export async function main(context: EventContext<Env, any, any>) {
|
|||
url.pathname === '/api/v1/timelines/public' ||
|
||||
url.pathname === '/api/v1/custom_emojis' ||
|
||||
url.pathname === '/.well-known/webfinger' ||
|
||||
url.pathname === '/start-instance' || // Access is required by the handler
|
||||
url.pathname === '/start-instance-test-access' || // Access is required by the handler
|
||||
url.pathname === '/api/v1/trends/statuses' ||
|
||||
url.pathname === '/api/v1/trends/links' ||
|
||||
url.pathname.startsWith('/ap/') // all ActivityPub endpoints
|
||||
|
|
|
@ -10,4 +10,10 @@ export interface Env {
|
|||
DOMAIN: string
|
||||
ACCESS_AUD: string
|
||||
ACCESS_AUTH_DOMAIN: string
|
||||
|
||||
// Configuration for the instance
|
||||
INSTANCE_TITLE: string
|
||||
ADMIN_EMAIL: string
|
||||
INSTANCE_DESCR: string
|
||||
VAPID_JWK: string
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ export async function readBody<T>(request: Request): Promise<T> {
|
|||
return request.json<T>()
|
||||
} else {
|
||||
const form = await request.formData()
|
||||
let out: any = {}
|
||||
const out: any = {}
|
||||
|
||||
for (const [key, value] of form) {
|
||||
out[key] = value
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { makeDB, isUrlValid } from './utils'
|
||||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
import { addFollowing } from 'wildebeest/backend/src/mastodon/follow'
|
||||
import { createPerson } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { configure, generateVAPIDKeys } from 'wildebeest/backend/src/config'
|
||||
import * as activityHandler from 'wildebeest/backend/src/activitypub/activities/handle'
|
||||
import { createPublicNote } from 'wildebeest/backend/src/activitypub/objects/note'
|
||||
import { addObjectInOutbox } from 'wildebeest/backend/src/activitypub/actors/outbox'
|
||||
|
@ -13,8 +13,10 @@ import * as ap_outbox from 'wildebeest/functions/ap/users/[id]/outbox'
|
|||
import * as ap_outbox_page from 'wildebeest/functions/ap/users/[id]/outbox/page'
|
||||
|
||||
const userKEK = 'test_kek5'
|
||||
const vapidKeys = {} as JWK
|
||||
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
|
||||
const domain = 'cloudflare.com'
|
||||
const adminEmail = 'admin@example.com'
|
||||
|
||||
describe('ActivityPub', () => {
|
||||
test('fetch non-existant user by id', async () => {
|
||||
|
@ -74,7 +76,7 @@ describe('ActivityPub', () => {
|
|||
},
|
||||
}
|
||||
|
||||
await activityHandler.handle(domain, activity, db, userKEK)
|
||||
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
||||
|
||||
const row = await db
|
||||
.prepare(`SELECT target_actor_id, state FROM actor_following WHERE actor_id=?`)
|
||||
|
@ -96,7 +98,7 @@ describe('ActivityPub', () => {
|
|||
object: 'a',
|
||||
}
|
||||
|
||||
await assert.rejects(activityHandler.handle(domain, activity, db, userKEK), {
|
||||
await assert.rejects(activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys), {
|
||||
message: '`activity.object` must be of type object',
|
||||
})
|
||||
})
|
||||
|
@ -114,7 +116,7 @@ describe('ActivityPub', () => {
|
|||
object: 'a',
|
||||
}
|
||||
|
||||
await assert.rejects(activityHandler.handle(domain, activity, db, userKEK), {
|
||||
await assert.rejects(activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys), {
|
||||
message: '`activity.object` must be of type object',
|
||||
})
|
||||
})
|
||||
|
@ -131,7 +133,7 @@ describe('ActivityPub', () => {
|
|||
object: 'a',
|
||||
}
|
||||
|
||||
await assert.rejects(activityHandler.handle(domain, activity, db, userKEK), {
|
||||
await assert.rejects(activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys), {
|
||||
message: '`activity.object` must be of type object',
|
||||
})
|
||||
})
|
||||
|
@ -150,7 +152,7 @@ describe('ActivityPub', () => {
|
|||
},
|
||||
}
|
||||
|
||||
await assert.rejects(activityHandler.handle(domain, activity, db, userKEK), {
|
||||
await assert.rejects(activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys), {
|
||||
message: 'object https://example.com/note2 does not exist',
|
||||
})
|
||||
})
|
||||
|
@ -174,7 +176,7 @@ describe('ActivityPub', () => {
|
|||
object: object,
|
||||
}
|
||||
|
||||
await assert.rejects(activityHandler.handle(domain, activity, db, userKEK), {
|
||||
await assert.rejects(activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys), {
|
||||
message: 'actorid mismatch when updating object',
|
||||
})
|
||||
})
|
||||
|
@ -204,7 +206,7 @@ describe('ActivityPub', () => {
|
|||
object: newObject,
|
||||
}
|
||||
|
||||
await activityHandler.handle(domain, activity, db, userKEK)
|
||||
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
||||
|
||||
const updatedObject = await db.prepare('SELECT * FROM objects WHERE original_object_id=?').bind(object.id).first()
|
||||
assert(updatedObject)
|
||||
|
@ -276,8 +278,6 @@ describe('ActivityPub', () => {
|
|||
}
|
||||
|
||||
const db = await makeDB()
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
await generateVAPIDKeys(db)
|
||||
await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||
|
||||
const activity: any = {
|
||||
|
@ -287,7 +287,7 @@ describe('ActivityPub', () => {
|
|||
cc: [],
|
||||
object: objectId,
|
||||
}
|
||||
await activityHandler.handle(domain, activity, db, userKEK)
|
||||
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
||||
|
||||
const object = await db.prepare('SELECT * FROM objects').bind(remoteActorId).first()
|
||||
assert(object)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as activityHandler from 'wildebeest/backend/src/activitypub/activities/handle'
|
||||
import { configure, generateVAPIDKeys } from 'wildebeest/backend/src/config'
|
||||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
import * as ap_followers_page from 'wildebeest/functions/ap/users/[id]/followers/page'
|
||||
import * as ap_following_page from 'wildebeest/functions/ap/users/[id]/following/page'
|
||||
import * as ap_followers from 'wildebeest/functions/ap/users/[id]/followers'
|
||||
|
@ -11,6 +11,8 @@ import { createPerson } from 'wildebeest/backend/src/activitypub/actors'
|
|||
|
||||
const userKEK = 'test_kek10'
|
||||
const domain = 'cloudflare.com'
|
||||
const adminEmail = 'admin@example.com'
|
||||
const vapidKeys = {} as JWK
|
||||
|
||||
describe('ActivityPub', () => {
|
||||
describe('Follow', () => {
|
||||
|
@ -34,8 +36,6 @@ describe('ActivityPub', () => {
|
|||
|
||||
test('Receive follow with Accept reply', async () => {
|
||||
const db = await makeDB()
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
await generateVAPIDKeys(db)
|
||||
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
||||
|
||||
|
@ -46,7 +46,7 @@ describe('ActivityPub', () => {
|
|||
object: actor.id.toString(),
|
||||
}
|
||||
|
||||
await activityHandler.handle(domain, activity, db, userKEK)
|
||||
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
||||
|
||||
const row = await db
|
||||
.prepare(`SELECT target_actor_id, state FROM actor_following WHERE actor_id=?`)
|
||||
|
@ -132,8 +132,6 @@ describe('ActivityPub', () => {
|
|||
|
||||
test('creates a notification', async () => {
|
||||
const db = await makeDB()
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
await generateVAPIDKeys(db)
|
||||
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
||||
|
||||
|
@ -144,7 +142,7 @@ describe('ActivityPub', () => {
|
|||
object: actor.id,
|
||||
}
|
||||
|
||||
await activityHandler.handle(domain, activity, db, userKEK)
|
||||
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
||||
|
||||
const entry = await db.prepare('SELECT * FROM actor_notifications').first()
|
||||
assert.equal(entry.type, 'follow')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { makeDB } from '../utils'
|
||||
import { generateVAPIDKeys, configure } from 'wildebeest/backend/src/config'
|
||||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
import * as objects from 'wildebeest/backend/src/activitypub/objects'
|
||||
import { createPublicNote } from 'wildebeest/backend/src/activitypub/objects/note'
|
||||
import * as ap_inbox from 'wildebeest/functions/ap/users/[id]/inbox'
|
||||
|
@ -8,6 +8,8 @@ import { strict as assert } from 'node:assert/strict'
|
|||
|
||||
const userKEK = 'test_kek9'
|
||||
const domain = 'cloudflare.com'
|
||||
const adminEmail = 'admin@example.com'
|
||||
const vapidKeys = {} as JWK
|
||||
|
||||
const kv_cache: any = {
|
||||
async put() {},
|
||||
|
@ -20,14 +22,22 @@ describe('ActivityPub', () => {
|
|||
const db = await makeDB()
|
||||
|
||||
const activity: any = {}
|
||||
const res = await ap_inbox.handleRequest(domain, db, kv_cache, 'sven', activity, userKEK, waitUntil)
|
||||
const res = await ap_inbox.handleRequest(
|
||||
domain,
|
||||
db,
|
||||
kv_cache,
|
||||
'sven',
|
||||
activity,
|
||||
userKEK,
|
||||
waitUntil,
|
||||
adminEmail,
|
||||
vapidKeys
|
||||
)
|
||||
assert.equal(res.status, 404)
|
||||
})
|
||||
|
||||
test('send Note to inbox stores in DB', async () => {
|
||||
const db = await makeDB()
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
await generateVAPIDKeys(db)
|
||||
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||
|
||||
const activity: any = {
|
||||
|
@ -41,7 +51,17 @@ describe('ActivityPub', () => {
|
|||
content: 'test note',
|
||||
},
|
||||
}
|
||||
const res = await ap_inbox.handleRequest(domain, db, kv_cache, 'sven', activity, userKEK, waitUntil)
|
||||
const res = await ap_inbox.handleRequest(
|
||||
domain,
|
||||
db,
|
||||
kv_cache,
|
||||
'sven',
|
||||
activity,
|
||||
userKEK,
|
||||
waitUntil,
|
||||
adminEmail,
|
||||
vapidKeys
|
||||
)
|
||||
assert.equal(res.status, 200)
|
||||
|
||||
const entry = await db
|
||||
|
@ -81,7 +101,17 @@ describe('ActivityPub', () => {
|
|||
content: 'test note',
|
||||
},
|
||||
}
|
||||
const res = await ap_inbox.handleRequest(domain, db, kv_cache, 'sven', activity, userKEK, waitUntil)
|
||||
const res = await ap_inbox.handleRequest(
|
||||
domain,
|
||||
db,
|
||||
kv_cache,
|
||||
'sven',
|
||||
activity,
|
||||
userKEK,
|
||||
waitUntil,
|
||||
adminEmail,
|
||||
vapidKeys
|
||||
)
|
||||
assert.equal(res.status, 200)
|
||||
|
||||
const entry = await db.prepare('SELECT * FROM outbox_objects WHERE actor_id=?').bind(remoteActorId).first()
|
||||
|
@ -90,8 +120,6 @@ describe('ActivityPub', () => {
|
|||
|
||||
test('local actor sends Note with mention create notification', async () => {
|
||||
const db = await makeDB()
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
await generateVAPIDKeys(db)
|
||||
const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com')
|
||||
const actorB = await createPerson(domain, db, userKEK, 'b@cloudflare.com')
|
||||
|
||||
|
@ -106,7 +134,17 @@ describe('ActivityPub', () => {
|
|||
content: 'test note',
|
||||
},
|
||||
}
|
||||
const res = await ap_inbox.handleRequest(domain, db, kv_cache, 'a', activity, userKEK, waitUntil)
|
||||
const res = await ap_inbox.handleRequest(
|
||||
domain,
|
||||
db,
|
||||
kv_cache,
|
||||
'a',
|
||||
activity,
|
||||
userKEK,
|
||||
waitUntil,
|
||||
adminEmail,
|
||||
vapidKeys
|
||||
)
|
||||
assert.equal(res.status, 200)
|
||||
|
||||
const entry = await db.prepare('SELECT * FROM actor_notifications').first()
|
||||
|
@ -132,8 +170,6 @@ describe('ActivityPub', () => {
|
|||
}
|
||||
|
||||
const db = await makeDB()
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
await generateVAPIDKeys(db)
|
||||
const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com')
|
||||
|
||||
const activity: any = {
|
||||
|
@ -147,7 +183,17 @@ describe('ActivityPub', () => {
|
|||
content: 'test note',
|
||||
},
|
||||
}
|
||||
const res = await ap_inbox.handleRequest(domain, db, kv_cache, 'a', activity, userKEK, waitUntil)
|
||||
const res = await ap_inbox.handleRequest(
|
||||
domain,
|
||||
db,
|
||||
kv_cache,
|
||||
'a',
|
||||
activity,
|
||||
userKEK,
|
||||
waitUntil,
|
||||
adminEmail,
|
||||
vapidKeys
|
||||
)
|
||||
assert.equal(res.status, 200)
|
||||
|
||||
const entry = await db.prepare('SELECT * FROM actors WHERE id=?').bind(actorB).first()
|
||||
|
@ -156,8 +202,6 @@ describe('ActivityPub', () => {
|
|||
|
||||
test('send Note records reply', async () => {
|
||||
const db = await makeDB()
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
await generateVAPIDKeys(db)
|
||||
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||
|
||||
{
|
||||
|
@ -171,7 +215,17 @@ describe('ActivityPub', () => {
|
|||
content: 'post',
|
||||
},
|
||||
}
|
||||
const res = await ap_inbox.handleRequest(domain, db, kv_cache, 'sven', activity, userKEK, waitUntil)
|
||||
const res = await ap_inbox.handleRequest(
|
||||
domain,
|
||||
db,
|
||||
kv_cache,
|
||||
'sven',
|
||||
activity,
|
||||
userKEK,
|
||||
waitUntil,
|
||||
adminEmail,
|
||||
vapidKeys
|
||||
)
|
||||
assert.equal(res.status, 200)
|
||||
}
|
||||
|
||||
|
@ -187,7 +241,17 @@ describe('ActivityPub', () => {
|
|||
content: 'reply',
|
||||
},
|
||||
}
|
||||
const res = await ap_inbox.handleRequest(domain, db, kv_cache, 'sven', activity, userKEK, waitUntil)
|
||||
const res = await ap_inbox.handleRequest(
|
||||
domain,
|
||||
db,
|
||||
kv_cache,
|
||||
'sven',
|
||||
activity,
|
||||
userKEK,
|
||||
waitUntil,
|
||||
adminEmail,
|
||||
vapidKeys
|
||||
)
|
||||
assert.equal(res.status, 200)
|
||||
}
|
||||
|
||||
|
@ -206,8 +270,6 @@ describe('ActivityPub', () => {
|
|||
describe('Announce', () => {
|
||||
test('records reblog in db', async () => {
|
||||
const db = await makeDB()
|
||||
await generateVAPIDKeys(db)
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com')
|
||||
const actorB = await createPerson(domain, db, userKEK, 'b@cloudflare.com')
|
||||
|
||||
|
@ -218,7 +280,17 @@ describe('ActivityPub', () => {
|
|||
actor: actorB.id,
|
||||
object: note.id,
|
||||
}
|
||||
const res = await ap_inbox.handleRequest(domain, db, kv_cache, 'a', activity, userKEK, waitUntil)
|
||||
const res = await ap_inbox.handleRequest(
|
||||
domain,
|
||||
db,
|
||||
kv_cache,
|
||||
'a',
|
||||
activity,
|
||||
userKEK,
|
||||
waitUntil,
|
||||
adminEmail,
|
||||
vapidKeys
|
||||
)
|
||||
assert.equal(res.status, 200)
|
||||
|
||||
const entry = await db.prepare('SELECT * FROM actor_reblogs').first()
|
||||
|
@ -228,8 +300,6 @@ describe('ActivityPub', () => {
|
|||
|
||||
test('creates notification', async () => {
|
||||
const db = await makeDB()
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
await generateVAPIDKeys(db)
|
||||
const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com')
|
||||
const actorB = await createPerson(domain, db, userKEK, 'b@cloudflare.com')
|
||||
|
||||
|
@ -240,7 +310,17 @@ describe('ActivityPub', () => {
|
|||
actor: actorB.id,
|
||||
object: note.id,
|
||||
}
|
||||
const res = await ap_inbox.handleRequest(domain, db, kv_cache, 'a', activity, userKEK, waitUntil)
|
||||
const res = await ap_inbox.handleRequest(
|
||||
domain,
|
||||
db,
|
||||
kv_cache,
|
||||
'a',
|
||||
activity,
|
||||
userKEK,
|
||||
waitUntil,
|
||||
adminEmail,
|
||||
vapidKeys
|
||||
)
|
||||
assert.equal(res.status, 200)
|
||||
|
||||
const entry = await db.prepare('SELECT * FROM actor_notifications').first()
|
||||
|
@ -254,8 +334,6 @@ describe('ActivityPub', () => {
|
|||
describe('Like', () => {
|
||||
test('records like in db', async () => {
|
||||
const db = await makeDB()
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
await generateVAPIDKeys(db)
|
||||
const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com')
|
||||
const actorB = await createPerson(domain, db, userKEK, 'b@cloudflare.com')
|
||||
|
||||
|
@ -266,7 +344,17 @@ describe('ActivityPub', () => {
|
|||
actor: actorB.id,
|
||||
object: note.id,
|
||||
}
|
||||
const res = await ap_inbox.handleRequest(domain, db, kv_cache, 'a', activity, userKEK, waitUntil)
|
||||
const res = await ap_inbox.handleRequest(
|
||||
domain,
|
||||
db,
|
||||
kv_cache,
|
||||
'a',
|
||||
activity,
|
||||
userKEK,
|
||||
waitUntil,
|
||||
adminEmail,
|
||||
vapidKeys
|
||||
)
|
||||
assert.equal(res.status, 200)
|
||||
|
||||
const entry = await db.prepare('SELECT * FROM actor_favourites').first()
|
||||
|
@ -276,8 +364,6 @@ describe('ActivityPub', () => {
|
|||
|
||||
test('creates notification', async () => {
|
||||
const db = await makeDB()
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
await generateVAPIDKeys(db)
|
||||
const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com')
|
||||
const actorB = await createPerson(domain, db, userKEK, 'b@cloudflare.com')
|
||||
|
||||
|
@ -288,7 +374,17 @@ describe('ActivityPub', () => {
|
|||
actor: actorB.id,
|
||||
object: note.id,
|
||||
}
|
||||
const res = await ap_inbox.handleRequest(domain, db, kv_cache, 'a', activity, userKEK, waitUntil)
|
||||
const res = await ap_inbox.handleRequest(
|
||||
domain,
|
||||
db,
|
||||
kv_cache,
|
||||
'a',
|
||||
activity,
|
||||
userKEK,
|
||||
waitUntil,
|
||||
adminEmail,
|
||||
vapidKeys
|
||||
)
|
||||
assert.equal(res.status, 200)
|
||||
|
||||
const entry = await db.prepare('SELECT * FROM actor_notifications').first()
|
||||
|
@ -299,8 +395,6 @@ describe('ActivityPub', () => {
|
|||
|
||||
test('records like in db', async () => {
|
||||
const db = await makeDB()
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
await generateVAPIDKeys(db)
|
||||
const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com')
|
||||
const actorB = await createPerson(domain, db, userKEK, 'b@cloudflare.com')
|
||||
|
||||
|
@ -311,7 +405,17 @@ describe('ActivityPub', () => {
|
|||
actor: actorB.id,
|
||||
object: note.id,
|
||||
}
|
||||
const res = await ap_inbox.handleRequest(domain, db, kv_cache, 'a', activity, userKEK, waitUntil)
|
||||
const res = await ap_inbox.handleRequest(
|
||||
domain,
|
||||
db,
|
||||
kv_cache,
|
||||
'a',
|
||||
activity,
|
||||
userKEK,
|
||||
waitUntil,
|
||||
adminEmail,
|
||||
vapidKeys
|
||||
)
|
||||
assert.equal(res.status, 200)
|
||||
|
||||
const entry = await db.prepare('SELECT * FROM actor_favourites').first()
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { strict as assert } from 'node:assert/strict'
|
||||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import * as v1_instance from 'wildebeest/functions/api/v1/instance'
|
||||
import * as v2_instance from 'wildebeest/functions/api/v2/instance'
|
||||
import * as apps from 'wildebeest/functions/api/v1/apps'
|
||||
|
@ -9,24 +11,29 @@ import { makeDB, assertCORS, assertJSON, assertCache, createTestClient } from '.
|
|||
import { createPerson } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { createSubscription } from '../src/mastodon/subscription'
|
||||
import * as subscription from 'wildebeest/functions/api/v1/push/subscription'
|
||||
import { configure, generateVAPIDKeys } from 'wildebeest/backend/src/config'
|
||||
|
||||
const userKEK = 'test_kek'
|
||||
const domain = 'cloudflare.com'
|
||||
|
||||
async function generateVAPIDKeys(): Promise<JWK> {
|
||||
const keyPair = (await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, [
|
||||
'sign',
|
||||
'verify',
|
||||
])) as CryptoKeyPair
|
||||
const jwk = (await crypto.subtle.exportKey('jwk', keyPair.privateKey)) as JWK
|
||||
return jwk
|
||||
}
|
||||
|
||||
describe('Mastodon APIs', () => {
|
||||
describe('instance', () => {
|
||||
test('return the instance infos v1', async () => {
|
||||
const db = await makeDB()
|
||||
const data = {
|
||||
title: 'title',
|
||||
uri: 'uri',
|
||||
email: 'email',
|
||||
description: 'description',
|
||||
}
|
||||
await configure(db, data)
|
||||
const env = {
|
||||
INSTANCE_TITLE: 'a',
|
||||
ADMIN_EMAIL: 'b',
|
||||
INSTANCE_DESCR: 'c',
|
||||
} as Env
|
||||
|
||||
const res = await v1_instance.handleRequest(domain, db)
|
||||
const res = await v1_instance.handleRequest(domain, env)
|
||||
assert.equal(res.status, 200)
|
||||
assertCORS(res)
|
||||
assertJSON(res)
|
||||
|
@ -35,55 +42,60 @@ describe('Mastodon APIs', () => {
|
|||
const data = await res.json<any>()
|
||||
assert.equal(data.rules.length, 0)
|
||||
assert.equal(data.uri, domain)
|
||||
assert.equal(data.title, 'a')
|
||||
assert.equal(data.email, 'b')
|
||||
assert.equal(data.description, 'c')
|
||||
}
|
||||
})
|
||||
|
||||
test('adds a short_description if missing v1', async () => {
|
||||
const db = await makeDB()
|
||||
const data = {
|
||||
title: 'title',
|
||||
uri: 'uri',
|
||||
email: 'email',
|
||||
description: 'description',
|
||||
}
|
||||
await configure(db, data)
|
||||
const env = {
|
||||
INSTANCE_DESCR: 'c',
|
||||
} as Env
|
||||
|
||||
const res = await v1_instance.handleRequest(domain, db)
|
||||
const res = await v1_instance.handleRequest(domain, env)
|
||||
assert.equal(res.status, 200)
|
||||
|
||||
{
|
||||
const data = await res.json<any>()
|
||||
assert.equal(data.short_description, 'description')
|
||||
assert.equal(data.short_description, 'c')
|
||||
}
|
||||
})
|
||||
|
||||
test('return the instance infos v2', async () => {
|
||||
const db = await makeDB()
|
||||
const data = {
|
||||
title: 'title',
|
||||
uri: 'uri',
|
||||
email: 'email',
|
||||
description: 'description',
|
||||
}
|
||||
await configure(db, data)
|
||||
|
||||
const res = await v2_instance.handleRequest(domain, db)
|
||||
const env = {
|
||||
INSTANCE_TITLE: 'a',
|
||||
ADMIN_EMAIL: 'b',
|
||||
INSTANCE_DESCR: 'c',
|
||||
} as Env
|
||||
const res = await v2_instance.handleRequest(domain, db, env)
|
||||
assert.equal(res.status, 200)
|
||||
assertCORS(res)
|
||||
assertJSON(res)
|
||||
|
||||
{
|
||||
const data = await res.json<any>()
|
||||
assert.equal(data.rules.length, 0)
|
||||
assert.equal(data.domain, domain)
|
||||
assert.equal(data.title, 'a')
|
||||
assert.equal(data.contact.email, 'b')
|
||||
assert.equal(data.description, 'c')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('apps', () => {
|
||||
test('return the app infos', async () => {
|
||||
const db = await makeDB()
|
||||
await generateVAPIDKeys(db)
|
||||
const vapidKeys = await generateVAPIDKeys()
|
||||
const request = new Request('https://example.com', {
|
||||
method: 'POST',
|
||||
body: '{"redirect_uris":"mastodon://joinmastodon.org/oauth","website":"https://app.joinmastodon.org/ios","client_name":"Mastodon for iOS","scopes":"read write follow push"}',
|
||||
})
|
||||
|
||||
const res = await apps.handleRequest(db, request)
|
||||
const res = await apps.handleRequest(db, request, vapidKeys)
|
||||
assert.equal(res.status, 200)
|
||||
assertCORS(res)
|
||||
assertJSON(res)
|
||||
|
@ -100,11 +112,14 @@ describe('Mastodon APIs', () => {
|
|||
})
|
||||
|
||||
test('returns 404 for GET request', async () => {
|
||||
const vapidKeys = await generateVAPIDKeys()
|
||||
const request = new Request('https://example.com')
|
||||
const ctx: any = {
|
||||
next: () => new Response(),
|
||||
data: null,
|
||||
env: {},
|
||||
env: {
|
||||
VAPID_JWK: JSON.stringify(vapidKeys),
|
||||
},
|
||||
request,
|
||||
}
|
||||
|
||||
|
@ -169,8 +184,8 @@ describe('Mastodon APIs', () => {
|
|||
test('create subscription', async () => {
|
||||
const db = await makeDB()
|
||||
const client = await createTestClient(db)
|
||||
await generateVAPIDKeys(db)
|
||||
const connectedActor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||
const vapidKeys = await generateVAPIDKeys()
|
||||
|
||||
const data: any = {
|
||||
subscription: {
|
||||
|
@ -190,7 +205,7 @@ describe('Mastodon APIs', () => {
|
|||
body: JSON.stringify(data),
|
||||
})
|
||||
|
||||
const res = await subscription.handlePostRequest(db, req, connectedActor, client.id)
|
||||
const res = await subscription.handlePostRequest(db, req, connectedActor, client.id, vapidKeys)
|
||||
assert.equal(res.status, 200)
|
||||
|
||||
const row: any = await db.prepare('SELECT * FROM subscriptions').first()
|
||||
|
@ -202,8 +217,8 @@ describe('Mastodon APIs', () => {
|
|||
test('create subscriptions only creates one', async () => {
|
||||
const db = await makeDB()
|
||||
const client = await createTestClient(db)
|
||||
await generateVAPIDKeys(db)
|
||||
const connectedActor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||
const vapidKeys = await generateVAPIDKeys()
|
||||
|
||||
const data: any = {
|
||||
subscription: {
|
||||
|
@ -225,7 +240,7 @@ describe('Mastodon APIs', () => {
|
|||
body: JSON.stringify(data),
|
||||
})
|
||||
|
||||
const res = await subscription.handlePostRequest(db, req, connectedActor, client.id)
|
||||
const res = await subscription.handlePostRequest(db, req, connectedActor, client.id, vapidKeys)
|
||||
assert.equal(res.status, 200)
|
||||
|
||||
const { count } = await db.prepare('SELECT count(*) as count FROM subscriptions').first()
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { strict as assert } from 'node:assert/strict'
|
||||
import { configure, generateVAPIDKeys } from 'wildebeest/backend/src/config'
|
||||
import { addObjectInOutbox } from 'wildebeest/backend/src/activitypub/actors/outbox'
|
||||
import { createPublicNote } from 'wildebeest/backend/src/activitypub/objects/note'
|
||||
import * as accounts_following from 'wildebeest/functions/api/v1/accounts/[id]/following'
|
||||
|
@ -418,8 +417,6 @@ describe('Mastodon APIs', () => {
|
|||
|
||||
test('get remote actor statuses', async () => {
|
||||
const db = await makeDB()
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
await generateVAPIDKeys(db)
|
||||
|
||||
const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com')
|
||||
|
||||
|
@ -508,7 +505,6 @@ describe('Mastodon APIs', () => {
|
|||
|
||||
test('get remote actor statuses ignoring object that fail to download', async () => {
|
||||
const db = await makeDB()
|
||||
await generateVAPIDKeys(db)
|
||||
|
||||
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||
await createPublicNote(domain, db, 'my localnote status', actor)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as notifications_get from 'wildebeest/functions/api/v1/notifications/[id]'
|
||||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
import { createPublicNote } from 'wildebeest/backend/src/activitypub/objects/note'
|
||||
import { createNotification, insertFollowNotification } from 'wildebeest/backend/src/mastodon/notification'
|
||||
import { createPerson } from 'wildebeest/backend/src/activitypub/actors'
|
||||
|
@ -7,13 +8,13 @@ import { makeDB, assertJSON, createTestClient } from '../utils'
|
|||
import { strict as assert } from 'node:assert/strict'
|
||||
import { sendLikeNotification } from 'wildebeest/backend/src/mastodon/notification'
|
||||
import { createSubscription } from 'wildebeest/backend/src/mastodon/subscription'
|
||||
import { generateVAPIDKeys, configure } from 'wildebeest/backend/src/config'
|
||||
import { arrayBufferToBase64 } from 'wildebeest/backend/src/utils/key-ops'
|
||||
import { getNotifications } from 'wildebeest/backend/src/mastodon/notification'
|
||||
|
||||
const userKEK = 'test_kek15'
|
||||
const domain = 'cloudflare.com'
|
||||
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
|
||||
const vapidKeys = {} as JWK
|
||||
|
||||
function parseCryptoKey(s: string): any {
|
||||
const parts = s.split(';')
|
||||
|
@ -92,8 +93,6 @@ describe('Mastodon APIs', () => {
|
|||
|
||||
test('send like notification', async () => {
|
||||
const db = await makeDB()
|
||||
await generateVAPIDKeys(db)
|
||||
await configure(db, { title: 'title', description: 'a', email: 'email' })
|
||||
|
||||
const clientKeys = (await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, [
|
||||
'sign',
|
||||
|
@ -140,7 +139,7 @@ describe('Mastodon APIs', () => {
|
|||
})
|
||||
|
||||
const fromActor = await createPerson(domain, db, userKEK, 'from@cloudflare.com')
|
||||
await sendLikeNotification(db, fromActor, actor, 'notifid')
|
||||
await sendLikeNotification(db, fromActor, actor, 'notifid', 'admin@example.com', vapidKeys)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -83,6 +83,21 @@ describe('Mastodon APIs', () => {
|
|||
assert.equal(await data.text(), 'cached data')
|
||||
})
|
||||
|
||||
test('home returns empty if not in cache', async () => {
|
||||
const db = await makeDB()
|
||||
const connectedActor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||
const kv_cache: any = {
|
||||
async get() {
|
||||
return null
|
||||
},
|
||||
}
|
||||
const req = new Request('https://' + domain)
|
||||
const data = await timelines_home.handleRequest(req, kv_cache, connectedActor)
|
||||
const posts = await data.json<Array<any>>()
|
||||
|
||||
assert.equal(posts.length, 0)
|
||||
})
|
||||
|
||||
test('public returns Notes', async () => {
|
||||
const db = await makeDB()
|
||||
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
import * as startInstance from 'wildebeest/functions/start-instance'
|
||||
import { TEST_JWT, ACCESS_CERTS } from './test-data'
|
||||
import { strict as assert } from 'node:assert/strict'
|
||||
import { makeDB } from './utils'
|
||||
|
||||
const accessDomain = 'access.com'
|
||||
const accessAud = 'abcd'
|
||||
|
||||
describe('Wildebeest', () => {
|
||||
globalThis.fetch = async (input: RequestInfo) => {
|
||||
if (input === 'https://' + accessDomain + '/cdn-cgi/access/certs') {
|
||||
return new Response(JSON.stringify(ACCESS_CERTS))
|
||||
}
|
||||
if (input === 'https://' + accessDomain + '/cdn-cgi/access/get-identity') {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
email: 'some@cloudflare.com',
|
||||
})
|
||||
)
|
||||
}
|
||||
throw new Error('unexpected request to ' + input)
|
||||
}
|
||||
|
||||
test('start instance should generate a VAPID key and store a JWK', async () => {
|
||||
const db = await makeDB()
|
||||
|
||||
const body = JSON.stringify({
|
||||
title: 'title',
|
||||
description: 'description',
|
||||
email: 'email',
|
||||
})
|
||||
|
||||
const headers = {
|
||||
cookie: 'CF_Authorization=' + TEST_JWT,
|
||||
}
|
||||
|
||||
const req = new Request('https://example.com', { method: 'POST', body, headers })
|
||||
const res = await startInstance.handlePostRequest(req, db, accessDomain, accessAud)
|
||||
assert.equal(res.status, 201)
|
||||
|
||||
const { value } = await db.prepare("SELECT value FROM instance_config WHERE key = 'vapid_jwk'").first()
|
||||
const jwk = JSON.parse(value)
|
||||
|
||||
assert.equal(jwk.key_ops.length, 1)
|
||||
assert.equal(jwk.key_ops[0], 'sign')
|
||||
assert.equal(jwk.crv, 'P-256')
|
||||
})
|
||||
})
|
|
@ -3,20 +3,12 @@ import * as statusesAPI from 'wildebeest/functions/api/v1/statuses'
|
|||
import { statuses } from 'wildebeest/frontend/src/dummyData'
|
||||
import type { MastodonStatus } from 'wildebeest/frontend/src/types'
|
||||
import type { MastodonAccount } from 'wildebeest/backend/src/types'
|
||||
import { configure } from 'wildebeest/backend/src/config'
|
||||
|
||||
const kek = 'test-kek'
|
||||
/**
|
||||
* Run helper commands to initialize the database with actors, statuses, etc.
|
||||
*/
|
||||
export async function init(domain: string, db: D1Database) {
|
||||
configure(db, {
|
||||
title: 'Wildebeest',
|
||||
email: '',
|
||||
description: 'Wildebeest dev instance',
|
||||
thumbnail: '/assets/wildebeest-logo.png',
|
||||
})
|
||||
|
||||
for (const status of statuses as MastodonStatus[]) {
|
||||
const actor = await getOrCreatePerson(domain, db, status.account.username)
|
||||
await createStatus(db, actor, status.content)
|
||||
|
@ -31,8 +23,12 @@ async function createStatus(db: D1Database, actor: Person, status: string, visib
|
|||
status,
|
||||
visibility,
|
||||
}
|
||||
const headers = {
|
||||
'content-type': 'application/json',
|
||||
}
|
||||
const req = new Request('https://example.com', {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
await statusesAPI.handleRequest(req, db, actor, kek)
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
import { $, component$, useStore, useClientEffect$, useSignal } from '@builder.io/qwik'
|
||||
import { DocumentHead } from '@builder.io/qwik-city'
|
||||
import { WildebeestLogo } from '~/components/MastodonLogo'
|
||||
import { useDomain } from '~/utils/useDomain'
|
||||
import Step1 from './step-1'
|
||||
import { type InstanceConfig, testInstance } from './utils'
|
||||
|
||||
export default component$(() => {
|
||||
const domain = useDomain()
|
||||
|
||||
const loading = useSignal(true)
|
||||
const instanceConfigured = useSignal(false)
|
||||
|
||||
const instanceConfig = useStore<InstanceConfig>({
|
||||
title: `${domain} Wildebeest`,
|
||||
email: `admin@${domain}`,
|
||||
description: 'My personal Wildebeest instance (powered by Cloudflare)',
|
||||
})
|
||||
|
||||
useClientEffect$(async () => {
|
||||
if (await testInstance()) {
|
||||
instanceConfigured.value = true
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
const getStepToShow = () => {
|
||||
if (loading.value) return 'loading'
|
||||
if (!instanceConfigured.value) return 'step-1'
|
||||
return 'all-good'
|
||||
}
|
||||
|
||||
const stepToShow = getStepToShow()
|
||||
|
||||
const setLoading = $((value: boolean) => {
|
||||
loading.value = value
|
||||
})
|
||||
|
||||
const setInstanceConfigured = $((value: boolean) => {
|
||||
instanceConfigured.value = value
|
||||
})
|
||||
|
||||
return (
|
||||
<div class="flex flex-col p-5 items-center max-w-lg mx-auto">
|
||||
<h1 class="text-center mt-7 mb-9 flex items-center">
|
||||
<WildebeestLogo size="large" />
|
||||
</h1>
|
||||
{stepToShow.startsWith('step-') && (
|
||||
<div class="text-center">
|
||||
<p class="mb-1">Welcome to Wildebeest...</p>
|
||||
<p class="mb-5"> Your instance hasn't been configured yet.</p>
|
||||
</div>
|
||||
)}
|
||||
{stepToShow === 'loading' && <p>Loading...</p>}
|
||||
{stepToShow === 'step-1' && (
|
||||
<Step1 instanceConfig={instanceConfig} setLoading={setLoading} setInstanceConfigured={setInstanceConfigured} />
|
||||
)}
|
||||
{stepToShow === 'all-good' && <p class="text-center">All good, your instance is ready.</p>}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export const head: DocumentHead = () => {
|
||||
return {
|
||||
title: 'Wildebeest Start Instance',
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content: 'Wildebeest Instance Setup page',
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
import { component$, QRL } from '@builder.io/qwik'
|
||||
import { configure, type InstanceConfig, testInstance } from './utils'
|
||||
|
||||
interface Props {
|
||||
instanceConfig: InstanceConfig
|
||||
setLoading: QRL<(loading: boolean) => void>
|
||||
setInstanceConfigured: QRL<(configured: boolean) => void>
|
||||
}
|
||||
|
||||
export default component$<Props>(({ instanceConfig, setLoading, setInstanceConfigured }) => {
|
||||
return (
|
||||
<>
|
||||
<h2 class="mb-5">Configure your instance</h2>
|
||||
|
||||
<div class="flex flex-col mb-6 w-full max-w-md">
|
||||
<label class="mb-2 max-w-max text-semi text-sm" for="start-instance-title">
|
||||
Title
|
||||
</label>
|
||||
<div class="flex justify-center items-center flex-wrap gap-1">
|
||||
<input
|
||||
id="start-instance-title"
|
||||
name="title"
|
||||
class="bg-black text-white p-3 rounded outline-none border border-black hover:border-wildebeest-vibrant-500 focus:border-wildebeest-vibrant-500 invalid:border-red-400 flex-1 w-full"
|
||||
value={instanceConfig.title}
|
||||
onInput$={(ev) => (instanceConfig.title = (ev.target as HTMLInputElement).value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col mb-6 w-full max-w-md">
|
||||
<label class="mb-2 max-w-max text-semi text-sm" for="start-instance-email">
|
||||
Administrator email
|
||||
</label>
|
||||
<div class="flex justify-center items-center flex-wrap gap-1">
|
||||
<input
|
||||
id="start-instance-email"
|
||||
name="email"
|
||||
type="email"
|
||||
class="bg-black text-white p-3 rounded outline-none border border-black hover:border-wildebeest-vibrant-500 focus:border-wildebeest-vibrant-500 invalid:border-red-400 flex-1 w-full"
|
||||
value={instanceConfig.email}
|
||||
onInput$={(ev) => (instanceConfig.email = (ev.target as HTMLInputElement).value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col mb-6 w-full max-w-md">
|
||||
<label class="mb-2 max-w-max text-semi text-sm" for="start-instance-description">
|
||||
Description
|
||||
</label>
|
||||
<div class="flex justify-center items-center flex-wrap gap-1">
|
||||
<input
|
||||
id="start-instance-description"
|
||||
name="description"
|
||||
class="bg-black text-white p-3 rounded outline-none border border-black hover:border-wildebeest-vibrant-500 focus:border-wildebeest-vibrant-500 invalid:border-red-400 flex-1 w-full"
|
||||
value={instanceConfig.description}
|
||||
onInput$={(ev) => (instanceConfig.description = (ev.target as HTMLInputElement).value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="mb-9 bg-wildebeest-vibrant-500 hover:bg-wildebeest-vibrant-600 p-3 text-white text-uppercase border-wildebeest-vibrant-500 text-lg text-semi outline-none border rounded hover:border-wildebeest-vibrant-600 focus:border-wildebeest-vibrant-600"
|
||||
preventdefault:click
|
||||
onClick$={async () => {
|
||||
setLoading(true)
|
||||
await configure(instanceConfig)
|
||||
|
||||
if (await testInstance()) {
|
||||
setInstanceConfigured(true)
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
}}
|
||||
>
|
||||
Configure and start your instance
|
||||
</button>
|
||||
</>
|
||||
)
|
||||
})
|
|
@ -1,21 +0,0 @@
|
|||
export type InstanceConfig = {
|
||||
title?: string
|
||||
email?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export async function configure(data: InstanceConfig) {
|
||||
const res = await fetch('/start-instance', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
if (!res.ok) {
|
||||
throw new Error('/start-instance returned: ' + res.status)
|
||||
}
|
||||
}
|
||||
|
||||
export async function testInstance(): Promise<boolean> {
|
||||
const res = await fetch('/api/v1/instance')
|
||||
const data = await res.json<{ title?: string }>()
|
||||
return !!data.title
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
.active {
|
||||
color: var(--wildebeest-color-200);
|
||||
position: relative;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
transform: translateX(-50%);
|
||||
border-color: transparent transparent var(--wildebeest-color-500);
|
||||
border-style: solid;
|
||||
border-width: 0 0.7rem 0.7rem;
|
||||
bottom: -1px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
bottom: -1px;
|
||||
border-color: transparent transparent var(--wildebeest-color-600);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import { component$, Slot, useStylesScoped$ } from '@builder.io/qwik'
|
||||
import { component$, Slot } from '@builder.io/qwik'
|
||||
import { Link, useLocation } from '@builder.io/qwik-city'
|
||||
import StickyHeader from '~/components/StickyHeader/StickyHeader'
|
||||
import styles from './layout.scss?inline'
|
||||
|
||||
type LinkConfig = {
|
||||
linkText: string
|
||||
|
@ -9,14 +8,13 @@ type LinkConfig = {
|
|||
}
|
||||
|
||||
export default component$(() => {
|
||||
useStylesScoped$(styles)
|
||||
const location = useLocation()
|
||||
|
||||
const renderNavLink = ({ linkText, linkTarget }: LinkConfig) => {
|
||||
const isActive = location.pathname.replace(/\/$/, '') === linkTarget
|
||||
|
||||
return (
|
||||
<div class={`py-4 ${isActive ? 'active' : ''}`}>
|
||||
<div class={`py-4 ${isActive ? activeClasses.join(' ') : ''}`}>
|
||||
<Link href={linkTarget} class="no-underline text-bold text-wildebeest-200 py-4">
|
||||
{linkText}
|
||||
</Link>
|
||||
|
@ -54,3 +52,35 @@ export default component$(() => {
|
|||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export const activeClasses = [
|
||||
'relative',
|
||||
'before:block',
|
||||
'before:content-[""]',
|
||||
'before:absolute',
|
||||
'before:w-0',
|
||||
'before:h-0',
|
||||
'before:bottom-[-1px]',
|
||||
'before:left-1/2',
|
||||
'before:translate-x-[-50%]',
|
||||
'before:border-solid',
|
||||
'before:border-t-0',
|
||||
'before:border-x-[0.7rem]',
|
||||
'before:border-b-[0.7rem]',
|
||||
'before:border-x-transparent',
|
||||
'before:border-b-wildebeest-500',
|
||||
'after:block',
|
||||
'after:content-[""]',
|
||||
'after:absolute',
|
||||
'after:w-0',
|
||||
'after:h-0',
|
||||
'after:bottom-[-1px]',
|
||||
'after:left-1/2',
|
||||
'after:translate-x-[-50%]',
|
||||
'after:border-solid',
|
||||
'after:border-t-0',
|
||||
'after:border-x-[0.7rem]',
|
||||
'after:border-b-[0.7rem]',
|
||||
'after:border-x-transparent',
|
||||
'after:border-b-wildebeest-600',
|
||||
]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { component$, Slot, useContextProvider } from '@builder.io/qwik'
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import { DocumentHead, loader$ } from '@builder.io/qwik-city'
|
||||
import * as instance from 'wildebeest/functions/api/v1/instance'
|
||||
import type { InstanceConfig } from 'wildebeest/backend/src/types/configs'
|
||||
|
@ -8,18 +9,20 @@ import { WildebeestLogo } from '~/components/MastodonLogo'
|
|||
import { getCommitHash } from '~/utils/getCommitHash'
|
||||
import { InstanceConfigContext } from '~/utils/instanceConfig'
|
||||
|
||||
export const instanceLoader = loader$<{ DATABASE: D1Database }, Promise<InstanceConfig>>(
|
||||
async ({ platform, redirect }) => {
|
||||
const response = await instance.handleRequest('', platform.DATABASE)
|
||||
const results = await response.text()
|
||||
const json = JSON.parse(results) as InstanceConfig
|
||||
if (!json.title) {
|
||||
// If there is no title set then we have not configured the instance
|
||||
throw redirect(302, '/start-instance')
|
||||
}
|
||||
return json
|
||||
}
|
||||
)
|
||||
export const instanceLoader = loader$<
|
||||
{ DATABASE: D1Database; INSTANCE_TITLE: string; INSTANCE_DESCR: string; ADMIN_EMAIL: string },
|
||||
Promise<InstanceConfig>
|
||||
>(async ({ platform }) => {
|
||||
const env = {
|
||||
INSTANCE_DESCR: platform.INSTANCE_DESCR,
|
||||
INSTANCE_TITLE: platform.INSTANCE_TITLE,
|
||||
ADMIN_EMAIL: platform.ADMIN_EMAIL,
|
||||
} as Env
|
||||
const response = await instance.handleRequest('', platform.DATABASE, env)
|
||||
const results = await response.text()
|
||||
const json = JSON.parse(results) as InstanceConfig
|
||||
return json
|
||||
})
|
||||
|
||||
export default component$(() => {
|
||||
useContextProvider(InstanceConfigContext, instanceLoader.use().value)
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
|
||||
import { getFollowers } from 'wildebeest/backend/src/mastodon/follow'
|
||||
import * as objects from 'wildebeest/backend/src/activitypub/objects'
|
||||
import type { Activity } from 'wildebeest/backend/src/activitypub/activities'
|
||||
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import type { Note } from 'wildebeest/backend/src/activitypub/objects/note'
|
||||
import * as activityCreate from 'wildebeest/backend/src/activitypub/activities/create'
|
||||
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params }) => {
|
||||
const domain = new URL(request.url).hostname
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
|
||||
import { getFollowingId } from 'wildebeest/backend/src/mastodon/follow'
|
||||
import * as objects from 'wildebeest/backend/src/activitypub/objects'
|
||||
import type { Activity } from 'wildebeest/backend/src/activitypub/activities'
|
||||
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import type { Note } from 'wildebeest/backend/src/activitypub/objects/note'
|
||||
import * as activityCreate from 'wildebeest/backend/src/activitypub/activities/create'
|
||||
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params }) => {
|
||||
const domain = new URL(request.url).hostname
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
|
||||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
import * as activityHandler from 'wildebeest/backend/src/activitypub/activities/handle'
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import * as actors from 'wildebeest/backend/src/activitypub/actors'
|
||||
import type { Activity } from 'wildebeest/backend/src/activitypub/activities'
|
||||
import * as activities from 'wildebeest/backend/src/activitypub/activities'
|
||||
import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { parseRequest } from 'wildebeest/backend/src/utils/httpsigjs/parser'
|
||||
import { fetchKey, verifySignature } from 'wildebeest/backend/src/utils/httpsigjs/verifier'
|
||||
import { generateDigestHeader } from 'wildebeest/backend/src/utils/http-signing-cavage'
|
||||
import * as timeline from 'wildebeest/backend/src/mastodon/timeline'
|
||||
import * as notification from 'wildebeest/backend/src/mastodon/notification'
|
||||
import { getVAPIDKeys } from 'wildebeest/backend/src/config'
|
||||
|
||||
export const onRequest: PagesFunction<Env, any> = async ({ params, request, env, waitUntil }) => {
|
||||
const parsedSignature = parseRequest(request)
|
||||
|
@ -30,7 +31,17 @@ export const onRequest: PagesFunction<Env, any> = async ({ params, request, env,
|
|||
|
||||
const activity: Activity = JSON.parse(body)
|
||||
const domain = new URL(request.url).hostname
|
||||
return handleRequest(domain, env.DATABASE, env.KV_CACHE, params.id as string, activity, env.userKEK, waitUntil)
|
||||
return handleRequest(
|
||||
domain,
|
||||
env.DATABASE,
|
||||
env.KV_CACHE,
|
||||
params.id as string,
|
||||
activity,
|
||||
env.userKEK,
|
||||
waitUntil,
|
||||
env.ADMIN_EMAIL,
|
||||
getVAPIDKeys(env)
|
||||
)
|
||||
}
|
||||
|
||||
export async function handleRequest(
|
||||
|
@ -40,7 +51,9 @@ export async function handleRequest(
|
|||
id: string,
|
||||
activity: Activity,
|
||||
userKEK: string,
|
||||
waitUntil: (p: Promise<any>) => void
|
||||
waitUntil: (p: Promise<any>) => void,
|
||||
adminEmail: string,
|
||||
vapidKeys: JWK
|
||||
): Promise<Response> {
|
||||
const handle = parseHandle(id)
|
||||
|
||||
|
@ -54,7 +67,7 @@ export async function handleRequest(
|
|||
return new Response('', { status: 404 })
|
||||
}
|
||||
|
||||
await activityHandler.handle(domain, activity, db, userKEK)
|
||||
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
||||
|
||||
// Assuming we received new posts or a like, pregenerate the user's timelines
|
||||
// and notifications.
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
|
||||
import * as objects from 'wildebeest/backend/src/activitypub/objects'
|
||||
import type { Activity } from 'wildebeest/backend/src/activitypub/activities'
|
||||
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import type { Note } from 'wildebeest/backend/src/activitypub/objects/note'
|
||||
import * as activityCreate from 'wildebeest/backend/src/activitypub/activities/create'
|
||||
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params }) => {
|
||||
const domain = new URL(request.url).hostname
|
||||
|
@ -17,6 +13,7 @@ const headers = {
|
|||
'content-type': 'application/json; charset=utf-8',
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TODO: use userKEK
|
||||
export async function handleRequest(domain: string, db: D1Database, id: string, userKEK: string): Promise<Response> {
|
||||
const handle = parseHandle(id)
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ const headers = {
|
|||
|
||||
const DEFAULT_LIMIT = 20
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TODO: use userKEK
|
||||
export async function handleRequest(domain: string, db: D1Database, id: string, userKEK: string): Promise<Response> {
|
||||
const handle = parseHandle(id)
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
|
|||
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import type { MastodonAccount } from 'wildebeest/backend/src/types/account'
|
||||
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
|
||||
import type { Handle } from 'wildebeest/backend/src/utils/parse'
|
||||
import { queryAcct } from 'wildebeest/backend/src/webfinger/index'
|
||||
|
|
|
@ -2,13 +2,12 @@ import type { Env } from 'wildebeest/backend/src/types/env'
|
|||
import type { Activity } from 'wildebeest/backend/src/activitypub/activities'
|
||||
import type { Note } from 'wildebeest/backend/src/activitypub/objects/note'
|
||||
import { loadExternalMastodonAccount } from 'wildebeest/backend/src/mastodon/account'
|
||||
import type { Object } from 'wildebeest/backend/src/activitypub/objects'
|
||||
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { makeGetActorAsId, makeGetObjectAsId } from 'wildebeest/backend/src/activitypub/activities/handle'
|
||||
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
|
||||
import type { Handle } from 'wildebeest/backend/src/utils/parse'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import type { MastodonAccount, MastodonStatus } from 'wildebeest/backend/src/types'
|
||||
import type { MastodonStatus } from 'wildebeest/backend/src/types'
|
||||
import { toMastodonStatusFromObject } from 'wildebeest/backend/src/mastodon/status'
|
||||
import * as objects from 'wildebeest/backend/src/activitypub/objects'
|
||||
import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
|
||||
|
@ -41,6 +40,7 @@ export async function handleRequest(request: Request, db: D1Database, id: string
|
|||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TODO: use userKEK
|
||||
async function getRemoteStatuses(request: Request, handle: Handle, db: D1Database, userKEK: string): Promise<Response> {
|
||||
const url = new URL(request.url)
|
||||
const domain = url.hostname
|
||||
|
@ -61,6 +61,7 @@ async function getRemoteStatuses(request: Request, handle: Handle, db: D1Databas
|
|||
|
||||
const activities = await outbox.get(actor)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TODO: use account
|
||||
const account = await loadExternalMastodonAccount(acct, actor)
|
||||
|
||||
const promises = activities.items.map(async (activity: Activity) => {
|
||||
|
|
|
@ -5,7 +5,7 @@ import type { Env } from 'wildebeest/backend/src/types/env'
|
|||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import { getFollowingAcct, getFollowingRequestedAcct } from 'wildebeest/backend/src/mastodon/follow'
|
||||
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params, data }) => {
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => {
|
||||
return handleRequest(request, env.DATABASE, data.connectedActor)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import * as images from 'wildebeest/backend/src/media/image'
|
|||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import type { Actor } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { updateActorProperty } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import type { CredentialAccount, MastodonAccount } from 'wildebeest/backend/src/types/account'
|
||||
import type { CredentialAccount } from 'wildebeest/backend/src/types/account'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import { loadLocalMastodonAccount } from 'wildebeest/backend/src/mastodon/account'
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import { b64ToUrlEncoded, exportPublicKeyPair } from 'wildebeest/backend/src/webpush/util'
|
||||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
import { Env } from 'wildebeest/backend/src/types/env'
|
||||
import { createClient } from 'wildebeest/backend/src/mastodon/client'
|
||||
import { getVAPIDKeys, VAPIDPublicKey } from 'wildebeest/backend/src/mastodon/subscription'
|
||||
import { VAPIDPublicKey } from 'wildebeest/backend/src/mastodon/subscription'
|
||||
import { getVAPIDKeys } from 'wildebeest/backend/src/config'
|
||||
|
||||
type AppsPost = {
|
||||
redirect_uris: string
|
||||
|
@ -12,11 +12,11 @@ type AppsPost = {
|
|||
scopes: string
|
||||
}
|
||||
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => {
|
||||
return handleRequest(env.DATABASE, request)
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env }) => {
|
||||
return handleRequest(env.DATABASE, request, getVAPIDKeys(env))
|
||||
}
|
||||
|
||||
export async function handleRequest(db: D1Database, request: Request) {
|
||||
export async function handleRequest(db: D1Database, request: Request, vapidKeys: JWK) {
|
||||
if (request.method !== 'POST') {
|
||||
return new Response('', { status: 400 })
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ export async function handleRequest(db: D1Database, request: Request) {
|
|||
const body = await request.json<AppsPost>()
|
||||
|
||||
const client = await createClient(db, body.client_name, body.redirect_uris, body.website, body.scopes)
|
||||
const vapidKey = VAPIDPublicKey(await getVAPIDKeys(db))
|
||||
const vapidKey = VAPIDPublicKey(vapidKeys)
|
||||
|
||||
const res = {
|
||||
name: body.client_name,
|
||||
|
|
|
@ -1,46 +1,37 @@
|
|||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import { DEFAULT_THUMBNAIL } from 'wildebeest/backend/src/config'
|
||||
|
||||
const INSTANCE_VERSION = '4.0.2'
|
||||
|
||||
export const onRequest: PagesFunction<Env, any> = async ({ env, request }) => {
|
||||
const domain = new URL(request.url).hostname
|
||||
return handleRequest(domain, env.DATABASE)
|
||||
return handleRequest(domain, env)
|
||||
}
|
||||
|
||||
export async function handleRequest(domain: string, db: D1Database) {
|
||||
export async function handleRequest(domain: string, env: Env) {
|
||||
const headers = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': 'content-type, authorization',
|
||||
'content-type': 'application/json; charset=utf-8',
|
||||
}
|
||||
|
||||
const query = `
|
||||
SELECT * FROM instance_config WHERE key IN ('title', 'description', 'email', 'short_description', 'thumbnail')
|
||||
`
|
||||
const { results, error, success } = await db.prepare(query).all()
|
||||
if (!success) {
|
||||
throw new Error('SQL error: ' + error)
|
||||
}
|
||||
|
||||
const res: any = {}
|
||||
if (results) {
|
||||
for (let i = 0, len = results.length; i < len; i++) {
|
||||
const row: any = results[i]
|
||||
res[row.key] = row.value
|
||||
}
|
||||
}
|
||||
|
||||
res.thumbnail = DEFAULT_THUMBNAIL
|
||||
|
||||
// Registration is disabled because unsupported by Wildebeest. Users
|
||||
// should go through the login flow and authenticate with Access.
|
||||
// The documentation is incorrect and registrations is a boolean.
|
||||
res.registrations = false
|
||||
|
||||
res.version = INSTANCE_VERSION
|
||||
res.rules = []
|
||||
res.uri = domain
|
||||
res.title = env.INSTANCE_TITLE
|
||||
res.email = env.ADMIN_EMAIL
|
||||
res.description = env.INSTANCE_DESCR
|
||||
|
||||
if (!res.short_description) {
|
||||
res.short_description = res.description
|
||||
}
|
||||
res.short_description = res.description
|
||||
|
||||
return new Response(JSON.stringify(res), { headers })
|
||||
}
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
// https://docs.joinmastodon.org/methods/notifications/#get
|
||||
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import * as objects from 'wildebeest/backend/src/activitypub/objects'
|
||||
import type { Person } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import { getNotifications } from 'wildebeest/backend/src/mastodon/notification'
|
||||
import type { MastodonStatus } from 'wildebeest/backend/src/types'
|
||||
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => {
|
||||
return handleRequest(request, env.KV_CACHE, data.connectedActor)
|
||||
|
|
|
@ -4,7 +4,6 @@ import type { Notification } from 'wildebeest/backend/src/types/notification'
|
|||
import { urlToHandle } from 'wildebeest/backend/src/utils/handle'
|
||||
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { loadExternalMastodonAccount } from 'wildebeest/backend/src/mastodon/account'
|
||||
import * as objects from 'wildebeest/backend/src/activitypub/objects'
|
||||
import type { Person } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
import { getClientById } from 'wildebeest/backend/src/mastodon/client'
|
||||
import { getVAPIDKeys } from 'wildebeest/backend/src/config'
|
||||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
import type { Actor } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { createSubscription, getSubscription } from 'wildebeest/backend/src/mastodon/subscription'
|
||||
import type { CreateRequest } from 'wildebeest/backend/src/mastodon/subscription'
|
||||
import { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import { Env } from 'wildebeest/backend/src/types/env'
|
||||
import * as errors from 'wildebeest/backend/src/errors'
|
||||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
import type { WebPushInfos, WebPushMessage, WebPushResult } from 'wildebeest/backend/src/webpush/webpushinfos'
|
||||
import { b64ToUrlEncoded, exportPublicKeyPair } from 'wildebeest/backend/src/webpush/util'
|
||||
import { generateWebPushMessage } from 'wildebeest/backend/src/webpush'
|
||||
import { getVAPIDKeys, VAPIDPublicKey } from 'wildebeest/backend/src/mastodon/subscription'
|
||||
import { VAPIDPublicKey } from 'wildebeest/backend/src/mastodon/subscription'
|
||||
|
||||
export const onRequestGet: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => {
|
||||
return handleGetRequest(env.DATABASE, request, data.connectedActor, data.clientId)
|
||||
}
|
||||
|
||||
export const onRequestPost: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => {
|
||||
return handlePostRequest(env.DATABASE, request, data.connectedActor, data.clientId)
|
||||
return handlePostRequest(env.DATABASE, request, data.connectedActor, data.clientId, getVAPIDKeys(env))
|
||||
}
|
||||
|
||||
const headers = {
|
||||
|
@ -56,7 +54,13 @@ export async function handleGetRequest(db: D1Database, request: Request, connect
|
|||
return new Response(JSON.stringify(res), { headers })
|
||||
}
|
||||
|
||||
export async function handlePostRequest(db: D1Database, request: Request, connectedActor: Actor, clientId: string) {
|
||||
export async function handlePostRequest(
|
||||
db: D1Database,
|
||||
request: Request,
|
||||
connectedActor: Actor,
|
||||
clientId: string,
|
||||
vapidKeys: JWK
|
||||
) {
|
||||
const client = await getClientById(db, clientId)
|
||||
if (client === null) {
|
||||
return errors.clientUnknown()
|
||||
|
@ -70,7 +74,7 @@ export async function handlePostRequest(db: D1Database, request: Request, connec
|
|||
subscription = await createSubscription(db, connectedActor, client, data)
|
||||
}
|
||||
|
||||
const vapidKey = VAPIDPublicKey(await getVAPIDKeys(db))
|
||||
const vapidKey = VAPIDPublicKey(vapidKeys)
|
||||
|
||||
const res = {
|
||||
id: 4,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// https://docs.joinmastodon.org/methods/statuses/#create
|
||||
|
||||
import { loadLocalMastodonAccount } from 'wildebeest/backend/src/mastodon/account'
|
||||
import type { MediaAttachment } from 'wildebeest/backend/src/types/media'
|
||||
import { createPublicNote } from 'wildebeest/backend/src/activitypub/objects/note'
|
||||
import type { Document } from 'wildebeest/backend/src/activitypub/objects'
|
||||
import { getObjectByMastodonId } from 'wildebeest/backend/src/activitypub/objects'
|
||||
|
@ -9,8 +8,6 @@ import { getMentions } from 'wildebeest/backend/src/mastodon/status'
|
|||
import * as activities from 'wildebeest/backend/src/activitypub/activities/create'
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import type { MastodonAccount } from 'wildebeest/backend/src/types/account'
|
||||
import type { MastodonStatus } from 'wildebeest/backend/src/types/status'
|
||||
import { queryAcct } from 'wildebeest/backend/src/webfinger'
|
||||
import { deliverFollowers, deliverToActor } from 'wildebeest/backend/src/activitypub/deliver'
|
||||
import { addObjectInOutbox } from 'wildebeest/backend/src/activitypub/actors/outbox'
|
||||
|
@ -50,7 +47,7 @@ export async function handleRequest(
|
|||
return new Response('', { status: 400 })
|
||||
}
|
||||
|
||||
let mediaAttachments: Array<Document> = []
|
||||
const mediaAttachments: Array<Document> = []
|
||||
if (body.media_ids && body.media_ids.length > 0) {
|
||||
for (let i = 0, len = body.media_ids.length; i < len; i++) {
|
||||
const id = body.media_ids[i]
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
// https://docs.joinmastodon.org/methods/statuses/#get
|
||||
|
||||
import type { UUID } from 'wildebeest/backend/src/types'
|
||||
import { urlToHandle } from 'wildebeest/backend/src/utils/handle'
|
||||
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
|
||||
import type { Person } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import { getFollowers } from 'wildebeest/backend/src/mastodon/follow'
|
||||
import { getMastodonStatusById } from 'wildebeest/backend/src/mastodon/status'
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ params, request, env, data }) => {
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ params, env }) => {
|
||||
return handleRequest(env.DATABASE, params.id as UUID)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// https://docs.joinmastodon.org/methods/statuses/#favourite
|
||||
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
|
||||
import { insertLike } from 'wildebeest/backend/src/mastodon/like'
|
||||
import { getSigningKey } from 'wildebeest/backend/src/mastodon/account'
|
||||
import { deliverToActor } from 'wildebeest/backend/src/activitypub/deliver'
|
||||
|
@ -11,10 +10,9 @@ import * as like from 'wildebeest/backend/src/activitypub/activities/like'
|
|||
import { getObjectByMastodonId } from 'wildebeest/backend/src/activitypub/objects'
|
||||
import type { Note } from 'wildebeest/backend/src/activitypub/objects/note'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import { queryAcct } from 'wildebeest/backend/src/webfinger'
|
||||
import { toMastodonStatusFromObject } from 'wildebeest/backend/src/mastodon/status'
|
||||
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, data, params }) => {
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ env, data, params }) => {
|
||||
return handleRequest(env.DATABASE, params.id as string, data.connectedActor, env.userKEK)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// https://docs.joinmastodon.org/methods/statuses/#boost
|
||||
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
|
||||
import { addObjectInOutbox } from 'wildebeest/backend/src/activitypub/actors/outbox'
|
||||
import { insertReblog } from 'wildebeest/backend/src/mastodon/reblog'
|
||||
import { getSigningKey } from 'wildebeest/backend/src/mastodon/account'
|
||||
|
@ -12,10 +11,9 @@ import * as announce from 'wildebeest/backend/src/activitypub/activities/announc
|
|||
import { getObjectByMastodonId } from 'wildebeest/backend/src/activitypub/objects'
|
||||
import type { Note } from 'wildebeest/backend/src/activitypub/objects/note'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import { queryAcct } from 'wildebeest/backend/src/webfinger'
|
||||
import { toMastodonStatusFromObject } from 'wildebeest/backend/src/mastodon/status'
|
||||
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, data, params }) => {
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ env, data, params }) => {
|
||||
return handleRequest(env.DATABASE, params.id as string, data.connectedActor, env.userKEK)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import type { Actor } from 'wildebeest/backend/src/activitypub/actors/'
|
||||
import * as objects from 'wildebeest/backend/src/activitypub/objects/'
|
||||
import { urlToHandle } from 'wildebeest/backend/src/utils/handle'
|
||||
import { getHomeTimeline } from 'wildebeest/backend/src/mastodon/timeline'
|
||||
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import type { MastodonAccount, MastodonStatus } from 'wildebeest/backend/src/types/'
|
||||
import * as errors from 'wildebeest/backend/src/errors'
|
||||
|
||||
const headers = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
|
@ -14,7 +8,7 @@ const headers = {
|
|||
'content-type': 'application/json; charset=utf-8',
|
||||
}
|
||||
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params, data }) => {
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => {
|
||||
return handleRequest(request, env.KV_CACHE, data.connectedActor)
|
||||
}
|
||||
|
||||
|
@ -29,7 +23,7 @@ export async function handleRequest(request: Request, cache: KVNamespace, actor:
|
|||
|
||||
const timeline = await cache.get(actor.id + '/timeline/home')
|
||||
if (timeline === null) {
|
||||
return errors.timelineMissing()
|
||||
return new Response(JSON.stringify([]), { headers })
|
||||
}
|
||||
return new Response(timeline, { headers })
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ const headers = {
|
|||
'content-type': 'application/json; charset=utf-8',
|
||||
}
|
||||
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params, data }) => {
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env }) => {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const local = searchParams.get('local') === 'true'
|
||||
const remote = searchParams.get('remote') === 'true'
|
||||
|
@ -21,6 +21,7 @@ export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request,
|
|||
export async function handleRequest(
|
||||
domain: string,
|
||||
db: D1Database,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TODO: use only_media
|
||||
{ local = false, remote = false, only_media = false, offset = 0 } = {}
|
||||
): Promise<Response> {
|
||||
let localParam = LocalPreference.NotSet
|
||||
|
|
|
@ -1,44 +1,29 @@
|
|||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import { DEFAULT_THUMBNAIL } from 'wildebeest/backend/src/config'
|
||||
import type { InstanceConfigV2 } from 'wildebeest/backend/src/types/configs'
|
||||
|
||||
const INSTANCE_VERSION = '4.0.2'
|
||||
|
||||
export const onRequest: PagesFunction<Env, any> = async ({ env, request }) => {
|
||||
const domain = new URL(request.url).hostname
|
||||
return handleRequest(domain, env.DATABASE)
|
||||
return handleRequest(domain, env.DATABASE, env)
|
||||
}
|
||||
|
||||
export async function handleRequest(domain: string, db: D1Database) {
|
||||
export async function handleRequest(domain: string, db: D1Database, env: Env) {
|
||||
const headers = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': 'content-type, authorization',
|
||||
'content-type': 'application/json; charset=utf-8',
|
||||
}
|
||||
|
||||
const query = `
|
||||
SELECT * FROM instance_config WHERE key IN ('title', 'description', 'email', 'short_description', 'thumbnail')
|
||||
`
|
||||
const { results, error, success } = await db.prepare(query).all()
|
||||
if (!success) {
|
||||
throw new Error('SQL error: ' + error)
|
||||
}
|
||||
|
||||
const config: any = {}
|
||||
if (results) {
|
||||
for (let i = 0, len = results.length; i < len; i++) {
|
||||
const row: any = results[i]
|
||||
config[row.key] = row.value
|
||||
}
|
||||
}
|
||||
|
||||
const res: InstanceConfigV2 = {
|
||||
domain,
|
||||
title: config.title,
|
||||
title: env.INSTANCE_TITLE,
|
||||
version: INSTANCE_VERSION,
|
||||
source_url: 'https://github.com/cloudflare/wildebeest',
|
||||
description: config.description,
|
||||
description: env.INSTANCE_DESCR,
|
||||
thumbnail: {
|
||||
url: config.thumbnail,
|
||||
url: DEFAULT_THUMBNAIL,
|
||||
},
|
||||
languages: ['en'],
|
||||
registrations: {
|
||||
|
@ -47,7 +32,7 @@ export async function handleRequest(domain: string, db: D1Database) {
|
|||
enabled: false,
|
||||
},
|
||||
contact: {
|
||||
email: config.email,
|
||||
email: env.ADMIN_EMAIL,
|
||||
},
|
||||
rules: [],
|
||||
}
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
// especially)
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import type { Person } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { createPerson } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import * as errors from 'wildebeest/backend/src/errors'
|
||||
|
||||
export const onRequestPost: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => {
|
||||
export const onRequestPost: PagesFunction<Env, any, ContextData> = async ({ request, env }) => {
|
||||
return handlePostRequest(request, env.DATABASE, env.userKEK)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import { getPersonByEmail } from 'wildebeest/backend/src/activitypub/actors'
|
|||
// Extract the JWT token sent by Access (running before us).
|
||||
const extractJWTFromRequest = (request: Request) => request.headers.get('Cf-Access-Jwt-Assertion') || ''
|
||||
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ data, request, env }) => {
|
||||
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env }) => {
|
||||
return handleRequest(request, env.DATABASE, env.userKEK, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD)
|
||||
}
|
||||
|
||||
|
@ -58,8 +58,6 @@ export async function handleRequest(
|
|||
return new Response('', { status: 403 })
|
||||
}
|
||||
|
||||
const scope = url.searchParams.get('scope') || ''
|
||||
|
||||
const jwt = extractJWTFromRequest(request)
|
||||
const validate = access.generateValidator({ jwt, domain: accessDomain, aud: accessAud })
|
||||
await validate(request)
|
||||
|
|
|
@ -9,7 +9,7 @@ type Body = {
|
|||
code: string | null
|
||||
}
|
||||
|
||||
export const onRequest: PagesFunction<Env, any> = async ({ params, request, env }) => {
|
||||
export const onRequest: PagesFunction<Env, any> = async ({ request, env }) => {
|
||||
return handleRequest(env.DATABASE, request)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import * as access from 'wildebeest/backend/src/access'
|
||||
import * as errors from 'wildebeest/backend/src/errors'
|
||||
import { parse } from 'cookie'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import type { InstanceConfig } from 'wildebeest/backend/src/config'
|
||||
import { configure } from 'wildebeest/backend/src/config'
|
||||
|
||||
export const onRequestGet: PagesFunction<Env, any, ContextData> = async ({ request, data, env }) => {
|
||||
return handleGetRequest(env.DATABASE, request)
|
||||
}
|
||||
|
||||
// Route to test if Access has been configured properly
|
||||
export async function handleGetRequest(db: D1Database, request: Request): Promise<Response> {
|
||||
const query = `
|
||||
SELECT * FROM instance_config WHERE key IN ('accessDomain', 'accessAud')
|
||||
`
|
||||
const { results, error, success } = await db.prepare(query).all()
|
||||
if (!success) {
|
||||
throw new Error('SQL error: ' + error)
|
||||
}
|
||||
|
||||
const data: any = {}
|
||||
if (results) {
|
||||
for (let i = 0, len = results.length; i < len; i++) {
|
||||
const row: any = results[i]
|
||||
data[row.key] = row.value
|
||||
}
|
||||
}
|
||||
|
||||
const cookie = parse(request.headers.get('Cookie') || '')
|
||||
const jwt = cookie['CF_Authorization']
|
||||
if (!jwt) {
|
||||
return errors.notAuthorized('missing authorization')
|
||||
}
|
||||
|
||||
const domain = env.ACCESS_AUTH_DOMAIN
|
||||
|
||||
const validator = access.generateValidator({ jwt, domain, aud: env.ACCESS_AUD })
|
||||
const { payload } = await validator(request)
|
||||
|
||||
const identity = await access.getIdentity({ jwt, domain })
|
||||
if (!identity) {
|
||||
return errors.notAuthorized('failed to load identity')
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify({ email: identity.email }))
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
// First screen to configure and start the instance
|
||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||
import * as errors from 'wildebeest/backend/src/errors'
|
||||
import * as access from 'wildebeest/backend/src/access'
|
||||
import { parse } from 'cookie'
|
||||
import type { ContextData } from 'wildebeest/backend/src/types/context'
|
||||
import type { InstanceConfig } from 'wildebeest/backend/src/config'
|
||||
import * as config from 'wildebeest/backend/src/config'
|
||||
|
||||
export const onRequestPost: PagesFunction<Env, any> = async ({ request, env }) => {
|
||||
return handlePostRequest(request, env.DATABASE, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD)
|
||||
}
|
||||
|
||||
export const onRequestGet: PagesFunction<Env, any> = async (ctx) => {
|
||||
const { request, env, next } = ctx
|
||||
const cookie = parse(request.headers.get('Cookie') || '')
|
||||
const jwt = cookie['CF_Authorization']
|
||||
if (!jwt) {
|
||||
const { hostname } = new URL(request.url)
|
||||
const url = access.generateLoginURL({
|
||||
redirectURL: new URL('/start-instance', 'https://' + env.DOMAIN),
|
||||
domain: env.ACCESS_AUTH_DOMAIN,
|
||||
aud: env.ACCESS_AUD,
|
||||
})
|
||||
return Response.redirect(url)
|
||||
}
|
||||
|
||||
const frontend = await import('../frontend/server/entry.cloudflare-pages')
|
||||
return frontend.onRequest(ctx)
|
||||
}
|
||||
|
||||
export async function handlePostRequest(
|
||||
request: Request,
|
||||
db: D1Database,
|
||||
accessDomain: string,
|
||||
accessAud: string
|
||||
): Promise<Response> {
|
||||
const data = await request.json<InstanceConfig>()
|
||||
|
||||
const cookie = parse(request.headers.get('Cookie') || '')
|
||||
const jwt = cookie['CF_Authorization']
|
||||
if (!jwt) {
|
||||
return new Response('', { status: 401 })
|
||||
}
|
||||
|
||||
const validator = access.generateValidator({ jwt, domain: accessDomain, aud: accessAud })
|
||||
const { payload } = await validator(request)
|
||||
|
||||
const identity = await access.getIdentity({ jwt, domain: accessDomain })
|
||||
if (!identity) {
|
||||
return errors.notAuthorized('failed to load identity')
|
||||
}
|
||||
|
||||
await config.configure(db, data)
|
||||
await config.generateVAPIDKeys(db)
|
||||
|
||||
return new Response('', { status: 201 })
|
||||
}
|
|
@ -139,11 +139,6 @@ CREATE TABLE IF NOT EXISTS subscriptions (
|
|||
FOREIGN KEY(client_id) REFERENCES clients(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS instance_config (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS search_fts USING fts5 (
|
||||
type,
|
||||
name,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"eslint": "^8.29.0",
|
||||
"jest": "^29.3.1",
|
||||
"jest-environment-miniflare": "^2.11.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.8.1",
|
||||
"ts-jest": "^29.0.3",
|
||||
"typescript": "^4.9.4",
|
||||
|
@ -26,8 +27,10 @@
|
|||
"scripts": {
|
||||
"pretty": "npx prettier --list-different './**/*.ts'",
|
||||
"test": "NODE_OPTIONS=--experimental-vm-modules yarn jest",
|
||||
"lint": "npx eslint .",
|
||||
"lint:be": "npx eslint backend",
|
||||
"lint": "run-s lint:* --print-label",
|
||||
"lint:frontend": "npm --prefix frontend run lint",
|
||||
"lint:backend": "npx eslint backend",
|
||||
"lint:functions": "npx eslint functions",
|
||||
"build": "yarn --cwd frontend install && yarn --cwd frontend build",
|
||||
"d1": "NO_D1_WARNING=true wrangler d1",
|
||||
"pages": "NO_D1_WARNING=true wrangler pages",
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
const PROJECT_URL = 'https://github.com/cloudflare/wildebeest'
|
||||
const ONE_CLICK_BASE_URL = 'https://deploy.workers.cloudflare.com'
|
||||
const FIELDS = [
|
||||
{
|
||||
name: 'Zone ID',
|
||||
secret: 'CF_ZONE_ID',
|
||||
descr: 'Get your Zone ID from the Cloudflare Dashboard',
|
||||
},
|
||||
{
|
||||
name: 'Domain',
|
||||
secret: 'CF_DEPLOY_DOMAIN',
|
||||
descr: 'Domain on which your instance will be running',
|
||||
},
|
||||
{
|
||||
name: 'Instance title',
|
||||
secret: 'INSTANCE_TITLE',
|
||||
descr: 'Title of your instance',
|
||||
},
|
||||
{
|
||||
name: 'Administrator Email',
|
||||
secret: 'ADMIN_EMAIL',
|
||||
descr: 'An Email address that can be messaged regarding inquiries or issues',
|
||||
},
|
||||
{
|
||||
name: 'Instance description',
|
||||
secret: 'INSTANCE_DESCR',
|
||||
descr: 'A short, plain-text description of your instance',
|
||||
},
|
||||
]
|
||||
|
||||
const fields = FIELDS.map((x) => JSON.stringify(x))
|
||||
.map((v) => `fields=${v}`)
|
||||
.join('&')
|
||||
const url = new URL(`/?url=${PROJECT_URL}&authed=true&${fields}`, ONE_CLICK_BASE_URL)
|
||||
console.log(url.href)
|
|
@ -0,0 +1,6 @@
|
|||
import { webcrypto } from 'node:crypto'
|
||||
|
||||
const key = await webcrypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
|
||||
const serverKey = await webcrypto.subtle.exportKey("jwk", key.privateKey);
|
||||
|
||||
console.log(JSON.stringify(serverKey));
|
30
tf/main.tf
30
tf/main.tf
|
@ -32,6 +32,19 @@ variable "access_auth_domain" {
|
|||
sensitive = true
|
||||
}
|
||||
|
||||
variable "wd_instance_title" {
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
variable "wd_admin_email" {
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
variable "wd_instance_description" {
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
terraform {
|
||||
required_providers {
|
||||
cloudflare = {
|
||||
|
@ -81,6 +94,11 @@ resource "cloudflare_pages_project" "wildebeest_pages_project" {
|
|||
DOMAIN = sensitive(trimspace(var.cloudflare_deploy_domain))
|
||||
ACCESS_AUD = sensitive(cloudflare_access_application.wildebeest_access.aud)
|
||||
ACCESS_AUTH_DOMAIN = sensitive(var.access_auth_domain)
|
||||
|
||||
INSTANCE_TITLE = var.wd_instance_title
|
||||
ADMIN_EMAIL = var.wd_admin_email
|
||||
INSTANCE_DESCR = var.wd_instance_description
|
||||
VAPID_JWK = sensitive(file("${path.module}/vapid_jwk"))
|
||||
}
|
||||
kv_namespaces = {
|
||||
KV_CACHE = sensitive(cloudflare_workers_kv_namespace.wildebeest_cache.id)
|
||||
|
@ -120,15 +138,3 @@ resource "cloudflare_access_application" "wildebeest_access" {
|
|||
session_duration = "168h"
|
||||
auto_redirect_to_identity = false
|
||||
}
|
||||
|
||||
resource "cloudflare_access_policy" "policy" {
|
||||
application_id = cloudflare_access_application.wildebeest_access.id
|
||||
account_id = var.cloudflare_account_id
|
||||
name = "policy"
|
||||
precedence = "1"
|
||||
decision = "allow"
|
||||
|
||||
include {
|
||||
email = ["CHANGEME@example.com"]
|
||||
}
|
||||
}
|
||||
|
|
555
yarn.lock
555
yarn.lock
|
@ -1127,6 +1127,11 @@ array-union@^2.1.0:
|
|||
resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz"
|
||||
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
|
||||
|
||||
available-typed-arrays@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
|
||||
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
|
||||
|
||||
babel-jest@^29.3.1:
|
||||
version "29.3.1"
|
||||
resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz"
|
||||
|
@ -1297,6 +1302,14 @@ busboy@^1.6.0:
|
|||
dependencies:
|
||||
streamsearch "^1.1.0"
|
||||
|
||||
call-bind@^1.0.0, call-bind@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
|
||||
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
get-intrinsic "^1.0.2"
|
||||
|
||||
callsites@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz"
|
||||
|
@ -1317,7 +1330,7 @@ caniuse-lite@^1.0.30001400:
|
|||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001436.tgz"
|
||||
integrity sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg==
|
||||
|
||||
chalk@^2.0.0:
|
||||
chalk@^2.0.0, chalk@^2.4.1:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
|
@ -1457,6 +1470,17 @@ cron-schedule@^3.0.4:
|
|||
resolved "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.6.tgz"
|
||||
integrity sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==
|
||||
|
||||
cross-spawn@^6.0.5:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
|
||||
dependencies:
|
||||
nice-try "^1.0.4"
|
||||
path-key "^2.0.1"
|
||||
semver "^5.5.0"
|
||||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
|
||||
|
@ -1505,6 +1529,14 @@ deepmerge@^4.2.2:
|
|||
resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz"
|
||||
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
|
||||
|
||||
define-properties@^1.1.3, define-properties@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1"
|
||||
integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==
|
||||
dependencies:
|
||||
has-property-descriptors "^1.0.0"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
detect-libc@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz"
|
||||
|
@ -1568,6 +1600,62 @@ error-ex@^1.3.1:
|
|||
dependencies:
|
||||
is-arrayish "^0.2.1"
|
||||
|
||||
es-abstract@^1.19.0, es-abstract@^1.20.4:
|
||||
version "1.21.0"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.0.tgz#dd1b69ea5bfc3c27199c9753efd4de015102c252"
|
||||
integrity sha512-GUGtW7eXQay0c+PRq0sGIKSdaBorfVqsCMhGHo4elP7YVqZu9nCZS4UkK4gv71gOWNMra/PaSKD3ao1oWExO0g==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
es-set-tostringtag "^2.0.0"
|
||||
es-to-primitive "^1.2.1"
|
||||
function-bind "^1.1.1"
|
||||
function.prototype.name "^1.1.5"
|
||||
get-intrinsic "^1.1.3"
|
||||
get-symbol-description "^1.0.0"
|
||||
globalthis "^1.0.3"
|
||||
gopd "^1.0.1"
|
||||
has "^1.0.3"
|
||||
has-property-descriptors "^1.0.0"
|
||||
has-proto "^1.0.1"
|
||||
has-symbols "^1.0.3"
|
||||
internal-slot "^1.0.4"
|
||||
is-array-buffer "^3.0.0"
|
||||
is-callable "^1.2.7"
|
||||
is-negative-zero "^2.0.2"
|
||||
is-regex "^1.1.4"
|
||||
is-shared-array-buffer "^1.0.2"
|
||||
is-string "^1.0.7"
|
||||
is-typed-array "^1.1.10"
|
||||
is-weakref "^1.0.2"
|
||||
object-inspect "^1.12.2"
|
||||
object-keys "^1.1.1"
|
||||
object.assign "^4.1.4"
|
||||
regexp.prototype.flags "^1.4.3"
|
||||
safe-regex-test "^1.0.0"
|
||||
string.prototype.trimend "^1.0.6"
|
||||
string.prototype.trimstart "^1.0.6"
|
||||
typed-array-length "^1.0.4"
|
||||
unbox-primitive "^1.0.2"
|
||||
which-typed-array "^1.1.9"
|
||||
|
||||
es-set-tostringtag@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8"
|
||||
integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.3"
|
||||
has "^1.0.3"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
es-to-primitive@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
|
||||
integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
|
||||
dependencies:
|
||||
is-callable "^1.1.4"
|
||||
is-date-object "^1.0.1"
|
||||
is-symbol "^1.0.2"
|
||||
|
||||
esbuild-android-64@0.14.51:
|
||||
version "0.14.51"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.51.tgz#414a087cb0de8db1e347ecca6c8320513de433db"
|
||||
|
@ -1979,6 +2067,13 @@ flatted@^3.1.0:
|
|||
resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz"
|
||||
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
|
||||
|
||||
for-each@^0.3.3:
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
|
||||
integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==
|
||||
dependencies:
|
||||
is-callable "^1.1.3"
|
||||
|
||||
fs-constants@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz"
|
||||
|
@ -1999,6 +2094,21 @@ function-bind@^1.1.1:
|
|||
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
||||
function.prototype.name@^1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621"
|
||||
integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.19.0"
|
||||
functions-have-names "^1.2.2"
|
||||
|
||||
functions-have-names@^1.2.2:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
||||
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
||||
|
||||
gensync@^1.0.0-beta.2:
|
||||
version "1.0.0-beta.2"
|
||||
resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz"
|
||||
|
@ -2009,6 +2119,15 @@ get-caller-file@^2.0.5:
|
|||
resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385"
|
||||
integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
has-symbols "^1.0.3"
|
||||
|
||||
get-package-type@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz"
|
||||
|
@ -2019,6 +2138,14 @@ get-stream@^6.0.0, get-stream@^6.0.1:
|
|||
resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz"
|
||||
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
|
||||
|
||||
get-symbol-description@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6"
|
||||
integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
get-intrinsic "^1.1.1"
|
||||
|
||||
github-from-package@0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz"
|
||||
|
@ -2062,6 +2189,13 @@ globals@^13.15.0:
|
|||
dependencies:
|
||||
type-fest "^0.20.2"
|
||||
|
||||
globalthis@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf"
|
||||
integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
|
||||
globby@^11.1.0:
|
||||
version "11.1.0"
|
||||
resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz"
|
||||
|
@ -2074,7 +2208,14 @@ globby@^11.1.0:
|
|||
merge2 "^1.4.1"
|
||||
slash "^3.0.0"
|
||||
|
||||
graceful-fs@^4.2.9:
|
||||
gopd@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
|
||||
integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.3"
|
||||
|
||||
graceful-fs@^4.1.2, graceful-fs@^4.2.9:
|
||||
version "4.2.10"
|
||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz"
|
||||
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
||||
|
@ -2084,6 +2225,11 @@ grapheme-splitter@^1.0.4:
|
|||
resolved "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz"
|
||||
integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
|
||||
|
||||
has-bigints@^1.0.1, has-bigints@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
|
||||
integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
|
||||
|
||||
has-flag@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
|
||||
|
@ -2094,6 +2240,30 @@ has-flag@^4.0.0:
|
|||
resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
|
||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||
|
||||
has-property-descriptors@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861"
|
||||
integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.1"
|
||||
|
||||
has-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0"
|
||||
integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==
|
||||
|
||||
has-symbols@^1.0.2, has-symbols@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
|
||||
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
|
||||
|
||||
has-tostringtag@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
|
||||
integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==
|
||||
dependencies:
|
||||
has-symbols "^1.0.2"
|
||||
|
||||
has@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz"
|
||||
|
@ -2101,6 +2271,11 @@ has@^1.0.3:
|
|||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
hosted-git-info@^2.1.4:
|
||||
version "2.8.9"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
|
||||
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
|
||||
|
||||
html-escaper@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz"
|
||||
|
@ -2180,11 +2355,36 @@ ini@~1.3.0:
|
|||
resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz"
|
||||
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
|
||||
|
||||
internal-slot@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3"
|
||||
integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.3"
|
||||
has "^1.0.3"
|
||||
side-channel "^1.0.4"
|
||||
|
||||
is-array-buffer@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a"
|
||||
integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
get-intrinsic "^1.1.3"
|
||||
is-typed-array "^1.1.10"
|
||||
|
||||
is-arrayish@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz"
|
||||
integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
|
||||
|
||||
is-bigint@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
|
||||
integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==
|
||||
dependencies:
|
||||
has-bigints "^1.0.1"
|
||||
|
||||
is-binary-path@~2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz"
|
||||
|
@ -2192,6 +2392,19 @@ is-binary-path@~2.1.0:
|
|||
dependencies:
|
||||
binary-extensions "^2.0.0"
|
||||
|
||||
is-boolean-object@^1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
|
||||
integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
|
||||
integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
|
||||
|
||||
is-core-module@^2.9.0:
|
||||
version "2.11.0"
|
||||
resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz"
|
||||
|
@ -2199,6 +2412,13 @@ is-core-module@^2.9.0:
|
|||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-date-object@^1.0.1:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
|
||||
integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
|
||||
dependencies:
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-extglob@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
|
||||
|
@ -2221,6 +2441,18 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
|
|||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-negative-zero@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
|
||||
integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
|
||||
|
||||
is-number-object@^1.0.4:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc"
|
||||
integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==
|
||||
dependencies:
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-number@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
|
||||
|
@ -2231,6 +2463,21 @@ is-path-inside@^3.0.3:
|
|||
resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz"
|
||||
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
||||
|
||||
is-regex@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
|
||||
integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-shared-array-buffer@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79"
|
||||
integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
|
||||
is-stream@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz"
|
||||
|
@ -2241,6 +2488,38 @@ is-stream@^3.0.0:
|
|||
resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz"
|
||||
integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
|
||||
|
||||
is-string@^1.0.5, is-string@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
|
||||
integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
|
||||
dependencies:
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-symbol@^1.0.2, is-symbol@^1.0.3:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
|
||||
integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
|
||||
dependencies:
|
||||
has-symbols "^1.0.2"
|
||||
|
||||
is-typed-array@^1.1.10, is-typed-array@^1.1.9:
|
||||
version "1.1.10"
|
||||
resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f"
|
||||
integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==
|
||||
dependencies:
|
||||
available-typed-arrays "^1.0.5"
|
||||
call-bind "^1.0.2"
|
||||
for-each "^0.3.3"
|
||||
gopd "^1.0.1"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-weakref@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
|
||||
integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
|
||||
|
@ -2694,6 +2973,11 @@ jsesc@^2.5.1:
|
|||
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"
|
||||
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
|
||||
|
||||
json-parse-better-errors@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
|
||||
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
|
||||
|
||||
json-parse-even-better-errors@^2.3.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz"
|
||||
|
@ -2742,6 +3026,16 @@ lines-and-columns@^1.1.6:
|
|||
resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz"
|
||||
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
|
||||
|
||||
load-json-file@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
|
||||
integrity sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
parse-json "^4.0.0"
|
||||
pify "^3.0.0"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
locate-path@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz"
|
||||
|
@ -2804,6 +3098,11 @@ makeerror@1.0.12:
|
|||
dependencies:
|
||||
tmpl "1.0.5"
|
||||
|
||||
memorystream@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2"
|
||||
integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz"
|
||||
|
@ -2916,6 +3215,11 @@ natural-compare@^1.4.0:
|
|||
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
|
||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||
|
||||
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==
|
||||
|
||||
node-abi@^3.3.0:
|
||||
version "3.30.0"
|
||||
resolved "https://registry.npmjs.org/node-abi/-/node-abi-3.30.0.tgz"
|
||||
|
@ -2938,11 +3242,36 @@ node-releases@^2.0.6:
|
|||
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz"
|
||||
integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
|
||||
|
||||
normalize-package-data@^2.3.2:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
||||
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
|
||||
dependencies:
|
||||
hosted-git-info "^2.1.4"
|
||||
resolve "^1.10.0"
|
||||
semver "2 || 3 || 4 || 5"
|
||||
validate-npm-package-license "^3.0.1"
|
||||
|
||||
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
|
||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||
|
||||
npm-run-all@^4.1.5:
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba"
|
||||
integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==
|
||||
dependencies:
|
||||
ansi-styles "^3.2.1"
|
||||
chalk "^2.4.1"
|
||||
cross-spawn "^6.0.5"
|
||||
memorystream "^0.3.1"
|
||||
minimatch "^3.0.4"
|
||||
pidtree "^0.3.0"
|
||||
read-pkg "^3.0.0"
|
||||
shell-quote "^1.6.1"
|
||||
string.prototype.padend "^3.0.0"
|
||||
|
||||
npm-run-path@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz"
|
||||
|
@ -2967,6 +3296,26 @@ npx-import@^1.1.3:
|
|||
semver "^7.3.7"
|
||||
validate-npm-package-name "^4.0.0"
|
||||
|
||||
object-inspect@^1.12.2, object-inspect@^1.9.0:
|
||||
version "1.12.2"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
|
||||
integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
|
||||
|
||||
object-keys@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
|
||||
|
||||
object.assign@^4.1.4:
|
||||
version "4.1.4"
|
||||
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
|
||||
integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.4"
|
||||
has-symbols "^1.0.3"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
|
||||
|
@ -3040,6 +3389,14 @@ parent-module@^1.0.0:
|
|||
dependencies:
|
||||
callsites "^3.0.0"
|
||||
|
||||
parse-json@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
|
||||
integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==
|
||||
dependencies:
|
||||
error-ex "^1.3.1"
|
||||
json-parse-better-errors "^1.0.1"
|
||||
|
||||
parse-json@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz"
|
||||
|
@ -3065,6 +3422,11 @@ path-is-absolute@^1.0.0:
|
|||
resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz"
|
||||
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
|
||||
|
||||
path-key@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
|
||||
integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==
|
||||
|
||||
path-key@^3.0.0, path-key@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz"
|
||||
|
@ -3085,6 +3447,13 @@ path-to-regexp@^6.2.0:
|
|||
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz"
|
||||
integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==
|
||||
|
||||
path-type@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
|
||||
integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==
|
||||
dependencies:
|
||||
pify "^3.0.0"
|
||||
|
||||
path-type@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz"
|
||||
|
@ -3100,6 +3469,16 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1:
|
|||
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
pidtree@^0.3.0:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a"
|
||||
integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==
|
||||
|
||||
pify@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
||||
integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==
|
||||
|
||||
pirates@^4.0.4:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz"
|
||||
|
@ -3190,6 +3569,15 @@ react-is@^18.0.0:
|
|||
resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz"
|
||||
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
||||
|
||||
read-pkg@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
|
||||
integrity sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==
|
||||
dependencies:
|
||||
load-json-file "^4.0.0"
|
||||
normalize-package-data "^2.3.2"
|
||||
path-type "^3.0.0"
|
||||
|
||||
readable-stream@^3.1.1, readable-stream@^3.4.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz"
|
||||
|
@ -3206,6 +3594,15 @@ readdirp@~3.6.0:
|
|||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
regexp.prototype.flags@^1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac"
|
||||
integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.3"
|
||||
functions-have-names "^1.2.2"
|
||||
|
||||
regexpp@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz"
|
||||
|
@ -3238,7 +3635,7 @@ resolve.exports@^1.1.0:
|
|||
resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz"
|
||||
integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==
|
||||
|
||||
resolve@^1.20.0:
|
||||
resolve@^1.10.0, resolve@^1.20.0:
|
||||
version "1.22.1"
|
||||
resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz"
|
||||
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
|
||||
|
@ -3301,6 +3698,15 @@ safe-buffer@^5.0.1, safe-buffer@~5.2.0:
|
|||
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
safe-regex-test@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295"
|
||||
integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
get-intrinsic "^1.1.3"
|
||||
is-regex "^1.1.4"
|
||||
|
||||
selfsigned@^2.0.0, selfsigned@^2.0.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz"
|
||||
|
@ -3313,6 +3719,11 @@ semiver@^1.1.0:
|
|||
resolved "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz"
|
||||
integrity sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==
|
||||
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.5.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
semver@7.x, semver@^7.0.0, semver@^7.3.5, semver@^7.3.7:
|
||||
version "7.3.8"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz"
|
||||
|
@ -3330,6 +3741,13 @@ set-cookie-parser@^2.4.8:
|
|||
resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz"
|
||||
integrity sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==
|
||||
|
||||
shebang-command@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
||||
integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==
|
||||
dependencies:
|
||||
shebang-regex "^1.0.0"
|
||||
|
||||
shebang-command@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz"
|
||||
|
@ -3337,16 +3755,30 @@ shebang-command@^2.0.0:
|
|||
dependencies:
|
||||
shebang-regex "^3.0.0"
|
||||
|
||||
shebang-regex@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
|
||||
integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==
|
||||
|
||||
shebang-regex@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz"
|
||||
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
||||
|
||||
shell-quote@^1.7.3:
|
||||
shell-quote@^1.6.1, shell-quote@^1.7.3:
|
||||
version "1.7.4"
|
||||
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.4.tgz#33fe15dee71ab2a81fcbd3a52106c5cfb9fb75d8"
|
||||
integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==
|
||||
|
||||
side-channel@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
|
||||
integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
|
||||
dependencies:
|
||||
call-bind "^1.0.0"
|
||||
get-intrinsic "^1.0.2"
|
||||
object-inspect "^1.9.0"
|
||||
|
||||
signal-exit@^3.0.3, signal-exit@^3.0.7:
|
||||
version "3.0.7"
|
||||
resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz"
|
||||
|
@ -3412,6 +3844,32 @@ spawn-command@^0.0.2-1:
|
|||
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0"
|
||||
integrity sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==
|
||||
|
||||
spdx-correct@^3.0.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
|
||||
integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==
|
||||
dependencies:
|
||||
spdx-expression-parse "^3.0.0"
|
||||
spdx-license-ids "^3.0.0"
|
||||
|
||||
spdx-exceptions@^2.1.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d"
|
||||
integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
|
||||
|
||||
spdx-expression-parse@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
|
||||
integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
|
||||
dependencies:
|
||||
spdx-exceptions "^2.1.0"
|
||||
spdx-license-ids "^3.0.0"
|
||||
|
||||
spdx-license-ids@^3.0.0:
|
||||
version "3.0.12"
|
||||
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779"
|
||||
integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==
|
||||
|
||||
sprintf-js@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
|
||||
|
@ -3451,6 +3909,33 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
|||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string.prototype.padend@^3.0.0:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.4.tgz#2c43bb3a89eb54b6750de5942c123d6c98dd65b6"
|
||||
integrity sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.4"
|
||||
es-abstract "^1.20.4"
|
||||
|
||||
string.prototype.trimend@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533"
|
||||
integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.4"
|
||||
es-abstract "^1.20.4"
|
||||
|
||||
string.prototype.trimstart@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4"
|
||||
integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.4"
|
||||
es-abstract "^1.20.4"
|
||||
|
||||
string_decoder@^1.1.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
|
||||
|
@ -3465,6 +3950,11 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
|||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-bom@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
|
||||
integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
|
||||
|
||||
strip-bom@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz"
|
||||
|
@ -3633,11 +4123,30 @@ type-fest@^0.21.3:
|
|||
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz"
|
||||
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
|
||||
|
||||
typed-array-length@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
|
||||
integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
for-each "^0.3.3"
|
||||
is-typed-array "^1.1.9"
|
||||
|
||||
typescript@^4.9.4:
|
||||
version "4.9.4"
|
||||
resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz"
|
||||
integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==
|
||||
|
||||
unbox-primitive@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
|
||||
integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
has-bigints "^1.0.2"
|
||||
has-symbols "^1.0.3"
|
||||
which-boxed-primitive "^1.0.2"
|
||||
|
||||
undici@5.9.1:
|
||||
version "5.9.1"
|
||||
resolved "https://registry.npmjs.org/undici/-/undici-5.9.1.tgz"
|
||||
|
@ -3677,6 +4186,14 @@ v8-to-istanbul@^9.0.1:
|
|||
"@types/istanbul-lib-coverage" "^2.0.1"
|
||||
convert-source-map "^1.6.0"
|
||||
|
||||
validate-npm-package-license@^3.0.1:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
|
||||
integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
|
||||
dependencies:
|
||||
spdx-correct "^3.0.0"
|
||||
spdx-expression-parse "^3.0.0"
|
||||
|
||||
validate-npm-package-name@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz"
|
||||
|
@ -3691,6 +4208,36 @@ walker@^1.0.8:
|
|||
dependencies:
|
||||
makeerror "1.0.12"
|
||||
|
||||
which-boxed-primitive@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
|
||||
integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
|
||||
dependencies:
|
||||
is-bigint "^1.0.1"
|
||||
is-boolean-object "^1.1.0"
|
||||
is-number-object "^1.0.4"
|
||||
is-string "^1.0.5"
|
||||
is-symbol "^1.0.3"
|
||||
|
||||
which-typed-array@^1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6"
|
||||
integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==
|
||||
dependencies:
|
||||
available-typed-arrays "^1.0.5"
|
||||
call-bind "^1.0.2"
|
||||
for-each "^0.3.3"
|
||||
gopd "^1.0.1"
|
||||
has-tostringtag "^1.0.0"
|
||||
is-typed-array "^1.1.10"
|
||||
|
||||
which@^1.2.9:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
which@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz"
|
||||
|
|
Ładowanie…
Reference in New Issue