diff --git a/.gitignore b/.gitignore index aade89c7..cba7ef1b 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,7 @@ pnpm-works runner-results vite.config.ts.timestamp* yalc.lock -yarn.lock \ No newline at end of file +yarn.lock +.parcel-cache/**/* +data.json.* +*.log.* \ No newline at end of file diff --git a/demos/quiet-sandbox/.gitignore b/demos/quiet-sandbox/.gitignore index a86f8f58..08c8f3e7 100644 --- a/demos/quiet-sandbox/.gitignore +++ b/demos/quiet-sandbox/.gitignore @@ -1,2 +1,5 @@ .peerid.* -.orbitdb/**/* \ No newline at end of file +.orbitdb/**/* +flamegraphs/**/* +isolate**.log.* +data.json.js \ No newline at end of file diff --git a/demos/quiet-sandbox/package.json b/demos/quiet-sandbox/package.json index afad9ded..f02888c5 100644 --- a/demos/quiet-sandbox/package.json +++ b/demos/quiet-sandbox/package.json @@ -11,7 +11,9 @@ "build": "npx tsc", "test": "echo \"Error: no test specified\" && exit 1", "ig": "tsc && npm i -g .", - "start": "node dist/index.js interactive" + "start": "node dist/index.js interactive", + "test:perf:isla": "DEBUG=localfirst*,qsb* node --max-old-space-size=8192 --experimental-global-customevent --max_old_space_size=8192 dist/scripts/isla-perf/index.js interactive", + "test:perf:isla:graphs": "parcel src/scripts/isla-perf/index.html --open" }, "keywords": [], "author": "", @@ -24,47 +26,63 @@ "@inquirer/prompts": "^5.3.0", "@inquirer/type": "^1.5.1", "@libp2p/bootstrap": "^10.0.24", + "@libp2p/crypto": "4.1.6", "@libp2p/echo": "^1.1.1", "@libp2p/identify": "^2.0.2", "@libp2p/interface": "^1.6.1", "@libp2p/interface-internal": "^1.3.1", "@libp2p/kad-dht": "^12.0.17", + "@libp2p/mdns": "10.1.5", "@libp2p/peer-id": "^4.2.1", "@libp2p/peer-id-factory": "^4.2.1", "@libp2p/tcp": "^9.0.26", - "@libp2p/crypto": "4.1.6", "@localfirst/auth": "workspace:*", "@localfirst/shared": "workspace:*", + "@multiformats/multiaddr": "^12.3.0", "@orbitdb/core": "^2.2.0", + "ansi-colors": "4.1.3", "ansi-escapes": "^7.0.0", + "bs58": "^6.0.0", "chalk": "^4.1.2", + "chart.js": "^4.4.3", "clipboardy": "4.0.0", "colors": "^1.4.0", "datastore-core": "9.2.9", + "debug": "4.3.6", + "fastq": "^1.17.1", "figlet": "^1.7.0", "figures": "^6.1.0", "getmac": "^6.6.0", "helia": "^4.2.4", "inquirer": "^10.0.1", "inquirer-select-pro": "^1.0.0-alpha.6", + "interface-datastore": "8.3.0", "it-length-prefixed": "^9.0.4", "it-map": "^3.1.1", "it-pipe": "^3.0.1", "it-protobuf-stream": "^1.1.3", "it-pushable": "^3.2.3", "libp2p": "^1.8.1", + "luxon": "3.5.0", "multiformats": "^13.2.0", - "@multiformats/multiaddr": "^12.3.0", "pad": "^3.2.0", "quiet-sandbox": "link:", + "rng": "0.2.2", "type-fest": "^4.21.0", - "uint8arraylist": "^2.4.8" + "uint8arraylist": "^2.4.8", + "weald": "1.0.2" }, "devDependencies": { + "@types/bs58": "4.0.4", + "@types/debug": "4.1.12", "@types/figlet": "^1.5.8", "@types/inquirer": "^9.0.7", + "@types/luxon": "3.4.2", "@types/node": "^18.18.13", + "0x": "^5.7.0", "eslint": "^9.7.0", + "nodemark": "0.3.0", + "parcel": "^2.6.2", "prettier": "^3.1.0", "ts-node": "^10.9.2", "typescript": "^5.5.3" diff --git a/demos/quiet-sandbox/src/auth/chain.ts b/demos/quiet-sandbox/src/auth/chain.ts index ca32279d..d0afe9f8 100644 --- a/demos/quiet-sandbox/src/auth/chain.ts +++ b/demos/quiet-sandbox/src/auth/chain.ts @@ -2,31 +2,37 @@ * Handles generating the chain and aggregating all chain operations */ -import * as auth from '@localfirst/auth' +import * as lfa from '@localfirst/auth' import { LoadedSigChain } from './types.js' import { UserService } from './services/members/userService.js' import { RoleService } from './services/roles/roleService.js' import { ChannelService } from './services/roles/channelService.js' import { DeviceService } from './services/members/deviceService.js' import { InviteService } from './services/invites/inviteService.js' -import { DMService } from './services/dm/dmService.js' import { CryptoService } from './services/crypto/cryptoService.js' import { RoleName } from './services/roles/roles.js' +import { findAllByKeyAndReplace } from '../utils/utils.js' class SigChain { - private _team: auth.Team + private _team: lfa.Team private _users: UserService | null = null private _devices: DeviceService | null = null private _roles: RoleService | null = null private _channels: ChannelService | null = null - private _dms: DMService | null = null private _invites: InviteService | null = null private _crypto: CryptoService | null = null - private constructor(team: auth.Team) { + private constructor(team: lfa.Team) { this._team = team } + private static init(team: lfa.Team): SigChain { + const sigChain = new SigChain(team) + sigChain.initServices() + + return sigChain + } + /** * Create a brand new SigChain with a given name and also generate the initial user with a given name * @@ -35,7 +41,7 @@ class SigChain { */ public static create(teamName: string, username: string): LoadedSigChain { const context = UserService.create(username) - const team: auth.Team = this.lfa.createTeam(teamName, context) + const team: lfa.Team = lfa.createTeam(teamName, context) const sigChain = this.init(team) sigChain.roles.createWithMembers(RoleName.MEMBER, [context.user.userId]) @@ -47,7 +53,7 @@ class SigChain { } } - public static createFromTeam(team: auth.Team, context: auth.LocalUserContext): LoadedSigChain { + public static createFromTeam(team: lfa.Team, context: lfa.LocalUserContext): LoadedSigChain { const sigChain = this.init(team) return { context, @@ -55,33 +61,11 @@ class SigChain { } } - // TODO: Is this the right signature for this method? - public static join(context: auth.LocalUserContext, serializedTeam: Uint8Array, teamKeyRing: auth.Keyring): LoadedSigChain { - const team: auth.Team = this.lfa.loadTeam(serializedTeam, context, teamKeyRing) - team.join(teamKeyRing) - - const sigChain = this.init(team) - // sigChain.persist() - - return { - sigChain, - context - } - } - - private static init(team: auth.Team): SigChain { - const sigChain = new SigChain(team) - sigChain.initServices() - - return sigChain - } - private initServices() { this._users = UserService.init(this) this._devices = DeviceService.init(this) this._roles = RoleService.init(this) this._channels = ChannelService.init(this) - this._dms = DMService.init(this) this._invites = InviteService.init(this) this._crypto = CryptoService.init(this) } @@ -91,14 +75,25 @@ class SigChain { return this.team.save() // this doesn't actually do anything but create the new state to save } - get team(): auth.Team { + get team(): lfa.Team { return this._team } - get teamGraph(): auth.TeamGraph { + get teamGraph(): lfa.TeamGraph { return this._team.graph } + get minifiedTeamGraph(): lfa.TeamGraph { + return findAllByKeyAndReplace(JSON.parse(JSON.stringify(this.teamGraph)), [ + { + key: 'data', + replace: { + replacerFunc: (dataArray: any[]) => Buffer.from(dataArray).toString('base64') + } + } + ]) + } + get users(): UserService { return this._users! } @@ -119,17 +114,9 @@ class SigChain { return this._invites! } - get dms(): DMService { - return this._dms! - } - get crypto(): CryptoService { return this._crypto! } - - static get lfa(): typeof auth { - return auth - } } export { diff --git a/demos/quiet-sandbox/src/auth/services/crypto/cryptoService.ts b/demos/quiet-sandbox/src/auth/services/crypto/cryptoService.ts index 4e254374..3c311b8b 100644 --- a/demos/quiet-sandbox/src/auth/services/crypto/cryptoService.ts +++ b/demos/quiet-sandbox/src/auth/services/crypto/cryptoService.ts @@ -1,11 +1,12 @@ /** * Handles invite-related chain operations */ +import * as bs58 from 'bs58' import { EncryptedAndSignedPayload, EncryptedPayload, EncryptionScope, EncryptionScopeType } from "./types.js" import { BaseChainService } from "../baseService.js" import { SigChain } from "../../chain.js" -import { Base58, Keyset, KeysetWithSecrets, LocalUserContext, Member, SignedEnvelope } from "@localfirst/auth" +import * as lfa from "@localfirst/auth" import { DEFAULT_SEARCH_OPTIONS, MemberSearchOptions } from "../members/types.js" import { ChannelService } from "../roles/channelService.js" @@ -15,124 +16,141 @@ class CryptoService extends BaseChainService { } // TODO: Can we get other members' keys by generation? - public getPublicKeysForMembersById(memberIds: string[], searchOptions: MemberSearchOptions = DEFAULT_SEARCH_OPTIONS): Keyset[] { + public getPublicKeysForMembersById(memberIds: string[], searchOptions: MemberSearchOptions = DEFAULT_SEARCH_OPTIONS): lfa.Keyset[] { const members = this.sigChain.users.getMembersById(memberIds, searchOptions) - return members.map((member: Member) => { + return members.map((member: lfa.Member) => { return member.keys }) } - public getKeysForRole(roleName: string, generation?: number): KeysetWithSecrets { + // ISLA: Can be deleted (Reason: only used for printing data in sandbox) + public getKeysForRole(roleName: string, generation?: number): lfa.KeysetWithSecrets { return this.sigChain.team.roleKeys(roleName, generation) } - public getKeysForChannel(channelName: string, generation?: number): KeysetWithSecrets { + // ISLA: Can be deleted (Reason: only used for printing data in sandbox) + public getKeysForChannel(channelName: string, generation?: number): lfa.KeysetWithSecrets { return this.getKeysForRole(ChannelService.getPrivateChannelRoleName(channelName), generation) } - public encryptAndSign(message: any, scope: EncryptionScope, context: LocalUserContext): EncryptedAndSignedPayload { - let recipientKey: Base58 - let senderKey: Base58 - let generation: number - if (scope.type === EncryptionScopeType.ROLE) { - if (scope.name == null) { - throw new Error(`Must provide a role name when encryption scope is set to ${scope.type}`) - } - const keys = this.getKeysForRole(scope.name) - recipientKey = keys.encryption.publicKey - senderKey = keys.encryption.secretKey - generation = keys.generation - } else if (scope.type === EncryptionScopeType.CHANNEL) { - if (scope.name == null) { - throw new Error(`Must provide a channel name when encryption scope is set to ${scope.type}`) - } - const keys = this.getKeysForChannel(scope.name) - recipientKey = keys.encryption.publicKey - senderKey = keys.encryption.secretKey - generation = keys.generation - } else if (scope.type === EncryptionScopeType.USER) { - if (scope.name == null) { - throw new Error(`Must provide a user ID when encryption scope is set to ${scope.type}`) + public encryptAndSign(message: any, scope: EncryptionScope, context: lfa.LocalUserContext): EncryptedAndSignedPayload { + let encryptedPayload: EncryptedPayload + switch (scope.type) { + // Symmetrical Encryption Types + case EncryptionScopeType.CHANNEL: + case EncryptionScopeType.ROLE: + case EncryptionScopeType.TEAM: + encryptedPayload = this.symEncrypt(message, scope) + break + // Asymmetrical Encryption Types + case EncryptionScopeType.USER: + encryptedPayload = this.asymUserEncrypt(message, scope, context) + break + // Unknown Type + default: + throw new Error(`Unknown encryption type ${scope.type} provided!`) + } + + const signature = this.sigChain.team.sign(encryptedPayload.contents) + + return { + encrypted: encryptedPayload, + signature, + ts: Date.now(), + username: context.user.userName + } + } + + private symEncrypt(message: any, scope: EncryptionScope): EncryptedPayload { + if (scope.type != EncryptionScopeType.TEAM && scope.name == null) { + throw new Error(`Must provide a scope name when encryption scope is set to ${scope.type}`) + } + + const envelope = this.sigChain.team.encrypt(message, scope.name) + return { + contents: bs58.default.encode(envelope.contents) as lfa.Base58, + scope: { + ...scope, + generation: envelope.recipient.generation } - const recipientKeys = this.getPublicKeysForMembersById([scope.name]) - recipientKey = recipientKeys[0].encryption - senderKey = context.user.keys.encryption.secretKey - generation = recipientKeys[0].generation - } else if (scope.type === EncryptionScopeType.TEAM) { - const keys = this.sigChain.team.teamKeys() - recipientKey = keys.encryption.publicKey - senderKey = keys.encryption.secretKey - generation = keys.generation - } else { - throw new Error(`Unknown encryption scope type ${scope.type}`) + } + } + + private asymUserEncrypt(message: any, scope: EncryptionScope, context: lfa.LocalUserContext): EncryptedPayload { + if (scope.name == null) { + throw new Error(`Must provide a user ID when encryption scope is set to ${scope.type}`) } - const encryptedContents = SigChain.lfa.asymmetric.encrypt({ + const recipientKeys = this.getPublicKeysForMembersById([scope.name]) + const recipientKey = recipientKeys[0].encryption + const senderKey = context.user.keys.encryption.secretKey + const generation = recipientKeys[0].generation + + const encryptedContents = lfa.asymmetric.encrypt({ secret: message, senderSecretKey: senderKey, recipientPublicKey: recipientKey }) - const signature = this.sigChain.team.sign(encryptedContents) - return { - encrypted: { - contents: encryptedContents, - scope: { - ...scope, - generation - } - }, - signature, - ts: Date.now(), - username: context.user.userName + contents: encryptedContents, + scope: { + ...scope, + generation + } } } - public decryptAndVerify(encrypted: EncryptedPayload, signature: SignedEnvelope, context: LocalUserContext): any { + public decryptAndVerify(encrypted: EncryptedPayload, signature: lfa.SignedEnvelope, context: lfa.LocalUserContext): any { const isValid = this.sigChain.team.verify(signature) if (!isValid) { throw new Error(`Couldn't verify signature on message`) } - let recipientKey: Base58 - let senderKey: Base58 - if (encrypted.scope.type === EncryptionScopeType.ROLE) { - if (encrypted.scope.name == null) { - throw new Error(`Must provide a role name when encryption scope is set to ${encrypted.scope.type}`) - } - const keys = this.getKeysForRole(encrypted.scope.name, encrypted.scope.generation) - recipientKey = keys.encryption.secretKey - senderKey = keys.encryption.publicKey - } else if (encrypted.scope.type === EncryptionScopeType.CHANNEL) { - if (encrypted.scope.name == null) { - throw new Error(`Must provide a channel name when encryption scope is set to ${encrypted.scope.type}`) - } - const keys = this.getKeysForChannel(encrypted.scope.name, encrypted.scope.generation) - recipientKey = keys.encryption.secretKey - senderKey = keys.encryption.publicKey - } else if (encrypted.scope.type === EncryptionScopeType.USER) { - if (encrypted.scope.name == null) { - throw new Error(`Must provide a user ID when encryption scope is set to ${encrypted.scope.type}`) + switch (encrypted.scope.type) { + // Symmetrical Encryption Types + case EncryptionScopeType.CHANNEL: + case EncryptionScopeType.ROLE: + case EncryptionScopeType.TEAM: + return this.symDecrypt(encrypted) + // Asymmetrical Encryption Types + case EncryptionScopeType.USER: + return this.asymUserDecrypt(encrypted, signature, context) + // Unknown Type + default: + throw new Error(`Unknown encryption scope type ${encrypted.scope.type}`) + } + } + + private symDecrypt(encrypted: EncryptedPayload): any { + if (encrypted.scope.type !== EncryptionScopeType.TEAM && encrypted.scope.name == null) { + throw new Error(`Must provide a scope name when encryption scope is set to ${encrypted.scope.type}`) + } + + return this.sigChain.team.decrypt({ + contents: bs58.default.decode(encrypted.contents), + recipient: { + ...encrypted.scope, + // you don't need a name on the scope when encrypting but you need one for decrypting because of how LFA searches for keys in lockboxes + name: encrypted.scope.type === EncryptionScopeType.TEAM ? EncryptionScopeType.TEAM : encrypted.scope.name! } - const senderKeys = this.sigChain.crypto.getPublicKeysForMembersById([signature.author.name]) - recipientKey = context.user.keys.encryption.secretKey - senderKey = senderKeys[0].encryption - } else if (encrypted.scope.type === EncryptionScopeType.TEAM) { - const keys = this.sigChain.team.teamKeys(encrypted.scope.generation) - recipientKey = keys.encryption.publicKey - senderKey = keys.encryption.secretKey - } else { - throw new Error(`Unknown encryption scope type ${encrypted.scope.type}`) + }) + } + + private asymUserDecrypt(encrypted: EncryptedPayload, signature: lfa.SignedEnvelope, context: lfa.LocalUserContext): any { + if (encrypted.scope.name == null) { + throw new Error(`Must provide a user ID when encryption scope is set to ${encrypted.scope.type}`) } - const decrypted = SigChain.lfa.asymmetric.decrypt({ + const senderKeys = this.sigChain.crypto.getPublicKeysForMembersById([signature.author.name]) + const recipientKey = context.user.keys.encryption.secretKey + const senderKey = senderKeys[0].encryption + + return lfa.asymmetric.decrypt({ cipher: encrypted.contents, senderPublicKey: senderKey, recipientSecretKey: recipientKey }) - - return decrypted } } diff --git a/demos/quiet-sandbox/src/auth/services/dm/dmService.ts b/demos/quiet-sandbox/src/auth/services/dm/dmService.ts deleted file mode 100644 index 6b4a954b..00000000 --- a/demos/quiet-sandbox/src/auth/services/dm/dmService.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Handles DM/Group DM-related chain operations - */ - -import { createHash } from "crypto" -import { BaseChainService } from "../baseService.js" -import { Keyset } from "@localfirst/auth" -import { SigChain } from "../../chain.js" - -class DMService extends BaseChainService { - private dmMap: Map = new Map() - - public static init(sigChain: SigChain): DMService { - return new DMService(sigChain) - } - - // TODO: incorporate persistence - public create(memberIds: string[]): string { - // this is the ID that will be used for storage and mapping purposes - const id = DMService.getDmId(memberIds) - - if (!this.dmMap.has(id)) { - this.dmMap.set(id, memberIds) - } - - return id - } - - public getDmKeysById(id: string): Keyset[] { - if (!this.dmMap.has(id)) { - throw new Error(`No DM mapping was found for id ${id}`) - } - - const memberIds = this.dmMap.get(id)! - return this.sigChain.crypto.getPublicKeysForMembersById(memberIds, { includeRemoved: false, throwOnMissing: false }) - } - - private static getDmId(memberIds: string[]): string { - const dmId = createHash('md5').update(memberIds.toString()).digest('hex') - return `priv_dm_${dmId}` - } -} - -export { - DMService -} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/auth/services/invites/inviteService.ts b/demos/quiet-sandbox/src/auth/services/invites/inviteService.ts index 50a4a11b..64f8c526 100644 --- a/demos/quiet-sandbox/src/auth/services/invites/inviteService.ts +++ b/demos/quiet-sandbox/src/auth/services/invites/inviteService.ts @@ -3,10 +3,8 @@ */ import { BaseChainService } from "../baseService.js" -import { ValidationResult } from "../../../../../../packages/crdx/dist/validator/types.js" -import { Base58, InvitationMap, InvitationState, InviteResult, Keyset, ProofOfInvitation, UnixTimestamp } from "@localfirst/auth" +import * as lfa from "@localfirst/auth" import { SigChain } from "../../chain.js" -import { RoleName } from "../roles/roles.js" export const DEFAULT_MAX_USES = 1 export const DEFAULT_INVITATION_VALID_FOR_MS = 604_800_000 // 1 week @@ -16,9 +14,9 @@ class InviteService extends BaseChainService { return new InviteService(sigChain) } - public create(validForMs: number = DEFAULT_INVITATION_VALID_FOR_MS, maxUses: number = DEFAULT_MAX_USES) { - const expiration = (Date.now() + validForMs) as UnixTimestamp - const invitation: InviteResult = this.sigChain.team.inviteMember({ + public create(validForMs: number = DEFAULT_INVITATION_VALID_FOR_MS, maxUses: number = DEFAULT_MAX_USES): lfa.InviteResult { + const expiration = (Date.now() + validForMs) as lfa.UnixTimestamp + const invitation: lfa.InviteResult = this.sigChain.team.inviteMember({ expiration, maxUses }) @@ -26,44 +24,30 @@ class InviteService extends BaseChainService { return invitation } + public createForDevice(validForMs: number = DEFAULT_INVITATION_VALID_FOR_MS): lfa.InviteResult { + const expiration = (Date.now() + validForMs) as lfa.UnixTimestamp + return this.sigChain.team.inviteDevice({ + expiration + }) + } + public revoke(id: string) { this.sigChain.team.revokeInvitation(id) // this.activeSigChain.persist() } - public getById(id: Base58): InvitationState { + public getById(id: lfa.Base58): lfa.InvitationState { return this.sigChain.team.getInvitation(id) } - public static generateProof(seed: string): ProofOfInvitation { - return SigChain.lfa.invitation.generateProof(seed) - } - - public validateProof(proof: ProofOfInvitation): boolean { - const validationResult = this.sigChain.team.validateInvitation(proof) as ValidationResult - if (!validationResult.isValid) { - console.error(`Proof was invalid or was on an invalid invitation`, validationResult.error) - return true - } - - return true - } - - public acceptProof(proof: ProofOfInvitation, username: string, publicKeys: Keyset) { - this.sigChain.team.admitMember(proof, publicKeys, username) - // this.activeSigChain.persist() - } - - public admitMemberFromInvite(proof: ProofOfInvitation, username: string, userId: string, publicKeys: Keyset): string { - this.sigChain.team.admitMember(proof, publicKeys, username) - this.sigChain.roles.addMember(userId, RoleName.MEMBER) - // this.activeSigChain.persist() - return username + public static generateProof(seed: string): lfa.ProofOfInvitation { + return lfa.invitation.generateProof(seed) } - public getAllInvites(): InvitationState[] { + // ISLA: Can be deleted (Reason: only used for printing data in sandbox) + public getAllInvites(): lfa.InvitationState[] { const inviteMap = this.sigChain.team.invitations() - const invites: InvitationState[] = [] + const invites: lfa.InvitationState[] = [] for (const invite of Object.entries(inviteMap)) { invites.push(invite[1]) } diff --git a/demos/quiet-sandbox/src/auth/services/members/deviceService.ts b/demos/quiet-sandbox/src/auth/services/members/deviceService.ts index ce6e6eb1..d6389207 100644 --- a/demos/quiet-sandbox/src/auth/services/members/deviceService.ts +++ b/demos/quiet-sandbox/src/auth/services/members/deviceService.ts @@ -4,7 +4,7 @@ import getMAC from "getmac" import { BaseChainService } from "../baseService.js" -import { Device, DeviceWithSecrets } from "@localfirst/auth" +import * as lfa from "@localfirst/auth" import { SigChain } from "../../chain.js" class DeviceService extends BaseChainService { @@ -18,13 +18,13 @@ class DeviceService extends BaseChainService { * @param userId User ID that this device is associated with * @returns A newly generated QuietDevice instance */ - public static generateDeviceForUser(userId: string): DeviceWithSecrets { + public static generateDeviceForUser(userId: string, deviceName?: string): lfa.DeviceWithSecrets { const params = { userId, - deviceName: DeviceService.determineDeviceName() + deviceName: deviceName != null ? deviceName : DeviceService.determineDeviceName() } - return SigChain.lfa.createDevice(params) + return lfa.createDevice(params) } /** @@ -32,13 +32,14 @@ class DeviceService extends BaseChainService { * * @returns Formatted MAC address of the current device */ + // ISLA: We probably want to rethink how we generate device names public static determineDeviceName(): string { const mac = getMAC() return mac.replaceAll(':','') } - public static redactDevice(device: DeviceWithSecrets): Device { - return SigChain.lfa.redactDevice(device) + public static redactDevice(device: lfa.DeviceWithSecrets): lfa.Device { + return lfa.redactDevice(device) } } diff --git a/demos/quiet-sandbox/src/auth/services/members/userService.ts b/demos/quiet-sandbox/src/auth/services/members/userService.ts index 2b0b3b28..e5bec2d9 100644 --- a/demos/quiet-sandbox/src/auth/services/members/userService.ts +++ b/demos/quiet-sandbox/src/auth/services/members/userService.ts @@ -5,11 +5,10 @@ //import { KeyMap } from '../../../../../../packages/auth/dist/team/selectors/keyMap.js' import { BaseChainService } from '../baseService.js' import { ProspectiveUser, MemberSearchOptions, DEFAULT_SEARCH_OPTIONS } from './types.js' -import { DeviceWithSecrets, LocalUserContext, Member, User, UserWithSecrets } from '@localfirst/auth' +import * as lfa from '@localfirst/auth' import { SigChain } from '../../chain.js' import { DeviceService } from './deviceService.js' import { InviteService } from '../invites/inviteService.js' -import { KeyMap } from '../../../../../../packages/auth/dist/team/selectors/keyMap.js' class UserService extends BaseChainService { public static init(sigChain: SigChain): UserService { @@ -23,9 +22,9 @@ class UserService extends BaseChainService { * @param id Optionally specify the user's ID (otherwise autogenerate) * @returns New QuietUser instance with an initial device */ - public static create(name: string, id?: string): LocalUserContext { - const user: UserWithSecrets = SigChain.lfa.createUser(name, id) - const device: DeviceWithSecrets = DeviceService.generateDeviceForUser(user.userId) + public static create(name: string, id?: string, deviceName?: string): lfa.LocalUserContext { + const user: lfa.UserWithSecrets = lfa.createUser(name, id) + const device: lfa.DeviceWithSecrets = DeviceService.generateDeviceForUser(user.userId, deviceName) return { user, @@ -33,8 +32,8 @@ class UserService extends BaseChainService { } } - public static createFromInviteSeed(name: string, seed: string): ProspectiveUser { - const context = this.create(name) + public static createFromInviteSeed(name: string, seed: string, deviceName?: string): ProspectiveUser { + const context = this.create(name, undefined, deviceName) const inviteProof = InviteService.generateProof(seed) const publicKeys = UserService.redactUser(context.user).keys @@ -45,15 +44,11 @@ class UserService extends BaseChainService { } } - public getKeys(): KeyMap { - return this.sigChain.team.allKeys() - } - - public getAllMembers(): Member[] { + public getAllMembers(): lfa.Member[] { return this.sigChain.team.members() } - public getMembersById(memberIds: string[], options: MemberSearchOptions = DEFAULT_SEARCH_OPTIONS): Member[] { + public getMembersById(memberIds: string[], options: MemberSearchOptions = DEFAULT_SEARCH_OPTIONS): lfa.Member[] { if (memberIds.length === 0) { return [] } @@ -61,12 +56,8 @@ class UserService extends BaseChainService { return this.sigChain.team.members(memberIds, options) } - public getMemberByName(memberName: string): Member | undefined { - return this.getAllMembers().find((member) => member.userName === memberName) - } - - public static redactUser(user: UserWithSecrets): User { - return SigChain.lfa.redactUser(user) + public static redactUser(user: lfa.UserWithSecrets): lfa.User { + return lfa.redactUser(user) } } diff --git a/demos/quiet-sandbox/src/auth/services/roles/channelService.ts b/demos/quiet-sandbox/src/auth/services/roles/channelService.ts index 1e6c690a..2c18d761 100644 --- a/demos/quiet-sandbox/src/auth/services/roles/channelService.ts +++ b/demos/quiet-sandbox/src/auth/services/roles/channelService.ts @@ -7,6 +7,7 @@ import { SigChain } from "../../chain.js" import { BaseChainService } from "../baseService.js" import { Channel, QuietRole } from "./roles.js" +// ISLA: We may want to rethink this strategy of just prefixing the name and instead choose to obfuscate whether something is a channel or not const CHANNEL_ROLE_KEY_PREFIX = "priv_chan_" class ChannelService extends BaseChainService { diff --git a/demos/quiet-sandbox/src/auth/services/roles/roleService.ts b/demos/quiet-sandbox/src/auth/services/roles/roleService.ts index 00f8b94a..a41e6344 100644 --- a/demos/quiet-sandbox/src/auth/services/roles/roleService.ts +++ b/demos/quiet-sandbox/src/auth/services/roles/roleService.ts @@ -70,6 +70,7 @@ class RoleService extends BaseChainService { return this.roleToQuietRole(role, context) } + // ISLA: Can be deleted (Reason: only used for printing data in sandbox) public getAllRoles(context: LocalUserContext, haveAccessOnly: boolean = false): QuietRole[] { const allRoles = this.sigChain.team.roles().map(role => this.roleToQuietRole(role, context)) if (haveAccessOnly) { diff --git a/demos/quiet-sandbox/src/network.ts b/demos/quiet-sandbox/src/network.ts deleted file mode 100644 index 6d2bc376..00000000 --- a/demos/quiet-sandbox/src/network.ts +++ /dev/null @@ -1,635 +0,0 @@ -import { createLibp2p, type Libp2p } from 'libp2p' -import { bootstrap } from '@libp2p/bootstrap' -import { tcp } from '@libp2p/tcp' -import { echo } from '@libp2p/echo' -import { noise } from '@chainsafe/libp2p-noise' -import { yamux } from '@chainsafe/libp2p-yamux' -import { createFromPrivKey, createFromProtobuf, exportToProtobuf } from '@libp2p/peer-id-factory' -import { base64 } from 'multiformats/bases/base64' -import { createHelia, type Helia } from 'helia' -import { createOrbitDB, type Events, Identities, KeyStore, type KeyValue, LogEntry, OrbitDB, OrbitDBAccessController } from '@orbitdb/core' -import { GossipSub, gossipsub } from '@chainsafe/libp2p-gossipsub' -import { identify } from '@libp2p/identify' -import fs from 'fs' -import { EventEmitter } from 'events' -import * as Auth from '@localfirst/auth' -import type { ConnectionEvents } from '@localfirst/auth' -import type { - Connection, - PeerId, - PeerStore, - ComponentLogger, - Topology, - Stream, - RSAPeerId, - Ed25519PeerId, - Secp256k1PeerId, - KeyType -} from '@libp2p/interface' -import type { ConnectionManager, IncomingStreamData, Registrar } from '@libp2p/interface-internal' -import { pipe } from 'it-pipe' -import { encode, decode } from 'it-length-prefixed' -import { pushable, type Pushable } from 'it-pushable' -import type { Uint8ArrayList } from 'uint8arraylist' -import map from 'it-map' -import { SigChain } from './auth/chain.js' -import { UserService } from './auth/services/members/userService.js'; -import { multiaddr, Multiaddr } from '@multiformats/multiaddr'; -import { EncryptedAndSignedPayload } from './auth/services/crypto/types.js' -import { MemoryDatastore } from 'datastore-core' -import { generateKeyPairFromSeed } from '@libp2p/crypto/keys' -import path from 'path' - -export class LocalStorage { - private authContext: Auth.Context | null - private context: Auth.LocalUserContext | null - private sigChain: SigChain | null - - constructor() { - this.authContext = null - this.context = null - this.sigChain = null - } - - public setAuthContext(context: Auth.Context) { - this.authContext = context - } - - public getAuthContext(): Auth.Context | null { - return this.authContext - } - - public setSigChain(sigChain: SigChain) { - this.sigChain = sigChain -} - -public setContext(context: Auth.LocalUserContext) { - this.context = context -} - - public getSigChain(): SigChain | null { - return this.sigChain - } - -public getContext(): Auth.LocalUserContext | null { - return this.context - } -} - -interface Libp2pAuthComponents { - peerId: PeerId - peerStore: PeerStore - registrar: Registrar - connectionManager: ConnectionManager - logger: ComponentLogger -} - -// Implementing local-first-auth as a service just to get started. I think we -// likely want to integrate it in a custom Transport/Muxer. -class Libp2pAuth { - private readonly protocol: string - private readonly components: Libp2pAuthComponents - private storage: LocalStorage - private authConnections: Record - private outboundStreamQueue: Pushable<{ peerId: PeerId, connection: Connection }> - private outboundStreams: Record> - private inboundStreams: Record - - constructor(storage: LocalStorage, components: Libp2pAuthComponents) { - this.protocol = '/local-first-auth/1.0.0' - this.components = components - this.storage = storage - this.authConnections = {} - this.outboundStreamQueue = pushable<{ peerId: PeerId, connection: Connection }>({ objectMode: true }) - this.outboundStreams = {} - this.inboundStreams = {} - - pipe( - this.outboundStreamQueue, - async (source) => { - for await (const { peerId, connection } of source) { - await this.openOutboundStream(peerId, connection) - } - } - ).catch((e) => { console.error('Outbound stream queue error', e) }) - } - - async start() { - console.log('Auth service starting') - - const topology: Topology = { - onConnect: this.onPeerConnected.bind(this), - onDisconnect: this.onPeerDisconnected.bind(this), - notifyOnTransient: false, - } - - const registrar = this.components.registrar - await registrar.register(this.protocol, topology) - await registrar.handle(this.protocol, this.onIncomingStream.bind(this), { - runOnTransientConnection: false - }) - } - - async stop() { - // TODO - } - - private async openOutboundStream(peerId: PeerId, connection: Connection) { - if (peerId.toString() in this.outboundStreams) { - return - } - - console.log('Opening outbound stream for peer', peerId.toString()) - const outboundStream = await connection.newStream(this.protocol, { - runOnTransientConnection: false - }) - const outboundPushable: Pushable = pushable() - this.outboundStreams[peerId.toString()] = outboundPushable - - pipe( - outboundPushable, - outboundStream - ).catch((e: Error) => console.error(e)) - - this.authConnections[peerId.toString()].start() - } - - private sendMessage(peerId: PeerId, message: Uint8Array) { - this.outboundStreams[peerId.toString()]?.push( - // length-prefix encoded - encode.single(message) - ) - } - - // NOTE: This is not awaited by the registrar - private async onPeerConnected(peerId: PeerId, connection: Connection) { - console.log('Peer connected!') - - // https://github.com/ChainSafe/js-libp2p-gossipsub/issues/398 - if (connection.status !== 'open') { - return - } - - if (peerId.toString() in this.authConnections) { - await connection.close() - return - } - - const context = this.storage.getAuthContext() - if (!context) { - throw new Error('Auth context required to connect to peer') - } - - this.outboundStreamQueue.push({ peerId, connection }) - - const authConnection = new Auth.Connection({ - context, - sendMessage: (message: Uint8Array) => { - this.sendMessage(peerId, message) - }, - }) - - // TODO: Listen for updates to context and update context in storage - authConnection.on('joined', ({ team, user }) => { - console.log(`${user.userId}: Joined team ${team.teamName}!`) - if (this.storage.getSigChain() == null) { - console.log(`${user.userId}: Creating SigChain for user with name ${user.userName} and team name ${team.teamName}`) - const context = this.storage.getContext()! - this.storage.setSigChain(SigChain.createFromTeam(team, context).sigChain) - console.log(`${user.userId}: Updating auth context`) - this.storage.setAuthContext({ - user: context.user, - device: context.device, - team - }) - } - }) - - authConnection.on('change', state => console.log(`${this.storage.getContext()?.user.userName} Change:`, state)) - - this.authConnections[peerId.toString()] = authConnection - } - - private onPeerDisconnected(peerId: PeerId): void { - // TODO - } - - private async onIncomingStream({ stream, connection }: IncomingStreamData) { - const peerId = connection.remotePeer - - const oldStream = this.inboundStreams[peerId.toString()] - if (oldStream) { - oldStream.close().catch(e => { - oldStream.abort(e) - }) - } - - pipe( - stream, - (source) => decode(source), - async (source) => { - for await (const data of source) { - try { - this.authConnections[peerId.toString()].deliver(data.subarray()) - } catch (e) { - console.error(e) - } - } - } - ) - - this.inboundStreams[peerId.toString()] = stream - console.log('New incoming stream for peer', peerId.toString()) - - this.outboundStreamQueue.push({ peerId, connection }) - } -} - -const libp2pAuth = (storage: LocalStorage): ((components: Libp2pAuthComponents) => Libp2pAuth) => { - return (components: Libp2pAuthComponents) => new Libp2pAuth(storage, components) -} - -export class Libp2pService { - - peerName: string - libp2p: Libp2p | null - storage: LocalStorage - peerId: RSAPeerId | Ed25519PeerId | Secp256k1PeerId | null - - constructor(peerName: string, storage: LocalStorage) { - this.peerName = peerName - this.libp2p = null - this.storage = storage - this.peerId = null - } - - /** - * Get a persistent peer ID for a given name. - */ - async getPeerId(): Promise { - if (this.peerId != null) { - return this.peerId - } - - let peerIdFilename = '.peerId.' + this.peerName - let peerIdB64 - - try { - peerIdB64 = fs.readFileSync(peerIdFilename, 'utf8') - } catch (e) { - console.error(e) - } - - if (!peerIdB64) { - console.log('Creating peer ID') - const keys: Auth.KeysetWithSecrets = this.storage.getContext()?.user!.keys - - const buf = Buffer.from(keys.encryption.secretKey) - const priv = await generateKeyPairFromSeed("Ed25519", new Uint8Array(buf).subarray(0, 32)) - const peerId = await createFromPrivKey(priv) - const peerIdBytes = exportToProtobuf(peerId) - peerIdB64 = base64.encoder.encode(peerIdBytes) - - try { - fs.writeFileSync(peerIdFilename, peerIdB64) - } catch (e) { - console.error(e) - } - - this.peerId = peerId - return peerId - } - - this.peerId = await createFromProtobuf(base64.decoder.decode(peerIdB64)) - return this.peerId - } - - async init(datastore: MemoryDatastore = new MemoryDatastore()) { - console.log('Starting libp2p client') - - const peerId = await this.getPeerId() - - this.libp2p = await createLibp2p({ - peerId, - addresses: { - // accept TCP connections on any port - listen: ['/ip4/127.0.0.1/tcp/0'] - }, - transports: [tcp()], - connectionEncryption: [noise()], - streamMuxers: [yamux()], - peerDiscovery: [ - bootstrap({ - list: [ - '/ip4/127.0.0.1/tcp/0/p2p/12D3KooWNYYYUtuxvmH7gsvApKE6YoiqBWNgZ6x3BBpea3RP1jTv' - ], - timeout: 1000, // in ms, - tagName: 'bootstrap', - tagValue: 50, - tagTTL: 120000 // in ms - }) - ], - services: { - echo: echo(), - pubsub: gossipsub({ - // neccessary to run a single peer - allowPublishToZeroTopicPeers: true, - emitSelf: true - }), - identify: identify(), - auth: libp2pAuth(this.storage) - }, - datastore - }); - - console.log('Peer ID: ', peerId.toString()) - - return this.libp2p - } - - async dial( addr: string | Multiaddr): Promise { - console.log(`Dialing peer at ${addr}`) - const multiaddrs: Multiaddr[] = [] - if (typeof addr === 'string') { - multiaddrs.push(multiaddr(addr)) - } else { - multiaddrs.push(addr) - } - await this.libp2p?.dial(multiaddrs) - - return true - } - - async close() { - console.log('Closing libp2p') - await this.libp2p?.stop() - } -} - -function getAccessController() { - return OrbitDBAccessController({ write: ["*"] }) -} - -export class OrbitDbService { - - libp2pService: Libp2pService - ipfs: Helia | null - orbitDb: OrbitDB | null - dir: string - - constructor(libp2pService: Libp2pService) { - this.libp2pService = libp2pService - this.ipfs = null - this.orbitDb = null - this.dir = `./.orbitdb/${libp2pService.peerName}` - } - - async init(datastore: MemoryDatastore = new MemoryDatastore()) { - console.log('Initializing OrbitDB') - if (!this.libp2pService.libp2p) { - throw new Error('Cannot initialize OrbitDB, libp2p instance required') - } - this.ipfs = await createHelia({ libp2p: this.libp2pService.libp2p, datastore }) - await this.ipfs.start() - const keystore = await KeyStore({ path: path.join(this.dir, './keystore') }) - const identities = await Identities({ ipfs: this.ipfs, keystore }) - this.orbitDb = await createOrbitDB({ - id: (await this.libp2pService.getPeerId()).toString(), - ipfs: this.ipfs, - directory: this.dir, - identities - }) - } - - async createDb(dbName: string): Promise { - if (!this.orbitDb) { - throw new Error('Must call init before creating a DB') - } - // Seems like we can improve the type definitions - return await this.orbitDb.open( - dbName, - { - write: ['*'], - sync: true, - meta: {}, - AccessController: getAccessController() - } - ) as unknown as Events - } - - async close() { - console.log('Closing OrbitDB') - await this.orbitDb?.stop() - await this.ipfs?.stop() - } -} - -interface Channel { - name: string - addr: string -} - -export class MessageService extends EventEmitter { - - orbitDbService: OrbitDbService - channelListDb: Events | null - channelDbs: Record - - constructor(orbitDbService: OrbitDbService) { - super() - - this.orbitDbService = orbitDbService - this.channelListDb = null - this.channelDbs = {} - } - - async init() { - if (this.orbitDbService.orbitDb == null) { - await this.orbitDbService.init() - } - - // Sharing the channelListDb address is necessary for other peers to connect - // to it. Each DB get's a unique address if only passing in a name to - // createDb. That's just how OrbitDB works because of immutable databases. - // Alternatively, I think you can share the owner's OrbitDB identity and - // create the database with the same access controller. - const name = 'channels' - console.log(`Initializing channel list DB with name '${name}'`); - this.channelListDb = await this.orbitDbService.createDb(name); - console.log(`Initialized channel list DB with address ${this.channelListDb.address}`) - this.channelListDb.events.on('update', entry => { - console.log(`Got new channel: ${JSON.stringify(entry.payload.value)}`) - this.openChannel(entry.payload.value) - }) - console.log(`Initializing known channel DBs`) - for (const entry of await this.channelListDb.all()) { - await this.openChannel(entry.value as Channel) - } - } - - async createChannel(channelName: string): Promise { - if (!this.channelListDb) { - throw new Error('Must call init before creating a channel') - } - - if (channelName in this.channelDbs) { - return this.channelDbs[channelName] - } - - const db = await this.createChannelDb('channel/' + channelName) - - this.channelDbs[channelName] = db - await this.channelListDb.add({ name: channelName, addr: db.address }) - - return db - } - - private async openChannel(channel: Channel) { - if (channel.name in this.channelDbs) { - return - } - - this.channelDbs[channel.name] = await this.createChannelDb(channel.addr) - } - - private async createChannelDb(nameOrAddr: string): Promise { - const db = await this.orbitDbService.createDb(nameOrAddr) - - db.events.on('update', entry => this.emit('message', entry.payload.value)) - - return db - } - - async sendMessage(channelName: string, message: EncryptedAndSignedPayload) { - if (channelName in this.channelDbs) { - this.channelDbs[channelName].add(message) - } else { - throw new Error('Channel does not exist') - } - } - - async readMessages(channelName: string): Promise { - let db: Events - if (channelName in this.channelDbs) { - db = this.channelDbs[channelName] - } else { - throw new Error('Channel does not exist') - } - - const messages = await db.all() - return messages.map((message) => message.value as EncryptedAndSignedPayload) - } - - async close() { - console.log('Closing OrbitDB') - await this.orbitDbService?.close() - } -} - -export class Networking { - private _libp2p: Libp2pService - private _orbitDb: OrbitDbService - private _messages: MessageService - - private constructor(libp2p: Libp2pService, orbitDb: OrbitDbService, messages: MessageService) { - this._libp2p = libp2p - this._orbitDb = orbitDb - this._messages = messages - } - - public static async init(storage: LocalStorage): Promise { - const context = storage.getContext() - if (context == null) { - throw new Error(`Context hasn't been initialized!`) - } - - const datastore = new MemoryDatastore() - const libp2p = new Libp2pService(context.user.userId, storage) - console.log(`Initializing new libp2p peer with ID ${await libp2p.getPeerId()}`) - await libp2p.init(datastore) - - console.log(libp2p.peerName) - - const orbitDb = new OrbitDbService(libp2p) - console.log(`Initializing new orbitdb service for peer ID ${await libp2p.getPeerId()}`) - await orbitDb.init(datastore) - - const messages = new MessageService(orbitDb) - console.log(`Initializing new message service for peer ID ${await libp2p.getPeerId()}`) - await messages.init() - - return new Networking(libp2p, orbitDb, messages) - } - - get libp2p(): Libp2pService { - return this._libp2p - } - - get orbitDb(): OrbitDbService { - return this._orbitDb - } - - get messages(): MessageService { - return this._messages - } -} - -const main = async () => { - const teamName = 'Test' - const username1 = 'isla' - const username2 = 'isntla' - const peerId1 = 'peer1' - const peerId2 = 'peer2' - - // Peer 1 - const storage1 = new LocalStorage() - - const { - context: founderContext, - sigChain - } = SigChain.create(teamName, username1) - - storage1.setContext(founderContext) - storage1.setAuthContext({ ...founderContext, team: sigChain.team }) - storage1.setSigChain(sigChain) - - const peer1 = new Libp2pService(peerId1, storage1); - await peer1.init(); - - const { seed } = sigChain.invites.create() - - // const orbitDb1 = new OrbitDbService(peer1); - // await orbitDb1.init(); - - // const db1 = await orbitDb1.createDb('messages'); - - // Peer 2 - const storage2 = new LocalStorage() - const prospectiveUser = UserService.createFromInviteSeed(username2, seed) - storage2.setContext(prospectiveUser.context) - storage2.setAuthContext({ - ...prospectiveUser.context, - invitationSeed: seed - }) - const peer2 = new Libp2pService(peerId2, storage2); - await peer2.init(); - - // const orbitDb2 = new OrbitDbService(peer2); - // await orbitDb2.init(); - - // const db2 = await orbitDb1.createDb(db1.db?.address || ''); - - // Test - // db2.on('update', (entry) => { console.log('Peer2: Received: ', entry.payload.value); }); - for (let addr of peer2.libp2p?.getMultiaddrs() ?? []) { - await peer1.libp2p?.dial(addr); - } - // console.log('Peer1: Adding entry'); - // await db1.db?.add("Hello world"); - - // await db1.close(); - // await orbitDb1.close(); - // await peer1.close(); -} - -// main().then().catch(console.error) diff --git a/demos/quiet-sandbox/src/network/events.ts b/demos/quiet-sandbox/src/network/events.ts new file mode 100644 index 00000000..d0789ac2 --- /dev/null +++ b/demos/quiet-sandbox/src/network/events.ts @@ -0,0 +1,40 @@ +import { EventEmitter } from "stream" +import { createQsbLogger, QuietLogger } from "../utils/logger/logger.js" + +export enum EVENTS { + INITIALIZED_CHAIN = 'INITIALIZED_CHAIN', + DIAL_FINISHED = 'DIAL_FINISHED', + AUTH_TIMEOUT = 'AUTH_TIMEOUT', + MISSING_DEVICE = 'MISSING_DEVICE' +} + +export class QuietAuthEvents { + private _events: EventEmitter + private _LOGGER: QuietLogger + + constructor(identifier: string) { + this._events = new EventEmitter() + this._LOGGER = createQsbLogger(`quietAuthEvents:${identifier}`) + } + + public emit(event: EVENTS, ...args: any[]) { + this._LOGGER.debug(`emit ${event}`) + this._events.emit(event, ...args) + } + + public on(event: EVENTS, listener: (...args: any[]) => void) { + this._events.on( + event, + // this.appendLogToListener(event, listener) + listener + ) + } + + public once(event: EVENTS, listener: (...args: any[]) => void) { + this._events.once( + event, + // this.appendLogToListener(event, listener) + listener + ) + } +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/network/libp2p/auth.ts b/demos/quiet-sandbox/src/network/libp2p/auth.ts new file mode 100644 index 00000000..f6cb4afb --- /dev/null +++ b/demos/quiet-sandbox/src/network/libp2p/auth.ts @@ -0,0 +1,376 @@ +// ISLA: This is what needs to be added to Quiet to make it work Libp2p and LFA work together +import { ComponentLogger, Connection, PeerId, PeerStore, Stream, Topology } from "@libp2p/interface" +import type { ConnectionManager, IncomingStreamData, Registrar } from '@libp2p/interface-internal' +import { LocalStorage } from "../storage.js" +import { EVENTS, QuietAuthEvents } from "../events.js" +import { SigChain } from "../../auth/chain.js" +import * as Auth from '@localfirst/auth' +import { pushable, type Pushable } from 'it-pushable' +import { Uint8ArrayList } from "uint8arraylist" +import { createQsbLogger, createQuietLogger, QuietLogger } from "../../utils/logger/logger.js" +import { pipe } from "it-pipe" +import { encode, decode } from 'it-length-prefixed' + +export interface Libp2pAuthComponents { + peerId: PeerId + peerStore: PeerStore + registrar: Registrar + connectionManager: ConnectionManager + logger: ComponentLogger +} + +interface PushableStream { + stream: Stream + pushable: Pushable +} + +enum JoinStatus { + PENDING = 'PENDING', + JOINING = 'JOINING', + JOINED = 'JOINED' +} + +const createLFALogger = createQuietLogger('localfirst') + +// Implementing local-first-auth as a service just to get started. I think we +// likely want to integrate it in a custom Transport/Muxer. +export class Libp2pAuth { + private readonly protocol: string + private readonly components: Libp2pAuthComponents + private storage: LocalStorage + private authConnections: Record + private outboundStreamQueue: Pushable<{ peerId: PeerId, connection: Connection }> + private outboundStreams: Record + private inboundStreams: Record + private restartableAuthConnections: Map + private bufferedConnections: { peerId: PeerId, connection: Connection }[] + private events: QuietAuthEvents + private peerId: PeerId + private joining: boolean = false + private restartInterval: NodeJS.Timeout + private unblockInterval: NodeJS.Timeout + private joinStatus: JoinStatus + private LOGGER: QuietLogger + + constructor(peerId: PeerId, storage: LocalStorage, components: Libp2pAuthComponents, events: QuietAuthEvents) { + this.protocol = '/local-first-auth/1.0.0' + this.peerId = peerId + this.components = components + this.storage = storage + this.authConnections = {} + this.restartableAuthConnections = new Map() + this.outboundStreamQueue = pushable<{ peerId: PeerId, connection: Connection }>({ objectMode: true }) + this.outboundStreams = {} + this.inboundStreams = {} + this.bufferedConnections = [] + this.joinStatus = JoinStatus.PENDING + this.events = events + this.LOGGER = createQsbLogger(`libp2p:auth:${peerId}`) + + pipe( + this.outboundStreamQueue, + async (source) => { + for await (const { peerId, connection } of source) { + await this.openOutboundStream(peerId, connection) + } + } + ).catch((e) => { this.LOGGER.error('Outbound stream queue error', e) }) + + this.restartInterval = setInterval(this.restartStoppedConnections, 45_000, this.restartableAuthConnections, this.LOGGER); + this.unblockInterval = setInterval(this.unblockConnections, 5_000, this.bufferedConnections, this.joinStatus, this.LOGGER) + } + + private restartStoppedConnections(restartableAuthConnections: Map, logger: QuietLogger) { + logger.info(`Attempting to restart stopped auth connections`) + for (const [ms, connection] of restartableAuthConnections.entries()) { + if (ms >= Date.now()) { + connection.start() + restartableAuthConnections.delete(ms) + } + } + } + + private async unblockConnections(conns: { peerId: PeerId, connection: Connection }[], status: JoinStatus, logger: QuietLogger) { + if (status !== JoinStatus.JOINED) return + + logger.info(`Unblocking ${conns.length} connections now that we've joined the chain`) + while(conns.length > 0) { + const conn = conns.pop() + if (conn != null) { + await this.onPeerConnected(conn.peerId, conn.connection) + } + } + } + + async start() { + this.LOGGER.info('Auth service starting') + + const topology: Topology = { + onConnect: this.onPeerConnected.bind(this), + onDisconnect: this.onPeerDisconnected.bind(this), + notifyOnTransient: false, + } + + const registrar = this.components.registrar + await registrar.register(this.protocol, topology) + await registrar.handle(this.protocol, this.onIncomingStream.bind(this), { + runOnTransientConnection: false + }) + } + + async stop() { + // TODO + } + + private async openOutboundStream(peerId: PeerId, connection: Connection) { + if (peerId.toString() in this.outboundStreams) { + return + } + + this.LOGGER.info('Opening outbound stream for peer', peerId.toString()) + const outboundStream = await connection.newStream(this.protocol, { + runOnTransientConnection: false, + negotiateFully: true + }) + const outboundPushable: Pushable = pushable() + this.outboundStreams[peerId.toString()] = { + stream: outboundStream, + pushable: outboundPushable + } + + pipe( + outboundPushable, + outboundStream + ).catch((e: Error) => this.LOGGER.error(`Error opening outbound stream to ${peerId}`, e)) + + if (connection.direction === 'outbound') { + await this.openInboundStream(peerId, connection) + } + + this.authConnections[peerId.toString()].start() + } + + private async openInboundStream(peerId: PeerId, connection: Connection) { + if (peerId.toString() in this.inboundStreams) { + return + } + + this.LOGGER.info('Opening new inbound stream for peer', peerId.toString()) + const inboundStream = await connection.newStream(this.protocol, { + runOnTransientConnection: false, + negotiateFully: true + }) + + this.handleIncomingMessages(peerId, inboundStream) + this.inboundStreams[peerId.toString()] = inboundStream + } + + private async onIncomingStream({ stream, connection }: IncomingStreamData) { + const peerId = connection.remotePeer + this.LOGGER.info(`Handling existing incoming stream ${peerId.toString()}`) + + const oldStream = this.inboundStreams[peerId.toString()] + if (oldStream) { + this.LOGGER.info(`Old inbound stream found!`) + await this.closeInboundStream(peerId, true) + } + + this.handleIncomingMessages(peerId, stream) + + this.inboundStreams[peerId.toString()] = stream + } + + private handleIncomingMessages(peerId: PeerId, stream: Stream) { + pipe( + stream, + (source) => decode(source), + async (source) => { + for await (const data of source) { + try { + if (!(peerId.toString() in this.authConnections)) { + this.LOGGER.error(`No auth connection established for ${peerId.toString()}`) + } else { + this.authConnections[peerId.toString()].deliver(data.subarray()) + } + } catch (e) { + this.LOGGER.error(`Error while delivering message to ${peerId}`, e) + } + } + } + ) + } + + private sendMessage(peerId: PeerId, message: Uint8Array) { + try { + this.outboundStreams[peerId.toString()]?.pushable.push( + // length-prefix encoded + encode.single(message) + ) + } catch (e) { + this.LOGGER.error(`Error while sending auth message over stream to ${peerId.toString()}`, e) + } + } + + // NOTE: This is not awaited by the registrar + private async onPeerConnected(peerId: PeerId, connection: Connection) { + if (this.joinStatus === JoinStatus.JOINING) { + this.LOGGER.warn(`Connection to ${peerId.toString()} will be buffered due to a concurrent join`) + this.bufferedConnections.push({ peerId, connection }) + return + } + + if (this.joinStatus === JoinStatus.PENDING) { + this.joinStatus = JoinStatus.JOINING + } + + this.LOGGER.info(`Peer connected (direction = ${connection.direction})!`) + + // https://github.com/ChainSafe/js-libp2p-gossipsub/issues/398 + if (connection.status !== 'open') { + this.LOGGER.warn(`The connection with ${peerId.toString()} was not in an open state!`) + return + } + + const context = this.storage.getAuthContext() + this.LOGGER.info(`Context with ${peerId.toString()} is a member context?: ${(context as Auth.InviteeMemberContext).invitationSeed == null}`) + if (!context) { + throw new Error('Auth context required to connect to peer') + } + + if (peerId.toString() in this.authConnections) { + this.LOGGER.info(`A connection with ${peerId.toString()} was already available, skipping connection initialization!`) + return + } + + const authConnection = new Auth.Connection({ + context, + sendMessage: (message: Uint8Array) => { + this.sendMessage(peerId, message) + }, + createLogger: createLFALogger + }) + + // TODO: Listen for updates to context and update context in storage + authConnection.on('joined', (payload) => { + const { team, user } = payload + this.LOGGER.info(`${this.storage.getContext()!.user.userId}: Joined team ${team.teamName} (userid: ${user.userId})!`) + if (this.storage.getSigChain() == null && !this.joining) { + this.joining = true + this.LOGGER.info(`${user.userId}: Creating SigChain for user with name ${user.userName} and team name ${team.teamName}`) + const context = this.storage.getContext()! + this.storage.setSigChain(SigChain.createFromTeam(team, context).sigChain) + this.LOGGER.info(`${user.userId}: Updating auth context`) + this.storage.setAuthContext({ + user: context.user, + device: context.device, + team + }) + this.joining = false + } + if (this.joinStatus === JoinStatus.JOINING) { + this.joinStatus = JoinStatus.JOINED + this.unblockConnections(this.bufferedConnections, this.joinStatus, this.LOGGER) + } + this.events.emit(EVENTS.INITIALIZED_CHAIN) + }) + + const handleAuthConnErrors = (error: Auth.ConnectionErrorPayload, remoteUsername: string | undefined) => { + this.LOGGER.error(`Got an error while handling auth connection with ${remoteUsername}`, JSON.stringify(error)) + if (error.type === 'TIMEOUT') { + this.events.emit(EVENTS.AUTH_TIMEOUT, { peerId, remoteUsername }) + } else if (error.type === 'DEVICE_UNKNOWN') { + this.events.emit(EVENTS.MISSING_DEVICE, { peerId, remoteUsername }) + } + } + + authConnection.on('localError', (error) => { + handleAuthConnErrors(error, authConnection._context.userName) + }) + + authConnection.on('remoteError', (error) => { + handleAuthConnErrors(error, authConnection._context.userName) + }) + + authConnection.on('connected', () => { + this.LOGGER.info(`LFA Connected!`) + if (this.storage.getContext() != null) { + this.LOGGER.debug(`Sending sync message because our chain is intialized`) + const team = this.storage.getSigChain()!.team + const user = this.storage.getContext()!.user + authConnection.emit('sync', { team, user }) + } + }) + + authConnection.on('disconnected', (event) => { + this.LOGGER.info(`LFA Disconnected!`, event) + authConnection.stop() + this.restartableAuthConnections.set(Date.now() + 30_000, authConnection) + }) + + this.authConnections[peerId.toString()] = authConnection + + this.outboundStreamQueue.push({ peerId, connection }) + } + + private async onPeerDisconnected(peerId: PeerId) { + this.LOGGER.warn(`Disconnecting auth connection with peer ${peerId.toString()}`) + await this.closeAuthConnection(peerId) + + } + + private async closeOutboundStream(peerId: PeerId, deleteRecord?: boolean) { + this.LOGGER.warn(`Closing outbound stream with ${peerId.toString()}`) + const outboundStream = this.outboundStreams[peerId.toString()] + + if (outboundStream == null) { + this.LOGGER.warn(`Can't close outbound stream with ${peerId.toString()} as it doesn't exist`) + return + } + + await outboundStream.pushable.end().onEmpty() + await outboundStream.stream.close().catch(e => { + outboundStream.stream.abort(e) + }) + + if (deleteRecord) { + delete this.outboundStreams[peerId.toString()] + } + } + + private async closeInboundStream(peerId: PeerId, deleteRecord?: boolean) { + this.LOGGER.warn(`Closing inbound stream with ${peerId.toString()}`) + const inboundStream = this.inboundStreams[peerId.toString()] + + if (inboundStream == null) { + this.LOGGER.warn(`Can't close inbound stream with ${peerId.toString()} as it doesn't exist`) + return + } + + await inboundStream.close().catch(e => { + inboundStream.abort(e) + }) + + if (deleteRecord) { + delete this.inboundStreams[peerId.toString()] + } + } + + private async closeAuthConnection(peerId: PeerId) { + this.LOGGER.warn(`Closing auth connection with ${peerId.toString()}`) + const connection = this.authConnections[peerId.toString()] + + if (connection == null) { + this.LOGGER.warn(`Can't close auth connection with ${peerId.toString()} as it doesn't exist`) + } else { + connection.stop() + delete this.authConnections[peerId.toString()] + } + + await this.closeOutboundStream(peerId, true) + await this.closeInboundStream(peerId, true) + } +} + +export const libp2pAuth = (peerId: PeerId, storage: LocalStorage, events: QuietAuthEvents): ((components: Libp2pAuthComponents) => Libp2pAuth) => { + return (components: Libp2pAuthComponents) => new Libp2pAuth(peerId, storage, components, events) +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/network/libp2p/libp2p.ts b/demos/quiet-sandbox/src/network/libp2p/libp2p.ts new file mode 100644 index 00000000..909d9efe --- /dev/null +++ b/demos/quiet-sandbox/src/network/libp2p/libp2p.ts @@ -0,0 +1,196 @@ +import { createLibp2p, type Libp2p } from 'libp2p' +import { tcp } from '@libp2p/tcp' +import { echo } from '@libp2p/echo' +import { noise } from '@chainsafe/libp2p-noise' +import { yamux } from '@chainsafe/libp2p-yamux' +import { mdns } from '@libp2p/mdns' +import { createFromPrivKey, createFromProtobuf, exportToProtobuf } from '@libp2p/peer-id-factory' +import { base64 } from 'multiformats/bases/base64' +import { gossipsub } from '@chainsafe/libp2p-gossipsub' +import { identify } from '@libp2p/identify' +import fs from 'fs' +import * as Auth from '@localfirst/auth' +import type { + PeerId, + RSAPeerId, + Ed25519PeerId, + Secp256k1PeerId, +} from '@libp2p/interface' +import { MemoryDatastore } from 'datastore-core' +import { generateKeyPairFromSeed } from '@libp2p/crypto/keys' +import { peerIdFromString } from '@libp2p/peer-id' + +import { createQsbLogger, QuietLogger } from '../../utils/logger/logger.js' +import { suffixLogger } from '../../utils/logger/libp2pLogger.js' +import { DEFAULT_NETWORK_INTERFACE, getIpAddresses } from '../utils.js' +import { LocalStorage } from '../storage.js' +import { EVENTS, QuietAuthEvents } from '../events.js' +import { libp2pAuth } from './auth.js' + +export class Libp2pService { + peerName: string + libp2p: Libp2p | null + storage: LocalStorage + peerId: RSAPeerId | Ed25519PeerId | Secp256k1PeerId | null + events: QuietAuthEvents + private LOGGER: QuietLogger + + constructor(peerName: string, storage: LocalStorage, events: QuietAuthEvents) { + this.peerName = peerName + this.libp2p = null + this.storage = storage + this.peerId = null + this.events = events + this.LOGGER = createQsbLogger("libp2p") + } + + /** + * Get a persistent peer ID for a given name. + */ + async getPeerId(): Promise { + if (this.peerId != null) { + return this.peerId + } + + let peerIdFilename = '.peerId.' + this.peerName + let peerIdB64 + + if (fs.existsSync(peerIdFilename)) { + try { + peerIdB64 = fs.readFileSync(peerIdFilename, 'utf8') + } catch (e) { + this.LOGGER.error(`Error reading peerId file ${peerIdFilename}`, e) + } + } + + if (!peerIdB64) { + this.LOGGER.info('Creating peer ID') + const keys: Auth.KeysetWithSecrets = this.storage.getContext()?.user!.keys + + const buf = Buffer.from(keys.encryption.secretKey) + const priv = await generateKeyPairFromSeed("Ed25519", new Uint8Array(buf).subarray(0, 32)) + const peerId = await createFromPrivKey(priv) + const peerIdBytes = exportToProtobuf(peerId) + peerIdB64 = base64.encoder.encode(peerIdBytes) + + try { + fs.writeFileSync(peerIdFilename, peerIdB64) + } catch (e) { + this.LOGGER.error(`Error writing peer ID to file ${peerIdFilename}`, e) + } + + this.peerId = peerId + return peerId + } + + this.peerId = await createFromProtobuf(base64.decoder.decode(peerIdB64)) + return this.peerId + } + + async init(datastore: MemoryDatastore = new MemoryDatastore()) { + this.LOGGER.info('Creating libp2p client') + const peerId = await this.getPeerId() + this.LOGGER = createQsbLogger(`libp2p:${peerId}`) + const ipAddresses = getIpAddresses() + const ipOfInterfaceInUse = ipAddresses[DEFAULT_NETWORK_INTERFACE][0] + const baseAddress = `/ip4/${ipOfInterfaceInUse}/tcp/0` + this.LOGGER.info(`Using network interface ${DEFAULT_NETWORK_INTERFACE} and base address ${baseAddress}`) + + this.libp2p = await createLibp2p({ + peerId, + logger: suffixLogger(peerId.toString()), + connectionManager: { + minConnections: 5, + maxConnections: 20, + dialTimeout: 60_000, + autoDialConcurrency: 1, + autoDialMaxQueueLength: 1000, + maxDialQueueLength: 1000, + inboundUpgradeTimeout: 45_000 + }, + addresses: { + // accept TCP connections on any port + listen: [baseAddress], + }, + transports: [tcp({ + })], + connectionEncryption: [noise()], + streamMuxers: [yamux({ + maxInboundStreams: 3_000, + maxOutboundStreams: 3_000 + })], + peerDiscovery: [ + mdns({ + interval: 30_000, + serviceTag: "mdns.peer-discovery", + peerName: this.peerId?.toString() + }) + ], + services: { + echo: echo(), + pubsub: gossipsub({ + // neccessary to run a single peer + allowPublishToZeroTopicPeers: true, + debugName: peerId.toString(), + fallbackToFloodsub: true, + prunePeers: 1000, + emitSelf: false, + scoreThresholds: { + "acceptPXThreshold": -1000, + "gossipThreshold": -1000, + "graylistThreshold": -1000, + "opportunisticGraftThreshold": -1000, + "publishThreshold": -1000 + }, + doPX: true + }), + identify: identify({ + timeout: 30_000 + }), + // ISLA: This is what needs to be added to Quiet to make it work Libp2p and LFA work together + auth: libp2pAuth(peerIdFromString(this.peerId!.toString()), this.storage, this.events) + }, + datastore + }); + + this.libp2p.addEventListener('peer:connect', (event) => { + this.LOGGER.info(`Connected to new peer`, event.detail) + }) + + this.libp2p.addEventListener('connection:close', (event) => { + this.LOGGER.warn(`Connection closing with ${event.detail.remotePeer}`) + }) + + this.libp2p.addEventListener('transport:close', (event) => { + this.LOGGER.warn(`Transport closing`) + }) + + this.libp2p.addEventListener('peer:discovery', (event) => { + this.LOGGER.info('Discovered new peer: ', event.detail) + }) + + this.LOGGER.info('Peer ID: ', peerId.toString()) + + // ISLA: The auth errors need to be reworked + const onAuthError = async (errorMessage: string, errorData: { peerId: PeerId, remoteUsername: string | undefined }) => { + this.LOGGER.error(errorMessage, errorData) + } + + this.events.on(EVENTS.AUTH_TIMEOUT, async (errorData: { peerId: PeerId, remoteUsername: string | undefined }) => { + const errorMessage = `Connection experienced an auth timeout, restarting!` + onAuthError(errorMessage, errorData) + }) + + this.events.on(EVENTS.MISSING_DEVICE, async (errorData: { peerId: PeerId, remoteUsername: string | undefined }) => { + const errorMessage = `Connection with ${peerId} failed due to missing device, restarting!` + onAuthError(errorMessage, errorData) + }) + + return this.libp2p + } + + async close() { + this.LOGGER.warn('Closing libp2p') + await this.libp2p?.stop() + } +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/network/network.ts b/demos/quiet-sandbox/src/network/network.ts new file mode 100644 index 00000000..16eabcc5 --- /dev/null +++ b/demos/quiet-sandbox/src/network/network.ts @@ -0,0 +1,74 @@ +import { MemoryDatastore } from 'datastore-core' + +import { createQsbLogger } from '../utils/logger/logger.js' +import { LocalStorage } from './storage.js' +import { QuietAuthEvents } from './events.js' +import { Libp2pService } from './libp2p/libp2p.js' +import { OrbitDbService } from './orbitdb/orbitdb.js' +import { MessageService } from './orbitdb/messages.js' + +export class Networking { + private _libp2p: Libp2pService + private _orbitDb: OrbitDbService + private _messages: MessageService + private _storage: LocalStorage + private _events: QuietAuthEvents + + private constructor( + libp2p: Libp2pService, + orbitDb: OrbitDbService, + messages: MessageService, + events: QuietAuthEvents + ) { + this._libp2p = libp2p + this._orbitDb = orbitDb + this._messages = messages + this._storage = libp2p.storage + this._events = events + } + + public static async init(storage: LocalStorage, events?: QuietAuthEvents): Promise { + const context = storage.getContext() + if (context == null) { + throw new Error(`Context hasn't been initialized!`) + } + + const quietEvents = events == null ? new QuietAuthEvents(context.user.userName) : events + const datastore = new MemoryDatastore() + + const libp2p = new Libp2pService(context.user.userId, storage, quietEvents) + const LOGGER = createQsbLogger(`networking:${await libp2p.getPeerId()}`) + LOGGER.info(`Initializing new libp2p service`) + await libp2p.init(datastore) + + const orbitDb = new OrbitDbService(libp2p) + LOGGER.info(`Initializing new orbitdb service`) + await orbitDb.init(datastore) + + const messages = new MessageService(orbitDb) + LOGGER.info(`Initializing new message service`) + await messages.init() + + return new Networking(libp2p, orbitDb, messages, quietEvents) + } + + get libp2p(): Libp2pService { + return this._libp2p + } + + get orbitDb(): OrbitDbService { + return this._orbitDb + } + + get messages(): MessageService { + return this._messages + } + + get storage(): LocalStorage { + return this._storage + } + + get events(): QuietAuthEvents { + return this._events + } +} diff --git a/demos/quiet-sandbox/src/network/orbitdb/messages.ts b/demos/quiet-sandbox/src/network/orbitdb/messages.ts new file mode 100644 index 00000000..a521c87b --- /dev/null +++ b/demos/quiet-sandbox/src/network/orbitdb/messages.ts @@ -0,0 +1,111 @@ +import { type Events } from '@orbitdb/core' +import { EventEmitter } from 'events' +import { EncryptedAndSignedPayload } from '../../auth/services/crypto/types.js' + +import { createQsbLogger, QuietLogger } from '../../utils/logger/logger.js' +import { OrbitDbService } from './orbitdb.js' + + +interface Channel { + name: string + addr: string +} + +export class MessageService extends EventEmitter { + + orbitDbService: OrbitDbService + channelListDb: Events | null + channelDbs: Record + private LOGGER: QuietLogger + + constructor(orbitDbService: OrbitDbService) { + super() + + this.orbitDbService = orbitDbService + this.channelListDb = null + this.channelDbs = {} + this.LOGGER = createQsbLogger(`orbitdb:messages:${orbitDbService.libp2pService.peerId}`) + } + + async init() { + if (this.orbitDbService.orbitDb == null) { + await this.orbitDbService.init() + } + + // Sharing the channelListDb address is necessary for other peers to connect + // to it. Each DB get's a unique address if only passing in a name to + // createDb. That's just how OrbitDB works because of immutable databases. + // Alternatively, I think you can share the owner's OrbitDB identity and + // create the database with the same access controller. + const name = 'channels' + this.LOGGER.info(`Initializing channel list DB with name '${name}'`); + this.channelListDb = await this.orbitDbService.createDb(name); + this.LOGGER.info(`Initialized channel list DB with address ${this.channelListDb.address}`) + this.channelListDb.events.on('update', entry => { + this.LOGGER.info(`Got new channel: ${JSON.stringify(entry.payload.value)}`) + this.openChannel(entry.payload.value) + }) + this.LOGGER.info(`Initializing known channel DBs`) + for (const entry of await this.channelListDb.all()) { + await this.openChannel(entry.value as Channel) + } + } + + async createChannel(channelName: string): Promise { + if (!this.channelListDb) { + throw new Error('Must call init before creating a channel') + } + + if (channelName in this.channelDbs) { + return this.channelDbs[channelName] + } + + const db = await this.createChannelDb('channel/' + channelName) + + this.channelDbs[channelName] = db + await this.channelListDb.add({ name: channelName, addr: db.address }) + + return db + } + + private async openChannel(channel: Channel) { + if (channel.name in this.channelDbs) { + return + } + + this.channelDbs[channel.name] = await this.createChannelDb(channel.addr) + } + + private async createChannelDb(nameOrAddr: string): Promise { + const db = await this.orbitDbService.createDb(nameOrAddr) + + db.events.on('update', entry => this.emit('message', entry.payload.value)) + + return db + } + + async sendMessage(channelName: string, message: EncryptedAndSignedPayload) { + if (channelName in this.channelDbs) { + this.channelDbs[channelName].add(message) + } else { + throw new Error('Channel does not exist') + } + } + + async readMessages(channelName: string): Promise { + let db: Events + if (channelName in this.channelDbs) { + db = this.channelDbs[channelName] + } else { + throw new Error('Channel does not exist') + } + + const messages = await db.all() + return messages.map((message) => message.value as EncryptedAndSignedPayload) + } + + async close() { + this.LOGGER.info('Closing OrbitDB') + await this.orbitDbService?.close() + } +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/network/orbitdb/orbitdb.ts b/demos/quiet-sandbox/src/network/orbitdb/orbitdb.ts new file mode 100644 index 00000000..17ac4a0b --- /dev/null +++ b/demos/quiet-sandbox/src/network/orbitdb/orbitdb.ts @@ -0,0 +1,84 @@ +import { createHelia, type Helia } from 'helia' +import { createOrbitDB, Documents, type Events, Identities, KeyStore, OrbitDB, OrbitDBAccessController } from '@orbitdb/core' +import { MemoryDatastore } from 'datastore-core' +import path from 'path' + +import { createQsbLogger, QuietLogger } from '../../utils/logger/logger.js' +import { Libp2pService } from '../libp2p/libp2p.js' + +export function getAccessController() { + return OrbitDBAccessController({ write: ["*"] }) +} + +export class OrbitDbService { + libp2pService: Libp2pService + ipfs: Helia | null + orbitDb: OrbitDB | null + dir: string + private LOGGER: QuietLogger + + constructor(libp2pService: Libp2pService) { + this.libp2pService = libp2pService + this.ipfs = null + this.orbitDb = null + this.dir = `./.orbitdb/${libp2pService.peerName}` + this.LOGGER = createQsbLogger(`orbitdb:${libp2pService.peerId}`) + } + + async init(datastore: MemoryDatastore = new MemoryDatastore()) { + this.LOGGER.info('Initializing OrbitDB') + if (!this.libp2pService.libp2p) { + throw new Error('Cannot initialize OrbitDB, libp2p instance required') + } + this.ipfs = await createHelia({ libp2p: this.libp2pService.libp2p, datastore }) + await this.ipfs.start() + const keystore = await KeyStore({ path: path.join(this.dir, './keystore') }) + const identities = await Identities({ ipfs: this.ipfs, keystore }) + this.orbitDb = await createOrbitDB({ + id: (await this.libp2pService.getPeerId()).toString(), + ipfs: this.ipfs, + directory: this.dir, + identities + }) + } + + async createDb(dbName: string): Promise { + if (!this.orbitDb) { + throw new Error('Must call init before creating a DB') + } + // Seems like we can improve the type definitions + return await this.orbitDb.open( + dbName, + { + type: 'events', + write: ['*'], + sync: true, + meta: {}, + AccessController: getAccessController() + } + ) as Events + } + + async createDocumentDb(dbName: string): Promise { + if (!this.orbitDb) { + throw new Error('Must call init before creating a DB') + } + // Seems like we can improve the type definitions + return await this.orbitDb.open( + dbName, + { + type: 'documents', + write: ['*'], + sync: true, + meta: {}, + AccessController: getAccessController() + } + ) as Documents + } + + async close() { + this.LOGGER.warn('Closing OrbitDB') + await this.orbitDb?.stop() + await this.ipfs?.stop() + } +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/network/storage.ts b/demos/quiet-sandbox/src/network/storage.ts new file mode 100644 index 00000000..86cf07c5 --- /dev/null +++ b/demos/quiet-sandbox/src/network/storage.ts @@ -0,0 +1,39 @@ +import * as Auth from '@localfirst/auth' +import { SigChain } from '../auth/chain.js' + + +export class LocalStorage { + private authContext: Auth.Context | null + private context: Auth.LocalUserContext | null + private sigChain: SigChain | null + + constructor() { + this.authContext = null + this.context = null + this.sigChain = null + } + +public setAuthContext(context: Auth.Context) { + this.authContext = context +} + +public getAuthContext(): Auth.Context | null { + return this.authContext +} + +public setSigChain(sigChain: SigChain) { + this.sigChain = sigChain +} + +public setContext(context: Auth.LocalUserContext) { + this.context = context +} + +public getSigChain(): SigChain | null { + return this.sigChain +} + +public getContext(): Auth.LocalUserContext | null { + return this.context +} +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/network/utils.ts b/demos/quiet-sandbox/src/network/utils.ts new file mode 100644 index 00000000..3b9be4fd --- /dev/null +++ b/demos/quiet-sandbox/src/network/utils.ts @@ -0,0 +1,31 @@ +import * as os from 'os' + +export type IPMap = { + [interfaceName: string]: string[] +} + +export const DEFAULT_NETWORK_INTERFACE = 'en0' + +/* +Shamelessly copied from https://stackoverflow.com/a/8440736 +*/ +export function getIpAddresses(): IPMap { + const interfaces = os.networkInterfaces(); + const results: IPMap = {}; // Or just '{}', an empty object + + for (const name in interfaces) { + for (const net of interfaces[name]!) { + // Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses + // 'IPv4' is in Node <= 17, from 18 it's a number 4 or 6 + const familyV4Value = typeof net.family === 'string' ? 'IPv4' : 4 + if (net.family === familyV4Value && !net.internal) { + if (!results[name]) { + results[name] = []; + } + results[name].push(net.address); + } + } + } + + return results +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/prompts/channels.ts b/demos/quiet-sandbox/src/prompts/channels.ts index 6f32a693..fd082258 100644 --- a/demos/quiet-sandbox/src/prompts/channels.ts +++ b/demos/quiet-sandbox/src/prompts/channels.ts @@ -6,7 +6,7 @@ import chalk from 'chalk'; import { LocalUserContext, Member } from '@localfirst/auth'; import actionSelect from '../components/actionSelect.js'; -import { Networking } from '../network.js'; +import { Networking } from '../network/network.js'; import { SigChain } from '../auth/chain.js'; import { Channel, RoleMemberInfo, TruncatedChannel } from '../auth/services/roles/roles.js'; import { EncryptedAndSignedPayload, EncryptionScopeType } from '../auth/services/crypto/types.js'; diff --git a/demos/quiet-sandbox/src/prompts/interactive.ts b/demos/quiet-sandbox/src/prompts/interactive.ts index 4aa9c751..f7ebb3c8 100644 --- a/demos/quiet-sandbox/src/prompts/interactive.ts +++ b/demos/quiet-sandbox/src/prompts/interactive.ts @@ -6,10 +6,11 @@ import actionSelect from '../components/actionSelect.js'; import { teamAdd, teamInfo } from './team.js'; import { channelCreate, channelsList } from './channels.js'; import { invitesList, inviteAdd } from './invites.js'; -import { LocalStorage, Networking } from '../network.js'; +import { Networking } from '../network/network.js'; import { peerConnect, peerInfo } from './peers.js'; import { me } from './me.js'; import { roleCreate, rolesList } from './roles.js'; +import { LocalStorage } from '../network/storage.js'; const mainLoop = async (storage: LocalStorage, networking?: Networking) => { let exit = false; diff --git a/demos/quiet-sandbox/src/prompts/invites.ts b/demos/quiet-sandbox/src/prompts/invites.ts index 01d9c018..d52ef7df 100644 --- a/demos/quiet-sandbox/src/prompts/invites.ts +++ b/demos/quiet-sandbox/src/prompts/invites.ts @@ -4,7 +4,7 @@ import clipboard from 'clipboardy'; import actionSelect from "../components/actionSelect.js"; import { DEFAULT_INVITATION_VALID_FOR_MS, DEFAULT_MAX_USES } from "../auth/services/invites/inviteService.js"; -import { LocalStorage } from "../network.js"; +import { LocalStorage } from "../network/storage.js"; const inviteSeedMap = new Map() diff --git a/demos/quiet-sandbox/src/prompts/me.ts b/demos/quiet-sandbox/src/prompts/me.ts index d08f2c5d..7dd4db42 100644 --- a/demos/quiet-sandbox/src/prompts/me.ts +++ b/demos/quiet-sandbox/src/prompts/me.ts @@ -4,7 +4,7 @@ import chalk from 'chalk'; import clipboard from 'clipboardy'; import { Peer } from '@libp2p/interface'; -import { Networking } from '../network.js'; +import { Networking } from '../network/network.js'; import actionSelect from '../components/actionSelect.js'; const displayMyInfo = async (networking: Networking) => { diff --git a/demos/quiet-sandbox/src/prompts/peers.ts b/demos/quiet-sandbox/src/prompts/peers.ts index 30c0bb64..6531f1f9 100644 --- a/demos/quiet-sandbox/src/prompts/peers.ts +++ b/demos/quiet-sandbox/src/prompts/peers.ts @@ -3,9 +3,8 @@ import { input } from '@inquirer/prompts'; import { Peer, PeerId } from '@libp2p/interface'; import clipboard from 'clipboardy'; -import { peerIdFromString } from '@libp2p/peer-id'; -import { Networking } from '../network.js'; +import { Networking } from '../network/network.js'; import actionSelect from '../components/actionSelect.js'; const peerInfo = async (networking: Networking | undefined) => { @@ -60,7 +59,7 @@ const peerConnect = async (networking: Networking | undefined) => { validate: (addr: string) => addr != null ? true : "Must enter a valid peer address!" }); - const success = await networking.libp2p.dial(addr); + const success = await networking.libp2p.dial(new Set([addr])); console.log(`Connection to ${addr} success? ${success}`) await peerInfo(networking) diff --git a/demos/quiet-sandbox/src/prompts/roles.ts b/demos/quiet-sandbox/src/prompts/roles.ts index 37730c61..f79b2005 100644 --- a/demos/quiet-sandbox/src/prompts/roles.ts +++ b/demos/quiet-sandbox/src/prompts/roles.ts @@ -6,7 +6,7 @@ import chalk from 'chalk'; import { LocalUserContext, Member } from '@localfirst/auth'; import actionSelect from '../components/actionSelect.js'; -import { Networking } from '../network.js'; +import { Networking } from '../network/network.js'; import { SigChain } from '../auth/chain.js'; import { QuietRole, RoleMemberInfo, TruncatedQuietRole } from '../auth/services/roles/roles.js'; diff --git a/demos/quiet-sandbox/src/prompts/team.ts b/demos/quiet-sandbox/src/prompts/team.ts index 6c70d488..a3c58f70 100644 --- a/demos/quiet-sandbox/src/prompts/team.ts +++ b/demos/quiet-sandbox/src/prompts/team.ts @@ -5,10 +5,11 @@ import { confirm, input } from '@inquirer/prompts'; import clipboard from 'clipboardy'; import { SigChain } from '../auth/chain.js'; -import { LocalStorage, Networking } from '../network.js'; +import { Networking } from '../network/network.js'; import { UserService } from '../auth/services/members/userService.js'; import { makeChannelsPrintable } from './channels.js'; import { makeRolesPrintable } from './roles.js'; +import { LocalStorage } from '../network/storage.js'; const teamInfo = async (networking: Networking | undefined) => { if (networking == null || networking.libp2p == null || networking.libp2p.libp2p == null) { diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/data.json b/demos/quiet-sandbox/src/scripts/isla-perf/data.json new file mode 100644 index 00000000..08ae195b --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/data.json @@ -0,0 +1,2692 @@ +[ + { + "userCount": 89, + "avgChainLoadTimeMs": null, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": null, + "connectedPeers": [] + }, + { + "userCount": 89, + "avgChainLoadTimeMs": null, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": null, + "connectedPeers": [] + }, + { + "userCount": 89, + "avgChainLoadTimeMs": null, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": null, + "connectedPeers": [] + }, + { + "userCount": 89, + "avgChainLoadTimeMs": null, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": null, + "connectedPeers": [] + }, + { + "userCount": 89, + "avgChainLoadTimeMs": null, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": null, + "connectedPeers": [] + }, + { + "userCount": 89, + "avgChainLoadTimeMs": null, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": null, + "connectedPeers": [] + }, + { + "userCount": 89, + "avgChainLoadTimeMs": null, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": null, + "connectedPeers": [] + }, + { + "userCount": 89, + "avgChainLoadTimeMs": null, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": null, + "connectedPeers": [] + }, + { + "userCount": 89, + "avgChainLoadTimeMs": null, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": null, + "connectedPeers": [] + }, + { + "userCount": 89, + "avgChainLoadTimeMs": null, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": null, + "connectedPeers": [] + }, + { + "userCount": 94, + "avgChainLoadTimeMs": 9838, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 7 + }, + { + "username": "perf-user-106", + "diff": 1 + }, + { + "username": "perf-user-109", + "diff": 2 + }, + { + "username": "perf-user-110", + "diff": 1 + }, + { + "username": "perf-user-111", + "diff": 1 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 43 + }, + { + "username": "perf-user-106", + "diff": 43 + }, + { + "username": "perf-user-109", + "diff": 47 + }, + { + "username": "perf-user-110", + "diff": 43 + }, + { + "username": "perf-user-111", + "diff": 43 + } + ], + "memberDiffMeta": { + "count": 5, + "avg": 2.4 + }, + "deviceDiffMeta": { + "count": 5, + "avg": 43.8 + }, + "avgConnectedPeers": 5.4, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 3 + }, + { + "username": "perf-user-110", + "connectedPeers": 6 + }, + { + "username": "perf-user-111", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 94, + "avgChainLoadTimeMs": 9838, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 7 + }, + { + "username": "perf-user-106", + "diff": 1 + }, + { + "username": "perf-user-109", + "diff": 2 + }, + { + "username": "perf-user-110", + "diff": 1 + }, + { + "username": "perf-user-111", + "diff": 1 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 43 + }, + { + "username": "perf-user-106", + "diff": 43 + }, + { + "username": "perf-user-109", + "diff": 47 + }, + { + "username": "perf-user-110", + "diff": 43 + }, + { + "username": "perf-user-111", + "diff": 43 + } + ], + "memberDiffMeta": { + "count": 5, + "avg": 2.4 + }, + "deviceDiffMeta": { + "count": 5, + "avg": 43.8 + }, + "avgConnectedPeers": 6.2, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 7 + }, + { + "username": "perf-user-111", + "connectedPeers": 6 + } + ] + }, + { + "userCount": 94, + "avgChainLoadTimeMs": 9838, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 7 + }, + { + "username": "perf-user-106", + "diff": 1 + }, + { + "username": "perf-user-109", + "diff": 2 + }, + { + "username": "perf-user-110", + "diff": 1 + }, + { + "username": "perf-user-111", + "diff": 1 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 43 + }, + { + "username": "perf-user-106", + "diff": 43 + }, + { + "username": "perf-user-109", + "diff": 47 + }, + { + "username": "perf-user-110", + "diff": 43 + }, + { + "username": "perf-user-111", + "diff": 43 + } + ], + "memberDiffMeta": { + "count": 5, + "avg": 2.4 + }, + "deviceDiffMeta": { + "count": 5, + "avg": 43.8 + }, + "avgConnectedPeers": 6.2, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 7 + }, + { + "username": "perf-user-111", + "connectedPeers": 6 + } + ] + }, + { + "userCount": 94, + "avgChainLoadTimeMs": 9838, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 7 + }, + { + "username": "perf-user-106", + "diff": 1 + }, + { + "username": "perf-user-109", + "diff": 2 + }, + { + "username": "perf-user-110", + "diff": 1 + }, + { + "username": "perf-user-111", + "diff": 1 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 43 + }, + { + "username": "perf-user-106", + "diff": 43 + }, + { + "username": "perf-user-109", + "diff": 47 + }, + { + "username": "perf-user-110", + "diff": 43 + }, + { + "username": "perf-user-111", + "diff": 43 + } + ], + "memberDiffMeta": { + "count": 5, + "avg": 2.4 + }, + "deviceDiffMeta": { + "count": 5, + "avg": 43.8 + }, + "avgConnectedPeers": 6.2, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 7 + }, + { + "username": "perf-user-111", + "connectedPeers": 6 + } + ] + }, + { + "userCount": 94, + "avgChainLoadTimeMs": 9838, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 7 + }, + { + "username": "perf-user-106", + "diff": 1 + }, + { + "username": "perf-user-109", + "diff": 2 + }, + { + "username": "perf-user-110", + "diff": 1 + }, + { + "username": "perf-user-111", + "diff": 1 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 43 + }, + { + "username": "perf-user-106", + "diff": 43 + }, + { + "username": "perf-user-109", + "diff": 47 + }, + { + "username": "perf-user-110", + "diff": 43 + }, + { + "username": "perf-user-111", + "diff": 43 + } + ], + "memberDiffMeta": { + "count": 5, + "avg": 2.4 + }, + "deviceDiffMeta": { + "count": 5, + "avg": 43.8 + }, + "avgConnectedPeers": 6.4, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 7 + }, + { + "username": "perf-user-111", + "connectedPeers": 7 + } + ] + }, + { + "userCount": 94, + "avgChainLoadTimeMs": 9838, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 7 + }, + { + "username": "perf-user-106", + "diff": 1 + }, + { + "username": "perf-user-109", + "diff": 2 + }, + { + "username": "perf-user-110", + "diff": 1 + }, + { + "username": "perf-user-111", + "diff": 1 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 43 + }, + { + "username": "perf-user-106", + "diff": 43 + }, + { + "username": "perf-user-109", + "diff": 47 + }, + { + "username": "perf-user-110", + "diff": 43 + }, + { + "username": "perf-user-111", + "diff": 43 + } + ], + "memberDiffMeta": { + "count": 5, + "avg": 2.4 + }, + "deviceDiffMeta": { + "count": 5, + "avg": 43.8 + }, + "avgConnectedPeers": 6.8, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 8 + }, + { + "username": "perf-user-111", + "connectedPeers": 8 + } + ] + }, + { + "userCount": 94, + "avgChainLoadTimeMs": 9838, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 7 + }, + { + "username": "perf-user-106", + "diff": 1 + }, + { + "username": "perf-user-109", + "diff": 2 + }, + { + "username": "perf-user-110", + "diff": 1 + }, + { + "username": "perf-user-111", + "diff": 1 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 43 + }, + { + "username": "perf-user-106", + "diff": 43 + }, + { + "username": "perf-user-109", + "diff": 47 + }, + { + "username": "perf-user-110", + "diff": 43 + }, + { + "username": "perf-user-111", + "diff": 43 + } + ], + "memberDiffMeta": { + "count": 5, + "avg": 2.4 + }, + "deviceDiffMeta": { + "count": 5, + "avg": 43.8 + }, + "avgConnectedPeers": 6.8, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 8 + }, + { + "username": "perf-user-111", + "connectedPeers": 8 + } + ] + }, + { + "userCount": 94, + "avgChainLoadTimeMs": 9838, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 7 + }, + { + "username": "perf-user-106", + "diff": 1 + }, + { + "username": "perf-user-109", + "diff": 2 + }, + { + "username": "perf-user-110", + "diff": 1 + }, + { + "username": "perf-user-111", + "diff": 1 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 43 + }, + { + "username": "perf-user-106", + "diff": 43 + }, + { + "username": "perf-user-109", + "diff": 47 + }, + { + "username": "perf-user-110", + "diff": 43 + }, + { + "username": "perf-user-111", + "diff": 43 + } + ], + "memberDiffMeta": { + "count": 5, + "avg": 2.4 + }, + "deviceDiffMeta": { + "count": 5, + "avg": 43.8 + }, + "avgConnectedPeers": 6.8, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 8 + }, + { + "username": "perf-user-111", + "connectedPeers": 8 + } + ] + }, + { + "userCount": 99, + "avgChainLoadTimeMs": 12018.3, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 11 + }, + { + "username": "perf-user-106", + "diff": 6 + }, + { + "username": "perf-user-109", + "diff": 7 + }, + { + "username": "perf-user-110", + "diff": 6 + }, + { + "username": "perf-user-111", + "diff": 6 + }, + { + "username": "perf-user-119", + "diff": 14 + }, + { + "username": "perf-user-122", + "diff": 29 + }, + { + "username": "perf-user-125", + "diff": 11 + }, + { + "username": "perf-user-132", + "diff": 21 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 51 + }, + { + "username": "perf-user-106", + "diff": 53 + }, + { + "username": "perf-user-109", + "diff": 57 + }, + { + "username": "perf-user-110", + "diff": 53 + }, + { + "username": "perf-user-111", + "diff": 53 + }, + { + "username": "perf-user-119", + "diff": 55 + }, + { + "username": "perf-user-122", + "diff": 59 + }, + { + "username": "perf-user-123", + "diff": 57 + }, + { + "username": "perf-user-125", + "diff": 51 + }, + { + "username": "perf-user-132", + "diff": 55 + } + ], + "memberDiffMeta": { + "count": 9, + "avg": 12.333333333333334 + }, + "deviceDiffMeta": { + "count": 10, + "avg": 54.4 + }, + "avgConnectedPeers": 6.1, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 8 + }, + { + "username": "perf-user-111", + "connectedPeers": 9 + }, + { + "username": "perf-user-119", + "connectedPeers": 5 + }, + { + "username": "perf-user-122", + "connectedPeers": 5 + }, + { + "username": "perf-user-123", + "connectedPeers": 6 + }, + { + "username": "perf-user-125", + "connectedPeers": 5 + }, + { + "username": "perf-user-132", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 99, + "avgChainLoadTimeMs": 12018.3, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 11 + }, + { + "username": "perf-user-106", + "diff": 6 + }, + { + "username": "perf-user-109", + "diff": 7 + }, + { + "username": "perf-user-110", + "diff": 6 + }, + { + "username": "perf-user-111", + "diff": 6 + }, + { + "username": "perf-user-119", + "diff": 14 + }, + { + "username": "perf-user-122", + "diff": 29 + }, + { + "username": "perf-user-125", + "diff": 11 + }, + { + "username": "perf-user-132", + "diff": 21 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 51 + }, + { + "username": "perf-user-106", + "diff": 53 + }, + { + "username": "perf-user-109", + "diff": 57 + }, + { + "username": "perf-user-110", + "diff": 53 + }, + { + "username": "perf-user-111", + "diff": 53 + }, + { + "username": "perf-user-119", + "diff": 55 + }, + { + "username": "perf-user-122", + "diff": 59 + }, + { + "username": "perf-user-123", + "diff": 57 + }, + { + "username": "perf-user-125", + "diff": 51 + }, + { + "username": "perf-user-132", + "diff": 55 + } + ], + "memberDiffMeta": { + "count": 9, + "avg": 12.333333333333334 + }, + "deviceDiffMeta": { + "count": 10, + "avg": 54.4 + }, + "avgConnectedPeers": 6.1, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 8 + }, + { + "username": "perf-user-111", + "connectedPeers": 9 + }, + { + "username": "perf-user-119", + "connectedPeers": 5 + }, + { + "username": "perf-user-122", + "connectedPeers": 5 + }, + { + "username": "perf-user-123", + "connectedPeers": 6 + }, + { + "username": "perf-user-125", + "connectedPeers": 5 + }, + { + "username": "perf-user-132", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 99, + "avgChainLoadTimeMs": 12018.3, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 11 + }, + { + "username": "perf-user-106", + "diff": 6 + }, + { + "username": "perf-user-109", + "diff": 7 + }, + { + "username": "perf-user-110", + "diff": 6 + }, + { + "username": "perf-user-111", + "diff": 6 + }, + { + "username": "perf-user-119", + "diff": 14 + }, + { + "username": "perf-user-122", + "diff": 29 + }, + { + "username": "perf-user-125", + "diff": 11 + }, + { + "username": "perf-user-132", + "diff": 21 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 51 + }, + { + "username": "perf-user-106", + "diff": 53 + }, + { + "username": "perf-user-109", + "diff": 57 + }, + { + "username": "perf-user-110", + "diff": 53 + }, + { + "username": "perf-user-111", + "diff": 53 + }, + { + "username": "perf-user-119", + "diff": 55 + }, + { + "username": "perf-user-122", + "diff": 59 + }, + { + "username": "perf-user-123", + "diff": 57 + }, + { + "username": "perf-user-125", + "diff": 51 + }, + { + "username": "perf-user-132", + "diff": 55 + } + ], + "memberDiffMeta": { + "count": 9, + "avg": 12.333333333333334 + }, + "deviceDiffMeta": { + "count": 10, + "avg": 54.4 + }, + "avgConnectedPeers": 6.1, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 8 + }, + { + "username": "perf-user-111", + "connectedPeers": 9 + }, + { + "username": "perf-user-119", + "connectedPeers": 5 + }, + { + "username": "perf-user-122", + "connectedPeers": 5 + }, + { + "username": "perf-user-123", + "connectedPeers": 6 + }, + { + "username": "perf-user-125", + "connectedPeers": 5 + }, + { + "username": "perf-user-132", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 99, + "avgChainLoadTimeMs": 12018.3, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 11 + }, + { + "username": "perf-user-106", + "diff": 6 + }, + { + "username": "perf-user-109", + "diff": 7 + }, + { + "username": "perf-user-110", + "diff": 6 + }, + { + "username": "perf-user-111", + "diff": 6 + }, + { + "username": "perf-user-119", + "diff": 14 + }, + { + "username": "perf-user-122", + "diff": 29 + }, + { + "username": "perf-user-125", + "diff": 11 + }, + { + "username": "perf-user-132", + "diff": 21 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 51 + }, + { + "username": "perf-user-106", + "diff": 53 + }, + { + "username": "perf-user-109", + "diff": 57 + }, + { + "username": "perf-user-110", + "diff": 53 + }, + { + "username": "perf-user-111", + "diff": 53 + }, + { + "username": "perf-user-119", + "diff": 55 + }, + { + "username": "perf-user-122", + "diff": 59 + }, + { + "username": "perf-user-123", + "diff": 57 + }, + { + "username": "perf-user-125", + "diff": 51 + }, + { + "username": "perf-user-132", + "diff": 55 + } + ], + "memberDiffMeta": { + "count": 9, + "avg": 12.333333333333334 + }, + "deviceDiffMeta": { + "count": 10, + "avg": 54.4 + }, + "avgConnectedPeers": 6.1, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 8 + }, + { + "username": "perf-user-111", + "connectedPeers": 9 + }, + { + "username": "perf-user-119", + "connectedPeers": 5 + }, + { + "username": "perf-user-122", + "connectedPeers": 5 + }, + { + "username": "perf-user-123", + "connectedPeers": 6 + }, + { + "username": "perf-user-125", + "connectedPeers": 5 + }, + { + "username": "perf-user-132", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 99, + "avgChainLoadTimeMs": 12018.3, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 11 + }, + { + "username": "perf-user-106", + "diff": 6 + }, + { + "username": "perf-user-109", + "diff": 7 + }, + { + "username": "perf-user-110", + "diff": 6 + }, + { + "username": "perf-user-111", + "diff": 6 + }, + { + "username": "perf-user-119", + "diff": 14 + }, + { + "username": "perf-user-122", + "diff": 29 + }, + { + "username": "perf-user-125", + "diff": 11 + }, + { + "username": "perf-user-132", + "diff": 21 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 51 + }, + { + "username": "perf-user-106", + "diff": 53 + }, + { + "username": "perf-user-109", + "diff": 57 + }, + { + "username": "perf-user-110", + "diff": 53 + }, + { + "username": "perf-user-111", + "diff": 53 + }, + { + "username": "perf-user-119", + "diff": 55 + }, + { + "username": "perf-user-122", + "diff": 59 + }, + { + "username": "perf-user-123", + "diff": 57 + }, + { + "username": "perf-user-125", + "diff": 51 + }, + { + "username": "perf-user-132", + "diff": 55 + } + ], + "memberDiffMeta": { + "count": 9, + "avg": 12.333333333333334 + }, + "deviceDiffMeta": { + "count": 10, + "avg": 54.4 + }, + "avgConnectedPeers": 6.1, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 8 + }, + { + "username": "perf-user-111", + "connectedPeers": 9 + }, + { + "username": "perf-user-119", + "connectedPeers": 5 + }, + { + "username": "perf-user-122", + "connectedPeers": 5 + }, + { + "username": "perf-user-123", + "connectedPeers": 6 + }, + { + "username": "perf-user-125", + "connectedPeers": 5 + }, + { + "username": "perf-user-132", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 99, + "avgChainLoadTimeMs": 12018.3, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 11 + }, + { + "username": "perf-user-106", + "diff": 6 + }, + { + "username": "perf-user-109", + "diff": 7 + }, + { + "username": "perf-user-110", + "diff": 6 + }, + { + "username": "perf-user-111", + "diff": 6 + }, + { + "username": "perf-user-119", + "diff": 14 + }, + { + "username": "perf-user-122", + "diff": 29 + }, + { + "username": "perf-user-125", + "diff": 11 + }, + { + "username": "perf-user-132", + "diff": 21 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 51 + }, + { + "username": "perf-user-106", + "diff": 53 + }, + { + "username": "perf-user-109", + "diff": 57 + }, + { + "username": "perf-user-110", + "diff": 53 + }, + { + "username": "perf-user-111", + "diff": 53 + }, + { + "username": "perf-user-119", + "diff": 55 + }, + { + "username": "perf-user-122", + "diff": 59 + }, + { + "username": "perf-user-123", + "diff": 57 + }, + { + "username": "perf-user-125", + "diff": 51 + }, + { + "username": "perf-user-132", + "diff": 55 + } + ], + "memberDiffMeta": { + "count": 9, + "avg": 12.333333333333334 + }, + "deviceDiffMeta": { + "count": 10, + "avg": 54.4 + }, + "avgConnectedPeers": 6.1, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 6 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 8 + }, + { + "username": "perf-user-111", + "connectedPeers": 9 + }, + { + "username": "perf-user-119", + "connectedPeers": 5 + }, + { + "username": "perf-user-122", + "connectedPeers": 5 + }, + { + "username": "perf-user-123", + "connectedPeers": 6 + }, + { + "username": "perf-user-125", + "connectedPeers": 5 + }, + { + "username": "perf-user-132", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 104, + "avgChainLoadTimeMs": 11542.133333333333, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 16 + }, + { + "username": "perf-user-106", + "diff": 11 + }, + { + "username": "perf-user-109", + "diff": 12 + }, + { + "username": "perf-user-110", + "diff": 11 + }, + { + "username": "perf-user-111", + "diff": 11 + }, + { + "username": "perf-user-119", + "diff": 19 + }, + { + "username": "perf-user-122", + "diff": 31 + }, + { + "username": "perf-user-125", + "diff": 16 + }, + { + "username": "perf-user-132", + "diff": 25 + }, + { + "username": "perf-user-138", + "diff": 24 + }, + { + "username": "perf-user-140", + "diff": 31 + }, + { + "username": "perf-user-141", + "diff": 31 + }, + { + "username": "perf-user-145", + "diff": 31 + }, + { + "username": "perf-user-153", + "diff": 25 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 61 + }, + { + "username": "perf-user-106", + "diff": 63 + }, + { + "username": "perf-user-109", + "diff": 67 + }, + { + "username": "perf-user-110", + "diff": 63 + }, + { + "username": "perf-user-111", + "diff": 63 + }, + { + "username": "perf-user-119", + "diff": 65 + }, + { + "username": "perf-user-122", + "diff": 63 + }, + { + "username": "perf-user-123", + "diff": 67 + }, + { + "username": "perf-user-125", + "diff": 61 + }, + { + "username": "perf-user-132", + "diff": 63 + }, + { + "username": "perf-user-138", + "diff": 65 + }, + { + "username": "perf-user-140", + "diff": 63 + }, + { + "username": "perf-user-141", + "diff": 63 + }, + { + "username": "perf-user-145", + "diff": 63 + }, + { + "username": "perf-user-153", + "diff": 63 + } + ], + "memberDiffMeta": { + "count": 14, + "avg": 21 + }, + "deviceDiffMeta": { + "count": 15, + "avg": 63.53333333333333 + }, + "avgConnectedPeers": 6.6, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 7 + }, + { + "username": "perf-user-106", + "connectedPeers": 8 + }, + { + "username": "perf-user-109", + "connectedPeers": 7 + }, + { + "username": "perf-user-110", + "connectedPeers": 8 + }, + { + "username": "perf-user-111", + "connectedPeers": 9 + }, + { + "username": "perf-user-119", + "connectedPeers": 6 + }, + { + "username": "perf-user-122", + "connectedPeers": 8 + }, + { + "username": "perf-user-123", + "connectedPeers": 8 + }, + { + "username": "perf-user-125", + "connectedPeers": 6 + }, + { + "username": "perf-user-132", + "connectedPeers": 6 + }, + { + "username": "perf-user-138", + "connectedPeers": 5 + }, + { + "username": "perf-user-140", + "connectedPeers": 6 + }, + { + "username": "perf-user-141", + "connectedPeers": 5 + }, + { + "username": "perf-user-145", + "connectedPeers": 5 + }, + { + "username": "perf-user-153", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 109, + "avgChainLoadTimeMs": 11009.55, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 21 + }, + { + "username": "perf-user-106", + "diff": 16 + }, + { + "username": "perf-user-109", + "diff": 17 + }, + { + "username": "perf-user-110", + "diff": 16 + }, + { + "username": "perf-user-111", + "diff": 16 + }, + { + "username": "perf-user-119", + "diff": 23 + }, + { + "username": "perf-user-122", + "diff": 36 + }, + { + "username": "perf-user-125", + "diff": 21 + }, + { + "username": "perf-user-132", + "diff": 30 + }, + { + "username": "perf-user-138", + "diff": 29 + }, + { + "username": "perf-user-140", + "diff": 36 + }, + { + "username": "perf-user-141", + "diff": 36 + }, + { + "username": "perf-user-145", + "diff": 36 + }, + { + "username": "perf-user-153", + "diff": 30 + }, + { + "username": "perf-user-154", + "diff": 23 + }, + { + "username": "perf-user-157", + "diff": 19 + }, + { + "username": "perf-user-159", + "diff": 19 + }, + { + "username": "perf-user-160", + "diff": 19 + }, + { + "username": "perf-user-163", + "diff": 19 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 71 + }, + { + "username": "perf-user-106", + "diff": 73 + }, + { + "username": "perf-user-109", + "diff": 77 + }, + { + "username": "perf-user-110", + "diff": 73 + }, + { + "username": "perf-user-111", + "diff": 73 + }, + { + "username": "perf-user-119", + "diff": 73 + }, + { + "username": "perf-user-122", + "diff": 73 + }, + { + "username": "perf-user-123", + "diff": 77 + }, + { + "username": "perf-user-125", + "diff": 71 + }, + { + "username": "perf-user-132", + "diff": 73 + }, + { + "username": "perf-user-138", + "diff": 75 + }, + { + "username": "perf-user-140", + "diff": 73 + }, + { + "username": "perf-user-141", + "diff": 73 + }, + { + "username": "perf-user-145", + "diff": 73 + }, + { + "username": "perf-user-153", + "diff": 73 + }, + { + "username": "perf-user-154", + "diff": 73 + }, + { + "username": "perf-user-157", + "diff": 69 + }, + { + "username": "perf-user-159", + "diff": 69 + }, + { + "username": "perf-user-160", + "diff": 69 + }, + { + "username": "perf-user-163", + "diff": 69 + } + ], + "memberDiffMeta": { + "count": 19, + "avg": 24.31578947368421 + }, + "deviceDiffMeta": { + "count": 20, + "avg": 72.5 + }, + "avgConnectedPeers": 6.05, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 5 + }, + { + "username": "perf-user-106", + "connectedPeers": 5 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 14 + }, + { + "username": "perf-user-111", + "connectedPeers": 5 + }, + { + "username": "perf-user-119", + "connectedPeers": 5 + }, + { + "username": "perf-user-122", + "connectedPeers": 5 + }, + { + "username": "perf-user-123", + "connectedPeers": 5 + }, + { + "username": "perf-user-125", + "connectedPeers": 5 + }, + { + "username": "perf-user-132", + "connectedPeers": 5 + }, + { + "username": "perf-user-138", + "connectedPeers": 5 + }, + { + "username": "perf-user-140", + "connectedPeers": 5 + }, + { + "username": "perf-user-141", + "connectedPeers": 5 + }, + { + "username": "perf-user-145", + "connectedPeers": 5 + }, + { + "username": "perf-user-153", + "connectedPeers": 5 + }, + { + "username": "perf-user-154", + "connectedPeers": 5 + }, + { + "username": "perf-user-157", + "connectedPeers": 10 + }, + { + "username": "perf-user-159", + "connectedPeers": 9 + }, + { + "username": "perf-user-160", + "connectedPeers": 8 + }, + { + "username": "perf-user-163", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 114, + "avgChainLoadTimeMs": 9874.64, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 26 + }, + { + "username": "perf-user-106", + "diff": 21 + }, + { + "username": "perf-user-109", + "diff": 22 + }, + { + "username": "perf-user-110", + "diff": 21 + }, + { + "username": "perf-user-111", + "diff": 21 + }, + { + "username": "perf-user-119", + "diff": 28 + }, + { + "username": "perf-user-122", + "diff": 41 + }, + { + "username": "perf-user-123", + "diff": 2 + }, + { + "username": "perf-user-125", + "diff": 26 + }, + { + "username": "perf-user-132", + "diff": 35 + }, + { + "username": "perf-user-138", + "diff": 34 + }, + { + "username": "perf-user-140", + "diff": 41 + }, + { + "username": "perf-user-141", + "diff": 41 + }, + { + "username": "perf-user-145", + "diff": 41 + }, + { + "username": "perf-user-153", + "diff": 35 + }, + { + "username": "perf-user-154", + "diff": 28 + }, + { + "username": "perf-user-157", + "diff": 19 + }, + { + "username": "perf-user-159", + "diff": 19 + }, + { + "username": "perf-user-160", + "diff": 19 + }, + { + "username": "perf-user-163", + "diff": 19 + }, + { + "username": "perf-user-164", + "diff": 19 + }, + { + "username": "perf-user-165", + "diff": 19 + }, + { + "username": "perf-user-166", + "diff": 19 + }, + { + "username": "perf-user-167", + "diff": 19 + }, + { + "username": "perf-user-171", + "diff": 19 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 81 + }, + { + "username": "perf-user-106", + "diff": 83 + }, + { + "username": "perf-user-109", + "diff": 87 + }, + { + "username": "perf-user-110", + "diff": 83 + }, + { + "username": "perf-user-111", + "diff": 83 + }, + { + "username": "perf-user-119", + "diff": 83 + }, + { + "username": "perf-user-122", + "diff": 83 + }, + { + "username": "perf-user-123", + "diff": 87 + }, + { + "username": "perf-user-125", + "diff": 81 + }, + { + "username": "perf-user-132", + "diff": 83 + }, + { + "username": "perf-user-138", + "diff": 85 + }, + { + "username": "perf-user-140", + "diff": 83 + }, + { + "username": "perf-user-141", + "diff": 83 + }, + { + "username": "perf-user-145", + "diff": 83 + }, + { + "username": "perf-user-153", + "diff": 83 + }, + { + "username": "perf-user-154", + "diff": 83 + }, + { + "username": "perf-user-157", + "diff": 69 + }, + { + "username": "perf-user-159", + "diff": 69 + }, + { + "username": "perf-user-160", + "diff": 69 + }, + { + "username": "perf-user-163", + "diff": 69 + }, + { + "username": "perf-user-164", + "diff": 69 + }, + { + "username": "perf-user-165", + "diff": 69 + }, + { + "username": "perf-user-166", + "diff": 69 + }, + { + "username": "perf-user-167", + "diff": 69 + }, + { + "username": "perf-user-171", + "diff": 69 + } + ], + "memberDiffMeta": { + "count": 25, + "avg": 25.36 + }, + "deviceDiffMeta": { + "count": 25, + "avg": 78.2 + }, + "avgConnectedPeers": 6.56, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 5 + }, + { + "username": "perf-user-106", + "connectedPeers": 5 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 14 + }, + { + "username": "perf-user-111", + "connectedPeers": 5 + }, + { + "username": "perf-user-119", + "connectedPeers": 5 + }, + { + "username": "perf-user-122", + "connectedPeers": 5 + }, + { + "username": "perf-user-123", + "connectedPeers": 5 + }, + { + "username": "perf-user-125", + "connectedPeers": 5 + }, + { + "username": "perf-user-132", + "connectedPeers": 5 + }, + { + "username": "perf-user-138", + "connectedPeers": 5 + }, + { + "username": "perf-user-140", + "connectedPeers": 5 + }, + { + "username": "perf-user-141", + "connectedPeers": 5 + }, + { + "username": "perf-user-145", + "connectedPeers": 5 + }, + { + "username": "perf-user-153", + "connectedPeers": 5 + }, + { + "username": "perf-user-154", + "connectedPeers": 5 + }, + { + "username": "perf-user-157", + "connectedPeers": 11 + }, + { + "username": "perf-user-159", + "connectedPeers": 12 + }, + { + "username": "perf-user-160", + "connectedPeers": 11 + }, + { + "username": "perf-user-163", + "connectedPeers": 7 + }, + { + "username": "perf-user-164", + "connectedPeers": 9 + }, + { + "username": "perf-user-165", + "connectedPeers": 6 + }, + { + "username": "perf-user-166", + "connectedPeers": 7 + }, + { + "username": "perf-user-167", + "connectedPeers": 7 + }, + { + "username": "perf-user-171", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 114, + "avgChainLoadTimeMs": 9874.64, + "memberDiffs": [ + { + "username": "perf-user-99", + "diff": 26 + }, + { + "username": "perf-user-106", + "diff": 21 + }, + { + "username": "perf-user-109", + "diff": 22 + }, + { + "username": "perf-user-110", + "diff": 21 + }, + { + "username": "perf-user-111", + "diff": 21 + }, + { + "username": "perf-user-119", + "diff": 28 + }, + { + "username": "perf-user-122", + "diff": 41 + }, + { + "username": "perf-user-123", + "diff": 2 + }, + { + "username": "perf-user-125", + "diff": 26 + }, + { + "username": "perf-user-132", + "diff": 35 + }, + { + "username": "perf-user-138", + "diff": 34 + }, + { + "username": "perf-user-140", + "diff": 41 + }, + { + "username": "perf-user-141", + "diff": 41 + }, + { + "username": "perf-user-145", + "diff": 41 + }, + { + "username": "perf-user-153", + "diff": 35 + }, + { + "username": "perf-user-154", + "diff": 28 + }, + { + "username": "perf-user-157", + "diff": 19 + }, + { + "username": "perf-user-159", + "diff": 19 + }, + { + "username": "perf-user-160", + "diff": 19 + }, + { + "username": "perf-user-163", + "diff": 19 + }, + { + "username": "perf-user-164", + "diff": 19 + }, + { + "username": "perf-user-165", + "diff": 19 + }, + { + "username": "perf-user-166", + "diff": 19 + }, + { + "username": "perf-user-167", + "diff": 19 + }, + { + "username": "perf-user-171", + "diff": 19 + } + ], + "deviceDiffs": [ + { + "username": "perf-user-99", + "diff": 81 + }, + { + "username": "perf-user-106", + "diff": 83 + }, + { + "username": "perf-user-109", + "diff": 87 + }, + { + "username": "perf-user-110", + "diff": 83 + }, + { + "username": "perf-user-111", + "diff": 83 + }, + { + "username": "perf-user-119", + "diff": 83 + }, + { + "username": "perf-user-122", + "diff": 83 + }, + { + "username": "perf-user-123", + "diff": 87 + }, + { + "username": "perf-user-125", + "diff": 81 + }, + { + "username": "perf-user-132", + "diff": 83 + }, + { + "username": "perf-user-138", + "diff": 85 + }, + { + "username": "perf-user-140", + "diff": 83 + }, + { + "username": "perf-user-141", + "diff": 83 + }, + { + "username": "perf-user-145", + "diff": 83 + }, + { + "username": "perf-user-153", + "diff": 83 + }, + { + "username": "perf-user-154", + "diff": 83 + }, + { + "username": "perf-user-157", + "diff": 69 + }, + { + "username": "perf-user-159", + "diff": 69 + }, + { + "username": "perf-user-160", + "diff": 69 + }, + { + "username": "perf-user-163", + "diff": 69 + }, + { + "username": "perf-user-164", + "diff": 69 + }, + { + "username": "perf-user-165", + "diff": 69 + }, + { + "username": "perf-user-166", + "diff": 69 + }, + { + "username": "perf-user-167", + "diff": 69 + }, + { + "username": "perf-user-171", + "diff": 69 + } + ], + "memberDiffMeta": { + "count": 25, + "avg": 25.36 + }, + "deviceDiffMeta": { + "count": 25, + "avg": 78.2 + }, + "avgConnectedPeers": 6.64, + "connectedPeers": [ + { + "username": "perf-user-99", + "connectedPeers": 5 + }, + { + "username": "perf-user-106", + "connectedPeers": 5 + }, + { + "username": "perf-user-109", + "connectedPeers": 5 + }, + { + "username": "perf-user-110", + "connectedPeers": 14 + }, + { + "username": "perf-user-111", + "connectedPeers": 5 + }, + { + "username": "perf-user-119", + "connectedPeers": 5 + }, + { + "username": "perf-user-122", + "connectedPeers": 5 + }, + { + "username": "perf-user-123", + "connectedPeers": 5 + }, + { + "username": "perf-user-125", + "connectedPeers": 5 + }, + { + "username": "perf-user-132", + "connectedPeers": 5 + }, + { + "username": "perf-user-138", + "connectedPeers": 5 + }, + { + "username": "perf-user-140", + "connectedPeers": 5 + }, + { + "username": "perf-user-141", + "connectedPeers": 5 + }, + { + "username": "perf-user-145", + "connectedPeers": 5 + }, + { + "username": "perf-user-153", + "connectedPeers": 5 + }, + { + "username": "perf-user-154", + "connectedPeers": 5 + }, + { + "username": "perf-user-157", + "connectedPeers": 11 + }, + { + "username": "perf-user-159", + "connectedPeers": 13 + }, + { + "username": "perf-user-160", + "connectedPeers": 12 + }, + { + "username": "perf-user-163", + "connectedPeers": 7 + }, + { + "username": "perf-user-164", + "connectedPeers": 9 + }, + { + "username": "perf-user-165", + "connectedPeers": 6 + }, + { + "username": "perf-user-166", + "connectedPeers": 7 + }, + { + "username": "perf-user-167", + "connectedPeers": 7 + }, + { + "username": "perf-user-171", + "connectedPeers": 5 + } + ] + } +] \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/data.json.js b/demos/quiet-sandbox/src/scripts/isla-perf/data.json.js new file mode 100644 index 00000000..a2920e84 --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/data.json.js @@ -0,0 +1,5283 @@ +const data = [ + { + "userCount": 5, + "avgChainLoadTimeMs": 114.6, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 4, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 4 + }, + { + "username": "perf-user-0", + "connectedPeers": 4 + }, + { + "username": "perf-user-1", + "connectedPeers": 4 + }, + { + "username": "perf-user-2", + "connectedPeers": 4 + }, + { + "username": "perf-user-3", + "connectedPeers": 4 + } + ] + }, + { + "userCount": 10, + "avgChainLoadTimeMs": 114.8, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 7, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 7 + }, + { + "username": "perf-user-0", + "connectedPeers": 9 + }, + { + "username": "perf-user-1", + "connectedPeers": 7 + }, + { + "username": "perf-user-2", + "connectedPeers": 7 + }, + { + "username": "perf-user-3", + "connectedPeers": 7 + }, + { + "username": "perf-user-4", + "connectedPeers": 8 + }, + { + "username": "perf-user-5", + "connectedPeers": 7 + }, + { + "username": "perf-user-6", + "connectedPeers": 7 + }, + { + "username": "perf-user-7", + "connectedPeers": 6 + }, + { + "username": "perf-user-8", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 15, + "avgChainLoadTimeMs": 123.26666666666667, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 8, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 8 + }, + { + "username": "perf-user-0", + "connectedPeers": 11 + }, + { + "username": "perf-user-1", + "connectedPeers": 10 + }, + { + "username": "perf-user-2", + "connectedPeers": 9 + }, + { + "username": "perf-user-3", + "connectedPeers": 9 + }, + { + "username": "perf-user-4", + "connectedPeers": 9 + }, + { + "username": "perf-user-5", + "connectedPeers": 10 + }, + { + "username": "perf-user-6", + "connectedPeers": 9 + }, + { + "username": "perf-user-7", + "connectedPeers": 8 + }, + { + "username": "perf-user-8", + "connectedPeers": 6 + }, + { + "username": "perf-user-9", + "connectedPeers": 8 + }, + { + "username": "perf-user-10", + "connectedPeers": 6 + }, + { + "username": "perf-user-11", + "connectedPeers": 6 + }, + { + "username": "perf-user-12", + "connectedPeers": 6 + }, + { + "username": "perf-user-13", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 20, + "avgChainLoadTimeMs": 137.55, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 8.5, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 10 + }, + { + "username": "perf-user-0", + "connectedPeers": 12 + }, + { + "username": "perf-user-1", + "connectedPeers": 13 + }, + { + "username": "perf-user-2", + "connectedPeers": 10 + }, + { + "username": "perf-user-3", + "connectedPeers": 11 + }, + { + "username": "perf-user-4", + "connectedPeers": 9 + }, + { + "username": "perf-user-5", + "connectedPeers": 11 + }, + { + "username": "perf-user-6", + "connectedPeers": 10 + }, + { + "username": "perf-user-7", + "connectedPeers": 9 + }, + { + "username": "perf-user-8", + "connectedPeers": 8 + }, + { + "username": "perf-user-9", + "connectedPeers": 8 + }, + { + "username": "perf-user-10", + "connectedPeers": 9 + }, + { + "username": "perf-user-11", + "connectedPeers": 7 + }, + { + "username": "perf-user-12", + "connectedPeers": 8 + }, + { + "username": "perf-user-13", + "connectedPeers": 6 + }, + { + "username": "perf-user-14", + "connectedPeers": 7 + }, + { + "username": "perf-user-15", + "connectedPeers": 6 + }, + { + "username": "perf-user-16", + "connectedPeers": 6 + }, + { + "username": "perf-user-17", + "connectedPeers": 5 + }, + { + "username": "perf-user-18", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 25, + "avgChainLoadTimeMs": 158.04, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 8.8, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 11 + }, + { + "username": "perf-user-0", + "connectedPeers": 13 + }, + { + "username": "perf-user-1", + "connectedPeers": 16 + }, + { + "username": "perf-user-2", + "connectedPeers": 11 + }, + { + "username": "perf-user-3", + "connectedPeers": 13 + }, + { + "username": "perf-user-4", + "connectedPeers": 9 + }, + { + "username": "perf-user-5", + "connectedPeers": 11 + }, + { + "username": "perf-user-6", + "connectedPeers": 11 + }, + { + "username": "perf-user-7", + "connectedPeers": 10 + }, + { + "username": "perf-user-8", + "connectedPeers": 8 + }, + { + "username": "perf-user-9", + "connectedPeers": 9 + }, + { + "username": "perf-user-10", + "connectedPeers": 13 + }, + { + "username": "perf-user-11", + "connectedPeers": 7 + }, + { + "username": "perf-user-12", + "connectedPeers": 11 + }, + { + "username": "perf-user-13", + "connectedPeers": 7 + }, + { + "username": "perf-user-14", + "connectedPeers": 7 + }, + { + "username": "perf-user-15", + "connectedPeers": 6 + }, + { + "username": "perf-user-16", + "connectedPeers": 7 + }, + { + "username": "perf-user-17", + "connectedPeers": 6 + }, + { + "username": "perf-user-18", + "connectedPeers": 5 + }, + { + "username": "perf-user-19", + "connectedPeers": 6 + }, + { + "username": "perf-user-20", + "connectedPeers": 7 + }, + { + "username": "perf-user-21", + "connectedPeers": 6 + }, + { + "username": "perf-user-22", + "connectedPeers": 5 + }, + { + "username": "perf-user-23", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 30, + "avgChainLoadTimeMs": 178.13333333333333, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 9, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 11 + }, + { + "username": "perf-user-0", + "connectedPeers": 14 + }, + { + "username": "perf-user-1", + "connectedPeers": 17 + }, + { + "username": "perf-user-2", + "connectedPeers": 11 + }, + { + "username": "perf-user-3", + "connectedPeers": 15 + }, + { + "username": "perf-user-4", + "connectedPeers": 9 + }, + { + "username": "perf-user-5", + "connectedPeers": 12 + }, + { + "username": "perf-user-6", + "connectedPeers": 12 + }, + { + "username": "perf-user-7", + "connectedPeers": 10 + }, + { + "username": "perf-user-8", + "connectedPeers": 12 + }, + { + "username": "perf-user-9", + "connectedPeers": 9 + }, + { + "username": "perf-user-10", + "connectedPeers": 14 + }, + { + "username": "perf-user-11", + "connectedPeers": 7 + }, + { + "username": "perf-user-12", + "connectedPeers": 12 + }, + { + "username": "perf-user-13", + "connectedPeers": 7 + }, + { + "username": "perf-user-14", + "connectedPeers": 8 + }, + { + "username": "perf-user-15", + "connectedPeers": 8 + }, + { + "username": "perf-user-16", + "connectedPeers": 8 + }, + { + "username": "perf-user-17", + "connectedPeers": 7 + }, + { + "username": "perf-user-18", + "connectedPeers": 7 + }, + { + "username": "perf-user-19", + "connectedPeers": 7 + }, + { + "username": "perf-user-20", + "connectedPeers": 9 + }, + { + "username": "perf-user-21", + "connectedPeers": 6 + }, + { + "username": "perf-user-22", + "connectedPeers": 5 + }, + { + "username": "perf-user-23", + "connectedPeers": 7 + }, + { + "username": "perf-user-24", + "connectedPeers": 5 + }, + { + "username": "perf-user-25", + "connectedPeers": 5 + }, + { + "username": "perf-user-26", + "connectedPeers": 5 + }, + { + "username": "perf-user-27", + "connectedPeers": 6 + }, + { + "username": "perf-user-28", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 35, + "avgChainLoadTimeMs": 204.9142857142857, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 9.142857142857142, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 13 + }, + { + "username": "perf-user-0", + "connectedPeers": 15 + }, + { + "username": "perf-user-1", + "connectedPeers": 18 + }, + { + "username": "perf-user-2", + "connectedPeers": 11 + }, + { + "username": "perf-user-3", + "connectedPeers": 16 + }, + { + "username": "perf-user-4", + "connectedPeers": 9 + }, + { + "username": "perf-user-5", + "connectedPeers": 13 + }, + { + "username": "perf-user-6", + "connectedPeers": 14 + }, + { + "username": "perf-user-7", + "connectedPeers": 10 + }, + { + "username": "perf-user-8", + "connectedPeers": 12 + }, + { + "username": "perf-user-9", + "connectedPeers": 11 + }, + { + "username": "perf-user-10", + "connectedPeers": 14 + }, + { + "username": "perf-user-11", + "connectedPeers": 7 + }, + { + "username": "perf-user-12", + "connectedPeers": 14 + }, + { + "username": "perf-user-13", + "connectedPeers": 7 + }, + { + "username": "perf-user-14", + "connectedPeers": 9 + }, + { + "username": "perf-user-15", + "connectedPeers": 9 + }, + { + "username": "perf-user-16", + "connectedPeers": 8 + }, + { + "username": "perf-user-17", + "connectedPeers": 8 + }, + { + "username": "perf-user-18", + "connectedPeers": 8 + }, + { + "username": "perf-user-19", + "connectedPeers": 7 + }, + { + "username": "perf-user-20", + "connectedPeers": 10 + }, + { + "username": "perf-user-21", + "connectedPeers": 7 + }, + { + "username": "perf-user-22", + "connectedPeers": 6 + }, + { + "username": "perf-user-23", + "connectedPeers": 8 + }, + { + "username": "perf-user-24", + "connectedPeers": 5 + }, + { + "username": "perf-user-25", + "connectedPeers": 5 + }, + { + "username": "perf-user-26", + "connectedPeers": 5 + }, + { + "username": "perf-user-27", + "connectedPeers": 8 + }, + { + "username": "perf-user-28", + "connectedPeers": 6 + }, + { + "username": "perf-user-29", + "connectedPeers": 6 + }, + { + "username": "perf-user-30", + "connectedPeers": 5 + }, + { + "username": "perf-user-31", + "connectedPeers": 6 + }, + { + "username": "perf-user-32", + "connectedPeers": 5 + }, + { + "username": "perf-user-33", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 40, + "avgChainLoadTimeMs": 230.475, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 9.25, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 13 + }, + { + "username": "perf-user-0", + "connectedPeers": 15 + }, + { + "username": "perf-user-1", + "connectedPeers": 18 + }, + { + "username": "perf-user-2", + "connectedPeers": 11 + }, + { + "username": "perf-user-3", + "connectedPeers": 16 + }, + { + "username": "perf-user-4", + "connectedPeers": 11 + }, + { + "username": "perf-user-5", + "connectedPeers": 14 + }, + { + "username": "perf-user-6", + "connectedPeers": 16 + }, + { + "username": "perf-user-7", + "connectedPeers": 11 + }, + { + "username": "perf-user-8", + "connectedPeers": 13 + }, + { + "username": "perf-user-9", + "connectedPeers": 11 + }, + { + "username": "perf-user-10", + "connectedPeers": 14 + }, + { + "username": "perf-user-11", + "connectedPeers": 9 + }, + { + "username": "perf-user-12", + "connectedPeers": 15 + }, + { + "username": "perf-user-13", + "connectedPeers": 7 + }, + { + "username": "perf-user-14", + "connectedPeers": 10 + }, + { + "username": "perf-user-15", + "connectedPeers": 9 + }, + { + "username": "perf-user-16", + "connectedPeers": 8 + }, + { + "username": "perf-user-17", + "connectedPeers": 8 + }, + { + "username": "perf-user-18", + "connectedPeers": 8 + }, + { + "username": "perf-user-19", + "connectedPeers": 8 + }, + { + "username": "perf-user-20", + "connectedPeers": 10 + }, + { + "username": "perf-user-21", + "connectedPeers": 8 + }, + { + "username": "perf-user-22", + "connectedPeers": 6 + }, + { + "username": "perf-user-23", + "connectedPeers": 8 + }, + { + "username": "perf-user-24", + "connectedPeers": 6 + }, + { + "username": "perf-user-25", + "connectedPeers": 5 + }, + { + "username": "perf-user-26", + "connectedPeers": 5 + }, + { + "username": "perf-user-27", + "connectedPeers": 8 + }, + { + "username": "perf-user-28", + "connectedPeers": 7 + }, + { + "username": "perf-user-29", + "connectedPeers": 6 + }, + { + "username": "perf-user-30", + "connectedPeers": 7 + }, + { + "username": "perf-user-31", + "connectedPeers": 7 + }, + { + "username": "perf-user-32", + "connectedPeers": 6 + }, + { + "username": "perf-user-33", + "connectedPeers": 6 + }, + { + "username": "perf-user-34", + "connectedPeers": 7 + }, + { + "username": "perf-user-35", + "connectedPeers": 6 + }, + { + "username": "perf-user-36", + "connectedPeers": 6 + }, + { + "username": "perf-user-37", + "connectedPeers": 6 + }, + { + "username": "perf-user-38", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 45, + "avgChainLoadTimeMs": 286.35555555555555, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 9.333333333333334, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 15 + }, + { + "username": "perf-user-0", + "connectedPeers": 15 + }, + { + "username": "perf-user-1", + "connectedPeers": 18 + }, + { + "username": "perf-user-2", + "connectedPeers": 12 + }, + { + "username": "perf-user-3", + "connectedPeers": 17 + }, + { + "username": "perf-user-4", + "connectedPeers": 12 + }, + { + "username": "perf-user-5", + "connectedPeers": 15 + }, + { + "username": "perf-user-6", + "connectedPeers": 18 + }, + { + "username": "perf-user-7", + "connectedPeers": 11 + }, + { + "username": "perf-user-8", + "connectedPeers": 13 + }, + { + "username": "perf-user-9", + "connectedPeers": 11 + }, + { + "username": "perf-user-10", + "connectedPeers": 14 + }, + { + "username": "perf-user-11", + "connectedPeers": 10 + }, + { + "username": "perf-user-12", + "connectedPeers": 15 + }, + { + "username": "perf-user-13", + "connectedPeers": 7 + }, + { + "username": "perf-user-14", + "connectedPeers": 11 + }, + { + "username": "perf-user-15", + "connectedPeers": 9 + }, + { + "username": "perf-user-16", + "connectedPeers": 9 + }, + { + "username": "perf-user-17", + "connectedPeers": 8 + }, + { + "username": "perf-user-18", + "connectedPeers": 8 + }, + { + "username": "perf-user-19", + "connectedPeers": 8 + }, + { + "username": "perf-user-20", + "connectedPeers": 11 + }, + { + "username": "perf-user-21", + "connectedPeers": 9 + }, + { + "username": "perf-user-22", + "connectedPeers": 6 + }, + { + "username": "perf-user-23", + "connectedPeers": 8 + }, + { + "username": "perf-user-24", + "connectedPeers": 7 + }, + { + "username": "perf-user-25", + "connectedPeers": 5 + }, + { + "username": "perf-user-26", + "connectedPeers": 5 + }, + { + "username": "perf-user-27", + "connectedPeers": 8 + }, + { + "username": "perf-user-28", + "connectedPeers": 8 + }, + { + "username": "perf-user-29", + "connectedPeers": 7 + }, + { + "username": "perf-user-30", + "connectedPeers": 8 + }, + { + "username": "perf-user-31", + "connectedPeers": 8 + }, + { + "username": "perf-user-32", + "connectedPeers": 6 + }, + { + "username": "perf-user-33", + "connectedPeers": 7 + }, + { + "username": "perf-user-34", + "connectedPeers": 7 + }, + { + "username": "perf-user-35", + "connectedPeers": 6 + }, + { + "username": "perf-user-36", + "connectedPeers": 7 + }, + { + "username": "perf-user-37", + "connectedPeers": 7 + }, + { + "username": "perf-user-38", + "connectedPeers": 6 + }, + { + "username": "perf-user-39", + "connectedPeers": 6 + }, + { + "username": "perf-user-40", + "connectedPeers": 6 + }, + { + "username": "perf-user-41", + "connectedPeers": 5 + }, + { + "username": "perf-user-42", + "connectedPeers": 6 + }, + { + "username": "perf-user-43", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 50, + "avgChainLoadTimeMs": 457.98, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 9.4, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 15 + }, + { + "username": "perf-user-0", + "connectedPeers": 15 + }, + { + "username": "perf-user-1", + "connectedPeers": 18 + }, + { + "username": "perf-user-2", + "connectedPeers": 12 + }, + { + "username": "perf-user-3", + "connectedPeers": 17 + }, + { + "username": "perf-user-4", + "connectedPeers": 12 + }, + { + "username": "perf-user-5", + "connectedPeers": 15 + }, + { + "username": "perf-user-6", + "connectedPeers": 19 + }, + { + "username": "perf-user-7", + "connectedPeers": 11 + }, + { + "username": "perf-user-8", + "connectedPeers": 14 + }, + { + "username": "perf-user-9", + "connectedPeers": 11 + }, + { + "username": "perf-user-10", + "connectedPeers": 16 + }, + { + "username": "perf-user-11", + "connectedPeers": 12 + }, + { + "username": "perf-user-12", + "connectedPeers": 16 + }, + { + "username": "perf-user-13", + "connectedPeers": 8 + }, + { + "username": "perf-user-14", + "connectedPeers": 11 + }, + { + "username": "perf-user-15", + "connectedPeers": 9 + }, + { + "username": "perf-user-16", + "connectedPeers": 9 + }, + { + "username": "perf-user-17", + "connectedPeers": 9 + }, + { + "username": "perf-user-18", + "connectedPeers": 9 + }, + { + "username": "perf-user-19", + "connectedPeers": 9 + }, + { + "username": "perf-user-20", + "connectedPeers": 11 + }, + { + "username": "perf-user-21", + "connectedPeers": 9 + }, + { + "username": "perf-user-22", + "connectedPeers": 6 + }, + { + "username": "perf-user-23", + "connectedPeers": 8 + }, + { + "username": "perf-user-24", + "connectedPeers": 7 + }, + { + "username": "perf-user-25", + "connectedPeers": 6 + }, + { + "username": "perf-user-26", + "connectedPeers": 5 + }, + { + "username": "perf-user-27", + "connectedPeers": 8 + }, + { + "username": "perf-user-28", + "connectedPeers": 9 + }, + { + "username": "perf-user-29", + "connectedPeers": 7 + }, + { + "username": "perf-user-30", + "connectedPeers": 8 + }, + { + "username": "perf-user-31", + "connectedPeers": 9 + }, + { + "username": "perf-user-32", + "connectedPeers": 6 + }, + { + "username": "perf-user-33", + "connectedPeers": 8 + }, + { + "username": "perf-user-34", + "connectedPeers": 8 + }, + { + "username": "perf-user-35", + "connectedPeers": 6 + }, + { + "username": "perf-user-36", + "connectedPeers": 9 + }, + { + "username": "perf-user-37", + "connectedPeers": 7 + }, + { + "username": "perf-user-38", + "connectedPeers": 7 + }, + { + "username": "perf-user-39", + "connectedPeers": 7 + }, + { + "username": "perf-user-40", + "connectedPeers": 7 + }, + { + "username": "perf-user-41", + "connectedPeers": 6 + }, + { + "username": "perf-user-42", + "connectedPeers": 7 + }, + { + "username": "perf-user-43", + "connectedPeers": 5 + }, + { + "username": "perf-user-44", + "connectedPeers": 5 + }, + { + "username": "perf-user-45", + "connectedPeers": 5 + }, + { + "username": "perf-user-46", + "connectedPeers": 6 + }, + { + "username": "perf-user-47", + "connectedPeers": 6 + }, + { + "username": "perf-user-48", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 55, + "avgChainLoadTimeMs": 733.9454545454546, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 9.454545454545455, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 15 + }, + { + "username": "perf-user-0", + "connectedPeers": 15 + }, + { + "username": "perf-user-1", + "connectedPeers": 18 + }, + { + "username": "perf-user-2", + "connectedPeers": 12 + }, + { + "username": "perf-user-3", + "connectedPeers": 17 + }, + { + "username": "perf-user-4", + "connectedPeers": 12 + }, + { + "username": "perf-user-5", + "connectedPeers": 17 + }, + { + "username": "perf-user-6", + "connectedPeers": 19 + }, + { + "username": "perf-user-7", + "connectedPeers": 11 + }, + { + "username": "perf-user-8", + "connectedPeers": 14 + }, + { + "username": "perf-user-9", + "connectedPeers": 12 + }, + { + "username": "perf-user-10", + "connectedPeers": 16 + }, + { + "username": "perf-user-11", + "connectedPeers": 14 + }, + { + "username": "perf-user-12", + "connectedPeers": 16 + }, + { + "username": "perf-user-13", + "connectedPeers": 8 + }, + { + "username": "perf-user-14", + "connectedPeers": 11 + }, + { + "username": "perf-user-15", + "connectedPeers": 10 + }, + { + "username": "perf-user-16", + "connectedPeers": 9 + }, + { + "username": "perf-user-17", + "connectedPeers": 9 + }, + { + "username": "perf-user-18", + "connectedPeers": 9 + }, + { + "username": "perf-user-19", + "connectedPeers": 9 + }, + { + "username": "perf-user-20", + "connectedPeers": 14 + }, + { + "username": "perf-user-21", + "connectedPeers": 9 + }, + { + "username": "perf-user-22", + "connectedPeers": 7 + }, + { + "username": "perf-user-23", + "connectedPeers": 8 + }, + { + "username": "perf-user-24", + "connectedPeers": 7 + }, + { + "username": "perf-user-25", + "connectedPeers": 7 + }, + { + "username": "perf-user-26", + "connectedPeers": 6 + }, + { + "username": "perf-user-27", + "connectedPeers": 8 + }, + { + "username": "perf-user-28", + "connectedPeers": 10 + }, + { + "username": "perf-user-29", + "connectedPeers": 8 + }, + { + "username": "perf-user-30", + "connectedPeers": 9 + }, + { + "username": "perf-user-31", + "connectedPeers": 9 + }, + { + "username": "perf-user-32", + "connectedPeers": 6 + }, + { + "username": "perf-user-33", + "connectedPeers": 8 + }, + { + "username": "perf-user-34", + "connectedPeers": 10 + }, + { + "username": "perf-user-35", + "connectedPeers": 6 + }, + { + "username": "perf-user-36", + "connectedPeers": 10 + }, + { + "username": "perf-user-37", + "connectedPeers": 7 + }, + { + "username": "perf-user-38", + "connectedPeers": 8 + }, + { + "username": "perf-user-39", + "connectedPeers": 8 + }, + { + "username": "perf-user-40", + "connectedPeers": 7 + }, + { + "username": "perf-user-41", + "connectedPeers": 6 + }, + { + "username": "perf-user-42", + "connectedPeers": 7 + }, + { + "username": "perf-user-43", + "connectedPeers": 5 + }, + { + "username": "perf-user-44", + "connectedPeers": 5 + }, + { + "username": "perf-user-45", + "connectedPeers": 5 + }, + { + "username": "perf-user-46", + "connectedPeers": 6 + }, + { + "username": "perf-user-47", + "connectedPeers": 6 + }, + { + "username": "perf-user-48", + "connectedPeers": 7 + }, + { + "username": "perf-user-49", + "connectedPeers": 7 + }, + { + "username": "perf-user-50", + "connectedPeers": 6 + }, + { + "username": "perf-user-51", + "connectedPeers": 5 + }, + { + "username": "perf-user-52", + "connectedPeers": 5 + }, + { + "username": "perf-user-53", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 60, + "avgChainLoadTimeMs": 1203.45, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 9.5, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 15 + }, + { + "username": "perf-user-0", + "connectedPeers": 16 + }, + { + "username": "perf-user-1", + "connectedPeers": 18 + }, + { + "username": "perf-user-2", + "connectedPeers": 13 + }, + { + "username": "perf-user-3", + "connectedPeers": 17 + }, + { + "username": "perf-user-4", + "connectedPeers": 12 + }, + { + "username": "perf-user-5", + "connectedPeers": 17 + }, + { + "username": "perf-user-6", + "connectedPeers": 20 + }, + { + "username": "perf-user-7", + "connectedPeers": 11 + }, + { + "username": "perf-user-8", + "connectedPeers": 14 + }, + { + "username": "perf-user-9", + "connectedPeers": 12 + }, + { + "username": "perf-user-10", + "connectedPeers": 16 + }, + { + "username": "perf-user-11", + "connectedPeers": 15 + }, + { + "username": "perf-user-12", + "connectedPeers": 17 + }, + { + "username": "perf-user-13", + "connectedPeers": 8 + }, + { + "username": "perf-user-14", + "connectedPeers": 11 + }, + { + "username": "perf-user-15", + "connectedPeers": 11 + }, + { + "username": "perf-user-16", + "connectedPeers": 9 + }, + { + "username": "perf-user-17", + "connectedPeers": 9 + }, + { + "username": "perf-user-18", + "connectedPeers": 9 + }, + { + "username": "perf-user-19", + "connectedPeers": 10 + }, + { + "username": "perf-user-20", + "connectedPeers": 15 + }, + { + "username": "perf-user-21", + "connectedPeers": 9 + }, + { + "username": "perf-user-22", + "connectedPeers": 8 + }, + { + "username": "perf-user-23", + "connectedPeers": 9 + }, + { + "username": "perf-user-24", + "connectedPeers": 7 + }, + { + "username": "perf-user-25", + "connectedPeers": 8 + }, + { + "username": "perf-user-26", + "connectedPeers": 7 + }, + { + "username": "perf-user-27", + "connectedPeers": 9 + }, + { + "username": "perf-user-28", + "connectedPeers": 11 + }, + { + "username": "perf-user-29", + "connectedPeers": 9 + }, + { + "username": "perf-user-30", + "connectedPeers": 11 + }, + { + "username": "perf-user-31", + "connectedPeers": 9 + }, + { + "username": "perf-user-32", + "connectedPeers": 6 + }, + { + "username": "perf-user-33", + "connectedPeers": 8 + }, + { + "username": "perf-user-34", + "connectedPeers": 10 + }, + { + "username": "perf-user-35", + "connectedPeers": 6 + }, + { + "username": "perf-user-36", + "connectedPeers": 11 + }, + { + "username": "perf-user-37", + "connectedPeers": 7 + }, + { + "username": "perf-user-38", + "connectedPeers": 8 + }, + { + "username": "perf-user-39", + "connectedPeers": 8 + }, + { + "username": "perf-user-40", + "connectedPeers": 8 + }, + { + "username": "perf-user-41", + "connectedPeers": 6 + }, + { + "username": "perf-user-42", + "connectedPeers": 7 + }, + { + "username": "perf-user-43", + "connectedPeers": 5 + }, + { + "username": "perf-user-44", + "connectedPeers": 5 + }, + { + "username": "perf-user-45", + "connectedPeers": 5 + }, + { + "username": "perf-user-46", + "connectedPeers": 6 + }, + { + "username": "perf-user-47", + "connectedPeers": 6 + }, + { + "username": "perf-user-48", + "connectedPeers": 7 + }, + { + "username": "perf-user-49", + "connectedPeers": 8 + }, + { + "username": "perf-user-50", + "connectedPeers": 7 + }, + { + "username": "perf-user-51", + "connectedPeers": 6 + }, + { + "username": "perf-user-52", + "connectedPeers": 6 + }, + { + "username": "perf-user-53", + "connectedPeers": 6 + }, + { + "username": "perf-user-54", + "connectedPeers": 5 + }, + { + "username": "perf-user-55", + "connectedPeers": 6 + }, + { + "username": "perf-user-56", + "connectedPeers": 5 + }, + { + "username": "perf-user-57", + "connectedPeers": 5 + }, + { + "username": "perf-user-58", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 65, + "avgChainLoadTimeMs": 1848.0153846153846, + "memberDiffs": [], + "deviceDiffs": [], + "memberDiffMeta": { + "count": 0, + "avg": 0 + }, + "deviceDiffMeta": { + "count": 0, + "avg": 0 + }, + "avgConnectedPeers": 9.492307692307692, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 15 + }, + { + "username": "perf-user-0", + "connectedPeers": 16 + }, + { + "username": "perf-user-1", + "connectedPeers": 18 + }, + { + "username": "perf-user-2", + "connectedPeers": 13 + }, + { + "username": "perf-user-3", + "connectedPeers": 17 + }, + { + "username": "perf-user-4", + "connectedPeers": 12 + }, + { + "username": "perf-user-5", + "connectedPeers": 17 + }, + { + "username": "perf-user-6", + "connectedPeers": 20 + }, + { + "username": "perf-user-7", + "connectedPeers": 11 + }, + { + "username": "perf-user-8", + "connectedPeers": 14 + }, + { + "username": "perf-user-9", + "connectedPeers": 12 + }, + { + "username": "perf-user-10", + "connectedPeers": 17 + }, + { + "username": "perf-user-11", + "connectedPeers": 15 + }, + { + "username": "perf-user-12", + "connectedPeers": 17 + }, + { + "username": "perf-user-13", + "connectedPeers": 8 + }, + { + "username": "perf-user-14", + "connectedPeers": 11 + }, + { + "username": "perf-user-15", + "connectedPeers": 12 + }, + { + "username": "perf-user-16", + "connectedPeers": 9 + }, + { + "username": "perf-user-17", + "connectedPeers": 9 + }, + { + "username": "perf-user-18", + "connectedPeers": 9 + }, + { + "username": "perf-user-19", + "connectedPeers": 10 + }, + { + "username": "perf-user-20", + "connectedPeers": 15 + }, + { + "username": "perf-user-21", + "connectedPeers": 9 + }, + { + "username": "perf-user-22", + "connectedPeers": 8 + }, + { + "username": "perf-user-23", + "connectedPeers": 9 + }, + { + "username": "perf-user-24", + "connectedPeers": 8 + }, + { + "username": "perf-user-25", + "connectedPeers": 8 + }, + { + "username": "perf-user-26", + "connectedPeers": 7 + }, + { + "username": "perf-user-27", + "connectedPeers": 9 + }, + { + "username": "perf-user-28", + "connectedPeers": 11 + }, + { + "username": "perf-user-29", + "connectedPeers": 9 + }, + { + "username": "perf-user-30", + "connectedPeers": 13 + }, + { + "username": "perf-user-31", + "connectedPeers": 9 + }, + { + "username": "perf-user-32", + "connectedPeers": 7 + }, + { + "username": "perf-user-33", + "connectedPeers": 8 + }, + { + "username": "perf-user-34", + "connectedPeers": 10 + }, + { + "username": "perf-user-35", + "connectedPeers": 7 + }, + { + "username": "perf-user-36", + "connectedPeers": 11 + }, + { + "username": "perf-user-37", + "connectedPeers": 7 + }, + { + "username": "perf-user-38", + "connectedPeers": 9 + }, + { + "username": "perf-user-39", + "connectedPeers": 8 + }, + { + "username": "perf-user-40", + "connectedPeers": 8 + }, + { + "username": "perf-user-41", + "connectedPeers": 7 + }, + { + "username": "perf-user-42", + "connectedPeers": 7 + }, + { + "username": "perf-user-43", + "connectedPeers": 5 + }, + { + "username": "perf-user-44", + "connectedPeers": 5 + }, + { + "username": "perf-user-45", + "connectedPeers": 6 + }, + { + "username": "perf-user-46", + "connectedPeers": 6 + }, + { + "username": "perf-user-47", + "connectedPeers": 8 + }, + { + "username": "perf-user-48", + "connectedPeers": 9 + }, + { + "username": "perf-user-49", + "connectedPeers": 9 + }, + { + "username": "perf-user-50", + "connectedPeers": 7 + }, + { + "username": "perf-user-51", + "connectedPeers": 6 + }, + { + "username": "perf-user-52", + "connectedPeers": 7 + }, + { + "username": "perf-user-53", + "connectedPeers": 6 + }, + { + "username": "perf-user-54", + "connectedPeers": 5 + }, + { + "username": "perf-user-55", + "connectedPeers": 6 + }, + { + "username": "perf-user-56", + "connectedPeers": 8 + }, + { + "username": "perf-user-57", + "connectedPeers": 7 + }, + { + "username": "perf-user-58", + "connectedPeers": 6 + }, + { + "username": "perf-user-59", + "connectedPeers": 6 + }, + { + "username": "perf-user-60", + "connectedPeers": 5 + }, + { + "username": "perf-user-61", + "connectedPeers": 6 + }, + { + "username": "perf-user-62", + "connectedPeers": 5 + }, + { + "username": "perf-user-63", + "connectedPeers": 3 + } + ] + }, + { + "userCount": 70, + "avgChainLoadTimeMs": 3063.942857142857, + "memberDiffs": [ + { + "username": "perf-user-66", + "diff": 2 + }, + { + "username": "perf-user-67", + "diff": 1 + } + ], + "deviceDiffs": [ + { + "username": "founding-perf-user", + "diff": 4 + }, + { + "username": "perf-user-0", + "diff": 4 + }, + { + "username": "perf-user-1", + "diff": 4 + }, + { + "username": "perf-user-2", + "diff": 4 + }, + { + "username": "perf-user-3", + "diff": 4 + }, + { + "username": "perf-user-4", + "diff": 4 + }, + { + "username": "perf-user-5", + "diff": 4 + }, + { + "username": "perf-user-6", + "diff": 4 + }, + { + "username": "perf-user-7", + "diff": 4 + }, + { + "username": "perf-user-8", + "diff": 4 + }, + { + "username": "perf-user-9", + "diff": 4 + }, + { + "username": "perf-user-10", + "diff": 4 + }, + { + "username": "perf-user-11", + "diff": 4 + }, + { + "username": "perf-user-12", + "diff": 4 + }, + { + "username": "perf-user-13", + "diff": 4 + }, + { + "username": "perf-user-14", + "diff": 4 + }, + { + "username": "perf-user-15", + "diff": 4 + }, + { + "username": "perf-user-16", + "diff": 4 + }, + { + "username": "perf-user-17", + "diff": 4 + }, + { + "username": "perf-user-18", + "diff": 4 + }, + { + "username": "perf-user-19", + "diff": 4 + }, + { + "username": "perf-user-20", + "diff": 4 + }, + { + "username": "perf-user-21", + "diff": 4 + }, + { + "username": "perf-user-22", + "diff": 4 + }, + { + "username": "perf-user-23", + "diff": 4 + }, + { + "username": "perf-user-24", + "diff": 4 + }, + { + "username": "perf-user-25", + "diff": 4 + }, + { + "username": "perf-user-26", + "diff": 4 + }, + { + "username": "perf-user-27", + "diff": 4 + }, + { + "username": "perf-user-28", + "diff": 4 + }, + { + "username": "perf-user-29", + "diff": 4 + }, + { + "username": "perf-user-30", + "diff": 4 + }, + { + "username": "perf-user-31", + "diff": 4 + }, + { + "username": "perf-user-32", + "diff": 4 + }, + { + "username": "perf-user-33", + "diff": 4 + }, + { + "username": "perf-user-34", + "diff": 4 + }, + { + "username": "perf-user-35", + "diff": 4 + }, + { + "username": "perf-user-36", + "diff": 4 + }, + { + "username": "perf-user-37", + "diff": 4 + }, + { + "username": "perf-user-38", + "diff": 4 + }, + { + "username": "perf-user-39", + "diff": 4 + }, + { + "username": "perf-user-40", + "diff": 4 + }, + { + "username": "perf-user-41", + "diff": 4 + }, + { + "username": "perf-user-42", + "diff": 4 + }, + { + "username": "perf-user-43", + "diff": 4 + }, + { + "username": "perf-user-44", + "diff": 4 + }, + { + "username": "perf-user-45", + "diff": 4 + }, + { + "username": "perf-user-46", + "diff": 4 + }, + { + "username": "perf-user-47", + "diff": 4 + }, + { + "username": "perf-user-48", + "diff": 4 + }, + { + "username": "perf-user-49", + "diff": 4 + }, + { + "username": "perf-user-50", + "diff": 4 + }, + { + "username": "perf-user-51", + "diff": 4 + }, + { + "username": "perf-user-52", + "diff": 4 + }, + { + "username": "perf-user-53", + "diff": 4 + }, + { + "username": "perf-user-54", + "diff": 4 + }, + { + "username": "perf-user-55", + "diff": 4 + }, + { + "username": "perf-user-56", + "diff": 4 + }, + { + "username": "perf-user-57", + "diff": 4 + }, + { + "username": "perf-user-58", + "diff": 4 + }, + { + "username": "perf-user-59", + "diff": 4 + }, + { + "username": "perf-user-60", + "diff": 4 + }, + { + "username": "perf-user-61", + "diff": 4 + }, + { + "username": "perf-user-62", + "diff": 4 + }, + { + "username": "perf-user-63", + "diff": 4 + }, + { + "username": "perf-user-64", + "diff": 4 + }, + { + "username": "perf-user-65", + "diff": 4 + }, + { + "username": "perf-user-66", + "diff": 4 + }, + { + "username": "perf-user-67", + "diff": 4 + }, + { + "username": "perf-user-68", + "diff": 4 + } + ], + "memberDiffMeta": { + "count": 2, + "avg": 1.5 + }, + "deviceDiffMeta": { + "count": 70, + "avg": 4 + }, + "avgConnectedPeers": 9.514285714285714, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 15 + }, + { + "username": "perf-user-0", + "connectedPeers": 17 + }, + { + "username": "perf-user-1", + "connectedPeers": 18 + }, + { + "username": "perf-user-2", + "connectedPeers": 13 + }, + { + "username": "perf-user-3", + "connectedPeers": 17 + }, + { + "username": "perf-user-4", + "connectedPeers": 13 + }, + { + "username": "perf-user-5", + "connectedPeers": 19 + }, + { + "username": "perf-user-6", + "connectedPeers": 20 + }, + { + "username": "perf-user-7", + "connectedPeers": 11 + }, + { + "username": "perf-user-8", + "connectedPeers": 15 + }, + { + "username": "perf-user-9", + "connectedPeers": 13 + }, + { + "username": "perf-user-10", + "connectedPeers": 18 + }, + { + "username": "perf-user-11", + "connectedPeers": 16 + }, + { + "username": "perf-user-12", + "connectedPeers": 17 + }, + { + "username": "perf-user-13", + "connectedPeers": 8 + }, + { + "username": "perf-user-14", + "connectedPeers": 11 + }, + { + "username": "perf-user-15", + "connectedPeers": 12 + }, + { + "username": "perf-user-16", + "connectedPeers": 9 + }, + { + "username": "perf-user-17", + "connectedPeers": 9 + }, + { + "username": "perf-user-18", + "connectedPeers": 9 + }, + { + "username": "perf-user-19", + "connectedPeers": 10 + }, + { + "username": "perf-user-20", + "connectedPeers": 15 + }, + { + "username": "perf-user-21", + "connectedPeers": 9 + }, + { + "username": "perf-user-22", + "connectedPeers": 8 + }, + { + "username": "perf-user-23", + "connectedPeers": 9 + }, + { + "username": "perf-user-24", + "connectedPeers": 8 + }, + { + "username": "perf-user-25", + "connectedPeers": 9 + }, + { + "username": "perf-user-26", + "connectedPeers": 7 + }, + { + "username": "perf-user-27", + "connectedPeers": 10 + }, + { + "username": "perf-user-28", + "connectedPeers": 12 + }, + { + "username": "perf-user-29", + "connectedPeers": 9 + }, + { + "username": "perf-user-30", + "connectedPeers": 13 + }, + { + "username": "perf-user-31", + "connectedPeers": 9 + }, + { + "username": "perf-user-32", + "connectedPeers": 7 + }, + { + "username": "perf-user-33", + "connectedPeers": 8 + }, + { + "username": "perf-user-34", + "connectedPeers": 10 + }, + { + "username": "perf-user-35", + "connectedPeers": 7 + }, + { + "username": "perf-user-36", + "connectedPeers": 11 + }, + { + "username": "perf-user-37", + "connectedPeers": 7 + }, + { + "username": "perf-user-38", + "connectedPeers": 9 + }, + { + "username": "perf-user-39", + "connectedPeers": 8 + }, + { + "username": "perf-user-40", + "connectedPeers": 8 + }, + { + "username": "perf-user-41", + "connectedPeers": 7 + }, + { + "username": "perf-user-42", + "connectedPeers": 8 + }, + { + "username": "perf-user-43", + "connectedPeers": 7 + }, + { + "username": "perf-user-44", + "connectedPeers": 5 + }, + { + "username": "perf-user-45", + "connectedPeers": 7 + }, + { + "username": "perf-user-46", + "connectedPeers": 6 + }, + { + "username": "perf-user-47", + "connectedPeers": 8 + }, + { + "username": "perf-user-48", + "connectedPeers": 9 + }, + { + "username": "perf-user-49", + "connectedPeers": 11 + }, + { + "username": "perf-user-50", + "connectedPeers": 8 + }, + { + "username": "perf-user-51", + "connectedPeers": 7 + }, + { + "username": "perf-user-52", + "connectedPeers": 7 + }, + { + "username": "perf-user-53", + "connectedPeers": 6 + }, + { + "username": "perf-user-54", + "connectedPeers": 5 + }, + { + "username": "perf-user-55", + "connectedPeers": 6 + }, + { + "username": "perf-user-56", + "connectedPeers": 8 + }, + { + "username": "perf-user-57", + "connectedPeers": 7 + }, + { + "username": "perf-user-58", + "connectedPeers": 6 + }, + { + "username": "perf-user-59", + "connectedPeers": 6 + }, + { + "username": "perf-user-60", + "connectedPeers": 5 + }, + { + "username": "perf-user-61", + "connectedPeers": 7 + }, + { + "username": "perf-user-62", + "connectedPeers": 6 + }, + { + "username": "perf-user-63", + "connectedPeers": 7 + }, + { + "username": "perf-user-64", + "connectedPeers": 5 + }, + { + "username": "perf-user-65", + "connectedPeers": 5 + }, + { + "username": "perf-user-66", + "connectedPeers": 5 + }, + { + "username": "perf-user-67", + "connectedPeers": 6 + }, + { + "username": "perf-user-68", + "connectedPeers": 3 + } + ] + }, + { + "userCount": 75, + "avgChainLoadTimeMs": 3757.6, + "memberDiffs": [ + { + "username": "perf-user-66", + "diff": 7 + }, + { + "username": "perf-user-67", + "diff": 6 + }, + { + "username": "perf-user-69", + "diff": 4 + }, + { + "username": "perf-user-72", + "diff": 1 + } + ], + "deviceDiffs": [ + { + "username": "founding-perf-user", + "diff": 12 + }, + { + "username": "perf-user-0", + "diff": 12 + }, + { + "username": "perf-user-1", + "diff": 12 + }, + { + "username": "perf-user-2", + "diff": 12 + }, + { + "username": "perf-user-3", + "diff": 12 + }, + { + "username": "perf-user-4", + "diff": 12 + }, + { + "username": "perf-user-5", + "diff": 12 + }, + { + "username": "perf-user-6", + "diff": 12 + }, + { + "username": "perf-user-7", + "diff": 12 + }, + { + "username": "perf-user-8", + "diff": 12 + }, + { + "username": "perf-user-9", + "diff": 12 + }, + { + "username": "perf-user-10", + "diff": 12 + }, + { + "username": "perf-user-11", + "diff": 12 + }, + { + "username": "perf-user-12", + "diff": 12 + }, + { + "username": "perf-user-13", + "diff": 12 + }, + { + "username": "perf-user-14", + "diff": 12 + }, + { + "username": "perf-user-15", + "diff": 12 + }, + { + "username": "perf-user-16", + "diff": 12 + }, + { + "username": "perf-user-17", + "diff": 12 + }, + { + "username": "perf-user-18", + "diff": 12 + }, + { + "username": "perf-user-19", + "diff": 12 + }, + { + "username": "perf-user-20", + "diff": 12 + }, + { + "username": "perf-user-21", + "diff": 12 + }, + { + "username": "perf-user-22", + "diff": 12 + }, + { + "username": "perf-user-23", + "diff": 12 + }, + { + "username": "perf-user-24", + "diff": 12 + }, + { + "username": "perf-user-25", + "diff": 12 + }, + { + "username": "perf-user-26", + "diff": 12 + }, + { + "username": "perf-user-27", + "diff": 12 + }, + { + "username": "perf-user-28", + "diff": 12 + }, + { + "username": "perf-user-29", + "diff": 12 + }, + { + "username": "perf-user-30", + "diff": 12 + }, + { + "username": "perf-user-31", + "diff": 12 + }, + { + "username": "perf-user-32", + "diff": 12 + }, + { + "username": "perf-user-33", + "diff": 12 + }, + { + "username": "perf-user-34", + "diff": 12 + }, + { + "username": "perf-user-35", + "diff": 12 + }, + { + "username": "perf-user-36", + "diff": 12 + }, + { + "username": "perf-user-37", + "diff": 12 + }, + { + "username": "perf-user-38", + "diff": 12 + }, + { + "username": "perf-user-39", + "diff": 12 + }, + { + "username": "perf-user-40", + "diff": 12 + }, + { + "username": "perf-user-41", + "diff": 12 + }, + { + "username": "perf-user-42", + "diff": 12 + }, + { + "username": "perf-user-43", + "diff": 12 + }, + { + "username": "perf-user-44", + "diff": 12 + }, + { + "username": "perf-user-45", + "diff": 12 + }, + { + "username": "perf-user-46", + "diff": 12 + }, + { + "username": "perf-user-47", + "diff": 12 + }, + { + "username": "perf-user-48", + "diff": 12 + }, + { + "username": "perf-user-49", + "diff": 12 + }, + { + "username": "perf-user-50", + "diff": 12 + }, + { + "username": "perf-user-51", + "diff": 12 + }, + { + "username": "perf-user-52", + "diff": 12 + }, + { + "username": "perf-user-53", + "diff": 12 + }, + { + "username": "perf-user-54", + "diff": 12 + }, + { + "username": "perf-user-55", + "diff": 12 + }, + { + "username": "perf-user-56", + "diff": 12 + }, + { + "username": "perf-user-57", + "diff": 12 + }, + { + "username": "perf-user-58", + "diff": 12 + }, + { + "username": "perf-user-59", + "diff": 12 + }, + { + "username": "perf-user-60", + "diff": 12 + }, + { + "username": "perf-user-61", + "diff": 12 + }, + { + "username": "perf-user-62", + "diff": 12 + }, + { + "username": "perf-user-63", + "diff": 12 + }, + { + "username": "perf-user-64", + "diff": 12 + }, + { + "username": "perf-user-65", + "diff": 12 + }, + { + "username": "perf-user-66", + "diff": 14 + }, + { + "username": "perf-user-67", + "diff": 14 + }, + { + "username": "perf-user-68", + "diff": 12 + }, + { + "username": "perf-user-69", + "diff": 12 + }, + { + "username": "perf-user-70", + "diff": 12 + }, + { + "username": "perf-user-72", + "diff": 10 + }, + { + "username": "perf-user-73", + "diff": 8 + }, + { + "username": "perf-user-74", + "diff": 8 + } + ], + "memberDiffMeta": { + "count": 4, + "avg": 4.5 + }, + "deviceDiffMeta": { + "count": 75, + "avg": 11.92 + }, + "avgConnectedPeers": 9.666666666666666, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 15 + }, + { + "username": "perf-user-0", + "connectedPeers": 17 + }, + { + "username": "perf-user-1", + "connectedPeers": 18 + }, + { + "username": "perf-user-2", + "connectedPeers": 14 + }, + { + "username": "perf-user-3", + "connectedPeers": 17 + }, + { + "username": "perf-user-4", + "connectedPeers": 13 + }, + { + "username": "perf-user-5", + "connectedPeers": 19 + }, + { + "username": "perf-user-6", + "connectedPeers": 20 + }, + { + "username": "perf-user-7", + "connectedPeers": 11 + }, + { + "username": "perf-user-8", + "connectedPeers": 15 + }, + { + "username": "perf-user-9", + "connectedPeers": 13 + }, + { + "username": "perf-user-10", + "connectedPeers": 20 + }, + { + "username": "perf-user-11", + "connectedPeers": 16 + }, + { + "username": "perf-user-12", + "connectedPeers": 17 + }, + { + "username": "perf-user-13", + "connectedPeers": 8 + }, + { + "username": "perf-user-14", + "connectedPeers": 13 + }, + { + "username": "perf-user-15", + "connectedPeers": 13 + }, + { + "username": "perf-user-16", + "connectedPeers": 9 + }, + { + "username": "perf-user-17", + "connectedPeers": 10 + }, + { + "username": "perf-user-18", + "connectedPeers": 10 + }, + { + "username": "perf-user-19", + "connectedPeers": 10 + }, + { + "username": "perf-user-20", + "connectedPeers": 16 + }, + { + "username": "perf-user-21", + "connectedPeers": 11 + }, + { + "username": "perf-user-22", + "connectedPeers": 8 + }, + { + "username": "perf-user-23", + "connectedPeers": 10 + }, + { + "username": "perf-user-24", + "connectedPeers": 8 + }, + { + "username": "perf-user-25", + "connectedPeers": 10 + }, + { + "username": "perf-user-26", + "connectedPeers": 8 + }, + { + "username": "perf-user-27", + "connectedPeers": 12 + }, + { + "username": "perf-user-28", + "connectedPeers": 13 + }, + { + "username": "perf-user-29", + "connectedPeers": 10 + }, + { + "username": "perf-user-30", + "connectedPeers": 14 + }, + { + "username": "perf-user-31", + "connectedPeers": 9 + }, + { + "username": "perf-user-32", + "connectedPeers": 7 + }, + { + "username": "perf-user-33", + "connectedPeers": 8 + }, + { + "username": "perf-user-34", + "connectedPeers": 10 + }, + { + "username": "perf-user-35", + "connectedPeers": 8 + }, + { + "username": "perf-user-36", + "connectedPeers": 11 + }, + { + "username": "perf-user-37", + "connectedPeers": 7 + }, + { + "username": "perf-user-38", + "connectedPeers": 9 + }, + { + "username": "perf-user-39", + "connectedPeers": 8 + }, + { + "username": "perf-user-40", + "connectedPeers": 8 + }, + { + "username": "perf-user-41", + "connectedPeers": 7 + }, + { + "username": "perf-user-42", + "connectedPeers": 8 + }, + { + "username": "perf-user-43", + "connectedPeers": 8 + }, + { + "username": "perf-user-44", + "connectedPeers": 5 + }, + { + "username": "perf-user-45", + "connectedPeers": 8 + }, + { + "username": "perf-user-46", + "connectedPeers": 6 + }, + { + "username": "perf-user-47", + "connectedPeers": 9 + }, + { + "username": "perf-user-48", + "connectedPeers": 9 + }, + { + "username": "perf-user-49", + "connectedPeers": 12 + }, + { + "username": "perf-user-50", + "connectedPeers": 8 + }, + { + "username": "perf-user-51", + "connectedPeers": 7 + }, + { + "username": "perf-user-52", + "connectedPeers": 7 + }, + { + "username": "perf-user-53", + "connectedPeers": 6 + }, + { + "username": "perf-user-54", + "connectedPeers": 5 + }, + { + "username": "perf-user-55", + "connectedPeers": 6 + }, + { + "username": "perf-user-56", + "connectedPeers": 8 + }, + { + "username": "perf-user-57", + "connectedPeers": 8 + }, + { + "username": "perf-user-58", + "connectedPeers": 7 + }, + { + "username": "perf-user-59", + "connectedPeers": 6 + }, + { + "username": "perf-user-60", + "connectedPeers": 5 + }, + { + "username": "perf-user-61", + "connectedPeers": 8 + }, + { + "username": "perf-user-62", + "connectedPeers": 7 + }, + { + "username": "perf-user-63", + "connectedPeers": 7 + }, + { + "username": "perf-user-64", + "connectedPeers": 5 + }, + { + "username": "perf-user-65", + "connectedPeers": 5 + }, + { + "username": "perf-user-66", + "connectedPeers": 6 + }, + { + "username": "perf-user-67", + "connectedPeers": 6 + }, + { + "username": "perf-user-68", + "connectedPeers": 6 + }, + { + "username": "perf-user-69", + "connectedPeers": 6 + }, + { + "username": "perf-user-70", + "connectedPeers": 5 + }, + { + "username": "perf-user-72", + "connectedPeers": 5 + }, + { + "username": "perf-user-73", + "connectedPeers": 6 + }, + { + "username": "perf-user-74", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 80, + "avgChainLoadTimeMs": 4259.5, + "memberDiffs": [ + { + "username": "founding-perf-user", + "diff": 1 + }, + { + "username": "perf-user-0", + "diff": 1 + }, + { + "username": "perf-user-1", + "diff": 1 + }, + { + "username": "perf-user-2", + "diff": 1 + }, + { + "username": "perf-user-3", + "diff": 1 + }, + { + "username": "perf-user-4", + "diff": 1 + }, + { + "username": "perf-user-5", + "diff": 1 + }, + { + "username": "perf-user-6", + "diff": 1 + }, + { + "username": "perf-user-7", + "diff": 1 + }, + { + "username": "perf-user-8", + "diff": 1 + }, + { + "username": "perf-user-9", + "diff": 1 + }, + { + "username": "perf-user-10", + "diff": 1 + }, + { + "username": "perf-user-11", + "diff": 1 + }, + { + "username": "perf-user-12", + "diff": 1 + }, + { + "username": "perf-user-13", + "diff": 1 + }, + { + "username": "perf-user-14", + "diff": 1 + }, + { + "username": "perf-user-15", + "diff": 1 + }, + { + "username": "perf-user-16", + "diff": 1 + }, + { + "username": "perf-user-17", + "diff": 1 + }, + { + "username": "perf-user-18", + "diff": 1 + }, + { + "username": "perf-user-19", + "diff": 1 + }, + { + "username": "perf-user-20", + "diff": 1 + }, + { + "username": "perf-user-21", + "diff": 1 + }, + { + "username": "perf-user-22", + "diff": 1 + }, + { + "username": "perf-user-23", + "diff": 1 + }, + { + "username": "perf-user-24", + "diff": 1 + }, + { + "username": "perf-user-25", + "diff": 1 + }, + { + "username": "perf-user-26", + "diff": 1 + }, + { + "username": "perf-user-27", + "diff": 1 + }, + { + "username": "perf-user-28", + "diff": 1 + }, + { + "username": "perf-user-29", + "diff": 1 + }, + { + "username": "perf-user-30", + "diff": 1 + }, + { + "username": "perf-user-31", + "diff": 1 + }, + { + "username": "perf-user-32", + "diff": 1 + }, + { + "username": "perf-user-33", + "diff": 1 + }, + { + "username": "perf-user-34", + "diff": 1 + }, + { + "username": "perf-user-35", + "diff": 1 + }, + { + "username": "perf-user-36", + "diff": 1 + }, + { + "username": "perf-user-37", + "diff": 1 + }, + { + "username": "perf-user-38", + "diff": 1 + }, + { + "username": "perf-user-39", + "diff": 1 + }, + { + "username": "perf-user-40", + "diff": 1 + }, + { + "username": "perf-user-41", + "diff": 1 + }, + { + "username": "perf-user-42", + "diff": 1 + }, + { + "username": "perf-user-43", + "diff": 1 + }, + { + "username": "perf-user-44", + "diff": 1 + }, + { + "username": "perf-user-45", + "diff": 1 + }, + { + "username": "perf-user-46", + "diff": 1 + }, + { + "username": "perf-user-47", + "diff": 1 + }, + { + "username": "perf-user-48", + "diff": 1 + }, + { + "username": "perf-user-49", + "diff": 1 + }, + { + "username": "perf-user-50", + "diff": 1 + }, + { + "username": "perf-user-51", + "diff": 1 + }, + { + "username": "perf-user-52", + "diff": 1 + }, + { + "username": "perf-user-53", + "diff": 1 + }, + { + "username": "perf-user-54", + "diff": 1 + }, + { + "username": "perf-user-55", + "diff": 1 + }, + { + "username": "perf-user-56", + "diff": 1 + }, + { + "username": "perf-user-57", + "diff": 1 + }, + { + "username": "perf-user-58", + "diff": 1 + }, + { + "username": "perf-user-59", + "diff": 1 + }, + { + "username": "perf-user-60", + "diff": 1 + }, + { + "username": "perf-user-61", + "diff": 1 + }, + { + "username": "perf-user-62", + "diff": 1 + }, + { + "username": "perf-user-63", + "diff": 1 + }, + { + "username": "perf-user-64", + "diff": 1 + }, + { + "username": "perf-user-65", + "diff": 1 + }, + { + "username": "perf-user-66", + "diff": 11 + }, + { + "username": "perf-user-67", + "diff": 11 + }, + { + "username": "perf-user-68", + "diff": 1 + }, + { + "username": "perf-user-69", + "diff": 9 + }, + { + "username": "perf-user-70", + "diff": 1 + }, + { + "username": "perf-user-72", + "diff": 6 + }, + { + "username": "perf-user-73", + "diff": 4 + }, + { + "username": "perf-user-74", + "diff": 4 + }, + { + "username": "perf-user-75", + "diff": 11 + }, + { + "username": "perf-user-76", + "diff": 4 + }, + { + "username": "perf-user-77", + "diff": 3 + }, + { + "username": "perf-user-78", + "diff": 2 + }, + { + "username": "perf-user-79", + "diff": 1 + } + ], + "deviceDiffs": [ + { + "username": "founding-perf-user", + "diff": 22 + }, + { + "username": "perf-user-0", + "diff": 22 + }, + { + "username": "perf-user-1", + "diff": 22 + }, + { + "username": "perf-user-2", + "diff": 22 + }, + { + "username": "perf-user-3", + "diff": 22 + }, + { + "username": "perf-user-4", + "diff": 22 + }, + { + "username": "perf-user-5", + "diff": 22 + }, + { + "username": "perf-user-6", + "diff": 22 + }, + { + "username": "perf-user-7", + "diff": 22 + }, + { + "username": "perf-user-8", + "diff": 22 + }, + { + "username": "perf-user-9", + "diff": 22 + }, + { + "username": "perf-user-10", + "diff": 22 + }, + { + "username": "perf-user-11", + "diff": 22 + }, + { + "username": "perf-user-12", + "diff": 22 + }, + { + "username": "perf-user-13", + "diff": 22 + }, + { + "username": "perf-user-14", + "diff": 22 + }, + { + "username": "perf-user-15", + "diff": 22 + }, + { + "username": "perf-user-16", + "diff": 22 + }, + { + "username": "perf-user-17", + "diff": 22 + }, + { + "username": "perf-user-18", + "diff": 22 + }, + { + "username": "perf-user-19", + "diff": 22 + }, + { + "username": "perf-user-20", + "diff": 22 + }, + { + "username": "perf-user-21", + "diff": 22 + }, + { + "username": "perf-user-22", + "diff": 22 + }, + { + "username": "perf-user-23", + "diff": 22 + }, + { + "username": "perf-user-24", + "diff": 22 + }, + { + "username": "perf-user-25", + "diff": 22 + }, + { + "username": "perf-user-26", + "diff": 22 + }, + { + "username": "perf-user-27", + "diff": 22 + }, + { + "username": "perf-user-28", + "diff": 22 + }, + { + "username": "perf-user-29", + "diff": 22 + }, + { + "username": "perf-user-30", + "diff": 22 + }, + { + "username": "perf-user-31", + "diff": 22 + }, + { + "username": "perf-user-32", + "diff": 22 + }, + { + "username": "perf-user-33", + "diff": 22 + }, + { + "username": "perf-user-34", + "diff": 22 + }, + { + "username": "perf-user-35", + "diff": 22 + }, + { + "username": "perf-user-36", + "diff": 22 + }, + { + "username": "perf-user-37", + "diff": 22 + }, + { + "username": "perf-user-38", + "diff": 22 + }, + { + "username": "perf-user-39", + "diff": 22 + }, + { + "username": "perf-user-40", + "diff": 22 + }, + { + "username": "perf-user-41", + "diff": 22 + }, + { + "username": "perf-user-42", + "diff": 22 + }, + { + "username": "perf-user-43", + "diff": 22 + }, + { + "username": "perf-user-44", + "diff": 22 + }, + { + "username": "perf-user-45", + "diff": 22 + }, + { + "username": "perf-user-46", + "diff": 22 + }, + { + "username": "perf-user-47", + "diff": 22 + }, + { + "username": "perf-user-48", + "diff": 22 + }, + { + "username": "perf-user-49", + "diff": 22 + }, + { + "username": "perf-user-50", + "diff": 22 + }, + { + "username": "perf-user-51", + "diff": 22 + }, + { + "username": "perf-user-52", + "diff": 22 + }, + { + "username": "perf-user-53", + "diff": 22 + }, + { + "username": "perf-user-54", + "diff": 22 + }, + { + "username": "perf-user-55", + "diff": 22 + }, + { + "username": "perf-user-56", + "diff": 22 + }, + { + "username": "perf-user-57", + "diff": 22 + }, + { + "username": "perf-user-58", + "diff": 22 + }, + { + "username": "perf-user-59", + "diff": 22 + }, + { + "username": "perf-user-60", + "diff": 22 + }, + { + "username": "perf-user-61", + "diff": 22 + }, + { + "username": "perf-user-62", + "diff": 22 + }, + { + "username": "perf-user-63", + "diff": 22 + }, + { + "username": "perf-user-64", + "diff": 22 + }, + { + "username": "perf-user-65", + "diff": 22 + }, + { + "username": "perf-user-66", + "diff": 22 + }, + { + "username": "perf-user-67", + "diff": 24 + }, + { + "username": "perf-user-68", + "diff": 22 + }, + { + "username": "perf-user-69", + "diff": 22 + }, + { + "username": "perf-user-70", + "diff": 22 + }, + { + "username": "perf-user-72", + "diff": 20 + }, + { + "username": "perf-user-73", + "diff": 18 + }, + { + "username": "perf-user-74", + "diff": 18 + }, + { + "username": "perf-user-75", + "diff": 22 + }, + { + "username": "perf-user-76", + "diff": 20 + }, + { + "username": "perf-user-77", + "diff": 20 + }, + { + "username": "perf-user-78", + "diff": 20 + }, + { + "username": "perf-user-79", + "diff": 20 + } + ], + "memberDiffMeta": { + "count": 80, + "avg": 1.6875 + }, + "deviceDiffMeta": { + "count": 80, + "avg": 21.8 + }, + "avgConnectedPeers": 9.6875, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 15 + }, + { + "username": "perf-user-0", + "connectedPeers": 17 + }, + { + "username": "perf-user-1", + "connectedPeers": 18 + }, + { + "username": "perf-user-2", + "connectedPeers": 14 + }, + { + "username": "perf-user-3", + "connectedPeers": 17 + }, + { + "username": "perf-user-4", + "connectedPeers": 13 + }, + { + "username": "perf-user-5", + "connectedPeers": 19 + }, + { + "username": "perf-user-6", + "connectedPeers": 20 + }, + { + "username": "perf-user-7", + "connectedPeers": 11 + }, + { + "username": "perf-user-8", + "connectedPeers": 15 + }, + { + "username": "perf-user-9", + "connectedPeers": 13 + }, + { + "username": "perf-user-10", + "connectedPeers": 20 + }, + { + "username": "perf-user-11", + "connectedPeers": 16 + }, + { + "username": "perf-user-12", + "connectedPeers": 19 + }, + { + "username": "perf-user-13", + "connectedPeers": 8 + }, + { + "username": "perf-user-14", + "connectedPeers": 13 + }, + { + "username": "perf-user-15", + "connectedPeers": 13 + }, + { + "username": "perf-user-16", + "connectedPeers": 10 + }, + { + "username": "perf-user-17", + "connectedPeers": 11 + }, + { + "username": "perf-user-18", + "connectedPeers": 12 + }, + { + "username": "perf-user-19", + "connectedPeers": 10 + }, + { + "username": "perf-user-20", + "connectedPeers": 17 + }, + { + "username": "perf-user-21", + "connectedPeers": 11 + }, + { + "username": "perf-user-22", + "connectedPeers": 8 + }, + { + "username": "perf-user-23", + "connectedPeers": 10 + }, + { + "username": "perf-user-24", + "connectedPeers": 8 + }, + { + "username": "perf-user-25", + "connectedPeers": 10 + }, + { + "username": "perf-user-26", + "connectedPeers": 8 + }, + { + "username": "perf-user-27", + "connectedPeers": 12 + }, + { + "username": "perf-user-28", + "connectedPeers": 14 + }, + { + "username": "perf-user-29", + "connectedPeers": 11 + }, + { + "username": "perf-user-30", + "connectedPeers": 14 + }, + { + "username": "perf-user-31", + "connectedPeers": 10 + }, + { + "username": "perf-user-32", + "connectedPeers": 7 + }, + { + "username": "perf-user-33", + "connectedPeers": 8 + }, + { + "username": "perf-user-34", + "connectedPeers": 10 + }, + { + "username": "perf-user-35", + "connectedPeers": 8 + }, + { + "username": "perf-user-36", + "connectedPeers": 12 + }, + { + "username": "perf-user-37", + "connectedPeers": 7 + }, + { + "username": "perf-user-38", + "connectedPeers": 9 + }, + { + "username": "perf-user-39", + "connectedPeers": 9 + }, + { + "username": "perf-user-40", + "connectedPeers": 8 + }, + { + "username": "perf-user-41", + "connectedPeers": 8 + }, + { + "username": "perf-user-42", + "connectedPeers": 8 + }, + { + "username": "perf-user-43", + "connectedPeers": 8 + }, + { + "username": "perf-user-44", + "connectedPeers": 5 + }, + { + "username": "perf-user-45", + "connectedPeers": 8 + }, + { + "username": "perf-user-46", + "connectedPeers": 6 + }, + { + "username": "perf-user-47", + "connectedPeers": 9 + }, + { + "username": "perf-user-48", + "connectedPeers": 9 + }, + { + "username": "perf-user-49", + "connectedPeers": 12 + }, + { + "username": "perf-user-50", + "connectedPeers": 9 + }, + { + "username": "perf-user-51", + "connectedPeers": 9 + }, + { + "username": "perf-user-52", + "connectedPeers": 7 + }, + { + "username": "perf-user-53", + "connectedPeers": 7 + }, + { + "username": "perf-user-54", + "connectedPeers": 5 + }, + { + "username": "perf-user-55", + "connectedPeers": 6 + }, + { + "username": "perf-user-56", + "connectedPeers": 8 + }, + { + "username": "perf-user-57", + "connectedPeers": 8 + }, + { + "username": "perf-user-58", + "connectedPeers": 7 + }, + { + "username": "perf-user-59", + "connectedPeers": 7 + }, + { + "username": "perf-user-60", + "connectedPeers": 5 + }, + { + "username": "perf-user-61", + "connectedPeers": 8 + }, + { + "username": "perf-user-62", + "connectedPeers": 8 + }, + { + "username": "perf-user-63", + "connectedPeers": 7 + }, + { + "username": "perf-user-64", + "connectedPeers": 6 + }, + { + "username": "perf-user-65", + "connectedPeers": 5 + }, + { + "username": "perf-user-66", + "connectedPeers": 8 + }, + { + "username": "perf-user-67", + "connectedPeers": 6 + }, + { + "username": "perf-user-68", + "connectedPeers": 6 + }, + { + "username": "perf-user-69", + "connectedPeers": 6 + }, + { + "username": "perf-user-70", + "connectedPeers": 6 + }, + { + "username": "perf-user-72", + "connectedPeers": 6 + }, + { + "username": "perf-user-73", + "connectedPeers": 6 + }, + { + "username": "perf-user-74", + "connectedPeers": 6 + }, + { + "username": "perf-user-75", + "connectedPeers": 5 + }, + { + "username": "perf-user-76", + "connectedPeers": 5 + }, + { + "username": "perf-user-77", + "connectedPeers": 5 + }, + { + "username": "perf-user-78", + "connectedPeers": 5 + }, + { + "username": "perf-user-79", + "connectedPeers": 5 + } + ] + }, + { + "userCount": 85, + "avgChainLoadTimeMs": 4576.8117647058825, + "memberDiffs": [ + { + "username": "founding-perf-user", + "diff": 2 + }, + { + "username": "perf-user-0", + "diff": 2 + }, + { + "username": "perf-user-1", + "diff": 2 + }, + { + "username": "perf-user-2", + "diff": 2 + }, + { + "username": "perf-user-3", + "diff": 2 + }, + { + "username": "perf-user-4", + "diff": 2 + }, + { + "username": "perf-user-5", + "diff": 2 + }, + { + "username": "perf-user-6", + "diff": 2 + }, + { + "username": "perf-user-7", + "diff": 2 + }, + { + "username": "perf-user-8", + "diff": 2 + }, + { + "username": "perf-user-9", + "diff": 2 + }, + { + "username": "perf-user-10", + "diff": 2 + }, + { + "username": "perf-user-11", + "diff": 2 + }, + { + "username": "perf-user-12", + "diff": 2 + }, + { + "username": "perf-user-13", + "diff": 2 + }, + { + "username": "perf-user-14", + "diff": 2 + }, + { + "username": "perf-user-15", + "diff": 2 + }, + { + "username": "perf-user-16", + "diff": 2 + }, + { + "username": "perf-user-17", + "diff": 2 + }, + { + "username": "perf-user-18", + "diff": 2 + }, + { + "username": "perf-user-19", + "diff": 2 + }, + { + "username": "perf-user-20", + "diff": 2 + }, + { + "username": "perf-user-21", + "diff": 2 + }, + { + "username": "perf-user-22", + "diff": 2 + }, + { + "username": "perf-user-23", + "diff": 2 + }, + { + "username": "perf-user-24", + "diff": 2 + }, + { + "username": "perf-user-25", + "diff": 2 + }, + { + "username": "perf-user-26", + "diff": 2 + }, + { + "username": "perf-user-27", + "diff": 2 + }, + { + "username": "perf-user-28", + "diff": 2 + }, + { + "username": "perf-user-29", + "diff": 2 + }, + { + "username": "perf-user-30", + "diff": 2 + }, + { + "username": "perf-user-31", + "diff": 2 + }, + { + "username": "perf-user-32", + "diff": 2 + }, + { + "username": "perf-user-33", + "diff": 2 + }, + { + "username": "perf-user-34", + "diff": 2 + }, + { + "username": "perf-user-35", + "diff": 2 + }, + { + "username": "perf-user-36", + "diff": 2 + }, + { + "username": "perf-user-37", + "diff": 2 + }, + { + "username": "perf-user-38", + "diff": 2 + }, + { + "username": "perf-user-39", + "diff": 2 + }, + { + "username": "perf-user-40", + "diff": 2 + }, + { + "username": "perf-user-41", + "diff": 2 + }, + { + "username": "perf-user-42", + "diff": 2 + }, + { + "username": "perf-user-43", + "diff": 2 + }, + { + "username": "perf-user-44", + "diff": 2 + }, + { + "username": "perf-user-45", + "diff": 2 + }, + { + "username": "perf-user-46", + "diff": 2 + }, + { + "username": "perf-user-47", + "diff": 2 + }, + { + "username": "perf-user-48", + "diff": 2 + }, + { + "username": "perf-user-49", + "diff": 2 + }, + { + "username": "perf-user-50", + "diff": 2 + }, + { + "username": "perf-user-51", + "diff": 2 + }, + { + "username": "perf-user-52", + "diff": 2 + }, + { + "username": "perf-user-53", + "diff": 2 + }, + { + "username": "perf-user-54", + "diff": 2 + }, + { + "username": "perf-user-55", + "diff": 2 + }, + { + "username": "perf-user-56", + "diff": 2 + }, + { + "username": "perf-user-57", + "diff": 2 + }, + { + "username": "perf-user-58", + "diff": 2 + }, + { + "username": "perf-user-59", + "diff": 2 + }, + { + "username": "perf-user-60", + "diff": 2 + }, + { + "username": "perf-user-61", + "diff": 2 + }, + { + "username": "perf-user-62", + "diff": 2 + }, + { + "username": "perf-user-63", + "diff": 2 + }, + { + "username": "perf-user-64", + "diff": 2 + }, + { + "username": "perf-user-65", + "diff": 2 + }, + { + "username": "perf-user-66", + "diff": 16 + }, + { + "username": "perf-user-67", + "diff": 16 + }, + { + "username": "perf-user-68", + "diff": 2 + }, + { + "username": "perf-user-69", + "diff": 13 + }, + { + "username": "perf-user-70", + "diff": 2 + }, + { + "username": "perf-user-72", + "diff": 11 + }, + { + "username": "perf-user-73", + "diff": 9 + }, + { + "username": "perf-user-74", + "diff": 9 + }, + { + "username": "perf-user-75", + "diff": 16 + }, + { + "username": "perf-user-76", + "diff": 9 + }, + { + "username": "perf-user-77", + "diff": 8 + }, + { + "username": "perf-user-78", + "diff": 7 + }, + { + "username": "perf-user-79", + "diff": 6 + }, + { + "username": "perf-user-80", + "diff": 5 + }, + { + "username": "perf-user-81", + "diff": 4 + }, + { + "username": "perf-user-82", + "diff": 13 + }, + { + "username": "perf-user-83", + "diff": 3 + }, + { + "username": "perf-user-84", + "diff": 2 + } + ], + "deviceDiffs": [ + { + "username": "founding-perf-user", + "diff": 32 + }, + { + "username": "perf-user-0", + "diff": 32 + }, + { + "username": "perf-user-1", + "diff": 32 + }, + { + "username": "perf-user-2", + "diff": 32 + }, + { + "username": "perf-user-3", + "diff": 32 + }, + { + "username": "perf-user-4", + "diff": 32 + }, + { + "username": "perf-user-5", + "diff": 32 + }, + { + "username": "perf-user-6", + "diff": 32 + }, + { + "username": "perf-user-7", + "diff": 32 + }, + { + "username": "perf-user-8", + "diff": 32 + }, + { + "username": "perf-user-9", + "diff": 32 + }, + { + "username": "perf-user-10", + "diff": 32 + }, + { + "username": "perf-user-11", + "diff": 32 + }, + { + "username": "perf-user-12", + "diff": 32 + }, + { + "username": "perf-user-13", + "diff": 32 + }, + { + "username": "perf-user-14", + "diff": 32 + }, + { + "username": "perf-user-15", + "diff": 32 + }, + { + "username": "perf-user-16", + "diff": 32 + }, + { + "username": "perf-user-17", + "diff": 32 + }, + { + "username": "perf-user-18", + "diff": 32 + }, + { + "username": "perf-user-19", + "diff": 32 + }, + { + "username": "perf-user-20", + "diff": 32 + }, + { + "username": "perf-user-21", + "diff": 32 + }, + { + "username": "perf-user-22", + "diff": 32 + }, + { + "username": "perf-user-23", + "diff": 32 + }, + { + "username": "perf-user-24", + "diff": 32 + }, + { + "username": "perf-user-25", + "diff": 32 + }, + { + "username": "perf-user-26", + "diff": 32 + }, + { + "username": "perf-user-27", + "diff": 32 + }, + { + "username": "perf-user-28", + "diff": 32 + }, + { + "username": "perf-user-29", + "diff": 32 + }, + { + "username": "perf-user-30", + "diff": 32 + }, + { + "username": "perf-user-31", + "diff": 32 + }, + { + "username": "perf-user-32", + "diff": 32 + }, + { + "username": "perf-user-33", + "diff": 32 + }, + { + "username": "perf-user-34", + "diff": 32 + }, + { + "username": "perf-user-35", + "diff": 32 + }, + { + "username": "perf-user-36", + "diff": 32 + }, + { + "username": "perf-user-37", + "diff": 32 + }, + { + "username": "perf-user-38", + "diff": 32 + }, + { + "username": "perf-user-39", + "diff": 32 + }, + { + "username": "perf-user-40", + "diff": 32 + }, + { + "username": "perf-user-41", + "diff": 32 + }, + { + "username": "perf-user-42", + "diff": 32 + }, + { + "username": "perf-user-43", + "diff": 32 + }, + { + "username": "perf-user-44", + "diff": 32 + }, + { + "username": "perf-user-45", + "diff": 32 + }, + { + "username": "perf-user-46", + "diff": 32 + }, + { + "username": "perf-user-47", + "diff": 32 + }, + { + "username": "perf-user-48", + "diff": 32 + }, + { + "username": "perf-user-49", + "diff": 32 + }, + { + "username": "perf-user-50", + "diff": 32 + }, + { + "username": "perf-user-51", + "diff": 32 + }, + { + "username": "perf-user-52", + "diff": 32 + }, + { + "username": "perf-user-53", + "diff": 32 + }, + { + "username": "perf-user-54", + "diff": 32 + }, + { + "username": "perf-user-55", + "diff": 32 + }, + { + "username": "perf-user-56", + "diff": 32 + }, + { + "username": "perf-user-57", + "diff": 32 + }, + { + "username": "perf-user-58", + "diff": 32 + }, + { + "username": "perf-user-59", + "diff": 32 + }, + { + "username": "perf-user-60", + "diff": 32 + }, + { + "username": "perf-user-61", + "diff": 32 + }, + { + "username": "perf-user-62", + "diff": 32 + }, + { + "username": "perf-user-63", + "diff": 32 + }, + { + "username": "perf-user-64", + "diff": 32 + }, + { + "username": "perf-user-65", + "diff": 32 + }, + { + "username": "perf-user-66", + "diff": 32 + }, + { + "username": "perf-user-67", + "diff": 34 + }, + { + "username": "perf-user-68", + "diff": 32 + }, + { + "username": "perf-user-69", + "diff": 30 + }, + { + "username": "perf-user-70", + "diff": 32 + }, + { + "username": "perf-user-72", + "diff": 30 + }, + { + "username": "perf-user-73", + "diff": 28 + }, + { + "username": "perf-user-74", + "diff": 28 + }, + { + "username": "perf-user-75", + "diff": 32 + }, + { + "username": "perf-user-76", + "diff": 30 + }, + { + "username": "perf-user-77", + "diff": 30 + }, + { + "username": "perf-user-78", + "diff": 30 + }, + { + "username": "perf-user-79", + "diff": 30 + }, + { + "username": "perf-user-80", + "diff": 30 + }, + { + "username": "perf-user-81", + "diff": 30 + }, + { + "username": "perf-user-82", + "diff": 30 + }, + { + "username": "perf-user-83", + "diff": 30 + }, + { + "username": "perf-user-84", + "diff": 30 + } + ], + "memberDiffMeta": { + "count": 85, + "avg": 3.3529411764705883 + }, + "deviceDiffMeta": { + "count": 85, + "avg": 31.67058823529412 + }, + "avgConnectedPeers": 9.705882352941176, + "connectedPeers": [ + { + "username": "founding-perf-user", + "connectedPeers": 15 + }, + { + "username": "perf-user-0", + "connectedPeers": 17 + }, + { + "username": "perf-user-1", + "connectedPeers": 18 + }, + { + "username": "perf-user-2", + "connectedPeers": 14 + }, + { + "username": "perf-user-3", + "connectedPeers": 17 + }, + { + "username": "perf-user-4", + "connectedPeers": 13 + }, + { + "username": "perf-user-5", + "connectedPeers": 20 + }, + { + "username": "perf-user-6", + "connectedPeers": 20 + }, + { + "username": "perf-user-7", + "connectedPeers": 11 + }, + { + "username": "perf-user-8", + "connectedPeers": 15 + }, + { + "username": "perf-user-9", + "connectedPeers": 13 + }, + { + "username": "perf-user-10", + "connectedPeers": 20 + }, + { + "username": "perf-user-11", + "connectedPeers": 16 + }, + { + "username": "perf-user-12", + "connectedPeers": 20 + }, + { + "username": "perf-user-13", + "connectedPeers": 8 + }, + { + "username": "perf-user-14", + "connectedPeers": 13 + }, + { + "username": "perf-user-15", + "connectedPeers": 14 + }, + { + "username": "perf-user-16", + "connectedPeers": 10 + }, + { + "username": "perf-user-17", + "connectedPeers": 11 + }, + { + "username": "perf-user-18", + "connectedPeers": 12 + }, + { + "username": "perf-user-19", + "connectedPeers": 11 + }, + { + "username": "perf-user-20", + "connectedPeers": 18 + }, + { + "username": "perf-user-21", + "connectedPeers": 11 + }, + { + "username": "perf-user-22", + "connectedPeers": 8 + }, + { + "username": "perf-user-23", + "connectedPeers": 10 + }, + { + "username": "perf-user-24", + "connectedPeers": 8 + }, + { + "username": "perf-user-25", + "connectedPeers": 10 + }, + { + "username": "perf-user-26", + "connectedPeers": 8 + }, + { + "username": "perf-user-27", + "connectedPeers": 12 + }, + { + "username": "perf-user-28", + "connectedPeers": 14 + }, + { + "username": "perf-user-29", + "connectedPeers": 11 + }, + { + "username": "perf-user-30", + "connectedPeers": 14 + }, + { + "username": "perf-user-31", + "connectedPeers": 11 + }, + { + "username": "perf-user-32", + "connectedPeers": 7 + }, + { + "username": "perf-user-33", + "connectedPeers": 8 + }, + { + "username": "perf-user-34", + "connectedPeers": 10 + }, + { + "username": "perf-user-35", + "connectedPeers": 8 + }, + { + "username": "perf-user-36", + "connectedPeers": 12 + }, + { + "username": "perf-user-37", + "connectedPeers": 8 + }, + { + "username": "perf-user-38", + "connectedPeers": 10 + }, + { + "username": "perf-user-39", + "connectedPeers": 9 + }, + { + "username": "perf-user-40", + "connectedPeers": 8 + }, + { + "username": "perf-user-41", + "connectedPeers": 8 + }, + { + "username": "perf-user-42", + "connectedPeers": 8 + }, + { + "username": "perf-user-43", + "connectedPeers": 8 + }, + { + "username": "perf-user-44", + "connectedPeers": 6 + }, + { + "username": "perf-user-45", + "connectedPeers": 8 + }, + { + "username": "perf-user-46", + "connectedPeers": 6 + }, + { + "username": "perf-user-47", + "connectedPeers": 9 + }, + { + "username": "perf-user-48", + "connectedPeers": 9 + }, + { + "username": "perf-user-49", + "connectedPeers": 12 + }, + { + "username": "perf-user-50", + "connectedPeers": 10 + }, + { + "username": "perf-user-51", + "connectedPeers": 9 + }, + { + "username": "perf-user-52", + "connectedPeers": 7 + }, + { + "username": "perf-user-53", + "connectedPeers": 8 + }, + { + "username": "perf-user-54", + "connectedPeers": 5 + }, + { + "username": "perf-user-55", + "connectedPeers": 7 + }, + { + "username": "perf-user-56", + "connectedPeers": 9 + }, + { + "username": "perf-user-57", + "connectedPeers": 8 + }, + { + "username": "perf-user-58", + "connectedPeers": 9 + }, + { + "username": "perf-user-59", + "connectedPeers": 7 + }, + { + "username": "perf-user-60", + "connectedPeers": 6 + }, + { + "username": "perf-user-61", + "connectedPeers": 9 + }, + { + "username": "perf-user-62", + "connectedPeers": 8 + }, + { + "username": "perf-user-63", + "connectedPeers": 7 + }, + { + "username": "perf-user-64", + "connectedPeers": 6 + }, + { + "username": "perf-user-65", + "connectedPeers": 5 + }, + { + "username": "perf-user-66", + "connectedPeers": 9 + }, + { + "username": "perf-user-67", + "connectedPeers": 7 + }, + { + "username": "perf-user-68", + "connectedPeers": 6 + }, + { + "username": "perf-user-69", + "connectedPeers": 7 + }, + { + "username": "perf-user-70", + "connectedPeers": 6 + }, + { + "username": "perf-user-72", + "connectedPeers": 6 + }, + { + "username": "perf-user-73", + "connectedPeers": 6 + }, + { + "username": "perf-user-74", + "connectedPeers": 6 + }, + { + "username": "perf-user-75", + "connectedPeers": 5 + }, + { + "username": "perf-user-76", + "connectedPeers": 5 + }, + { + "username": "perf-user-77", + "connectedPeers": 6 + }, + { + "username": "perf-user-78", + "connectedPeers": 6 + }, + { + "username": "perf-user-79", + "connectedPeers": 7 + }, + { + "username": "perf-user-80", + "connectedPeers": 6 + }, + { + "username": "perf-user-81", + "connectedPeers": 5 + }, + { + "username": "perf-user-82", + "connectedPeers": 5 + }, + { + "username": "perf-user-83", + "connectedPeers": 5 + }, + { + "username": "perf-user-84", + "connectedPeers": 5 + } + ] + } +]; \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/devices.ts b/demos/quiet-sandbox/src/scripts/isla-perf/devices.ts new file mode 100644 index 00000000..25710f9c --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/devices.ts @@ -0,0 +1,6 @@ +import { createHash } from "crypto" +import { DeviceService } from "../../auth/services/members/deviceService.js" + +export const generateDeviceName = (username: string, index: number = 1): string => { + return createHash('md5').update(`${username}-${DeviceService.determineDeviceName()}-${index}`).digest('hex') +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/graphing/averageConnections.ts b/demos/quiet-sandbox/src/scripts/isla-perf/graphing/averageConnections.ts new file mode 100644 index 00000000..136346c4 --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/graphing/averageConnections.ts @@ -0,0 +1,35 @@ + +import Chart, { ChartItem } from 'chart.js/auto' + + +(async function() { + new Chart( + document.getElementById('average-connections')! as ChartItem, + { + type: 'bar', + data: { + // @ts-ignore + labels: data.map(row => row.userCount), + datasets: [ + { + label: 'Avg Number of Connected Peers Per User', + // @ts-ignore + data: data.map(row => row.avgConnectedPeers), + } + ], + }, + options: { + responsive: true, + plugins: { + legend: { + display: false + }, + title: { + text: 'Avg Number of Connected Peers Per User', + display: true + } + } + } + } + ); +})(); diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/graphing/averageDiffs.ts b/demos/quiet-sandbox/src/scripts/isla-perf/graphing/averageDiffs.ts new file mode 100644 index 00000000..781280fc --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/graphing/averageDiffs.ts @@ -0,0 +1,51 @@ + +import Chart, { ChartItem } from 'chart.js/auto' + + +(async function() { + new Chart( + document.getElementById('average-diffs')! as ChartItem, + { + type: 'bar', + data: { + // @ts-ignore + labels: data.map(row => row.userCount), + datasets: [ + { + label: 'Avg Member Diff', + // @ts-ignore + data: data.map(row => row.memberDiffMeta.avg), + }, + { + label: 'Number Of Members With Member Diff', + // @ts-ignore + data: data.map(row => row.memberDiffMeta.count) + }, + { + label: 'Avg Device Diff', + // @ts-ignore + data: data.map(row => row.deviceDiffMeta.avg) + }, + { + label: 'Number Of Members With Device Diff', + // @ts-ignore + data: data.map(row => row.deviceDiffMeta.count) + }, + ], + }, + options: { + responsive: true, + plugins: { + legend: { + position: 'bottom', + display: true + }, + title: { + text: 'Avg Diffs By User Count', + display: true + } + } + } + } + ); +})(); diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/graphing/averageLoadTimes.ts b/demos/quiet-sandbox/src/scripts/isla-perf/graphing/averageLoadTimes.ts new file mode 100644 index 00000000..140db0ee --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/graphing/averageLoadTimes.ts @@ -0,0 +1,35 @@ + +import Chart, { ChartItem } from 'chart.js/auto' + + +(async function() { + new Chart( + document.getElementById('average-load-times')! as ChartItem, + { + type: 'bar', + data: { + // @ts-ignore + labels: data.map(row => row.userCount), + datasets: [ + { + label: 'Avg Chain Load Time Per User (ms)', + // @ts-ignore + data: data.map(row => row.avgChainLoadTimeMs), + } + ], + }, + options: { + responsive: true, + plugins: { + legend: { + display: false + }, + title: { + text: 'Avg Chain Load Times (ms)', + display: true + } + } + } + } + ); +})(); diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/graphing/connectionScatter.ts b/demos/quiet-sandbox/src/scripts/isla-perf/graphing/connectionScatter.ts new file mode 100644 index 00000000..d18e1c0e --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/graphing/connectionScatter.ts @@ -0,0 +1,62 @@ +// @ts-ignore +var connectedPeersChart: Chart | undefined + +function getUserIndex(username: string): number { + if (username === 'founding-perf-user') { + return 0 + } + + return Number(username.split('-')[2]) + 1 +} + +async function drawConnectedPeersChart() { + if (connectedPeersChart != null) { + connectedPeersChart.destroy() + } + + // @ts-ignore + const userCount = selectedUserCount != null ? selectedUserCount : data[0].userCount; + // @ts-ignore + const dataRow = data.find(row => { + return row.userCount == userCount + }); + const DATA = { + // @ts-ignore + labels: dataRow.connectedPeers.map(connectedPeers => connectedPeers.username), + datasets: [{ + // @ts-ignore + data: dataRow.connectedPeers.map(connectedPeers => ({ + x: getUserIndex(connectedPeers.username), + y: connectedPeers.connectedPeers + }) + ) + }] + }; + + // @ts-ignore + connectedPeersChart = new Chart( + document.getElementById('connected-peers-chart')!, + { + type: 'line', + data: DATA, + options: { + scales: { + x: { + type: 'linear', + position: 'bottom', + } + }, + plugins: { + title: { + text: `Connected Peers Per User (${userCount} Users)`, + display: true + }, + legend: { + display: false + } + }, + responsive: true + }, + } + ); +}; \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/graphing/diffScatter.ts b/demos/quiet-sandbox/src/scripts/isla-perf/graphing/diffScatter.ts new file mode 100644 index 00000000..ce3b7c18 --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/graphing/diffScatter.ts @@ -0,0 +1,119 @@ +// @ts-ignore +var memberDiffChart: Chart | undefined + +function getUserIndex(username: string): number { + if (username === 'founding-perf-user') { + return 0 + } + + return Number(username.split('-')[2]) + 1 +} + +async function drawMemberDiffScatter() { + if (memberDiffChart != null) { + memberDiffChart.destroy() + } + + // @ts-ignore + const userCount = selectedUserCount != null ? selectedUserCount : data[0].userCount; + // @ts-ignore + const dataRow = data.find(row => { + return row.userCount == userCount + }); + const DATA = { + // @ts-ignore + labels: dataRow.memberDiffs.map(diff => diff.username), + datasets: [{ + // @ts-ignore + data: dataRow.memberDiffs.map(diff => ({ + x: getUserIndex(diff.username), + y: diff.diff + }) + ) + }] + }; + + // @ts-ignore + memberDiffChart = new Chart( + document.getElementById('member-diff-scatter')!, + { + type: 'scatter', + data: DATA, + options: { + scales: { + x: { + type: 'linear', + position: 'bottom', + } + }, + indexAxis: 'x', + plugins: { + title: { + text: `Member Diffs (${userCount} Users)`, + display: true + }, + legend: { + display: false + } + }, + responsive: true + }, + } + ); +}; + +// @ts-ignore +var deviceDiffChart: Chart | undefined + +async function drawDeviceDiffScatter() { + if (deviceDiffChart != null) { + deviceDiffChart.destroy() + } + + // @ts-ignore + const userCount = selectedUserCount != null ? selectedUserCount : data[0].userCount; + // @ts-ignore + const dataRow = data.find(row => { + return row.userCount == userCount + }); + const DATA = { + // @ts-ignore + labels: dataRow.deviceDiffs.map(diff => diff.username), + datasets: [{ + // @ts-ignore + data: dataRow.deviceDiffs.map(diff => ({ + x: getUserIndex(diff.username), + y: diff.diff + }) + ) + }] + }; + + // @ts-ignore + deviceDiffChart = new Chart( + document.getElementById('device-diff-scatter')!, + { + type: 'scatter', + data: DATA, + options: { + scales: { + x: { + type: 'linear', + position: 'bottom', + } + }, + indexAxis: 'x', + plugins: { + title: { + text: `Device Diffs (${userCount} Users)`, + display: true + }, + legend: { + display: false + } + }, + responsive: true + }, + } + ); +}; \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/index.html b/demos/quiet-sandbox/src/scripts/isla-perf/index.html new file mode 100644 index 00000000..4b4b614c --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/index.html @@ -0,0 +1,41 @@ + + + + Chart.js example + + + + + + + + + +
+
+
+ +
+ + + +
+
+
+ + + diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/index.ts b/demos/quiet-sandbox/src/scripts/isla-perf/index.ts new file mode 100644 index 00000000..10716fbf --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/index.ts @@ -0,0 +1,202 @@ +#! /usr/bin/env ts-node + +import { program } from '@commander-js/extra-typings'; +import { confirm, input } from '@inquirer/prompts'; +import chalk from 'chalk'; +import { mainLoop } from './mainLoop.js'; +import { generateSnapshot, loadRemoteSnapshotFile, storeSnapshotData } from './snapshots.js'; +import { loadRunData, RUN_DATA_FILENAME, RunData } from './runData.js'; +import { createLogger } from './logger.js'; +import { LogQueue } from '../../utils/logger/logQueue.js'; +import { sleep } from '../../utils/utils.js'; + +LogQueue.init(2) + +export type AppSettings = { + userCount: number + snapshotInterval: number + runningFromRemote?: boolean + endAutomatically?: boolean +} + +const LOGGER = createLogger("interactive") + +const startAppFromRemote = async (): Promise => { + const remoteRunDataFilename = await input({ + message: "What is the location of the remote run data file?", + default: RUN_DATA_FILENAME, + required: true, + validate: (remoteRunDataFilename: string) => remoteRunDataFilename != null ? true : "Must enter a remote run data filename!" + }); + + const runData = loadRunData(remoteRunDataFilename) + runData.appSettings = { + ...runData.appSettings, + runningFromRemote: true + } + + return runData +} + +const startNewApp = async (): Promise => { + const userCount = Number(await input({ + message: "How many users do you want to spin up?", + default: '50', + required: true, + validate: (userCount: string) => userCount != null && !Number.isNaN(Number(userCount)) ? true : "Must enter a valid user count!" + })); + + const snapshotInterval = Number(await input({ + message: "At what interval of user generation should metric snapshots be generated?", + default: '10', + required: true, + validate: (snapshotInterval: string) => snapshotInterval != null && !Number.isNaN(Number(snapshotInterval)) ? true : "Must enter a valid snapshot interval!" + })); + + const endAutomatically = await confirm({ + message: 'Would you like to end the run automatically when finished on this machine?', + default: true + }) + + const appSettings = { + userCount, + snapshotInterval, + endAutomatically + } + + return { + snapshots: [], + appSettings, + users: [], + teamName: 'perf-test-team', + peerAddresses: new Set(), + runMetadata: new Map(), + inviteSeeds: [] + } +} + +const startApp = async (): Promise => { + const loadRemoteRunData = await confirm({ + message: 'Would you like to start from a remote run?', + default: false + }) + + if (loadRemoteRunData) { + return startAppFromRemote() + } + + return startNewApp() +} + +const continueRemotely = async (runData: RunData) => { + if (runData.appSettings.runningFromRemote) { + LOGGER.info(`Ending remote run and writing JSON snapshot data!`) + storeSnapshotData(runData.snapshots, { jsonOnly: true }) + return + } + + if (runData.appSettings.endAutomatically) { + LOGGER.info(`Ending run without checking for remote runs!`) + storeSnapshotData(runData.snapshots) + return + } + + let continueRunning = await confirm({ + message: 'Would you like to continue running on another machine?', + default: true + }) + + if (!continueRunning) { + storeSnapshotData(runData.snapshots) + return + } + + let remoteSnapshotFilename: string + let exit = false + while (!exit) { + const doneWithRemoteRun = await confirm({ + message: 'Is the remote run done?', + default: true + }) + + if (!doneWithRemoteRun) { + LOGGER.info(`Waiting until remote run is done!`) + continue + } + + remoteSnapshotFilename = await input({ + message: "Where is the remote snapshot file?", + required: true, + validate: (remoteSnapshotFilename: string) => remoteSnapshotFilename != null ? true : "Must enter a valid remote snapshot filename!" + }); + } + + const remoteSnapshots = loadRemoteSnapshotFile(remoteSnapshotFilename!) + const snapshot = await generateSnapshot(runData, remoteSnapshots) + runData.snapshots.push(snapshot) + storeSnapshotData(runData.snapshots, { remoteSnapshots }) +} + +const interactive = async () => { + LOGGER.info(chalk.underline("Isla Perf Test")) + let runData: RunData | undefined + let exit = false; + while (!exit) { + runData = await startApp() + if (runData != null) { + exit = true + } + }; + + if (runData == null) { + throw new Error("App hasn't been started!") + } + + let exitCode = 0 + + const handleUncaught = async (message: string, e: any, ...args: any[]) => { + LOGGER.error(message, e, ...args) + await generateSnapshot(runData) + storeSnapshotData(runData.snapshots) + // process.exit(1) + } + + process.on('uncaughtException', async (e, origin) => { + await handleUncaught(`UNCAUGHT EXCEPTION`, e, origin) + }) + + process.on('unhandledRejection', async (e, promise) => { + await handleUncaught(`UNCAUGHT REJECTION`, e, promise) + }) + + try { + await mainLoop(runData).catch(async (e) => { + await handleUncaught(`Error while running main loop`, e) + }) + + await continueRemotely(runData).catch(async (e) => { + await handleUncaught(`Error while waiting for remote run`, e) + }) + } catch (e) { + await handleUncaught(`Error while running appliation`, e) + } + + LOGGER.info("Goodbye!"); + process.exit(exitCode) +}; + +program + .name('test-isla-perf') + .description('Quiet Sandbox CLI') + .version('0.0.1'); + +program + .command('interactive') + .description('Interactive mode') + .action(() => { + interactive().catch((e) => { + LOGGER.error(`Run failed with uncaught error`, e) + }); + }); + +program.parse(process.argv); \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/logger.ts b/demos/quiet-sandbox/src/scripts/isla-perf/logger.ts new file mode 100644 index 00000000..4991a681 --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/logger.ts @@ -0,0 +1,3 @@ +import { createQuietLogger } from "../../utils/logger/logger.js"; + +export const createLogger = createQuietLogger("qsb:isla:perf") \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/mainLoop.ts b/demos/quiet-sandbox/src/scripts/isla-perf/mainLoop.ts new file mode 100644 index 00000000..497861ed --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/mainLoop.ts @@ -0,0 +1,53 @@ +import { sleep } from "../../utils/utils.js"; +import { createLogger } from "./logger.js"; +import { RunData, storeRunData } from "./runData.js"; +import { generateSnapshot } from "./snapshots.js"; +import { createInvites } from "./team.js"; +import { createFounderAndChain, createUserAndDial } from "./users.js"; + +const LOGGER = createLogger("main") + +export async function mainLoop(runData: RunData): Promise { + let usersToGenerate: number = runData.appSettings.userCount + let startingIndex = 0 + if (runData.remoteUserNames == null) { + const founder = await createFounderAndChain(runData) + runData.inviteSeeds = await createInvites(founder) + runData.users.push(founder) + usersToGenerate -= 1 + } else { + startingIndex = runData.remoteUserNames.length - 1 + } + + let inviteIndex = 0 + LOGGER.info(`Generating ${usersToGenerate} users`); + for (let i = startingIndex; i < usersToGenerate+startingIndex; i++) { + try { + await sleep(1_000) + const user = await createUserAndDial(i, runData.inviteSeeds[inviteIndex], runData) + runData.users.push(user); + runData.peerAddresses.add(user.libp2p.libp2p!.getMultiaddrs()[0].toString()) + } catch (e) { + LOGGER.error(`Nothing to do, this user failed!`, e) + } + + if (inviteIndex === runData.inviteSeeds.length - 1) { + inviteIndex = 0; + } else { + inviteIndex++; + } + + if (runData.users.length % runData.appSettings.snapshotInterval === 0) { + if (i === usersToGenerate+startingIndex-1) { + LOGGER.info(`Waiting for a bit to see if connections take hold`) + await sleep(30000) + } + const snapshot = await generateSnapshot(runData) + runData.snapshots.push(snapshot) + } + } + + storeRunData(runData) + + return runData +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/runData.ts b/demos/quiet-sandbox/src/scripts/isla-perf/runData.ts new file mode 100644 index 00000000..1fda1439 --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/runData.ts @@ -0,0 +1,72 @@ +import { PeerRunMetadataMap, Snapshot } from './snapshots.js' +import { AppSettings } from './index.js' +import { Networking } from '../../network/network.js' + +import * as fs from 'fs' +import * as path from 'path' +import { createLogger } from './logger.js' + +const LOGGER = createLogger("runData") + +export type RunData = { + snapshots: Snapshot[] + appSettings: AppSettings + users: Networking[] + remoteUserNames?: string[] + teamName: string + peerAddresses: Set + runMetadata: PeerRunMetadataMap + inviteSeeds: string[] +} + +export type TruncatedRunData = { + appSettings: AppSettings + usernames: string[] + remoteUserNames?: string[] + teamName: string + peerAddresses: string[] + inviteSeeds: string[] +} + +function truncateRunData(runData: RunData): TruncatedRunData { + return { + appSettings: runData.appSettings, + teamName: runData.teamName, + peerAddresses: Array.from(runData.peerAddresses), + remoteUserNames: runData.remoteUserNames, + usernames: runData.users.map(user => user.storage.getContext()!.user.userName), + inviteSeeds: runData.inviteSeeds + } +} + +function runDataFromTruncated(truncatedData: TruncatedRunData): RunData { + return { + snapshots: [], + users: [], + runMetadata: new Map(), + appSettings: truncatedData.appSettings, + teamName: truncatedData.teamName, + peerAddresses: new Set(truncatedData.peerAddresses), + remoteUserNames: truncatedData.usernames, + inviteSeeds: truncatedData.inviteSeeds + } +} + +export const RUN_DATA_FILENAME = './src/scripts/isla-perf/run_data.json' + +export function storeRunData(runData: RunData) { + LOGGER.info(`Storing run data to file ${RUN_DATA_FILENAME}`) + + const data = JSON.stringify(truncateRunData(runData), null, 2) + fs.rmSync(RUN_DATA_FILENAME, { force: true }) + fs.writeFileSync(path.join(RUN_DATA_FILENAME), data, { encoding: 'utf-8' }) +} + +export function loadRunData(filename?: string): RunData { + const actualFilename = filename || RUN_DATA_FILENAME + LOGGER.info(`Loading run data from file ${actualFilename}`) + + const dataString = fs.readFileSync(actualFilename, { encoding: 'utf-8' }).toString() + const truncatedData = JSON.parse(dataString) as TruncatedRunData + return runDataFromTruncated(truncatedData) +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/run_data.json b/demos/quiet-sandbox/src/scripts/isla-perf/run_data.json new file mode 100644 index 00000000..ee6a32dc --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/run_data.json @@ -0,0 +1,202 @@ +{ + "appSettings": { + "userCount": 90, + "snapshotInterval": 5, + "endAutomatically": false + }, + "teamName": "perf-test-team", + "peerAddresses": [ + "/ip4/192.168.1.220/tcp/54009/p2p/12D3KooWK3LWyxCJyahYdmpsnmPCg6f86N78UV7JNjpKKjX6tdbv", + "/ip4/192.168.1.220/tcp/54010/p2p/12D3KooWH1hnmJKucKogzTvYWagntHsYUWN8FxNbKq6jCmYhyFvk", + "/ip4/192.168.1.220/tcp/54013/p2p/12D3KooWHC2qNyLzzLqJSUvPxmiGY9VgvmggJn1ymHJC8XggBvUP", + "/ip4/192.168.1.220/tcp/54018/p2p/12D3KooWHSKotQi3tzgAyVMD5UpRXwiXFueRuA7cWLZRfZRaghSU", + "/ip4/192.168.1.220/tcp/54023/p2p/12D3KooWM7va62rNQPNkqwC5TA8tCnFhQxNgoY6vZKTNPGh3Bt8F", + "/ip4/192.168.1.220/tcp/54030/p2p/12D3KooWR4xUAdD9epARWBruGLEe9432WmW4LQbsdMzXJPS6hKx6", + "/ip4/192.168.1.220/tcp/54038/p2p/12D3KooWP6JaJzf7WCKV2r1ALHjGqBiEB1q9Lev76xurMoUSbw2v", + "/ip4/192.168.1.220/tcp/54044/p2p/12D3KooWHhmhsXLSQVAk9qsLbUTVwX1J4Jww8X542haMxecSax4N", + "/ip4/192.168.1.220/tcp/54051/p2p/12D3KooWDToACXVsoBZLTNNZQoXS19iUBf48JwJU2BhZFCcce2Sf", + "/ip4/192.168.1.220/tcp/54057/p2p/12D3KooWCcr2HhpNe54725eCzv28GjeHmy4wyQ85r6nh2qn9XuPc", + "/ip4/192.168.1.220/tcp/54063/p2p/12D3KooWR4mijR3DPjkSot7fcMzYGF2PcjHod9A3a5MtvYEWLUzM", + "/ip4/192.168.1.220/tcp/54069/p2p/12D3KooWJXBfsuNRTuWiKXwWLZcvpaZJdDvJUp1iF1JpYFYzFB1A", + "/ip4/192.168.1.220/tcp/54075/p2p/12D3KooWJUciQ7afy7EwRaEYyJrEVtUq7cVE9hScs1HrxHZAyzBs", + "/ip4/192.168.1.220/tcp/54081/p2p/12D3KooWPz94m23a7rfpU7qKixmeqMySG53tPYyXDofZjFMzZDSX", + "/ip4/192.168.1.220/tcp/54087/p2p/12D3KooWBo9qNFtCPknh5kHXtFCN9hELoZ5HJLt41e9yLrMPegRc", + "/ip4/192.168.1.220/tcp/54093/p2p/12D3KooWLTcTTgPNa9tnkBTenXZ8BiuyUyhUoppmqtEjoTYVrJ6b", + "/ip4/192.168.1.220/tcp/54099/p2p/12D3KooWNzALLcSjqMA4w694iVcGcSqjWk7cYXHjQFdqTa1zy7t7", + "/ip4/192.168.1.220/tcp/54105/p2p/12D3KooWQ7jXxS77iHM3Ci8dc75AUYCGKHR67bzPT7HiGgrJPgxd", + "/ip4/192.168.1.220/tcp/54111/p2p/12D3KooWH67uExMxFqNr8emtpZpHWEGaW8NqFFYnGdpx3KacBmaW", + "/ip4/192.168.1.220/tcp/54118/p2p/12D3KooWH6KKXaDUypsK1E18xeH3L9pxKzxoBWUnsdtXi3fiK9fb", + "/ip4/192.168.1.220/tcp/54144/p2p/12D3KooWLD4DPkJHNhye6s6oRsNbUMPPrTCYhfAYxFNW7DYph5wX", + "/ip4/192.168.1.220/tcp/54151/p2p/12D3KooWL5j9JP3z3w51Z5pAUP2ERdnMH1ZirTrJQDwdw7j9nKuu", + "/ip4/192.168.1.220/tcp/54157/p2p/12D3KooWE8od7BJWJWKzJT2qHnHWNnuUJPJNhN66SCSRapERH1dp", + "/ip4/192.168.1.220/tcp/54172/p2p/12D3KooWFbiFVuqTWmHdpuYdXJzkVPMyuHZGYKMddzhc2xfULpfW", + "/ip4/192.168.1.220/tcp/54189/p2p/12D3KooWNygsmWjzVEX6aSf7X78Lk5jsz6NSsVYA3eYQRxxK38AD", + "/ip4/192.168.1.220/tcp/54205/p2p/12D3KooWPdkyoTKz5rszLA4LUhxttBzyJb2HcSJ56yFukyMDuuFx", + "/ip4/192.168.1.220/tcp/54215/p2p/12D3KooWS51F18sCCfFGx5caqkKLZ8t8GnLJhupCMGSEqHB8mPB4", + "/ip4/192.168.1.220/tcp/54239/p2p/12D3KooWC6sgsZof2a1643ULXnZ5L21mae2sw5n7KW1w4EyxfXdh", + "/ip4/192.168.1.220/tcp/54264/p2p/12D3KooWPW1ahUmDVrfaEKcETxmtAfLMZz72GAvLgw2Uy4EeCNzp", + "/ip4/192.168.1.220/tcp/54272/p2p/12D3KooWPVyUqmqhL29NnwowZe3DQkExyzzksMRkXVzx1Ar3JXNL", + "/ip4/192.168.1.220/tcp/54293/p2p/12D3KooWQNbPwhe4bEzdPeHLmaF9CWMLNmtd4X27rQtddxKF7uZE", + "/ip4/192.168.1.220/tcp/54300/p2p/12D3KooWALcZfXnheYFG1go8Se7fLMkL4eivWECPJgPT6SEapLRy", + "/ip4/192.168.1.220/tcp/54306/p2p/12D3KooWECbyewWe5xXCSLcs3AAxVss9tHuY7QFdt3J8VDqQ5GEz", + "/ip4/192.168.1.220/tcp/54312/p2p/12D3KooWLCuDBgymuAxmDcvjmm9JKJq1hEzLg9EUza6kMrYFmntR", + "/ip4/192.168.1.220/tcp/54318/p2p/12D3KooWSQFuGoB5CVMgAoYMRdr6T8soqBgT2oNQkucXXPJVPgwZ", + "/ip4/192.168.1.220/tcp/54354/p2p/12D3KooWCYdkDQqBX7t3yvE1GaN9Bxm8reqmmPWJW5HHMVq25avJ", + "/ip4/192.168.1.220/tcp/54363/p2p/12D3KooWHDCpAKiC23GArDbSzij5Qbivya3QHQH6AojgiVtW1eYS", + "/ip4/192.168.1.220/tcp/54369/p2p/12D3KooWEhvWpYiXK9QF5wGLiEs16KYQ8DJrr1rPA9o3m7Cs6toq", + "/ip4/192.168.1.220/tcp/54375/p2p/12D3KooWNgGdjF7CPxS4WDYTKVATzMhKuZjnGSY1zF3DFhkfesJ9", + "/ip4/192.168.1.220/tcp/54383/p2p/12D3KooWPp7YVUB6SNApqMjcB9UrzVF4aGSep2vambp9QGD91UZi", + "/ip4/192.168.1.220/tcp/54389/p2p/12D3KooWEaDP1AjL463jKe5q6x9xwCYPQc2NY9JV2LdKN2CeZnQK", + "/ip4/192.168.1.220/tcp/54396/p2p/12D3KooWJ7U8LSCRaJCNDSXMvux4VsGJfgt1Q2puGHet3n892s5h", + "/ip4/192.168.1.220/tcp/54402/p2p/12D3KooWBFNdsYodVXAiskDtJedUBL4gWypf6A7tBn1mkUPeGAGo", + "/ip4/192.168.1.220/tcp/54408/p2p/12D3KooWMFMBcgTeC5GKRq6nYEagHq3rcrnNXg95WLBvQf5s2rSS", + "/ip4/192.168.1.220/tcp/54414/p2p/12D3KooWJzVB2wcgAbMDUXJNAxNeCbunN2HsFR6RTzLGWVXnu81a", + "/ip4/192.168.1.220/tcp/54420/p2p/12D3KooWByBDivAYqTGQGufE4tUFZ3qXzcFvZiCzadFKbE9MPj5W", + "/ip4/192.168.1.220/tcp/54428/p2p/12D3KooWMdsUto4gvD3Awv36KY7XGvgkBbXgAW52nJXnkTbPNLPq", + "/ip4/192.168.1.220/tcp/54435/p2p/12D3KooWEnw2ozgj7vuNxvbjouDeTfH9P2CJXGfnYsyFuwWSxWAa", + "/ip4/192.168.1.220/tcp/54441/p2p/12D3KooWB76pa56kiKkKzjmpAbMdGThDiWP5vqRfzeeUzCGwu8uW", + "/ip4/192.168.1.220/tcp/54448/p2p/12D3KooWDXQy7ZWKNz3qUfoVLXeoFb1vFR36eM1vQZQiXKSzoE8R", + "/ip4/192.168.1.220/tcp/54454/p2p/12D3KooWFXQwUJbJ6TN4puFqzXFkzbJBjuEJKEV7gdreYBD8TNg7", + "/ip4/192.168.1.220/tcp/54465/p2p/12D3KooWAVByuzCdJ6Sv2U85srjcx3DvnZgZvHiDURucD3WKyf5t", + "/ip4/192.168.1.220/tcp/54472/p2p/12D3KooWBhEoM2A2e4Rb8AyMSfWoaUXyoy86bfihw7wmhvk5dF9f", + "/ip4/192.168.1.220/tcp/54479/p2p/12D3KooWS4rvQctTKqreE2xBSqgpma3ETQFhGNXduRshH2zRq2Bb", + "/ip4/192.168.1.220/tcp/54486/p2p/12D3KooWHFrFWy5TC53sTTVZ4p6LG5QD3QEChNTTzsbKLiBz9aGA", + "/ip4/192.168.1.220/tcp/54495/p2p/12D3KooWNL6qxWFg4ZZi7mRUQkmQsaJ76pQwXyMraSMt9f2gxgDh", + "/ip4/192.168.1.220/tcp/54502/p2p/12D3KooWJeC75q5dFe2sqnqt5LA63oU4X8L3owJsbSHfd3jBG5o6", + "/ip4/192.168.1.220/tcp/54513/p2p/12D3KooWGLdxm6D1jTyvX5cXu7VZXTBJPF7Mz8GAvSBrtHoPYsmP", + "/ip4/192.168.1.220/tcp/54523/p2p/12D3KooWBYMKGjcuBpjvXt9G3iYqdW1mBV32uTLy6Zwscr8sskHd", + "/ip4/192.168.1.220/tcp/54535/p2p/12D3KooWEdCNKTugu5GZuYiRc9K8wEAu6FpArB3SBWHJhXsnMPBU", + "/ip4/192.168.1.220/tcp/54551/p2p/12D3KooWMr47RHpCBkaKQ8hNRheGjejbauFsgiz8Z2jD8H7GHA7N", + "/ip4/192.168.1.220/tcp/54563/p2p/12D3KooWMobJmc5MgrWeqG65CVninBi6cshe9UnTQjNH7BDHHvA7", + "/ip4/192.168.1.220/tcp/54569/p2p/12D3KooWLRMZRmPRCF1ei7QmZhwrSByQDAgCkgAMMiePFYCroNnu", + "/ip4/192.168.1.220/tcp/54576/p2p/12D3KooWJLgCQAGNtC7iZzxnZGNLh8dXgxpBwBoyjsgG9cwAh3vv", + "/ip4/192.168.1.220/tcp/54583/p2p/12D3KooWEpSCAAqDzdPkMEqnAhEV2ufi2af7NLm3r94Qkubj9ViT", + "/ip4/192.168.1.220/tcp/54592/p2p/12D3KooWAJDiENY8Nj6xMmGdGHfAyoM59GAp86VYLUz439JgwGvm", + "/ip4/192.168.1.220/tcp/54600/p2p/12D3KooWLmEQmeXt2bn2FNy9w5FVVPeoyA3EYDowJY44Gy2xQH3C", + "/ip4/192.168.1.220/tcp/54610/p2p/12D3KooWEPhsLJXViGW1KkCjVgi74DrALm6EqYavU3XE2tQ4U7vw", + "/ip4/192.168.1.220/tcp/54621/p2p/12D3KooWEkCpNQK21oynQaLMmACL63HpSoyNgq45KaCwuaJ2zvov", + "/ip4/192.168.1.220/tcp/54631/p2p/12D3KooWRS7o2rjbjz854HXpkVYs5C6wzrAW7qzPyY7FvPn3sV8N", + "/ip4/192.168.1.220/tcp/54646/p2p/12D3KooWLDhYGTJi9zLKCmnBuU5GYGWRvwGHP62TLtb2D2ocBkze", + "/ip4/192.168.1.220/tcp/54653/p2p/12D3KooWNaMEwBkp5kdThf5LfVnbQCMvcV6DB6rLQGPznPutdVUy", + "/ip4/192.168.1.220/tcp/54678/p2p/12D3KooWMh1vdvhE5ZN1mRDZkuWTjNZkbMFkRzPYdVUzuSnV13Pz", + "/ip4/192.168.1.220/tcp/54689/p2p/12D3KooWNNY1hpe8mazmWNURJrcgW1aKrKcMwp7kzqWZ5u71Umin", + "/ip4/192.168.1.220/tcp/54695/p2p/12D3KooWMz1TUqCKCSsmjbTibcNBcUWF4qS1P3vkV66DRY5vd9mx", + "/ip4/192.168.1.220/tcp/54703/p2p/12D3KooWB4qJEF8xc93C3msMqVTbyCkGwRJF7wKbSfeUx199bKj7", + "/ip4/192.168.1.220/tcp/54709/p2p/12D3KooWP57HUBdUowNXs57TQ5ugbdnLZgPU6XJxEBq3raNL1tjz", + "/ip4/192.168.1.220/tcp/54723/p2p/12D3KooWCAySaxcTqtjkpwvFR3jb6U28ikdJHpF9YbbxBzw67vze", + "/ip4/192.168.1.220/tcp/54735/p2p/12D3KooWKGoMsbdT1gh1BJZPxo68ianEgfAMEDBhr3qLpFUH6fTX", + "/ip4/192.168.1.220/tcp/54746/p2p/12D3KooWNBsCiD3vDcHVVCaZ96Xum5ivNfRSTu7f4oqmhkTomnGH", + "/ip4/192.168.1.220/tcp/54792/p2p/12D3KooWC9U7jnJ1TnVNnVv1xwcw3UMyURkP6TyM6pscWR8sUC6S", + "/ip4/192.168.1.220/tcp/54812/p2p/12D3KooWLeFfEdgzdVwKiwCCFard1P32ut2LMi7gQrgR3GhXf2uY", + "/ip4/192.168.1.220/tcp/54826/p2p/12D3KooWN4wLPjRJLyNKosJ26fhWkEPg3pAyRz3AbMRcKfVzM2uk", + "/ip4/192.168.1.220/tcp/54833/p2p/12D3KooWGUaVoqj1vQzPX3MWb1XVzqw14SPDGuiNmFzAGedJkNK7", + "/ip4/192.168.1.220/tcp/54854/p2p/12D3KooWGV4U8yWTdVjRB4gRL6RpkGe9DzxuZXMdKbwNaJa8Vt1Z", + "/ip4/192.168.1.220/tcp/54863/p2p/12D3KooWGQTSZaYNbnKwTedUuasv3fn4zNUXipX31Yax1ZzRVZ6A", + "/ip4/192.168.1.220/tcp/54872/p2p/12D3KooWLanqQrnQesko2sTvLusswqVNWQmDM71PkKxYCDVniw9S", + "/ip4/192.168.1.220/tcp/54883/p2p/12D3KooWFKrxocXQwWgQLu5Yaz9vFANKLJsoRQiYW358Sh2kpX2Z", + "/ip4/192.168.1.220/tcp/54892/p2p/12D3KooWGASJqyp5ZGfoytVuBJeBT6m9NheZyYNBjSMWBgGBzuUZ" + ], + "usernames": [ + "founding-perf-user", + "perf-user-0", + "perf-user-1", + "perf-user-2", + "perf-user-3", + "perf-user-4", + "perf-user-5", + "perf-user-6", + "perf-user-7", + "perf-user-8", + "perf-user-9", + "perf-user-10", + "perf-user-11", + "perf-user-12", + "perf-user-13", + "perf-user-14", + "perf-user-15", + "perf-user-16", + "perf-user-17", + "perf-user-18", + "perf-user-19", + "perf-user-20", + "perf-user-21", + "perf-user-22", + "perf-user-23", + "perf-user-24", + "perf-user-25", + "perf-user-26", + "perf-user-27", + "perf-user-28", + "perf-user-29", + "perf-user-30", + "perf-user-31", + "perf-user-32", + "perf-user-33", + "perf-user-34", + "perf-user-35", + "perf-user-36", + "perf-user-37", + "perf-user-38", + "perf-user-39", + "perf-user-40", + "perf-user-41", + "perf-user-42", + "perf-user-43", + "perf-user-44", + "perf-user-45", + "perf-user-46", + "perf-user-47", + "perf-user-48", + "perf-user-49", + "perf-user-50", + "perf-user-51", + "perf-user-52", + "perf-user-53", + "perf-user-54", + "perf-user-55", + "perf-user-56", + "perf-user-57", + "perf-user-58", + "perf-user-59", + "perf-user-60", + "perf-user-61", + "perf-user-62", + "perf-user-63", + "perf-user-64", + "perf-user-65", + "perf-user-66", + "perf-user-67", + "perf-user-68", + "perf-user-69", + "perf-user-70", + "perf-user-72", + "perf-user-73", + "perf-user-74", + "perf-user-75", + "perf-user-76", + "perf-user-77", + "perf-user-78", + "perf-user-79", + "perf-user-80", + "perf-user-81", + "perf-user-82", + "perf-user-83", + "perf-user-84", + "perf-user-85", + "perf-user-86", + "perf-user-87", + "perf-user-88" + ], + "inviteSeeds": [ + "8mWfiKdPLoAELuR9", + "4c3HZX4nxoVeShnx", + "4JxsqPx5G6EP1qjU", + "9ccoG3kxRUAmqHXr", + "43EXv24NMWpyNAWj", + "7Z9xKs6gAyzKErBC", + "72EhDdE4YyisGBB8", + "7XM1YXrQgJ8NJcKy", + "7dV7F1CYnzFvT5Rt", + "AMxDQ5tcVfjJQ8Ro" + ] +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/snapshots.ts b/demos/quiet-sandbox/src/scripts/isla-perf/snapshots.ts new file mode 100644 index 00000000..3866e1f7 --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/snapshots.ts @@ -0,0 +1,162 @@ +import { sleep } from "../../utils/utils.js" + +import * as fs from 'fs' +import { RunData } from "./runData.js" +import { createLogger } from "./logger.js" + +const LOGGER = createLogger("snapshots") + +export type PeerRunMetadata = { + chainLoadTimeMs: number + memberCount: number + memberDiff: number + deviceCount: number + deviceDiff: number + connectedPeers: number +} + +export type PeerRunMetadataMap = Map + +export type Diff = { + username: string + diff: number +} + +export type DiffMeta = { + count: number + avg: number +} + +export type ConnectedPeers = { + username: string + connectedPeers: number +} + +export type Snapshot = { + userCount: number + avgChainLoadTimeMs: number + memberDiffMeta: DiffMeta + deviceDiffMeta: DiffMeta + memberDiffs: Diff[] + deviceDiffs: Diff[] + avgConnectedPeers: number + connectedPeers: ConnectedPeers[] +} + +export async function generateSnapshot(runData: RunData, remoteSnapshots: Snapshot[] = []): Promise { + let remoteUserCount: number = 0 + if (runData.remoteUserNames) { + remoteUserCount = runData.remoteUserNames.length + } else if (remoteSnapshots.length > 0) { + remoteUserCount = remoteSnapshots[remoteSnapshots.length - 1].userCount + } + + const totalUsers = runData.users.length + remoteUserCount + LOGGER.info(`Capturing snapshot at ${totalUsers} users`) + const expectedDeviceCount = totalUsers * 2 - 1 - (remoteUserCount > 0 ? 1 : 0) + const memberDiffs: Diff[] = [] + const deviceDiffs: Diff[] = [] + const connectedPeersList: ConnectedPeers[] = [] + + let sumChainLoadTimeMs = 0 + let sumMemberDiff = 0 + let sumDeviceDiff = 0 + let sumConnectedPeers = 0 + + await sleep(5000) + for (const user of runData.users) { + const username = user.storage.getContext()!.user.userName + LOGGER.info(`Generating snapshot for user ${username}`) + const members = user.storage.getSigChain()!.users.getAllMembers() + const memberCount = members.length + const memberDiff = totalUsers - memberCount + if (memberDiff > 0) { + memberDiffs.push({ + username, + diff: memberDiff + }) + sumMemberDiff += memberDiff + } + + let deviceCount = 0 + for (const member of members) { + deviceCount += member.devices?.length || 0 + } + const deviceDiff = expectedDeviceCount - deviceCount + if (deviceDiff > 0) { + deviceDiffs.push({ + username, + diff: deviceDiff + }) + sumDeviceDiff += deviceDiff + } + const connectedPeers = user.libp2p.libp2p!.getPeers().length + const existingMetadata = runData.runMetadata.get(username)! + runData.runMetadata.set(username, { + ...existingMetadata, + memberCount, + memberDiff, + deviceCount, + deviceDiff, + connectedPeers + }) + sumChainLoadTimeMs += existingMetadata.chainLoadTimeMs + sumConnectedPeers += connectedPeers + connectedPeersList.push({ username, connectedPeers }) + } + const snapshot: Snapshot = { + userCount: totalUsers, + avgChainLoadTimeMs: sumChainLoadTimeMs / runData.users.length, + memberDiffs, + deviceDiffs, + memberDiffMeta: { + count: memberDiffs.length, + avg: sumMemberDiff / memberDiffs.length || 0 + }, + deviceDiffMeta: { + count: deviceDiffs.length, + avg: sumDeviceDiff / deviceDiffs.length || 0 + }, + avgConnectedPeers: sumConnectedPeers / runData.users.length, + connectedPeers: connectedPeersList + } + + return snapshot +} + +type SnapshotConfig = { + jsonOnly?: boolean + remoteSnapshots?: Snapshot[] +} + +export function loadRemoteSnapshotFile(filename: string): Snapshot[] { + if (filename.endsWith('.js')) { + throw new Error('You need to use a .json snapshot file!') + } + + const dataString = fs.readFileSync(filename, { encoding: 'utf-8' }).toString() + return JSON.parse(dataString) as Snapshot[] +} + +export function storeSnapshotData(snapshots: Snapshot[], config: SnapshotConfig = {}) { + let filename = './src/scripts/isla-perf/data.json' + let data: string + let writableSnapshots = snapshots + if (config.remoteSnapshots) { + writableSnapshots = [ + ...writableSnapshots, + ...config.remoteSnapshots + ] + } + + if (config.jsonOnly) { + data = JSON.stringify(writableSnapshots, null, 2) + } else { + data = `const data = ${JSON.stringify(writableSnapshots, null, 2)};` + filename = `${filename}.js` + } + + LOGGER.info(`Storing snapshot data to file ${filename}`) + fs.rmSync(filename, { force: true }) + fs.writeFileSync(filename, data, { encoding: 'utf-8' }) +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/team.ts b/demos/quiet-sandbox/src/scripts/isla-perf/team.ts new file mode 100644 index 00000000..761239b6 --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/team.ts @@ -0,0 +1,66 @@ +import { InviteeMemberContext } from "@localfirst/auth"; +import { SigChain } from "../../auth/chain.js"; +import { DEFAULT_INVITATION_VALID_FOR_MS } from "../../auth/services/invites/inviteService.js"; +import { UserService } from "../../auth/services/members/userService.js"; +import { Networking } from "../../network/network.js"; +import { generateDeviceName } from "./devices.js"; +import { createLogger } from "./logger.js"; +import { LocalStorage } from "../../network/storage.js"; +import { QuietAuthEvents } from "../../network/events.js"; + +const LOGGER = createLogger("team") + +export const createTeam = async (name: string, username: string): Promise => { + LOGGER.info(`Initializing team with name ${name} for user ${username}`); + const storage = new LocalStorage() + const loadedSigChain = SigChain.create(name, username); + storage.setContext(loadedSigChain.context); + storage.setSigChain(loadedSigChain.sigChain); + storage.setAuthContext({ + user: loadedSigChain.context.user, + device: loadedSigChain.context.device, + team: loadedSigChain.sigChain.team + }); + + LOGGER.info(`Initializing networking`); + const networking = await Networking.init(storage); + // await networking.sigChain.writeInitialChain(loadedSigChain.sigChain); + return networking; +} + +export const preJoin = (name: string, username: string, inviteSeed: string): LocalStorage => { + LOGGER.info(`Creating prerequisites to join team ${name} as user ${username} with inviteSeed ${inviteSeed}`); + const storage = new LocalStorage() + const deviceName = generateDeviceName(username, 1) + const prospectiveUser = UserService.createFromInviteSeed(username, inviteSeed, deviceName) + storage.setContext(prospectiveUser.context) + storage.setAuthContext({ + user: prospectiveUser.context.user, + device: prospectiveUser.context.device, + invitationSeed: inviteSeed + }); + + return storage +} + +export const joinTeam = async (storage: LocalStorage, events?: QuietAuthEvents): Promise => { + LOGGER.info(`Joining team as user ${(storage.getAuthContext()! as InviteeMemberContext).user.userName} with inviteSeed ${(storage.getAuthContext()! as InviteeMemberContext).invitationSeed}`) + LOGGER.info(`Initializing networking`); + const networking = await Networking.init(storage, events); + + return networking; +} + +export async function createInvites(founder: Networking): Promise { + const inviteCount = 10; + const inviteMaxUses = 50; + const inviteSeeds: string[] = []; + + LOGGER.info(`Generating ${inviteCount} invites as founder`); + for (let i = 0; i < inviteCount; i++) { + const invite = founder.libp2p.storage.getSigChain()!.invites.create(DEFAULT_INVITATION_VALID_FOR_MS, inviteMaxUses); + inviteSeeds.push(invite.seed); + } + + return inviteSeeds +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/isla-perf/users.ts b/demos/quiet-sandbox/src/scripts/isla-perf/users.ts new file mode 100644 index 00000000..0a3e2c41 --- /dev/null +++ b/demos/quiet-sandbox/src/scripts/isla-perf/users.ts @@ -0,0 +1,201 @@ +import { DEFAULT_INVITATION_VALID_FOR_MS, InviteService } from "../../auth/services/invites/inviteService.js"; +import { DeviceService } from "../../auth/services/members/deviceService.js"; +import { EVENTS, QuietAuthEvents } from "../../network/events.js"; +import { Networking } from "../../network/network.js"; +import { sleep } from "../../utils/utils.js"; +import { generateDeviceName } from "./devices.js"; +import { createLogger } from "./logger.js"; +import { RunData } from "./runData.js"; +import { createTeam, joinTeam, preJoin } from "./team.js"; + +const LOGGER = createLogger("users") + +export async function createFounderAndChain( + runData: RunData +): Promise { + const ownerStartTimeMs = Date.now() + const foundingUsername = 'founding-perf-user'; + const founder = await createTeam(runData.teamName, foundingUsername); + const ownerLoadTimeMs = Date.now() - ownerStartTimeMs + runData.runMetadata.set(foundingUsername, { + chainLoadTimeMs: ownerLoadTimeMs, + memberCount: 0, + deviceCount: 0, + memberDiff: 0, + deviceDiff: 0, + connectedPeers: 0 + }) + LOGGER.info( + `Took owner ${ownerLoadTimeMs}ms to create the chain`, + { username: foundingUsername, peerId: founder.libp2p.peerId } + ) + + runData.peerAddresses.add(founder.libp2p.libp2p!.getMultiaddrs()[0].toString()) + + return founder +} + +async function waitForDial(user: Networking, startTimeMs: number, runData: RunData): Promise { + const loggerData = { username: user.storage.getContext()!.user.userName, peerId: user.libp2p.peerId } + // let dialFinished = false + // user.events.on(EVENTS.DIAL_FINISHED, () => { + // if () + // LOGGER.info('Dial finished!', loggerData) + // dialFinished = true + // }) + + let initialized: boolean = false + user.events.on(EVENTS.INITIALIZED_CHAIN, () => { + if (initialized) return + + const username = user.storage.getContext()!.user.userName + const userLoadTimeMs = Date.now() - startTimeMs + LOGGER.info(`Took user ${username} ${userLoadTimeMs}ms to load the chain`, loggerData) + LOGGER.info(`User ${username} has initialized their chain!`, loggerData) + initialized = true + runData.runMetadata.set(username, { + chainLoadTimeMs: userLoadTimeMs, + memberCount: 0, + deviceCount: 0, + memberDiff: 0, + deviceDiff: 0, + connectedPeers: 0 + }) + + const deviceName = generateDeviceName(username, 2) + const newDevice = DeviceService.generateDeviceForUser(user.storage.getContext()!.user.userId, deviceName) + const inviteForDevice = user.storage.getSigChain()!.invites.createForDevice(DEFAULT_INVITATION_VALID_FOR_MS) + const deviceProof = InviteService.generateProof(inviteForDevice.seed) + user.storage.getSigChain()!.invites.admitDevice(deviceProof, newDevice) + user.events.emit(EVENTS.INITIALIZED_CHAIN) + }) + + // let attempts = 5 + // while (attempts > 0) { + // try { + // LOGGER.info(`Connecting to ${runData.peerAddresses.size} peers`, loggerData); + // try { + // await user.libp2p.dial(runData.peerAddresses); + // } catch (e) { + // LOGGER.error(`Failed to dial peers on ${user.libp2p.peerId}`, loggerData, e) + // return user + // } + + // if (!dialFinished || !initialized) { + // const waitIntervalMs = 250 + // const waitTimeMs = 60_000 + // const waitEndTimeMs = Date.now() + waitTimeMs + // LOGGER.info(`Waiting for user to be ready!`, loggerData) + // while ((!dialFinished || !initialized) && Date.now() < waitEndTimeMs) { + // process.stdout.write('-') + // await sleep(waitIntervalMs) + // } + // if (!dialFinished || !initialized) { + // throw new Error(`Failed to finishing dialing in ${waitEndTimeMs}ms timeout`) + // } + // console.log('\n') + // } + // return user + // } catch (e) { + // LOGGER.error(`Failed to dial peers for user ${user.storage.getContext()!.user.userName}`, loggerData, e) + // await user.libp2p.hangUpOnAll() + // attempts -= 1 + // } + // } + + // throw new Error(`Failed to initialize user ${user.storage.getContext()!.user.userName}`) + if (!initialized) { + const waitIntervalMs = 250 + const waitTimeMs = 60_000 + const waitEndTimeMs = Date.now() + waitTimeMs + LOGGER.info(`Waiting for user to be ready!`, loggerData) + while ((!initialized) && Date.now() < waitEndTimeMs) { + process.stdout.write('-') + await sleep(waitIntervalMs) + } + if (!initialized) { + throw new Error(`Failed to finish loading chain in ${waitEndTimeMs}ms timeout`) + } + console.log('\n') + } + return user +} + +export async function createUserAndDial( + userIndex: number, + inviteSeed: string, + runData: RunData +): Promise { + const startTimeMs = Date.now() + const username = `perf-user-${userIndex}`; + LOGGER.info(`Creating user ${username}`) + + let initialized: boolean = false + let dialed: boolean = false + const events = new QuietAuthEvents(username) + + const storage = preJoin(runData.teamName, username, inviteSeed) + events.on(EVENTS.INITIALIZED_CHAIN, () => { + if (initialized) return + + const username = storage.getContext()!.user.userName + const userLoadTimeMs = Date.now() - startTimeMs + LOGGER.info(`Took user ${username} ${userLoadTimeMs}ms to load the chain`) + LOGGER.info(`User ${username} has initialized their chain!`) + initialized = true + runData.runMetadata.set(username, { + chainLoadTimeMs: userLoadTimeMs, + memberCount: 0, + deviceCount: 0, + memberDiff: 0, + deviceDiff: 0, + connectedPeers: 0 + }) + + const deviceName = generateDeviceName(username, 2) + const newDevice = DeviceService.generateDeviceForUser(storage.getContext()!.user.userId, deviceName) + const inviteForDevice = storage.getSigChain()!.invites.createForDevice(DEFAULT_INVITATION_VALID_FOR_MS) + const deviceProof = InviteService.generateProof(inviteForDevice.seed) + storage.getSigChain()!.invites.admitDevice(deviceProof, newDevice) + events.emit(EVENTS.INITIALIZED_CHAIN) + }) + + const user = await joinTeam(storage, events); + await sleep(2000) + + if (!initialized && user.storage.getSigChain() != null) { + user.events.emit(EVENTS.INITIALIZED_CHAIN) + } + + const minDialAmount = Math.min(3, runData.users.length) + const hasDialedEnough = (user: Networking): boolean => { + if (user.libp2p.libp2p && user.libp2p.libp2p?.getPeers().length >= minDialAmount) { + LOGGER.info(`Connected to ${minDialAmount} peers! Moving on!`) + return true + } + + return false + } + + dialed = hasDialedEnough(user) + + if (!initialized || !dialed) { + const waitIntervalMs = 500 + const waitTimeMs = 60_000 + const waitEndTimeMs = Date.now() + waitTimeMs + LOGGER.info(`Waiting for user ${username} to be ready!`) + while ((!initialized || !dialed) && Date.now() < waitEndTimeMs) { + process.stdout.write('-') + await sleep(waitIntervalMs) + if (!dialed) { + dialed = hasDialedEnough(user) + } + } + if (!initialized) { + throw new Error(`Failed to finish loading chain for ${username} in ${waitTimeMs}ms timeout`) + } + console.log('\n') + } + + return user +} \ No newline at end of file diff --git a/demos/quiet-sandbox/src/scripts/test_auth.ts b/demos/quiet-sandbox/src/scripts/test_auth.ts deleted file mode 100644 index be0f5921..00000000 --- a/demos/quiet-sandbox/src/scripts/test_auth.ts +++ /dev/null @@ -1,87 +0,0 @@ -#! /usr/bin/env ts-node - -import { SigChain } from "../auth/chain.js"; - -import figlet from 'figlet' -import { RoleName } from "../auth/services/roles/roles.js"; -import { EncryptionScopeType } from "../auth/services/crypto/types.js"; -import { UserService } from "../auth/services/members/userService.js"; - -console.log(figlet.textSync('Quiet Sandbox')); - -const teamName = 'test-team' -const username = 'isla' -const { context, sigChain } = SigChain.create(teamName, username) - -console.log('\n---- USER ----\n') -console.log(`ID: ${context.user.userId}`) -console.log(`Name: ${context.user.userName}`) -console.log(`Keys: ${JSON.stringify(context.user.keys, null, 2)}`) - -console.log('\n---- Team ----\n') -console.log(`Members: ${JSON.stringify(sigChain.users.getAllMembers(), null, 2)}`) -console.log(`Role keys: ${JSON.stringify(sigChain.team.roleKeys(RoleName.MEMBER), null, 2)}`) - -const dmId = sigChain.dms.create([context.user.userId]) -const keys = sigChain.dms.getDmKeysById(dmId) - -console.log('\n---- DM ----\n') -console.log(`DM ID: ${dmId}`) -console.log(`DM Keys: ${JSON.stringify(keys, null, 2)}`) - -const channelName = 'some-channel' -sigChain.channels.createPrivateChannel(channelName, context) -const encryptedAndSignedChannel = sigChain.crypto.encryptAndSign('foobar', { type: EncryptionScopeType.CHANNEL, name: channelName }, context) - -console.log('\n---- Channels ----\n') -console.log(`Channel Keys: ${JSON.stringify(sigChain.crypto.getKeysForChannel(channelName), null, 2)}`) -console.log(`Encrypted and signed: ${JSON.stringify(encryptedAndSignedChannel, null, 2)}`) -console.log(`Decrypted: ${sigChain.crypto.decryptAndVerify(encryptedAndSignedChannel.encrypted, encryptedAndSignedChannel.signature, context)}`) - -const invitation = sigChain.invites.create() - -console.log('\n---- Invite ----\n') -console.log(`Invite Result: ${JSON.stringify(invitation, null, 2)}`) - -let invitationState = sigChain.invites.getById(invitation.id) - -console.log(`Invite State (Active): ${JSON.stringify(invitationState, null, 2)}`) - -sigChain.invites.revoke(invitation.id) -invitationState = sigChain.invites.getById(invitation.id) - -console.log(`Invite State (Revoked): ${JSON.stringify(invitationState, null, 2)}`) - -const newInvitation = sigChain.invites.create() - -console.log('\n---- Invite With Proof ----\n') -console.log(`Invite Result: ${JSON.stringify(newInvitation, null, 2)}`) - -const newUsername = 'isntla' -const prospectiveMember = UserService.createFromInviteSeed(newUsername, newInvitation.seed) - -console.log(`Prospective Member: ${JSON.stringify(prospectiveMember, null, 2)}`) - -sigChain.invites.admitMemberFromInvite( - prospectiveMember.inviteProof, - prospectiveMember.context.user.userName, - prospectiveMember.context.user.userId, - prospectiveMember.publicKeys -) - -const { - context: newUsersContext, - sigChain: newUsersChain -} = SigChain.join( - prospectiveMember.context, - sigChain.persist(), - sigChain.team.teamKeyring() -) - -console.log(`All keys for new user: ${JSON.stringify(newUsersChain.users.getKeys(), null, 2)}`) - -console.log(`Members: ${JSON.stringify(newUsersChain.users.getAllMembers(), null, 2)}`) - -const encryptedAndSigned = newUsersChain.crypto.encryptAndSign('foobar', { type: EncryptionScopeType.ROLE, name: RoleName.MEMBER }, newUsersContext) -console.log(`Encrypted and signed: ${JSON.stringify(encryptedAndSigned, null, 2)}`) -console.log(`Decrypted: ${newUsersChain.crypto.decryptAndVerify(encryptedAndSigned.encrypted, encryptedAndSigned.signature, newUsersContext)}`) \ No newline at end of file diff --git a/demos/quiet-sandbox/src/types/orbitdb.d.ts b/demos/quiet-sandbox/src/types/orbitdb.d.ts index b0d42ef3..60e8d909 100644 --- a/demos/quiet-sandbox/src/types/orbitdb.d.ts +++ b/demos/quiet-sandbox/src/types/orbitdb.d.ts @@ -4,6 +4,21 @@ declare module "@orbitdb/core" { import EventEmitter from "events"; import type { Helia } from "helia"; + export type Database = { + type: string; + addOperation: (args: { + op: string; + key: string | null; + value: unknown; + }) => Promise; + address: string; + close(): Promise; + drop(): Promise; + events: EventEmitter; + access: AccessController; + log: Log; + }; + export function Database(args: { ipfs: Helia; identity?: Identity; @@ -18,20 +33,7 @@ declare module "@orbitdb/core" { referencesCount?: number; syncAutomatically?: boolean; onUpdate?: () => void; - }): Promise<{ - type: string; - addOperation: (args: { - op: string; - key: string | null; - value: unknown; - }) => Promise; - address: string; - close(): Promise; - drop(): Promise; - events: EventEmitter; - access: AccessController; - log: Log; - }>; + }): Promise; export type Identity = { id: string; @@ -194,8 +196,8 @@ declare module "@orbitdb/core" { getPublic } - export type Events = { - type: "keyvalue" + export type Events = Database & { + type: "events" address: string add(value: unknown): Promise all(): Promise<{ key: string; value: unknown; hash: string }[]> @@ -205,4 +207,12 @@ declare module "@orbitdb/core" { access: AccessController log: Log } + + export type Documents = Database & { + type: "documents" + put(doc: any): Promise + del(key: string): Promise + get(key: string): Promise + all(): Promise + } } diff --git a/demos/quiet-sandbox/src/utils/logger/libp2pLogger.ts b/demos/quiet-sandbox/src/utils/logger/libp2pLogger.ts new file mode 100644 index 00000000..7eca2e88 --- /dev/null +++ b/demos/quiet-sandbox/src/utils/logger/libp2pLogger.ts @@ -0,0 +1,130 @@ +import type { PeerId } from '@libp2p/interface' +import { CallableQuietLogger, createQuietLogger, QuietLogger } from './logger.js' + + +export interface QuietLibp2pLogger { + (formatter: string, ...args: any[]): void + error(formatter: string, ...args: any[]): void + trace(formatter: any, ...args: any[]): void + enabled: boolean +} + +export interface ComponentLogger { + forComponent(name: string): CallableQuietLogger +} + +export interface PeerLoggerOptions { + prefixLength: number + suffixLength: number +} + +/** + * Create a component logger that will prefix any log messages with a truncated + * peer id. + * + * @example + * + * ```TypeScript + * import { peerLogger } from '@libp2p/logger' + * import { peerIdFromString } from '@libp2p/peer-id' + * + * const peerId = peerIdFromString('12D3FooBar') + * const logger = peerLogger(peerId) + * + * const log = logger.forComponent('my-component') + * log.info('hello world') + * // logs "12…oBar:my-component hello world" + * ``` + */ +export function peerLogger (peerId: PeerId, options: Partial = {}): ComponentLogger { + return prefixLogger(peerId.toString()) +} + +/** + * Create a component logger that will prefix any log messages with the passed + * string. + * + * @example + * + * ```TypeScript + * import { prefixLogger } from '@libp2p/logger' + * + * const logger = prefixLogger('my-node') + * + * const log = logger.forComponent('my-component') + * log.info('hello world') + * // logs "my-node:my-component hello world" + * ``` + */ +export function prefixLogger (prefix: string): ComponentLogger { + return { + forComponent (name: string) { + return logger(`${prefix}:${name}`)() + } + } +} + +export function suffixLogger (suffix: string): ComponentLogger { + return { + forComponent (name: string) { + return logger(`${name}:${suffix}`)() + } + } +} + +/** + * Create a component logger + * + * @example + * + * ```TypeScript + * import { defaultLogger } from '@libp2p/logger' + * import { peerIdFromString } from '@libp2p/peer-id' + * + * const logger = defaultLogger() + * + * const log = logger.forComponent('my-component') + * log.info('hello world') + * // logs "my-component hello world" + * ``` + */ +export function defaultLogger (): ComponentLogger { + return { + forComponent (name: string) { + return logger(name)() + } + } +} + +/** + * Creates a logger for the passed component name. + * + * @example + * + * ```TypeScript + * import { logger } from '@libp2p/logger' + * + * const log = logger('my-component') + * log.info('hello world') + * // logs "my-component hello world" + * ``` + */ +export function logger (name: string): () => CallableQuietLogger { + return (): CallableQuietLogger => { + const LOGGER = createQuietLogger(name)() + + const makeCallable = (logger: QuietLogger): CallableQuietLogger => { + const callable = { + LOGGER: logger, + enabled: logger.enabled, + error: (message: any, ...optionalParams: any[]) => logger.error(message, ...optionalParams), + trace: (message: any, ...optionalParams: any[]) => logger.trace(message, ...optionalParams) + } + const func = (message: any, ...optionalParams: any[]) => logger.info(message, ...optionalParams) + return Object.assign(func, callable) + } + + + return makeCallable(LOGGER) + } +} diff --git a/demos/quiet-sandbox/src/utils/logger/logQueue.ts b/demos/quiet-sandbox/src/utils/logger/logQueue.ts new file mode 100644 index 00000000..1e774aef --- /dev/null +++ b/demos/quiet-sandbox/src/utils/logger/logQueue.ts @@ -0,0 +1,90 @@ +import { EventEmitter } from 'events' +import fastq, { queueAsPromised } from 'fastq' +import { randomUUID } from 'crypto' +import { createQuietLogger, LogLevel } from './logger.js' + +const DEFAULT_CHUNK_SIZE = 10 +export const DEFAULT_NUM_TRIES = 2 + +export type LogMessage = { + message: string[] + level: LogLevel +} + +type ProcessTask = { + data: LogMessage + tries: number + taskId: string +} + +export class LogQueue extends EventEmitter { + private static _instance: LogQueue + private LOGGER = createQuietLogger("log:queue", false, "test.log")() + + private taskQueue: queueAsPromised + + constructor(private chunkSize: number) { + super() + + this.taskQueue = fastq.promise(this, this.writeLogMessage, this.chunkSize) + } + + public static init(chunkSize: number = DEFAULT_CHUNK_SIZE): LogQueue { + if (LogQueue._instance != null) { + throw new Error("Can only init one instance of LogQueue") + } + + LogQueue._instance = new LogQueue(chunkSize) + return LogQueue._instance + } + + public async addToTaskQueue(task: ProcessTask): Promise + public async addToTaskQueue(item: LogMessage): Promise + public async addToTaskQueue(itemOrTask: LogMessage | ProcessTask): Promise { + if (!itemOrTask) { + this.LOGGER.error('Item/task is null or undefined, skipping!') + return + } + + let task: ProcessTask + if ((itemOrTask as ProcessTask).taskId != null) { + task = itemOrTask as ProcessTask + } else { + task = { data: itemOrTask as LogMessage, tries: 0, taskId: randomUUID() } + } + + try { + const success = await this.pushToQueueAndRun(task) + if (!success) { + await this.pushToQueueAndRun({ ...task, tries: task.tries + 1 }) + } + } catch (e) { + this.LOGGER.error(`Error occurred while adding new task ${task.taskId} with data ${task.data} to the queue`, e) + } + } + + public async writeLogMessage({ taskId, data }: ProcessTask): Promise { + let success: boolean = false + try { + //@ts-ignore + this.LOGGER.printLog(data.level, ...data.message) + success = true + } catch (e) { + this.LOGGER.error(`Processing task ${taskId} with data ${data} failed`, e) + } + return success + } + + private async pushToQueueAndRun(task: ProcessTask): Promise { + const success = await this.taskQueue.push(task) + return success + } + + static get instance(): LogQueue { + if (LogQueue._instance == null) { + throw new Error("Must run init to initialize the LogQueue before accessing it") + } + + return LogQueue._instance + } +} diff --git a/demos/quiet-sandbox/src/utils/logger/logger.ts b/demos/quiet-sandbox/src/utils/logger/logger.ts new file mode 100644 index 00000000..8c3f9e28 --- /dev/null +++ b/demos/quiet-sandbox/src/utils/logger/logger.ts @@ -0,0 +1,628 @@ +import debug from 'debug' +import { Console } from 'console' + +//@ts-ignore +import colors from 'ansi-colors' +import { DateTime } from 'luxon' +import { isPeerId } from '@libp2p/interface' +import { CID } from 'multiformats' +import type { Key } from 'interface-datastore' +import { isMultiaddr } from '@multiformats/multiaddr' +import { createHash } from 'crypto' +import * as fs from 'fs' +import * as util from 'util' +import { base32 } from 'multiformats/bases/base32' +import { base58btc } from 'multiformats/bases/base58' +import { base64 } from 'multiformats/bases/base64' +import type { PeerId } from '@libp2p/interface' +import type { Multiaddr } from '@multiformats/multiaddr' + +import { findAllByKeyAndReplace } from '../utils.js' +import { LogMessage, LogQueue } from './logQueue.js' + +const COLORIZE = process.env['COLORIZE'] === 'true' + +export type CreateLoggerFunction = (moduleName: string) => QuietLogger + +export type CallableQuietLogger = { + (message: any, ...optionalParams: any[]): void; + error(formatter: string, ...args: any[]): void + trace(formatter: any, ...args: any[]): void + enabled: boolean + LOGGER: QuietLogger +} + +/** + * Available log levels + */ +export enum LogLevel { + DEBUG = 'debug', + ERROR = 'error', + INFO = 'info', + LOG = 'log', + WARN = 'warn', + TIMER = 'timer', + TRACE = 'trace' +} + +/** + * This determines the color scheme of each log type + */ +//@ts-ignore +colors.theme({ + // debug + debug: colors.bold.cyan, + debug_text: colors.cyan, + + // log + log: colors.bold.gray, + log_text: colors.gray, + + // info + info: colors.bold.blue, + info_text: colors.blue, + + // warn + warn: colors.bold.yellow, + warn_text: colors.yellow, + + // error + error: colors.bold.redBright, + error_text: colors.redBright, + + // timers + timer: colors.bold.yellowBright, + timer_text: colors.yellowBright, + + // trace + trace: colors.bold.gray, + trace_text: colors.gray, + + // misc + scope: colors.magenta, + date: colors.bold.gray, + object: colors.green, + object_error: colors.red, +}) + +/** + * This is the base logger we use to write to the node terminal. Due to the ways that we import the node logger + * we have to account for that (hence the ternary statement). + */ +export const nodeConsoleLogger = Console instanceof Function ? new Console(process.stdout, process.stderr) : console + +// setup our custom string formatters +const formatters: { [char: string]: (value?: any) => string } = {} + +// Add a formatter for converting to a base58 string +formatters.b = (v?: Uint8Array): string => { + return v == null ? 'undefined' : base58btc.baseEncode(v) +} + +// Add a formatter for converting to a base32 string +formatters.t = (v?: Uint8Array): string => { + return v == null ? 'undefined' : base32.baseEncode(v) +} + +// Add a formatter for converting to a base64 string +formatters.m = (v?: Uint8Array): string => { + return v == null ? 'undefined' : base64.baseEncode(v) +} + +// Add a formatter for stringifying peer ids +formatters.p = (v?: PeerId): string => { + return v == null ? 'undefined' : v.toString() +} + +// Add a formatter for stringifying CIDs +formatters.c = (v?: CID): string => { + return v == null ? 'undefined' : v.toString() +} + +// Add a formatter for stringifying Datastore keys +formatters.k = (v: Key): string => { + return v == null ? 'undefined' : v.toString() +} + +// Add a formatter for stringifying Multiaddrs +formatters.a = (v?: Multiaddr): string => { + return v == null ? 'undefined' : v.toString() +} + +formatters.d = (v?: number): string => { + return v == null ? 'NaN' : v.toString() +} + +formatters.s = (v?: string): string => { + return v == null ? 'undefined' : v +} + +formatters.o = (v?: any): string => { + return v == null ? 'undefined' : JSON.stringify(v, null, 2) +} + +// // Add a formatter for converting to a base58 string +// debug.formatters.b = (v?: Uint8Array): string => { +// return v == null ? 'undefined' : base58btc.baseEncode(v) +// } + +// // Add a formatter for converting to a base32 string +// debug.formatters.t = (v?: Uint8Array): string => { +// return v == null ? 'undefined' : base32.baseEncode(v) +// } + +// // Add a formatter for converting to a base64 string +// debug.formatters.m = (v?: Uint8Array): string => { +// return v == null ? 'undefined' : base64.baseEncode(v) +// } + +// // Add a formatter for stringifying peer ids +// debug.formatters.p = (v?: PeerId): string => { +// return v == null ? 'undefined' : v.toString() +// } + +// // Add a formatter for stringifying CIDs +// debug.formatters.c = (v?: CID): string => { +// return v == null ? 'undefined' : v.toString() +// } + +// // Add a formatter for stringifying Datastore keys +// debug.formatters.k = (v: Key): string => { +// return v == null ? 'undefined' : v.toString() +// } + +// // Add a formatter for stringifying Multiaddrs +// debug.formatters.a = (v?: Multiaddr): string => { +// return v == null ? 'undefined' : v.toString() +// } + +/** + * This class is what we use to log to the node console and, optionally, the native console for browser-facing code + * like the desktop renderer + * + * NOTE: This is exported because it needs to be exposed for the logger to work but you should use `createQuietLogger` in + * (probably) all contexts + */ +export class QuietLogger { + // This is based on the `debug` package and is backwards-compatible with the old logger's behavior (for the most part) + private isDebug: boolean + private isTrace: boolean + private timers: Map = new Map() + private appendableData: any | undefined + private fileStream: fs.WriteStream | undefined + + /** + * + * @param name This is the name that will be printed in the log entry + * @param parallelConsoleLog If true we will also log to the native console (e.g. browser console) + */ + constructor( + public name: string, + public writeToQueue: boolean = false, + private filename: string | undefined = undefined + ) { + this.isDebug = debug.enabled(name) + this.isTrace = debug.enabled(`${name}:trace`) && debug.names.map((r: any) => r.toString()).find((n: string) => n.includes(':trace')) != null + if (this.filename != null) { + this.fileStream = fs.createWriteStream(this.filename, { flags: 'a' }); + } + } + + /* + Log Level Methods + */ + + /** + * Log a debug-level message if the DEBUG environment variable is set for this package/module + * + * @param message Message to log + * @param optionalParams Optional parameters to log + */ + debug(message: any, ...optionalParams: any[]) { + if (!this.isDebug) return + + this.callLogMethods(LogLevel.DEBUG, message, ...optionalParams) + } + + /** + * Log an error-level message + * + * @param message Message to log + * @param optionalParams Optional parameters to log + */ + error(message: any, ...optionalParams: any[]) { + this.callLogMethods(LogLevel.ERROR, message, ...optionalParams) + } + + /** + * Log an info-level message + * + * @param message Message to log + * @param optionalParams Optional parameters to log + */ + info(message: any, ...optionalParams: any[]) { + if (!this.isDebug) return + + this.callLogMethods(LogLevel.INFO, message, ...optionalParams) + } + + /** + * Log a log-level message if the DEBUG environment variable is set for this package/module + * + * @param message Message to log + * @param optionalParams Optional parameters to log + */ + log(message: any, ...optionalParams: any[]) { + if (!this.isDebug) return + + this.callLogMethods(LogLevel.LOG, message, ...optionalParams) + } + + /** + * Log a warn-level message + * + * @param message Message to log + * @param optionalParams Optional parameters to log + */ + warn(message: any, ...optionalParams: any[]) { + this.callLogMethods(LogLevel.WARN, message, ...optionalParams) + } + + /** + * Log a trace-level message + * + * @param message Message to log + * @param optionalParams Optional parameters to log + */ + trace(message: any, ...optionalParams: any[]) { + if (!this.isTrace) return + + this.callLogMethods(LogLevel.TRACE, message, ...optionalParams) + } + + /** + * Start a timer with a given name + * + * @param name Name of the timer + */ + time(name: string) { + if (this.timers.has(name)) { + this.warn(`Timer with name ${name} already exists!`) + return + } + + const startMs = DateTime.utc().toMillis() + this.timers.set(name, startMs) + } + + /** + * Calculate the runtime of the timer with a given name and log the formatted timing message + * + * @param name Name of the timer + */ + timeEnd(name: string) { + if (!this.timers.has(name)) { + this.warn(`No timer started with name ${name}!`) + return + } + + const endMs = DateTime.utc().toMillis() + const startMs = this.timers.get(name)! + this.timers.delete(name) + + const formattedLogStrings = this.formatLog(LogLevel.TIMER, name, `${endMs - startMs}ms - timer ended`) + this.printLog(LogLevel.LOG, ...formattedLogStrings) + } + + setAppendData(data: any, overwrite: boolean = true) { + if (!overwrite && this.appendableData != null) { + this.appendableData = { + ...this.appendableData, + ...data + } + } else { + this.appendableData = data + } + } + + extend(name: string) { + this.name = `${this.name}:${name}` + } + + get enabled(): boolean { + return this.isDebug + } + + // makeCallable(): () => CallableQuietLogger { + // const callable: Partial = { + // LOGGER: this + // } + + // callable.enabled = callable.LOGGER!.enabled + // callable.error = callable.LOGGER!.error + // callable.trace = callable.LOGGER!.trace + // const func = (message: any, ...optionalParams: any[]) => callable.LOGGER!.info(message, ...optionalParams) + + // return func & this + // } + + /** + * Formats the message and writes it out to the node logger and, optionally, to the native console with + * colorized text and parameters + * + * NOTE: The text and optional parameter are printed in different colors for clarity when reading a given log + * line + * + * @param level The level we are logging at + * @param message The main log message + * @param optionalParams Other parameters we want to log + */ + private callLogMethods(level: LogLevel, message: any, ...optionalParams: any[]): void { + const formattedLogStrings = this.formatLog(level, message, ...optionalParams) + this.printLog(level, ...formattedLogStrings) + } + + /** + * Print logs to node console and, optionally, the native console (e.g. browser) + * + * @param level The level we are logging at + * @param formattedLogStrings Array of formatted log strings + */ + public printLog(level: LogLevel, ...formattedLogStrings: string[]): void { + if (!this.writeToQueue) { + if (this.fileStream != null) { + this.fileStream.write(formattedLogStrings.join(' ') + '\n'); + } + + if ([LogLevel.WARN, LogLevel.ERROR].includes(level) || formattedLogStrings[0].includes('qsb:')) { + // @ts-ignore + nodeConsoleLogger[level](...formattedLogStrings) + } + + return + } + + const queuable: LogMessage = { + message: formattedLogStrings, + level + } + LogQueue.instance.addToTaskQueue(queuable) + } + + /** + * Format the message and optional parameters according to the formatting rules for a given log level + * + * @param level The level we are logging at + * @param message The main log message + * @param optionalParams Other parameters we want to log + * @returns Array of formatted log strings + */ + private formatLog(level: LogLevel, message: any, ...optionalParams: any[]): string[] { + const { formatted, params } = this.formatMessage(message, level, ...optionalParams) + const formattedMessage = [formatted] + const formattedAppendableData = this.formatAppendableData() + if (formattedAppendableData != null) { + formattedMessage.push(formattedAppendableData) + } + const formattedOptionalParams = params.map((param: any) => this.formatObject(param)) + return [...formattedMessage, ...formattedOptionalParams] + } + + /** + * Formats the primary log message and applies the level-specific coloring + * + * @param message Primary message to log + * @param level The level we are logging at + * @returns A colorized log string + */ + private formatMessage(message: any, level: string, ...optionalParams: any[]): { formatted: string, params: any[] } { + let formattedLevel = level.toUpperCase() + let scope = this.name + let date = DateTime.utc().toISO() + const { formatted, params } = this.formatMessageText(message, level, ...optionalParams) + + if (COLORIZE) { + //@ts-ignore + formattedLevel = colors[level](formattedLevel) + //@ts-ignore + scope = colors['scope'](scope) + //@ts-ignore + date = colors['date'](date) + } + + return { + formatted: `${date} ${formattedLevel} ${scope} ${formatted}`, + params + } + } + + private formatMessageText(message: any, level: string, ...optionalParams: any[]): { formatted: string, params: any[] } { + if (['string', 'number', 'boolean', 'bigint'].includes(typeof message)) { + let formatted = message + let params: any[] = optionalParams + if (typeof message === 'string') { + const withFormatters = this.applyFormatters(formatted, ...optionalParams) + formatted = withFormatters.formatted + params = withFormatters.params + } + + if (COLORIZE) { + //@ts-ignore + formatted = colors[`${level}_text`](message) + } + + return { + formatted, + params + } + } + + return { + formatted: this.formatObject(message, level), + params: optionalParams + } + } + + // stolen from the debug package and retooled + private applyFormatters(message: string, ...optionalParams: any[]): { formatted: string, params: any[] } { + let index = 0; + const formatted = message.replace(/%([a-zA-Z%])/g, (match, format) => { + // If we encounter an escaped % then don't increase the array index + if (match === '%%') { + return '%'; + } + if (index > 0) index++; + const formatter = formatters[format] + if (typeof formatter === 'function') { + const val = optionalParams[index]; + match = formatter(val) + + // Now we need to remove `args[index]` since it's inlined in the `format` + optionalParams.splice(index, 1); + if (index > 0) index--; + } + return match; + }); + + return { + formatted, + params: optionalParams + } + } + + /** + * Colorizes an object parameter based on its type. + * - Errors are printed in red and we attempt to log the full stacktrace + * - Objects are stringified and logged + * - All other types are logged as-is + * + * @param param Object to format + * @returns Colorized string + */ + private formatObject(param: any, overrideColorKey: string | undefined = undefined): string { + if (param instanceof Error) { + let formattedError = param.stack || `${param.name}: ${param.message}` + if (COLORIZE) { + //@ts-ignore + formattedError = colors[overrideColorKey || 'object_error'](formattedError) + } + return formattedError + } + + const colorize = (stringifiedParam: string): string => { + //@ts-ignore + return COLORIZE ? colors[overrideColorKey || 'object'](stringifiedParam) : stringifiedParam + } + + let formatted: string + if (['string', 'number', 'boolean', 'bigint'].includes(typeof param)) { + formatted = param + } else if (param == null) { + formatted = "undefined" + } else if (isPeerId(param)) { + formatted = param.toString() + } else if (param instanceof CID) { + formatted = param.toString() + } else if ((param as Key).baseNamespace != null) { + formatted = param.toString() + } else if (isMultiaddr(param)) { + formatted = param.toString() + } else { + try { + let truncatedOrNot: string + if ((param as ArrayLike).length != undefined) { + truncatedOrNot = param + } else { + truncatedOrNot = this.truncateMessageForLogging(param) + } + formatted = JSON.stringify(truncatedOrNot, null, 2) + } catch(e) { + formatted = param.toString() + if (formatted.startsWith('[object')) { + formatted = param + } + } + } + + return colorize(formatted) + } + + private formatAppendableData(): string | undefined { + if (this.appendableData != null) { + return JSON.stringify(this.appendableData, null, 2) + } + + return undefined + } + + private truncateMessageForLogging(obj: any): string { + return findAllByKeyAndReplace(JSON.parse(JSON.stringify(obj)), [ + { + key: 'data', + replace: { + replacerFunc: (dataArray: any[]) => { + if (dataArray.length != undefined) { + return Buffer.from(dataArray).toString('base64') + } + return dataArray + } + } + }, + { + key: 'serializedGraph', + replace: { + replacerFunc: (graphObj: any) => createHash('md5').update(JSON.stringify(graphObj)).digest('hex') + } + }, + { + key: 'encryptedBody', + replace: { + replacerFunc: (body: any) => { + if (body["0"] != null) { + return createHash('md5').update(JSON.stringify(body)).digest('hex') + } + return body + } + } + }, + { + key: 'encryptedPayload', + replace: { + replacerFunc: (body: any) => { + if (body["0"] != null) { + return createHash('md5').update(JSON.stringify(body)).digest('hex') + } + return body + } + } + } + ]) + } +} + +/** + * Generate a function that creates a module-level logger with a name like `packageName:moduleName`. This is the main + * entry point for logging in Quiet. + * + * @param packageName Name of the package we are logging in + * @param writeToQueue If true we will write the message to queue for processing + * @returns A function that can be used to generate a module-level logger + */ +export const createQuietLogger = ( + packageName: string, + writeToQueue: boolean = true, + filename: string | undefined = undefined +): ((moduleName?: string | undefined) => QuietLogger) => { + return (moduleName?: string | undefined) => { + let name: string + if (moduleName == null) { + name = packageName + } else { + name = `${packageName}:${moduleName}` + } + nodeConsoleLogger.info(`Initializing logger ${name}`) + return new QuietLogger(name, writeToQueue, filename) + } +} + +export const createQsbLogger = createQuietLogger("qsb") diff --git a/demos/quiet-sandbox/src/utils/utils.ts b/demos/quiet-sandbox/src/utils/utils.ts index 25aea215..9b9a25f2 100644 --- a/demos/quiet-sandbox/src/utils/utils.ts +++ b/demos/quiet-sandbox/src/utils/utils.ts @@ -1 +1,42 @@ -export const sleep = (ms: number) => new Promise(r => setTimeout(r, ms)); \ No newline at end of file +export const sleep = (ms: number) => new Promise(r => setTimeout(r, ms)); + +export type ReplacerFunc = (originalValue: any) => T +export type ReplacerConfig = { newValue?: T, replacerFunc?: ReplacerFunc } +export type KeyValueReplacerConfig = { key: string, replace: ReplacerConfig } +export type ReplacerMap = Map> + +const getReplacerForKey = ({ key, replace }: KeyValueReplacerConfig): ReplacerFunc => { + if (replace.newValue == null && replace.replacerFunc == null) { + throw new Error(`Must provide a replacement value or a replacement function!`) + } + + const replacerFunc = replace.newValue != null ? (originalValue: any): T => replace.newValue! : replace.replacerFunc! + return replacerFunc +} + +const populateReplacerFuncs = (replacers: KeyValueReplacerConfig[]): ReplacerMap => { + const replacerMap: ReplacerMap = new Map() + for (const replacerConfig of replacers) { + replacerMap.set(replacerConfig.key, getReplacerForKey(replacerConfig)) + } + + return replacerMap +} + +export const findAllByKeyAndReplace = (object: any, replacers: KeyValueReplacerConfig[]) => { + const replacerMap = populateReplacerFuncs(replacers) + const newObject = { ...object } + const looper = (obj: any) => { + for (let k in obj){ + if(replacerMap.has(k)){ + obj[k] = replacerMap.get(k)!(obj[k]) + } + if(typeof obj[k] === "object"){ + looper(obj[k]) + } + } + } + looper(newObject) + + return newObject +} \ No newline at end of file diff --git a/package.json b/package.json index 0302a751..77b85c53 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "type": "module", "scripts": { "build": "pnpm -r build", + "qsb:perf:isla": "pnpm -F @localfirst/quiet-sandbox test:perf:isla", "dev:qsb": "pnpm -F @localfirst/quiet-sandbox start", "dev:qsb:command": "pnpm -F @localfirst/quiet-sandbox run-command", "dev:demo": "pnpm -F @localfirst/automerge-repo-todos dev", diff --git a/packages/auth/src/connection/Connection.ts b/packages/auth/src/connection/Connection.ts index 84411a27..5bc47161 100644 --- a/packages/auth/src/connection/Connection.ts +++ b/packages/auth/src/connection/Connection.ts @@ -97,12 +97,39 @@ export class Connection extends EventEmitter { readonly #messageQueue: MessageQueue #started = false #log = debug.extend('auth:connection') + #sharedLogger: any | undefined + private LOG = (level: 'info' | 'warn' | 'error', message: any, ...params: any[]) => { + if (this.#sharedLogger == null) { + this.#log(message, params) + return + } + + switch (level) { + case 'info': + this.#sharedLogger.info(message, ...params) + break + case 'warn': + this.#sharedLogger.warn(message, ...params) + break + case 'error': + this.#sharedLogger.error(message, ...params) + break + default: + throw new Error(`Unknown log level ${level}`) + } + } - constructor({ sendMessage, context }: ConnectionParams) { + + constructor({ sendMessage, context, createLogger }: ConnectionParams) { super() - this.#messageQueue = this.#initializeMessageQueue(sendMessage) - this.#log = this.#log.extend(getUserName(context)) // add our name to the debug logger + const username = getUserName(context) + if (createLogger != null) { + this.#sharedLogger = createLogger(`auth:connection:${username}`) + } + + this.#messageQueue = this.#initializeMessageQueue(sendMessage, createLogger, username) + this.#log = this.#log.extend(username) // add our name to the debug logger // On sync server, the server keys act as both user keys and device keys const initialContext = isServerContext(context) ? extendServerContext(context) : context @@ -245,9 +272,13 @@ export class Connection extends EventEmitter { const { deviceId } = theirIdentityClaim const theirDevice = team.device(deviceId, { includeRemoved: true }) const peer = team.memberByDeviceId(deviceId, { includeRemoved: true }) + this.LOG("info", "Found the following device and member information", theirDevice, peer) // we now have a user name so add that to the debug logger this.#log = this.#log.extend(peer.userName) + if (this.#sharedLogger != null) { + this.#sharedLogger.extend(peer.userName) + } // send them an identity challenge const challenge = identity.challenge({ type: KeyType.DEVICE, name: deviceId }) @@ -287,10 +318,10 @@ export class Connection extends EventEmitter { // Undefined message means we're already synced if (syncMessage) { - this.#log('sending sync message', syncMessageSummary(syncMessage)) + this.LOG('info', 'sending sync message', syncMessageSummary(syncMessage)) this.#queueMessage('SYNC', syncMessage) } else { - this.#log('no sync message to send') + this.LOG('info', 'no sync message to send') } return { syncState } @@ -318,9 +349,11 @@ export class Connection extends EventEmitter { ) if (headsAreEqual(newChain.head, team.graph.head)) { + // console.log(`${context!.user!.userName}: Sync message received but heads were equal`) // nothing changed return { syncState } } else { + // console.log(`${context!.user!.userName}: Sync message received and merging`) this.emit('updated', newChain.head) return { team: team.merge(newChain), syncState } } @@ -334,7 +367,7 @@ export class Connection extends EventEmitter { const recipientPublicKey = peer!.keys.encryption const senderSecretKey = user!.keys.encryption.secretKey - this.#log(`encrypting seed with key ${recipientPublicKey}`) + this.LOG('info', `encrypting seed with key ${recipientPublicKey}`) const encryptedSeed = asymmetric.encryptBytes({ secret: seed, recipientPublicKey, @@ -364,7 +397,7 @@ export class Connection extends EventEmitter { return { sessionKey: deriveSharedKey(seed, theirSeed) } } catch (error) { if (String(error).includes('incorrect key pair')) { - this.#log(`failed to decrypt seed using public key ${senderPublicKey}`, error) + this.LOG('error', `failed to decrypt seed using public key ${senderPublicKey}`, error) return this.#fail(ENCRYPTION_FAILURE) } else throw error } @@ -382,7 +415,8 @@ export class Connection extends EventEmitter { this.emit('message', decryptedMessage) } catch (error) { if (String(error).includes('wrong secret key')) { - this.#log( + this.LOG( + 'error', `failed to decrypt message using session key ${base58.encode(sessionKey)}`, error ) @@ -400,15 +434,15 @@ export class Connection extends EventEmitter { receiveError: assign(({ event }) => { assertEvent(event, 'ERROR') const error = event.payload - this.#log('receiveError: %o', error) - this.emit('remoteError', error) + this.LOG('error', 'receiveError', error) + this.LOG('error', 'remoteError', error) return { error } }), sendError: assign(({ event }) => { assertEvent(event, 'LOCAL_ERROR') const error = event.payload - this.#log('sendError %o', error) + this.LOG('error', 'sendError', error) this.#messageQueue.send(createErrorMessage(error.type, 'REMOTE')) this.emit('localError', error) return { error } @@ -416,7 +450,10 @@ export class Connection extends EventEmitter { // EVENTS FOR EXTERNAL LISTENERS - onConnected: () => this.emit('connected'), + onConnected: (context) => { + this.emit('connected') + // this.#machine.send({ type: 'SYNC', payload: { head } }) // Send update event to local machine + }, onDisconnected: ({ event }) => this.emit('disconnected', event), }, @@ -453,7 +490,22 @@ export class Connection extends EventEmitter { const { theirIdentityClaim } = context // This is only for existing members (authenticating with deviceId rather than invitation) assert(isMemberClaim(theirIdentityClaim!)) - return !context.team!.hasDevice(theirIdentityClaim.deviceId, { includeRemoved: true }) + const deviceMissing = !context.team!.hasDevice(theirIdentityClaim.deviceId, { includeRemoved: true }) + if (deviceMissing) { + this.LOG("error", `Device ${theirIdentityClaim.deviceId} was unknown and these were the members we had`, context.team!.members().map((member) => { + return { + name: member.userName, + id: member.userId, + devices: member.devices?.map((device) => { + return { + id: device.deviceId, + name: device.deviceName + } + }) + } + })) + } + return deviceMissing }, identityIsValid: ({ context, event }) => { @@ -654,6 +706,7 @@ export class Connection extends EventEmitter { // Synchronize our team graph with the peer synchronizing: { + id: 'sync', entry: 'sendSyncMessage', always: { guard: 'headsAreEqual', target: 'connected' }, on: { SYNC: { actions: ['receiveSyncMessage', 'sendSyncMessage'] } }, @@ -700,17 +753,20 @@ export class Connection extends EventEmitter { next: state => { const summary = stateSummary(state.value as string) this.emit('change', summary) - this.#log(`⏩ ${summary} `) + this.LOG('info', `⏩ ${summary} `) }, error: error => { - console.error('Connection encountered an unhandled error', error) + this.LOG('error', 'Connection encountered an unhandled error', error) this.#fail(UNHANDLED) }, }) // add automatic logging to all events this.emit = (event, ...args) => { - this.#log(`emit ${event} %o`, ...args) + for (const arg of args) { + this.LOG('info', typeof arg) + } + this.LOG('info', `emit ${event}`, ...args) return super.emit(event, ...args) } } @@ -719,7 +775,7 @@ export class Connection extends EventEmitter { /** Starts the state machine. Returns this Connection object. */ public start = (storedMessages: Uint8Array[] = []) => { - this.#log('starting') + this.LOG('info', 'starting') this.#machine.start() this.#messageQueue.start() this.#started = true @@ -740,7 +796,7 @@ export class Connection extends EventEmitter { this.removeAllListeners() this.#messageQueue.stop() - this.#log('connection stopped') + this.LOG('warn', 'connection stopped') return this } @@ -760,7 +816,7 @@ export class Connection extends EventEmitter { public send = (message: any) => { assert(this._sessionKey, "Can't send encrypted messages until we've finished connecting") const encryptedMessage = symmetric.encryptBytes(message, this._sessionKey) - this.#log(`encrypted message with session key ${base58.encode(this._sessionKey)}`) + this.LOG('info', `encrypted message with session key ${base58.encode(this._sessionKey)}`) this.#queueMessage('ENCRYPTED_MESSAGE', encryptedMessage) } @@ -795,15 +851,21 @@ export class Connection extends EventEmitter { return this.#machine.getSnapshot().context } - #initializeMessageQueue(sendMessage: (message: Uint8Array) => void) { + get _started(): boolean { + return this.#started + } + + #initializeMessageQueue(sendMessage: (message: Uint8Array) => void, createLogger?: (name: string) => any, username?: string) { // To send messages to our peer, we give them to the ordered message queue, which will deliver // them using the `sendMessage` function provided. return new MessageQueue({ sendMessage: message => { this.#logMessage('out', message) const serialized = pack(message) - sendMessage(serialized) + sendMessage(Uint8Array.from(serialized)) }, + createLogger, + username }) .on('message', message => { this.#logMessage('in', message) @@ -824,7 +886,7 @@ export class Connection extends EventEmitter { /** Force local error state */ #fail(error: ConnectionErrorType) { - this.#log('error: %o', error) + this.LOG('error', 'error', error) const localMessage = createErrorMessage(error, 'LOCAL') this.#machine.send(localMessage) return { error: localMessage.payload } @@ -843,7 +905,7 @@ export class Connection extends EventEmitter { #logMessage(direction: 'in' | 'out', message: NumberedMessage) { const arrow = direction === 'in' ? '<-' : '->' const peerUserName = this.#started ? this._context.peer?.userName ?? '?' : '?' - this.#log(`${arrow}${peerUserName} #${message.index} ${messageSummary(message)}`) + this.LOG('info', `${arrow}${peerUserName} #${message.index} ${messageSummary(message)}`) } } @@ -858,7 +920,7 @@ const fail = (error: ConnectionErrorType) => }) as const // timeout configuration -const TIMEOUT_DELAY = 7000 +const TIMEOUT_DELAY = 30_000 const timeout = { after: { [TIMEOUT_DELAY]: fail(TIMEOUT) } } as const // TYPES @@ -868,5 +930,6 @@ export type ConnectionParams = { sendMessage: (message: Uint8Array) => void /** The initial context. */ - context: Context + context: Context, + createLogger?: (packageName: string) => any } diff --git a/packages/auth/src/connection/MessageQueue.ts b/packages/auth/src/connection/MessageQueue.ts index f8c4c924..0300398c 100644 --- a/packages/auth/src/connection/MessageQueue.ts +++ b/packages/auth/src/connection/MessageQueue.ts @@ -19,14 +19,21 @@ export class MessageQueue extends EventEmitter> { #outbound: Record> = {} #nextOutbound = 0 readonly #sendMessage: (message: NumberedMessage) => void + #sharedLogger: any | undefined - constructor({ sendMessage, timeout = 1000 }: Options) { + constructor({ sendMessage, timeout = 1000, createLogger = undefined, username = undefined }: Options) { super() this.#sendMessage = (message: NumberedMessage) => { this.#nextOutbound = message.index + 1 sendMessage(message) } this.#timeout = timeout + if (createLogger != null) { + this.#sharedLogger = createLogger('message-queue') + if (username != null) { + this.#sharedLogger.extend(username) + } + } } /** @@ -55,7 +62,7 @@ export class MessageQueue extends EventEmitter> { const index = highestIndex(this.#outbound) + 1 const numberedMessage = { ...message, index } this.#outbound[index] = numberedMessage - log('send %o', numberedMessage) + this.#LOG('info', 'send', numberedMessage) if (this.#started) this.#sendMessage(numberedMessage) return this } @@ -67,7 +74,7 @@ export class MessageQueue extends EventEmitter> { const message = this.#outbound[index] if (!message) throw new Error(`Received resend request for message #${index}, which doesn't exist.`) - log('resend %o', message) + this.#LOG('warn', 'resend', message) this.#sendMessage(message) return this } @@ -77,7 +84,7 @@ export class MessageQueue extends EventEmitter> { */ public receive(message: NumberedMessage) { const { index } = message - log('receive %o', message) + this.#LOG('info', 'receive', message) if (!this.#inbound[index]) { this.#inbound[index] = message if (this.#started) this.#processInbound() @@ -87,7 +94,7 @@ export class MessageQueue extends EventEmitter> { #processOutbound() { // send outbound messages in order - log('processOutbound') + this.#LOG('info', 'processOutbound') while (this.#outbound[this.#nextOutbound]) { const message = this.#outbound[this.#nextOutbound] this.#sendMessage(message) @@ -98,7 +105,7 @@ export class MessageQueue extends EventEmitter> { * Receives any messages that are pending in the inbound queue, and requests any missing messages. */ #processInbound() { - log('processInbound') + this.#LOG('info', 'processInbound') // emit received messages in order while (this.#inbound[this.#nextInbound]) { const message = this.#inbound[this.#nextInbound] @@ -120,6 +127,33 @@ export class MessageQueue extends EventEmitter> { }, this.#timeout) } } + + #LOG(level: 'info' | 'warn' | 'error', message: string, ...params: any[]) { + if (this.#sharedLogger == null) { + log(message, params) + return + } + + switch (level) { + case 'info': + this.#sharedLogger.info(message, ...params) + break + case 'warn': + this.#sharedLogger.warn(message, ...params) + break + case 'error': + this.#sharedLogger.error(message, ...params) + break + default: + throw new Error(`Unknown log level ${level}`) + } + } + + #truncateMessageForLogging(message: any): string { + return findAllByKeyAndReplace(JSON.parse(JSON.stringify(message)), ['data', 'encryptedBody', 'encryptedPayload'], { + replacerFunc: (dataArray: any[]) => Buffer.from(dataArray).toString('base64') + }) + } } // HELPERS @@ -128,6 +162,29 @@ function highestIndex(queue: Record) { return Math.max(...Object.keys(queue).map(Number), -1) } +const findAllByKeyAndReplace = (object: any, keys: string[], replace: { newValue?: any, replacerFunc?: (originalValue: any) => any}) => { + if (replace.newValue == null && replace.replacerFunc == null) { + throw new Error(`Must provide a replacement value or a replacement function!`) + } + + const replacerFunc = replace.newValue ? (originalValue: any) => replace.newValue : replace.replacerFunc! + + const newObject = { ...object } + const looper = (obj: any) => { + for (let k in obj){ + if(keys.includes(k)){ + obj[k] = replacerFunc(obj[k]) + } + else if("object" === typeof obj[k]){ + looper(obj[k]) + } + } + } + looper(newObject) + + return newObject +} + // TYPES export type NumberedMessage = T & { index: number } @@ -143,6 +200,9 @@ type Options = { /** Time to wait (in ms) before requesting a missing message */ timeout?: number + + createLogger?: (name: string) => any, + username?: string } type TimeoutId = ReturnType diff --git a/packages/auth/src/connection/types.ts b/packages/auth/src/connection/types.ts index 1fe65d78..ef097098 100644 --- a/packages/auth/src/connection/types.ts +++ b/packages/auth/src/connection/types.ts @@ -51,6 +51,8 @@ export type ConnectionEvents = { /** The auth connection disconnects from a peer after entering an error state. */ disconnected: (event: ConnectionMessage) => void + + sync: ({ team, user }: { team: Team; user: UserWithSecrets }) => void } // IDENTITY CLAIMS diff --git a/packages/auth/src/team/isAdminOnlyAction.ts b/packages/auth/src/team/isAdminOnlyAction.ts index 52c09ae0..1490d788 100644 --- a/packages/auth/src/team/isAdminOnlyAction.ts +++ b/packages/auth/src/team/isAdminOnlyAction.ts @@ -10,6 +10,7 @@ export const isAdminOnlyAction = (action: TeamLinkBody) => { 'CHANGE_SERVER_KEYS', 'ADMIT_MEMBER', 'ADMIT_DEVICE', + 'ADD_MEMBER_ROLE' ] return !nonAdminActions.includes(action.type) diff --git a/packages/crdx/src/graph/concurrency.ts b/packages/crdx/src/graph/concurrency.ts index c0dc61f7..47597f4b 100644 --- a/packages/crdx/src/graph/concurrency.ts +++ b/packages/crdx/src/graph/concurrency.ts @@ -1,9 +1,8 @@ import { memoize } from '@localfirst/shared' import { type Hash } from 'util/index.js' -import { getHashes } from './getHashes.js' import { getLink } from './getLink.js' -import { isPredecessorHash } from './isPredecessor.js' -import { isSuccessorHash } from './isSuccessor.js' +import { getPredecessorHashes } from './getPredecessors.js' +import { getChildrenHashes } from './children.js' import type { Action, Graph, Link } from './types.js' /** Returns all links that are concurrent with the given link. */ @@ -30,24 +29,41 @@ export const getConcurrentHashes = (graph: Graph, hash: Hash): Hash[] * ``` */ export const calculateConcurrency = memoize((graph: Graph) => { + const toVisit = [graph.root] + const visited: Set = new Set() const concurrencyLookup: Record = {} - // for each link, find all links that are concurrent with it - for (const _ in graph.links) { - const hash = _ as Hash - concurrencyLookup[hash] = getHashes(graph) - .filter(b => isConcurrent(graph, hash, b)) - .sort() + while (toVisit.length > 0) { + const current = toVisit.shift() as Hash + + if (visited.has(current)) { + continue + } + + const predecessors = new Set(getPredecessorHashes(graph, current)) + const concurrent: Hash[] = [] + + for (const v of Array.from(visited)) { + if (!predecessors.has(v) && !getPredecessorHashes(graph, v).includes(current)) { + concurrent.push(v) + } + } + + concurrencyLookup[current] = concurrent + + for (const c of concurrent) { + concurrencyLookup[c].push(current) + } + + for (const c of getChildrenHashes(graph, current)) { + toVisit.push(c) + } + visited.add(current) } return concurrencyLookup }) -export const isConcurrent = (graph: Graph, a: Hash, b: Hash) => - a !== b && // a link isn't concurrent with itself - !isPredecessorHash(graph, a, b) && // a link isn't concurrent with any of its predecessors - !isSuccessorHash(graph, a, b) // a link isn't concurrent with any of its successors - export const getConcurrentBubbles = (graph: Graph): Hash[][] => { const seen: Record = {} diff --git a/packages/crdx/src/graph/decrypt.ts b/packages/crdx/src/graph/decrypt.ts index bb4e26c4..c90df9a2 100644 --- a/packages/crdx/src/graph/decrypt.ts +++ b/packages/crdx/src/graph/decrypt.ts @@ -51,34 +51,32 @@ export const decryptGraph: DecryptFn = ({ keys: KeysetWithSecrets | KeysetWithSecrets[] | Keyring }): Graph => { const { encryptedLinks, root, childMap = {} } = encryptedGraph - const links = encryptedGraph.links ?? {} + const toVisit = [root] + const visited: Set = new Set() + const decryptedLinks: Record> = {} + + while (toVisit.length > 0) { + const current = toVisit.pop() as Hash + + if (visited.has(current)) { + continue + } - /** Recursively decrypts a link and its children. */ - const decrypt = ( - hash: Hash, - prevLinks: Record> = {} - ): Record> => { - // decrypt this link - const encryptedLink = encryptedLinks[hash] + const encryptedLink = encryptedLinks[current] const decryptedLink = - links[hash] ?? // if it's already decrypted, don't bother decrypting it again + links[current] ?? // if it's already decrypted, don't bother decrypting it again decryptLink(encryptedLink, keys) - let newLinks = { - [hash]: decryptedLink, - } - // decrypt its children - const children = childMap[hash] ?? [] - for (const hash of children) { - newLinks = { ...newLinks, ...decrypt(hash, newLinks) } - } + decryptedLinks[current] = decryptedLink - return { ...prevLinks, ...newLinks } + const children = childMap[current] ?? [] + for (const child of children) { + toVisit.push(child) + } + visited.add(current) } - const decryptedLinks = decrypt(root) - return { ...encryptedGraph, links: decryptedLinks, diff --git a/packages/crdx/src/graph/getSuccessors.ts b/packages/crdx/src/graph/getSuccessors.ts deleted file mode 100644 index dc4ecdc3..00000000 --- a/packages/crdx/src/graph/getSuccessors.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { memoize } from '@localfirst/shared' -import { uniq } from 'lodash-es' -import { type Hash } from 'util/index.js' -import { getChildrenHashes } from './children.js' -import type { Action, Graph, Link } from './types.js' - -const memoizeResolver = (graph: Graph, hash: Hash) => `${graph.head.join('')}:${hash}` - -export const getSuccessorHashes = memoize((graph: Graph, hash: Hash): Hash[] => { - const children = getChildrenHashes(graph, hash) - const successors = children.flatMap(parent => getSuccessorHashes(graph, parent)) - return uniq(children.concat(successors)) -}, memoizeResolver) - -/** Returns the set of successors of `link` (not including `link`) */ -export const getSuccessors = ( - graph: Graph, - link: Link -): Array> => - getSuccessorHashes(graph, link.hash) - .map(h => graph.links[h]) - .filter(link => link !== undefined) diff --git a/packages/crdx/src/graph/index.ts b/packages/crdx/src/graph/index.ts index 705f8ab5..bab5acb2 100644 --- a/packages/crdx/src/graph/index.ts +++ b/packages/crdx/src/graph/index.ts @@ -12,10 +12,8 @@ export * from './getParents.js' export * from './getPredecessors.js' export * from './getRoot.js' export * from './getSequence.js' -export * from './getSuccessors.js' export * from './headsAreEqual.js' export * from './isPredecessor.js' -export * from './isSuccessor.js' export * from './merge.js' export * from './redactGraph.js' export * from './serialize.js' diff --git a/packages/crdx/src/graph/isSuccessor.ts b/packages/crdx/src/graph/isSuccessor.ts deleted file mode 100644 index 7f5eb924..00000000 --- a/packages/crdx/src/graph/isSuccessor.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { type Action, type Link, type Graph } from './types.js' -import { type Hash } from 'util/index.js' -import { getSuccessorHashes } from './getSuccessors.js' - -/** Returns true if `a` is a successor of `b` */ -export const isSuccessor = ( - graph: Graph, - a: Link, - b: Link -): boolean => { - return ( - a !== undefined && - b !== undefined && - a.hash in graph.links && - b.hash in graph.links && - getSuccessorHashes(graph, b.hash).includes(a.hash) - ) -} - -export const isSuccessorHash = (graph: Graph, a: Hash, b: Hash) => - getSuccessorHashes(graph, b).includes(a) diff --git a/packages/crdx/src/graph/test/getSuccessors.test.ts b/packages/crdx/src/graph/test/getSuccessors.test.ts deleted file mode 100644 index 8e10b8b2..00000000 --- a/packages/crdx/src/graph/test/getSuccessors.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { describe, expect, it, test } from 'vitest' -import { type XLink, buildGraph, findByPayload, getPayloads } from 'util/testing/graph.js' -import { getRoot, getSuccessors, isSuccessor } from 'graph/index.js' - -describe('graphs', () => { - describe('successors', () => { - const graph = buildGraph(` - ┌─ e ─ g ─┐ - ┌─ c ─ d ─┤ ├─ o ─┐ - a ─ b ─┤ └─── f ───┤ ├─ n - ├──── h ──── i ─────┘ │ - └───── j ─── k ── l ──────┘ - `) - - describe('getSuccessors', () => { - const getSuccessorPayloads = (link: XLink): string => { - const successors = getSuccessors(graph, link) - return getPayloads(successors).split('').sort().join('') - } - - test('root', () => { - const root = getRoot(graph) - expect(getSuccessorPayloads(root)).toEqual('abcdefghijklno') - }) - - test('d', () => { - const d = findByPayload(graph, 'd') - expect(getSuccessorPayloads(d)).toEqual('efgno') - }) - - test('o', () => { - const o = findByPayload(graph, 'o') - expect(getSuccessorPayloads(o)).toEqual('n') - }) - }) - - describe('isSuccessor', () => { - const testCase = (a: string, b: string) => { - const aLink = findByPayload(graph, a) - const bLink = findByPayload(graph, b) - - return isSuccessor(graph, aLink, bLink) - } - - it('f succeeds c', () => expect(testCase('f', 'c')).toBe(true)) - it(`c doesn't succeed f`, () => expect(testCase('c', 'f')).toBe(false)) - it(`c doesn't succeed c`, () => expect(testCase('c', 'c')).toBe(false)) - }) - }) -}) diff --git a/packages/crdx/src/validator/validators.ts b/packages/crdx/src/validator/validators.ts index 11c1632c..0d95fac1 100644 --- a/packages/crdx/src/validator/validators.ts +++ b/packages/crdx/src/validator/validators.ts @@ -86,7 +86,7 @@ export const fail = (msg: string, args?: any) => { const memoizeFunctionMap = (source: ValidatorSet) => { const result = {} as ValidatorSet const memoizeResolver = (link: Link, graph: Graph) => { - return `${hash('memoize', link)}:${hash('memoize', graph)}` + return `${link.hash}:${graph.root}` } for (const key in source) result[key] = memoize(source[key], memoizeResolver) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3e3db002..a0010413 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -82,13 +82,13 @@ importers: version: 5.4.5 vite: specifier: ^5.2.11 - version: 5.2.12(@types/node@18.19.33)(terser@5.31.0) + version: 5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0) vite-tsconfig-paths: specifier: ^4.2.1 - version: 4.3.2(typescript@5.4.5)(vite@5.2.12(@types/node@18.19.33)(terser@5.31.0)) + version: 4.3.2(typescript@5.4.5)(vite@5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0)) vitest: specifier: ^1.6.0 - version: 1.6.0(@types/node@18.19.33)(terser@5.31.0) + version: 1.6.0(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0) xo: specifier: ^0.56.0 version: 0.56.0(@types/eslint@8.56.10)(webpack@5.91.0(@swc/core@1.5.24)(esbuild@0.19.12)) @@ -100,19 +100,19 @@ importers: version: 2.2.2 '@automerge/automerge-repo': specifier: ^1.1.2 - version: 1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3) + version: 1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3) '@automerge/automerge-repo-network-broadcastchannel': specifier: ^1.1.2 - version: 1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3) + version: 1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3) '@automerge/automerge-repo-network-websocket': specifier: ^1.1.2 - version: 1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3) + version: 1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3) '@automerge/automerge-repo-react-hooks': specifier: ^1.1.2 - version: 1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3) + version: 1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3) '@automerge/automerge-repo-storage-indexeddb': specifier: ^1.1.2 - version: 1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3) + version: 1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3) '@ibm/plex': specifier: ^6.1.1 version: 6.4.1 @@ -170,22 +170,22 @@ importers: version: 0.7.39 '@vitejs/plugin-react': specifier: ^4.2.1 - version: 4.3.0(vite@5.2.12(@types/node@18.19.33)(terser@5.31.0)) + version: 4.3.0(vite@5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0)) tailwindcss: specifier: ^3.3.7 - version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3)) + version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3)) vite: specifier: ^5.2.11 - version: 5.2.12(@types/node@18.19.33)(terser@5.31.0) + version: 5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0) vite-plugin-top-level-await: specifier: ^1.4.1 - version: 1.4.1(rollup@4.18.0)(vite@5.2.12(@types/node@18.19.33)(terser@5.31.0)) + version: 1.4.1(@swc/helpers@0.5.12)(rollup@4.18.0)(vite@5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0)) vite-plugin-wasm: specifier: ^3.3.0 - version: 3.3.0(vite@5.2.12(@types/node@18.19.33)(terser@5.31.0)) + version: 3.3.0(vite@5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0)) vitest: specifier: ^1.6.0 - version: 1.6.0(@types/node@18.19.33)(terser@5.31.0) + version: 1.6.0(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0) demos/quiet-sandbox: dependencies: @@ -231,6 +231,9 @@ importers: '@libp2p/kad-dht': specifier: ^12.0.17 version: 12.1.2 + '@libp2p/mdns': + specifier: 10.1.5 + version: 10.1.5 '@libp2p/peer-id': specifier: ^4.2.1 version: 4.2.1 @@ -252,12 +255,21 @@ importers: '@orbitdb/core': specifier: ^2.2.0 version: 2.2.0 + ansi-colors: + specifier: 4.1.3 + version: 4.1.3 ansi-escapes: specifier: ^7.0.0 version: 7.0.0 + bs58: + specifier: ^6.0.0 + version: 6.0.0 chalk: specifier: ^4.1.2 version: 4.1.2 + chart.js: + specifier: ^4.4.3 + version: 4.4.3 clipboardy: specifier: 4.0.0 version: 4.0.0 @@ -267,6 +279,12 @@ importers: datastore-core: specifier: 9.2.9 version: 9.2.9 + debug: + specifier: 4.3.6 + version: 4.3.6(supports-color@8.1.1) + fastq: + specifier: ^1.17.1 + version: 1.17.1 figlet: specifier: ^1.7.0 version: 1.7.0 @@ -285,6 +303,9 @@ importers: inquirer-select-pro: specifier: ^1.0.0-alpha.6 version: 1.0.0-alpha.6 + interface-datastore: + specifier: 8.3.0 + version: 8.3.0 it-length-prefixed: specifier: ^9.0.4 version: 9.0.4 @@ -303,6 +324,9 @@ importers: libp2p: specifier: ^1.8.1 version: 1.8.1 + luxon: + specifier: 3.5.0 + version: 3.5.0 multiformats: specifier: ^13.2.0 version: 13.2.1 @@ -312,31 +336,55 @@ importers: quiet-sandbox: specifier: 'link:' version: 'link:' + rng: + specifier: 0.2.2 + version: 0.2.2 type-fest: specifier: ^4.21.0 version: 4.23.0 uint8arraylist: specifier: ^2.4.8 version: 2.4.8 + weald: + specifier: 1.0.2 + version: 1.0.2 devDependencies: + 0x: + specifier: ^5.7.0 + version: 5.7.0 + '@types/bs58': + specifier: 4.0.4 + version: 4.0.4 + '@types/debug': + specifier: 4.1.12 + version: 4.1.12 '@types/figlet': specifier: ^1.5.8 version: 1.5.8 '@types/inquirer': specifier: ^9.0.7 version: 9.0.7 + '@types/luxon': + specifier: 3.4.2 + version: 3.4.2 '@types/node': specifier: ^18.18.13 version: 18.19.33 eslint: specifier: ^9.7.0 version: 9.7.0 + nodemark: + specifier: 0.3.0 + version: 0.3.0 + parcel: + specifier: ^2.6.2 + version: 2.12.0(@swc/helpers@0.5.12)(postcss@8.4.38)(terser@5.31.0)(typescript@5.5.3) prettier: specifier: ^3.1.0 version: 3.2.5 ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3) + version: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3) typescript: specifier: ^5.5.3 version: 5.5.3 @@ -372,7 +420,7 @@ importers: version: 9.3.4 '@testing-library/jest-dom': specifier: ^6.1.4 - version: 6.4.5(vitest@1.6.0(@types/node@20.14.11)(terser@5.31.0)) + version: 6.4.5(vitest@1.6.0(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0)) '@testing-library/react': specifier: ^14.1.2 version: 14.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -381,7 +429,7 @@ importers: version: 14.5.2(@testing-library/dom@9.3.4) '@vitejs/plugin-react': specifier: ^4.2.0 - version: 4.3.0(vite@5.2.12(@types/node@20.14.11)(terser@5.31.0)) + version: 4.3.0(vite@5.2.12(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0)) async-mutex: specifier: ^0.4.0 version: 0.4.1 @@ -399,7 +447,7 @@ importers: version: 7.0.3 debug: specifier: ^4.3.4 - version: 4.3.5(supports-color@8.1.1) + version: 4.3.5 friendly-words: specifier: ^1.2.4 version: 1.3.1 @@ -426,10 +474,10 @@ importers: version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.24)(@types/node@20.14.11)(typescript@5.4.5)) vite: specifier: ^5.2.11 - version: 5.2.12(@types/node@20.14.11)(terser@5.31.0) + version: 5.2.12(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0) vite-plugin-top-level-await: specifier: ^1.3.1 - version: 1.4.1(rollup@4.18.0)(vite@5.2.12(@types/node@20.14.11)(terser@5.31.0)) + version: 1.4.1(@swc/helpers@0.5.12)(rollup@4.18.0)(vite@5.2.12(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0)) ws: specifier: ^8.15.1 version: 8.17.0 @@ -615,7 +663,7 @@ importers: version: 0.3.3 debug: specifier: ^4.3.4 - version: 4.3.5(supports-color@8.1.1) + version: 4.3.5 eventemitter3: specifier: ^5.0.1 version: 5.0.1 @@ -625,6 +673,11 @@ importers: packages: + 0x@5.7.0: + resolution: {integrity: sha512-oc5lqaJP7lu3C5zx+MRbsigfRlTTUg0LKjFDCr0NmR9g+nkZcR7yRU6jzMOiedSVKZ0p0b4y2TBQ+YYo6O3sZg==} + engines: {node: '>=8.5.0'} + hasBin: true + '@achingbrain/nat-port-mapper@1.0.13': resolution: {integrity: sha512-B5GL6ILDek72OjoEyFGEuuNYaEOYxO06Ulhcaf/5iQ4EO8uaZWS+OkolYST7L+ecJrkjfaSNmSAsWRRuh+1Z5A==} @@ -2079,6 +2132,9 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@kurkle/color@0.3.2': + resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==} + '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} @@ -2090,6 +2146,12 @@ packages: resolution: {integrity: sha512-1wplFbQ52K8E/unnqB0Tq39Z4e+NEoNrpovEnl6GpsTUrC6WDp8+w0Le2uCBV0hXyemxChduCkLz4/y1H1wTeg==} engines: {node: '>=16.0.0'} + '@lezer/common@1.2.1': + resolution: {integrity: sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==} + + '@lezer/lr@1.4.2': + resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} + '@libp2p/autonat@1.1.2': resolution: {integrity: sha512-GG6aaQgDranvO0wWpFo+kDm+nUdHmJ7Hp41DLoPFF69sdfrdGsaL3qsE0TU0I814z8kxfQ0F2eo0FN2Mcclc6g==} @@ -2105,6 +2167,9 @@ packages: '@libp2p/crypto@4.1.6': resolution: {integrity: sha512-KQxgFcRpEYUynmEfANqyKR9C2YZ8w/SzfZjB+gbrbRRHz1cclIA8g2y/MdW1D4x21EXF7+COY1klCCFQS3ppOg==} + '@libp2p/crypto@4.1.9': + resolution: {integrity: sha512-8Cf2VKh0uC/rQLvTLSloIOMqUvf4jsSTHXgjWQRf47lDNJlNNI0wSv2S6gakT72GZsRV/jCjYwKPqRlsa5S0iA==} + '@libp2p/dcutr@1.1.2': resolution: {integrity: sha512-pc5qxNpKxASpgapiNot/4reeiG8CoryaC4CipnTfgoQ4HBazqo47aA6tppYLzVVjEfDhoEPtymHFAHhaF8IWeg==} @@ -2117,9 +2182,15 @@ packages: '@libp2p/interface-internal@1.3.1': resolution: {integrity: sha512-81e+4JLJPjgIuxFOqOJUr4w5rr+SrfwG5PhkVsnqA4F4Sxb8x4ImRjDgPKqbTUp/taSNUz9zMGgwwdG6m4mvGA==} + '@libp2p/interface-internal@1.3.4': + resolution: {integrity: sha512-8x/0sdeH8T16yZ9t/Cfja0ms6Ho9fF3riX56WhQrNxMU6C1sIgAFmzUNzHLxxOR+rkKyL9cyXIyB+RcBf4gzjA==} + '@libp2p/interface@1.6.1': resolution: {integrity: sha512-bpkIYTvZhGGc/ajITKvgFpaP8UtPWoSj+xHVrj6zyAN8U/cAqN0IQQt4a7daJr5VZa8B86i4d1iccdG42/mz+g==} + '@libp2p/interface@1.7.0': + resolution: {integrity: sha512-/zFyaIaIGW0aihhsH7/93vQdpWInUzFocxF11RO/029Y6h0SVjs24HHbils+DqaFDTqN+L7oNlBx2rM2MnmTjA==} + '@libp2p/kad-dht@12.1.2': resolution: {integrity: sha512-f9SKXOHcKq9DELgl5oeBkv3fowlPydGycIO86uhZIB5m0cu0kLpn8FWHNpMMjc6o2tUWURB9hF0JYWehl4CdSA==} @@ -2129,8 +2200,11 @@ packages: '@libp2p/logger@4.0.17': resolution: {integrity: sha512-NPGN27uOXFGuKkxnX39InMvxS0lMenq6/aFqQHN1N0f0S3LaG9RuTcz/VE3qyO1Ik1aAockR6qqCwbfFxJuO0g==} - '@libp2p/mdns@10.1.2': - resolution: {integrity: sha512-OmKa6RnFt2SDVoBK6d/gUUF5AC5+IeL5poBfvDh90ihqZihEukKWWixaiPcmLRt375sMEkGqeY0Jk5vuy+XUsA==} + '@libp2p/logger@4.0.20': + resolution: {integrity: sha512-TTh2dhHsOTAlMPxSa9ncFPHa/0jTt+0AQxwHdlxg/OGLAgc9VRhnrhHUbJZp07Crcw4T/MOfS4KhjlxgqYgJRw==} + + '@libp2p/mdns@10.1.5': + resolution: {integrity: sha512-iSBnjaUgPdPudXP3ZPXJMWzJkT+I+TQHRCBTh6OMPa0V2C/MhL+FfCGn0SDMWKSDDwqK6I6UOeTflf4YUzy/8w==} '@libp2p/mplex@10.1.2': resolution: {integrity: sha512-RJPbfD8Pgu2ye/akTo9ESAae6ZefwuzEuPjMgg4adVKNfcU0C7HXXrmXQ1B4DfHfVlWW7k+M3u31jZtyXodh3w==} @@ -2141,12 +2215,18 @@ packages: '@libp2p/peer-collections@5.2.6': resolution: {integrity: sha512-a6P1euFoUXX4FvYomifZMLCdRhUyV/X44Y4CrWuY+OSv3pg2siTU8LuCo8EOXtFWpwJAQIzh+v/roYJhjiwyGA==} + '@libp2p/peer-collections@5.2.9': + resolution: {integrity: sha512-8gBmzQlCWjjb+FSQBKK33T25Y5Df/8FWCXFtJDsprVxVUzDOQoibQJ5Tb4Y+mb96HUhNzoaRWVEamB78MMB3DA==} + '@libp2p/peer-id-factory@4.2.1': resolution: {integrity: sha512-BxQzL2unriOxRwgg1sHMhtYz0Zwshm1h8fMxWD5GqxrX2cPq9zcjLNgT7qJJYddBh8Iq5Z2Eu8aIF+7wRzaowQ==} '@libp2p/peer-id@4.2.1': resolution: {integrity: sha512-ttPdHqWoZzITODY8WF1EK8HMGLN7fXnV/FlHlrvGKQF4TqqTXgqFM3+4AhhGJJK34bqcq2U3r4vy474MmUQgjA==} + '@libp2p/peer-id@4.2.4': + resolution: {integrity: sha512-mvvsVxt4HkF14BrTNKbqr14VObW+KBJBWu1Oe6BFCoDttGMQLaI+PdduE1r6Tquntv5IONBqoITgD7ow5dQ+vQ==} + '@libp2p/peer-record@7.0.22': resolution: {integrity: sha512-7QnpzENWtuU75E1L9xANmNAoiOMElMR5DZUZdXouvs7Yw0hGq1xI2MzqSH8TYISqjsDvE5SwKod6YQX0vCfoXw==} @@ -2171,6 +2251,9 @@ packages: '@libp2p/utils@5.4.6': resolution: {integrity: sha512-UBHsXO9OvMJZex/u8bJFYGjF2Tdd/eNh1Ys6A24iyJR6TlYoOfwO1BxB++CPDKPo7Zuz5V5IOwzfvr3llzfybg==} + '@libp2p/utils@5.4.9': + resolution: {integrity: sha512-0fRdX98WqhTmXU2WEVLegLFxs/kKTtUHanHk5Lzs4oGsIzlPHR7zE6lj/U1WfsFA+Xo1eYQpNLiXEL29hG+Nyw==} + '@libp2p/webrtc@4.1.2': resolution: {integrity: sha512-1tvIP9mEH6hLhHXL4TfOSN7imvt4kLFYHkp+Qv15gKVhrdrsaI7wq/t6zcpTMKFXXmvNWq0twrkmg3ARsD4SCQ==} @@ -2180,10 +2263,44 @@ packages: '@libp2p/webtransport@4.1.2': resolution: {integrity: sha512-TAKb+dKxVxA8mNFsqio/gJSkK2jvZbKZECSZnHWMehpzQ8x8FFfGSK5ogy99Y1LhuC206e4YaDY/FocrFgNcfA==} + '@lmdb/lmdb-darwin-arm64@2.8.5': + resolution: {integrity: sha512-KPDeVScZgA1oq0CiPBcOa3kHIqU+pTOwRFDIhxvmf8CTNvqdZQYp5cCKW0bUk69VygB2PuTiINFWbY78aR2pQw==} + cpu: [arm64] + os: [darwin] + + '@lmdb/lmdb-darwin-x64@2.8.5': + resolution: {integrity: sha512-w/sLhN4T7MW1nB3R/U8WK5BgQLz904wh+/SmA2jD8NnF7BLLoUgflCNxOeSPOWp8geP6nP/+VjWzZVip7rZ1ug==} + cpu: [x64] + os: [darwin] + + '@lmdb/lmdb-linux-arm64@2.8.5': + resolution: {integrity: sha512-vtbZRHH5UDlL01TT5jB576Zox3+hdyogvpcbvVJlmU5PdL3c5V7cj1EODdh1CHPksRl+cws/58ugEHi8bcj4Ww==} + cpu: [arm64] + os: [linux] + + '@lmdb/lmdb-linux-arm@2.8.5': + resolution: {integrity: sha512-c0TGMbm2M55pwTDIfkDLB6BpIsgxV4PjYck2HiOX+cy/JWiBXz32lYbarPqejKs9Flm7YVAKSILUducU9g2RVg==} + cpu: [arm] + os: [linux] + + '@lmdb/lmdb-linux-x64@2.8.5': + resolution: {integrity: sha512-Xkc8IUx9aEhP0zvgeKy7IQ3ReX2N8N1L0WPcQwnZweWmOuKfwpS3GRIYqLtK5za/w3E60zhFfNdS+3pBZPytqQ==} + cpu: [x64] + os: [linux] + + '@lmdb/lmdb-win32-x64@2.8.5': + resolution: {integrity: sha512-4wvrf5BgnR8RpogHhtpCPJMKBmvyZPhhUtEwMJbXh0ni2BucpfF07jlmyM11zRqQ2XIq6PbC2j7W7UCCcm1rRQ==} + cpu: [x64] + os: [win32] + '@localfirst/relay@4.2.2': resolution: {integrity: sha512-8sA6MGOfx5O8tsYQdL/SiQd/WGR+Uyo3qLZolifO5Lh41aJZKuWs0840P82S/5c/Pg9n52b6u8yoo5XomoL8GQ==} engines: {node: '>=18.0.0'} + '@mischnic/json-sourcemap@0.1.1': + resolution: {integrity: sha512-iA7+tyVqfrATAIsIRWQG+a7ZLLD0VaOCKV2Wd/v4mqIU3J9c4jx9p7S0nw1XH3gJCKNBOOwACOPYYSUu9pgT+w==} + engines: {node: '>=12.0.0'} + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2': resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==} cpu: [arm64] @@ -2427,10 +2544,308 @@ packages: '@paralleldrive/cuid2@2.2.2': resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} + '@parcel/bundler-default@2.12.0': + resolution: {integrity: sha512-3ybN74oYNMKyjD6V20c9Gerdbh7teeNvVMwIoHIQMzuIFT6IGX53PyOLlOKRLbjxMc0TMimQQxIt2eQqxR5LsA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/cache@2.12.0': + resolution: {integrity: sha512-FX5ZpTEkxvq/yvWklRHDESVRz+c7sLTXgFuzz6uEnBcXV38j6dMSikflNpHA6q/L4GKkCqRywm9R6XQwhwIMyw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/codeframe@2.12.0': + resolution: {integrity: sha512-v2VmneILFiHZJTxPiR7GEF1wey1/IXPdZMcUlNXBiPZyWDfcuNgGGVQkx/xW561rULLIvDPharOMdxz5oHOKQg==} + engines: {node: '>= 12.0.0'} + + '@parcel/compressor-raw@2.12.0': + resolution: {integrity: sha512-h41Q3X7ZAQ9wbQ2csP8QGrwepasLZdXiuEdpUryDce6rF9ZiHoJ97MRpdLxOhOPyASTw/xDgE1xyaPQr0Q3f5A==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/config-default@2.12.0': + resolution: {integrity: sha512-dPNe2n9eEsKRc1soWIY0yToMUPirPIa2QhxcCB3Z5RjpDGIXm0pds+BaiqY6uGLEEzsjhRO0ujd4v2Rmm0vuFg==} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/core@2.12.0': + resolution: {integrity: sha512-s+6pwEj+GfKf7vqGUzN9iSEPueUssCCQrCBUlcAfKrJe0a22hTUCjewpB0I7lNrCIULt8dkndD+sMdOrXsRl6Q==} + engines: {node: '>= 12.0.0'} + + '@parcel/diagnostic@2.12.0': + resolution: {integrity: sha512-8f1NOsSFK+F4AwFCKynyIu9Kr/uWHC+SywAv4oS6Bv3Acig0gtwUjugk0C9UaB8ztBZiW5TQZhw+uPZn9T/lJA==} + engines: {node: '>= 12.0.0'} + + '@parcel/events@2.12.0': + resolution: {integrity: sha512-nmAAEIKLjW1kB2cUbCYSmZOGbnGj8wCzhqnK727zCCWaA25ogzAtt657GPOeFyqW77KyosU728Tl63Fc8hphIA==} + engines: {node: '>= 12.0.0'} + + '@parcel/fs@2.12.0': + resolution: {integrity: sha512-NnFkuvou1YBtPOhTdZr44WN7I60cGyly2wpHzqRl62yhObyi1KvW0SjwOMa0QGNcBOIzp4G0CapoZ93hD0RG5Q==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/graph@3.2.0': + resolution: {integrity: sha512-xlrmCPqy58D4Fg5umV7bpwDx5Vyt7MlnQPxW68vae5+BA4GSWetfZt+Cs5dtotMG2oCHzZxhIPt7YZ7NRyQzLA==} + engines: {node: '>= 12.0.0'} + + '@parcel/logger@2.12.0': + resolution: {integrity: sha512-cJ7Paqa7/9VJ7C+KwgJlwMqTQBOjjn71FbKk0G07hydUEBISU2aDfmc/52o60ErL9l+vXB26zTrIBanbxS8rVg==} + engines: {node: '>= 12.0.0'} + + '@parcel/markdown-ansi@2.12.0': + resolution: {integrity: sha512-WZz3rzL8k0H3WR4qTHX6Ic8DlEs17keO9gtD4MNGyMNQbqQEvQ61lWJaIH0nAtgEetu0SOITiVqdZrb8zx/M7w==} + engines: {node: '>= 12.0.0'} + + '@parcel/namer-default@2.12.0': + resolution: {integrity: sha512-9DNKPDHWgMnMtqqZIMiEj/R9PNWW16lpnlHjwK3ciRlMPgjPJ8+UNc255teZODhX0T17GOzPdGbU/O/xbxVPzA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/node-resolver-core@3.3.0': + resolution: {integrity: sha512-rhPW9DYPEIqQBSlYzz3S0AjXxjN6Ub2yS6tzzsW/4S3Gpsgk/uEq4ZfxPvoPf/6TgZndVxmKwpmxaKtGMmf3cA==} + engines: {node: '>= 12.0.0'} + + '@parcel/optimizer-css@2.12.0': + resolution: {integrity: sha512-ifbcC97fRzpruTjaa8axIFeX4MjjSIlQfem3EJug3L2AVqQUXnM1XO8L0NaXGNLTW2qnh1ZjIJ7vXT/QhsphsA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/optimizer-htmlnano@2.12.0': + resolution: {integrity: sha512-MfPMeCrT8FYiOrpFHVR+NcZQlXAptK2r4nGJjfT+ndPBhEEZp4yyL7n1y7HfX9geg5altc4WTb4Gug7rCoW8VQ==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/optimizer-image@2.12.0': + resolution: {integrity: sha512-bo1O7raeAIbRU5nmNVtx8divLW9Xqn0c57GVNGeAK4mygnQoqHqRZ0mR9uboh64pxv6ijXZHPhKvU9HEpjPjBQ==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/optimizer-svgo@2.12.0': + resolution: {integrity: sha512-Kyli+ZZXnoonnbeRQdoWwee9Bk2jm/49xvnfb+2OO8NN0d41lblBoRhOyFiScRnJrw7eVl1Xrz7NTkXCIO7XFQ==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/optimizer-swc@2.12.0': + resolution: {integrity: sha512-iBi6LZB3lm6WmbXfzi8J3DCVPmn4FN2lw7DGXxUXu7MouDPVWfTsM6U/5TkSHJRNRogZ2gqy5q9g34NPxHbJcw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/package-manager@2.12.0': + resolution: {integrity: sha512-0nvAezcjPx9FT+hIL+LS1jb0aohwLZXct7jAh7i0MLMtehOi0z1Sau+QpgMlA9rfEZZ1LIeFdnZZwqSy7Ccspw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/packager-css@2.12.0': + resolution: {integrity: sha512-j3a/ODciaNKD19IYdWJT+TP+tnhhn5koBGBWWtrKSu0UxWpnezIGZetit3eE+Y9+NTePalMkvpIlit2eDhvfJA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/packager-html@2.12.0': + resolution: {integrity: sha512-PpvGB9hFFe+19NXGz2ApvPrkA9GwEqaDAninT+3pJD57OVBaxB8U+HN4a5LICKxjUppPPqmrLb6YPbD65IX4RA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/packager-js@2.12.0': + resolution: {integrity: sha512-viMF+FszITRRr8+2iJyk+4ruGiL27Y6AF7hQ3xbJfzqnmbOhGFtLTQwuwhOLqN/mWR2VKdgbLpZSarWaO3yAMg==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/packager-raw@2.12.0': + resolution: {integrity: sha512-tJZqFbHqP24aq1F+OojFbQIc09P/u8HAW5xfndCrFnXpW4wTgM3p03P0xfw3gnNq+TtxHJ8c3UFE5LnXNNKhYA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/packager-svg@2.12.0': + resolution: {integrity: sha512-ldaGiacGb2lLqcXas97k8JiZRbAnNREmcvoY2W2dvW4loVuDT9B9fU777mbV6zODpcgcHWsLL3lYbJ5Lt3y9cg==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/packager-wasm@2.12.0': + resolution: {integrity: sha512-fYqZzIqO9fGYveeImzF8ll6KRo2LrOXfD+2Y5U3BiX/wp9wv17dz50QLDQm9hmTcKGWxK4yWqKQh+Evp/fae7A==} + engines: {node: '>=12.0.0', parcel: ^2.12.0} + + '@parcel/plugin@2.12.0': + resolution: {integrity: sha512-nc/uRA8DiMoe4neBbzV6kDndh/58a4wQuGKw5oEoIwBCHUvE2W8ZFSu7ollSXUGRzfacTt4NdY8TwS73ScWZ+g==} + engines: {node: '>= 12.0.0'} + + '@parcel/profiler@2.12.0': + resolution: {integrity: sha512-q53fvl5LDcFYzMUtSusUBZSjQrKjMlLEBgKeQHFwkimwR1mgoseaDBDuNz0XvmzDzF1UelJ02TUKCGacU8W2qA==} + engines: {node: '>= 12.0.0'} + + '@parcel/reporter-cli@2.12.0': + resolution: {integrity: sha512-TqKsH4GVOLPSCanZ6tcTPj+rdVHERnt5y4bwTM82cajM21bCX1Ruwp8xOKU+03091oV2pv5ieB18pJyRF7IpIw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/reporter-dev-server@2.12.0': + resolution: {integrity: sha512-tIcDqRvAPAttRlTV28dHcbWT5K2r/MBFks7nM4nrEDHWtnrCwimkDmZTc1kD8QOCCjGVwRHcQybpHvxfwol6GA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/reporter-tracer@2.12.0': + resolution: {integrity: sha512-g8rlu9GxB8Ut/F8WGx4zidIPQ4pcYFjU9bZO+fyRIPrSUFH2bKijCnbZcr4ntqzDGx74hwD6cCG4DBoleq2UlQ==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/resolver-default@2.12.0': + resolution: {integrity: sha512-uuhbajTax37TwCxu7V98JtRLiT6hzE4VYSu5B7Qkauy14/WFt2dz6GOUXPgVsED569/hkxebPx3KCMtZW6cHHA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/runtime-browser-hmr@2.12.0': + resolution: {integrity: sha512-4ZLp2FWyD32r0GlTulO3+jxgsA3oO1P1b5oO2IWuWilfhcJH5LTiazpL5YdusUjtNn9PGN6QLAWfxmzRIfM+Ow==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/runtime-js@2.12.0': + resolution: {integrity: sha512-sBerP32Z1crX5PfLNGDSXSdqzlllM++GVnVQVeM7DgMKS8JIFG3VLi28YkX+dYYGtPypm01JoIHCkvwiZEcQJg==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/runtime-react-refresh@2.12.0': + resolution: {integrity: sha512-SCHkcczJIDFTFdLTzrHTkQ0aTrX3xH6jrA4UsCBL6ji61+w+ohy4jEEe9qCgJVXhnJfGLE43HNXek+0MStX+Mw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/runtime-service-worker@2.12.0': + resolution: {integrity: sha512-BXuMBsfiwpIEnssn+jqfC3jkgbS8oxeo3C7xhSQsuSv+AF2FwY3O3AO1c1RBskEW3XrBLNINOJujroNw80VTKA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/rust@2.12.0': + resolution: {integrity: sha512-005cldMdFZFDPOjbDVEXcINQ3wT4vrxvSavRWI3Az0e3E18exO/x/mW9f648KtXugOXMAqCEqhFHcXECL9nmMw==} + engines: {node: '>= 12.0.0'} + + '@parcel/source-map@2.1.1': + resolution: {integrity: sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==} + engines: {node: ^12.18.3 || >=14} + + '@parcel/transformer-babel@2.12.0': + resolution: {integrity: sha512-zQaBfOnf/l8rPxYGnsk/ufh/0EuqvmnxafjBIpKZ//j6rGylw5JCqXSb1QvvAqRYruKeccxGv7+HrxpqKU6V4A==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-css@2.12.0': + resolution: {integrity: sha512-vXhOqoAlQGATYyQ433Z1DXKmiKmzOAUmKysbYH3FD+LKEKLMEl/pA14goqp00TW+A/EjtSKKyeMyHlMIIUqj4Q==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-html@2.12.0': + resolution: {integrity: sha512-5jW4dFFBlYBvIQk4nrH62rfA/G/KzVzEDa6S+Nne0xXhglLjkm64Ci9b/d4tKZfuGWUbpm2ASAq8skti/nfpXw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-image@2.12.0': + resolution: {integrity: sha512-8hXrGm2IRII49R7lZ0RpmNk27EhcsH+uNKsvxuMpXPuEnWgC/ha/IrjaI29xCng1uGur74bJF43NUSQhR4aTdw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/transformer-js@2.12.0': + resolution: {integrity: sha512-OSZpOu+FGDbC/xivu24v092D9w6EGytB3vidwbdiJ2FaPgfV7rxS0WIUjH4I0OcvHAcitArRXL0a3+HrNTdQQw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/transformer-json@2.12.0': + resolution: {integrity: sha512-Utv64GLRCQILK5r0KFs4o7I41ixMPllwOLOhkdjJKvf1hZmN6WqfOmB1YLbWS/y5Zb/iB52DU2pWZm96vLFQZQ==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-postcss@2.12.0': + resolution: {integrity: sha512-FZqn+oUtiLfPOn67EZxPpBkfdFiTnF4iwiXPqvst3XI8H+iC+yNgzmtJkunOOuylpYY6NOU5jT8d7saqWSDv2Q==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-posthtml@2.12.0': + resolution: {integrity: sha512-z6Z7rav/pcaWdeD+2sDUcd0mmNZRUvtHaUGa50Y2mr+poxrKilpsnFMSiWBT+oOqPt7j71jzDvrdnAF4XkCljg==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-raw@2.12.0': + resolution: {integrity: sha512-Ht1fQvXxix0NncdnmnXZsa6hra20RXYh1VqhBYZLsDfkvGGFnXIgO03Jqn4Z8MkKoa0tiNbDhpKIeTjyclbBxQ==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-react-refresh-wrap@2.12.0': + resolution: {integrity: sha512-GE8gmP2AZtkpBIV5vSCVhewgOFRhqwdM5Q9jNPOY5PKcM3/Ff0qCqDiTzzGLhk0/VMBrdjssrfZkVx6S/lHdJw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-svg@2.12.0': + resolution: {integrity: sha512-cZJqGRJ4JNdYcb+vj94J7PdOuTnwyy45dM9xqbIMH+HSiiIkfrMsdEwYft0GTyFTdsnf+hdHn3tau7Qa5hhX+A==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/types@2.12.0': + resolution: {integrity: sha512-8zAFiYNCwNTQcglIObyNwKfRYQK5ELlL13GuBOrSMxueUiI5ylgsGbTS1N7J3dAGZixHO8KhHGv5a71FILn9rQ==} + + '@parcel/utils@2.12.0': + resolution: {integrity: sha512-z1JhLuZ8QmDaYoEIuUCVZlhcFrS7LMfHrb2OCRui5SQFntRWBH2fNM6H/fXXUkT9SkxcuFP2DUA6/m4+Gkz72g==} + engines: {node: '>= 12.0.0'} + + '@parcel/watcher-android-arm64@2.4.1': + resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.4.1': + resolution: {integrity: sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.4.1': + resolution: {integrity: sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.4.1': + resolution: {integrity: sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.4.1': + resolution: {integrity: sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.4.1': + resolution: {integrity: sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.4.1': + resolution: {integrity: sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.4.1': + resolution: {integrity: sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.4.1': + resolution: {integrity: sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.4.1': + resolution: {integrity: sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.4.1': + resolution: {integrity: sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.4.1': + resolution: {integrity: sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + '@parcel/watcher@2.0.4': resolution: {integrity: sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg==} engines: {node: '>= 10.0.0'} + '@parcel/watcher@2.4.1': + resolution: {integrity: sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==} + engines: {node: '>= 10.0.0'} + + '@parcel/workers@2.12.0': + resolution: {integrity: sha512-zv5We5Jmb+ZWXlU6A+AufyjY4oZckkxsZ8J4dvyWL0W8IQvGO1JB4FGeryyttzQv3RM3OxcN/BpTGPiDG6keBw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.12.0 + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -2749,6 +3164,9 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@swc/helpers@0.5.12': + resolution: {integrity: sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==} + '@swc/types@0.1.7': resolution: {integrity: sha512-scHWahbHF0eyj3JsxG9CFJgFdFNaVQCNAimBlT6PzS3n/HptxqREjsm4OH6AN3lYcffZYSPxXW8ua2BEHp0lJQ==} @@ -2808,6 +3226,10 @@ packages: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -2846,6 +3268,9 @@ packages: '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + '@types/bs58@4.0.4': + resolution: {integrity: sha512-0IEpMFXXQi2zXaXl9GJ3sRwQo0uEkD+yFOv+FnAU5lkPtcu6h61xb7jc2CFPEZ5BUOaiP13ThuGc9HD4R8lR5g==} + '@types/cacheable-request@6.0.3': resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} @@ -2927,6 +3352,9 @@ packages: '@types/lodash@4.17.4': resolution: {integrity: sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==} + '@types/luxon@3.4.2': + resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==} + '@types/mdast@3.0.15': resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} @@ -3197,6 +3625,9 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + abortcontroller-polyfill@1.7.5: + resolution: {integrity: sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==} + abstract-level@1.0.4: resolution: {integrity: sha512-eUP/6pbXBkMbXFdx4IH2fVgvB7M0JvR7/lIL33zcs0IBcwjdzSSl31TOJsaCzmKSSDF9h8QYSOJux4Nd4YJqFg==} engines: {node: '>=12'} @@ -3215,10 +3646,22 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-node@1.8.2: + resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==} + + acorn-walk@7.2.0: + resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} + engines: {node: '>=0.4.0'} + acorn-walk@8.3.2: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} + acorn@7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + acorn@8.11.3: resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} @@ -3256,6 +3699,9 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + anser@1.4.10: resolution: {integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==} @@ -3277,6 +3723,10 @@ packages: ansi-fragments@0.2.1: resolution: {integrity: sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==} + ansi-regex@2.1.1: + resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} + engines: {node: '>=0.10.0'} + ansi-regex@3.0.1: resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==} engines: {node: '>=4'} @@ -3293,6 +3743,10 @@ packages: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} + ansi-styles@2.2.1: + resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} + engines: {node: '>=0.10.0'} + ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -3423,6 +3877,9 @@ packages: asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + asn1.js@4.10.1: + resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + asn1@0.2.6: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} @@ -3434,6 +3891,9 @@ packages: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} + assert@1.5.1: + resolution: {integrity: sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==} + assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} @@ -3524,9 +3984,15 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base-x@3.0.10: + resolution: {integrity: sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==} + base-x@4.0.0: resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==} + base-x@5.0.0: + resolution: {integrity: sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -3555,10 +4021,19 @@ packages: blueimp-md5@2.19.0: resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} + bn.js@4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + + bn.js@5.2.1: + resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + body-parser@1.20.2: resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + boolean@3.2.0: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} @@ -3576,12 +4051,49 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + browser-level@1.0.1: resolution: {integrity: sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==} + browser-pack@6.1.0: + resolution: {integrity: sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==} + hasBin: true + + browser-process-hrtime@0.1.3: + resolution: {integrity: sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==} + browser-readablestream-to-it@2.0.7: resolution: {integrity: sha512-g1Aznml3HmqTLSXylZhGwdfnAa67+vlNAYhT9ROJZkAxY7yYmWusND10olvCMPe4sVhZyVwn5tPkRzOg85kBEg==} + browser-resolve@2.0.0: + resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} + + browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + + browserify-cipher@1.0.1: + resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + + browserify-des@1.0.2: + resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + + browserify-rsa@4.1.0: + resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==} + + browserify-sign@4.2.3: + resolution: {integrity: sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==} + engines: {node: '>= 0.12'} + + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + + browserify@17.0.0: + resolution: {integrity: sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==} + engines: {node: '>= 0.8'} + hasBin: true + browserslist@4.23.0: resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -3595,6 +4107,9 @@ packages: bs58@5.0.0: resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + bs58check@3.0.1: resolution: {integrity: sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==} @@ -3607,6 +4122,12 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + + buffer@5.2.1: + resolution: {integrity: sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -3617,6 +4138,9 @@ packages: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} + builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + builtins@1.0.3: resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} @@ -3661,6 +4185,9 @@ packages: resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} engines: {node: '>=8'} + cached-path-relative@1.1.0: + resolution: {integrity: sha512-WF0LihfemtesFcJgO7xfOoOcnWzY/QHR4qeDqV44jPU3HTI54+LnfXK3SA27AVVGCdZFgjjFFaqUA9Jx7dMJZA==} + cachedir@2.4.0: resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==} engines: {node: '>=6'} @@ -3689,6 +4216,9 @@ packages: resolution: {integrity: sha512-aBMbD1Xxay75ViYezwT40aQONfr+pSXTHwNKvIXhXD6+LY3F1dLIcceoC5OZKBVHbXcysz1hL9D2w0JJIMXpUw==} engines: {node: '>=12.20'} + camel-case@3.0.0: + resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==} + camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} @@ -3748,6 +4278,10 @@ packages: resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} engines: {node: '>=4'} + chalk@1.1.3: + resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} + engines: {node: '>=0.10.0'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -3774,6 +4308,10 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + chart.js@4.4.3: + resolution: {integrity: sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==} + engines: {pnpm: '>=8'} + check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} @@ -3817,6 +4355,9 @@ packages: ci-parallel-vars@1.0.1: resolution: {integrity: sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==} + cipher-base@1.0.4: + resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + classic-level@1.4.1: resolution: {integrity: sha512-qGx/KJl3bvtOHrGau2WklEZuXhS3zme+jf+fsu6Ej7W7IP/C49v7KNlWIsT1jZu0YnfzSIYDGcEWpCa1wKGWXQ==} engines: {node: '>=12'} @@ -3908,6 +4449,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + cmd-shim@6.0.1: resolution: {integrity: sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3916,6 +4461,10 @@ packages: resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + code-point-at@1.1.0: + resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} + engines: {node: '>=0.10.0'} + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -3947,6 +4496,9 @@ packages: resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==} engines: {node: '>=8.0.0'} + combine-source-map@0.8.0: + resolution: {integrity: sha512-UlxQ9Vw0b/Bt/KYwCFqdEwsQ1eL8d1gibiFb7lxQJFdvTgc2hIZi6ugsg+kyhzhPV+QEpUiEIwInIAIrgoEkrg==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -4005,6 +4557,10 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + concat-stream@2.0.0: resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} engines: {'0': node >= 6.0} @@ -4027,9 +4583,15 @@ packages: resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} engines: {node: '>= 0.10.0'} + console-browserify@1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + constants-browserify@1.0.0: + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -4073,6 +4635,12 @@ packages: resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} engines: {node: '>=12'} + convert-source-map@1.1.3: + resolution: {integrity: sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -4116,10 +4684,28 @@ packages: typescript: optional: true + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + create-ecdh@4.0.4: + resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + create-error-class@3.0.2: resolution: {integrity: sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==} engines: {node: '>=0.10.0'} + create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + + create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -4139,10 +4725,24 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + crypto-browserify@3.12.0: + resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} + crypto-random-string@1.0.0: resolution: {integrity: sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==} engines: {node: '>=4'} + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} @@ -4151,6 +4751,10 @@ packages: engines: {node: '>=4'} hasBin: true + csso@4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -4195,6 +4799,12 @@ packages: resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} engines: {node: '>=12'} + d3-color@1.4.1: + resolution: {integrity: sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==} + + d3-color@2.0.0: + resolution: {integrity: sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==} + d3-color@3.1.0: resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} engines: {node: '>=12'} @@ -4207,10 +4817,16 @@ packages: resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} engines: {node: '>=12'} + d3-dispatch@1.0.6: + resolution: {integrity: sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==} + d3-dispatch@3.0.1: resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} engines: {node: '>=12'} + d3-drag@1.2.5: + resolution: {integrity: sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==} + d3-drag@3.0.0: resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} engines: {node: '>=12'} @@ -4220,6 +4836,9 @@ packages: engines: {node: '>=12'} hasBin: true + d3-ease@1.0.7: + resolution: {integrity: sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==} + d3-ease@3.0.1: resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} engines: {node: '>=12'} @@ -4228,10 +4847,16 @@ packages: resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} engines: {node: '>=12'} + d3-fg@6.14.0: + resolution: {integrity: sha512-M4QpFZOEvAq4ZDzwabJp2inL+KXS85T2SQl00zWwjnolaCJR+gHxUbT7Ha4GxTeW1NXwzbykhv/38I1fxQqbyg==} + d3-force@3.0.0: resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} engines: {node: '>=12'} + d3-format@2.0.0: + resolution: {integrity: sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==} + d3-format@3.1.0: resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} engines: {node: '>=12'} @@ -4240,10 +4865,19 @@ packages: resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} engines: {node: '>=12'} + d3-hierarchy@1.1.9: + resolution: {integrity: sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==} + d3-hierarchy@3.1.2: resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} engines: {node: '>=12'} + d3-interpolate@1.4.0: + resolution: {integrity: sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==} + + d3-interpolate@2.0.1: + resolution: {integrity: sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==} + d3-interpolate@3.0.1: resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} engines: {node: '>=12'} @@ -4274,10 +4908,16 @@ packages: resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} engines: {node: '>=12'} + d3-scale@3.3.0: + resolution: {integrity: sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==} + d3-scale@4.0.2: resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} engines: {node: '>=12'} + d3-selection@1.4.2: + resolution: {integrity: sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==} + d3-selection@3.0.0: resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} engines: {node: '>=12'} @@ -4289,24 +4929,39 @@ packages: resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} engines: {node: '>=12'} + d3-time-format@3.0.0: + resolution: {integrity: sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==} + d3-time-format@4.1.0: resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} engines: {node: '>=12'} + d3-time@2.1.1: + resolution: {integrity: sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==} + d3-time@3.1.0: resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} engines: {node: '>=12'} + d3-timer@1.0.10: + resolution: {integrity: sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==} + d3-timer@3.0.1: resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} engines: {node: '>=12'} + d3-transition@1.3.2: + resolution: {integrity: sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==} + d3-transition@3.0.1: resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} engines: {node: '>=12'} peerDependencies: d3-selection: 2 - 3 + d3-zoom@1.8.3: + resolution: {integrity: sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==} + d3-zoom@3.0.0: resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} engines: {node: '>=12'} @@ -4322,6 +4977,9 @@ packages: resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} engines: {node: '>=8'} + dash-ast@1.0.0: + resolution: {integrity: sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==} + dashdash@1.14.1: resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} engines: {node: '>=0.10'} @@ -4351,6 +5009,9 @@ packages: dayjs@1.11.11: resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==} + debounce@1.2.1: + resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -4385,6 +5046,15 @@ packages: supports-color: optional: true + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} engines: {node: '>=0.10.0'} @@ -4449,6 +5119,9 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + defined@1.0.1: + resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==} + delaunator@5.0.1: resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} @@ -4480,10 +5153,17 @@ packages: deprecation@2.3.1: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + deps-sort@2.0.1: + resolution: {integrity: sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==} + hasBin: true + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -4495,13 +5175,23 @@ packages: resolution: {integrity: sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==} engines: {node: '>=4'} - detect-libc@2.0.3: - resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + detective@5.2.1: + resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==} + engines: {node: '>=0.8.0'} + hasBin: true + didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -4517,6 +5207,9 @@ packages: resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} engines: {node: '>=0.3.1'} + diffie-hellman@5.0.3: + resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -4542,9 +5235,26 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + domain-browser@1.2.0: + resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==} + engines: {node: '>=0.4', npm: '>=1.2'} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + dompurify@3.1.5: resolution: {integrity: sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==} + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + dot-prop@4.2.1: resolution: {integrity: sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==} engines: {node: '>=4'} @@ -4557,16 +5267,29 @@ packages: resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} engines: {node: '>=12'} + dotenv-expand@5.1.0: + resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} + dotenv@16.3.2: resolution: {integrity: sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==} engines: {node: '>=12'} + dotenv@7.0.0: + resolution: {integrity: sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==} + engines: {node: '>=6'} + + duplexer2@0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + duplexify@4.1.3: + resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -4595,6 +5318,9 @@ packages: elkjs@0.9.3: resolution: {integrity: sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==} + elliptic@6.5.6: + resolution: {integrity: sha512-mpzdtpeCLuS3BmE3pO3Cpp5bbjlOPY2Q0PgoF+Od1XZrHLYI28Xe3ossCmYCQt11FQKEYd9+PF8jymTvtWJSHQ==} + emittery@1.0.3: resolution: {integrity: sha512-tJdCJitoy2lrC2ldJcqN4vkqJ00lT+tOWNT1hBJjO/3FDMJa5TTIiYGCKGkn/WfCyOzUMObeohbVTj00fhiLiA==} engines: {node: '>=14.16'} @@ -4635,6 +5361,13 @@ packages: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + + entities@3.0.1: + resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} + engines: {node: '>=0.12'} + env-editor@1.1.0: resolution: {integrity: sha512-7AXskzN6T7Q9TFcKAGJprUbpQa4i1VsAetO9rdBqbGMGlragTziBgWt4pVYJMBWHQlLoX0buy6WFikzPH4Qjpw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4643,6 +5376,9 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} + env-string@1.0.1: + resolution: {integrity: sha512-/DhCJDf5DSFK32joQiWRpWrT0h7p3hVQfMKxiBb7Nt8C8IF8BYyPtclDnuGGLOoj16d/8udKeiE7JbkotDmorQ==} + envinfo@7.13.0: resolution: {integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==} engines: {node: '>=4'} @@ -4978,6 +5714,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-is-member-expression@1.0.0: + resolution: {integrity: sha512-Ec+X44CapIGExvSZN+pGkmr5p7HwUVQoPQSd458Lqwvaf4/61k/invHSh4BYK8OXnCkfEhWuIoG5hayKLQStIg==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -5013,6 +5752,9 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + execa@0.7.0: resolution: {integrity: sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==} engines: {node: '>=4'} @@ -5037,6 +5779,9 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + execspawn@1.0.1: + resolution: {integrity: sha512-s2k06Jy9i8CUkYe0+DxRlvtkZoOkwwfhB+Xxo5HGUtrISVW2m98jO2tr67DGRFxZwkjQqloA3v/tNtjhBRBieg==} + executable@4.1.1: resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==} engines: {node: '>=4'} @@ -5090,9 +5835,15 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-sha256@1.3.0: resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + fast-uri@3.0.1: + resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + fast-xml-parser@4.4.0: resolution: {integrity: sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==} hasBin: true @@ -5247,6 +5998,10 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + fs-extra@11.2.0: resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} engines: {node: '>=14.14'} @@ -5303,6 +6058,9 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-assigned-identifiers@1.2.0: + resolution: {integrity: sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -5322,6 +6080,10 @@ packages: engines: {node: '>=6.9.0'} hasBin: true + get-port@4.2.0: + resolution: {integrity: sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==} + engines: {node: '>=6'} + get-port@5.1.1: resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} engines: {node: '>=8'} @@ -5498,6 +6260,10 @@ packages: resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} engines: {node: '>=6'} + has-ansi@2.0.0: + resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} + engines: {node: '>=0.10.0'} + has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} @@ -5531,6 +6297,17 @@ packages: resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==} engines: {node: '>= 0.4.0'} + hash-base@3.0.4: + resolution: {integrity: sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==} + engines: {node: '>=4'} + + hash-base@3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + hashlru@2.3.0: resolution: {integrity: sha512-0cMsjjIC8I+D3M44pOQdsy0OHXGLVz6Z0beRuufhKa0KfaD2wGwAev6jILzXsd3/vpnNQJmWyZtIILqM1N+n5A==} @@ -5557,6 +6334,9 @@ packages: resolution: {integrity: sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ==} engines: {node: '>=8'} + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} @@ -5572,6 +6352,45 @@ packages: resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hsl-to-rgb-for-reals@1.1.1: + resolution: {integrity: sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==} + + htmlescape@1.1.1: + resolution: {integrity: sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==} + engines: {node: '>=0.10'} + + htmlnano@2.1.1: + resolution: {integrity: sha512-kAERyg/LuNZYmdqgCdYvugyLWNFAm8MWXpQMz1pLpetmCbFwoMxvkSoaAMlFrOC4OKTWI4KlZGT/RsNxg4ghOw==} + peerDependencies: + cssnano: ^7.0.0 + postcss: ^8.3.11 + purgecss: ^6.0.0 + relateurl: ^0.2.7 + srcset: 5.0.1 + svgo: ^3.0.2 + terser: ^5.10.0 + uncss: ^0.17.3 + peerDependenciesMeta: + cssnano: + optional: true + postcss: + optional: true + purgecss: + optional: true + relateurl: + optional: true + srcset: + optional: true + svgo: + optional: true + terser: + optional: true + uncss: + optional: true + + htmlparser2@7.2.0: + resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==} + http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} @@ -5591,6 +6410,9 @@ packages: resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} engines: {node: '>=10.19.0'} + https-browserify@1.0.0: + resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} + https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -5614,6 +6436,12 @@ packages: humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + hyperscript-attribute-to-property@1.0.2: + resolution: {integrity: sha512-oerMul16jZCmrbNsUw8QgrtDzF8lKgFri1bKQjReLw1IhiiNkI59CWuzZjJDGT79UQ1YiWqXhJMv/tRMVqgtkA==} + + hyperx@2.5.4: + resolution: {integrity: sha512-iOkSh7Yse7lsN/B9y7OsevLWjeXPqGuHQ5SbwaiJM5xAhWFqhoN6erpK1dQsS12OFU36lyai1pnx1mmzWLQqcA==} + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -5689,6 +6517,9 @@ packages: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -5703,6 +6534,9 @@ packages: resolution: {integrity: sha512-kBhlSheBfYmq3e0L1ii+VKe3zBTLL5lDCDWR+f9dLmEGSB3MqLlMlsolubSsyI88Bg6EA+BIMlomAnQ1SwgQBw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + inline-source-map@0.6.3: + resolution: {integrity: sha512-1aVsPEsJWMJq/pdMU61CDlm1URcW702MTB4w9/zUjMus6H/Py8o7g68Pr9D4I6QluWGt/KdmswuRhaA05xVR1w==} + inquirer-select-pro@1.0.0-alpha.6: resolution: {integrity: sha512-Jk54JJWBS7jJvApLFUoRxq84Yx9Zs8skLvim9mrR1rkXt+WsTXlTPbdYHt0vRNwDkqTAmkDmkjUX4wtzYBsVYA==} engines: {node: '>=18.0.0', pnpm: '>=8.15.4'} @@ -5715,15 +6549,22 @@ packages: resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} engines: {node: '>=12.0.0'} + insert-module-globals@7.2.1: + resolution: {integrity: sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==} + hasBin: true + interface-blockstore@5.2.10: resolution: {integrity: sha512-9K48hTvBCGsKVD3pF4ILgDcf+W2P/gq0oxLcsHGB6E6W6nDutYkzR+7k7bCs9REHrBEfKzcVDEKieiuNM9WRZg==} - interface-datastore@8.2.11: - resolution: {integrity: sha512-9E0iXehfp/j0UbZ2mvlYB4K9pP7uQBCppfuy8WHs1EHF6wLQrM9+zwyX+8Qt6HnH4GKZRyXX/CNXm6oD4+QYgA==} + interface-datastore@8.3.0: + resolution: {integrity: sha512-RM/rTSmRcnoCwGZIHrPm+nlGYVoT4R0lcFvNnDyhdFT4R6BuHHhfFP47UldVEjs98SfxLuMhaNMsyjI918saHw==} interface-store@5.1.8: resolution: {integrity: sha512-7na81Uxkl0vqk0CBPO5PvyTkdaJBaezwUJGsMOz7riPOq0rJt+7W31iaopaMICWea/iykUsvNlPx/Tc+MxC3/w==} + interface-store@6.0.0: + resolution: {integrity: sha512-HkjsDPsjA7SKkCr+TH1elUQApAAM3X3JPwrz3vFzaf614wI+ZD6GVvwKGZCHYcbSRqeZP/uzVPqezzeISeo5kA==} + internal-slot@1.0.7: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} @@ -5790,10 +6631,16 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} + is-boolean-attribute@0.0.1: + resolution: {integrity: sha512-0kXT52Scokg2Miscvsn5UVqg6y1691vcLJcagie1YHJB4zOEuAhMERLX992jtvaStGy2xQTqOtJhvmG/MK1T5w==} + is-boolean-object@1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + is-buffer@2.0.5: resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} engines: {node: '>=4'} @@ -5852,6 +6699,10 @@ packages: is-finalizationregistry@1.0.2: resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + is-fullwidth-code-point@1.0.0: + resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==} + engines: {node: '>=0.10.0'} + is-fullwidth-code-point@2.0.0: resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} engines: {node: '>=4'} @@ -5899,6 +6750,9 @@ packages: is-js-type@2.0.0: resolution: {integrity: sha512-Aj13l47+uyTjlQNHtXBV8Cji3jb037vxwMWCgopRR8h6xocgBGW3qG8qGlIOEmbXQtkKShKuBM9e8AA1OeQ+xw==} + is-json@2.0.1: + resolution: {integrity: sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==} + is-lambda@1.0.1: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} @@ -6311,6 +7165,9 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -6342,6 +7199,11 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} + jsonstream2@3.0.0: + resolution: {integrity: sha512-8ngq2XB8NjYrpe3+Xtl9lFJl6RoV2dNT4I7iyaHwxUpTBwsj0AlAR7epGfeYVP0z4Z7KxMoSxRgJWrd2jmBT/Q==} + engines: {node: '>=5.10.0'} + hasBin: true + jsprim@2.0.2: resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==} engines: {'0': node >=0.6.0} @@ -6375,6 +7237,9 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + labeled-stream-splicer@2.0.2: + resolution: {integrity: sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==} + latest-version@3.1.0: resolution: {integrity: sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==} engines: {node: '>=4'} @@ -6431,6 +7296,70 @@ packages: lighthouse-logger@1.4.2: resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==} + lightningcss-darwin-arm64@1.26.0: + resolution: {integrity: sha512-n4TIvHO1NY1ondKFYpL2ZX0bcC2y6yjXMD6JfyizgR8BCFNEeArINDzEaeqlfX9bXz73Bpz/Ow0nu+1qiDrBKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.26.0: + resolution: {integrity: sha512-Rf9HuHIDi1R6/zgBkJh25SiJHF+dm9axUZW/0UoYCW1/8HV0gMI0blARhH4z+REmWiU1yYT/KyNF3h7tHyRXUg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.26.0: + resolution: {integrity: sha512-C/io7POAxp6sZxFSVGezjajMlCKQ8KSwISLLGRq8xLQpQMokYrUoqYEwmIX8mLmF6C/CZPk0gFmRSzd8biWM0g==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.26.0: + resolution: {integrity: sha512-Aag9kqXqkyPSW+dXMgyWk66C984Nay2pY8Nws+67gHlDzV3cWh7TvFlzuaTaVFMVqdDTzN484LSK3u39zFBnzg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.26.0: + resolution: {integrity: sha512-iJmZM7fUyVjH+POtdiCtExG+67TtPUTer7K/5A8DIfmPfrmeGvzfRyBltGhQz13Wi15K1lf2cPYoRaRh6vcwNA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.26.0: + resolution: {integrity: sha512-XxoEL++tTkyuvu+wq/QS8bwyTXZv2y5XYCMcWL45b8XwkiS8eEEEej9BkMGSRwxa5J4K+LDeIhLrS23CpQyfig==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.26.0: + resolution: {integrity: sha512-1dkTfZQAYLj8MUSkd6L/+TWTG8V6Kfrzfa0T1fSlXCXQHrt1HC1/UepXHtKHDt/9yFwyoeayivxXAsApVxn6zA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.26.0: + resolution: {integrity: sha512-yX3Rk9m00JGCUzuUhFEojY+jf/6zHs3XU8S8Vk+FRbnr4St7cjyMXdNjuA2LjiT8e7j8xHRCH8hyZ4H/btRE4A==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.26.0: + resolution: {integrity: sha512-X/597/cFnCogy9VItj/+7Tgu5VLbAtDF7KZDPdSw0MaL6FL940th1y3HiOzFIlziVvAtbo0RB3NAae1Oofr+Tw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.26.0: + resolution: {integrity: sha512-pYS3EyGP3JRhfqEFYmfFDiZ9/pVNfy8jVIYtrx9TVNusVyDK3gpW1w/rbvroQ4bDJi7grdUtyrYU6V2xkY/bBw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.26.0: + resolution: {integrity: sha512-a/XZ5hdgifrofQJUArr5AiJjx26SwMam3SJUSMjgebZbESZ96i+6Qsl8tLi0kaUsdMzBWXh9sN1Oe6hp2/dkQw==} + engines: {node: '>= 12.0.0'} + lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -6459,6 +7388,10 @@ packages: enquirer: optional: true + lmdb@2.8.5: + resolution: {integrity: sha512-9bMdFfc80S+vSldBmG3HOuLVHnxRdNTlpzR6QDnzqCQtCzGUEAGTzBKYMeIM+I/sU4oZfgbcbS7X7F65/z/oxQ==} + hasBin: true + load-json-file@4.0.0: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} @@ -6512,6 +7445,9 @@ packages: lodash.ismatch@4.4.0: resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} + lodash.memoize@3.0.4: + resolution: {integrity: sha512-eDn9kqrAmVUC1wmZvlQ6Uhde44n+tXpqPrN8olQJbttgh0oKclk+SF54P47VEGE9CEiMeRwAP8BaM7UHvBkz2A==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -6549,6 +7485,9 @@ packages: loupe@2.3.7: resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + lower-case@1.1.4: + resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} + lowercase-keys@1.0.1: resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==} engines: {node: '>=0.10.0'} @@ -6579,10 +7518,17 @@ packages: resolution: {integrity: sha512-5OUtoiVIGU4VXBOshidmtOsvBIvcQR6FD/RzWSvaeHyxCGB+PCUCu+52lqMfdc0h/2CLvHhZS4TwUmMQrrMbBQ==} engines: {node: '>= 0.4.0'} + luxon@3.5.0: + resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==} + engines: {node: '>=12'} + lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true + magic-string@0.23.2: + resolution: {integrity: sha512-oIUZaAxbcxYIp4AyLafV6OVKoB3YouZs0UTCJ8mOKBHNyJgGDaMJ4TgA+VylJh6fx7EQCC52XkbURxxG9IoJXA==} + magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} @@ -6594,6 +7540,10 @@ packages: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -6639,12 +7589,18 @@ packages: resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==} engines: {node: '>=8'} + md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + mdast-util-from-markdown@1.3.1: resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==} mdast-util-to-string@3.2.0: resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -6678,6 +7634,9 @@ packages: resolution: {integrity: sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==} engines: {node: '>=10'} + merge-source-map@1.0.4: + resolution: {integrity: sha512-PGSmS0kfnTnMJCzJ16BLLCEe6oeYCamKFFdQKshi4BmM6FUwipjVOcBFGxqtQtirtAG4iZvHlqST9CpZKqlRjA==} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -6820,6 +7779,10 @@ packages: resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} engines: {node: '>=8.6'} + miller-rabin@4.0.1: + resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} + hasBin: true + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -6858,6 +7821,12 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + minimatch@3.0.5: resolution: {integrity: sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==} @@ -6953,10 +7922,18 @@ packages: resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} engines: {node: '>=0.10.0'} + module-deps@6.2.3: + resolution: {integrity: sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==} + engines: {node: '>= 0.8.0'} + hasBin: true + module-error@1.0.2: resolution: {integrity: sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==} engines: {node: '>=10'} + morphdom@2.7.4: + resolution: {integrity: sha512-ATTbWMgGa+FaMU3FhnFYB6WgulCqwf6opOll4CBzmVDTLvPMmUPrEv8CudmLPK0MESa64+6B89fWOxP3+YIlxQ==} + mortice@3.0.4: resolution: {integrity: sha512-MUHRCAztSl4v/dAmK8vbYi5u1n9NZtQu4H3FsqS7qgMFQIAFw9lTpHiErd9kJpapqmvEdD1L3dUmiikifAvLsQ==} @@ -6973,6 +7950,10 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + ms@3.0.0-canary.1: + resolution: {integrity: sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==} + engines: {node: '>=12.13'} + msgpackr-extract@3.0.2: resolution: {integrity: sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==} hasBin: true @@ -7006,9 +7987,22 @@ packages: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mutexify@1.4.0: + resolution: {integrity: sha512-pbYSsOrSB/AKN5h/WzzLRMFgZhClWccf2XIB4RSMC8JbquiB0e0/SH5AIfdQMdyHmYtv4seU7yV/TvAwPLJ1Yg==} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoassert@1.1.0: + resolution: {integrity: sha512-C40jQ3NzfkP53NsO8kEOFd79p4b9kDXQMwgiY1z8ZwrDZgUyom0AHwGegF4Dm99L+YoYhuaB0ceerUcXmqr1rQ==} + + nanobench@2.1.1: + resolution: {integrity: sha512-z+Vv7zElcjN+OpzAxAquUayFLGK3JI/ubCl0Oh64YQqsTGG09CGqieJVQw4ui8huDnnAgrvTv93qi5UaOoNj8A==} + hasBin: true + + nanohtml@1.10.0: + resolution: {integrity: sha512-r/3AQl+jxAxUIJRiKExUjBtFcE1cm4yTOsTIdVqqlxPNtBxJh522ANrcQYzdNHhPzbPgb7j6qujq6eGehBX0kg==} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -7037,6 +8031,9 @@ packages: nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + no-case@2.3.2: + resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} + nocache@3.0.4: resolution: {integrity: sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==} engines: {node: '>=12.0.0'} @@ -7051,6 +8048,12 @@ packages: node-addon-api@3.2.1: resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} + node-addon-api@6.1.0: + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-datachannel@0.10.1: resolution: {integrity: sha512-rhxb1iQgbFLY6HMt3W6Xcs8Q1k4jIMgI7KduXcYvIn2UMKYK6e/eegya2caF/+XYAqTeo1743gOr11CXvJ/DJA==} engines: {node: '>=16.0.0'} @@ -7106,6 +8109,9 @@ packages: resolution: {integrity: sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==} engines: {node: '>=0.12.0'} + nodemark@0.3.0: + resolution: {integrity: sha512-ehT+NfV5liLY1sZVcosWiCViWHltQTnjsh/7GYkMkirzuyYw/K2VAbgwEIldAM1e9LJ3coGF6uBlTw1/EdZ1XA==} + nofilter@3.1.0: resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==} engines: {node: '>=12.19'} @@ -7118,6 +8124,10 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} hasBin: true + normalize-html-whitespace@0.2.0: + resolution: {integrity: sha512-5CZAEQ4bQi8Msqw0GAT6rrkrjNN4ZKqAG3+jJMwms4O6XoMvh6ekwOueG4mRS1LbPUR1r9EdnhxxfpzMTOdzKw==} + engines: {node: '>= 0.10'} + normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} @@ -7206,9 +8216,16 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This package is no longer supported. + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + nullthrows@1.1.1: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + number-is-nan@1.0.1: + resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} + engines: {node: '>=0.10.0'} + nx@16.10.0: resolution: {integrity: sha512-gZl4iCC0Hx0Qe1VWmO4Bkeul2nttuXdPpfnlcDKSACGu3ZIo+uySqwOF8yBAxSTIf8xe2JRhgzJN1aFkuezEBg==} hasBin: true @@ -7284,6 +8301,10 @@ packages: resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} engines: {node: '>= 0.8'} + on-net-listen@1.1.2: + resolution: {integrity: sha512-y1HRYy8s/RlcBvDUwKXSmkODMdx4KSuIvloCnQYJ2LdBBC1asY4HtfhXwe3UWknLakATZDnbzht2Ijw3M1EqFg==} + engines: {node: '>=9.4.0 || ^8.9.4'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -7311,6 +8332,10 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + opn@5.5.0: + resolution: {integrity: sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==} + engines: {node: '>=4'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -7319,6 +8344,12 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} + ordered-binary@1.5.1: + resolution: {integrity: sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==} + + os-browserify@0.3.0: + resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -7455,10 +8486,25 @@ packages: resolution: {integrity: sha512-2u0TrjcGbOjBTJpyewEl4hBO3OeX5wWue7eIFPzQTg6wFSvoaHcBTTUY5m+n0hd04gmTCPuY0kCpVIVuw5etwg==} engines: {node: '>= 4.0.0'} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + parcel@2.12.0: + resolution: {integrity: sha512-W+gxAq7aQ9dJIg/XLKGcRT0cvnStFAQHPaI0pvD0U2l6IVLueUAm3nwN7lkY62zZNmlvNx6jNtE4wlbS+CyqSg==} + engines: {node: '>= 12.0.0'} + hasBin: true + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parents@1.0.1: + resolution: {integrity: sha512-mXKF3xkoUt5td2DoxpLmtOmZvko9VfFpwRwkKDHSNvgmpLAeBo18YDhcPbBzJq+QLCHMbGOfzia2cX4U+0v9Mg==} + + parse-asn1@5.1.7: + resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==} + engines: {node: '>= 0.10'} + parse-json@4.0.0: resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} engines: {node: '>=4'} @@ -7481,6 +8527,9 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} @@ -7515,6 +8564,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-platform@0.11.15: + resolution: {integrity: sha512-Y30dB6rab1A/nfEKsZxmr01nUotHX0c/ZiIAsCTatEe1CmS5Pm5He7fZ195bPT7RdquoaL8lLxFCMQi/bS7IJg==} + engines: {node: '>= 0.8.0'} + path-scurry@1.11.1: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} @@ -7536,6 +8589,10 @@ packages: pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pbkdf2@3.1.2: + resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} + engines: {node: '>=0.12'} + pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -7668,6 +8725,22 @@ packages: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} + posthtml-parser@0.10.2: + resolution: {integrity: sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==} + engines: {node: '>=12'} + + posthtml-parser@0.11.0: + resolution: {integrity: sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==} + engines: {node: '>=12'} + + posthtml-render@3.0.0: + resolution: {integrity: sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==} + engines: {node: '>=12'} + + posthtml@0.16.6: + resolution: {integrity: sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==} + engines: {node: '>=12.0.0'} + prebuild-install@7.1.2: resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} engines: {node: '>=10'} @@ -7706,6 +8779,10 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-hrtime@1.0.3: + resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} + engines: {node: '>= 0.8'} + pretty-ms@8.0.0: resolution: {integrity: sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==} engines: {node: '>=14.16'} @@ -7780,9 +8857,18 @@ packages: psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + public-encrypt@4.0.3: + resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + pump@3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + pumpify@2.0.1: + resolution: {integrity: sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==} + + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -7802,6 +8888,14 @@ packages: resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} engines: {node: '>=0.6'} + qs@6.12.3: + resolution: {integrity: sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==} + engines: {node: '>=0.6'} + + querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + querystring@0.2.1: resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==} engines: {node: '>=0.4.x'} @@ -7813,6 +8907,9 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + queue@6.0.2: resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} @@ -7833,6 +8930,9 @@ packages: randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -7860,6 +8960,9 @@ packages: peerDependencies: react: ^18.3.1 + react-error-overlay@6.0.9: + resolution: {integrity: sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==} + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -7889,6 +8992,10 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-refresh@0.9.0: + resolution: {integrity: sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==} + engines: {node: '>=0.10.0'} + react-shallow-renderer@16.15.0: resolution: {integrity: sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==} peerDependencies: @@ -7910,6 +9017,9 @@ packages: resolution: {integrity: sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + read-only-stream@2.0.0: + resolution: {integrity: sha512-3ALe0bjBVZtkdWKIcThYpQCLbBMd/+Tbh2CDSrAIDO3UsZ4Xs+tnyjv2MjCOMMgBG+AsUOeuP1cgtY1INISc8w==} + read-package-json-fast@3.0.2: resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -8023,6 +9133,10 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} @@ -8100,6 +9214,12 @@ packages: engines: {node: '>=14.18'} hasBin: true + ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + + rng@0.2.2: + resolution: {integrity: sha512-3F/A3swXdqb4JLpF22zwlCllyXlUWEE3T35GzJ+Vvtx4HnUYVAXovaK2TyUC04UlpxdVxB+E7HaKdDrGeOcJuA==} + roarr@2.15.4: resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} engines: {node: '>=8.0'} @@ -8237,10 +9357,17 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + shallow-clone@3.0.1: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} + shasum-object@1.0.0: + resolution: {integrity: sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==} + shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -8295,6 +9422,9 @@ packages: simple-get@4.0.1: resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + single-line-log@1.1.2: + resolution: {integrity: sha512-awzaaIPtYFdexLr6TBpcZSGPB6D1RInNO/qNetgaJloPDF/D0GkVtLvGEp8InfmLV7CyLyQ5fIRP+tVN/JmWQA==} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -8365,6 +9495,10 @@ packages: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} + sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -8380,6 +9514,10 @@ packages: split2@3.2.2: resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + split@1.0.1: resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} @@ -8389,6 +9527,10 @@ packages: sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + srcset@4.0.0: + resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} + engines: {node: '>=12'} + sshpk@1.18.0: resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} engines: {node: '>=0.10.0'} @@ -8402,6 +9544,10 @@ packages: resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + stable@0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -8431,9 +9577,28 @@ packages: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + + stream-combiner2@1.1.1: + resolution: {integrity: sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==} + + stream-http@3.2.0: + resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} + + stream-shift@1.0.3: + resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + + stream-splicer@2.0.1: + resolution: {integrity: sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==} + stream-to-it@1.0.1: resolution: {integrity: sha512-AqHYAYPHcmvMrcLNgncE/q0Aj/ajP6A4qGhxP6EVn7K3YTNs0bJpJyk57wc2Heb7MUL64jurvmnmui8D9kjZgA==} + string-width@1.0.2: + resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==} + engines: {node: '>=0.10.0'} + string-width@2.1.1: resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==} engines: {node: '>=4'} @@ -8471,6 +9636,10 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@3.0.1: + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} + engines: {node: '>=0.10.0'} + strip-ansi@4.0.0: resolution: {integrity: sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==} engines: {node: '>=4'} @@ -8533,6 +9702,9 @@ packages: stylis@4.3.2: resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==} + subarg@1.0.0: + resolution: {integrity: sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==} + sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} @@ -8553,6 +9725,10 @@ packages: resolution: {integrity: sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + supports-color@2.0.0: + resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} + engines: {node: '>=0.8.0'} + supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -8565,6 +9741,10 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} + supports-color@9.4.0: + resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==} + engines: {node: '>=12'} + supports-hyperlinks@2.3.0: resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} engines: {node: '>=8'} @@ -8573,14 +9753,25 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svgo@2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + synckit@0.8.8: resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==} engines: {node: ^14.18.0 || >=16.0.0} + syntax-error@1.4.0: + resolution: {integrity: sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==} + system-architecture@0.1.0: resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} engines: {node: '>=18'} + tachyons@4.12.0: + resolution: {integrity: sha512-2nA2IrYFy3raCM9fxJ2KODRGHVSZNTW3BR0YnlGsLUf1DA3pk3YfWZ/DdfbnZK6zLZS+jUenlUGJsKcA5fUiZg==} + tailwindcss@3.4.3: resolution: {integrity: sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==} engines: {node: '>=14.0.0'} @@ -8625,6 +9816,10 @@ packages: resolution: {integrity: sha512-7dPUZQGy/+m3/wjVz3ZW5dobSoD/02NxJpoXUX0WIyjfVS3l0c+b/+9phIDFA7FHzkYtwtMFgeGZ/Y8jVTeqQQ==} engines: {node: '>=4'} + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + terser-webpack-plugin@5.3.10: resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} @@ -8669,6 +9864,12 @@ packages: through2@2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + through2@3.0.2: + resolution: {integrity: sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==} + + through2@4.0.2: + resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -8690,10 +9891,17 @@ packages: timeout-abort-controller@3.0.0: resolution: {integrity: sha512-O3e+2B8BKrQxU2YRyEjC/2yFdb33slI22WRdUaDx6rvysfi9anloNZyR2q0l6LnePo5qH7gSM7uZtvvwZbc2yA==} + timers-browserify@1.4.2: + resolution: {integrity: sha512-PIxwAupJZiYU4JmVZYwXp9FKsHMXb5h0ZEFyuXTAn8WLHOlcij+FEcbrvDsom1o5dr1YggEtFbECvGCW2sT53Q==} + engines: {node: '>=0.6.0'} + timestamp-nano@1.0.1: resolution: {integrity: sha512-4oGOVZWTu5sl89PtCDnhQBSt7/vL1zVEwAfxH1p49JhTosxzVQWYBYFRFZ8nJmo0G6f824iyP/44BFAwIoKvIA==} engines: {node: '>= 4.5.0'} + timsort@0.3.0: + resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==} + tiny-emitter@2.1.0: resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} @@ -8748,6 +9956,9 @@ packages: tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + transform-ast@2.4.4: + resolution: {integrity: sha512-AxjeZAcIOUO2lev2GDe3/xZ1Q0cVGjIMk5IsriTy8zbWlsEnjeB025AhkhBJHoy997mXpLd4R+kRbvnnQVuQHQ==} + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -8825,6 +10036,9 @@ packages: typescript: optional: true + tty-browserify@0.0.1: + resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} + tuf-js@1.1.7: resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -8839,6 +10053,9 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-component@0.0.1: + resolution: {integrity: sha512-mDZRBQS2yZkwRQKfjJvQ8UIYJeBNNWCq+HBNstl9N5s9jZ4dkVYXEGkVPsSCEh5Ld4JM1kmrZTzjnrqSAIQ7dw==} + type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} @@ -8936,6 +10153,10 @@ packages: uint8arrays@5.1.0: resolution: {integrity: sha512-vA6nFepEmlSKkMBnLBaUMVvAC4G3CTmO58C12y4sq6WPDOR7mOFYOi7GlrQ4djeSbP6JG9Pv9tJDM97PedRSww==} + umd@3.0.3: + resolution: {integrity: sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==} + hasBin: true + unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -8943,6 +10164,10 @@ packages: resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} engines: {node: '>=0.10.0'} + undeclared-identifiers@1.1.3: + resolution: {integrity: sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==} + hasBin: true + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -9032,6 +10257,9 @@ packages: resolution: {integrity: sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==} engines: {node: '>=4'} + upper-case@1.1.3: + resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -9045,12 +10273,29 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + url@0.11.4: + resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} + engines: {node: '>= 0.4'} + utf8-byte-length@1.0.5: resolution: {integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==} util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util-extend@1.0.3: + resolution: {integrity: sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==} + + util@0.10.4: + resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} + + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + + utility-types@3.11.0: + resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==} + engines: {node: '>= 4'} + utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -9171,6 +10416,9 @@ packages: vlq@1.0.1: resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} + vm-browserify@1.1.2: + resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} @@ -9181,6 +10429,12 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + weak-lru-cache@1.2.2: + resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==} + + weald@1.0.2: + resolution: {integrity: sha512-iG5cIuBwsPe1ZcoGGd4X6QYlepU1vLr4l4oWpzQWqeJPSo9B8bxxyE6xlnj3TCmThtha7gyVL+uuZgUFkPyfDg==} + web-worker@1.3.0: resolution: {integrity: sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==} @@ -9432,6 +10686,39 @@ packages: snapshots: + 0x@5.7.0: + dependencies: + ajv: 8.17.1 + browserify: 17.0.0 + concat-stream: 2.0.0 + d3-fg: 6.14.0 + debounce: 1.2.1 + debug: 4.3.6(supports-color@8.1.1) + end-of-stream: 1.4.4 + env-string: 1.0.1 + escape-string-regexp: 4.0.0 + execspawn: 1.0.1 + fs-extra: 10.1.0 + has-unicode: 2.0.1 + hsl-to-rgb-for-reals: 1.1.1 + jsonstream2: 3.0.0 + make-dir: 3.1.0 + minimist: 1.2.8 + morphdom: 2.7.4 + nanohtml: 1.10.0 + on-net-listen: 1.1.2 + opn: 5.5.0 + pump: 3.0.0 + pumpify: 2.0.1 + semver: 7.6.2 + single-line-log: 1.1.2 + split2: 4.2.0 + tachyons: 4.12.0 + through2: 4.0.2 + which: 2.0.2 + transitivePeerDependencies: + - supports-color + '@achingbrain/nat-port-mapper@1.0.13': dependencies: '@achingbrain/ssdp': 4.0.6 @@ -9461,9 +10748,9 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@automerge/automerge-repo-network-broadcastchannel@1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3)': + '@automerge/automerge-repo-network-broadcastchannel@1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3)': dependencies: - '@automerge/automerge-repo': 1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3) + '@automerge/automerge-repo': 1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3) transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -9474,7 +10761,7 @@ snapshots: '@automerge/automerge-repo-network-messagechannel@1.1.12(@swc/core@1.5.24)(@types/node@20.14.11)(typescript@5.4.5)': dependencies: '@automerge/automerge-repo': 1.1.12(@swc/core@1.5.24)(@types/node@20.14.11)(typescript@5.4.5) - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) eventemitter3: 5.0.1 transitivePeerDependencies: - '@swc/core' @@ -9483,11 +10770,11 @@ snapshots: - supports-color - typescript - '@automerge/automerge-repo-network-websocket@1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3)': + '@automerge/automerge-repo-network-websocket@1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3)': dependencies: - '@automerge/automerge-repo': 1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3) + '@automerge/automerge-repo': 1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3) cbor-x: 1.5.9 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 eventemitter3: 5.0.1 isomorphic-ws: 5.0.0(ws@8.17.0) ws: 8.17.0 @@ -9504,7 +10791,7 @@ snapshots: dependencies: '@automerge/automerge-repo': 1.1.12(@swc/core@1.5.24)(@types/node@20.14.11)(typescript@5.5.3) cbor-x: 1.5.9 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 eventemitter3: 5.0.1 isomorphic-ws: 5.0.0(ws@8.17.0) ws: 8.17.0 @@ -9517,10 +10804,10 @@ snapshots: - typescript - utf-8-validate - '@automerge/automerge-repo-react-hooks@1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3)': + '@automerge/automerge-repo-react-hooks@1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3)': dependencies: '@automerge/automerge': 2.2.2 - '@automerge/automerge-repo': 1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3) + '@automerge/automerge-repo': 1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3) eventemitter3: 5.0.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -9532,9 +10819,9 @@ snapshots: - supports-color - typescript - '@automerge/automerge-repo-storage-indexeddb@1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3)': + '@automerge/automerge-repo-storage-indexeddb@1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3)': dependencies: - '@automerge/automerge-repo': 1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3) + '@automerge/automerge-repo': 1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3) transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -9564,16 +10851,16 @@ snapshots: - supports-color - typescript - '@automerge/automerge-repo@1.1.12(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3)': + '@automerge/automerge-repo@1.1.12(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3)': dependencies: '@automerge/automerge': 2.2.2 bs58check: 3.0.1 cbor-x: 1.5.9 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 eventemitter3: 5.0.1 fast-sha256: 1.3.0 tiny-typed-emitter: 2.1.0 - ts-node: 10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3) + ts-node: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3) uuid: 9.0.1 xstate: 5.13.0 transitivePeerDependencies: @@ -9588,7 +10875,7 @@ snapshots: '@automerge/automerge': 2.2.2 bs58check: 3.0.1 cbor-x: 1.5.9 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 eventemitter3: 5.0.1 fast-sha256: 1.3.0 tiny-typed-emitter: 2.1.0 @@ -9607,7 +10894,7 @@ snapshots: '@automerge/automerge': 2.2.2 bs58check: 3.0.1 cbor-x: 1.5.9 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 eventemitter3: 5.0.1 fast-sha256: 1.3.0 tiny-typed-emitter: 2.1.0 @@ -9655,7 +10942,7 @@ snapshots: '@babel/traverse': 7.24.6 '@babel/types': 7.24.6 convert-source-map: 2.0.0 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -9730,7 +11017,7 @@ snapshots: '@babel/core': 7.24.6 '@babel/helper-compilation-targets': 7.24.8 '@babel/helper-plugin-utils': 7.24.8 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -10631,7 +11918,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.6 '@babel/parser': 7.24.6 '@babel/types': 7.24.6 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -10646,7 +11933,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.24.8 '@babel/types': 7.24.9 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -10792,7 +12079,7 @@ snapshots: '@electron/get@2.0.3': dependencies: - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) env-paths: 2.2.1 fs-extra: 8.1.0 got: 11.8.6 @@ -10963,7 +12250,7 @@ snapshots: '@eslint/config-array@0.17.1': dependencies: '@eslint/object-schema': 2.1.4 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -10971,7 +12258,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -10985,7 +12272,7 @@ snapshots: '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) espree: 10.1.0 globals: 14.0.0 ignore: 5.3.1 @@ -11080,7 +12367,7 @@ snapshots: '@libp2p/interface': 1.6.1 '@multiformats/dns': 1.0.6 interface-blockstore: 5.2.10 - interface-datastore: 8.2.11 + interface-datastore: 8.3.0 interface-store: 5.1.8 multiformats: 13.2.1 progress-events: 1.0.1 @@ -11115,7 +12402,7 @@ snapshots: blockstore-core: 4.4.1 cborg: 4.2.3 interface-blockstore: 5.2.10 - interface-datastore: 8.2.11 + interface-datastore: 8.3.0 interface-store: 5.1.8 it-drain: 3.0.7 it-filter: 3.1.1 @@ -11140,7 +12427,7 @@ snapshots: '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -11383,6 +12670,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + '@kurkle/color@0.3.2': {} + '@leichtgewicht/ip-codec@2.0.5': {} '@lerna/child-process@7.4.2': @@ -11467,6 +12756,12 @@ snapshots: - supports-color - typescript + '@lezer/common@1.2.1': {} + + '@lezer/lr@1.4.2': + dependencies: + '@lezer/common': 1.2.1 + '@libp2p/autonat@1.1.2': dependencies: '@libp2p/interface': 1.6.1 @@ -11536,10 +12831,21 @@ snapshots: uint8arraylist: 2.4.8 uint8arrays: 5.1.0 - '@libp2p/dcutr@1.1.2': + '@libp2p/crypto@4.1.9': dependencies: - '@libp2p/interface': 1.6.1 - '@libp2p/interface-internal': 1.3.1 + '@libp2p/interface': 1.7.0 + '@noble/curves': 1.4.2 + '@noble/hashes': 1.4.0 + asn1js: 3.0.5 + multiformats: 13.2.1 + protons-runtime: 5.4.0 + uint8arraylist: 2.4.8 + uint8arrays: 5.1.0 + + '@libp2p/dcutr@1.1.2': + dependencies: + '@libp2p/interface': 1.6.1 + '@libp2p/interface-internal': 1.3.1 '@libp2p/utils': 5.4.6 '@multiformats/multiaddr': 12.3.0 '@multiformats/multiaddr-matcher': 1.2.4 @@ -11586,6 +12892,14 @@ snapshots: transitivePeerDependencies: - supports-color + '@libp2p/interface-internal@1.3.4': + dependencies: + '@libp2p/interface': 1.7.0 + '@libp2p/peer-collections': 5.2.9 + '@multiformats/multiaddr': 12.3.0 + progress-events: 1.0.1 + uint8arraylist: 2.4.8 + '@libp2p/interface@1.6.1': dependencies: '@multiformats/multiaddr': 12.3.0 @@ -11595,6 +12909,15 @@ snapshots: progress-events: 1.0.1 uint8arraylist: 2.4.8 + '@libp2p/interface@1.7.0': + dependencies: + '@multiformats/multiaddr': 12.3.0 + it-pushable: 3.2.3 + it-stream-types: 2.0.1 + multiformats: 13.2.1 + progress-events: 1.0.1 + uint8arraylist: 2.4.8 + '@libp2p/kad-dht@12.1.2': dependencies: '@libp2p/crypto': 4.1.6 @@ -11607,7 +12930,7 @@ snapshots: '@multiformats/multiaddr': 12.3.0 any-signal: 4.1.1 hashlru: 2.3.0 - interface-datastore: 8.2.11 + interface-datastore: 8.3.0 it-drain: 3.0.7 it-length: 3.0.6 it-length-prefixed: 9.0.4 @@ -11635,7 +12958,7 @@ snapshots: '@libp2p/crypto': 4.1.6 '@libp2p/interface': 1.6.1 '@libp2p/peer-id': 4.2.1 - interface-datastore: 8.2.11 + interface-datastore: 8.3.0 merge-options: 3.0.4 multiformats: 13.2.1 sanitize-filename: 1.6.3 @@ -11645,24 +12968,30 @@ snapshots: dependencies: '@libp2p/interface': 1.6.1 '@multiformats/multiaddr': 12.3.0 - debug: 4.3.5(supports-color@8.1.1) - interface-datastore: 8.2.11 + debug: 4.3.6(supports-color@8.1.1) + interface-datastore: 8.3.0 multiformats: 13.2.1 transitivePeerDependencies: - supports-color - '@libp2p/mdns@10.1.2': + '@libp2p/logger@4.0.20': dependencies: - '@libp2p/interface': 1.6.1 - '@libp2p/interface-internal': 1.3.1 - '@libp2p/peer-id': 4.2.1 - '@libp2p/utils': 5.4.6 + '@libp2p/interface': 1.7.0 + '@multiformats/multiaddr': 12.3.0 + interface-datastore: 8.3.0 + multiformats: 13.2.1 + weald: 1.0.2 + + '@libp2p/mdns@10.1.5': + dependencies: + '@libp2p/interface': 1.7.0 + '@libp2p/interface-internal': 1.3.4 + '@libp2p/peer-id': 4.2.4 + '@libp2p/utils': 5.4.9 '@multiformats/multiaddr': 12.3.0 '@types/multicast-dns': 7.2.4 dns-packet: 5.6.1 multicast-dns: 7.2.5 - transitivePeerDependencies: - - supports-color '@libp2p/mplex@10.1.2': dependencies: @@ -11697,6 +13026,12 @@ snapshots: transitivePeerDependencies: - supports-color + '@libp2p/peer-collections@5.2.9': + dependencies: + '@libp2p/interface': 1.7.0 + '@libp2p/peer-id': 4.2.4 + '@libp2p/utils': 5.4.9 + '@libp2p/peer-id-factory@4.2.1': dependencies: '@libp2p/crypto': 4.1.6 @@ -11712,6 +13047,12 @@ snapshots: multiformats: 13.2.1 uint8arrays: 5.1.0 + '@libp2p/peer-id@4.2.4': + dependencies: + '@libp2p/interface': 1.7.0 + multiformats: 13.2.1 + uint8arrays: 5.1.0 + '@libp2p/peer-record@7.0.22': dependencies: '@libp2p/crypto': 4.1.6 @@ -11733,7 +13074,7 @@ snapshots: '@libp2p/peer-id': 4.2.1 '@libp2p/peer-record': 7.0.22 '@multiformats/multiaddr': 12.3.0 - interface-datastore: 8.2.11 + interface-datastore: 8.3.0 it-all: 3.0.6 mortice: 3.0.4 multiformats: 13.2.1 @@ -11828,6 +13169,30 @@ snapshots: transitivePeerDependencies: - supports-color + '@libp2p/utils@5.4.9': + dependencies: + '@chainsafe/is-ip': 2.0.2 + '@libp2p/crypto': 4.1.9 + '@libp2p/interface': 1.7.0 + '@libp2p/logger': 4.0.20 + '@multiformats/multiaddr': 12.3.0 + '@multiformats/multiaddr-matcher': 1.2.4 + '@sindresorhus/fnv1a': 3.1.0 + '@types/murmurhash3js-revisited': 3.0.3 + any-signal: 4.1.1 + delay: 6.0.0 + get-iterator: 2.0.1 + is-loopback-addr: 2.0.2 + it-pushable: 3.2.3 + it-stream-types: 2.0.1 + murmurhash3js-revisited: 3.0.0 + netmask: 2.0.2 + p-defer: 4.0.1 + race-event: 1.3.0 + race-signal: 1.0.2 + uint8arraylist: 2.4.8 + uint8arrays: 5.1.0 + '@libp2p/webrtc@4.1.2(react-native@0.74.3(@babel/core@7.24.6)(@babel/preset-env@7.24.8(@babel/core@7.24.6))(@types/react@18.3.3)(encoding@0.1.13)(react@18.3.1))': dependencies: '@chainsafe/libp2p-noise': 15.1.0 @@ -11894,10 +13259,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@lmdb/lmdb-darwin-arm64@2.8.5': + optional: true + + '@lmdb/lmdb-darwin-x64@2.8.5': + optional: true + + '@lmdb/lmdb-linux-arm64@2.8.5': + optional: true + + '@lmdb/lmdb-linux-arm@2.8.5': + optional: true + + '@lmdb/lmdb-linux-x64@2.8.5': + optional: true + + '@lmdb/lmdb-win32-x64@2.8.5': + optional: true + '@localfirst/relay@4.2.2': dependencies: cuid: 3.0.0 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) eventemitter3: 5.0.1 express: 4.19.2 express-ws: 5.0.2(express@4.19.2) @@ -11909,6 +13292,12 @@ snapshots: - supports-color - utf-8-validate + '@mischnic/json-sourcemap@0.1.1': + dependencies: + '@lezer/common': 1.2.1 + '@lezer/lr': 1.4.2 + json5: 2.2.3 + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2': optional: true @@ -12190,11 +13579,593 @@ snapshots: dependencies: '@noble/hashes': 1.4.0 + '@parcel/bundler-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/graph': 3.2.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/rust': 2.12.0 + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/cache@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.12) + '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/logger': 2.12.0 + '@parcel/utils': 2.12.0 + lmdb: 2.8.5 + transitivePeerDependencies: + - '@swc/helpers' + + '@parcel/codeframe@2.12.0': + dependencies: + chalk: 4.1.2 + + '@parcel/compressor-raw@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/config-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(postcss@8.4.38)(terser@5.31.0)(typescript@5.5.3)': + dependencies: + '@parcel/bundler-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/compressor-raw': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/core': 2.12.0(@swc/helpers@0.5.12) + '@parcel/namer-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/optimizer-css': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/optimizer-htmlnano': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(postcss@8.4.38)(terser@5.31.0)(typescript@5.5.3) + '@parcel/optimizer-image': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/optimizer-svgo': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/optimizer-swc': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/packager-css': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/packager-html': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/packager-js': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/packager-raw': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/packager-svg': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/packager-wasm': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/reporter-dev-server': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/resolver-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/runtime-browser-hmr': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/runtime-js': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/runtime-react-refresh': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/runtime-service-worker': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/transformer-babel': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/transformer-css': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/transformer-html': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/transformer-image': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/transformer-js': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/transformer-json': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/transformer-postcss': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/transformer-posthtml': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/transformer-raw': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/transformer-react-refresh-wrap': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/transformer-svg': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + transitivePeerDependencies: + - '@swc/helpers' + - cssnano + - postcss + - purgecss + - relateurl + - srcset + - terser + - typescript + - uncss + + '@parcel/core@2.12.0(@swc/helpers@0.5.12)': + dependencies: + '@mischnic/json-sourcemap': 0.1.1 + '@parcel/cache': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/diagnostic': 2.12.0 + '@parcel/events': 2.12.0 + '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/graph': 3.2.0 + '@parcel/logger': 2.12.0 + '@parcel/package-manager': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/profiler': 2.12.0 + '@parcel/rust': 2.12.0 + '@parcel/source-map': 2.1.1 + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/utils': 2.12.0 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + abortcontroller-polyfill: 1.7.5 + base-x: 3.0.10 + browserslist: 4.23.2 + clone: 2.1.2 + dotenv: 7.0.0 + dotenv-expand: 5.1.0 + json5: 2.2.3 + msgpackr: 1.10.2 + nullthrows: 1.1.1 + semver: 7.6.2 + transitivePeerDependencies: + - '@swc/helpers' + + '@parcel/diagnostic@2.12.0': + dependencies: + '@mischnic/json-sourcemap': 0.1.1 + nullthrows: 1.1.1 + + '@parcel/events@2.12.0': {} + + '@parcel/fs@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.12) + '@parcel/rust': 2.12.0 + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/utils': 2.12.0 + '@parcel/watcher': 2.4.1 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + transitivePeerDependencies: + - '@swc/helpers' + + '@parcel/graph@3.2.0': + dependencies: + nullthrows: 1.1.1 + + '@parcel/logger@2.12.0': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/events': 2.12.0 + + '@parcel/markdown-ansi@2.12.0': + dependencies: + chalk: 4.1.2 + + '@parcel/namer-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/node-resolver-core@3.3.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@mischnic/json-sourcemap': 0.1.1 + '@parcel/diagnostic': 2.12.0 + '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/rust': 2.12.0 + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + semver: 7.6.2 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/optimizer-css@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.12.0 + browserslist: 4.23.2 + lightningcss: 1.26.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/optimizer-htmlnano@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(postcss@8.4.38)(terser@5.31.0)(typescript@5.5.3)': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + htmlnano: 2.1.1(postcss@8.4.38)(svgo@2.8.0)(terser@5.31.0)(typescript@5.5.3) + nullthrows: 1.1.1 + posthtml: 0.16.6 + svgo: 2.8.0 + transitivePeerDependencies: + - '@parcel/core' + - cssnano + - postcss + - purgecss + - relateurl + - srcset + - terser + - typescript + - uncss + + '@parcel/optimizer-image@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.12) + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/rust': 2.12.0 + '@parcel/utils': 2.12.0 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + + '@parcel/optimizer-svgo@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/utils': 2.12.0 + svgo: 2.8.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/optimizer-swc@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.12.0 + '@swc/core': 1.5.24(@swc/helpers@0.5.12) + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + - '@swc/helpers' + + '@parcel/package-manager@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.12) + '@parcel/diagnostic': 2.12.0 + '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/logger': 2.12.0 + '@parcel/node-resolver-core': 3.3.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/utils': 2.12.0 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@swc/core': 1.5.24(@swc/helpers@0.5.12) + semver: 7.6.2 + transitivePeerDependencies: + - '@swc/helpers' + + '@parcel/packager-css@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.12.0 + lightningcss: 1.26.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-html@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + posthtml: 0.16.6 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-js@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/rust': 2.12.0 + '@parcel/source-map': 2.1.1 + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/utils': 2.12.0 + globals: 13.24.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-raw@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-svg@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/utils': 2.12.0 + posthtml: 0.16.6 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-wasm@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/plugin@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/profiler@2.12.0': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/events': 2.12.0 + chrome-trace-event: 1.0.4 + + '@parcel/reporter-cli@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/utils': 2.12.0 + chalk: 4.1.2 + term-size: 2.2.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/reporter-dev-server@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/utils': 2.12.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/reporter-tracer@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/utils': 2.12.0 + chrome-trace-event: 1.0.4 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/resolver-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/node-resolver-core': 3.3.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-browser-hmr@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/utils': 2.12.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-js@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-react-refresh@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/utils': 2.12.0 + react-error-overlay: 6.0.9 + react-refresh: 0.9.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-service-worker@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/rust@2.12.0': {} + + '@parcel/source-map@2.1.1': + dependencies: + detect-libc: 1.0.3 + + '@parcel/transformer-babel@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.12.0 + browserslist: 4.23.2 + json5: 2.2.3 + nullthrows: 1.1.1 + semver: 7.6.2 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-css@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.12.0 + browserslist: 4.23.2 + lightningcss: 1.26.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-html@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/rust': 2.12.0 + nullthrows: 1.1.1 + posthtml: 0.16.6 + posthtml-parser: 0.10.2 + posthtml-render: 3.0.0 + semver: 7.6.2 + srcset: 4.0.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-image@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.12) + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/utils': 2.12.0 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + nullthrows: 1.1.1 + + '@parcel/transformer-js@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.12) + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/rust': 2.12.0 + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.12.0 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@swc/helpers': 0.5.12 + browserslist: 4.23.2 + nullthrows: 1.1.1 + regenerator-runtime: 0.13.11 + semver: 7.6.2 + + '@parcel/transformer-json@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + json5: 2.2.3 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-postcss@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/rust': 2.12.0 + '@parcel/utils': 2.12.0 + clone: 2.1.2 + nullthrows: 1.1.1 + postcss-value-parser: 4.2.0 + semver: 7.6.2 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-posthtml@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + posthtml: 0.16.6 + posthtml-parser: 0.10.2 + posthtml-render: 3.0.0 + semver: 7.6.2 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-raw@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-react-refresh-wrap@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/utils': 2.12.0 + react-refresh: 0.9.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-svg@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/rust': 2.12.0 + nullthrows: 1.1.1 + posthtml: 0.16.6 + posthtml-parser: 0.10.2 + posthtml-render: 3.0.0 + semver: 7.6.2 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/types@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)': + dependencies: + '@parcel/cache': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/diagnostic': 2.12.0 + '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/package-manager': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/source-map': 2.1.1 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + utility-types: 3.11.0 + transitivePeerDependencies: + - '@parcel/core' + - '@swc/helpers' + + '@parcel/utils@2.12.0': + dependencies: + '@parcel/codeframe': 2.12.0 + '@parcel/diagnostic': 2.12.0 + '@parcel/logger': 2.12.0 + '@parcel/markdown-ansi': 2.12.0 + '@parcel/rust': 2.12.0 + '@parcel/source-map': 2.1.1 + chalk: 4.1.2 + nullthrows: 1.1.1 + + '@parcel/watcher-android-arm64@2.4.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.4.1': + optional: true + + '@parcel/watcher-darwin-x64@2.4.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.4.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.4.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.4.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.4.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.4.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.4.1': + optional: true + + '@parcel/watcher-win32-arm64@2.4.1': + optional: true + + '@parcel/watcher-win32-ia32@2.4.1': + optional: true + + '@parcel/watcher-win32-x64@2.4.1': + optional: true + '@parcel/watcher@2.0.4': dependencies: node-addon-api: 3.2.1 node-gyp-build: 4.8.1 + '@parcel/watcher@2.4.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.7 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.4.1 + '@parcel/watcher-darwin-arm64': 2.4.1 + '@parcel/watcher-darwin-x64': 2.4.1 + '@parcel/watcher-freebsd-x64': 2.4.1 + '@parcel/watcher-linux-arm-glibc': 2.4.1 + '@parcel/watcher-linux-arm64-glibc': 2.4.1 + '@parcel/watcher-linux-arm64-musl': 2.4.1 + '@parcel/watcher-linux-x64-glibc': 2.4.1 + '@parcel/watcher-linux-x64-musl': 2.4.1 + '@parcel/watcher-win32-arm64': 2.4.1 + '@parcel/watcher-win32-ia32': 2.4.1 + '@parcel/watcher-win32-x64': 2.4.1 + + '@parcel/workers@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.12) + '@parcel/diagnostic': 2.12.0 + '@parcel/logger': 2.12.0 + '@parcel/profiler': 2.12.0 + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + '@pkgjs/parseargs@0.11.0': optional: true @@ -12628,7 +14599,7 @@ snapshots: '@swc/core-win32-x64-msvc@1.5.24': optional: true - '@swc/core@1.5.24': + '@swc/core@1.5.24(@swc/helpers@0.5.12)': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.7 @@ -12643,9 +14614,14 @@ snapshots: '@swc/core-win32-arm64-msvc': 1.5.24 '@swc/core-win32-ia32-msvc': 1.5.24 '@swc/core-win32-x64-msvc': 1.5.24 + '@swc/helpers': 0.5.12 '@swc/counter@0.1.3': {} + '@swc/helpers@0.5.12': + dependencies: + tslib: 2.6.2 + '@swc/types@0.1.7': dependencies: '@swc/counter': 0.1.3 @@ -12682,7 +14658,7 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.4.5(vitest@1.6.0(@types/node@20.14.11)(terser@5.31.0))': + '@testing-library/jest-dom@6.4.5(vitest@1.6.0(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0))': dependencies: '@adobe/css-tools': 4.3.3 '@babel/runtime': 7.24.6 @@ -12693,7 +14669,7 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 optionalDependencies: - vitest: 1.6.0(@types/node@20.14.11)(terser@5.31.0) + vitest: 1.6.0(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0) '@testing-library/react@14.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -12709,6 +14685,8 @@ snapshots: '@tootallnate/once@2.0.0': {} + '@trysound/sax@0.2.0': {} + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -12752,6 +14730,11 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 18.19.33 + '@types/bs58@4.0.4': + dependencies: + '@types/node': 18.19.33 + base-x: 3.0.10 + '@types/cacheable-request@6.0.3': dependencies: '@types/http-cache-semantics': 4.0.4 @@ -12850,6 +14833,8 @@ snapshots: '@types/lodash@4.17.4': {} + '@types/luxon@3.4.2': {} + '@types/mdast@3.0.15': dependencies: '@types/unist': 2.0.10 @@ -12972,7 +14957,7 @@ snapshots: '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.5.3) '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.5.3) '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 @@ -12990,7 +14975,7 @@ snapshots: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.3) '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) eslint: 8.57.0 optionalDependencies: typescript: 5.5.3 @@ -13006,7 +14991,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.3) '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.5.3) - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.5.3) optionalDependencies: @@ -13020,7 +15005,7 @@ snapshots: dependencies: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 @@ -13057,25 +15042,25 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-react@4.3.0(vite@5.2.12(@types/node@18.19.33)(terser@5.31.0))': + '@vitejs/plugin-react@4.3.0(vite@5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0))': dependencies: '@babel/core': 7.24.6 '@babel/plugin-transform-react-jsx-self': 7.24.6(@babel/core@7.24.6) '@babel/plugin-transform-react-jsx-source': 7.24.6(@babel/core@7.24.6) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.2.12(@types/node@18.19.33)(terser@5.31.0) + vite: 5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@4.3.0(vite@5.2.12(@types/node@20.14.11)(terser@5.31.0))': + '@vitejs/plugin-react@4.3.0(vite@5.2.12(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0))': dependencies: '@babel/core': 7.24.6 '@babel/plugin-transform-react-jsx-self': 7.24.6(@babel/core@7.24.6) '@babel/plugin-transform-react-jsx-source': 7.24.6(@babel/core@7.24.6) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.2.12(@types/node@20.14.11)(terser@5.31.0) + vite: 5.2.12(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0) transitivePeerDependencies: - supports-color @@ -13210,6 +15195,8 @@ snapshots: dependencies: event-target-shim: 5.0.1 + abortcontroller-polyfill@1.7.5: {} + abstract-level@1.0.4: dependencies: buffer: 6.0.3 @@ -13237,8 +15224,18 @@ snapshots: dependencies: acorn: 8.12.1 + acorn-node@1.8.2: + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + xtend: 4.0.2 + + acorn-walk@7.2.0: {} + acorn-walk@8.3.2: {} + acorn@7.4.1: {} + acorn@8.11.3: {} acorn@8.12.1: {} @@ -13247,7 +15244,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -13276,6 +15273,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.1 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + anser@1.4.10: {} ansi-align@2.0.0: @@ -13298,6 +15302,8 @@ snapshots: slice-ansi: 2.1.0 strip-ansi: 5.2.0 + ansi-regex@2.1.1: {} + ansi-regex@3.0.1: {} ansi-regex@4.1.1: {} @@ -13306,6 +15312,8 @@ snapshots: ansi-regex@6.0.1: {} + ansi-styles@2.2.1: {} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 @@ -13447,6 +15455,12 @@ snapshots: asap@2.0.6: {} + asn1.js@4.10.1: + dependencies: + bn.js: 4.12.0 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + asn1@0.2.6: dependencies: safer-buffer: 2.1.2 @@ -13459,6 +15473,11 @@ snapshots: assert-plus@1.0.0: {} + assert@1.5.1: + dependencies: + object.assign: 4.1.5 + util: 0.10.4 + assertion-error@1.1.0: {} ast-types@0.15.2: @@ -13515,7 +15534,7 @@ snapshots: common-path-prefix: 3.0.0 concordance: 5.0.4 currently-unhandled: 0.4.1 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) emittery: 1.0.3 figures: 5.0.0 globby: 13.2.2 @@ -13595,8 +15614,14 @@ snapshots: balanced-match@1.0.2: {} + base-x@3.0.10: + dependencies: + safe-buffer: 5.2.1 + base-x@4.0.0: {} + base-x@5.0.0: {} + base64-js@1.5.1: {} bcrypt-pbkdf@1.0.2: @@ -13633,6 +15658,10 @@ snapshots: blueimp-md5@2.19.0: {} + bn.js@4.12.0: {} + + bn.js@5.2.1: {} + body-parser@1.20.2: dependencies: bytes: 3.1.2 @@ -13650,6 +15679,8 @@ snapshots: transitivePeerDependencies: - supports-color + boolbase@1.0.0: {} + boolean@3.2.0: optional: true @@ -13676,6 +15707,8 @@ snapshots: dependencies: fill-range: 7.1.1 + brorand@1.1.0: {} + browser-level@1.0.1: dependencies: abstract-level: 1.0.4 @@ -13683,8 +15716,118 @@ snapshots: module-error: 1.0.2 run-parallel-limit: 1.1.0 + browser-pack@6.1.0: + dependencies: + JSONStream: 1.3.5 + combine-source-map: 0.8.0 + defined: 1.0.1 + safe-buffer: 5.2.1 + through2: 2.0.5 + umd: 3.0.3 + + browser-process-hrtime@0.1.3: {} + browser-readablestream-to-it@2.0.7: {} + browser-resolve@2.0.0: + dependencies: + resolve: 1.22.8 + + browserify-aes@1.2.0: + dependencies: + buffer-xor: 1.0.3 + cipher-base: 1.0.4 + create-hash: 1.2.0 + evp_bytestokey: 1.0.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-cipher@1.0.1: + dependencies: + browserify-aes: 1.2.0 + browserify-des: 1.0.2 + evp_bytestokey: 1.0.3 + + browserify-des@1.0.2: + dependencies: + cipher-base: 1.0.4 + des.js: 1.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-rsa@4.1.0: + dependencies: + bn.js: 5.2.1 + randombytes: 2.1.0 + + browserify-sign@4.2.3: + dependencies: + bn.js: 5.2.1 + browserify-rsa: 4.1.0 + create-hash: 1.2.0 + create-hmac: 1.1.7 + elliptic: 6.5.6 + hash-base: 3.0.4 + inherits: 2.0.4 + parse-asn1: 5.1.7 + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + + browserify-zlib@0.2.0: + dependencies: + pako: 1.0.11 + + browserify@17.0.0: + dependencies: + JSONStream: 1.3.5 + assert: 1.5.1 + browser-pack: 6.1.0 + browser-resolve: 2.0.0 + browserify-zlib: 0.2.0 + buffer: 5.2.1 + cached-path-relative: 1.1.0 + concat-stream: 1.6.2 + console-browserify: 1.2.0 + constants-browserify: 1.0.0 + crypto-browserify: 3.12.0 + defined: 1.0.1 + deps-sort: 2.0.1 + domain-browser: 1.2.0 + duplexer2: 0.1.4 + events: 3.3.0 + glob: 7.2.3 + has: 1.0.4 + htmlescape: 1.1.1 + https-browserify: 1.0.0 + inherits: 2.0.4 + insert-module-globals: 7.2.1 + labeled-stream-splicer: 2.0.2 + mkdirp-classic: 0.5.3 + module-deps: 6.2.3 + os-browserify: 0.3.0 + parents: 1.0.1 + path-browserify: 1.0.1 + process: 0.11.10 + punycode: 1.4.1 + querystring-es3: 0.2.1 + read-only-stream: 2.0.0 + readable-stream: 2.3.8 + resolve: 1.22.8 + shasum-object: 1.0.0 + shell-quote: 1.8.1 + stream-browserify: 3.0.0 + stream-http: 3.2.0 + string_decoder: 1.3.0 + subarg: 1.0.0 + syntax-error: 1.4.0 + through2: 2.0.5 + timers-browserify: 1.4.2 + tty-browserify: 0.0.1 + url: 0.11.4 + util: 0.12.5 + vm-browserify: 1.1.2 + xtend: 4.0.2 + browserslist@4.23.0: dependencies: caniuse-lite: 1.0.30001625 @@ -13703,6 +15846,10 @@ snapshots: dependencies: base-x: 4.0.0 + bs58@6.0.0: + dependencies: + base-x: 5.0.0 + bs58check@3.0.1: dependencies: '@noble/hashes': 1.4.0 @@ -13716,6 +15863,13 @@ snapshots: buffer-from@1.1.2: {} + buffer-xor@1.0.3: {} + + buffer@5.2.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -13728,6 +15882,8 @@ snapshots: builtin-modules@3.3.0: {} + builtin-status-codes@3.0.0: {} + builtins@1.0.3: {} builtins@5.1.0: @@ -13797,6 +15953,8 @@ snapshots: normalize-url: 6.1.0 responselike: 2.0.1 + cached-path-relative@1.1.0: {} + cachedir@2.4.0: {} call-bind@1.0.7: @@ -13821,6 +15979,11 @@ snapshots: callsites@4.1.0: {} + camel-case@3.0.0: + dependencies: + no-case: 2.3.2 + upper-case: 1.1.3 + camelcase-css@2.0.1: {} camelcase-keys@6.2.2: @@ -13879,6 +16042,14 @@ snapshots: pathval: 1.1.1 type-detect: 4.0.8 + chalk@1.1.3: + dependencies: + ansi-styles: 2.2.1 + escape-string-regexp: 1.0.5 + has-ansi: 2.0.0 + strip-ansi: 3.0.1 + supports-color: 2.0.0 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -13906,6 +16077,10 @@ snapshots: chardet@0.7.0: {} + chart.js@4.4.3: + dependencies: + '@kurkle/color': 0.3.2 + check-error@1.0.3: dependencies: get-func-name: 2.0.2 @@ -13949,6 +16124,11 @@ snapshots: ci-parallel-vars@1.0.1: {} + cipher-base@1.0.4: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + classic-level@1.4.1: dependencies: abstract-level: 1.0.4 @@ -14047,12 +16227,16 @@ snapshots: clone@1.0.4: {} + clone@2.1.2: {} + cmd-shim@6.0.1: {} code-excerpt@4.0.0: dependencies: convert-to-spaces: 2.0.1 + code-point-at@1.1.0: {} + color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -14078,6 +16262,13 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 + combine-source-map@0.8.0: + dependencies: + convert-source-map: 1.1.3 + inline-source-map: 0.6.3 + lodash.memoize: 3.0.4 + source-map: 0.5.7 + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -14127,6 +16318,13 @@ snapshots: concat-map@0.0.1: {} + concat-stream@1.6.2: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + concat-stream@2.0.0: dependencies: buffer-from: 1.1.2 @@ -14167,8 +16365,12 @@ snapshots: transitivePeerDependencies: - supports-color + console-browserify@1.2.0: {} + console-control-strings@1.1.0: {} + constants-browserify@1.0.0: {} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -14229,6 +16431,10 @@ snapshots: convert-hrtime@5.0.0: {} + convert-source-map@1.1.3: {} + + convert-source-map@1.9.0: {} + convert-source-map@2.0.0: {} convert-to-spaces@2.0.1: {} @@ -14270,10 +16476,41 @@ snapshots: optionalDependencies: typescript: 5.5.3 + cosmiconfig@9.0.0(typescript@5.5.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.5.3 + + create-ecdh@4.0.4: + dependencies: + bn.js: 4.12.0 + elliptic: 6.5.6 + create-error-class@3.0.2: dependencies: capture-stack-trace: 1.0.2 + create-hash@1.2.0: + dependencies: + cipher-base: 1.0.4 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.2 + sha.js: 2.4.11 + + create-hmac@1.1.7: + dependencies: + cipher-base: 1.0.4 + create-hash: 1.2.0 + inherits: 2.0.4 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + create-require@1.1.1: {} cross-env@7.0.3: @@ -14300,12 +16537,45 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crypto-browserify@3.12.0: + dependencies: + browserify-cipher: 1.0.1 + browserify-sign: 4.2.3 + create-ecdh: 4.0.4 + create-hash: 1.2.0 + create-hmac: 1.1.7 + diffie-hellman: 5.0.3 + inherits: 2.0.4 + pbkdf2: 3.1.2 + public-encrypt: 4.0.3 + randombytes: 2.1.0 + randomfill: 1.0.4 + crypto-random-string@1.0.0: {} + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + + css-what@6.1.0: {} + css.escape@1.5.1: {} cssesc@3.0.0: {} + csso@4.2.0: + dependencies: + css-tree: 1.1.3 + csstype@3.1.3: {} cuid@3.0.0: {} @@ -14332,7 +16602,7 @@ snapshots: commander: 6.2.1 common-tags: 1.8.2 dayjs: 1.11.11 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) enquirer: 2.4.1 eventemitter2: 6.4.7 execa: 4.1.0 @@ -14388,6 +16658,10 @@ snapshots: dependencies: d3-path: 3.1.0 + d3-color@1.4.1: {} + + d3-color@2.0.0: {} + d3-color@3.1.0: {} d3-contour@4.0.2: @@ -14398,8 +16672,15 @@ snapshots: dependencies: delaunator: 5.0.1 + d3-dispatch@1.0.6: {} + d3-dispatch@3.0.1: {} + d3-drag@1.2.5: + dependencies: + d3-dispatch: 1.0.6 + d3-selection: 1.4.2 + d3-drag@3.0.0: dependencies: d3-dispatch: 3.0.1 @@ -14411,26 +16692,52 @@ snapshots: iconv-lite: 0.6.3 rw: 1.3.3 + d3-ease@1.0.7: {} + d3-ease@3.0.1: {} d3-fetch@3.0.1: dependencies: d3-dsv: 3.0.1 + d3-fg@6.14.0: + dependencies: + d3-array: 2.12.1 + d3-dispatch: 1.0.6 + d3-ease: 1.0.7 + d3-hierarchy: 1.1.9 + d3-scale: 3.3.0 + d3-selection: 1.4.2 + d3-zoom: 1.8.3 + escape-string-regexp: 1.0.5 + hsl-to-rgb-for-reals: 1.1.1 + d3-force@3.0.0: dependencies: d3-dispatch: 3.0.1 d3-quadtree: 3.0.1 d3-timer: 3.0.1 + d3-format@2.0.0: {} + d3-format@3.1.0: {} d3-geo@3.1.1: dependencies: d3-array: 3.2.4 + d3-hierarchy@1.1.9: {} + d3-hierarchy@3.1.2: {} + d3-interpolate@1.4.0: + dependencies: + d3-color: 1.4.1 + + d3-interpolate@2.0.1: + dependencies: + d3-color: 2.0.0 + d3-interpolate@3.0.1: dependencies: d3-color: 3.1.0 @@ -14455,6 +16762,14 @@ snapshots: d3-color: 3.1.0 d3-interpolate: 3.0.1 + d3-scale@3.3.0: + dependencies: + d3-array: 2.12.1 + d3-format: 2.0.0 + d3-interpolate: 2.0.1 + d3-time: 2.1.1 + d3-time-format: 3.0.0 + d3-scale@4.0.2: dependencies: d3-array: 3.2.4 @@ -14463,6 +16778,8 @@ snapshots: d3-time: 3.1.0 d3-time-format: 4.1.0 + d3-selection@1.4.2: {} + d3-selection@3.0.0: {} d3-shape@1.3.7: @@ -14473,16 +16790,35 @@ snapshots: dependencies: d3-path: 3.1.0 + d3-time-format@3.0.0: + dependencies: + d3-time: 2.1.1 + d3-time-format@4.1.0: dependencies: d3-time: 3.1.0 + d3-time@2.1.1: + dependencies: + d3-array: 2.12.1 + d3-time@3.1.0: dependencies: d3-array: 3.2.4 + d3-timer@1.0.10: {} + d3-timer@3.0.1: {} + d3-transition@1.3.2: + dependencies: + d3-color: 1.4.1 + d3-dispatch: 1.0.6 + d3-ease: 1.0.7 + d3-interpolate: 1.4.0 + d3-selection: 1.4.2 + d3-timer: 1.0.10 + d3-transition@3.0.1(d3-selection@3.0.0): dependencies: d3-color: 3.1.0 @@ -14492,6 +16828,14 @@ snapshots: d3-selection: 3.0.0 d3-timer: 3.0.1 + d3-zoom@1.8.3: + dependencies: + d3-dispatch: 1.0.6 + d3-drag: 1.2.5 + d3-interpolate: 1.4.0 + d3-selection: 1.4.2 + d3-transition: 1.3.2 + d3-zoom@3.0.0: dependencies: d3-dispatch: 3.0.1 @@ -14540,6 +16884,8 @@ snapshots: dargs@7.0.0: {} + dash-ast@1.0.0: {} + dashdash@1.14.1: dependencies: assert-plus: 1.0.0 @@ -14566,7 +16912,7 @@ snapshots: dependencies: '@libp2p/logger': 4.0.17 err-code: 3.0.1 - interface-datastore: 8.2.11 + interface-datastore: 8.3.0 interface-store: 5.1.8 it-drain: 3.0.7 it-filter: 3.1.1 @@ -14587,6 +16933,8 @@ snapshots: dayjs@1.11.11: {} + debounce@1.2.1: {} + debug@2.6.9: dependencies: ms: 2.0.0 @@ -14601,7 +16949,11 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.3.5(supports-color@8.1.1): + debug@4.3.5: + dependencies: + ms: 2.1.2 + + debug@4.3.6(supports-color@8.1.1): dependencies: ms: 2.1.2 optionalDependencies: @@ -14681,6 +17033,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + defined@1.0.1: {} + delaunator@5.0.1: dependencies: robust-predicates: 3.0.2 @@ -14701,19 +17055,39 @@ snapshots: deprecation@2.3.1: {} + deps-sort@2.0.1: + dependencies: + JSONStream: 1.3.5 + shasum-object: 1.0.0 + subarg: 1.0.0 + through2: 2.0.5 + dequal@2.0.3: {} + des.js@1.1.0: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + destroy@1.2.0: {} detect-browser@5.3.0: {} detect-indent@5.0.0: {} + detect-libc@1.0.3: {} + detect-libc@2.0.3: {} detect-node@2.1.0: optional: true + detective@5.2.1: + dependencies: + acorn-node: 1.8.2 + defined: 1.0.1 + minimist: 1.2.8 + didyoumean@1.2.2: {} diff-sequences@29.6.3: {} @@ -14722,6 +17096,12 @@ snapshots: diff@5.2.0: {} + diffie-hellman@5.0.3: + dependencies: + bn.js: 4.12.0 + miller-rabin: 4.0.1 + randombytes: 2.1.0 + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -14744,8 +17124,28 @@ snapshots: dom-accessibility-api@0.6.3: {} + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + domain-browser@1.2.0: {} + + domelementtype@2.3.0: {} + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + dompurify@3.1.5: {} + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + dot-prop@4.2.1: dependencies: is-obj: 1.0.1 @@ -14756,12 +17156,27 @@ snapshots: dotenv-expand@10.0.0: {} + dotenv-expand@5.1.0: {} + dotenv@16.3.2: {} + dotenv@7.0.0: {} + + duplexer2@0.1.4: + dependencies: + readable-stream: 2.3.8 + duplexer3@0.1.5: {} duplexer@0.1.2: {} + duplexify@4.1.3: + dependencies: + end-of-stream: 1.4.4 + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-shift: 1.0.3 + eastasianwidth@0.2.0: {} ecc-jsbn@0.1.2: @@ -14789,6 +17204,16 @@ snapshots: elkjs@0.9.3: {} + elliptic@6.5.6: + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + emittery@1.0.3: {} emoji-regex@8.0.0: {} @@ -14830,10 +17255,16 @@ snapshots: ansi-colors: 4.1.3 strip-ansi: 6.0.1 + entities@2.2.0: {} + + entities@3.0.1: {} + env-editor@1.1.0: {} env-paths@2.2.1: {} + env-string@1.0.1: {} + envinfo@7.13.0: {} envinfo@7.8.1: {} @@ -15076,7 +17507,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.27.5(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(webpack@5.91.0(@swc/core@1.5.24)(esbuild@0.19.12)): + eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.27.5)(webpack@5.91.0(@swc/core@1.5.24)(esbuild@0.19.12)): dependencies: array.prototype.find: 2.2.3 debug: 3.2.7(supports-color@8.1.1) @@ -15094,14 +17525,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.27.5(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(webpack@5.91.0(@swc/core@1.5.24)(esbuild@0.19.12)))(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.27.5)(webpack@5.91.0(@swc/core@1.5.24)(esbuild@0.19.12)))(eslint@8.57.0): dependencies: debug: 3.2.7(supports-color@8.1.1) optionalDependencies: '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.5.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-webpack: 0.13.8(eslint-plugin-import@2.27.5(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(webpack@5.91.0(@swc/core@1.5.24)(esbuild@0.19.12)) + eslint-import-resolver-webpack: 0.13.8(eslint-plugin-import@2.27.5)(webpack@5.91.0(@swc/core@1.5.24)(esbuild@0.19.12)) transitivePeerDependencies: - supports-color @@ -15139,7 +17570,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.27.5(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(webpack@5.91.0(@swc/core@1.5.24)(esbuild@0.19.12)))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.27.5)(webpack@5.91.0(@swc/core@1.5.24)(esbuild@0.19.12)))(eslint@8.57.0) has: 1.0.4 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -15282,7 +17713,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -15325,7 +17756,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) escape-string-regexp: 4.0.0 eslint-scope: 8.0.2 eslint-visitor-keys: 4.0.0 @@ -15384,6 +17815,8 @@ snapshots: estraverse@5.3.0: {} + estree-is-member-expression@1.0.0: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.5 @@ -15406,6 +17839,11 @@ snapshots: events@3.3.0: {} + evp_bytestokey@1.0.3: + dependencies: + md5.js: 1.3.5 + safe-buffer: 5.2.1 + execa@0.7.0: dependencies: cross-spawn: 5.1.0 @@ -15476,6 +17914,10 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + execspawn@1.0.1: + dependencies: + util-extend: 1.0.3 + executable@4.1.1: dependencies: pify: 2.3.0 @@ -15538,7 +17980,7 @@ snapshots: extract-zip@2.0.1(supports-color@8.1.1): dependencies: - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -15564,8 +18006,12 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-safe-stringify@2.1.1: {} + fast-sha256@1.3.0: {} + fast-uri@3.0.1: {} + fast-xml-parser@4.4.0: dependencies: strnum: 1.0.5 @@ -15736,6 +18182,12 @@ snapshots: fs-constants@1.0.0: {} + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + fs-extra@11.2.0: dependencies: graceful-fs: 4.2.11 @@ -15797,6 +18249,8 @@ snapshots: gensync@1.0.0-beta.2: {} + get-assigned-identifiers@1.2.0: {} + get-caller-file@2.0.5: {} get-func-name@2.0.2: {} @@ -15818,6 +18272,8 @@ snapshots: through2: 2.0.5 yargs: 16.2.0 + get-port@4.2.0: {} + get-port@5.1.1: {} get-set-props@0.1.0: {} @@ -16041,6 +18497,10 @@ snapshots: hard-rejection@2.1.0: {} + has-ansi@2.0.0: + dependencies: + ansi-regex: 2.1.1 + has-bigints@1.0.2: {} has-flag@3.0.0: {} @@ -16063,6 +18523,22 @@ snapshots: has@1.0.4: {} + hash-base@3.0.4: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + + hash-base@3.1.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + safe-buffer: 5.2.1 + + hash.js@1.1.7: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + hashlru@2.3.0: {} hasown@2.0.2: @@ -16087,7 +18563,7 @@ snapshots: '@libp2p/kad-dht': 12.1.2 '@libp2p/keychain': 4.1.2 '@libp2p/logger': 4.0.17 - '@libp2p/mdns': 10.1.2 + '@libp2p/mdns': 10.1.5 '@libp2p/mplex': 10.1.2 '@libp2p/ping': 1.1.2 '@libp2p/tcp': 9.1.2 @@ -16099,7 +18575,7 @@ snapshots: blockstore-core: 4.4.1 datastore-core: 9.2.9 interface-blockstore: 5.2.10 - interface-datastore: 8.2.11 + interface-datastore: 8.3.0 ipns: 9.1.0 libp2p: 1.8.1 multiformats: 13.2.1 @@ -16125,6 +18601,12 @@ snapshots: dependencies: source-map: 0.7.4 + hmac-drbg@1.0.1: + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + hosted-git-info@2.8.9: {} hosted-git-info@3.0.8: @@ -16139,6 +18621,29 @@ snapshots: dependencies: lru-cache: 7.18.3 + hsl-to-rgb-for-reals@1.1.1: {} + + htmlescape@1.1.1: {} + + htmlnano@2.1.1(postcss@8.4.38)(svgo@2.8.0)(terser@5.31.0)(typescript@5.5.3): + dependencies: + cosmiconfig: 9.0.0(typescript@5.5.3) + posthtml: 0.16.6 + timsort: 0.3.0 + optionalDependencies: + postcss: 8.4.38 + svgo: 2.8.0 + terser: 5.31.0 + transitivePeerDependencies: + - typescript + + htmlparser2@7.2.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 3.0.1 + http-cache-semantics@4.1.1: {} http-errors@2.0.0: @@ -16153,7 +18658,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -16168,10 +18673,12 @@ snapshots: quick-lru: 5.1.1 resolve-alpn: 1.2.1 + https-browserify@1.0.0: {} + https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -16187,6 +18694,12 @@ snapshots: dependencies: ms: 2.1.3 + hyperscript-attribute-to-property@1.0.2: {} + + hyperx@2.5.4: + dependencies: + hyperscript-attribute-to-property: 1.0.2 + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -16247,6 +18760,8 @@ snapshots: once: 1.4.0 wrappy: 1.0.2 + inherits@2.0.3: {} + inherits@2.0.4: {} ini@1.3.8: {} @@ -16263,6 +18778,10 @@ snapshots: validate-npm-package-license: 3.0.4 validate-npm-package-name: 5.0.0 + inline-source-map@0.6.3: + dependencies: + source-map: 0.5.7 + inquirer-select-pro@1.0.0-alpha.6: dependencies: '@inquirer/core': 8.2.4 @@ -16299,18 +18818,33 @@ snapshots: through: 2.3.8 wrap-ansi: 6.2.0 + insert-module-globals@7.2.1: + dependencies: + JSONStream: 1.3.5 + acorn-node: 1.8.2 + combine-source-map: 0.8.0 + concat-stream: 1.6.2 + is-buffer: 1.1.6 + path-is-absolute: 1.0.1 + process: 0.11.10 + through2: 2.0.5 + undeclared-identifiers: 1.1.3 + xtend: 4.0.2 + interface-blockstore@5.2.10: dependencies: interface-store: 5.1.8 multiformats: 13.2.1 - interface-datastore@8.2.11: + interface-datastore@8.3.0: dependencies: - interface-store: 5.1.8 + interface-store: 6.0.0 uint8arrays: 5.1.0 interface-store@5.1.8: {} + interface-store@6.0.0: {} + internal-slot@1.0.7: dependencies: es-errors: 1.3.0 @@ -16346,7 +18880,7 @@ snapshots: '@libp2p/peer-id': 4.2.1 cborg: 4.2.3 err-code: 3.0.1 - interface-datastore: 8.2.11 + interface-datastore: 8.3.0 multiformats: 13.2.1 protons-runtime: 5.4.0 timestamp-nano: 1.0.1 @@ -16386,11 +18920,15 @@ snapshots: dependencies: binary-extensions: 2.3.0 + is-boolean-attribute@0.0.1: {} + is-boolean-object@1.1.2: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 + is-buffer@1.1.6: {} + is-buffer@2.0.5: {} is-builtin-module@3.2.1: @@ -16435,6 +18973,10 @@ snapshots: dependencies: call-bind: 1.0.7 + is-fullwidth-code-point@1.0.0: + dependencies: + number-is-nan: 1.0.1 + is-fullwidth-code-point@2.0.0: {} is-fullwidth-code-point@3.0.0: {} @@ -16479,6 +19021,8 @@ snapshots: dependencies: js-types: 1.0.0 + is-json@2.0.1: {} + is-lambda@1.0.1: {} is-loopback-addr@2.0.2: {} @@ -16901,6 +19445,8 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-schema@0.4.0: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -16927,6 +19473,12 @@ snapshots: jsonparse@1.3.1: {} + jsonstream2@3.0.0: + dependencies: + jsonparse: 1.3.1 + through2: 3.0.2 + type-component: 0.0.1 + jsprim@2.0.2: dependencies: assert-plus: 1.0.0 @@ -16959,6 +19511,11 @@ snapshots: kleur@4.1.5: {} + labeled-stream-splicer@2.0.2: + dependencies: + inherits: 2.0.4 + stream-splicer: 2.0.1 + latest-version@3.1.0: dependencies: package-json: 4.0.1 @@ -17109,7 +19666,7 @@ snapshots: '@multiformats/multiaddr-matcher': 1.2.4 any-signal: 4.1.1 datastore-core: 9.2.9 - interface-datastore: 8.2.11 + interface-datastore: 8.3.0 it-merge: 3.0.5 it-parallel: 3.0.8 merge-options: 3.0.4 @@ -17135,6 +19692,51 @@ snapshots: transitivePeerDependencies: - supports-color + lightningcss-darwin-arm64@1.26.0: + optional: true + + lightningcss-darwin-x64@1.26.0: + optional: true + + lightningcss-freebsd-x64@1.26.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.26.0: + optional: true + + lightningcss-linux-arm64-gnu@1.26.0: + optional: true + + lightningcss-linux-arm64-musl@1.26.0: + optional: true + + lightningcss-linux-x64-gnu@1.26.0: + optional: true + + lightningcss-linux-x64-musl@1.26.0: + optional: true + + lightningcss-win32-arm64-msvc@1.26.0: + optional: true + + lightningcss-win32-x64-msvc@1.26.0: + optional: true + + lightningcss@1.26.0: + dependencies: + detect-libc: 1.0.3 + optionalDependencies: + lightningcss-darwin-arm64: 1.26.0 + lightningcss-darwin-x64: 1.26.0 + lightningcss-freebsd-x64: 1.26.0 + lightningcss-linux-arm-gnueabihf: 1.26.0 + lightningcss-linux-arm64-gnu: 1.26.0 + lightningcss-linux-arm64-musl: 1.26.0 + lightningcss-linux-x64-gnu: 1.26.0 + lightningcss-linux-x64-musl: 1.26.0 + lightningcss-win32-arm64-msvc: 1.26.0 + lightningcss-win32-x64-msvc: 1.26.0 + lilconfig@2.1.0: {} lilconfig@3.1.1: {} @@ -17160,6 +19762,21 @@ snapshots: optionalDependencies: enquirer: 2.4.1 + lmdb@2.8.5: + dependencies: + msgpackr: 1.10.2 + node-addon-api: 6.1.0 + node-gyp-build-optional-packages: 5.1.1 + ordered-binary: 1.5.1 + weak-lru-cache: 1.2.2 + optionalDependencies: + '@lmdb/lmdb-darwin-arm64': 2.8.5 + '@lmdb/lmdb-darwin-x64': 2.8.5 + '@lmdb/lmdb-linux-arm': 2.8.5 + '@lmdb/lmdb-linux-arm64': 2.8.5 + '@lmdb/lmdb-linux-x64': 2.8.5 + '@lmdb/lmdb-win32-x64': 2.8.5 + load-json-file@4.0.0: dependencies: graceful-fs: 4.2.11 @@ -17213,6 +19830,8 @@ snapshots: lodash.ismatch@4.4.0: {} + lodash.memoize@3.0.4: {} + lodash.merge@4.6.2: {} lodash.once@4.1.1: {} @@ -17251,6 +19870,8 @@ snapshots: dependencies: get-func-name: 2.0.2 + lower-case@1.1.4: {} + lowercase-keys@1.0.1: {} lowercase-keys@2.0.0: {} @@ -17276,8 +19897,14 @@ snapshots: dependencies: inherits: 2.0.4 + luxon@3.5.0: {} + lz-string@1.5.0: {} + magic-string@0.23.2: + dependencies: + sourcemap-codec: 1.4.8 + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -17291,6 +19918,10 @@ snapshots: pify: 4.0.1 semver: 5.7.2 + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + make-dir@4.0.0: dependencies: semver: 7.6.2 @@ -17366,6 +19997,12 @@ snapshots: dependencies: blueimp-md5: 2.19.0 + md5.js@1.3.5: + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + mdast-util-from-markdown@1.3.1: dependencies: '@types/mdast': 3.0.15 @@ -17387,6 +20024,8 @@ snapshots: dependencies: '@types/mdast': 3.0.15 + mdn-data@2.0.14: {} + media-typer@0.3.0: {} mem@9.0.2: @@ -17422,6 +20061,10 @@ snapshots: dependencies: is-plain-obj: 2.1.0 + merge-source-map@1.0.4: + dependencies: + source-map: 0.5.7 + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -17736,7 +20379,7 @@ snapshots: micromark@3.2.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) decode-named-character-reference: 1.0.2 micromark-core-commonmark: 1.1.0 micromark-factory-space: 1.1.0 @@ -17760,6 +20403,11 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + miller-rabin@4.0.1: + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + mime-db@1.52.0: {} mime-types@2.1.35: @@ -17780,6 +20428,10 @@ snapshots: min-indent@1.0.1: {} + minimalistic-assert@1.0.1: {} + + minimalistic-crypto-utils@1.0.1: {} + minimatch@3.0.5: dependencies: brace-expansion: 1.1.11 @@ -17881,8 +20533,28 @@ snapshots: modify-values@1.0.1: {} + module-deps@6.2.3: + dependencies: + JSONStream: 1.3.5 + browser-resolve: 2.0.0 + cached-path-relative: 1.1.0 + concat-stream: 1.6.2 + defined: 1.0.1 + detective: 5.2.1 + duplexer2: 0.1.4 + inherits: 2.0.4 + parents: 1.0.1 + readable-stream: 2.3.8 + resolve: 1.22.8 + stream-combiner2: 1.1.1 + subarg: 1.0.0 + through2: 2.0.5 + xtend: 4.0.2 + module-error@1.0.2: {} + morphdom@2.7.4: {} + mortice@3.0.4: dependencies: observable-webworkers: 2.0.1 @@ -17897,6 +20569,8 @@ snapshots: ms@2.1.3: {} + ms@3.0.0-canary.1: {} + msgpackr-extract@3.0.2: dependencies: node-gyp-build-optional-packages: 5.0.7 @@ -17936,12 +20610,39 @@ snapshots: mute-stream@1.0.0: {} + mutexify@1.4.0: + dependencies: + queue-tick: 1.0.1 + mz@2.7.0: dependencies: any-promise: 1.3.0 object-assign: 4.1.1 thenify-all: 1.6.0 + nanoassert@1.1.0: {} + + nanobench@2.1.1: + dependencies: + browser-process-hrtime: 0.1.3 + chalk: 1.1.3 + mutexify: 1.4.0 + pretty-hrtime: 1.0.3 + + nanohtml@1.10.0: + dependencies: + acorn-node: 1.8.2 + camel-case: 3.0.0 + convert-source-map: 1.9.0 + estree-is-member-expression: 1.0.0 + hyperx: 2.5.4 + is-boolean-attribute: 0.0.1 + nanoassert: 1.1.0 + nanobench: 2.1.1 + normalize-html-whitespace: 0.2.0 + through2: 2.0.5 + transform-ast: 2.4.4 + nanoid@3.3.7: {} napi-build-utils@1.0.2: {} @@ -17958,6 +20659,10 @@ snapshots: nice-try@1.0.5: {} + no-case@2.3.2: + dependencies: + lower-case: 1.1.4 + nocache@3.0.4: {} node-abi@3.65.0: @@ -17968,6 +20673,10 @@ snapshots: node-addon-api@3.2.1: {} + node-addon-api@6.1.0: {} + + node-addon-api@7.1.1: {} + node-datachannel@0.10.1: dependencies: node-domexception: 2.0.1 @@ -17993,7 +20702,6 @@ snapshots: node-gyp-build-optional-packages@5.1.1: dependencies: detect-libc: 2.0.3 - optional: true node-gyp-build@4.8.1: {} @@ -18022,6 +20730,8 @@ snapshots: node-stream-zip@1.15.0: {} + nodemark@0.3.0: {} + nofilter@3.1.0: {} non-layered-tidy-tree-layout@2.0.2: {} @@ -18030,6 +20740,8 @@ snapshots: dependencies: abbrev: 1.1.1 + normalize-html-whitespace@0.2.0: {} + normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 @@ -18147,8 +20859,14 @@ snapshots: gauge: 4.0.4 set-blocking: 2.0.0 + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + nullthrows@1.1.1: {} + number-is-nan@1.0.1: {} + nx@16.10.0(@swc/core@1.5.24): dependencies: '@nrwl/tao': 16.10.0(@swc/core@1.5.24) @@ -18198,7 +20916,7 @@ snapshots: '@nx/nx-linux-x64-musl': 16.10.0 '@nx/nx-win32-arm64-msvc': 16.10.0 '@nx/nx-win32-x64-msvc': 16.10.0 - '@swc/core': 1.5.24 + '@swc/core': 1.5.24(@swc/helpers@0.5.12) transitivePeerDependencies: - debug @@ -18263,6 +20981,8 @@ snapshots: on-headers@1.0.2: {} + on-net-listen@1.1.2: {} + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -18297,6 +21017,10 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + opn@5.5.0: + dependencies: + is-wsl: 1.1.0 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -18318,6 +21042,10 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 + ordered-binary@1.5.1: {} + + os-browserify@0.3.0: {} + os-tmpdir@1.0.2: {} ospath@1.2.2: {} @@ -18453,10 +21181,52 @@ snapshots: dependencies: wcwidth: 1.0.1 + pako@1.0.11: {} + + parcel@2.12.0(@swc/helpers@0.5.12)(postcss@8.4.38)(terser@5.31.0)(typescript@5.5.3): + dependencies: + '@parcel/config-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(postcss@8.4.38)(terser@5.31.0)(typescript@5.5.3) + '@parcel/core': 2.12.0(@swc/helpers@0.5.12) + '@parcel/diagnostic': 2.12.0 + '@parcel/events': 2.12.0 + '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/logger': 2.12.0 + '@parcel/package-manager': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12))(@swc/helpers@0.5.12) + '@parcel/reporter-cli': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/reporter-dev-server': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/reporter-tracer': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.12)) + '@parcel/utils': 2.12.0 + chalk: 4.1.2 + commander: 7.2.0 + get-port: 4.2.0 + transitivePeerDependencies: + - '@swc/helpers' + - cssnano + - postcss + - purgecss + - relateurl + - srcset + - terser + - typescript + - uncss + parent-module@1.0.1: dependencies: callsites: 3.1.0 + parents@1.0.1: + dependencies: + path-platform: 0.11.15 + + parse-asn1@5.1.7: + dependencies: + asn1.js: 4.10.1 + browserify-aes: 1.2.0 + evp_bytestokey: 1.0.3 + hash-base: 3.0.4 + pbkdf2: 3.1.2 + safe-buffer: 5.2.1 + parse-json@4.0.0: dependencies: error-ex: 1.3.2 @@ -18481,6 +21251,8 @@ snapshots: parseurl@1.3.3: {} + path-browserify@1.0.1: {} + path-exists@3.0.0: {} path-exists@4.0.0: {} @@ -18499,6 +21271,8 @@ snapshots: path-parse@1.0.7: {} + path-platform@0.11.15: {} + path-scurry@1.11.1: dependencies: lru-cache: 10.2.2 @@ -18516,6 +21290,14 @@ snapshots: pathval@1.1.1: {} + pbkdf2@3.1.2: + dependencies: + create-hash: 1.2.0 + create-hmac: 1.1.7 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + pend@1.2.0: {} performance-now@2.1.0: {} @@ -18603,21 +21385,21 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.4.38 - postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.4.5)): + postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3)): dependencies: lilconfig: 3.1.1 yaml: 2.4.2 optionalDependencies: postcss: 8.4.38 - ts-node: 10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.4.5) + ts-node: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3) - postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3)): + postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.4.5)): dependencies: lilconfig: 3.1.1 yaml: 2.4.2 optionalDependencies: postcss: 8.4.38 - ts-node: 10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3) + ts-node: 10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.4.5) postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2(@swc/core@1.5.24)(@types/node@20.14.11)(typescript@5.4.5)): dependencies: @@ -18645,6 +21427,23 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + posthtml-parser@0.10.2: + dependencies: + htmlparser2: 7.2.0 + + posthtml-parser@0.11.0: + dependencies: + htmlparser2: 7.2.0 + + posthtml-render@3.0.0: + dependencies: + is-json: 2.0.1 + + posthtml@0.16.6: + dependencies: + posthtml-parser: 0.11.0 + posthtml-render: 3.0.0 + prebuild-install@7.1.2: dependencies: detect-libc: 2.0.3 @@ -18691,6 +21490,8 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + pretty-hrtime@1.0.3: {} + pretty-ms@8.0.0: dependencies: parse-ms: 3.0.0 @@ -18754,11 +21555,28 @@ snapshots: psl@1.9.0: {} + public-encrypt@4.0.3: + dependencies: + bn.js: 4.12.0 + browserify-rsa: 4.1.0 + create-hash: 1.2.0 + parse-asn1: 5.1.7 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + pump@3.0.0: dependencies: end-of-stream: 1.4.4 once: 1.4.0 + pumpify@2.0.1: + dependencies: + duplexify: 4.1.3 + inherits: 2.0.4 + pump: 3.0.0 + + punycode@1.4.1: {} + punycode@2.3.1: {} pvtsutils@1.3.5: @@ -18775,12 +21593,20 @@ snapshots: dependencies: side-channel: 1.0.6 + qs@6.12.3: + dependencies: + side-channel: 1.0.6 + + querystring-es3@0.2.1: {} + querystring@0.2.1: {} querystringify@2.2.0: {} queue-microtask@1.2.3: {} + queue-tick@1.0.1: {} + queue@6.0.2: dependencies: inherits: 2.0.4 @@ -18797,6 +21623,11 @@ snapshots: dependencies: safe-buffer: 5.2.1 + randomfill@1.0.4: + dependencies: + randombytes: 2.1.0 + safe-buffer: 5.2.1 + range-parser@1.2.1: {} raw-body@2.5.2: @@ -18848,6 +21679,8 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-error-overlay@6.0.9: {} + react-is@16.13.1: {} react-is@17.0.2: {} @@ -18915,6 +21748,8 @@ snapshots: react-refresh@0.14.2: {} + react-refresh@0.9.0: {} + react-shallow-renderer@16.15.0(react@18.3.1): dependencies: object-assign: 4.1.1 @@ -18935,6 +21770,10 @@ snapshots: read-cmd-shim@4.0.0: {} + read-only-stream@2.0.0: + dependencies: + readable-stream: 2.3.8 + read-package-json-fast@3.0.2: dependencies: json-parse-even-better-errors: 3.0.2 @@ -19082,6 +21921,8 @@ snapshots: require-directory@2.1.1: {} + require-from-string@2.0.2: {} + require-main-filename@2.0.0: {} requires-port@1.0.0: {} @@ -19145,6 +21986,13 @@ snapshots: dependencies: glob: 10.4.1 + ripemd160@2.0.2: + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + + rng@0.2.2: {} + roarr@2.15.4: dependencies: boolean: 3.2.0 @@ -19323,10 +22171,19 @@ snapshots: setprototypeof@1.2.0: {} + sha.js@2.4.11: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + shallow-clone@3.0.1: dependencies: kind-of: 6.0.3 + shasum-object@1.0.0: + dependencies: + fast-safe-stringify: 2.1.1 + shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 @@ -19383,6 +22240,10 @@ snapshots: once: 1.4.0 simple-concat: 1.0.1 + single-line-log@1.1.2: + dependencies: + string-width: 1.0.2 + sisteransi@1.0.5: {} slash@3.0.0: {} @@ -19419,7 +22280,7 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -19450,6 +22311,8 @@ snapshots: dependencies: whatwg-url: 7.1.0 + sourcemap-codec@1.4.8: {} + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 @@ -19468,6 +22331,8 @@ snapshots: dependencies: readable-stream: 3.6.2 + split2@4.2.0: {} + split@1.0.1: dependencies: through: 2.3.8 @@ -19476,6 +22341,8 @@ snapshots: sprintf-js@1.1.3: {} + srcset@4.0.0: {} + sshpk@1.18.0: dependencies: asn1: 0.2.6 @@ -19496,6 +22363,8 @@ snapshots: dependencies: minipass: 3.3.6 + stable@0.1.8: {} + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -19518,10 +22387,40 @@ snapshots: dependencies: internal-slot: 1.0.7 + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + + stream-combiner2@1.1.1: + dependencies: + duplexer2: 0.1.4 + readable-stream: 2.3.8 + + stream-http@3.2.0: + dependencies: + builtin-status-codes: 3.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + xtend: 4.0.2 + + stream-shift@1.0.3: {} + + stream-splicer@2.0.1: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.8 + stream-to-it@1.0.1: dependencies: it-stream-types: 2.0.1 + string-width@1.0.2: + dependencies: + code-point-at: 1.1.0 + is-fullwidth-code-point: 1.0.0 + strip-ansi: 3.0.1 + string-width@2.1.1: dependencies: is-fullwidth-code-point: 2.0.0 @@ -19588,6 +22487,10 @@ snapshots: dependencies: safe-buffer: 5.2.1 + strip-ansi@3.0.1: + dependencies: + ansi-regex: 2.1.1 + strip-ansi@4.0.0: dependencies: ansi-regex: 3.0.1 @@ -19636,6 +22539,10 @@ snapshots: stylis@4.3.2: {} + subarg@1.0.0: + dependencies: + minimist: 1.2.8 + sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.5 @@ -19650,7 +22557,7 @@ snapshots: sumchecker@3.0.1: dependencies: - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -19667,6 +22574,8 @@ snapshots: serialize-error: 7.0.1 strip-ansi: 7.1.0 + supports-color@2.0.0: {} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -19679,6 +22588,8 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-color@9.4.0: {} + supports-hyperlinks@2.3.0: dependencies: has-flag: 4.0.0 @@ -19686,14 +22597,30 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svgo@2.8.0: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 4.3.0 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.0.1 + stable: 0.1.8 + synckit@0.8.8: dependencies: '@pkgr/core': 0.1.1 tslib: 2.6.2 + syntax-error@1.4.0: + dependencies: + acorn-node: 1.8.2 + system-architecture@0.1.0: {} - tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3)): + tachyons@4.12.0: {} + + tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -19712,7 +22639,7 @@ snapshots: postcss: 8.4.38 postcss-import: 15.1.0(postcss@8.4.38) postcss-js: 4.0.1(postcss@8.4.38) - postcss-load-config: 4.0.2(postcss@8.4.38)(ts-node@10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3)) + postcss-load-config: 4.0.2(postcss@8.4.38)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3)) postcss-nested: 6.0.1(postcss@8.4.38) postcss-selector-parser: 6.1.0 resolve: 1.22.8 @@ -19789,6 +22716,8 @@ snapshots: dependencies: execa: 0.7.0 + term-size@2.2.1: {} + terser-webpack-plugin@5.3.10(@swc/core@1.5.24)(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.5.24)(esbuild@0.19.12)): dependencies: '@jridgewell/trace-mapping': 0.3.25 @@ -19798,7 +22727,7 @@ snapshots: terser: 5.31.0 webpack: 5.91.0(@swc/core@1.5.24)(esbuild@0.19.12) optionalDependencies: - '@swc/core': 1.5.24 + '@swc/core': 1.5.24(@swc/helpers@0.5.12) esbuild: 0.19.12 terser@5.31.0: @@ -19829,6 +22758,15 @@ snapshots: readable-stream: 2.3.8 xtend: 4.0.2 + through2@3.0.2: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + + through2@4.0.2: + dependencies: + readable-stream: 3.6.2 + through@2.3.8: {} thunky@1.1.0: {} @@ -19845,8 +22783,14 @@ snapshots: dependencies: retimer: 3.0.0 + timers-browserify@1.4.2: + dependencies: + process: 0.11.10 + timestamp-nano@1.0.1: {} + timsort@0.3.0: {} + tiny-emitter@2.1.0: {} tiny-typed-emitter@2.1.0: {} @@ -19891,6 +22835,16 @@ snapshots: dependencies: punycode: 2.3.1 + transform-ast@2.4.4: + dependencies: + acorn-node: 1.8.2 + convert-source-map: 1.9.0 + dash-ast: 1.0.0 + is-buffer: 2.0.5 + magic-string: 0.23.2 + merge-source-map: 1.0.4 + nanobench: 2.1.1 + tree-kill@1.2.2: {} trim-newlines@3.0.1: {} @@ -19907,7 +22861,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.4.5): + ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.12))(@types/node@18.19.33)(typescript@5.5.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -19921,14 +22875,13 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.4.5 + typescript: 5.5.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.5.24 - optional: true + '@swc/core': 1.5.24(@swc/helpers@0.5.12) - ts-node@10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.5.3): + ts-node@10.9.2(@swc/core@1.5.24)(@types/node@18.19.33)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -19942,11 +22895,12 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.5.3 + typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.5.24 + '@swc/core': 1.5.24(@swc/helpers@0.5.12) + optional: true ts-node@10.9.2(@swc/core@1.5.24)(@types/node@20.14.11)(typescript@5.4.5): dependencies: @@ -19966,7 +22920,7 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.5.24 + '@swc/core': 1.5.24(@swc/helpers@0.5.12) ts-node@10.9.2(@swc/core@1.5.24)(@types/node@20.14.11)(typescript@5.5.3): dependencies: @@ -19986,7 +22940,7 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.5.24 + '@swc/core': 1.5.24(@swc/helpers@0.5.12) tsconfck@3.1.0(typescript@5.4.5): optionalDependencies: @@ -20012,7 +22966,7 @@ snapshots: bundle-require: 4.1.0(esbuild@0.19.12) cac: 6.7.14 chokidar: 3.6.0 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) esbuild: 0.19.12 execa: 5.1.1 globby: 11.1.0 @@ -20024,17 +22978,19 @@ snapshots: sucrase: 3.35.0 tree-kill: 1.2.2 optionalDependencies: - '@swc/core': 1.5.24 + '@swc/core': 1.5.24(@swc/helpers@0.5.12) postcss: 8.4.38 typescript: 5.4.5 transitivePeerDependencies: - supports-color - ts-node + tty-browserify@0.0.1: {} + tuf-js@1.1.7: dependencies: '@tufjs/models': 1.0.4 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color @@ -20049,6 +23005,8 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-component@0.0.1: {} + type-detect@4.0.8: {} type-fest@0.13.1: {} @@ -20134,6 +23092,8 @@ snapshots: dependencies: multiformats: 13.2.1 + umd@3.0.3: {} + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 @@ -20143,6 +23103,14 @@ snapshots: unc-path-regex@0.1.2: {} + undeclared-identifiers@1.1.3: + dependencies: + acorn-node: 1.8.2 + dash-ast: 1.0.0 + get-assigned-identifiers: 1.2.0 + simple-concat: 1.0.1 + xtend: 4.0.2 + undici-types@5.26.5: {} unicode-canonical-property-names-ecmascript@2.0.0: {} @@ -20221,6 +23189,8 @@ snapshots: semver-diff: 2.1.0 xdg-basedir: 3.0.0 + upper-case@1.1.3: {} + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -20236,10 +23206,31 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + url@0.11.4: + dependencies: + punycode: 1.4.1 + qs: 6.12.3 + utf8-byte-length@1.0.5: {} util-deprecate@1.0.2: {} + util-extend@1.0.3: {} + + util@0.10.4: + dependencies: + inherits: 2.0.3 + + util@0.12.5: + dependencies: + inherits: 2.0.4 + is-arguments: 1.1.1 + is-generator-function: 1.0.10 + is-typed-array: 1.1.13 + which-typed-array: 1.1.15 + + utility-types@3.11.0: {} + utils-merge@1.0.1: {} uuid@8.3.2: {} @@ -20278,13 +23269,13 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 - vite-node@1.6.0(@types/node@18.19.33)(terser@5.31.0): + vite-node@1.6.0(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0): dependencies: cac: 6.7.14 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) pathe: 1.1.2 picocolors: 1.0.1 - vite: 5.2.12(@types/node@18.19.33)(terser@5.31.0) + vite: 5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0) transitivePeerDependencies: - '@types/node' - less @@ -20295,13 +23286,13 @@ snapshots: - supports-color - terser - vite-node@1.6.0(@types/node@20.14.11)(terser@5.31.0): + vite-node@1.6.0(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0): dependencies: cac: 6.7.14 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) pathe: 1.1.2 picocolors: 1.0.1 - vite: 5.2.12(@types/node@20.14.11)(terser@5.31.0) + vite: 5.2.12(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0) transitivePeerDependencies: - '@types/node' - less @@ -20313,42 +23304,42 @@ snapshots: - terser optional: true - vite-plugin-top-level-await@1.4.1(rollup@4.18.0)(vite@5.2.12(@types/node@18.19.33)(terser@5.31.0)): + vite-plugin-top-level-await@1.4.1(@swc/helpers@0.5.12)(rollup@4.18.0)(vite@5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0)): dependencies: '@rollup/plugin-virtual': 3.0.2(rollup@4.18.0) - '@swc/core': 1.5.24 + '@swc/core': 1.5.24(@swc/helpers@0.5.12) uuid: 9.0.1 - vite: 5.2.12(@types/node@18.19.33)(terser@5.31.0) + vite: 5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0) transitivePeerDependencies: - '@swc/helpers' - rollup - vite-plugin-top-level-await@1.4.1(rollup@4.18.0)(vite@5.2.12(@types/node@20.14.11)(terser@5.31.0)): + vite-plugin-top-level-await@1.4.1(@swc/helpers@0.5.12)(rollup@4.18.0)(vite@5.2.12(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0)): dependencies: '@rollup/plugin-virtual': 3.0.2(rollup@4.18.0) - '@swc/core': 1.5.24 + '@swc/core': 1.5.24(@swc/helpers@0.5.12) uuid: 9.0.1 - vite: 5.2.12(@types/node@20.14.11)(terser@5.31.0) + vite: 5.2.12(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0) transitivePeerDependencies: - '@swc/helpers' - rollup - vite-plugin-wasm@3.3.0(vite@5.2.12(@types/node@18.19.33)(terser@5.31.0)): + vite-plugin-wasm@3.3.0(vite@5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0)): dependencies: - vite: 5.2.12(@types/node@18.19.33)(terser@5.31.0) + vite: 5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0) - vite-tsconfig-paths@4.3.2(typescript@5.4.5)(vite@5.2.12(@types/node@18.19.33)(terser@5.31.0)): + vite-tsconfig-paths@4.3.2(typescript@5.4.5)(vite@5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0)): dependencies: - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) globrex: 0.1.2 tsconfck: 3.1.0(typescript@5.4.5) optionalDependencies: - vite: 5.2.12(@types/node@18.19.33)(terser@5.31.0) + vite: 5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0) transitivePeerDependencies: - supports-color - typescript - vite@5.2.12(@types/node@18.19.33)(terser@5.31.0): + vite@5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0): dependencies: esbuild: 0.20.2 postcss: 8.4.38 @@ -20356,9 +23347,10 @@ snapshots: optionalDependencies: '@types/node': 18.19.33 fsevents: 2.3.3 + lightningcss: 1.26.0 terser: 5.31.0 - vite@5.2.12(@types/node@20.14.11)(terser@5.31.0): + vite@5.2.12(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0): dependencies: esbuild: 0.20.2 postcss: 8.4.38 @@ -20366,9 +23358,10 @@ snapshots: optionalDependencies: '@types/node': 20.14.11 fsevents: 2.3.3 + lightningcss: 1.26.0 terser: 5.31.0 - vitest@1.6.0(@types/node@18.19.33)(terser@5.31.0): + vitest@1.6.0(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -20377,7 +23370,7 @@ snapshots: '@vitest/utils': 1.6.0 acorn-walk: 8.3.2 chai: 4.4.1 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 execa: 8.0.1 local-pkg: 0.5.0 magic-string: 0.30.10 @@ -20387,8 +23380,8 @@ snapshots: strip-literal: 2.1.0 tinybench: 2.8.0 tinypool: 0.8.4 - vite: 5.2.12(@types/node@18.19.33)(terser@5.31.0) - vite-node: 1.6.0(@types/node@18.19.33)(terser@5.31.0) + vite: 5.2.12(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0) + vite-node: 1.6.0(@types/node@18.19.33)(lightningcss@1.26.0)(terser@5.31.0) why-is-node-running: 2.2.2 optionalDependencies: '@types/node': 18.19.33 @@ -20401,7 +23394,7 @@ snapshots: - supports-color - terser - vitest@1.6.0(@types/node@20.14.11)(terser@5.31.0): + vitest@1.6.0(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -20410,7 +23403,7 @@ snapshots: '@vitest/utils': 1.6.0 acorn-walk: 8.3.2 chai: 4.4.1 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 execa: 8.0.1 local-pkg: 0.5.0 magic-string: 0.30.10 @@ -20420,8 +23413,8 @@ snapshots: strip-literal: 2.1.0 tinybench: 2.8.0 tinypool: 0.8.4 - vite: 5.2.12(@types/node@20.14.11)(terser@5.31.0) - vite-node: 1.6.0(@types/node@20.14.11)(terser@5.31.0) + vite: 5.2.12(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0) + vite-node: 1.6.0(@types/node@20.14.11)(lightningcss@1.26.0)(terser@5.31.0) why-is-node-running: 2.2.2 optionalDependencies: '@types/node': 20.14.11 @@ -20437,6 +23430,8 @@ snapshots: vlq@1.0.1: {} + vm-browserify@1.1.2: {} + walker@1.0.8: dependencies: makeerror: 1.0.12 @@ -20450,6 +23445,13 @@ snapshots: dependencies: defaults: 1.0.4 + weak-lru-cache@1.2.2: {} + + weald@1.0.2: + dependencies: + ms: 3.0.0-canary.1 + supports-color: 9.4.0 + web-worker@1.3.0: {} webidl-conversions@3.0.1: {} @@ -20653,7 +23655,7 @@ snapshots: eslint-config-xo: 0.43.1(eslint@8.57.0) eslint-config-xo-typescript: 1.0.1(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3))(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3) eslint-formatter-pretty: 5.0.0 - eslint-import-resolver-webpack: 0.13.8(eslint-plugin-import@2.27.5(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(webpack@5.91.0(@swc/core@1.5.24)(esbuild@0.19.12)) + eslint-import-resolver-webpack: 0.13.8(eslint-plugin-import@2.27.5)(webpack@5.91.0(@swc/core@1.5.24)(esbuild@0.19.12)) eslint-plugin-ava: 14.0.0(eslint@8.57.0) eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.0) eslint-plugin-import: 2.27.5(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-webpack@0.13.8)(eslint@8.57.0) diff --git a/wallaby.conf.cjs b/wallaby.conf.cjs deleted file mode 100644 index 4c8ef11d..00000000 --- a/wallaby.conf.cjs +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = function (wallaby) { - return { - autoDetect: true, - runMode: 'onsave', - slowTestThreshold: 5000, - lowCoverageThreshold: 99, - hints: { - ignoreCoverageForFile: /ignore file coverage/, - ignoreCoverage: /ignore coverage/, - }, - } -} diff --git a/xo.config.cjs b/xo.config.cjs deleted file mode 100644 index 38344f67..00000000 --- a/xo.config.cjs +++ /dev/null @@ -1,104 +0,0 @@ -const OFF = 0 -const WARN = 1 -const ERROR = 2 -const NEVER = 'never' -const ALWAYS = 'always' - -module.exports = { - plugins: ['unused-imports'], - - // use existing prettier config - prettier: true, - - ignore: [ - // not bothering with config files & scripts for now - '*.cjs', - '*.js', - // or with the demo - 'demos/**/*', - ], - - rules: { - // ADDED RULES - - 'unused-imports/no-unused-imports': ERROR, - 'unused-imports/no-unused-vars': [ - ERROR, - { - vars: 'all', - varsIgnorePattern: '^_', - args: 'after-used', - argsIgnorePattern: '^_', - }, - ], - - // DISABLED RULES - - '@typescript-eslint/consistent-type-assertions': OFF, // sometimes you need to assert - '@typescript-eslint/no-empty-function': OFF, // don't see the problem - '@typescript-eslint/no-implicit-any-catch': OFF, // deprecated - '@typescript-eslint/padding-line-between-statements': OFF, // leave formatting to prettierjs - 'capitalized-comments': OFF, // case in point this comment - 'default-case': OFF, // not necessary with typescript - 'guard-for-in': OFF, // not necessary with typescript - 'import/no-extraneous-dependencies': OFF, // haven't figured out how to make this work with monorepo - 'n/file-extension-in-import': OFF, // duplicate of import/extensions - 'n/prefer-global/process': OFF, // use globalThis - 'no-else-return': OFF, // don't agree - 'unicorn/no-array-reduce': OFF, // sometimes I like to reduce - 'unicorn/no-negated-condition': OFF, // sometimes prefer to keep conditions in a certain order - 'unicorn/prefer-node-protocol': OFF, // false positives with /util folder - 'unicorn/prefer-spread': OFF, // don't find [...a] readable compared to a.split('') - 'unicorn/prevent-abbreviations': OFF, // gets mad about "numLikes" etc. - - // DISABLED FOR EXPEDIENCY, MIGHT REVISIT - 'max-params': OFF, - 'no-prototype-builtins': OFF, - 'import/no-unassigned-import': OFF, - 'unicorn/no-array-callback-reference': OFF, - 'unicorn/prefer-event-target': OFF, - 'no-warning-comments': OFF, - 'unicorn/filename-case': OFF, - 'unicorn/no-object-as-default-parameter': OFF, - 'unicorn/prefer-array-some': OFF, - '@typescript-eslint/default-param-last': OFF, - '@typescript-eslint/no-unsafe-argument': OFF, - '@typescript-eslint/no-unsafe-assignment': OFF, - '@typescript-eslint/no-unsafe-return': OFF, - '@typescript-eslint/no-unsafe-call': OFF, - '@typescript-eslint/member-ordering': OFF, - '@typescript-eslint/no-redeclare': OFF, - 'import/order': OFF, - 'max-nested-callbacks': OFF, - complexity: OFF, - - // MODIFIED RULES - - // default makes us wrap every arrow function shorthand expression with braces, - // which spreads a single line out to 3 lines - '@typescript-eslint/no-confusing-void-expression': [WARN, { ignoreArrowShorthand: true }], - - // default is camelCase only. We want PascalCase for React components, and UPPER_CASE for constants. - '@typescript-eslint/naming-convention': [ - ERROR, - { - selector: 'variable', - format: ['camelCase', 'PascalCase', 'UPPER_CASE'], - leadingUnderscore: 'allow', - }, - ], - - // require file extensions on imports - 'import/extensions': [ERROR, ALWAYS, { ignorePackages: true }], - - // default is kebabCase - 'unicorn/filename-case': [ERROR, { cases: { camelCase: true, pascalCase: true } }], - - // don't flag wallaby magic comment `//?` - 'spaced-comment': [ERROR, ALWAYS, { block: { markers: ['?'], balanced: true } }], - - 'ava/no-import-test-files': [ERROR, { files: ['*.test.ts'] }], - }, - - overrides: [], -}