Client side atproto account migrator in your web browser, along with services for backups and adversarial migrations. pdsmoover.com
pds atproto migrations moo cow

moved to the oxfmt

authored by baileytownsend.dev and committed by tangled.org d829485d b6b47d85

+5499 -1761
+8
.oxfmtrc.json
··· 1 + { 2 + "$schema": "https://unpkg.com/oxfmt/configuration_schema.json", 3 + "semi": false, 4 + "singleQuote": true, 5 + "arrowParens": "avoid", 6 + "quoteProps": "consistent", 7 + "experimentalSortPackageJson": false 8 + }
+113
.oxlintrc.json
··· 1 + { 2 + "$schema": "https://unpkg.com/oxlint/configuration_schema.json", 3 + "plugins": ["unicorn", "typescript", "oxc", "vue", "vitest"], 4 + "jsPlugins": ["@e18e/eslint-plugin", "eslint-plugin-regexp"], 5 + "categories": { 6 + "correctness": "error", 7 + "suspicious": "warn", 8 + "perf": "warn" 9 + }, 10 + "rules": { 11 + "no-console": "warn", 12 + "no-await-in-loop": "off", 13 + "unicorn/no-array-sort": "off", 14 + "no-restricted-globals": "error", 15 + "typescript/consistent-type-imports": "error", 16 + "e18e/prefer-array-from-map": "error", 17 + "e18e/prefer-timer-args": "error", 18 + "e18e/prefer-date-now": "error", 19 + "e18e/prefer-regex-test": "error", 20 + "e18e/prefer-array-some": "error", 21 + // RegExp - Possible Errors (most critical) 22 + "regexp/no-contradiction-with-assertion": "error", 23 + "regexp/no-dupe-disjunctions": "error", 24 + "regexp/no-empty-alternative": "error", 25 + "regexp/no-empty-capturing-group": "error", 26 + "regexp/no-empty-character-class": "error", 27 + "regexp/no-empty-group": "error", 28 + "regexp/no-empty-lookarounds-assertion": "error", 29 + "regexp/no-escape-backspace": "error", 30 + "regexp/no-invalid-regexp": "error", 31 + "regexp/no-lazy-ends": "error", 32 + "regexp/no-misleading-capturing-group": "error", 33 + "regexp/no-misleading-unicode-character": "error", 34 + "regexp/no-missing-g-flag": "error", 35 + "regexp/no-optional-assertion": "error", 36 + "regexp/no-potentially-useless-backreference": "error", 37 + "regexp/no-super-linear-backtracking": "error", 38 + "regexp/no-useless-assertions": "error", 39 + "regexp/no-useless-backreference": "error", 40 + "regexp/no-useless-dollar-replacements": "error", 41 + "regexp/strict": "error", 42 + // RegExp - Best Practices 43 + "regexp/confusing-quantifier": "warn", 44 + "regexp/control-character-escape": "error", 45 + "regexp/negation": "error", 46 + "regexp/no-dupe-characters-character-class": "error", 47 + "regexp/no-empty-string-literal": "error", 48 + "regexp/no-extra-lookaround-assertions": "error", 49 + "regexp/no-invisible-character": "error", 50 + "regexp/no-legacy-features": "error", 51 + "regexp/no-non-standard-flag": "error", 52 + "regexp/no-obscure-range": "error", 53 + "regexp/no-octal": "error", 54 + "regexp/no-standalone-backslash": "error", 55 + "regexp/no-trivially-nested-assertion": "error", 56 + "regexp/no-trivially-nested-quantifier": "error", 57 + "regexp/no-unused-capturing-group": "warn", 58 + "regexp/no-useless-character-class": "error", 59 + "regexp/no-useless-flag": "error", 60 + "regexp/no-useless-lazy": "error", 61 + "regexp/no-useless-quantifier": "error", 62 + "regexp/no-useless-range": "error", 63 + "regexp/no-useless-set-operand": "error", 64 + "regexp/no-useless-string-literal": "error", 65 + "regexp/no-useless-two-nums-quantifier": "error", 66 + "regexp/no-zero-quantifier": "error", 67 + "regexp/optimal-lookaround-quantifier": "warn", 68 + "regexp/optimal-quantifier-concatenation": "error", 69 + "regexp/prefer-predefined-assertion": "error", 70 + "regexp/prefer-range": "error", 71 + "regexp/prefer-set-operation": "error", 72 + "regexp/simplify-set-operations": "error", 73 + "regexp/use-ignore-case": "error", 74 + // RegExp - Stylistic Issues (less critical, focused on consistency) 75 + "regexp/match-any": "warn", 76 + "regexp/no-useless-escape": "warn", 77 + "regexp/no-useless-non-capturing-group": "warn", 78 + "regexp/prefer-character-class": "warn", 79 + "regexp/prefer-d": "warn", 80 + "regexp/prefer-plus-quantifier": "warn", 81 + "regexp/prefer-question-quantifier": "warn", 82 + "regexp/prefer-star-quantifier": "warn", 83 + "regexp/prefer-unicode-codepoint-escapes": "warn", 84 + "regexp/prefer-w": "warn", 85 + "regexp/sort-flags": "warn" 86 + }, 87 + "overrides": [ 88 + { 89 + "files": [ 90 + "server/**/*", 91 + "cli/**/*", 92 + "scripts/**/*", 93 + "modules/**/*", 94 + "app/components/OgImage/*" 95 + ], 96 + "rules": { 97 + "no-console": "off" 98 + } 99 + } 100 + ], 101 + "ignorePatterns": [ 102 + ".output/**", 103 + ".data/**", 104 + ".nuxt/**", 105 + ".nitro/**", 106 + ".cache/**", 107 + "dist/**", 108 + "node_modules/**", 109 + "coverage/**", 110 + "playwright-report/**", 111 + "test-results/**" 112 + ] 113 + }
+87
.zed/settings.json
··· 1 + { 2 + "lsp": { 3 + "oxlint": { 4 + "initialization_options": { 5 + "settings": { 6 + "configPath": ".oxlintrc.json", 7 + "run": "onType" 8 + } 9 + } 10 + }, 11 + "oxfmt": { 12 + "initialization_options": { 13 + "settings": { 14 + "run": "onSave", 15 + "configPath": ".oxfmtrc.json" 16 + } 17 + } 18 + } 19 + }, 20 + "languages": { 21 + "TypeScript": { 22 + "format_on_save": "on", 23 + "prettier": { 24 + "allowed": false 25 + }, 26 + "formatter": [ 27 + { 28 + "language_server": { 29 + "name": "oxfmt" 30 + } 31 + } 32 + ] 33 + }, 34 + "Svelte": { 35 + "format_on_save": "on", 36 + "prettier": { 37 + "allowed": false 38 + }, 39 + "formatter": [ 40 + { 41 + "language_server": { 42 + "name": "oxfmt" 43 + } 44 + } 45 + ] 46 + }, 47 + "JSON": { 48 + "format_on_save": "on", 49 + "prettier": { 50 + "allowed": false 51 + }, 52 + "formatter": [ 53 + { 54 + "language_server": { 55 + "name": "oxfmt" 56 + } 57 + } 58 + ] 59 + }, 60 + "JSONC": { 61 + "format_on_save": "on", 62 + "prettier": { 63 + "allowed": false 64 + }, 65 + "formatter": [ 66 + { 67 + "language_server": { 68 + "name": "oxfmt" 69 + } 70 + } 71 + ] 72 + }, 73 + "YAML": { 74 + "format_on_save": "on", 75 + "prettier": { 76 + "allowed": false 77 + }, 78 + "formatter": [ 79 + { 80 + "language_server": { 81 + "name": "oxfmt" 82 + } 83 + } 84 + ] 85 + } 86 + } 87 + }
+1 -1
packages/moover/README.md
··· 12 12 instance 13 13 - [MissingBlobs](./lib/missingBlobs.js) - Finds missing blobs on your old PDS and uploads them to your new PDS 14 14 - [PlcOps](./lib/plc-ops.js) - Helpers for manual PCL operations 15 - - [Restore](./lib/restore.js) - Handles a recovery and restores the at proto from the backup 15 + - [Restore](./lib/restore.js) - Handles a recovery and restores the at proto from the backup
+9 -9
packages/moover/index.html
··· 1 1 <!doctype html> 2 2 <html lang="en"> 3 - <head> 4 - <meta charset="UTF-8"/> 5 - <link rel="icon" type="image/svg+xml" href="/vite.svg"/> 6 - <meta name="viewport" content="width=device-width, initial-scale=1.0"/> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <link rel="icon" type="image/svg+xml" href="/vite.svg" /> 6 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 7 7 <title>moover</title> 8 - </head> 9 - <body> 10 - <div id="app"></div> 11 - <script type="module" src="/lib/main.js"></script> 12 - </body> 8 + </head> 9 + <body> 10 + <div id="app"></div> 11 + <script type="module" src="/lib/main.js"></script> 12 + </body> 13 13 </html>
+50
packages/moover/lexicons/blue/microcosm/identity/resolveMiniDoc.json
··· 1 + { 2 + "id": "blue.microcosm.identity.resolveMiniDoc", 3 + "defs": { 4 + "main": { 5 + "type": "query", 6 + "output": { 7 + "schema": { 8 + "type": "object", 9 + "required": ["did", "handle", "pds", "signing_key"], 10 + "properties": { 11 + "did": { 12 + "type": "string", 13 + "format": "did", 14 + "description": "DID, bi-directionally verified if a handle was provided in the query." 15 + }, 16 + "pds": { 17 + "type": "string", 18 + "format": "uri", 19 + "description": "The identity's PDS URL" 20 + }, 21 + "handle": { 22 + "type": "string", 23 + "format": "handle", 24 + "description": "The validated handle of the account or `handle.invalid` if the handle\ndid not bi-directionally match the DID document." 25 + }, 26 + "signing_key": { 27 + "type": "string", 28 + "description": "The atproto signing key publicKeyMultibase\n\nLegacy key encoding not supported. the key is returned directly; `id`,\n`type`, and `controller` are omitted." 29 + } 30 + } 31 + }, 32 + "encoding": "application/json" 33 + }, 34 + "parameters": { 35 + "type": "params", 36 + "required": ["identifier"], 37 + "properties": { 38 + "identifier": { 39 + "type": "string", 40 + "format": "at-identifier", 41 + "description": "Handle or DID to resolve" 42 + } 43 + } 44 + }, 45 + "description": "Like [com.atproto.identity.resolveIdentity](https://docs.bsky.app/docs/api/com-atproto-identity-resolve-identity) but instead of the full `didDoc` it returns an atproto-relevant subset." 46 + } 47 + }, 48 + "$type": "com.atproto.lexicon.schema", 49 + "lexicon": 1 50 + }
+47 -50
packages/moover/lib/atprotoUtils.js
··· 1 1 import { 2 - CompositeDidDocumentResolver, CompositeHandleResolver, 3 - DohJsonHandleResolver, 4 - PlcDidDocumentResolver, WebDidDocumentResolver, 5 - WellKnownHandleResolver 6 - } from '@atcute/identity-resolver'; 2 + CompositeDidDocumentResolver, 3 + CompositeHandleResolver, 4 + DohJsonHandleResolver, 5 + PlcDidDocumentResolver, 6 + WebDidDocumentResolver, 7 + WellKnownHandleResolver, 8 + } from '@atcute/identity-resolver' 7 9 8 10 const handleResolver = new CompositeHandleResolver({ 9 - strategy: 'race', 10 - methods: { 11 - dns: new DohJsonHandleResolver({ 12 - dohUrl: 'https://mozilla.cloudflare-dns.com/dns-query', 13 - }), 14 - http: new WellKnownHandleResolver(), 15 - }, 16 - }); 11 + strategy: 'race', 12 + methods: { 13 + dns: new DohJsonHandleResolver({ 14 + dohUrl: 'https://mozilla.cloudflare-dns.com/dns-query', 15 + }), 16 + http: new WellKnownHandleResolver(), 17 + }, 18 + }) 17 19 18 20 const docResolver = new CompositeDidDocumentResolver({ 19 - methods: { 20 - plc: new PlcDidDocumentResolver(), 21 - web: new WebDidDocumentResolver(), 22 - }, 23 - }); 21 + methods: { 22 + plc: new PlcDidDocumentResolver(), 23 + web: new WebDidDocumentResolver(), 24 + }, 25 + }) 24 26 25 27 /** 26 28 * Cleans the handle of @ and some other unicode characters that used to show up when copied from the profile 27 29 * @param handle {string} 28 30 * @returns {string} 29 31 */ 30 - const cleanHandle = (handle) => 31 - handle.replace('@', '').trim().replace( 32 - /[\u202A\u202C\u200E\u200F\u2066-\u2069]/g, 33 - '', 34 - ); 35 - 32 + const cleanHandle = handle => 33 + handle 34 + .replace('@', '') 35 + .trim() 36 + .replace(/[\u202A\u202C\u200E\u200F\u2066-\u2069]/g, '') 36 37 37 38 /** 38 39 * Convince helper to resolve a handle to a did and then find the PDS url from the did document. ··· 41 42 * @returns {Promise<{usersDid: string, pds: string}>} 42 43 */ 43 44 async function handleAndPDSResolver(handle) { 44 - let usersDid = null; 45 - if (handle.startsWith('did:')) { 46 - usersDid = handle; 47 - } else { 48 - const cleanedHandle = cleanHandle(handle); 49 - usersDid = await handleResolver.resolve(cleanedHandle); 50 - } 51 - const didDoc = await docResolver.resolve(usersDid); 45 + let usersDid = null 46 + if (handle.startsWith('did:')) { 47 + usersDid = handle 48 + } else { 49 + const cleanedHandle = cleanHandle(handle) 50 + usersDid = await handleResolver.resolve(cleanedHandle) 51 + } 52 + const didDoc = await docResolver.resolve(usersDid) 52 53 53 - let pds; 54 - try { 55 - pds = didDoc.service?.filter((s) => 56 - s.type === 'AtprotoPersonalDataServer' 57 - )[0].serviceEndpoint; 58 - } catch (error) { 59 - throw new Error('Could not find a PDS in the DID document.'); 60 - } 61 - return {usersDid, pds}; 54 + let pds 55 + try { 56 + pds = didDoc.service?.filter(s => s.type === 'AtprotoPersonalDataServer')[0].serviceEndpoint 57 + } catch (error) { 58 + throw new Error('Could not find a PDS in the DID document.') 59 + } 60 + return { usersDid, pds } 62 61 } 63 - 64 62 65 63 /** 66 64 * Fetches the DID Web from the .well-known/did.json endpoint of the server. ··· 69 67 * @returns {Promise<*>} 70 68 */ 71 69 async function fetchPDSMooverDIDWeb(baseUrl) { 72 - const response = await fetch(`${baseUrl}/.well-known/did.json`); 73 - if (!response.ok) { 74 - throw new Error(`Failed to fetch DID document: ${response.status}`); 75 - } 76 - const didDoc = await response.json(); 77 - return didDoc.id; 70 + const response = await fetch(`${baseUrl}/.well-known/did.json`) 71 + if (!response.ok) { 72 + throw new Error(`Failed to fetch DID document: ${response.status}`) 73 + } 74 + const didDoc = await response.json() 75 + return didDoc.id 78 76 } 79 77 80 - 81 - export {handleResolver, docResolver, cleanHandle, handleAndPDSResolver, fetchPDSMooverDIDWeb}; 78 + export { handleResolver, docResolver, cleanHandle, handleAndPDSResolver, fetchPDSMooverDIDWeb }
+221 -225
packages/moover/lib/backup.js
··· 1 - import {Client, CredentialManager, ok} from '@atcute/client'; 2 - import {handleAndPDSResolver} from './atprotoUtils.js'; 1 + import { Client, CredentialManager, ok } from '@atcute/client' 2 + import { handleAndPDSResolver } from './atprotoUtils.js' 3 3 //Shows as unused, but is used in the return types 4 - import {ComPdsmooverBackupDescribeServer} from '@pds-moover/lexicons'; 4 + import { ComPdsmooverBackupDescribeServer } from '@pds-moover/lexicons' 5 5 6 6 /** 7 7 * JSDoc type-only import to avoid runtime import errors in the browser. 8 8 * @typedef {import('@atcute/lexicons').InferXRPCBodyOutput} InferXRPCBodyOutput 9 9 */ 10 - 11 10 12 11 /** 13 12 * Logic to sign up and manage backups for pdsmoover.com (or your own selfhosted instance) 14 13 */ 15 14 class BackupService { 15 + /** 16 + * 17 + * @param backupDidWeb {string} - The did:web for the xrpc service for backups, defaults to did:web:pdsmoover.com 18 + */ 19 + constructor(backupDidWeb = 'did:web:pdsmoover.com') { 16 20 /** 17 21 * 18 - * @param backupDidWeb {string} - The did:web for the xrpc service for backups, defaults to did:web:pdsmoover.com 22 + * @type {Client} 19 23 */ 20 - constructor(backupDidWeb = 'did:web:pdsmoover.com') { 21 - /** 22 - * 23 - * @type {Client} 24 - */ 25 - this.atCuteClient = null; 26 - /** 27 - * 28 - * @type {CredentialManager} 29 - */ 30 - this.atCuteCredentialManager = null; 31 - 32 - /** 33 - * The did:web for the xrpc service for backups, defaults to pdsmoover.com 34 - * @type {string} 35 - */ 36 - this.backupDidWeb = backupDidWeb; 37 - } 38 - 39 - 24 + this.atCuteClient = null 40 25 /** 41 - * Logs in and returns the backup status. 42 - * To use the rest of the BackupService, it is assumed that this has ran first, 43 - * and the user has successfully signed up. A successful login is a returned null if the user has not signed up. 44 - * or the backup status if they are 45 26 * 46 - * If the server requires 2FA, 47 - * it will throw with error.error === 'AuthFactorTokenRequired'. 48 - * @param identifier {string} handle or did 49 - * @param password {string} 50 - * @param {function|null} onStatus - a function that takes a string used to update the UI. 51 - * Like (status) => console.log(status) 52 - * @param twoFactorCode {string|null} 53 - * 54 - * @returns {Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>|null>} 27 + * @type {CredentialManager} 55 28 */ 56 - async loginAndStatus(identifier, password, onStatus = null, twoFactorCode = null) { 57 - let {pds} = await handleAndPDSResolver(identifier); 29 + this.atCuteCredentialManager = null 58 30 31 + /** 32 + * The did:web for the xrpc service for backups, defaults to pdsmoover.com 33 + * @type {string} 34 + */ 35 + this.backupDidWeb = backupDidWeb 36 + } 59 37 60 - const manager = new CredentialManager({ 61 - service: pds 62 - }); 38 + /** 39 + * Logs in and returns the backup status. 40 + * To use the rest of the BackupService, it is assumed that this has ran first, 41 + * and the user has successfully signed up. A successful login is a returned null if the user has not signed up. 42 + * or the backup status if they are 43 + * 44 + * If the server requires 2FA, 45 + * it will throw with error.error === 'AuthFactorTokenRequired'. 46 + * @param identifier {string} handle or did 47 + * @param password {string} 48 + * @param {function|null} onStatus - a function that takes a string used to update the UI. 49 + * Like (status) => console.log(status) 50 + * @param twoFactorCode {string|null} 51 + * 52 + * @returns {Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>|null>} 53 + */ 54 + async loginAndStatus(identifier, password, onStatus = null, twoFactorCode = null) { 55 + let { pds } = await handleAndPDSResolver(identifier) 63 56 57 + const manager = new CredentialManager({ 58 + service: pds, 59 + }) 64 60 65 - const rpc = new Client({ 66 - handler: manager, 67 - proxy: { 68 - did: this.backupDidWeb, 69 - serviceId: '#repo_backup' 70 - } 71 - }); 61 + const rpc = new Client({ 62 + handler: manager, 63 + proxy: { 64 + did: this.backupDidWeb, 65 + serviceId: '#repo_backup', 66 + }, 67 + }) 72 68 73 - try { 74 - if (onStatus) onStatus('Signing in…'); 69 + try { 70 + if (onStatus) onStatus('Signing in…') 75 71 76 - let loginInput = { 77 - identifier, 78 - password, 72 + let loginInput = { 73 + identifier, 74 + password, 75 + } 76 + if (twoFactorCode) { 77 + loginInput.code = twoFactorCode 78 + } 79 + await manager.login(loginInput) 79 80 80 - }; 81 - if (twoFactorCode) { 82 - loginInput.code = twoFactorCode; 83 - } 84 - await manager.login(loginInput); 85 - 86 - 87 - // Make the client/manager available regardless of repo status so we can sign up if needed. 88 - this.atCuteClient = rpc; 89 - this.atCuteCredentialManager = manager; 90 - 91 - if (onStatus) onStatus('Checking backup status'); 92 - const result = await rpc.get('com.pdsmoover.backup.getRepoStatus', { 93 - params: { 94 - did: manager.session.did.toString() 95 - } 96 - }); 97 - if (result.ok) { 98 - return result.data; 99 - } else { 100 - switch (result.data.error) { 101 - case 'RepoNotFound': 102 - return null; 103 - default: 104 - throw result.data.error; 105 - } 81 + // Make the client/manager available regardless of repo status so we can sign up if needed. 82 + this.atCuteClient = rpc 83 + this.atCuteCredentialManager = manager 106 84 107 - } 108 - } catch (err) { 109 - throw err; 85 + if (onStatus) onStatus('Checking backup status') 86 + const result = await rpc.get('com.pdsmoover.backup.getRepoStatus', { 87 + params: { 88 + did: manager.session.did.toString(), 89 + }, 90 + }) 91 + if (result.ok) { 92 + return result.data 93 + } else { 94 + switch (result.data.error) { 95 + case 'RepoNotFound': 96 + return null 97 + default: 98 + throw result.data.error 110 99 } 100 + } 101 + } catch (err) { 102 + throw err 111 103 } 104 + } 112 105 113 - /** 114 - * Signs the user up for backups with the service 115 - * @param onStatus 116 - * @returns {Promise<void>} 117 - */ 118 - async signUp(onStatus = null) { 119 - if (!this.atCuteClient || !this.atCuteCredentialManager) { 120 - throw new Error('Not signed in'); 121 - } 122 - if (onStatus) onStatus('Creating backup registration…'); 123 - await ok( 124 - this.atCuteClient.post('com.pdsmoover.backup.signUp', { 125 - as: null, 126 - }) 127 - ); 128 - if (onStatus) onStatus('Backup registration complete'); 129 - //No return if successful 106 + /** 107 + * Signs the user up for backups with the service 108 + * @param onStatus 109 + * @returns {Promise<void>} 110 + */ 111 + async signUp(onStatus = null) { 112 + if (!this.atCuteClient || !this.atCuteCredentialManager) { 113 + throw new Error('Not signed in') 130 114 } 115 + if (onStatus) onStatus('Creating backup registration…') 116 + await ok( 117 + this.atCuteClient.post('com.pdsmoover.backup.signUp', { 118 + as: null, 119 + }), 120 + ) 121 + if (onStatus) onStatus('Backup registration complete') 122 + //No return if successful 123 + } 131 124 132 - /** 133 - * Requests a PLC token to be sent to the user's email, needed to add a new rotation key 134 - * @returns {Promise<void>} 135 - */ 136 - async requestAPlcToken() { 137 - if (!this.atCuteClient || !this.atCuteCredentialManager) { 138 - throw new Error('Not signed in'); 139 - } 140 - const rpc = new Client({ 141 - handler: this.atCuteCredentialManager, 142 - }); 143 - 144 - let response = await rpc.post('com.atproto.identity.requestPlcOperationSignature', { 145 - as: null, 146 - }); 147 - if (!response.ok) { 148 - throw new Error(response.data?.message || 'Failed to request PLC token'); 149 - } 125 + /** 126 + * Requests a PLC token to be sent to the user's email, needed to add a new rotation key 127 + * @returns {Promise<void>} 128 + */ 129 + async requestAPlcToken() { 130 + if (!this.atCuteClient || !this.atCuteCredentialManager) { 131 + throw new Error('Not signed in') 150 132 } 151 - 152 - /** 153 - * Adds a new rotation to the users did document. Assumes you are already signed in. 154 - * 155 - * WARNING: This will overwrite any existing rotation keys with the new one at the top, and the PDS key as the second one 156 - * @param plcToken {string} - PLC token from the user's email that was sent from requestAPlcToken 157 - * @param rotationKey {string} - The new rotation key to add to the user's did document 158 - * @returns {Promise<void>} 159 - */ 160 - async addANewRotationKey(plcToken, rotationKey) { 161 - if (!this.atCuteClient || !this.atCuteCredentialManager) { 162 - throw new Error('Not signed in'); 163 - } 164 - 165 - 166 - const rpc = new Client({ 167 - handler: this.atCuteCredentialManager, 168 - }); 133 + const rpc = new Client({ 134 + handler: this.atCuteCredentialManager, 135 + }) 169 136 170 - let getDidCredentials = await rpc.get('com.atproto.identity.getRecommendedDidCredentials'); 137 + let response = await rpc.post('com.atproto.identity.requestPlcOperationSignature', { 138 + as: null, 139 + }) 140 + if (!response.ok) { 141 + throw new Error(response.data?.message || 'Failed to request PLC token') 142 + } 143 + } 171 144 172 - if (getDidCredentials.ok) { 173 - const pdsProvidedRotationKeys = getDidCredentials.data.rotationKeys ?? []; 174 - const updatedRotationKeys = [rotationKey, ...pdsProvidedRotationKeys]; 145 + /** 146 + * Adds a new rotation to the users did document. Assumes you are already signed in. 147 + * 148 + * WARNING: This will overwrite any existing rotation keys with the new one at the top, and the PDS key as the second one 149 + * @param plcToken {string} - PLC token from the user's email that was sent from requestAPlcToken 150 + * @param rotationKey {string} - The new rotation key to add to the user's did document 151 + * @returns {Promise<void>} 152 + */ 153 + async addANewRotationKey(plcToken, rotationKey) { 154 + if (!this.atCuteClient || !this.atCuteCredentialManager) { 155 + throw new Error('Not signed in') 156 + } 175 157 176 - const credentials = { 177 - ...getDidCredentials.data, 178 - rotationKeys: updatedRotationKeys, 179 - }; 158 + const rpc = new Client({ 159 + handler: this.atCuteCredentialManager, 160 + }) 180 161 181 - const signDocRes = await rpc.post('com.atproto.identity.signPlcOperation', { 182 - input: { 183 - token: plcToken, 184 - ...credentials, 185 - } 186 - }); 162 + let getDidCredentials = await rpc.get('com.atproto.identity.getRecommendedDidCredentials') 187 163 188 - if (signDocRes.ok) { 189 - const submitDocRes = await rpc.post('com.atproto.identity.submitPlcOperation', { 190 - input: signDocRes.data, 191 - as: null, 192 - }); 164 + if (getDidCredentials.ok) { 165 + const pdsProvidedRotationKeys = getDidCredentials.data.rotationKeys ?? [] 166 + const updatedRotationKeys = [rotationKey, ...pdsProvidedRotationKeys] 193 167 194 - if (!submitDocRes.ok) { 195 - throw new Error(submitDocRes.data?.message || 'Failed to submit PLC operation'); 196 - } 168 + const credentials = { 169 + ...getDidCredentials.data, 170 + rotationKeys: updatedRotationKeys, 171 + } 197 172 198 - } else { 199 - throw new Error(signDocRes.data?.message || 'Failed to sign PLC operation'); 200 - } 173 + const signDocRes = await rpc.post('com.atproto.identity.signPlcOperation', { 174 + input: { 175 + token: plcToken, 176 + ...credentials, 177 + }, 178 + }) 201 179 180 + if (signDocRes.ok) { 181 + const submitDocRes = await rpc.post('com.atproto.identity.submitPlcOperation', { 182 + input: signDocRes.data, 183 + as: null, 184 + }) 202 185 203 - } else { 204 - throw new Error(getDidCredentials.data?.message || 'Failed to get status'); 186 + if (!submitDocRes.ok) { 187 + throw new Error(submitDocRes.data?.message || 'Failed to submit PLC operation') 205 188 } 189 + } else { 190 + throw new Error(signDocRes.data?.message || 'Failed to sign PLC operation') 191 + } 192 + } else { 193 + throw new Error(getDidCredentials.data?.message || 'Failed to get status') 206 194 } 195 + } 207 196 197 + /** 198 + * 199 + * Gets the current status of the user's backup repository. 200 + * 201 + * @param onStatus {function|null} - a function that takes a string used to update the UI. 202 + * @returns {Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>>} 203 + */ 204 + async getUsersRepoStatus(onStatus = null) { 205 + if (!this.atCuteClient || !this.atCuteCredentialManager) { 206 + throw new Error('Not signed in') 207 + } 208 + if (onStatus) onStatus('Refreshing backup status…') 209 + const result = await this.atCuteClient.get('com.pdsmoover.backup.getRepoStatus', { 210 + params: { did: this.atCuteCredentialManager.session.did.toString() }, 211 + }) 212 + if (result.ok) { 213 + return result.data 214 + } else { 215 + throw new Error(result.data?.message || 'Failed to get status') 216 + } 217 + } 208 218 209 - /** 210 - * 211 - * Gets the current status of the user's backup repository. 212 - * 213 - * @param onStatus {function|null} - a function that takes a string used to update the UI. 214 - * @returns {Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>>} 215 - */ 216 - async getUsersRepoStatus(onStatus = null) { 217 - if (!this.atCuteClient || !this.atCuteCredentialManager) { 218 - throw new Error('Not signed in'); 219 - } 220 - if (onStatus) onStatus('Refreshing backup status…'); 221 - const result = await this.atCuteClient.get('com.pdsmoover.backup.getRepoStatus', { 222 - params: {did: this.atCuteCredentialManager.session.did.toString()} 223 - }); 224 - if (result.ok) { 225 - return result.data; 226 - } else { 227 - throw new Error(result.data?.message || 'Failed to get status'); 219 + /** 220 + * Requests a backup to be run immediately for the signed-in user. Usually does, depend on the server's backup queue 221 + * @param onStatus 222 + * @returns {Promise<boolean>} 223 + */ 224 + async runBackupNow(onStatus = null) { 225 + if (!this.atCuteClient || !this.atCuteCredentialManager) { 226 + throw new Error('Not signed in') 227 + } 228 + if (onStatus) onStatus('Requesting backup…') 229 + const res = await this.atCuteClient.post('com.pdsmoover.backup.requestBackup', { 230 + as: null, 231 + data: {}, 232 + }) 233 + if (res.ok) { 234 + if (onStatus) onStatus('Backup requested.') 235 + return true 236 + } else { 237 + const err = res.data 238 + if (err?.error === 'Timeout') { 239 + throw { 240 + error: 'Timeout', 241 + message: err?.message || 'Please wait a few minutes before requesting again.', 228 242 } 243 + } 244 + throw new Error(err?.message || 'Failed to request backup') 229 245 } 246 + } 230 247 231 - /** 232 - * Requests a backup to be run immediately for the signed-in user. Usually does, depend on the server's backup queue 233 - * @param onStatus 234 - * @returns {Promise<boolean>} 235 - */ 236 - async runBackupNow(onStatus = null) { 237 - if (!this.atCuteClient || !this.atCuteCredentialManager) { 238 - throw new Error('Not signed in'); 239 - } 240 - if (onStatus) onStatus('Requesting backup…'); 241 - const res = await this.atCuteClient.post('com.pdsmoover.backup.requestBackup', {as: null, data: {}}); 242 - if (res.ok) { 243 - if (onStatus) onStatus('Backup requested.'); 244 - return true; 245 - } else { 246 - const err = res.data; 247 - if (err?.error === 'Timeout') { 248 - throw {error: 'Timeout', message: err?.message || 'Please wait a few minutes before requesting again.'}; 249 - } 250 - throw new Error(err?.message || 'Failed to request backup'); 251 - } 248 + /** 249 + * Remove (delete) the signed-in user's backup repository. this also deletes all the user's backup data. 250 + * @param onStatus 251 + * @returns {Promise<boolean>} 252 + */ 253 + async removeRepo(onStatus = null) { 254 + if (!this.atCuteClient || !this.atCuteCredentialManager) { 255 + throw new Error('Not signed in') 252 256 } 253 - 254 - /** 255 - * Remove (delete) the signed-in user's backup repository. this also deletes all the user's backup data. 256 - * @param onStatus 257 - * @returns {Promise<boolean>} 258 - */ 259 - async removeRepo(onStatus = null) { 260 - if (!this.atCuteClient || !this.atCuteCredentialManager) { 261 - throw new Error('Not signed in'); 262 - } 263 - if (onStatus) onStatus('Deleting backup repository…'); 264 - const res = await this.atCuteClient.post('com.pdsmoover.backup.removeRepo', {as: null, data: {}}); 265 - if (res.ok) { 266 - if (onStatus) onStatus('Backup repository deleted.'); 267 - return true; 268 - } else { 269 - const err = res.data; 270 - throw new Error(err?.message || 'Failed to delete backup repository'); 271 - } 257 + if (onStatus) onStatus('Deleting backup repository…') 258 + const res = await this.atCuteClient.post('com.pdsmoover.backup.removeRepo', { 259 + as: null, 260 + data: {}, 261 + }) 262 + if (res.ok) { 263 + if (onStatus) onStatus('Backup repository deleted.') 264 + return true 265 + } else { 266 + const err = res.data 267 + throw new Error(err?.message || 'Failed to delete backup repository') 272 268 } 269 + } 273 270 } 274 271 275 - 276 - export {BackupService}; 272 + export { BackupService }
+5
packages/moover/lib/lexicons/blue.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * as microcosm from './blue/microcosm.js'
+5
packages/moover/lib/lexicons/blue/microcosm.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * as identity from './microcosm/identity.js'
+5
packages/moover/lib/lexicons/blue/microcosm/identity.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * as resolveMiniDoc from './identity/resolveMiniDoc.js'
+30
packages/moover/lib/lexicons/blue/microcosm/identity/resolveMiniDoc.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from '@atproto/lex' 6 + 7 + const $nsid = 'blue.microcosm.identity.resolveMiniDoc' 8 + 9 + export { $nsid } 10 + 11 + /** Like [com.atproto.identity.resolveIdentity](https://docs.bsky.app/docs/api/com-atproto-identity-resolve-identity) but instead of the full `didDoc` it returns an atproto-relevant subset. */ 12 + const main = l.query( 13 + $nsid, 14 + l.params({ identifier: l.string({ format: 'at-identifier' }) }), 15 + l.jsonPayload({ 16 + did: l.string({ format: 'did' }), 17 + pds: l.string({ format: 'uri' }), 18 + handle: l.string({ format: 'handle' }), 19 + signing_key: l.string(), 20 + }), 21 + ) 22 + export { main } 23 + 24 + export type Params = l.InferMethodParams<typeof main> 25 + export type Output = l.InferMethodOutput<typeof main> 26 + export type OutputBody = l.InferMethodOutputBody<typeof main> 27 + 28 + export const $lxm = main.nsid, 29 + $params = main.parameters, 30 + $output = main.output
+6
packages/moover/lib/lexicons/blue/microcosm/identity/resolveMiniDoc.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from './resolveMiniDoc.defs.js' 6 + export * as $defs from './resolveMiniDoc.defs.js'
+7 -16
packages/moover/lib/main.js
··· 1 - import {Migrator} from './pdsmoover.js'; 2 - import {MissingBlobs} from './missingBlobs.js'; 3 - import {BackupService} from './backup.js'; 4 - import {PlcOps} from './plc-ops.js'; 5 - import {Restore} from './restore.js'; 6 - import {handleAndPDSResolver} from './atprotoUtils.js'; 1 + import { Migrator } from './pdsmoover.js' 2 + import { MissingBlobs } from './missingBlobs.js' 3 + import { BackupService } from './backup.js' 4 + import { PlcOps } from './plc-ops.js' 5 + import { Restore } from './restore.js' 6 + import { handleAndPDSResolver } from './atprotoUtils.js' 7 7 8 - export { 9 - Migrator, 10 - MissingBlobs, 11 - BackupService, 12 - PlcOps, 13 - Restore, 14 - handleAndPDSResolver, 15 - 16 - } 17 - 8 + export { Migrator, MissingBlobs, BackupService, PlcOps, Restore, handleAndPDSResolver }
+162 -166
packages/moover/lib/missingBlobs.js
··· 1 - import {AtpAgent} from '@atproto/api'; 2 - import {handleAndPDSResolver} from './atprotoUtils.js'; 3 - 1 + import { AtpAgent } from '@atproto/api' 2 + import { handleAndPDSResolver } from './atprotoUtils.js' 4 3 5 4 /** 6 5 * Class to help find missing blobs from the did's previous PDS and import them into the current PDS 7 6 */ 8 7 class MissingBlobs { 9 - 10 - constructor() { 11 - /** 12 - * The user's current PDS agent 13 - * @type {AtpAgent} 14 - */ 15 - this.currentPdsAgent = null; 16 - /** 17 - * The user's old PDS agent 18 - * @type {AtpAgent} 19 - */ 20 - this.oldPdsAgent = null; 21 - /** 22 - * the user's did 23 - * @type {string|null} 24 - */ 25 - this.did = null; 26 - /** 27 - * The user's current PDS url 28 - * @type {null} 29 - */ 30 - this.currentPdsUrl = null; 31 - /** 32 - * A list of the missing cids blobs from the old PDS. In this case if a retry upload fails it gets put in this array for the ui 33 - * @type {string[]} 34 - */ 35 - this.missingBlobs = []; 36 - 37 - } 38 - 8 + constructor() { 39 9 /** 40 - * Logs the user into the current PDS and gets the account status 41 - * @param handle {string} 42 - * @param password {string} 43 - * @param twoFactorCode {string|null} 44 - * @returns {Promise<{accountStatus: OutputSchema, missingBlobsCount: number}>} 10 + * The user's current PDS agent 11 + * @type {AtpAgent} 45 12 */ 46 - async currentAgentLogin( 47 - handle, 48 - password, 49 - twoFactorCode = null, 50 - ) { 51 - let {usersDid, pds} = await handleAndPDSResolver(handle); 52 - this.did = usersDid; 53 - this.currentPdsUrl = pds; 54 - const agent = new AtpAgent({ 55 - service: pds, 56 - }); 57 - 58 - if (twoFactorCode === null) { 59 - await agent.login({identifier: usersDid, password}); 60 - } else { 61 - await agent.login({identifier: usersDid, password: password, authFactorToken: twoFactorCode}); 62 - } 13 + this.currentPdsAgent = null 14 + /** 15 + * The user's old PDS agent 16 + * @type {AtpAgent} 17 + */ 18 + this.oldPdsAgent = null 19 + /** 20 + * the user's did 21 + * @type {string|null} 22 + */ 23 + this.did = null 24 + /** 25 + * The user's current PDS url 26 + * @type {null} 27 + */ 28 + this.currentPdsUrl = null 29 + /** 30 + * A list of the missing cids blobs from the old PDS. In this case if a retry upload fails it gets put in this array for the ui 31 + * @type {string[]} 32 + */ 33 + this.missingBlobs = [] 34 + } 63 35 64 - this.currentPdsAgent = agent; 36 + /** 37 + * Logs the user into the current PDS and gets the account status 38 + * @param handle {string} 39 + * @param password {string} 40 + * @param twoFactorCode {string|null} 41 + * @returns {Promise<{accountStatus: OutputSchema, missingBlobsCount: number}>} 42 + */ 43 + async currentAgentLogin(handle, password, twoFactorCode = null) { 44 + let { usersDid, pds } = await handleAndPDSResolver(handle) 45 + this.did = usersDid 46 + this.currentPdsUrl = pds 47 + const agent = new AtpAgent({ 48 + service: pds, 49 + }) 65 50 66 - const result = await agent.com.atproto.server.checkAccountStatus(); 67 - const missingBlobs = await this.currentPdsAgent.com.atproto.repo.listMissingBlobs({ 68 - limit: 10, 69 - }); 70 - return {accountStatus: result.data, missingBlobsCount: missingBlobs.data.blobs.length}; 51 + if (twoFactorCode === null) { 52 + await agent.login({ identifier: usersDid, password }) 53 + } else { 54 + await agent.login({ 55 + identifier: usersDid, 56 + password: password, 57 + authFactorToken: twoFactorCode, 58 + }) 71 59 } 72 60 73 - /** 74 - * Logs into the old PDS and gets the account status. 75 - * Does not need a handle 76 - * since it is assumed the user has already logged in with the current PDS and we are using their did 77 - * @param password {string} 78 - * @param twoFactorCode {string|null} 79 - * @param pdsUrl {string|null} - If you know the url of the old PDS you can pass it in here. If not it will be guessed at from plc ops 80 - * @returns {Promise<void>} 81 - */ 82 - async oldAgentLogin( 83 - password, 84 - twoFactorCode = null, 85 - pdsUrl = null, 86 - ) { 87 - let oldPds = null; 61 + this.currentPdsAgent = agent 88 62 89 - if (pdsUrl === null) { 90 - const response = await fetch(`https://plc.directory/${this.did}/log`); 91 - let auditLog = await response.json(); 92 - auditLog = auditLog.reverse(); 93 - let debugCount = 0; 94 - for (const entry of auditLog) { 95 - console.log(`Loop: ${debugCount++}`); 96 - console.log(entry); 97 - if (entry.services) { 98 - if (entry.services.atproto_pds) { 99 - if (entry.services.atproto_pds.type === 'AtprotoPersonalDataServer') { 100 - const pds = entry.services.atproto_pds.endpoint; 101 - console.log(`Found PDS: ${pds}`); 102 - if (pds.toLowerCase() !== this.currentPdsUrl.toLowerCase()) { 103 - oldPds = pds; 104 - break; 105 - } 106 - } 107 - } 108 - } 109 - } 110 - if (oldPds === null) { 111 - throw new Error('Could not find your old PDS'); 112 - } 113 - } else { 114 - oldPds = pdsUrl; 115 - } 63 + const result = await agent.com.atproto.server.checkAccountStatus() 64 + const missingBlobs = await this.currentPdsAgent.com.atproto.repo.listMissingBlobs({ 65 + limit: 10, 66 + }) 67 + return { accountStatus: result.data, missingBlobsCount: missingBlobs.data.blobs.length } 68 + } 116 69 117 - const agent = new AtpAgent({ 118 - service: oldPds, 119 - }); 70 + /** 71 + * Logs into the old PDS and gets the account status. 72 + * Does not need a handle 73 + * since it is assumed the user has already logged in with the current PDS and we are using their did 74 + * @param password {string} 75 + * @param twoFactorCode {string|null} 76 + * @param pdsUrl {string|null} - If you know the url of the old PDS you can pass it in here. If not it will be guessed at from plc ops 77 + * @returns {Promise<void>} 78 + */ 79 + async oldAgentLogin(password, twoFactorCode = null, pdsUrl = null) { 80 + let oldPds = null 120 81 121 - if (twoFactorCode === null) { 122 - await agent.login({identifier: this.did, password}); 123 - } else { 124 - await agent.login({identifier: this.did, password: password, authFactorToken: twoFactorCode}); 82 + if (pdsUrl === null) { 83 + const response = await fetch(`https://plc.directory/${this.did}/log`) 84 + let auditLog = await response.json() 85 + auditLog = auditLog.reverse() 86 + let debugCount = 0 87 + for (const entry of auditLog) { 88 + console.log(`Loop: ${debugCount++}`) 89 + console.log(entry) 90 + if (entry.services) { 91 + if (entry.services.atproto_pds) { 92 + if (entry.services.atproto_pds.type === 'AtprotoPersonalDataServer') { 93 + const pds = entry.services.atproto_pds.endpoint 94 + console.log(`Found PDS: ${pds}`) 95 + if (pds.toLowerCase() !== this.currentPdsUrl.toLowerCase()) { 96 + oldPds = pds 97 + break 98 + } 99 + } 100 + } 125 101 } 126 - this.oldPdsAgent = agent; 102 + } 103 + if (oldPds === null) { 104 + throw new Error('Could not find your old PDS') 105 + } 106 + } else { 107 + oldPds = pdsUrl 127 108 } 128 109 129 - /** 130 - * Gets the missing blobs from the old PDS and uploads them to the current PDS 131 - * @param statusUpdateHandler {function} - A function to update the status of the migration. This is useful for showing the user the progress of the migration 132 - * @returns {Promise<{accountStatus: OutputSchema, missingBlobsCount: number}>} 133 - */ 134 - async migrateMissingBlobs(statusUpdateHandler) { 135 - if (this.currentPdsAgent === null) { 136 - throw new Error('Current PDS agent is not set'); 137 - } 138 - if (this.oldPdsAgent === null) { 139 - throw new Error('Old PDS agent is not set'); 140 - } 141 - statusUpdateHandler('Starting to import blobs...'); 110 + const agent = new AtpAgent({ 111 + service: oldPds, 112 + }) 142 113 143 - let totalMissingBlobs = 0; 144 - let missingBlobCursor = undefined; 145 - let missingUploadedBlobs = 0; 146 - 147 - do { 148 - 149 - const missingBlobs = await this.currentPdsAgent.com.atproto.repo.listMissingBlobs({ 150 - cursor: missingBlobCursor, 151 - limit: 1000, 152 - }); 153 - totalMissingBlobs += missingBlobs.data.blobs.length; 154 - 155 - for (const recordBlob of missingBlobs.data.blobs) { 156 - try { 114 + if (twoFactorCode === null) { 115 + await agent.login({ identifier: this.did, password }) 116 + } else { 117 + await agent.login({ 118 + identifier: this.did, 119 + password: password, 120 + authFactorToken: twoFactorCode, 121 + }) 122 + } 123 + this.oldPdsAgent = agent 124 + } 157 125 158 - const blobRes = await this.oldPdsAgent.com.atproto.sync.getBlob({ 159 - did: this.did, 160 - cid: recordBlob.cid, 161 - }); 162 - let result = await this.currentPdsAgent.com.atproto.repo.uploadBlob(blobRes.data, { 163 - encoding: blobRes.headers['content-type'], 164 - }); 126 + /** 127 + * Gets the missing blobs from the old PDS and uploads them to the current PDS 128 + * @param statusUpdateHandler {function} - A function to update the status of the migration. This is useful for showing the user the progress of the migration 129 + * @returns {Promise<{accountStatus: OutputSchema, missingBlobsCount: number}>} 130 + */ 131 + async migrateMissingBlobs(statusUpdateHandler) { 132 + if (this.currentPdsAgent === null) { 133 + throw new Error('Current PDS agent is not set') 134 + } 135 + if (this.oldPdsAgent === null) { 136 + throw new Error('Old PDS agent is not set') 137 + } 138 + statusUpdateHandler('Starting to import blobs...') 165 139 166 - if (result.status === 429) { 167 - statusUpdateHandler(`You are being rate limited. Will need to try again later to get the rest of the blobs. Migrated blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`); 168 - } 140 + let totalMissingBlobs = 0 141 + let missingBlobCursor = undefined 142 + let missingUploadedBlobs = 0 169 143 170 - if (missingUploadedBlobs % 2 === 0) { 171 - statusUpdateHandler(`Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs} (The total may increase as we find more)`); 172 - } 173 - missingUploadedBlobs++; 174 - } catch (error) { 175 - console.error(error); 176 - this.missingBlobs.push(recordBlob.cid); 177 - } 178 - } 179 - missingBlobCursor = missingBlobs.data.cursor; 180 - } while (missingBlobCursor); 144 + do { 145 + const missingBlobs = await this.currentPdsAgent.com.atproto.repo.listMissingBlobs({ 146 + cursor: missingBlobCursor, 147 + limit: 1000, 148 + }) 149 + totalMissingBlobs += missingBlobs.data.blobs.length 181 150 182 - const accountStatus = await this.currentPdsAgent.com.atproto.server.checkAccountStatus(); 183 - const missingBlobs = await this.currentPdsAgent.com.atproto.repo.listMissingBlobs({ 184 - limit: 10, 185 - }); 186 - return {accountStatus: accountStatus.data, missingBlobsCount: missingBlobs.data.blobs.length}; 151 + for (const recordBlob of missingBlobs.data.blobs) { 152 + try { 153 + const blobRes = await this.oldPdsAgent.com.atproto.sync.getBlob({ 154 + did: this.did, 155 + cid: recordBlob.cid, 156 + }) 157 + let result = await this.currentPdsAgent.com.atproto.repo.uploadBlob(blobRes.data, { 158 + encoding: blobRes.headers['content-type'], 159 + }) 187 160 161 + if (result.status === 429) { 162 + statusUpdateHandler( 163 + `You are being rate limited. Will need to try again later to get the rest of the blobs. Migrated blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`, 164 + ) 165 + } 188 166 189 - } 167 + if (missingUploadedBlobs % 2 === 0) { 168 + statusUpdateHandler( 169 + `Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs} (The total may increase as we find more)`, 170 + ) 171 + } 172 + missingUploadedBlobs++ 173 + } catch (error) { 174 + console.error(error) 175 + this.missingBlobs.push(recordBlob.cid) 176 + } 177 + } 178 + missingBlobCursor = missingBlobs.data.cursor 179 + } while (missingBlobCursor) 190 180 181 + const accountStatus = await this.currentPdsAgent.com.atproto.server.checkAccountStatus() 182 + const missingBlobs = await this.currentPdsAgent.com.atproto.repo.listMissingBlobs({ 183 + limit: 10, 184 + }) 185 + return { accountStatus: accountStatus.data, missingBlobsCount: missingBlobs.data.blobs.length } 186 + } 191 187 } 192 188 193 - export {MissingBlobs}; 189 + export { MissingBlobs }
+392 -341
packages/moover/lib/pdsmoover.js
··· 1 - import {docResolver, cleanHandle, handleResolver} from './atprotoUtils.js'; 2 - import {AtpAgent} from '@atproto/api'; 3 - 1 + import { docResolver, cleanHandle, handleResolver } from './atprotoUtils.js' 2 + import { AtpAgent } from '@atproto/api' 4 3 5 4 function safeStatusUpdate(statusUpdateHandler, status) { 6 - if (statusUpdateHandler) { 7 - statusUpdateHandler(status); 8 - } 5 + if (statusUpdateHandler) { 6 + statusUpdateHandler(status) 7 + } 9 8 } 10 9 11 10 /** ··· 13 12 * On pdsmoover.com this is the logic for the MOOver 14 13 */ 15 14 class Migrator { 16 - constructor() { 17 - /** @type {AtpAgent} */ 18 - this.oldAgent = null; 19 - /** @type {AtpAgent} */ 20 - this.newAgent = null; 21 - /** @type {[string]} */ 22 - this.missingBlobs = []; 23 - //State for reruns 24 - /** @type {boolean} */ 25 - this.createNewAccount = true; 26 - /** @type {boolean} */ 27 - this.migrateRepo = true; 28 - /** @type {boolean} */ 29 - this.migrateBlobs = true; 30 - /** @type {boolean} */ 31 - this.migrateMissingBlobs = true; 32 - /** @type {boolean} */ 33 - this.migratePrefs = true; 34 - /** @type {boolean} */ 35 - this.migratePlcRecord = true; 36 - } 15 + constructor() { 16 + /** @type {AtpAgent} */ 17 + this.oldAgent = null 18 + /** @type {AtpAgent} */ 19 + this.newAgent = null 20 + /** @type {[string]} */ 21 + this.missingBlobs = [] 22 + //State for reruns 23 + /** @type {boolean} */ 24 + this.createNewAccount = true 25 + /** @type {boolean} */ 26 + this.migrateRepo = true 27 + /** @type {boolean} */ 28 + this.migrateBlobs = true 29 + /** @type {boolean} */ 30 + this.migrateMissingBlobs = true 31 + /** @type {boolean} */ 32 + this.migratePrefs = true 33 + /** @type {boolean} */ 34 + this.migratePlcRecord = true 35 + } 37 36 38 - /** 39 - * This migrator is pretty cut and dry and makes a few assumptions 40 - * 1. You are using the same password between each account 41 - * 2. If this command fails for something like oauth 2fa code it throws an error and expects the same values when ran again. 42 - * 3. You can control which "actions" happen by setting the class variables to false. 43 - * 4. Each instance of the class is assumed to be for a single migration 44 - * @param {string} oldHandle - The handle you use on your old pds, something like alice.bsky.social 45 - * @param {string} password - Your password for your current login. Has to be your real password, no app password. When setting up a new account we reuse it as well for that account 46 - * @param {string} newPdsUrl - The new URL for your pds. Like https://coolnewpds.com 47 - * @param {string} newEmail - The email you want to use on the new pds (can be the same as the previous one as long as it's not already being used on the new pds) 48 - * @param {string} newHandle - The new handle you want, like alice.bsky.social, or if you already have a domain name set as a handle can use it myname.com. 49 - * @param {string|null} inviteCode - The invite code you got from the PDS you are migrating to. If null does not include one 50 - * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. Like (status) => console.log(status) 51 - * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 52 - * @param verificationCode - Optional verification captcha code for account creation if the PDS requires it 53 - */ 54 - async migrate(oldHandle, password, newPdsUrl, newEmail, newHandle, inviteCode, statusUpdateHandler = null, twoFactorCode = null, verificationCode = null) { 55 - //Leaving this logic that either sets the agent to bsky.social, or the PDS since it's what I found worked best for migrations. 56 - // handleAndPDSResolver should be able to handle it, but there have been edge cases and this was what worked best 57 - oldHandle = cleanHandle(oldHandle); 58 - let oldAgent; 59 - let usersDid; 60 - //If it's a bsky handle just go with the entryway and let it sort everything 61 - if (oldHandle.endsWith('.bsky.social')) { 62 - oldAgent = new AtpAgent({service: 'https://bsky.social'}); 63 - const publicAgent = new AtpAgent({service: 'https://public.api.bsky.app'}); 64 - const resolveIdentityFromEntryway = await publicAgent.com.atproto.identity.resolveHandle({handle: oldHandle}); 65 - usersDid = resolveIdentityFromEntryway.data.did; 37 + /** 38 + * This migrator is pretty cut and dry and makes a few assumptions 39 + * 1. You are using the same password between each account 40 + * 2. If this command fails for something like oauth 2fa code it throws an error and expects the same values when ran again. 41 + * 3. You can control which "actions" happen by setting the class variables to false. 42 + * 4. Each instance of the class is assumed to be for a single migration 43 + * @param {string} oldHandle - The handle you use on your old pds, something like alice.bsky.social 44 + * @param {string} password - Your password for your current login. Has to be your real password, no app password. When setting up a new account we reuse it as well for that account 45 + * @param {string} newPdsUrl - The new URL for your pds. Like https://coolnewpds.com 46 + * @param {string} newEmail - The email you want to use on the new pds (can be the same as the previous one as long as it's not already being used on the new pds) 47 + * @param {string} newHandle - The new handle you want, like alice.bsky.social, or if you already have a domain name set as a handle can use it myname.com. 48 + * @param {string|null} inviteCode - The invite code you got from the PDS you are migrating to. If null does not include one 49 + * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. Like (status) => console.log(status) 50 + * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 51 + * @param verificationCode - Optional verification captcha code for account creation if the PDS requires it 52 + */ 53 + async migrate( 54 + oldHandle, 55 + password, 56 + newPdsUrl, 57 + newEmail, 58 + newHandle, 59 + inviteCode, 60 + statusUpdateHandler = null, 61 + twoFactorCode = null, 62 + verificationCode = null, 63 + ) { 64 + //Leaving this logic that either sets the agent to bsky.social, or the PDS since it's what I found worked best for migrations. 65 + // handleAndPDSResolver should be able to handle it, but there have been edge cases and this was what worked best 66 + oldHandle = cleanHandle(oldHandle) 67 + let oldAgent 68 + let usersDid 69 + //If it's a bsky handle just go with the entryway and let it sort everything 70 + if (oldHandle.endsWith('.bsky.social')) { 71 + oldAgent = new AtpAgent({ service: 'https://bsky.social' }) 72 + const publicAgent = new AtpAgent({ 73 + service: 'https://public.api.bsky.app', 74 + }) 75 + const resolveIdentityFromEntryway = await publicAgent.com.atproto.identity.resolveHandle({ 76 + handle: oldHandle, 77 + }) 78 + usersDid = resolveIdentityFromEntryway.data.did 79 + } else { 80 + //Resolves the did and finds the did document for the old PDS 81 + safeStatusUpdate(statusUpdateHandler, 'Resolving old PDS') 82 + usersDid = await handleResolver.resolve(oldHandle) 83 + const didDoc = await docResolver.resolve(usersDid) 84 + safeStatusUpdate( 85 + statusUpdateHandler, 86 + 'Resolving did document and finding your current PDS URL', 87 + ) 66 88 67 - } else { 68 - //Resolves the did and finds the did document for the old PDS 69 - safeStatusUpdate(statusUpdateHandler, 'Resolving old PDS'); 70 - usersDid = await handleResolver.resolve(oldHandle); 71 - const didDoc = await docResolver.resolve(usersDid); 72 - safeStatusUpdate(statusUpdateHandler, 'Resolving did document and finding your current PDS URL'); 89 + let oldPds 90 + try { 91 + oldPds = didDoc.service.filter(s => s.type === 'AtprotoPersonalDataServer')[0] 92 + .serviceEndpoint 93 + } catch (error) { 94 + console.error(error) 95 + throw new Error('Could not find a PDS in the DID document.') 96 + } 73 97 74 - let oldPds; 75 - try { 76 - oldPds = didDoc.service.filter(s => s.type === 'AtprotoPersonalDataServer')[0].serviceEndpoint; 77 - } catch (error) { 78 - console.error(error); 79 - throw new Error('Could not find a PDS in the DID document.'); 80 - } 98 + oldAgent = new AtpAgent({ 99 + service: oldPds, 100 + }) 101 + } 81 102 82 - oldAgent = new AtpAgent({ 83 - service: oldPds, 84 - }); 103 + safeStatusUpdate(statusUpdateHandler, 'Logging you in to the old PDS') 104 + //Login to the old PDS 105 + if (twoFactorCode === null) { 106 + await oldAgent.login({ identifier: oldHandle, password }) 107 + } else { 108 + await oldAgent.login({ 109 + identifier: oldHandle, 110 + password: password, 111 + authFactorToken: twoFactorCode, 112 + }) 113 + } 85 114 86 - } 115 + safeStatusUpdate( 116 + statusUpdateHandler, 117 + 'Checking that the new PDS is an actual PDS (if the url is wrong this takes a while to error out)', 118 + ) 119 + const newAgent = new AtpAgent({ service: newPdsUrl }) 120 + const newHostDesc = await newAgent.com.atproto.server.describeServer() 121 + if (this.createNewAccount) { 122 + const newHostWebDid = newHostDesc.data.did 87 123 88 - safeStatusUpdate(statusUpdateHandler, 'Logging you in to the old PDS'); 89 - //Login to the old PDS 90 - if (twoFactorCode === null) { 91 - await oldAgent.login({identifier: oldHandle, password}); 92 - } else { 93 - await oldAgent.login({identifier: oldHandle, password: password, authFactorToken: twoFactorCode}); 94 - } 124 + safeStatusUpdate(statusUpdateHandler, 'Creating a new account on the new PDS') 95 125 96 - safeStatusUpdate(statusUpdateHandler, 'Checking that the new PDS is an actual PDS (if the url is wrong this takes a while to error out)'); 97 - const newAgent = new AtpAgent({service: newPdsUrl}); 98 - const newHostDesc = await newAgent.com.atproto.server.describeServer(); 99 - if (this.createNewAccount) { 100 - const newHostWebDid = newHostDesc.data.did; 126 + const createAuthResp = await oldAgent.com.atproto.server.getServiceAuth({ 127 + aud: newHostWebDid, 128 + lxm: 'com.atproto.server.createAccount', 129 + }) 130 + const serviceJwt = createAuthResp.data.token 101 131 102 - safeStatusUpdate(statusUpdateHandler, 'Creating a new account on the new PDS'); 132 + let createAccountRequest = { 133 + did: usersDid, 134 + handle: newHandle, 135 + email: newEmail, 136 + password: password, 137 + } 138 + if (inviteCode) { 139 + createAccountRequest.inviteCode = inviteCode 140 + } 141 + if (verificationCode) { 142 + createAccountRequest.verificationCode = verificationCode 143 + } 144 + const createNewAccount = await newAgent.com.atproto.server.createAccount( 145 + createAccountRequest, 146 + { 147 + headers: { authorization: `Bearer ${serviceJwt}` }, 148 + encoding: 'application/json', 149 + }, 150 + ) 103 151 104 - const createAuthResp = await oldAgent.com.atproto.server.getServiceAuth({ 105 - aud: newHostWebDid, 106 - lxm: 'com.atproto.server.createAccount', 107 - }); 108 - const serviceJwt = createAuthResp.data.token; 152 + if (createNewAccount.data.did !== usersDid.toString()) { 153 + throw new Error('Did not create the new account with the same did as the old account') 154 + } 155 + } 156 + safeStatusUpdate(statusUpdateHandler, 'Logging in with the new account') 109 157 110 - let createAccountRequest = { 111 - did: usersDid, 112 - handle: newHandle, 113 - email: newEmail, 114 - password: password, 115 - }; 116 - if (inviteCode) { 117 - createAccountRequest.inviteCode = inviteCode; 118 - } 119 - if (verificationCode) { 120 - createAccountRequest.verificationCode = verificationCode; 121 - } 122 - const createNewAccount = await newAgent.com.atproto.server.createAccount( 123 - createAccountRequest, 124 - { 125 - headers: {authorization: `Bearer ${serviceJwt}`}, 126 - encoding: 'application/json', 127 - }); 158 + await newAgent.login({ 159 + identifier: usersDid, 160 + password: password, 161 + }) 128 162 129 - if (createNewAccount.data.did !== usersDid.toString()) { 130 - throw new Error('Did not create the new account with the same did as the old account'); 131 - } 132 - } 133 - safeStatusUpdate(statusUpdateHandler, 'Logging in with the new account'); 163 + if (this.migrateRepo) { 164 + safeStatusUpdate(statusUpdateHandler, 'Migrating your repo') 165 + const repoRes = await oldAgent.com.atproto.sync.getRepo({ 166 + did: usersDid, 167 + }) 168 + await newAgent.com.atproto.repo.importRepo(repoRes.data, { 169 + encoding: 'application/vnd.ipld.car', 170 + }) 171 + } 134 172 135 - await newAgent.login({ 136 - identifier: usersDid, 137 - password: password, 138 - }); 173 + let newAccountStatus = await newAgent.com.atproto.server.checkAccountStatus() 139 174 140 - if (this.migrateRepo) { 141 - safeStatusUpdate(statusUpdateHandler, 'Migrating your repo'); 142 - const repoRes = await oldAgent.com.atproto.sync.getRepo({did: usersDid}); 143 - await newAgent.com.atproto.repo.importRepo(repoRes.data, { 144 - encoding: 'application/vnd.ipld.car', 145 - }); 146 - } 175 + if (this.migrateBlobs) { 176 + safeStatusUpdate(statusUpdateHandler, 'Migrating your blobs') 147 177 148 - let newAccountStatus = await newAgent.com.atproto.server.checkAccountStatus(); 178 + let blobCursor = undefined 179 + let uploadedBlobs = 0 180 + do { 181 + safeStatusUpdate( 182 + statusUpdateHandler, 183 + `Migrating blobs: ${uploadedBlobs}/${newAccountStatus.data.expectedBlobs}`, 184 + ) 149 185 150 - if (this.migrateBlobs) { 151 - safeStatusUpdate(statusUpdateHandler, 'Migrating your blobs'); 186 + const listedBlobs = await oldAgent.com.atproto.sync.listBlobs({ 187 + did: usersDid, 188 + cursor: blobCursor, 189 + limit: 100, 190 + }) 152 191 153 - let blobCursor = undefined; 154 - let uploadedBlobs = 0; 155 - do { 156 - safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${uploadedBlobs}/${newAccountStatus.data.expectedBlobs}`); 157 - 158 - const listedBlobs = await oldAgent.com.atproto.sync.listBlobs({ 159 - did: usersDid, 160 - cursor: blobCursor, 161 - limit: 100, 162 - }); 163 - 164 - for (const cid of listedBlobs.data.cids) { 165 - try { 166 - const blobRes = await oldAgent.com.atproto.sync.getBlob({ 167 - did: usersDid, 168 - cid, 169 - }); 170 - await newAgent.com.atproto.repo.uploadBlob(blobRes.data, { 171 - encoding: blobRes.headers['content-type'], 172 - }); 173 - uploadedBlobs++; 174 - if (uploadedBlobs % 10 === 0) { 175 - safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${uploadedBlobs}/${newAccountStatus.data.expectedBlobs}`); 176 - } 177 - } catch (error) { 178 - console.error(error); 179 - } 180 - } 181 - blobCursor = listedBlobs.data.cursor; 182 - } while (blobCursor); 192 + for (const cid of listedBlobs.data.cids) { 193 + try { 194 + const blobRes = await oldAgent.com.atproto.sync.getBlob({ 195 + did: usersDid, 196 + cid, 197 + }) 198 + await newAgent.com.atproto.repo.uploadBlob(blobRes.data, { 199 + encoding: blobRes.headers['content-type'], 200 + }) 201 + uploadedBlobs++ 202 + if (uploadedBlobs % 10 === 0) { 203 + safeStatusUpdate( 204 + statusUpdateHandler, 205 + `Migrating blobs: ${uploadedBlobs}/${newAccountStatus.data.expectedBlobs}`, 206 + ) 207 + } 208 + } catch (error) { 209 + console.error(error) 210 + } 183 211 } 212 + blobCursor = listedBlobs.data.cursor 213 + } while (blobCursor) 214 + } 184 215 185 - if (this.migrateMissingBlobs) { 186 - newAccountStatus = await newAgent.com.atproto.server.checkAccountStatus(); 187 - if (newAccountStatus.data.expectedBlobs !== newAccountStatus.data.importedBlobs) { 188 - let totalMissingBlobs = newAccountStatus.data.expectedBlobs - newAccountStatus.data.importedBlobs; 189 - safeStatusUpdate(statusUpdateHandler, 'Looks like there are some missing blobs. Going to try and upload them now.'); 190 - //Probably should be shared between main blob uploader, but eh 191 - let missingBlobCursor = undefined; 192 - let missingUploadedBlobs = 0; 193 - do { 194 - safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`); 216 + if (this.migrateMissingBlobs) { 217 + newAccountStatus = await newAgent.com.atproto.server.checkAccountStatus() 218 + if (newAccountStatus.data.expectedBlobs !== newAccountStatus.data.importedBlobs) { 219 + let totalMissingBlobs = 220 + newAccountStatus.data.expectedBlobs - newAccountStatus.data.importedBlobs 221 + safeStatusUpdate( 222 + statusUpdateHandler, 223 + 'Looks like there are some missing blobs. Going to try and upload them now.', 224 + ) 225 + //Probably should be shared between main blob uploader, but eh 226 + let missingBlobCursor = undefined 227 + let missingUploadedBlobs = 0 228 + do { 229 + safeStatusUpdate( 230 + statusUpdateHandler, 231 + `Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`, 232 + ) 195 233 196 - const missingBlobs = await newAgent.com.atproto.repo.listMissingBlobs({ 197 - cursor: missingBlobCursor, 198 - limit: 100, 199 - }); 200 - 201 - for (const recordBlob of missingBlobs.data.blobs) { 202 - try { 203 - 204 - const blobRes = await oldAgent.com.atproto.sync.getBlob({ 205 - did: usersDid, 206 - cid: recordBlob.cid, 207 - }); 208 - await newAgent.com.atproto.repo.uploadBlob(blobRes.data, { 209 - encoding: blobRes.headers['content-type'], 210 - }); 211 - if (missingUploadedBlobs % 10 === 0) { 212 - safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`); 213 - } 214 - missingUploadedBlobs++; 215 - } catch (error) { 216 - //TODO silently logging prob should list them so user can manually download 217 - console.error(error); 218 - this.missingBlobs.push(recordBlob.cid); 219 - } 220 - } 221 - missingBlobCursor = missingBlobs.data.cursor; 222 - } while (missingBlobCursor); 234 + const missingBlobs = await newAgent.com.atproto.repo.listMissingBlobs({ 235 + cursor: missingBlobCursor, 236 + limit: 100, 237 + }) 223 238 239 + for (const recordBlob of missingBlobs.data.blobs) { 240 + try { 241 + const blobRes = await oldAgent.com.atproto.sync.getBlob({ 242 + did: usersDid, 243 + cid: recordBlob.cid, 244 + }) 245 + await newAgent.com.atproto.repo.uploadBlob(blobRes.data, { 246 + encoding: blobRes.headers['content-type'], 247 + }) 248 + if (missingUploadedBlobs % 10 === 0) { 249 + safeStatusUpdate( 250 + statusUpdateHandler, 251 + `Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`, 252 + ) 253 + } 254 + missingUploadedBlobs++ 255 + } catch (error) { 256 + //TODO silently logging prob should list them so user can manually download 257 + console.error(error) 258 + this.missingBlobs.push(recordBlob.cid) 224 259 } 225 - } 226 - if (this.migratePrefs) { 227 - const prefs = await oldAgent.app.bsky.actor.getPreferences(); 228 - await newAgent.app.bsky.actor.putPreferences(prefs.data); 229 - } 260 + } 261 + missingBlobCursor = missingBlobs.data.cursor 262 + } while (missingBlobCursor) 263 + } 264 + } 265 + if (this.migratePrefs) { 266 + const prefs = await oldAgent.app.bsky.actor.getPreferences() 267 + await newAgent.app.bsky.actor.putPreferences(prefs.data) 268 + } 230 269 231 - this.oldAgent = oldAgent; 232 - this.newAgent = newAgent; 270 + this.oldAgent = oldAgent 271 + this.newAgent = newAgent 233 272 234 - if (this.migratePlcRecord) { 235 - await oldAgent.com.atproto.identity.requestPlcOperationSignature(); 236 - safeStatusUpdate(statusUpdateHandler, 'Please check your email for a PLC token'); 237 - } 273 + if (this.migratePlcRecord) { 274 + await oldAgent.com.atproto.identity.requestPlcOperationSignature() 275 + safeStatusUpdate(statusUpdateHandler, 'Please check your email for a PLC token') 238 276 } 277 + } 239 278 240 - /** 241 - * Sign and submits the PLC operation to officially migrate the account 242 - * @param {string} token - the PLC token sent in the email. If you're just wanting to run this rerun migrate with all the flags set as false except for migratePlcRecord 243 - * @param additionalRotationKeysToAdd {string[]} - additional rotation keys to add in addition to the ones provided by the new PDS. 244 - * @returns {Promise<void>} 245 - */ 246 - async signPlcOperation(token, additionalRotationKeysToAdd = []) { 247 - const getDidCredentials = 248 - await this.newAgent.com.atproto.identity.getRecommendedDidCredentials(); 249 - const pdsProvidedRotationKeys = getDidCredentials.data.rotationKeys ?? []; 250 - // Prepend any additional rotation keys (e.g., user-added keys, newly created key) so they appear above the new PDS rotation key 251 - const rotationKeys = [...(additionalRotationKeysToAdd || []), ...pdsProvidedRotationKeys]; 252 - if (!rotationKeys) { 253 - throw new Error('No rotation key provided from the new PDS'); 254 - } 255 - const credentials = { 256 - ...getDidCredentials.data, 257 - rotationKeys: rotationKeys, 258 - }; 279 + /** 280 + * Sign and submits the PLC operation to officially migrate the account 281 + * @param {string} token - the PLC token sent in the email. If you're just wanting to run this rerun migrate with all the flags set as false except for migratePlcRecord 282 + * @param additionalRotationKeysToAdd {string[]} - additional rotation keys to add in addition to the ones provided by the new PDS. 283 + * @returns {Promise<void>} 284 + */ 285 + async signPlcOperation(token, additionalRotationKeysToAdd = []) { 286 + const getDidCredentials = 287 + await this.newAgent.com.atproto.identity.getRecommendedDidCredentials() 288 + const pdsProvidedRotationKeys = getDidCredentials.data.rotationKeys ?? [] 289 + // Prepend any additional rotation keys (e.g., user-added keys, newly created key) so they appear above the new PDS rotation key 290 + const rotationKeys = [...(additionalRotationKeysToAdd || []), ...pdsProvidedRotationKeys] 291 + if (!rotationKeys) { 292 + throw new Error('No rotation key provided from the new PDS') 293 + } 294 + const credentials = { 295 + ...getDidCredentials.data, 296 + rotationKeys: rotationKeys, 297 + } 259 298 299 + const plcOp = await this.oldAgent.com.atproto.identity.signPlcOperation({ 300 + token: token, 301 + ...credentials, 302 + }) 260 303 261 - const plcOp = await this.oldAgent.com.atproto.identity.signPlcOperation({ 262 - token: token, 263 - ...credentials, 264 - }); 304 + await this.newAgent.com.atproto.identity.submitPlcOperation({ 305 + operation: plcOp.data.operation, 306 + }) 265 307 266 - await this.newAgent.com.atproto.identity.submitPlcOperation({ 267 - operation: plcOp.data.operation, 268 - }); 308 + await this.newAgent.com.atproto.server.activateAccount() 309 + await this.oldAgent.com.atproto.server.deactivateAccount({}) 310 + } 269 311 270 - await this.newAgent.com.atproto.server.activateAccount(); 271 - await this.oldAgent.com.atproto.server.deactivateAccount({}); 312 + /** 313 + * Using this method assumes the Migrator class was constructed new and this was called. 314 + * Find the user's previous PDS from the PLC op logs, 315 + * logs in and deactivates their old account if it was found still active. 316 + * 317 + * @param oldHandle {string} 318 + * @param oldPassword {string} 319 + * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. 320 + * Like (status) => console.log(status) 321 + * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 322 + * @returns {Promise<void>} 323 + */ 324 + async deactivateOldAccount( 325 + oldHandle, 326 + oldPassword, 327 + statusUpdateHandler = null, 328 + twoFactorCode = null, 329 + ) { 330 + //Leaving this logic that either sets the agent to bsky.social, or the PDS since it's what I found worked best for migrations. 331 + // handleAndPDSResolver should be able to handle it, but there have been edge cases and this was what worked best oldHandle = cleanHandle(oldHandle); 332 + let usersDid 333 + //If it's a bsky handle just go with the entryway and let it sort everything 334 + if (oldHandle.endsWith('.bsky.social')) { 335 + const publicAgent = new AtpAgent({ 336 + service: 'https://public.api.bsky.app', 337 + }) 338 + const resolveIdentityFromEntryway = await publicAgent.com.atproto.identity.resolveHandle({ 339 + handle: oldHandle, 340 + }) 341 + usersDid = resolveIdentityFromEntryway.data.did 342 + } else { 343 + //Resolves the did and finds the did document for the old PDS 344 + safeStatusUpdate(statusUpdateHandler, 'Resolving did from handle') 345 + usersDid = await handleResolver.resolve(oldHandle) 272 346 } 273 347 274 - /** 275 - * Using this method assumes the Migrator class was constructed new and this was called. 276 - * Find the user's previous PDS from the PLC op logs, 277 - * logs in and deactivates their old account if it was found still active. 278 - * 279 - * @param oldHandle {string} 280 - * @param oldPassword {string} 281 - * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. 282 - * Like (status) => console.log(status) 283 - * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 284 - * @returns {Promise<void>} 285 - */ 286 - async deactivateOldAccount(oldHandle, oldPassword, statusUpdateHandler = null, twoFactorCode = null) { 287 - //Leaving this logic that either sets the agent to bsky.social, or the PDS since it's what I found worked best for migrations. 288 - // handleAndPDSResolver should be able to handle it, but there have been edge cases and this was what worked best oldHandle = cleanHandle(oldHandle); 289 - let usersDid; 290 - //If it's a bsky handle just go with the entryway and let it sort everything 291 - if (oldHandle.endsWith('.bsky.social')) { 292 - const publicAgent = new AtpAgent({service: 'https://public.api.bsky.app'}); 293 - const resolveIdentityFromEntryway = await publicAgent.com.atproto.identity.resolveHandle({handle: oldHandle}); 294 - usersDid = resolveIdentityFromEntryway.data.did; 295 - } else { 296 - //Resolves the did and finds the did document for the old PDS 297 - safeStatusUpdate(statusUpdateHandler, 'Resolving did from handle'); 298 - usersDid = await handleResolver.resolve(oldHandle); 299 - } 348 + const didDoc = await docResolver.resolve(usersDid) 349 + let currentPds 350 + try { 351 + currentPds = didDoc.service.filter(s => s.type === 'AtprotoPersonalDataServer')[0] 352 + .serviceEndpoint 353 + } catch (error) { 354 + console.error(error) 355 + throw new Error('Could not find a PDS in the DID document.') 356 + } 300 357 301 - const didDoc = await docResolver.resolve(usersDid); 302 - let currentPds; 303 - try { 304 - currentPds = didDoc.service.filter(s => s.type === 'AtprotoPersonalDataServer')[0].serviceEndpoint; 305 - } catch (error) { 306 - console.error(error); 307 - throw new Error('Could not find a PDS in the DID document.'); 358 + const plcLogRequest = await fetch(`https://plc.directory/${usersDid}/log`) 359 + const plcLog = await plcLogRequest.json() 360 + let pdsBeforeCurrent = '' 361 + for (const log of plcLog) { 362 + try { 363 + const pds = log.services.atproto_pds.endpoint 364 + if (pds.toLowerCase() === currentPds.toLowerCase()) { 365 + console.log('Found the PDS before the current one') 366 + break 308 367 } 368 + pdsBeforeCurrent = pds 369 + } catch (e) { 370 + console.log(e) 371 + } 372 + } 373 + if (pdsBeforeCurrent === '') { 374 + throw new Error('Could not find the PDS before the current one') 375 + } 309 376 310 - const plcLogRequest = await fetch(`https://plc.directory/${usersDid}/log`); 311 - const plcLog = await plcLogRequest.json(); 312 - let pdsBeforeCurrent = ''; 313 - for (const log of plcLog) { 314 - try { 315 - const pds = log.services.atproto_pds.endpoint; 316 - if (pds.toLowerCase() === currentPds.toLowerCase()) { 317 - console.log('Found the PDS before the current one'); 318 - break; 319 - } 320 - pdsBeforeCurrent = pds; 321 - } catch (e) { 322 - console.log(e); 323 - } 324 - } 325 - if (pdsBeforeCurrent === '') { 326 - throw new Error('Could not find the PDS before the current one'); 327 - } 377 + let oldAgent = new AtpAgent({ service: pdsBeforeCurrent }) 378 + safeStatusUpdate(statusUpdateHandler, `Logging you in to the old PDS: ${pdsBeforeCurrent}`) 379 + //Login to the old PDS 380 + if (twoFactorCode === null) { 381 + await oldAgent.login({ identifier: oldHandle, password: oldPassword }) 382 + } else { 383 + await oldAgent.login({ 384 + identifier: oldHandle, 385 + password: oldPassword, 386 + authFactorToken: twoFactorCode, 387 + }) 388 + } 389 + safeStatusUpdate(statusUpdateHandler, "Checking this isn't your current PDS") 390 + if (pdsBeforeCurrent === currentPds) { 391 + throw new Error('This is your current PDS. Login to your old account username and password') 392 + } 328 393 329 - let oldAgent = new AtpAgent({service: pdsBeforeCurrent}); 330 - safeStatusUpdate(statusUpdateHandler, `Logging you in to the old PDS: ${pdsBeforeCurrent}`); 331 - //Login to the old PDS 332 - if (twoFactorCode === null) { 333 - await oldAgent.login({identifier: oldHandle, password: oldPassword}); 334 - } else { 335 - await oldAgent.login({identifier: oldHandle, password: oldPassword, authFactorToken: twoFactorCode}); 336 - } 337 - safeStatusUpdate(statusUpdateHandler, 'Checking this isn\'t your current PDS'); 338 - if (pdsBeforeCurrent === currentPds) { 339 - throw new Error('This is your current PDS. Login to your old account username and password'); 340 - } 341 - 342 - let currentAccountStatus = await oldAgent.com.atproto.server.checkAccountStatus(); 343 - if (!currentAccountStatus.data.activated) { 344 - safeStatusUpdate(statusUpdateHandler, 'All good. Your old account is not activated.'); 345 - } 346 - safeStatusUpdate(statusUpdateHandler, 'Deactivating your OLD account'); 347 - await oldAgent.com.atproto.server.deactivateAccount({}); 348 - safeStatusUpdate(statusUpdateHandler, 'Successfully deactivated your OLD account'); 394 + let currentAccountStatus = await oldAgent.com.atproto.server.checkAccountStatus() 395 + if (!currentAccountStatus.data.activated) { 396 + safeStatusUpdate(statusUpdateHandler, 'All good. Your old account is not activated.') 349 397 } 398 + safeStatusUpdate(statusUpdateHandler, 'Deactivating your OLD account') 399 + await oldAgent.com.atproto.server.deactivateAccount({}) 400 + safeStatusUpdate(statusUpdateHandler, 'Successfully deactivated your OLD account') 401 + } 350 402 351 - /** 352 - * Signs the logged-in user in this.newAgent for backups with PDS MOOver. This is usually called after migrate and signPlcOperation are successful 353 - * 354 - * @param {string} didWeb 355 - * @returns {Promise<void>} 356 - */ 357 - async signUpForBackupsFromMigration(didWeb = 'did:web:pdsmoover.com') { 403 + /** 404 + * Signs the logged-in user in this.newAgent for backups with PDS MOOver. This is usually called after migrate and signPlcOperation are successful 405 + * 406 + * @param {string} didWeb 407 + * @returns {Promise<void>} 408 + */ 409 + async signUpForBackupsFromMigration(didWeb = 'did:web:pdsmoover.com') { 410 + //Manually grabbing the jwt and making a call with fetch cause for the life of me I could not figure out 411 + //how you used @atproto/api to make a call for proxying 412 + const url = `${this.newAgent.serviceUrl.origin}/xrpc/com.pdsmoover.backup.signUp` 358 413 359 - //Manually grabbing the jwt and making a call with fetch cause for the life of me I could not figure out 360 - //how you used @atproto/api to make a call for proxying 361 - const url = `${this.newAgent.serviceUrl.origin}/xrpc/com.pdsmoover.backup.signUp`; 414 + const accessJwt = this.newAgent?.session?.accessJwt 415 + if (!accessJwt) { 416 + throw new Error('Missing access token for authorization') 417 + } 362 418 363 - const accessJwt = this.newAgent?.session?.accessJwt; 364 - if (!accessJwt) { 365 - throw new Error('Missing access token for authorization'); 366 - } 419 + const res = await fetch(url, { 420 + method: 'POST', 421 + headers: { 422 + 'Authorization': `Bearer ${accessJwt}`, 423 + 'Content-Type': 'application/json', 424 + 'Accept': 'application/json', 425 + 'atproto-proxy': `${didWeb}#repo_backup`, 426 + }, 427 + body: JSON.stringify({}), 428 + }) 367 429 368 - const res = await fetch(url, { 369 - method: 'POST', 370 - headers: { 371 - 'Authorization': `Bearer ${accessJwt}`, 372 - 'Content-Type': 'application/json', 373 - 'Accept': 'application/json', 374 - 'atproto-proxy': `${didWeb}#repo_backup`, 375 - }, 376 - body: JSON.stringify({}), 377 - }); 430 + if (!res.ok) { 431 + let bodyText = '' 432 + try { 433 + bodyText = await res.text() 434 + } catch {} 435 + throw new Error( 436 + `Backup signup failed: ${res.status} ${res.statusText}${bodyText ? ` - ${bodyText}` : ''}`, 437 + ) 438 + } 378 439 379 - if (!res.ok) { 380 - let bodyText = ''; 381 - try { 382 - bodyText = await res.text(); 383 - } catch { 384 - } 385 - throw new Error(`Backup signup failed: ${res.status} ${res.statusText}${bodyText ? ` - ${bodyText}` : ''}`); 386 - } 387 - 388 - //No return the success is all that is needed, if there's an error it will throw 389 - } 440 + //No return the success is all that is needed, if there's an error it will throw 441 + } 390 442 } 391 443 392 - export {Migrator}; 393 - 444 + export { Migrator }
+242 -239
packages/moover/lib/plc-ops.js
··· 8 8 * @typedef {import('@atcute/did-plc').IndexedEntry} IndexedEntry 9 9 */ 10 10 11 - import {defs, normalizeOp} from '@atcute/did-plc'; 12 - import {P256PrivateKey, parsePrivateMultikey, Secp256k1PrivateKey, Secp256k1PrivateKeyExportable} from '@atcute/crypto'; 13 - import * as CBOR from '@atcute/cbor'; 14 - import {fromBase16, toBase64Url} from '@atcute/multibase'; 15 - 11 + import { defs, normalizeOp } from '@atcute/did-plc' 12 + import { 13 + P256PrivateKey, 14 + parsePrivateMultikey, 15 + Secp256k1PrivateKey, 16 + Secp256k1PrivateKeyExportable, 17 + } from '@atcute/crypto' 18 + import * as CBOR from '@atcute/cbor' 19 + import { fromBase16, toBase64Url } from '@atcute/multibase' 16 20 17 21 // Helper to base64url-encode JSON 18 - const jsonToB64Url = (obj) => { 19 - const enc = new TextEncoder(); 20 - const json = JSON.stringify(obj); 21 - return toBase64Url(enc.encode(json)); 22 - }; 22 + const jsonToB64Url = obj => { 23 + const enc = new TextEncoder() 24 + const json = JSON.stringify(obj) 25 + return toBase64Url(enc.encode(json)) 26 + } 23 27 24 28 /** 25 29 * Class to help with various PLC operations 26 30 */ 27 31 class PlcOps { 32 + /** 33 + * 34 + * @param plcDirectoryUrl {string} - The url of the plc directory, defaults to https://plc.directory 35 + */ 36 + constructor(plcDirectoryUrl = 'https://plc.directory') { 28 37 /** 29 - * 30 - * @param plcDirectoryUrl {string} - The url of the plc directory, defaults to https://plc.directory 38 + * The url of the plc directory 39 + * @type {string} 31 40 */ 32 - constructor(plcDirectoryUrl = 'https://plc.directory') { 33 - /** 34 - * The url of the plc directory 35 - * @type {string} 36 - */ 37 - this.plcDirectoryUrl = plcDirectoryUrl; 38 - } 41 + this.plcDirectoryUrl = plcDirectoryUrl 42 + } 39 43 40 - /** 41 - * Gets the current rotation keys for a user via their last PlC operation 42 - * @param did 43 - * @returns {Promise<string[]>} 44 - */ 45 - async getCurrentRotationKeysForUser(did) { 46 - const logs = await this.getPlcAuditLogs(did); 47 - const {lastOperation} = this.getLastPlcOp(logs); 48 - return lastOperation.rotationKeys || []; 49 - } 44 + /** 45 + * Gets the current rotation keys for a user via their last PlC operation 46 + * @param did 47 + * @returns {Promise<string[]>} 48 + */ 49 + async getCurrentRotationKeysForUser(did) { 50 + const logs = await this.getPlcAuditLogs(did) 51 + const { lastOperation } = this.getLastPlcOp(logs) 52 + return lastOperation.rotationKeys || [] 53 + } 54 + 55 + /** 56 + * Gets the last PlC operation for a user from the plc directory 57 + * @param did 58 + * @returns {Promise<{lastOperation: Operation, base: any}>} 59 + */ 60 + async getLastPlcOpFromPlc(did) { 61 + const logs = await this.getPlcAuditLogs(did) 62 + return this.getLastPlcOp(logs) 63 + } 50 64 51 - /** 52 - * Gets the last PlC operation for a user from the plc directory 53 - * @param did 54 - * @returns {Promise<{lastOperation: Operation, base: any}>} 55 - */ 56 - async getLastPlcOpFromPlc(did) { 57 - const logs = await this.getPlcAuditLogs(did); 58 - return this.getLastPlcOp(logs); 59 - } 65 + /** 66 + * 67 + * @param logs {IndexedEntryLog} 68 + * @returns {{lastOperation: Operation, base: IndexedEntry}} 69 + */ 70 + getLastPlcOp(logs) { 71 + const lastOp = logs.at(-1) 72 + return { lastOperation: normalizeOp(lastOp.operation), base: lastOp } 73 + } 60 74 61 - /** 62 - * 63 - * @param logs {IndexedEntryLog} 64 - * @returns {{lastOperation: Operation, base: IndexedEntry}} 65 - */ 66 - getLastPlcOp(logs) { 67 - const lastOp = logs.at(-1); 68 - return {lastOperation: normalizeOp(lastOp.operation), base: lastOp}; 75 + /** 76 + * Gets the plc audit logs for a user from the plc directory 77 + * @param did 78 + * @returns {Promise<IndexedEntryLog>} 79 + */ 80 + async getPlcAuditLogs(did) { 81 + const response = await fetch(`${this.plcDirectoryUrl}/${did}/log/audit`) 82 + if (!response.ok) { 83 + throw new Error(`got response ${response.status}`) 69 84 } 70 85 86 + const json = await response.json() 87 + return defs.indexedEntryLog.parse(json) 88 + } 71 89 72 - /** 73 - * Gets the plc audit logs for a user from the plc directory 74 - * @param did 75 - * @returns {Promise<IndexedEntryLog>} 76 - */ 77 - async getPlcAuditLogs(did) { 78 - const response = await fetch(`${this.plcDirectoryUrl}/${did}/log/audit`); 79 - if (!response.ok) { 80 - throw new Error(`got response ${response.status}`); 81 - } 82 - 83 - const json = await response.json(); 84 - return defs.indexedEntryLog.parse(json); 90 + /** 91 + * Creates a new secp256k1 key that can be used for either rotation or verification key 92 + * @returns {Promise<{privateKey: string, publicKey: `did:key:${string}`}>} 93 + */ 94 + async createANewSecp256k1() { 95 + let keypair = await Secp256k1PrivateKeyExportable.createKeypair() 96 + let publicKey = await keypair.exportPublicKey('did') 97 + let privateKey = await keypair.exportPrivateKey('multikey') 98 + return { 99 + privateKey, 100 + publicKey, 85 101 } 102 + } 86 103 87 - /** 88 - * Creates a new secp256k1 key that can be used for either rotation or verification key 89 - * @returns {Promise<{privateKey: string, publicKey: `did:key:${string}`}>} 90 - */ 91 - async createANewSecp256k1() { 92 - let keypair = await Secp256k1PrivateKeyExportable.createKeypair(); 93 - let publicKey = await keypair.exportPublicKey('did'); 94 - let privateKey = await keypair.exportPrivateKey('multikey'); 95 - return { 96 - privateKey, 97 - publicKey 98 - }; 104 + /** 105 + * Signs a new operation with the provided signing key, and information and submits it to the plc directory 106 + * @param did {string} - The user's did 107 + * @param signingRotationKey { P256PrivateKey|Secp256k1PrivateKey} - The keypair to sign the op with 108 + * @param alsoKnownAs {string[]} 109 + * @param rotationKeys {string[]} 110 + * @param pds {string} 111 + * @param verificationKey {string} - The public verification key 112 + * @param prev {string} - The previous valid operation's cid. 113 + * @returns {Promise<void>} 114 + */ 115 + async signAndPublishNewOp( 116 + did, 117 + signingRotationKey, 118 + alsoKnownAs, 119 + rotationKeys, 120 + pds, 121 + verificationKey, 122 + prev, 123 + ) { 124 + const rotationKeysToUse = [...new Set(rotationKeys)] 125 + if (!rotationKeysToUse) { 126 + throw new Error('No rotation keys were found to be added to the PLC') 99 127 } 100 128 101 - 102 - /** 103 - * Signs a new operation with the provided signing key, and information and submits it to the plc directory 104 - * @param did {string} - The user's did 105 - * @param signingRotationKey { P256PrivateKey|Secp256k1PrivateKey} - The keypair to sign the op with 106 - * @param alsoKnownAs {string[]} 107 - * @param rotationKeys {string[]} 108 - * @param pds {string} 109 - * @param verificationKey {string} - The public verification key 110 - * @param prev {string} - The previous valid operation's cid. 111 - * @returns {Promise<void>} 112 - */ 113 - async signAndPublishNewOp(did, signingRotationKey, alsoKnownAs, rotationKeys, pds, verificationKey, prev) { 114 - 115 - const rotationKeysToUse = [...new Set(rotationKeys)]; 116 - if (!rotationKeysToUse) { 117 - throw new Error('No rotation keys were found to be added to the PLC'); 118 - } 119 - 120 - if (rotationKeysToUse.length > 5) { 121 - throw new Error('You can only add up to 5 rotation keys to the PLC'); 122 - } 123 - 124 - const operation = { 125 - type: 'plc_operation', 126 - prev, 127 - alsoKnownAs, 128 - rotationKeys: rotationKeysToUse, 129 - services: { 130 - atproto_pds: { 131 - type: 'AtprotoPersonalDataServer', 132 - endpoint: pds 133 - } 134 - }, 135 - verificationMethods: { 136 - atproto: verificationKey 137 - } 138 - }; 139 - const opBytes = CBOR.encode(operation); 140 - const sigBytes = await signingRotationKey.sign(opBytes); 129 + if (rotationKeysToUse.length > 5) { 130 + throw new Error('You can only add up to 5 rotation keys to the PLC') 131 + } 141 132 142 - const signature = toBase64Url(sigBytes); 133 + const operation = { 134 + type: 'plc_operation', 135 + prev, 136 + alsoKnownAs, 137 + rotationKeys: rotationKeysToUse, 138 + services: { 139 + atproto_pds: { 140 + type: 'AtprotoPersonalDataServer', 141 + endpoint: pds, 142 + }, 143 + }, 144 + verificationMethods: { 145 + atproto: verificationKey, 146 + }, 147 + } 148 + const opBytes = CBOR.encode(operation) 149 + const sigBytes = await signingRotationKey.sign(opBytes) 143 150 144 - const signedOperation = { 145 - ...operation, 146 - sig: signature, 147 - }; 151 + const signature = toBase64Url(sigBytes) 148 152 149 - await this.pushPlcOperation(did, signedOperation); 153 + const signedOperation = { 154 + ...operation, 155 + sig: signature, 150 156 } 151 157 152 - /** 153 - * Takes a multi or hex based private key and returns a keypair 154 - * @param privateKeyString {string} 155 - * @param type {string} - secp256k1 or p256, needed if the private key is hex based, can be assumed if it's a multikey 156 - * @returns {Promise<{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}>} 157 - */ 158 - async getKeyPair(privateKeyString, type = 'secp256k1') { 159 - const HEX_REGEX = /^[0-9a-f]+$/i; 160 - const MULTIKEY_REGEX = /^z[a-km-zA-HJ-NP-Z1-9]+$/; 161 - let keypair = undefined; 158 + await this.pushPlcOperation(did, signedOperation) 159 + } 162 160 163 - if (HEX_REGEX.test(privateKeyString)) { 164 - const privateKeyBytes = fromBase16(privateKeyString); 161 + /** 162 + * Takes a multi or hex based private key and returns a keypair 163 + * @param privateKeyString {string} 164 + * @param type {string} - secp256k1 or p256, needed if the private key is hex based, can be assumed if it's a multikey 165 + * @returns {Promise<{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}>} 166 + */ 167 + async getKeyPair(privateKeyString, type = 'secp256k1') { 168 + const HEX_REGEX = /^[0-9a-f]+$/i 169 + const MULTIKEY_REGEX = /^z[a-km-zA-HJ-NP-Z1-9]+$/ 170 + let keypair = undefined 165 171 166 - switch (type) { 167 - case 'p256': { 168 - keypair = await P256PrivateKey.importRaw(privateKeyBytes); 169 - break; 170 - } 171 - case 'secp256k1': { 172 - keypair = await Secp256k1PrivateKey.importRaw(privateKeyBytes); 173 - break; 174 - } 175 - default: { 176 - throw new Error(`unsupported "${type}" type`); 177 - } 178 - } 179 - } else if (MULTIKEY_REGEX.test(privateKeyString)) { 172 + if (HEX_REGEX.test(privateKeyString)) { 173 + const privateKeyBytes = fromBase16(privateKeyString) 180 174 181 - const match = parsePrivateMultikey(privateKeyString); 182 - const privateKeyBytes = match.privateKeyBytes; 175 + switch (type) { 176 + case 'p256': { 177 + keypair = await P256PrivateKey.importRaw(privateKeyBytes) 178 + break 179 + } 180 + case 'secp256k1': { 181 + keypair = await Secp256k1PrivateKey.importRaw(privateKeyBytes) 182 + break 183 + } 184 + default: { 185 + throw new Error(`unsupported "${type}" type`) 186 + } 187 + } 188 + } else if (MULTIKEY_REGEX.test(privateKeyString)) { 189 + const match = parsePrivateMultikey(privateKeyString) 190 + const privateKeyBytes = match.privateKeyBytes 183 191 184 - switch (match.type) { 185 - case 'p256': { 186 - keypair = await P256PrivateKey.importRaw(privateKeyBytes); 187 - console.log(keypair); 188 - break; 189 - } 190 - case 'secp256k1': { 191 - keypair = await Secp256k1PrivateKey.importRaw(privateKeyBytes); 192 - break; 193 - } 194 - default: { 195 - throw new Error(`unsupported "${type}" type`); 196 - } 197 - } 198 - } else { 199 - throw new Error('unknown input format'); 192 + switch (match.type) { 193 + case 'p256': { 194 + keypair = await P256PrivateKey.importRaw(privateKeyBytes) 195 + console.log(keypair) 196 + break 200 197 } 201 - return { 202 - type: 'private_key', 203 - didPublicKey: await keypair.exportPublicKey('did'), 204 - keypair: keypair, 205 - }; 198 + case 'secp256k1': { 199 + keypair = await Secp256k1PrivateKey.importRaw(privateKeyBytes) 200 + break 201 + } 202 + default: { 203 + throw new Error(`unsupported "${type}" type`) 204 + } 205 + } 206 + } else { 207 + throw new Error('unknown input format') 206 208 } 207 - 208 - /** 209 - * Submits a new operation to the plc directory 210 - * @param did {string} - The user's did 211 - * @param operation 212 - * @returns {Promise<void>} 213 - */ 214 - async pushPlcOperation(did, operation) { 215 - const response = await fetch(`${this.plcDirectoryUrl}/${did}`, { 216 - method: 'post', 217 - headers: { 218 - 'content-type': 'application/json', 219 - }, 220 - body: JSON.stringify(operation), 221 - }); 209 + return { 210 + type: 'private_key', 211 + didPublicKey: await keypair.exportPublicKey('did'), 212 + keypair: keypair, 213 + } 214 + } 222 215 223 - const headers = response.headers; 224 - if (!response.ok) { 225 - const type = headers.get('content-type'); 216 + /** 217 + * Submits a new operation to the plc directory 218 + * @param did {string} - The user's did 219 + * @param operation 220 + * @returns {Promise<void>} 221 + */ 222 + async pushPlcOperation(did, operation) { 223 + const response = await fetch(`${this.plcDirectoryUrl}/${did}`, { 224 + method: 'post', 225 + headers: { 226 + 'content-type': 'application/json', 227 + }, 228 + body: JSON.stringify(operation), 229 + }) 226 230 227 - if (type?.includes('application/json')) { 228 - const json = await response.json(); 229 - if (typeof json === 'object' && json !== null && typeof json.message === 'string') { 230 - throw new Error(json.message); 231 - } 232 - } 231 + const headers = response.headers 232 + if (!response.ok) { 233 + const type = headers.get('content-type') 233 234 234 - throw new Error(`got http ${response.status} from plc`); 235 + if (type?.includes('application/json')) { 236 + const json = await response.json() 237 + if (typeof json === 'object' && json !== null && typeof json.message === 'string') { 238 + throw new Error(json.message) 235 239 } 236 - }; 237 - 238 - 239 - /** 240 - * Creates a new service auth token for a user. This is what is used to create a new account on a PDS for your did 241 - * 242 - * @param iss The user's did 243 - * @param aud The did:web, if it's a PDS it's usually from /xrpc/com.atproto.server.describeServer 244 - * @param keypair The keypair to sign with only supporting ES256K atm 245 - * @param lxm The lxm which is usually com.atproto.server.createAccount for creating a new account 246 - * @returns {Promise<string>} 247 - */ 248 - async createANewServiceAuthToken(iss, aud, keypair, lxm) { 249 - 250 - 251 - // Compute iat/exp defaults (60s window like reference: MINUTE/1e3) 252 - const iat = Math.floor(Date.now() / 1e3); 253 - const exp = iat + 60; 254 - 255 - // Generate a 16-byte hex jti 256 - const jti = (() => { 257 - const bytes = new Uint8Array(16); 258 - // crypto in browser or node; fall back safely 259 - (globalThis.crypto || window.crypto).getRandomValues(bytes); 260 - return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''); 261 - })(); 240 + } 262 241 242 + throw new Error(`got http ${response.status} from plc`) 243 + } 244 + } 263 245 264 - // Build header and payload (omit undefined fields) 265 - // Just defaulting to ES256K since p256 was not importing on firefox 266 - const header = {typ: 'JWT', alg: 'ES256K'}; 267 - const payload = {}; 268 - payload.iat = iat; 269 - payload.iss = iss; 270 - payload.aud = aud; 271 - payload.exp = exp; 272 - payload.lxm = lxm; 273 - payload.jti = jti; 246 + /** 247 + * Creates a new service auth token for a user. This is what is used to create a new account on a PDS for your did 248 + * 249 + * @param iss The user's did 250 + * @param aud The did:web, if it's a PDS it's usually from /xrpc/com.atproto.server.describeServer 251 + * @param keypair The keypair to sign with only supporting ES256K atm 252 + * @param lxm The lxm which is usually com.atproto.server.createAccount for creating a new account 253 + * @returns {Promise<string>} 254 + */ 255 + async createANewServiceAuthToken(iss, aud, keypair, lxm) { 256 + // Compute iat/exp defaults (60s window like reference: MINUTE/1e3) 257 + const iat = Math.floor(Date.now() / 1e3) 258 + const exp = iat + 60 274 259 275 - const headerB64 = jsonToB64Url(header); 276 - const payloadB64 = jsonToB64Url(payload); 277 - const toSignStr = `${headerB64}.${payloadB64}`; 260 + // Generate a 16-byte hex jti 261 + const jti = (() => { 262 + const bytes = new Uint8Array(16) 263 + // crypto in browser or node; fall back safely 264 + ;(globalThis.crypto || window.crypto).getRandomValues(bytes) 265 + return Array.from(bytes) 266 + .map(b => b.toString(16).padStart(2, '0')) 267 + .join('') 268 + })() 278 269 279 - // Sign 280 - const toSignBytes = new TextEncoder().encode(toSignStr); 281 - const sigBytes = await keypair.sign(toSignBytes); 270 + // Build header and payload (omit undefined fields) 271 + // Just defaulting to ES256K since p256 was not importing on firefox 272 + const header = { typ: 'JWT', alg: 'ES256K' } 273 + const payload = {} 274 + payload.iat = iat 275 + payload.iss = iss 276 + payload.aud = aud 277 + payload.exp = exp 278 + payload.lxm = lxm 279 + payload.jti = jti 282 280 283 - // Return compact JWS 284 - const sigB64 = toBase64Url(sigBytes); 285 - return `${toSignStr}.${sigB64}`; 286 - } 281 + const headerB64 = jsonToB64Url(header) 282 + const payloadB64 = jsonToB64Url(payload) 283 + const toSignStr = `${headerB64}.${payloadB64}` 287 284 285 + // Sign 286 + const toSignBytes = new TextEncoder().encode(toSignStr) 287 + const sigBytes = await keypair.sign(toSignBytes) 288 288 289 + // Return compact JWS 290 + const sigB64 = toBase64Url(sigBytes) 291 + return `${toSignStr}.${sigB64}` 292 + } 289 293 } 290 294 291 - 292 - export {PlcOps}; 295 + export { PlcOps }
+283 -281
packages/moover/lib/restore.js
··· 1 1 /** 2 2 * @typedef {import('@atcute/did-plc').Operation} Operation 3 3 */ 4 - import {P256PrivateKey, Secp256k1PrivateKey} from '@atcute/crypto'; 5 - import {handleAndPDSResolver} from './atprotoUtils.js'; 6 - import {PlcOps} from './plc-ops.js'; 7 - import {normalizeOp} from '@atcute/did-plc'; 8 - import {AtpAgent} from '@atproto/api'; 9 - import {Secp256k1PrivateKeyExportable} from '@atcute/crypto'; 10 - import * as CBOR from '@atcute/cbor'; 11 - import {toBase64Url} from '@atcute/multibase'; 4 + import { P256PrivateKey, Secp256k1PrivateKey } from '@atcute/crypto' 5 + import { handleAndPDSResolver } from './atprotoUtils.js' 6 + import { PlcOps } from './plc-ops.js' 7 + import { normalizeOp } from '@atcute/did-plc' 8 + import { AtpAgent } from '@atproto/api' 9 + import { Secp256k1PrivateKeyExportable } from '@atcute/crypto' 10 + import * as CBOR from '@atcute/cbor' 11 + import { toBase64Url } from '@atcute/multibase' 12 12 13 13 class Restore { 14 + /** 15 + * 16 + * @param pdsMooverInstance {string} - The url of the pds moover instance to restore from. Defaults to https://pdsmover.com 17 + */ 18 + constructor(pdsMooverInstance = 'https://pdsmover.com') { 19 + /** 20 + * If you want to use a different plc directory create your own instance of the plc ops class and pass it in here 21 + * @type {PlcOps} */ 22 + this.plcOps = new PlcOps() 14 23 15 24 /** 16 - * 17 - * @param pdsMooverInstance {string} - The url of the pds moover instance to restore from. Defaults to https://pdsmover.com 25 + * This is the base url for the pds moover instance used to restore the files from a backup. 26 + * @type {string} 18 27 */ 19 - constructor(pdsMooverInstance = 'https://pdsmover.com') { 20 - /** 21 - * If you want to use a different plc directory create your own instance of the plc ops class and pass it in here 22 - * @type {PlcOps} */ 23 - this.plcOps = new PlcOps(); 24 - 25 - /** 26 - * This is the base url for the pds moover instance used to restore the files from a backup. 27 - * @type {string} 28 - */ 29 - this.pdsMooverInstance = pdsMooverInstance 30 - 31 - /** 32 - * To keep it simple, only uses secp256k for the temp verification key that is used to create the new account on the new PDS 33 - * and is temporarily assigned to the user's account on PLC 34 - * @type {null|Secp256k1PrivateKeyExportable} 35 - */ 36 - this.tempVerificationKeypair = null; 37 - 38 - /** @type {AtpAgent} */ 39 - this.atpAgent = null; 40 - 41 - /** 42 - * The keypair that is used to sign the plc operation 43 - * @type {null|{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}} 44 - */ 45 - this.recoveryRotationKeyPair = null; 46 - 47 - /** 48 - * If this is true we are just restoring the repo and blobs. Ideally for rerunning a restore process after account recovery 49 - * @type {boolean} 50 - */ 51 - this.RestoreFromBackup = true; 52 - 53 - /** 54 - * If set to true then it will do the account recovery. Writes a temp key to the did doc, 55 - * create a new account on the new pds, and then submit a new plc op for the pds to have control (finishes the migration, can always restore the backup later) 56 - * @type {boolean} 57 - */ 58 - this.AccountRecovery = true; 59 - } 28 + this.pdsMooverInstance = pdsMooverInstance 60 29 61 30 /** 62 - * Recovers an account with the users rotation key and restores the repo from a PDS MOOver backup 63 - * This method can fail, and the account was still recovered, it's best to check the PLC logs to see where an account stands before reruns 64 - * @param rotationKey {string} - The users private rotation key, can be a multi key or hex key 65 - * @param rotationKeyType {string} - The type of the key, secp256k1 or p256. Required if the key is in hex format, defaults to secp256k1 66 - * @param currentHandleOrDid {string} - The users current handle or did, if they don't have a DNS record it will have to be their did for success 67 - * @param newPDS {string} - The new PDS url, like https://coolnewpds.com 68 - * @param newHandle {string} - Can be the users DNS handle if it is already setup with their did, if not it's bob.mypds.com 69 - * @param newPassword {string} - The new password for the new account 70 - * @param newEmail {string} - The new email for the new account 71 - * @param inviteCode {string|null} - The invite code for the new PDS if it requires one 72 - * @param cidToRestoreTo {string|null} - The cid of the plc op to restore to, used mostly to revert a fraudulent plc op. Want to give it the last valid operations cid 73 - * @param onStatus {function|null} - A function that takes a string used to update the UI. Like (status) => console.log(status) 74 - * @returns {Promise<void>} If there is a failure during restoring the back up (after the status Success! Restoring your repo...) then your account is most likely 75 - * recovered and future runs need to have the RestoreFromBackup flag set to true and AccountRecovery set to false. 31 + * To keep it simple, only uses secp256k for the temp verification key that is used to create the new account on the new PDS 32 + * and is temporarily assigned to the user's account on PLC 33 + * @type {null|Secp256k1PrivateKeyExportable} 76 34 */ 77 - async recover( 78 - rotationKey, 79 - rotationKeyType = 'secp256k1', 80 - currentHandleOrDid, 81 - newPDS, 82 - newHandle, 83 - newPassword, 84 - newEmail, 85 - inviteCode, 86 - cidToRestoreTo = null, 87 - onStatus = null) { 88 - 89 - if (onStatus) onStatus('Resolving your handle...'); 90 - 91 - let {usersDid} = await handleAndPDSResolver(currentHandleOrDid); 92 - 93 - if (onStatus) onStatus('Checking that the new PDS is an actual PDS (if the url is wrong, this takes a while to error out)'); 94 - this.atpAgent = new AtpAgent({service: newPDS}); 95 - const newHostDesc = await this.atpAgent.com.atproto.server.describeServer(); 96 - 97 - 98 - //Check to see if the user already has a repo on the new PDS, if they do no reason to try and restore via the plc operations 99 - try { 100 - await this.atpAgent.com.atproto.repo.describeRepo({repo: usersDid.toString()}); 101 - //If we got this far and there is a repo on the new PDS with the users did, we can just move on and restore the files. 102 - //We do not want to mess with the plc ops if we dont have to 103 - this.AccountRecovery = false; 35 + this.tempVerificationKeypair = null 104 36 105 - } catch (error) { 106 - console.error(error); 107 - let parsedError = error.error; 108 - if (parsedError === 'RepoDeactivated') { 109 - //Ideally should mean they already have a repo on the new PDS and we just need to restore the files 110 - this.AccountRecovery = false; 111 - } 112 - //This is the error we want to see, anything else throw 113 - if (parsedError !== 'RepoNotFound') { 114 - throw error; 115 - } 116 - } 117 - 118 - //We need to double check that the new handle has not been taken, if it has we need to throw an error 119 - //We care a bit more because we do not want any unnecessary plc ops to be created 120 - try { 121 - let resolveHandle = await this.atpAgent.com.atproto.identity.resolveHandle({handle: newHandle}); 122 - if (resolveHandle.data.did === usersDid.toString()) { 123 - //This was originally setting the AccountRecovery to false, which works if it is resolved via .well-known, but not dns 124 - //The idea was to check and see if the handle has been taken. just leaving for now since it does that check and if the user owns the handle 125 - //their did should be set anyhow 126 - 127 - } else { 128 - //There is a repo with that name and it's not the users did, 129 - throw new Error('The new handle is already taken, please select a different handle'); 130 - } 131 - } catch (error) { 132 - // Going to silently log this and just assume the handle has not been taken. 133 - console.error(error); 134 - if (error.message.startsWith('The new handle')) { 135 - //it's not our custom error, so we can just throw it 136 - throw error; 137 - } 37 + /** @type {AtpAgent} */ 38 + this.atpAgent = null 138 39 139 - } 40 + /** 41 + * The keypair that is used to sign the plc operation 42 + * @type {null|{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}} 43 + */ 44 + this.recoveryRotationKeyPair = null 140 45 141 - if (this.AccountRecovery) { 46 + /** 47 + * If this is true we are just restoring the repo and blobs. Ideally for rerunning a restore process after account recovery 48 + * @type {boolean} 49 + */ 50 + this.RestoreFromBackup = true 142 51 143 - if (onStatus) onStatus('Validating your private rotation key is in the correct format...'); 52 + /** 53 + * If set to true then it will do the account recovery. Writes a temp key to the did doc, 54 + * create a new account on the new pds, and then submit a new plc op for the pds to have control (finishes the migration, can always restore the backup later) 55 + * @type {boolean} 56 + */ 57 + this.AccountRecovery = true 58 + } 144 59 145 - this.recoveryRotationKeyPair = await this.plcOps.getKeyPair(rotationKey, rotationKeyType); 60 + /** 61 + * Recovers an account with the users rotation key and restores the repo from a PDS MOOver backup 62 + * This method can fail, and the account was still recovered, it's best to check the PLC logs to see where an account stands before reruns 63 + * @param rotationKey {string} - The users private rotation key, can be a multi key or hex key 64 + * @param rotationKeyType {string} - The type of the key, secp256k1 or p256. Required if the key is in hex format, defaults to secp256k1 65 + * @param currentHandleOrDid {string} - The users current handle or did, if they don't have a DNS record it will have to be their did for success 66 + * @param newPDS {string} - The new PDS url, like https://coolnewpds.com 67 + * @param newHandle {string} - Can be the users DNS handle if it is already setup with their did, if not it's bob.mypds.com 68 + * @param newPassword {string} - The new password for the new account 69 + * @param newEmail {string} - The new email for the new account 70 + * @param inviteCode {string|null} - The invite code for the new PDS if it requires one 71 + * @param cidToRestoreTo {string|null} - The cid of the plc op to restore to, used mostly to revert a fraudulent plc op. Want to give it the last valid operations cid 72 + * @param onStatus {function|null} - A function that takes a string used to update the UI. Like (status) => console.log(status) 73 + * @returns {Promise<void>} If there is a failure during restoring the back up (after the status Success! Restoring your repo...) then your account is most likely 74 + * recovered and future runs need to have the RestoreFromBackup flag set to true and AccountRecovery set to false. 75 + */ 76 + async recover( 77 + rotationKey, 78 + rotationKeyType = 'secp256k1', 79 + currentHandleOrDid, 80 + newPDS, 81 + newHandle, 82 + newPassword, 83 + newEmail, 84 + inviteCode, 85 + cidToRestoreTo = null, 86 + onStatus = null, 87 + ) { 88 + if (onStatus) onStatus('Resolving your handle...') 146 89 90 + let { usersDid } = await handleAndPDSResolver(currentHandleOrDid) 147 91 148 - if (onStatus) onStatus('Resolving PlC operation logs...'); 92 + if (onStatus) 93 + onStatus( 94 + 'Checking that the new PDS is an actual PDS (if the url is wrong, this takes a while to error out)', 95 + ) 96 + this.atpAgent = new AtpAgent({ service: newPDS }) 97 + const newHostDesc = await this.atpAgent.com.atproto.server.describeServer() 149 98 150 - /** @type {Operation} */ 151 - let baseOpForSigning = null; 152 - let opPrevCid = null; 99 + //Check to see if the user already has a repo on the new PDS, if they do no reason to try and restore via the plc operations 100 + try { 101 + await this.atpAgent.com.atproto.repo.describeRepo({ repo: usersDid.toString() }) 102 + //If we got this far and there is a repo on the new PDS with the users did, we can just move on and restore the files. 103 + //We do not want to mess with the plc ops if we dont have to 104 + this.AccountRecovery = false 105 + } catch (error) { 106 + console.error(error) 107 + let parsedError = error.error 108 + if (parsedError === 'RepoDeactivated') { 109 + //Ideally should mean they already have a repo on the new PDS and we just need to restore the files 110 + this.AccountRecovery = false 111 + } 112 + //This is the error we want to see, anything else throw 113 + if (parsedError !== 'RepoNotFound') { 114 + throw error 115 + } 116 + } 153 117 154 - //This is for reversals against a rogue plc op and you want to restore to a specific cid in the audit log 155 - if (cidToRestoreTo) { 156 - let auditLogs = await this.plcOps.getPlcAuditLogs(usersDid); 157 - for (const log of auditLogs) { 158 - if (log.cid === cidToRestoreTo) { 159 - baseOpForSigning = normalizeOp(log.operation); 160 - opPrevCid = log.cid; 161 - break; 162 - } 163 - } 164 - if (!baseOpForSigning) { 165 - throw new Error('Could not find the cid in the audit logs'); 166 - } 167 - } else { 168 - let {lastOperation, base} = await this.plcOps.getLastPlcOpFromPlc(usersDid); 169 - opPrevCid = base.cid; 170 - baseOpForSigning = lastOperation; 171 - } 118 + //We need to double check that the new handle has not been taken, if it has we need to throw an error 119 + //We care a bit more because we do not want any unnecessary plc ops to be created 120 + try { 121 + let resolveHandle = await this.atpAgent.com.atproto.identity.resolveHandle({ 122 + handle: newHandle, 123 + }) 124 + if (resolveHandle.data.did === usersDid.toString()) { 125 + //This was originally setting the AccountRecovery to false, which works if it is resolved via .well-known, but not dns 126 + //The idea was to check and see if the handle has been taken. just leaving for now since it does that check and if the user owns the handle 127 + //their did should be set anyhow 128 + } else { 129 + //There is a repo with that name and it's not the users did, 130 + throw new Error('The new handle is already taken, please select a different handle') 131 + } 132 + } catch (error) { 133 + // Going to silently log this and just assume the handle has not been taken. 134 + console.error(error) 135 + if (error.message.startsWith('The new handle')) { 136 + //it's not our custom error, so we can just throw it 137 + throw error 138 + } 139 + } 172 140 173 - if (onStatus) onStatus('Preparing to switch to a temp atproto key...'); 174 - if (this.tempVerificationKeypair == null) { 175 - if (onStatus) onStatus('Creating a new temp atproto key...'); 176 - this.tempVerificationKeypair = await Secp256k1PrivateKeyExportable.createKeypair(); 177 - } 178 - //Just defaulting to the user's recovery key for now. Advance cases will be something else 179 - //Maybe just a new ui to edit the PLC doc in a limited capacity, but sinc ethis is a temp plc op i don't think it's needed 180 - let tempRotationKeys = [this.recoveryRotationKeyPair.didPublicKey]; 141 + if (this.AccountRecovery) { 142 + if (onStatus) onStatus('Validating your private rotation key is in the correct format...') 181 143 182 - if (onStatus) onStatus('Modifying the PLC OP for recovery...'); 183 - //A temp plc op for control of the atproto key to create a serviceAuth and new account on the new PDS 184 - await this.plcOps.signAndPublishNewOp( 185 - usersDid, 186 - this.recoveryRotationKeyPair.keypair, 187 - baseOpForSigning.alsoKnownAs, 188 - tempRotationKeys, 189 - newPDS, 190 - await this.tempVerificationKeypair.exportPublicKey('did'), 191 - opPrevCid); 144 + this.recoveryRotationKeyPair = await this.plcOps.getKeyPair(rotationKey, rotationKeyType) 192 145 146 + if (onStatus) onStatus('Resolving PlC operation logs...') 193 147 194 - if (onStatus) onStatus('Creating your new account on the new PDS...'); 195 - let serviceAuthToken = await this.plcOps.createANewServiceAuthToken(usersDid, newHostDesc.data.did, this.tempVerificationKeypair, 'com.atproto.server.createAccount'); 148 + /** @type {Operation} */ 149 + let baseOpForSigning = null 150 + let opPrevCid = null 196 151 197 - let createAccountRequest = { 198 - did: usersDid, 199 - handle: newHandle, 200 - email: newEmail, 201 - password: newPassword, 202 - }; 203 - if (inviteCode) { 204 - createAccountRequest.inviteCode = inviteCode; 205 - } 206 - const _ = await this.atpAgent.com.atproto.server.createAccount( 207 - createAccountRequest, 208 - { 209 - headers: {authorization: `Bearer ${serviceAuthToken}`}, 210 - encoding: 'application/json', 211 - }); 152 + //This is for reversals against a rogue plc op and you want to restore to a specific cid in the audit log 153 + if (cidToRestoreTo) { 154 + let auditLogs = await this.plcOps.getPlcAuditLogs(usersDid) 155 + for (const log of auditLogs) { 156 + if (log.cid === cidToRestoreTo) { 157 + baseOpForSigning = normalizeOp(log.operation) 158 + opPrevCid = log.cid 159 + break 160 + } 212 161 } 213 - 214 - await this.atpAgent.login({ 215 - identifier: usersDid, 216 - password: newPassword, 217 - }); 218 - 219 - if (this.AccountRecovery) { 220 - //Moving the user offically to the new PDS 221 - if (onStatus) onStatus('Signing the papers...'); 222 - let {base} = await this.plcOps.getLastPlcOpFromPlc(usersDid); 223 - await this.signRestorePlcOperation(usersDid, [this.recoveryRotationKeyPair.didPublicKey], base.cid); 162 + if (!baseOpForSigning) { 163 + throw new Error('Could not find the cid in the audit logs') 224 164 } 165 + } else { 166 + let { lastOperation, base } = await this.plcOps.getLastPlcOpFromPlc(usersDid) 167 + opPrevCid = base.cid 168 + baseOpForSigning = lastOperation 169 + } 225 170 226 - if (this.RestoreFromBackup) { 227 - if (onStatus) onStatus('Success! Restoring your repo...'); 228 - const pdsMoover = new AtpAgent({service: this.pdsMooverInstance}); 229 - const repoRes = await pdsMoover.com.atproto.sync.getRepo({did: usersDid}); 230 - await this.atpAgent.com.atproto.repo.importRepo(repoRes.data, { 231 - encoding: 'application/vnd.ipld.car', 232 - }); 171 + if (onStatus) onStatus('Preparing to switch to a temp atproto key...') 172 + if (this.tempVerificationKeypair == null) { 173 + if (onStatus) onStatus('Creating a new temp atproto key...') 174 + this.tempVerificationKeypair = await Secp256k1PrivateKeyExportable.createKeypair() 175 + } 176 + //Just defaulting to the user's recovery key for now. Advance cases will be something else 177 + //Maybe just a new ui to edit the PLC doc in a limited capacity, but sinc ethis is a temp plc op i don't think it's needed 178 + let tempRotationKeys = [this.recoveryRotationKeyPair.didPublicKey] 233 179 234 - if (onStatus) onStatus('Restoring your blobs...'); 180 + if (onStatus) onStatus('Modifying the PLC OP for recovery...') 181 + //A temp plc op for control of the atproto key to create a serviceAuth and new account on the new PDS 182 + await this.plcOps.signAndPublishNewOp( 183 + usersDid, 184 + this.recoveryRotationKeyPair.keypair, 185 + baseOpForSigning.alsoKnownAs, 186 + tempRotationKeys, 187 + newPDS, 188 + await this.tempVerificationKeypair.exportPublicKey('did'), 189 + opPrevCid, 190 + ) 235 191 236 - //Using the missing endpoint to findout what's missing then the PDS MOOver endpoint to restore 237 - let totalMissingBlobs = 0; 238 - let missingBlobCursor = undefined; 239 - let missingUploadedBlobs = 0; 192 + if (onStatus) onStatus('Creating your new account on the new PDS...') 193 + let serviceAuthToken = await this.plcOps.createANewServiceAuthToken( 194 + usersDid, 195 + newHostDesc.data.did, 196 + this.tempVerificationKeypair, 197 + 'com.atproto.server.createAccount', 198 + ) 240 199 241 - do { 242 - 243 - const missingBlobs = await this.atpAgent.com.atproto.repo.listMissingBlobs({ 244 - cursor: missingBlobCursor, 245 - limit: 1000, 246 - }); 247 - totalMissingBlobs += missingBlobs.data.blobs.length; 248 - 249 - for (const recordBlob of missingBlobs.data.blobs) { 250 - try { 200 + let createAccountRequest = { 201 + did: usersDid, 202 + handle: newHandle, 203 + email: newEmail, 204 + password: newPassword, 205 + } 206 + if (inviteCode) { 207 + createAccountRequest.inviteCode = inviteCode 208 + } 209 + const _ = await this.atpAgent.com.atproto.server.createAccount(createAccountRequest, { 210 + headers: { authorization: `Bearer ${serviceAuthToken}` }, 211 + encoding: 'application/json', 212 + }) 213 + } 251 214 252 - const blobRes = await pdsMoover.com.atproto.sync.getBlob({ 253 - did: usersDid, 254 - cid: recordBlob.cid, 255 - }); 256 - let result = await this.atpAgent.com.atproto.repo.uploadBlob(blobRes.data, { 257 - encoding: blobRes.headers['content-type'], 258 - }); 215 + await this.atpAgent.login({ 216 + identifier: usersDid, 217 + password: newPassword, 218 + }) 259 219 220 + if (this.AccountRecovery) { 221 + //Moving the user offically to the new PDS 222 + if (onStatus) onStatus('Signing the papers...') 223 + let { base } = await this.plcOps.getLastPlcOpFromPlc(usersDid) 224 + await this.signRestorePlcOperation( 225 + usersDid, 226 + [this.recoveryRotationKeyPair.didPublicKey], 227 + base.cid, 228 + ) 229 + } 260 230 261 - if (missingUploadedBlobs % 2 === 0) { 262 - if (onStatus) onStatus(`Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs} (The total may increase as we find more)`); 263 - } 264 - missingUploadedBlobs++; 265 - } catch (error) { 266 - console.error(error); 267 - } 268 - } 269 - missingBlobCursor = missingBlobs.data.cursor; 270 - } while (missingBlobCursor); 271 - } 272 - const accountStatus = await this.atpAgent.com.atproto.server.checkAccountStatus(); 273 - if (!accountStatus.data.activated) { 274 - if (onStatus) onStatus('Activating your account...'); 275 - await this.atpAgent.com.atproto.server.activateAccount(); 276 - } 231 + if (this.RestoreFromBackup) { 232 + if (onStatus) onStatus('Success! Restoring your repo...') 233 + const pdsMoover = new AtpAgent({ service: this.pdsMooverInstance }) 234 + const repoRes = await pdsMoover.com.atproto.sync.getRepo({ did: usersDid }) 235 + await this.atpAgent.com.atproto.repo.importRepo(repoRes.data, { 236 + encoding: 'application/vnd.ipld.car', 237 + }) 277 238 278 - } 239 + if (onStatus) onStatus('Restoring your blobs...') 279 240 241 + //Using the missing endpoint to findout what's missing then the PDS MOOver endpoint to restore 242 + let totalMissingBlobs = 0 243 + let missingBlobCursor = undefined 244 + let missingUploadedBlobs = 0 280 245 281 - /** 282 - * This method signs the plc operation over to the new PDS and activates the account 283 - * Assumes you have already created a new account during the recovery process and logged in 284 - * Uses the recommended did doc from the PDS as a base and adds the users rotation key to the rotation keys array 285 - * 286 - * @param usersDid 287 - * @param additionalRotationKeysToAdd 288 - * @param prevCid 289 - * @returns {Promise<void>} 290 - */ 291 - async signRestorePlcOperation(usersDid, additionalRotationKeysToAdd = [], prevCid) { 292 - const getDidCredentials = 293 - await this.atpAgent.com.atproto.identity.getRecommendedDidCredentials(); 246 + do { 247 + const missingBlobs = await this.atpAgent.com.atproto.repo.listMissingBlobs({ 248 + cursor: missingBlobCursor, 249 + limit: 1000, 250 + }) 251 + totalMissingBlobs += missingBlobs.data.blobs.length 294 252 295 - const pdsProvidedRotationKeys = getDidCredentials.data.rotationKeys ?? []; 296 - //Puts the provided rotation keys above the pds pro 297 - const rotationKeys = [...new Set([...(additionalRotationKeysToAdd || []), ...pdsProvidedRotationKeys])]; 298 - if (!rotationKeys) { 299 - throw new Error('No rotation keys were found to be added to the PLC'); 300 - } 253 + for (const recordBlob of missingBlobs.data.blobs) { 254 + try { 255 + const blobRes = await pdsMoover.com.atproto.sync.getBlob({ 256 + did: usersDid, 257 + cid: recordBlob.cid, 258 + }) 259 + let result = await this.atpAgent.com.atproto.repo.uploadBlob(blobRes.data, { 260 + encoding: blobRes.headers['content-type'], 261 + }) 301 262 302 - if (rotationKeys.length > 5) { 303 - throw new Error('You can only add up to 5 rotation keys to the PLC'); 263 + if (missingUploadedBlobs % 2 === 0) { 264 + if (onStatus) 265 + onStatus( 266 + `Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs} (The total may increase as we find more)`, 267 + ) 268 + } 269 + missingUploadedBlobs++ 270 + } catch (error) { 271 + console.error(error) 272 + } 304 273 } 274 + missingBlobCursor = missingBlobs.data.cursor 275 + } while (missingBlobCursor) 276 + } 277 + const accountStatus = await this.atpAgent.com.atproto.server.checkAccountStatus() 278 + if (!accountStatus.data.activated) { 279 + if (onStatus) onStatus('Activating your account...') 280 + await this.atpAgent.com.atproto.server.activateAccount() 281 + } 282 + } 305 283 284 + /** 285 + * This method signs the plc operation over to the new PDS and activates the account 286 + * Assumes you have already created a new account during the recovery process and logged in 287 + * Uses the recommended did doc from the PDS as a base and adds the users rotation key to the rotation keys array 288 + * 289 + * @param usersDid 290 + * @param additionalRotationKeysToAdd 291 + * @param prevCid 292 + * @returns {Promise<void>} 293 + */ 294 + async signRestorePlcOperation(usersDid, additionalRotationKeysToAdd = [], prevCid) { 295 + const getDidCredentials = 296 + await this.atpAgent.com.atproto.identity.getRecommendedDidCredentials() 306 297 307 - const plcOpToSubmit = { 308 - type: 'plc_operation', 309 - ...getDidCredentials.data, 310 - prev: prevCid, 311 - rotationKeys: rotationKeys, 312 - }; 313 - 298 + const pdsProvidedRotationKeys = getDidCredentials.data.rotationKeys ?? [] 299 + //Puts the provided rotation keys above the pds pro 300 + const rotationKeys = [ 301 + ...new Set([...(additionalRotationKeysToAdd || []), ...pdsProvidedRotationKeys]), 302 + ] 303 + if (!rotationKeys) { 304 + throw new Error('No rotation keys were found to be added to the PLC') 305 + } 314 306 315 - const opBytes = CBOR.encode(plcOpToSubmit); 316 - const sigBytes = await this.recoveryRotationKeyPair.keypair.sign(opBytes); 307 + if (rotationKeys.length > 5) { 308 + throw new Error('You can only add up to 5 rotation keys to the PLC') 309 + } 317 310 318 - const signature = toBase64Url(sigBytes); 311 + const plcOpToSubmit = { 312 + type: 'plc_operation', 313 + ...getDidCredentials.data, 314 + prev: prevCid, 315 + rotationKeys: rotationKeys, 316 + } 319 317 320 - const signedOperation = { 321 - ...plcOpToSubmit, 322 - sig: signature, 323 - }; 318 + const opBytes = CBOR.encode(plcOpToSubmit) 319 + const sigBytes = await this.recoveryRotationKeyPair.keypair.sign(opBytes) 324 320 325 - await this.plcOps.pushPlcOperation(usersDid, signedOperation); 326 - await this.atpAgent.com.atproto.server.activateAccount(); 321 + const signature = toBase64Url(sigBytes) 327 322 323 + const signedOperation = { 324 + ...plcOpToSubmit, 325 + sig: signature, 328 326 } 327 + 328 + await this.plcOps.pushPlcOperation(usersDid, signedOperation) 329 + await this.atpAgent.com.atproto.server.activateAccount() 330 + } 329 331 } 330 332 331 - export {Restore}; 333 + export { Restore }
+3 -1
packages/moover/package.json
··· 35 35 "dev": "vite", 36 36 "build": "vite build", 37 37 "gen:types": "tsc -p .", 38 - "preview": "vite preview" 38 + "preview": "vite preview", 39 + "generate:lexicons": "lex build --lexicons lexicons --out lib/lexicons --clear" 39 40 }, 40 41 "devDependencies": { 41 42 "eslint": "^9.34.0", ··· 51 52 "@atcute/lexicons": "^1.2.2", 52 53 "@atcute/multibase": "^1.1.6", 53 54 "@atproto/api": "^0.16.7", 55 + "@atproto/lex": "^0.0.16", 54 56 "@pds-moover/lexicons": "^1.0.0", 55 57 "alpinejs": "^3.15.0", 56 58 "vite-plugin-full-reload": "^1.2.0",
+3342
packages/moover/pnpm-lock.yaml
··· 1 + lockfileVersion: '9.0' 2 + 3 + settings: 4 + autoInstallPeers: true 5 + excludeLinksFromLockfile: false 6 + 7 + importers: 8 + 9 + .: 10 + dependencies: 11 + '@atcute/cbor': 12 + specifier: ^2.2.7 13 + version: 2.3.0 14 + '@atcute/client': 15 + specifier: ^4.0.4 16 + version: 4.2.1 17 + '@atcute/crypto': 18 + specifier: ^2.2.5 19 + version: 2.3.0 20 + '@atcute/did-plc': 21 + specifier: ^0.1.7 22 + version: 0.1.7 23 + '@atcute/identity-resolver': 24 + specifier: ^1.1.3 25 + version: 1.2.2(@atcute/identity@1.1.3) 26 + '@atcute/lexicons': 27 + specifier: ^1.2.2 28 + version: 1.2.7 29 + '@atcute/multibase': 30 + specifier: ^1.1.6 31 + version: 1.1.7 32 + '@atproto/api': 33 + specifier: ^0.16.7 34 + version: 0.16.11 35 + '@atproto/lex': 36 + specifier: ^0.0.16 37 + version: 0.0.16 38 + '@pds-moover/lexicons': 39 + specifier: ^1.0.0 40 + version: 1.0.1 41 + alpinejs: 42 + specifier: ^3.15.0 43 + version: 3.15.8 44 + vite-plugin-full-reload: 45 + specifier: ^1.2.0 46 + version: 1.2.0 47 + vite-rs-plugin: 48 + specifier: 1.0.1 49 + version: 1.0.1(vite@7.3.1) 50 + devDependencies: 51 + eslint: 52 + specifier: ^9.34.0 53 + version: 9.39.2 54 + eslint-plugin-import: 55 + specifier: ^2.32.0 56 + version: 2.32.0(eslint@9.39.2) 57 + vite: 58 + specifier: ^7.1.7 59 + version: 7.3.1 60 + 61 + packages: 62 + 63 + '@atcute/cbor@2.3.0': 64 + resolution: {integrity: sha512-7G2AndkfYzIXMBOBqUPUWP6oIJJm77KY5nYzS4Mr5NNxnmnrBrXEQqp+seCE3X5TV8FUSWQK5YRTU87uPjafMQ==} 65 + 66 + '@atcute/cid@2.4.0': 67 + resolution: {integrity: sha512-6+5u9MpUrgSRQ94z7vaIX4BYk8fYr2KXUBS+rrr2NhlPy8xam8nbTlmd3hvBbtpSwShbhRAE4tA5Ab7eYUp2Yw==} 68 + 69 + '@atcute/client@4.2.1': 70 + resolution: {integrity: sha512-ZBFM2pW075JtgGFu5g7HHZBecrClhlcNH8GVP9Zz1aViWR+cjjBsTpeE63rJs+FCOHFYlirUyo5L8SGZ4kMINw==} 71 + 72 + '@atcute/crypto@2.3.0': 73 + resolution: {integrity: sha512-w5pkJKCjbNMQu+F4JRHbR3ROQyhi1wbn+GSC6WDQamcYHkZmEZk1/eoI354bIQOOfkEM6aFLv718iskrkon4GQ==} 74 + 75 + '@atcute/did-plc@0.1.7': 76 + resolution: {integrity: sha512-a7yOQNqViae3rB5/xa3U0EPJbFD9l8zOHXx6XASZ5F8+Vy2uTgXK3omurpNZ5UxRpy1ni1AMhSohXr61cqWbkg==} 77 + 78 + '@atcute/identity-resolver@1.2.2': 79 + resolution: {integrity: sha512-eUh/UH4bFvuXS0X7epYCeJC/kj4rbBXfSRumLEH4smMVwNOgTo7cL/0Srty+P/qVPoZEyXdfEbS0PHJyzoXmHw==} 80 + peerDependencies: 81 + '@atcute/identity': ^1.0.0 82 + 83 + '@atcute/identity@1.1.3': 84 + resolution: {integrity: sha512-oIqPoI8TwWeQxvcLmFEZLdN2XdWcaLVtlm8pNk0E72As9HNzzD9pwKPrLr3rmTLRIoULPPFmq9iFNsTeCIU9ng==} 85 + 86 + '@atcute/lexicons@1.2.7': 87 + resolution: {integrity: sha512-gCvkSMI1F1zx7xXa59iPiSKMH3L5Hga6iurGqQjaQbE2V/np/2QuDqQzt96TNbWfaFAXE9f9oY+0z3ljf/bweA==} 88 + 89 + '@atcute/multibase@1.1.7': 90 + resolution: {integrity: sha512-YmWds7U52b7Qri0xNfGeqSOvgyNfHR8Yy/NNDQx4d5TkCX2fHJIo0pXquEhCyMNAwKt53uH5yQDswy4TNP1Zhw==} 91 + 92 + '@atcute/uint8array@1.1.0': 93 + resolution: {integrity: sha512-JtHXIVW6LPU9FMWp7SgE4HbUs3uV2WdfkK/2RWdEGjr4EgMV50P3FdU6fPeGlTfDNBJVYMIsuD2wwaKRPV/Aqg==} 94 + 95 + '@atcute/util-fetch@1.0.5': 96 + resolution: {integrity: sha512-qjHj01BGxjSjIFdPiAjSARnodJIIyKxnCMMEcXMESo9TAyND6XZQqrie5fia+LlYWVXdpsTds8uFQwc9jdKTig==} 97 + 98 + '@atcute/util-text@1.1.0': 99 + resolution: {integrity: sha512-34G9KD5Z9f7oEdFpZOmqrMnU86p8ne6LlxJowfZzKNszRcl1GH+FtEPh3N1woelJT2SkPXMK2anwT8DESTluwA==} 100 + 101 + '@atproto-labs/did-resolver@0.2.6': 102 + resolution: {integrity: sha512-2K1bC04nI2fmgNcvof+yA28IhGlpWn2JKYlPa7To9JTKI45FINCGkQSGiL2nyXlyzDJJ34fZ1aq6/IRFIOIiqg==} 103 + 104 + '@atproto-labs/fetch@0.2.3': 105 + resolution: {integrity: sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw==} 106 + 107 + '@atproto-labs/pipe@0.1.1': 108 + resolution: {integrity: sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg==} 109 + 110 + '@atproto-labs/simple-store-memory@0.1.4': 111 + resolution: {integrity: sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw==} 112 + 113 + '@atproto-labs/simple-store@0.3.0': 114 + resolution: {integrity: sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ==} 115 + 116 + '@atproto/api@0.16.11': 117 + resolution: {integrity: sha512-1dhfQNHiclb102RW+Ea8Nft5olfqU0Ev/vlQaSX6mWNo1aP5zT+sPODJ8+BTUOYk3vcuvL7QMkqA/rLYy2PMyw==} 118 + 119 + '@atproto/common-web@0.4.16': 120 + resolution: {integrity: sha512-Ufvaff5JgxUyUyTAG0/3o7ltpy3lnZ1DvLjyAnvAf+hHfiK7OMQg+8byr+orN+KP9MtIQaRTsCgYPX+PxMKUoA==} 121 + 122 + '@atproto/common@0.5.11': 123 + resolution: {integrity: sha512-WRlT4s+wv80WdQuzkQub9D5vTD82O8dH2p91u4b+x3O17q5IQbmA3Lj+1NICINNSy2voqloqAWdqXEkRfdlAPw==} 124 + engines: {node: '>=18.7.0'} 125 + 126 + '@atproto/crypto@0.4.5': 127 + resolution: {integrity: sha512-n40aKkMoCatP0u9Yvhrdk6fXyOHFDDbkdm4h4HCyWW+KlKl8iXfD5iV+ECq+w5BM+QH25aIpt3/j6EUNerhLxw==} 128 + engines: {node: '>=18.7.0'} 129 + 130 + '@atproto/did@0.3.0': 131 + resolution: {integrity: sha512-raUPzUGegtW/6OxwCmM8bhZvuIMzxG5t9oWsth6Tp91Kb5fTnHV2h/KKNF1C82doeA4BdXCErTyg7ISwLbQkzA==} 132 + 133 + '@atproto/lex-builder@0.0.15': 134 + resolution: {integrity: sha512-OtAjQD02vTaJNOWFMO5+9o+z3sGEDTb6Gre9/8JsjnweZk9JzZ46rGpK7old8Unw9oQDP/l58uzAGTywV350NQ==} 135 + 136 + '@atproto/lex-cbor@0.0.11': 137 + resolution: {integrity: sha512-A7ETtPsEsJ/VuPJOFw4bPNTKxHvFN1JbTQ2NjLuisd3ry7fVxgMpo/qGXsUQsAh/I/uziGbhpNqdS6GnI2p/Wg==} 138 + 139 + '@atproto/lex-client@0.0.12': 140 + resolution: {integrity: sha512-ef4jQQ7SOtBsXr+Gf1UHuBfCiAGYZxO5PCCXl3eT4ObO83SROtIf7pyO06jBQI/IZChSVsXqXsgakR0aru6lYQ==} 141 + 142 + '@atproto/lex-data@0.0.11': 143 + resolution: {integrity: sha512-4+KTtHdqwlhiTKA7D4SACea4jprsNpCQsNALW09wsZ6IHhCDGO5tr1cmV+QnLYe3G3mu1E1yXHXbPUHrUUDT/A==} 144 + 145 + '@atproto/lex-document@0.0.13': 146 + resolution: {integrity: sha512-LWsBsKIbyuG7jFObTtnCFQNYHxWWVpVVspqv6UtnS/QsaCyCMg1GIz5vlgi8QBnmGvaPiQxIzGt6mERpTvEXpg==} 147 + 148 + '@atproto/lex-installer@0.0.16': 149 + resolution: {integrity: sha512-fHLu8VUf9zf2Jz+wJ9+5fvFGxi3930VyUBk/FtszcccTLg1uzb0qElUpVHpieFAHbA2fYqjvPgCpZpH6iploYQ==} 150 + 151 + '@atproto/lex-json@0.0.11': 152 + resolution: {integrity: sha512-2IExAoQ4KsR5fyPa1JjIvtR316PvdgRH/l3BVGLBd3cSxM3m5MftIv1B6qZ9HjNiK60SgkWp0mi9574bTNDhBQ==} 153 + 154 + '@atproto/lex-resolver@0.0.14': 155 + resolution: {integrity: sha512-jBkYRmMKap2OM1zm0VDvs7Heuf3pGjw9xJEHQx1ohkMmM5f+cHPS40RQ8x8SNE+Vl9gMuOrgmgKyPDIuYSIBTw==} 156 + 157 + '@atproto/lex-schema@0.0.12': 158 + resolution: {integrity: sha512-l1RNYmqNwIEjgMEjC9i2o6FLsUFdpAc610xQYK/CRxN31cRzY0lAJ2GFbp2GZ4rRAD3FGYCXid6gZ42KsieUcw==} 159 + 160 + '@atproto/lex@0.0.16': 161 + resolution: {integrity: sha512-k7cxIFxEiztHVVMFzqOOvThPDZ2RaK8+X9L27v0B2DmNPhbqRoKFdDU3dWHFhUhBP/CrK34zDo8YPQ4JmcradA==} 162 + hasBin: true 163 + 164 + '@atproto/lexicon@0.5.2': 165 + resolution: {integrity: sha512-lRmJgMA8f5j7VB5Iu5cp188ald5FuI4FlmZ7nn6EBrk1dgOstWVrI5Ft6K3z2vjyLZRG6nzknlsw+tDP63p7bQ==} 166 + 167 + '@atproto/lexicon@0.6.1': 168 + resolution: {integrity: sha512-/vI1kVlY50Si+5MXpvOucelnYwb0UJ6Qto5mCp+7Q5C+Jtp+SoSykAPVvjVtTnQUH2vrKOFOwpb3C375vSKzXw==} 169 + 170 + '@atproto/repo@0.8.12': 171 + resolution: {integrity: sha512-QpVTVulgfz5PUiCTELlDBiRvnsnwrFWi+6CfY88VwXzrRHd9NE8GItK7sfxQ6U65vD/idH8ddCgFrlrsn1REPQ==} 172 + engines: {node: '>=18.7.0'} 173 + 174 + '@atproto/syntax@0.4.3': 175 + resolution: {integrity: sha512-YoZUz40YAJr5nPwvCDWgodEOlt5IftZqPJvA0JDWjuZKD8yXddTwSzXSaKQAzGOpuM+/A3uXRtPzJJqlScc+iA==} 176 + 177 + '@atproto/xrpc@0.7.7': 178 + resolution: {integrity: sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==} 179 + 180 + '@badrap/valita@0.4.6': 181 + resolution: {integrity: sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg==} 182 + engines: {node: '>= 18'} 183 + 184 + '@esbuild/aix-ppc64@0.27.3': 185 + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} 186 + engines: {node: '>=18'} 187 + cpu: [ppc64] 188 + os: [aix] 189 + 190 + '@esbuild/android-arm64@0.27.3': 191 + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} 192 + engines: {node: '>=18'} 193 + cpu: [arm64] 194 + os: [android] 195 + 196 + '@esbuild/android-arm@0.27.3': 197 + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} 198 + engines: {node: '>=18'} 199 + cpu: [arm] 200 + os: [android] 201 + 202 + '@esbuild/android-x64@0.27.3': 203 + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} 204 + engines: {node: '>=18'} 205 + cpu: [x64] 206 + os: [android] 207 + 208 + '@esbuild/darwin-arm64@0.27.3': 209 + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} 210 + engines: {node: '>=18'} 211 + cpu: [arm64] 212 + os: [darwin] 213 + 214 + '@esbuild/darwin-x64@0.27.3': 215 + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} 216 + engines: {node: '>=18'} 217 + cpu: [x64] 218 + os: [darwin] 219 + 220 + '@esbuild/freebsd-arm64@0.27.3': 221 + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} 222 + engines: {node: '>=18'} 223 + cpu: [arm64] 224 + os: [freebsd] 225 + 226 + '@esbuild/freebsd-x64@0.27.3': 227 + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} 228 + engines: {node: '>=18'} 229 + cpu: [x64] 230 + os: [freebsd] 231 + 232 + '@esbuild/linux-arm64@0.27.3': 233 + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} 234 + engines: {node: '>=18'} 235 + cpu: [arm64] 236 + os: [linux] 237 + 238 + '@esbuild/linux-arm@0.27.3': 239 + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} 240 + engines: {node: '>=18'} 241 + cpu: [arm] 242 + os: [linux] 243 + 244 + '@esbuild/linux-ia32@0.27.3': 245 + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} 246 + engines: {node: '>=18'} 247 + cpu: [ia32] 248 + os: [linux] 249 + 250 + '@esbuild/linux-loong64@0.27.3': 251 + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} 252 + engines: {node: '>=18'} 253 + cpu: [loong64] 254 + os: [linux] 255 + 256 + '@esbuild/linux-mips64el@0.27.3': 257 + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} 258 + engines: {node: '>=18'} 259 + cpu: [mips64el] 260 + os: [linux] 261 + 262 + '@esbuild/linux-ppc64@0.27.3': 263 + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} 264 + engines: {node: '>=18'} 265 + cpu: [ppc64] 266 + os: [linux] 267 + 268 + '@esbuild/linux-riscv64@0.27.3': 269 + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} 270 + engines: {node: '>=18'} 271 + cpu: [riscv64] 272 + os: [linux] 273 + 274 + '@esbuild/linux-s390x@0.27.3': 275 + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} 276 + engines: {node: '>=18'} 277 + cpu: [s390x] 278 + os: [linux] 279 + 280 + '@esbuild/linux-x64@0.27.3': 281 + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} 282 + engines: {node: '>=18'} 283 + cpu: [x64] 284 + os: [linux] 285 + 286 + '@esbuild/netbsd-arm64@0.27.3': 287 + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} 288 + engines: {node: '>=18'} 289 + cpu: [arm64] 290 + os: [netbsd] 291 + 292 + '@esbuild/netbsd-x64@0.27.3': 293 + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} 294 + engines: {node: '>=18'} 295 + cpu: [x64] 296 + os: [netbsd] 297 + 298 + '@esbuild/openbsd-arm64@0.27.3': 299 + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} 300 + engines: {node: '>=18'} 301 + cpu: [arm64] 302 + os: [openbsd] 303 + 304 + '@esbuild/openbsd-x64@0.27.3': 305 + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} 306 + engines: {node: '>=18'} 307 + cpu: [x64] 308 + os: [openbsd] 309 + 310 + '@esbuild/openharmony-arm64@0.27.3': 311 + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} 312 + engines: {node: '>=18'} 313 + cpu: [arm64] 314 + os: [openharmony] 315 + 316 + '@esbuild/sunos-x64@0.27.3': 317 + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} 318 + engines: {node: '>=18'} 319 + cpu: [x64] 320 + os: [sunos] 321 + 322 + '@esbuild/win32-arm64@0.27.3': 323 + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} 324 + engines: {node: '>=18'} 325 + cpu: [arm64] 326 + os: [win32] 327 + 328 + '@esbuild/win32-ia32@0.27.3': 329 + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} 330 + engines: {node: '>=18'} 331 + cpu: [ia32] 332 + os: [win32] 333 + 334 + '@esbuild/win32-x64@0.27.3': 335 + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} 336 + engines: {node: '>=18'} 337 + cpu: [x64] 338 + os: [win32] 339 + 340 + '@eslint-community/eslint-utils@4.9.1': 341 + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} 342 + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 343 + peerDependencies: 344 + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 345 + 346 + '@eslint-community/regexpp@4.12.2': 347 + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} 348 + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 349 + 350 + '@eslint/config-array@0.21.1': 351 + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} 352 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 353 + 354 + '@eslint/config-helpers@0.4.2': 355 + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} 356 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 357 + 358 + '@eslint/core@0.17.0': 359 + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} 360 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 361 + 362 + '@eslint/eslintrc@3.3.3': 363 + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} 364 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 365 + 366 + '@eslint/js@9.39.2': 367 + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} 368 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 369 + 370 + '@eslint/object-schema@2.1.7': 371 + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} 372 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 373 + 374 + '@eslint/plugin-kit@0.4.1': 375 + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} 376 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 377 + 378 + '@humanfs/core@0.19.1': 379 + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} 380 + engines: {node: '>=18.18.0'} 381 + 382 + '@humanfs/node@0.16.7': 383 + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} 384 + engines: {node: '>=18.18.0'} 385 + 386 + '@humanwhocodes/module-importer@1.0.1': 387 + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 388 + engines: {node: '>=12.22'} 389 + 390 + '@humanwhocodes/retry@0.4.3': 391 + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} 392 + engines: {node: '>=18.18'} 393 + 394 + '@ipld/dag-cbor@7.0.3': 395 + resolution: {integrity: sha512-1VVh2huHsuohdXC1bGJNE8WR72slZ9XE2T3wbBBq31dm7ZBatmKLLxrB+XAqafxfRFjv08RZmj/W/ZqaM13AuA==} 396 + 397 + '@noble/curves@1.9.7': 398 + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} 399 + engines: {node: ^14.21.3 || >=16} 400 + 401 + '@noble/hashes@1.8.0': 402 + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} 403 + engines: {node: ^14.21.3 || >=16} 404 + 405 + '@noble/secp256k1@3.0.0': 406 + resolution: {integrity: sha512-NJBaR352KyIvj3t6sgT/+7xrNyF9Xk9QlLSIqUGVUYlsnDTAUqY8LOmwpcgEx4AMJXRITQ5XEVHD+mMaPfr3mg==} 407 + 408 + '@pds-moover/lexicons@1.0.1': 409 + resolution: {integrity: sha512-fv5b/DtHM7FEo/JklyF9gdK0ainlb6mWjWrBe6cmSAeg9G/4O2jBlQUOqfOAICY9gOcrCpkOrk9PHgGw//JQ2A==} 410 + 411 + '@rollup/rollup-android-arm-eabi@4.57.1': 412 + resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} 413 + cpu: [arm] 414 + os: [android] 415 + 416 + '@rollup/rollup-android-arm64@4.57.1': 417 + resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==} 418 + cpu: [arm64] 419 + os: [android] 420 + 421 + '@rollup/rollup-darwin-arm64@4.57.1': 422 + resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==} 423 + cpu: [arm64] 424 + os: [darwin] 425 + 426 + '@rollup/rollup-darwin-x64@4.57.1': 427 + resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==} 428 + cpu: [x64] 429 + os: [darwin] 430 + 431 + '@rollup/rollup-freebsd-arm64@4.57.1': 432 + resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==} 433 + cpu: [arm64] 434 + os: [freebsd] 435 + 436 + '@rollup/rollup-freebsd-x64@4.57.1': 437 + resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==} 438 + cpu: [x64] 439 + os: [freebsd] 440 + 441 + '@rollup/rollup-linux-arm-gnueabihf@4.57.1': 442 + resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} 443 + cpu: [arm] 444 + os: [linux] 445 + libc: [glibc] 446 + 447 + '@rollup/rollup-linux-arm-musleabihf@4.57.1': 448 + resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} 449 + cpu: [arm] 450 + os: [linux] 451 + libc: [musl] 452 + 453 + '@rollup/rollup-linux-arm64-gnu@4.57.1': 454 + resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} 455 + cpu: [arm64] 456 + os: [linux] 457 + libc: [glibc] 458 + 459 + '@rollup/rollup-linux-arm64-musl@4.57.1': 460 + resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} 461 + cpu: [arm64] 462 + os: [linux] 463 + libc: [musl] 464 + 465 + '@rollup/rollup-linux-loong64-gnu@4.57.1': 466 + resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} 467 + cpu: [loong64] 468 + os: [linux] 469 + libc: [glibc] 470 + 471 + '@rollup/rollup-linux-loong64-musl@4.57.1': 472 + resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} 473 + cpu: [loong64] 474 + os: [linux] 475 + libc: [musl] 476 + 477 + '@rollup/rollup-linux-ppc64-gnu@4.57.1': 478 + resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} 479 + cpu: [ppc64] 480 + os: [linux] 481 + libc: [glibc] 482 + 483 + '@rollup/rollup-linux-ppc64-musl@4.57.1': 484 + resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} 485 + cpu: [ppc64] 486 + os: [linux] 487 + libc: [musl] 488 + 489 + '@rollup/rollup-linux-riscv64-gnu@4.57.1': 490 + resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} 491 + cpu: [riscv64] 492 + os: [linux] 493 + libc: [glibc] 494 + 495 + '@rollup/rollup-linux-riscv64-musl@4.57.1': 496 + resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} 497 + cpu: [riscv64] 498 + os: [linux] 499 + libc: [musl] 500 + 501 + '@rollup/rollup-linux-s390x-gnu@4.57.1': 502 + resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} 503 + cpu: [s390x] 504 + os: [linux] 505 + libc: [glibc] 506 + 507 + '@rollup/rollup-linux-x64-gnu@4.57.1': 508 + resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} 509 + cpu: [x64] 510 + os: [linux] 511 + libc: [glibc] 512 + 513 + '@rollup/rollup-linux-x64-musl@4.57.1': 514 + resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} 515 + cpu: [x64] 516 + os: [linux] 517 + libc: [musl] 518 + 519 + '@rollup/rollup-openbsd-x64@4.57.1': 520 + resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} 521 + cpu: [x64] 522 + os: [openbsd] 523 + 524 + '@rollup/rollup-openharmony-arm64@4.57.1': 525 + resolution: {integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==} 526 + cpu: [arm64] 527 + os: [openharmony] 528 + 529 + '@rollup/rollup-win32-arm64-msvc@4.57.1': 530 + resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==} 531 + cpu: [arm64] 532 + os: [win32] 533 + 534 + '@rollup/rollup-win32-ia32-msvc@4.57.1': 535 + resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==} 536 + cpu: [ia32] 537 + os: [win32] 538 + 539 + '@rollup/rollup-win32-x64-gnu@4.57.1': 540 + resolution: {integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==} 541 + cpu: [x64] 542 + os: [win32] 543 + 544 + '@rollup/rollup-win32-x64-msvc@4.57.1': 545 + resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==} 546 + cpu: [x64] 547 + os: [win32] 548 + 549 + '@rtsao/scc@1.1.0': 550 + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} 551 + 552 + '@standard-schema/spec@1.1.0': 553 + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} 554 + 555 + '@ts-morph/common@0.28.1': 556 + resolution: {integrity: sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==} 557 + 558 + '@types/estree@1.0.8': 559 + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 560 + 561 + '@types/json-schema@7.0.15': 562 + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 563 + 564 + '@types/json5@0.0.29': 565 + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} 566 + 567 + '@vue/reactivity@3.1.5': 568 + resolution: {integrity: sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==} 569 + 570 + '@vue/shared@3.1.5': 571 + resolution: {integrity: sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==} 572 + 573 + abort-controller@3.0.0: 574 + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} 575 + engines: {node: '>=6.5'} 576 + 577 + acorn-jsx@5.3.2: 578 + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 579 + peerDependencies: 580 + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 581 + 582 + acorn@8.15.0: 583 + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} 584 + engines: {node: '>=0.4.0'} 585 + hasBin: true 586 + 587 + ajv@6.12.6: 588 + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 589 + 590 + alpinejs@3.15.8: 591 + resolution: {integrity: sha512-zxIfCRTBGvF1CCLIOMQOxAyBuqibxSEwS6Jm1a3HGA9rgrJVcjEWlwLcQTVGAWGS8YhAsTRLVrtQ5a5QT9bSSQ==} 592 + 593 + ansi-regex@5.0.1: 594 + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 595 + engines: {node: '>=8'} 596 + 597 + ansi-styles@4.3.0: 598 + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 599 + engines: {node: '>=8'} 600 + 601 + argparse@2.0.1: 602 + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 603 + 604 + array-buffer-byte-length@1.0.2: 605 + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} 606 + engines: {node: '>= 0.4'} 607 + 608 + array-includes@3.1.9: 609 + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} 610 + engines: {node: '>= 0.4'} 611 + 612 + array.prototype.findlastindex@1.2.6: 613 + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} 614 + engines: {node: '>= 0.4'} 615 + 616 + array.prototype.flat@1.3.3: 617 + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} 618 + engines: {node: '>= 0.4'} 619 + 620 + array.prototype.flatmap@1.3.3: 621 + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} 622 + engines: {node: '>= 0.4'} 623 + 624 + arraybuffer.prototype.slice@1.0.4: 625 + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} 626 + engines: {node: '>= 0.4'} 627 + 628 + async-function@1.0.0: 629 + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} 630 + engines: {node: '>= 0.4'} 631 + 632 + atomic-sleep@1.0.0: 633 + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} 634 + engines: {node: '>=8.0.0'} 635 + 636 + available-typed-arrays@1.0.7: 637 + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} 638 + engines: {node: '>= 0.4'} 639 + 640 + await-lock@2.2.2: 641 + resolution: {integrity: sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==} 642 + 643 + balanced-match@1.0.2: 644 + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 645 + 646 + balanced-match@4.0.3: 647 + resolution: {integrity: sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==} 648 + engines: {node: 20 || >=22} 649 + 650 + base64-js@1.5.1: 651 + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} 652 + 653 + brace-expansion@1.1.12: 654 + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} 655 + 656 + brace-expansion@5.0.2: 657 + resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==} 658 + engines: {node: 20 || >=22} 659 + 660 + buffer@6.0.3: 661 + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} 662 + 663 + call-bind-apply-helpers@1.0.2: 664 + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} 665 + engines: {node: '>= 0.4'} 666 + 667 + call-bind@1.0.8: 668 + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} 669 + engines: {node: '>= 0.4'} 670 + 671 + call-bound@1.0.4: 672 + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} 673 + engines: {node: '>= 0.4'} 674 + 675 + callsites@3.1.0: 676 + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 677 + engines: {node: '>=6'} 678 + 679 + cborg@1.10.2: 680 + resolution: {integrity: sha512-b3tFPA9pUr2zCUiCfRd2+wok2/LBSNUMKOuRRok+WlvvAgEt/PlbgPTsZUcwCOs53IJvLgTp0eotwtosE6njug==} 681 + hasBin: true 682 + 683 + chalk@4.1.2: 684 + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 685 + engines: {node: '>=10'} 686 + 687 + cliui@8.0.1: 688 + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} 689 + engines: {node: '>=12'} 690 + 691 + code-block-writer@13.0.3: 692 + resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} 693 + 694 + color-convert@2.0.1: 695 + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 696 + engines: {node: '>=7.0.0'} 697 + 698 + color-name@1.1.4: 699 + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 700 + 701 + concat-map@0.0.1: 702 + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 703 + 704 + core-js@3.48.0: 705 + resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==} 706 + 707 + cross-spawn@7.0.6: 708 + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 709 + engines: {node: '>= 8'} 710 + 711 + data-view-buffer@1.0.2: 712 + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} 713 + engines: {node: '>= 0.4'} 714 + 715 + data-view-byte-length@1.0.2: 716 + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} 717 + engines: {node: '>= 0.4'} 718 + 719 + data-view-byte-offset@1.0.1: 720 + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} 721 + engines: {node: '>= 0.4'} 722 + 723 + debug@3.2.7: 724 + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} 725 + peerDependencies: 726 + supports-color: '*' 727 + peerDependenciesMeta: 728 + supports-color: 729 + optional: true 730 + 731 + debug@4.4.3: 732 + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} 733 + engines: {node: '>=6.0'} 734 + peerDependencies: 735 + supports-color: '*' 736 + peerDependenciesMeta: 737 + supports-color: 738 + optional: true 739 + 740 + deep-is@0.1.4: 741 + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 742 + 743 + define-data-property@1.1.4: 744 + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} 745 + engines: {node: '>= 0.4'} 746 + 747 + define-properties@1.2.1: 748 + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} 749 + engines: {node: '>= 0.4'} 750 + 751 + doctrine@2.1.0: 752 + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} 753 + engines: {node: '>=0.10.0'} 754 + 755 + dunder-proto@1.0.1: 756 + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} 757 + engines: {node: '>= 0.4'} 758 + 759 + emoji-regex@8.0.0: 760 + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 761 + 762 + es-abstract@1.24.1: 763 + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} 764 + engines: {node: '>= 0.4'} 765 + 766 + es-define-property@1.0.1: 767 + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} 768 + engines: {node: '>= 0.4'} 769 + 770 + es-errors@1.3.0: 771 + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 772 + engines: {node: '>= 0.4'} 773 + 774 + es-object-atoms@1.1.1: 775 + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} 776 + engines: {node: '>= 0.4'} 777 + 778 + es-set-tostringtag@2.1.0: 779 + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} 780 + engines: {node: '>= 0.4'} 781 + 782 + es-shim-unscopables@1.1.0: 783 + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} 784 + engines: {node: '>= 0.4'} 785 + 786 + es-to-primitive@1.3.0: 787 + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} 788 + engines: {node: '>= 0.4'} 789 + 790 + esbuild@0.27.3: 791 + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} 792 + engines: {node: '>=18'} 793 + hasBin: true 794 + 795 + escalade@3.2.0: 796 + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} 797 + engines: {node: '>=6'} 798 + 799 + escape-string-regexp@4.0.0: 800 + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 801 + engines: {node: '>=10'} 802 + 803 + eslint-import-resolver-node@0.3.9: 804 + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} 805 + 806 + eslint-module-utils@2.12.1: 807 + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} 808 + engines: {node: '>=4'} 809 + peerDependencies: 810 + '@typescript-eslint/parser': '*' 811 + eslint: '*' 812 + eslint-import-resolver-node: '*' 813 + eslint-import-resolver-typescript: '*' 814 + eslint-import-resolver-webpack: '*' 815 + peerDependenciesMeta: 816 + '@typescript-eslint/parser': 817 + optional: true 818 + eslint: 819 + optional: true 820 + eslint-import-resolver-node: 821 + optional: true 822 + eslint-import-resolver-typescript: 823 + optional: true 824 + eslint-import-resolver-webpack: 825 + optional: true 826 + 827 + eslint-plugin-import@2.32.0: 828 + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} 829 + engines: {node: '>=4'} 830 + peerDependencies: 831 + '@typescript-eslint/parser': '*' 832 + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 833 + peerDependenciesMeta: 834 + '@typescript-eslint/parser': 835 + optional: true 836 + 837 + eslint-scope@8.4.0: 838 + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} 839 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 840 + 841 + eslint-visitor-keys@3.4.3: 842 + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 843 + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 844 + 845 + eslint-visitor-keys@4.2.1: 846 + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} 847 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 848 + 849 + eslint@9.39.2: 850 + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} 851 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 852 + hasBin: true 853 + peerDependencies: 854 + jiti: '*' 855 + peerDependenciesMeta: 856 + jiti: 857 + optional: true 858 + 859 + esm-env@1.2.2: 860 + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} 861 + 862 + espree@10.4.0: 863 + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} 864 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 865 + 866 + esquery@1.7.0: 867 + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} 868 + engines: {node: '>=0.10'} 869 + 870 + esrecurse@4.3.0: 871 + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 872 + engines: {node: '>=4.0'} 873 + 874 + estraverse@5.3.0: 875 + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 876 + engines: {node: '>=4.0'} 877 + 878 + esutils@2.0.3: 879 + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 880 + engines: {node: '>=0.10.0'} 881 + 882 + event-target-shim@5.0.1: 883 + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} 884 + engines: {node: '>=6'} 885 + 886 + events@3.3.0: 887 + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} 888 + engines: {node: '>=0.8.x'} 889 + 890 + fast-deep-equal@3.1.3: 891 + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 892 + 893 + fast-json-stable-stringify@2.1.0: 894 + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 895 + 896 + fast-levenshtein@2.0.6: 897 + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 898 + 899 + fast-redact@3.5.0: 900 + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} 901 + engines: {node: '>=6'} 902 + 903 + fdir@6.5.0: 904 + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} 905 + engines: {node: '>=12.0.0'} 906 + peerDependencies: 907 + picomatch: ^3 || ^4 908 + peerDependenciesMeta: 909 + picomatch: 910 + optional: true 911 + 912 + file-entry-cache@8.0.0: 913 + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 914 + engines: {node: '>=16.0.0'} 915 + 916 + find-up@5.0.0: 917 + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 918 + engines: {node: '>=10'} 919 + 920 + flat-cache@4.0.1: 921 + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} 922 + engines: {node: '>=16'} 923 + 924 + flatted@3.3.3: 925 + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} 926 + 927 + for-each@0.3.5: 928 + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} 929 + engines: {node: '>= 0.4'} 930 + 931 + fsevents@2.3.3: 932 + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 933 + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 934 + os: [darwin] 935 + 936 + function-bind@1.1.2: 937 + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 938 + 939 + function.prototype.name@1.1.8: 940 + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} 941 + engines: {node: '>= 0.4'} 942 + 943 + functions-have-names@1.2.3: 944 + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} 945 + 946 + generator-function@2.0.1: 947 + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} 948 + engines: {node: '>= 0.4'} 949 + 950 + get-caller-file@2.0.5: 951 + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} 952 + engines: {node: 6.* || 8.* || >= 10.*} 953 + 954 + get-intrinsic@1.3.0: 955 + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} 956 + engines: {node: '>= 0.4'} 957 + 958 + get-proto@1.0.1: 959 + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} 960 + engines: {node: '>= 0.4'} 961 + 962 + get-symbol-description@1.1.0: 963 + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} 964 + engines: {node: '>= 0.4'} 965 + 966 + glob-parent@6.0.2: 967 + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 968 + engines: {node: '>=10.13.0'} 969 + 970 + globals@14.0.0: 971 + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} 972 + engines: {node: '>=18'} 973 + 974 + globalthis@1.0.4: 975 + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} 976 + engines: {node: '>= 0.4'} 977 + 978 + gopd@1.2.0: 979 + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} 980 + engines: {node: '>= 0.4'} 981 + 982 + has-bigints@1.1.0: 983 + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} 984 + engines: {node: '>= 0.4'} 985 + 986 + has-flag@4.0.0: 987 + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 988 + engines: {node: '>=8'} 989 + 990 + has-property-descriptors@1.0.2: 991 + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} 992 + 993 + has-proto@1.2.0: 994 + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} 995 + engines: {node: '>= 0.4'} 996 + 997 + has-symbols@1.1.0: 998 + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} 999 + engines: {node: '>= 0.4'} 1000 + 1001 + has-tostringtag@1.0.2: 1002 + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} 1003 + engines: {node: '>= 0.4'} 1004 + 1005 + hasown@2.0.2: 1006 + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 1007 + engines: {node: '>= 0.4'} 1008 + 1009 + ieee754@1.2.1: 1010 + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} 1011 + 1012 + ignore@5.3.2: 1013 + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 1014 + engines: {node: '>= 4'} 1015 + 1016 + import-fresh@3.3.1: 1017 + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} 1018 + engines: {node: '>=6'} 1019 + 1020 + imurmurhash@0.1.4: 1021 + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 1022 + engines: {node: '>=0.8.19'} 1023 + 1024 + internal-slot@1.1.0: 1025 + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} 1026 + engines: {node: '>= 0.4'} 1027 + 1028 + is-array-buffer@3.0.5: 1029 + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} 1030 + engines: {node: '>= 0.4'} 1031 + 1032 + is-async-function@2.1.1: 1033 + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} 1034 + engines: {node: '>= 0.4'} 1035 + 1036 + is-bigint@1.1.0: 1037 + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} 1038 + engines: {node: '>= 0.4'} 1039 + 1040 + is-boolean-object@1.2.2: 1041 + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} 1042 + engines: {node: '>= 0.4'} 1043 + 1044 + is-callable@1.2.7: 1045 + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} 1046 + engines: {node: '>= 0.4'} 1047 + 1048 + is-core-module@2.16.1: 1049 + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} 1050 + engines: {node: '>= 0.4'} 1051 + 1052 + is-data-view@1.0.2: 1053 + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} 1054 + engines: {node: '>= 0.4'} 1055 + 1056 + is-date-object@1.1.0: 1057 + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} 1058 + engines: {node: '>= 0.4'} 1059 + 1060 + is-extglob@2.1.1: 1061 + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 1062 + engines: {node: '>=0.10.0'} 1063 + 1064 + is-finalizationregistry@1.1.1: 1065 + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} 1066 + engines: {node: '>= 0.4'} 1067 + 1068 + is-fullwidth-code-point@3.0.0: 1069 + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 1070 + engines: {node: '>=8'} 1071 + 1072 + is-generator-function@1.1.2: 1073 + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} 1074 + engines: {node: '>= 0.4'} 1075 + 1076 + is-glob@4.0.3: 1077 + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 1078 + engines: {node: '>=0.10.0'} 1079 + 1080 + is-map@2.0.3: 1081 + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} 1082 + engines: {node: '>= 0.4'} 1083 + 1084 + is-negative-zero@2.0.3: 1085 + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} 1086 + engines: {node: '>= 0.4'} 1087 + 1088 + is-number-object@1.1.1: 1089 + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} 1090 + engines: {node: '>= 0.4'} 1091 + 1092 + is-regex@1.2.1: 1093 + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} 1094 + engines: {node: '>= 0.4'} 1095 + 1096 + is-set@2.0.3: 1097 + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} 1098 + engines: {node: '>= 0.4'} 1099 + 1100 + is-shared-array-buffer@1.0.4: 1101 + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} 1102 + engines: {node: '>= 0.4'} 1103 + 1104 + is-string@1.1.1: 1105 + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} 1106 + engines: {node: '>= 0.4'} 1107 + 1108 + is-symbol@1.1.1: 1109 + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} 1110 + engines: {node: '>= 0.4'} 1111 + 1112 + is-typed-array@1.1.15: 1113 + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} 1114 + engines: {node: '>= 0.4'} 1115 + 1116 + is-weakmap@2.0.2: 1117 + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} 1118 + engines: {node: '>= 0.4'} 1119 + 1120 + is-weakref@1.1.1: 1121 + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} 1122 + engines: {node: '>= 0.4'} 1123 + 1124 + is-weakset@2.0.4: 1125 + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} 1126 + engines: {node: '>= 0.4'} 1127 + 1128 + isarray@2.0.5: 1129 + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} 1130 + 1131 + isexe@2.0.0: 1132 + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 1133 + 1134 + iso-datestring-validator@2.2.2: 1135 + resolution: {integrity: sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==} 1136 + 1137 + js-yaml@4.1.1: 1138 + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} 1139 + hasBin: true 1140 + 1141 + json-buffer@3.0.1: 1142 + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 1143 + 1144 + json-schema-traverse@0.4.1: 1145 + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 1146 + 1147 + json-stable-stringify-without-jsonify@1.0.1: 1148 + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 1149 + 1150 + json5@1.0.2: 1151 + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} 1152 + hasBin: true 1153 + 1154 + keyv@4.5.4: 1155 + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 1156 + 1157 + levn@0.4.1: 1158 + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 1159 + engines: {node: '>= 0.8.0'} 1160 + 1161 + locate-path@6.0.0: 1162 + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 1163 + engines: {node: '>=10'} 1164 + 1165 + lodash.merge@4.6.2: 1166 + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 1167 + 1168 + lru-cache@10.4.3: 1169 + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 1170 + 1171 + math-intrinsics@1.1.0: 1172 + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} 1173 + engines: {node: '>= 0.4'} 1174 + 1175 + minimatch@10.2.1: 1176 + resolution: {integrity: sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==} 1177 + engines: {node: 20 || >=22} 1178 + 1179 + minimatch@3.1.2: 1180 + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 1181 + 1182 + minimist@1.2.8: 1183 + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} 1184 + 1185 + ms@2.1.3: 1186 + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 1187 + 1188 + multiformats@9.9.0: 1189 + resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} 1190 + 1191 + nanoid@3.3.11: 1192 + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 1193 + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 1194 + hasBin: true 1195 + 1196 + natural-compare@1.4.0: 1197 + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 1198 + 1199 + object-inspect@1.13.4: 1200 + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} 1201 + engines: {node: '>= 0.4'} 1202 + 1203 + object-keys@1.1.1: 1204 + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} 1205 + engines: {node: '>= 0.4'} 1206 + 1207 + object.assign@4.1.7: 1208 + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} 1209 + engines: {node: '>= 0.4'} 1210 + 1211 + object.fromentries@2.0.8: 1212 + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} 1213 + engines: {node: '>= 0.4'} 1214 + 1215 + object.groupby@1.0.3: 1216 + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} 1217 + engines: {node: '>= 0.4'} 1218 + 1219 + object.values@1.2.1: 1220 + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} 1221 + engines: {node: '>= 0.4'} 1222 + 1223 + on-exit-leak-free@2.1.2: 1224 + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} 1225 + engines: {node: '>=14.0.0'} 1226 + 1227 + optionator@0.9.4: 1228 + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 1229 + engines: {node: '>= 0.8.0'} 1230 + 1231 + own-keys@1.0.1: 1232 + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} 1233 + engines: {node: '>= 0.4'} 1234 + 1235 + p-limit@3.1.0: 1236 + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 1237 + engines: {node: '>=10'} 1238 + 1239 + p-locate@5.0.0: 1240 + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 1241 + engines: {node: '>=10'} 1242 + 1243 + parent-module@1.0.1: 1244 + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 1245 + engines: {node: '>=6'} 1246 + 1247 + path-browserify@1.0.1: 1248 + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} 1249 + 1250 + path-exists@4.0.0: 1251 + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 1252 + engines: {node: '>=8'} 1253 + 1254 + path-key@3.1.1: 1255 + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1256 + engines: {node: '>=8'} 1257 + 1258 + path-parse@1.0.7: 1259 + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 1260 + 1261 + picocolors@1.1.1: 1262 + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 1263 + 1264 + picomatch@2.3.1: 1265 + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 1266 + engines: {node: '>=8.6'} 1267 + 1268 + picomatch@4.0.3: 1269 + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} 1270 + engines: {node: '>=12'} 1271 + 1272 + pino-abstract-transport@1.2.0: 1273 + resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} 1274 + 1275 + pino-std-serializers@6.2.2: 1276 + resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} 1277 + 1278 + pino@8.21.0: 1279 + resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} 1280 + hasBin: true 1281 + 1282 + possible-typed-array-names@1.1.0: 1283 + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} 1284 + engines: {node: '>= 0.4'} 1285 + 1286 + postcss@8.5.6: 1287 + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 1288 + engines: {node: ^10 || ^12 || >=14} 1289 + 1290 + prelude-ls@1.2.1: 1291 + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 1292 + engines: {node: '>= 0.8.0'} 1293 + 1294 + prettier@3.8.1: 1295 + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} 1296 + engines: {node: '>=14'} 1297 + hasBin: true 1298 + 1299 + process-warning@3.0.0: 1300 + resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} 1301 + 1302 + process@0.11.10: 1303 + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} 1304 + engines: {node: '>= 0.6.0'} 1305 + 1306 + punycode@2.3.1: 1307 + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 1308 + engines: {node: '>=6'} 1309 + 1310 + quick-format-unescaped@4.0.4: 1311 + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} 1312 + 1313 + readable-stream@4.7.0: 1314 + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} 1315 + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 1316 + 1317 + real-require@0.2.0: 1318 + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} 1319 + engines: {node: '>= 12.13.0'} 1320 + 1321 + reflect.getprototypeof@1.0.10: 1322 + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} 1323 + engines: {node: '>= 0.4'} 1324 + 1325 + regexp.prototype.flags@1.5.4: 1326 + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} 1327 + engines: {node: '>= 0.4'} 1328 + 1329 + require-directory@2.1.1: 1330 + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} 1331 + engines: {node: '>=0.10.0'} 1332 + 1333 + resolve-from@4.0.0: 1334 + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 1335 + engines: {node: '>=4'} 1336 + 1337 + resolve@1.22.11: 1338 + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} 1339 + engines: {node: '>= 0.4'} 1340 + hasBin: true 1341 + 1342 + rollup@4.57.1: 1343 + resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==} 1344 + engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1345 + hasBin: true 1346 + 1347 + safe-array-concat@1.1.3: 1348 + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} 1349 + engines: {node: '>=0.4'} 1350 + 1351 + safe-buffer@5.2.1: 1352 + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 1353 + 1354 + safe-push-apply@1.0.0: 1355 + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} 1356 + engines: {node: '>= 0.4'} 1357 + 1358 + safe-regex-test@1.1.0: 1359 + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} 1360 + engines: {node: '>= 0.4'} 1361 + 1362 + safe-stable-stringify@2.5.0: 1363 + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} 1364 + engines: {node: '>=10'} 1365 + 1366 + semver@6.3.1: 1367 + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} 1368 + hasBin: true 1369 + 1370 + set-function-length@1.2.2: 1371 + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} 1372 + engines: {node: '>= 0.4'} 1373 + 1374 + set-function-name@2.0.2: 1375 + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} 1376 + engines: {node: '>= 0.4'} 1377 + 1378 + set-proto@1.0.0: 1379 + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} 1380 + engines: {node: '>= 0.4'} 1381 + 1382 + shebang-command@2.0.0: 1383 + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 1384 + engines: {node: '>=8'} 1385 + 1386 + shebang-regex@3.0.0: 1387 + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 1388 + engines: {node: '>=8'} 1389 + 1390 + side-channel-list@1.0.0: 1391 + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} 1392 + engines: {node: '>= 0.4'} 1393 + 1394 + side-channel-map@1.0.1: 1395 + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} 1396 + engines: {node: '>= 0.4'} 1397 + 1398 + side-channel-weakmap@1.0.2: 1399 + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} 1400 + engines: {node: '>= 0.4'} 1401 + 1402 + side-channel@1.1.0: 1403 + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} 1404 + engines: {node: '>= 0.4'} 1405 + 1406 + sonic-boom@3.8.1: 1407 + resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} 1408 + 1409 + source-map-js@1.2.1: 1410 + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 1411 + engines: {node: '>=0.10.0'} 1412 + 1413 + split2@4.2.0: 1414 + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} 1415 + engines: {node: '>= 10.x'} 1416 + 1417 + stop-iteration-iterator@1.1.0: 1418 + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} 1419 + engines: {node: '>= 0.4'} 1420 + 1421 + string-width@4.2.3: 1422 + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 1423 + engines: {node: '>=8'} 1424 + 1425 + string.prototype.trim@1.2.10: 1426 + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} 1427 + engines: {node: '>= 0.4'} 1428 + 1429 + string.prototype.trimend@1.0.9: 1430 + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} 1431 + engines: {node: '>= 0.4'} 1432 + 1433 + string.prototype.trimstart@1.0.8: 1434 + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} 1435 + engines: {node: '>= 0.4'} 1436 + 1437 + string_decoder@1.3.0: 1438 + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} 1439 + 1440 + strip-ansi@6.0.1: 1441 + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 1442 + engines: {node: '>=8'} 1443 + 1444 + strip-bom@3.0.0: 1445 + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} 1446 + engines: {node: '>=4'} 1447 + 1448 + strip-json-comments@3.1.1: 1449 + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 1450 + engines: {node: '>=8'} 1451 + 1452 + supports-color@7.2.0: 1453 + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 1454 + engines: {node: '>=8'} 1455 + 1456 + supports-preserve-symlinks-flag@1.0.0: 1457 + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 1458 + engines: {node: '>= 0.4'} 1459 + 1460 + thread-stream@2.7.0: 1461 + resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} 1462 + 1463 + tinyglobby@0.2.15: 1464 + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} 1465 + engines: {node: '>=12.0.0'} 1466 + 1467 + tlds@1.261.0: 1468 + resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==} 1469 + hasBin: true 1470 + 1471 + ts-morph@27.0.2: 1472 + resolution: {integrity: sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==} 1473 + 1474 + tsconfig-paths@3.15.0: 1475 + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} 1476 + 1477 + tslib@2.8.1: 1478 + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 1479 + 1480 + type-check@0.4.0: 1481 + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 1482 + engines: {node: '>= 0.8.0'} 1483 + 1484 + typed-array-buffer@1.0.3: 1485 + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} 1486 + engines: {node: '>= 0.4'} 1487 + 1488 + typed-array-byte-length@1.0.3: 1489 + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} 1490 + engines: {node: '>= 0.4'} 1491 + 1492 + typed-array-byte-offset@1.0.4: 1493 + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} 1494 + engines: {node: '>= 0.4'} 1495 + 1496 + typed-array-length@1.0.7: 1497 + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} 1498 + engines: {node: '>= 0.4'} 1499 + 1500 + uint8arrays@3.0.0: 1501 + resolution: {integrity: sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==} 1502 + 1503 + unbox-primitive@1.1.0: 1504 + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} 1505 + engines: {node: '>= 0.4'} 1506 + 1507 + unicode-segmenter@0.14.5: 1508 + resolution: {integrity: sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==} 1509 + 1510 + uri-js@4.4.1: 1511 + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 1512 + 1513 + varint@6.0.0: 1514 + resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} 1515 + 1516 + vite-plugin-full-reload@1.2.0: 1517 + resolution: {integrity: sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==} 1518 + 1519 + vite-rs-plugin@1.0.1: 1520 + resolution: {integrity: sha512-YhgflKQIRzuS5x66J3yICoVLH25D2fNU+jThK8tpYl/jGrXeIKT4w5VH1lkLPRC0SjK2ZCm9S6K9Z2ZFVDHjPQ==} 1521 + hasBin: true 1522 + peerDependencies: 1523 + vite: ^5.0.0 1524 + 1525 + vite@7.3.1: 1526 + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} 1527 + engines: {node: ^20.19.0 || >=22.12.0} 1528 + hasBin: true 1529 + peerDependencies: 1530 + '@types/node': ^20.19.0 || >=22.12.0 1531 + jiti: '>=1.21.0' 1532 + less: ^4.0.0 1533 + lightningcss: ^1.21.0 1534 + sass: ^1.70.0 1535 + sass-embedded: ^1.70.0 1536 + stylus: '>=0.54.8' 1537 + sugarss: ^5.0.0 1538 + terser: ^5.16.0 1539 + tsx: ^4.8.1 1540 + yaml: ^2.4.2 1541 + peerDependenciesMeta: 1542 + '@types/node': 1543 + optional: true 1544 + jiti: 1545 + optional: true 1546 + less: 1547 + optional: true 1548 + lightningcss: 1549 + optional: true 1550 + sass: 1551 + optional: true 1552 + sass-embedded: 1553 + optional: true 1554 + stylus: 1555 + optional: true 1556 + sugarss: 1557 + optional: true 1558 + terser: 1559 + optional: true 1560 + tsx: 1561 + optional: true 1562 + yaml: 1563 + optional: true 1564 + 1565 + which-boxed-primitive@1.1.1: 1566 + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} 1567 + engines: {node: '>= 0.4'} 1568 + 1569 + which-builtin-type@1.2.1: 1570 + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} 1571 + engines: {node: '>= 0.4'} 1572 + 1573 + which-collection@1.0.2: 1574 + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} 1575 + engines: {node: '>= 0.4'} 1576 + 1577 + which-typed-array@1.1.20: 1578 + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} 1579 + engines: {node: '>= 0.4'} 1580 + 1581 + which@2.0.2: 1582 + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1583 + engines: {node: '>= 8'} 1584 + hasBin: true 1585 + 1586 + word-wrap@1.2.5: 1587 + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 1588 + engines: {node: '>=0.10.0'} 1589 + 1590 + wrap-ansi@7.0.0: 1591 + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1592 + engines: {node: '>=10'} 1593 + 1594 + y18n@5.0.8: 1595 + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} 1596 + engines: {node: '>=10'} 1597 + 1598 + yargs-parser@21.1.1: 1599 + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} 1600 + engines: {node: '>=12'} 1601 + 1602 + yargs@17.7.2: 1603 + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} 1604 + engines: {node: '>=12'} 1605 + 1606 + yocto-queue@0.1.0: 1607 + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 1608 + engines: {node: '>=10'} 1609 + 1610 + zod@3.25.76: 1611 + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} 1612 + 1613 + snapshots: 1614 + 1615 + '@atcute/cbor@2.3.0': 1616 + dependencies: 1617 + '@atcute/cid': 2.4.0 1618 + '@atcute/multibase': 1.1.7 1619 + '@atcute/uint8array': 1.1.0 1620 + 1621 + '@atcute/cid@2.4.0': 1622 + dependencies: 1623 + '@atcute/multibase': 1.1.7 1624 + '@atcute/uint8array': 1.1.0 1625 + 1626 + '@atcute/client@4.2.1': 1627 + dependencies: 1628 + '@atcute/identity': 1.1.3 1629 + '@atcute/lexicons': 1.2.7 1630 + 1631 + '@atcute/crypto@2.3.0': 1632 + dependencies: 1633 + '@atcute/multibase': 1.1.7 1634 + '@atcute/uint8array': 1.1.0 1635 + '@noble/secp256k1': 3.0.0 1636 + 1637 + '@atcute/did-plc@0.1.7': 1638 + dependencies: 1639 + '@atcute/cbor': 2.3.0 1640 + '@atcute/cid': 2.4.0 1641 + '@atcute/crypto': 2.3.0 1642 + '@atcute/identity': 1.1.3 1643 + '@atcute/lexicons': 1.2.7 1644 + '@atcute/multibase': 1.1.7 1645 + '@atcute/uint8array': 1.1.0 1646 + '@badrap/valita': 0.4.6 1647 + 1648 + '@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.3)': 1649 + dependencies: 1650 + '@atcute/identity': 1.1.3 1651 + '@atcute/lexicons': 1.2.7 1652 + '@atcute/util-fetch': 1.0.5 1653 + '@badrap/valita': 0.4.6 1654 + 1655 + '@atcute/identity@1.1.3': 1656 + dependencies: 1657 + '@atcute/lexicons': 1.2.7 1658 + '@badrap/valita': 0.4.6 1659 + 1660 + '@atcute/lexicons@1.2.7': 1661 + dependencies: 1662 + '@atcute/uint8array': 1.1.0 1663 + '@atcute/util-text': 1.1.0 1664 + '@standard-schema/spec': 1.1.0 1665 + esm-env: 1.2.2 1666 + 1667 + '@atcute/multibase@1.1.7': 1668 + dependencies: 1669 + '@atcute/uint8array': 1.1.0 1670 + 1671 + '@atcute/uint8array@1.1.0': {} 1672 + 1673 + '@atcute/util-fetch@1.0.5': 1674 + dependencies: 1675 + '@badrap/valita': 0.4.6 1676 + 1677 + '@atcute/util-text@1.1.0': 1678 + dependencies: 1679 + unicode-segmenter: 0.14.5 1680 + 1681 + '@atproto-labs/did-resolver@0.2.6': 1682 + dependencies: 1683 + '@atproto-labs/fetch': 0.2.3 1684 + '@atproto-labs/pipe': 0.1.1 1685 + '@atproto-labs/simple-store': 0.3.0 1686 + '@atproto-labs/simple-store-memory': 0.1.4 1687 + '@atproto/did': 0.3.0 1688 + zod: 3.25.76 1689 + 1690 + '@atproto-labs/fetch@0.2.3': 1691 + dependencies: 1692 + '@atproto-labs/pipe': 0.1.1 1693 + 1694 + '@atproto-labs/pipe@0.1.1': {} 1695 + 1696 + '@atproto-labs/simple-store-memory@0.1.4': 1697 + dependencies: 1698 + '@atproto-labs/simple-store': 0.3.0 1699 + lru-cache: 10.4.3 1700 + 1701 + '@atproto-labs/simple-store@0.3.0': {} 1702 + 1703 + '@atproto/api@0.16.11': 1704 + dependencies: 1705 + '@atproto/common-web': 0.4.16 1706 + '@atproto/lexicon': 0.5.2 1707 + '@atproto/syntax': 0.4.3 1708 + '@atproto/xrpc': 0.7.7 1709 + await-lock: 2.2.2 1710 + multiformats: 9.9.0 1711 + tlds: 1.261.0 1712 + zod: 3.25.76 1713 + 1714 + '@atproto/common-web@0.4.16': 1715 + dependencies: 1716 + '@atproto/lex-data': 0.0.11 1717 + '@atproto/lex-json': 0.0.11 1718 + '@atproto/syntax': 0.4.3 1719 + zod: 3.25.76 1720 + 1721 + '@atproto/common@0.5.11': 1722 + dependencies: 1723 + '@atproto/common-web': 0.4.16 1724 + '@atproto/lex-cbor': 0.0.11 1725 + '@atproto/lex-data': 0.0.11 1726 + iso-datestring-validator: 2.2.2 1727 + multiformats: 9.9.0 1728 + pino: 8.21.0 1729 + 1730 + '@atproto/crypto@0.4.5': 1731 + dependencies: 1732 + '@noble/curves': 1.9.7 1733 + '@noble/hashes': 1.8.0 1734 + uint8arrays: 3.0.0 1735 + 1736 + '@atproto/did@0.3.0': 1737 + dependencies: 1738 + zod: 3.25.76 1739 + 1740 + '@atproto/lex-builder@0.0.15': 1741 + dependencies: 1742 + '@atproto/lex-document': 0.0.13 1743 + '@atproto/lex-schema': 0.0.12 1744 + prettier: 3.8.1 1745 + ts-morph: 27.0.2 1746 + tslib: 2.8.1 1747 + 1748 + '@atproto/lex-cbor@0.0.11': 1749 + dependencies: 1750 + '@atproto/lex-data': 0.0.11 1751 + tslib: 2.8.1 1752 + 1753 + '@atproto/lex-client@0.0.12': 1754 + dependencies: 1755 + '@atproto/lex-data': 0.0.11 1756 + '@atproto/lex-json': 0.0.11 1757 + '@atproto/lex-schema': 0.0.12 1758 + tslib: 2.8.1 1759 + 1760 + '@atproto/lex-data@0.0.11': 1761 + dependencies: 1762 + multiformats: 9.9.0 1763 + tslib: 2.8.1 1764 + uint8arrays: 3.0.0 1765 + unicode-segmenter: 0.14.5 1766 + 1767 + '@atproto/lex-document@0.0.13': 1768 + dependencies: 1769 + '@atproto/lex-schema': 0.0.12 1770 + core-js: 3.48.0 1771 + tslib: 2.8.1 1772 + 1773 + '@atproto/lex-installer@0.0.16': 1774 + dependencies: 1775 + '@atproto/lex-builder': 0.0.15 1776 + '@atproto/lex-cbor': 0.0.11 1777 + '@atproto/lex-data': 0.0.11 1778 + '@atproto/lex-document': 0.0.13 1779 + '@atproto/lex-resolver': 0.0.14 1780 + '@atproto/lex-schema': 0.0.12 1781 + '@atproto/syntax': 0.4.3 1782 + tslib: 2.8.1 1783 + 1784 + '@atproto/lex-json@0.0.11': 1785 + dependencies: 1786 + '@atproto/lex-data': 0.0.11 1787 + tslib: 2.8.1 1788 + 1789 + '@atproto/lex-resolver@0.0.14': 1790 + dependencies: 1791 + '@atproto-labs/did-resolver': 0.2.6 1792 + '@atproto/crypto': 0.4.5 1793 + '@atproto/lex-client': 0.0.12 1794 + '@atproto/lex-data': 0.0.11 1795 + '@atproto/lex-document': 0.0.13 1796 + '@atproto/lex-schema': 0.0.12 1797 + '@atproto/repo': 0.8.12 1798 + '@atproto/syntax': 0.4.3 1799 + tslib: 2.8.1 1800 + 1801 + '@atproto/lex-schema@0.0.12': 1802 + dependencies: 1803 + '@atproto/lex-data': 0.0.11 1804 + '@atproto/syntax': 0.4.3 1805 + tslib: 2.8.1 1806 + 1807 + '@atproto/lex@0.0.16': 1808 + dependencies: 1809 + '@atproto/lex-builder': 0.0.15 1810 + '@atproto/lex-client': 0.0.12 1811 + '@atproto/lex-data': 0.0.11 1812 + '@atproto/lex-installer': 0.0.16 1813 + '@atproto/lex-json': 0.0.11 1814 + '@atproto/lex-schema': 0.0.12 1815 + tslib: 2.8.1 1816 + yargs: 17.7.2 1817 + 1818 + '@atproto/lexicon@0.5.2': 1819 + dependencies: 1820 + '@atproto/common-web': 0.4.16 1821 + '@atproto/syntax': 0.4.3 1822 + iso-datestring-validator: 2.2.2 1823 + multiformats: 9.9.0 1824 + zod: 3.25.76 1825 + 1826 + '@atproto/lexicon@0.6.1': 1827 + dependencies: 1828 + '@atproto/common-web': 0.4.16 1829 + '@atproto/syntax': 0.4.3 1830 + iso-datestring-validator: 2.2.2 1831 + multiformats: 9.9.0 1832 + zod: 3.25.76 1833 + 1834 + '@atproto/repo@0.8.12': 1835 + dependencies: 1836 + '@atproto/common': 0.5.11 1837 + '@atproto/common-web': 0.4.16 1838 + '@atproto/crypto': 0.4.5 1839 + '@atproto/lexicon': 0.6.1 1840 + '@ipld/dag-cbor': 7.0.3 1841 + multiformats: 9.9.0 1842 + uint8arrays: 3.0.0 1843 + varint: 6.0.0 1844 + zod: 3.25.76 1845 + 1846 + '@atproto/syntax@0.4.3': 1847 + dependencies: 1848 + tslib: 2.8.1 1849 + 1850 + '@atproto/xrpc@0.7.7': 1851 + dependencies: 1852 + '@atproto/lexicon': 0.6.1 1853 + zod: 3.25.76 1854 + 1855 + '@badrap/valita@0.4.6': {} 1856 + 1857 + '@esbuild/aix-ppc64@0.27.3': 1858 + optional: true 1859 + 1860 + '@esbuild/android-arm64@0.27.3': 1861 + optional: true 1862 + 1863 + '@esbuild/android-arm@0.27.3': 1864 + optional: true 1865 + 1866 + '@esbuild/android-x64@0.27.3': 1867 + optional: true 1868 + 1869 + '@esbuild/darwin-arm64@0.27.3': 1870 + optional: true 1871 + 1872 + '@esbuild/darwin-x64@0.27.3': 1873 + optional: true 1874 + 1875 + '@esbuild/freebsd-arm64@0.27.3': 1876 + optional: true 1877 + 1878 + '@esbuild/freebsd-x64@0.27.3': 1879 + optional: true 1880 + 1881 + '@esbuild/linux-arm64@0.27.3': 1882 + optional: true 1883 + 1884 + '@esbuild/linux-arm@0.27.3': 1885 + optional: true 1886 + 1887 + '@esbuild/linux-ia32@0.27.3': 1888 + optional: true 1889 + 1890 + '@esbuild/linux-loong64@0.27.3': 1891 + optional: true 1892 + 1893 + '@esbuild/linux-mips64el@0.27.3': 1894 + optional: true 1895 + 1896 + '@esbuild/linux-ppc64@0.27.3': 1897 + optional: true 1898 + 1899 + '@esbuild/linux-riscv64@0.27.3': 1900 + optional: true 1901 + 1902 + '@esbuild/linux-s390x@0.27.3': 1903 + optional: true 1904 + 1905 + '@esbuild/linux-x64@0.27.3': 1906 + optional: true 1907 + 1908 + '@esbuild/netbsd-arm64@0.27.3': 1909 + optional: true 1910 + 1911 + '@esbuild/netbsd-x64@0.27.3': 1912 + optional: true 1913 + 1914 + '@esbuild/openbsd-arm64@0.27.3': 1915 + optional: true 1916 + 1917 + '@esbuild/openbsd-x64@0.27.3': 1918 + optional: true 1919 + 1920 + '@esbuild/openharmony-arm64@0.27.3': 1921 + optional: true 1922 + 1923 + '@esbuild/sunos-x64@0.27.3': 1924 + optional: true 1925 + 1926 + '@esbuild/win32-arm64@0.27.3': 1927 + optional: true 1928 + 1929 + '@esbuild/win32-ia32@0.27.3': 1930 + optional: true 1931 + 1932 + '@esbuild/win32-x64@0.27.3': 1933 + optional: true 1934 + 1935 + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': 1936 + dependencies: 1937 + eslint: 9.39.2 1938 + eslint-visitor-keys: 3.4.3 1939 + 1940 + '@eslint-community/regexpp@4.12.2': {} 1941 + 1942 + '@eslint/config-array@0.21.1': 1943 + dependencies: 1944 + '@eslint/object-schema': 2.1.7 1945 + debug: 4.4.3 1946 + minimatch: 3.1.2 1947 + transitivePeerDependencies: 1948 + - supports-color 1949 + 1950 + '@eslint/config-helpers@0.4.2': 1951 + dependencies: 1952 + '@eslint/core': 0.17.0 1953 + 1954 + '@eslint/core@0.17.0': 1955 + dependencies: 1956 + '@types/json-schema': 7.0.15 1957 + 1958 + '@eslint/eslintrc@3.3.3': 1959 + dependencies: 1960 + ajv: 6.12.6 1961 + debug: 4.4.3 1962 + espree: 10.4.0 1963 + globals: 14.0.0 1964 + ignore: 5.3.2 1965 + import-fresh: 3.3.1 1966 + js-yaml: 4.1.1 1967 + minimatch: 3.1.2 1968 + strip-json-comments: 3.1.1 1969 + transitivePeerDependencies: 1970 + - supports-color 1971 + 1972 + '@eslint/js@9.39.2': {} 1973 + 1974 + '@eslint/object-schema@2.1.7': {} 1975 + 1976 + '@eslint/plugin-kit@0.4.1': 1977 + dependencies: 1978 + '@eslint/core': 0.17.0 1979 + levn: 0.4.1 1980 + 1981 + '@humanfs/core@0.19.1': {} 1982 + 1983 + '@humanfs/node@0.16.7': 1984 + dependencies: 1985 + '@humanfs/core': 0.19.1 1986 + '@humanwhocodes/retry': 0.4.3 1987 + 1988 + '@humanwhocodes/module-importer@1.0.1': {} 1989 + 1990 + '@humanwhocodes/retry@0.4.3': {} 1991 + 1992 + '@ipld/dag-cbor@7.0.3': 1993 + dependencies: 1994 + cborg: 1.10.2 1995 + multiformats: 9.9.0 1996 + 1997 + '@noble/curves@1.9.7': 1998 + dependencies: 1999 + '@noble/hashes': 1.8.0 2000 + 2001 + '@noble/hashes@1.8.0': {} 2002 + 2003 + '@noble/secp256k1@3.0.0': {} 2004 + 2005 + '@pds-moover/lexicons@1.0.1': 2006 + dependencies: 2007 + '@atproto/lexicon': 0.5.2 2008 + '@atproto/xrpc': 0.7.7 2009 + 2010 + '@rollup/rollup-android-arm-eabi@4.57.1': 2011 + optional: true 2012 + 2013 + '@rollup/rollup-android-arm64@4.57.1': 2014 + optional: true 2015 + 2016 + '@rollup/rollup-darwin-arm64@4.57.1': 2017 + optional: true 2018 + 2019 + '@rollup/rollup-darwin-x64@4.57.1': 2020 + optional: true 2021 + 2022 + '@rollup/rollup-freebsd-arm64@4.57.1': 2023 + optional: true 2024 + 2025 + '@rollup/rollup-freebsd-x64@4.57.1': 2026 + optional: true 2027 + 2028 + '@rollup/rollup-linux-arm-gnueabihf@4.57.1': 2029 + optional: true 2030 + 2031 + '@rollup/rollup-linux-arm-musleabihf@4.57.1': 2032 + optional: true 2033 + 2034 + '@rollup/rollup-linux-arm64-gnu@4.57.1': 2035 + optional: true 2036 + 2037 + '@rollup/rollup-linux-arm64-musl@4.57.1': 2038 + optional: true 2039 + 2040 + '@rollup/rollup-linux-loong64-gnu@4.57.1': 2041 + optional: true 2042 + 2043 + '@rollup/rollup-linux-loong64-musl@4.57.1': 2044 + optional: true 2045 + 2046 + '@rollup/rollup-linux-ppc64-gnu@4.57.1': 2047 + optional: true 2048 + 2049 + '@rollup/rollup-linux-ppc64-musl@4.57.1': 2050 + optional: true 2051 + 2052 + '@rollup/rollup-linux-riscv64-gnu@4.57.1': 2053 + optional: true 2054 + 2055 + '@rollup/rollup-linux-riscv64-musl@4.57.1': 2056 + optional: true 2057 + 2058 + '@rollup/rollup-linux-s390x-gnu@4.57.1': 2059 + optional: true 2060 + 2061 + '@rollup/rollup-linux-x64-gnu@4.57.1': 2062 + optional: true 2063 + 2064 + '@rollup/rollup-linux-x64-musl@4.57.1': 2065 + optional: true 2066 + 2067 + '@rollup/rollup-openbsd-x64@4.57.1': 2068 + optional: true 2069 + 2070 + '@rollup/rollup-openharmony-arm64@4.57.1': 2071 + optional: true 2072 + 2073 + '@rollup/rollup-win32-arm64-msvc@4.57.1': 2074 + optional: true 2075 + 2076 + '@rollup/rollup-win32-ia32-msvc@4.57.1': 2077 + optional: true 2078 + 2079 + '@rollup/rollup-win32-x64-gnu@4.57.1': 2080 + optional: true 2081 + 2082 + '@rollup/rollup-win32-x64-msvc@4.57.1': 2083 + optional: true 2084 + 2085 + '@rtsao/scc@1.1.0': {} 2086 + 2087 + '@standard-schema/spec@1.1.0': {} 2088 + 2089 + '@ts-morph/common@0.28.1': 2090 + dependencies: 2091 + minimatch: 10.2.1 2092 + path-browserify: 1.0.1 2093 + tinyglobby: 0.2.15 2094 + 2095 + '@types/estree@1.0.8': {} 2096 + 2097 + '@types/json-schema@7.0.15': {} 2098 + 2099 + '@types/json5@0.0.29': {} 2100 + 2101 + '@vue/reactivity@3.1.5': 2102 + dependencies: 2103 + '@vue/shared': 3.1.5 2104 + 2105 + '@vue/shared@3.1.5': {} 2106 + 2107 + abort-controller@3.0.0: 2108 + dependencies: 2109 + event-target-shim: 5.0.1 2110 + 2111 + acorn-jsx@5.3.2(acorn@8.15.0): 2112 + dependencies: 2113 + acorn: 8.15.0 2114 + 2115 + acorn@8.15.0: {} 2116 + 2117 + ajv@6.12.6: 2118 + dependencies: 2119 + fast-deep-equal: 3.1.3 2120 + fast-json-stable-stringify: 2.1.0 2121 + json-schema-traverse: 0.4.1 2122 + uri-js: 4.4.1 2123 + 2124 + alpinejs@3.15.8: 2125 + dependencies: 2126 + '@vue/reactivity': 3.1.5 2127 + 2128 + ansi-regex@5.0.1: {} 2129 + 2130 + ansi-styles@4.3.0: 2131 + dependencies: 2132 + color-convert: 2.0.1 2133 + 2134 + argparse@2.0.1: {} 2135 + 2136 + array-buffer-byte-length@1.0.2: 2137 + dependencies: 2138 + call-bound: 1.0.4 2139 + is-array-buffer: 3.0.5 2140 + 2141 + array-includes@3.1.9: 2142 + dependencies: 2143 + call-bind: 1.0.8 2144 + call-bound: 1.0.4 2145 + define-properties: 1.2.1 2146 + es-abstract: 1.24.1 2147 + es-object-atoms: 1.1.1 2148 + get-intrinsic: 1.3.0 2149 + is-string: 1.1.1 2150 + math-intrinsics: 1.1.0 2151 + 2152 + array.prototype.findlastindex@1.2.6: 2153 + dependencies: 2154 + call-bind: 1.0.8 2155 + call-bound: 1.0.4 2156 + define-properties: 1.2.1 2157 + es-abstract: 1.24.1 2158 + es-errors: 1.3.0 2159 + es-object-atoms: 1.1.1 2160 + es-shim-unscopables: 1.1.0 2161 + 2162 + array.prototype.flat@1.3.3: 2163 + dependencies: 2164 + call-bind: 1.0.8 2165 + define-properties: 1.2.1 2166 + es-abstract: 1.24.1 2167 + es-shim-unscopables: 1.1.0 2168 + 2169 + array.prototype.flatmap@1.3.3: 2170 + dependencies: 2171 + call-bind: 1.0.8 2172 + define-properties: 1.2.1 2173 + es-abstract: 1.24.1 2174 + es-shim-unscopables: 1.1.0 2175 + 2176 + arraybuffer.prototype.slice@1.0.4: 2177 + dependencies: 2178 + array-buffer-byte-length: 1.0.2 2179 + call-bind: 1.0.8 2180 + define-properties: 1.2.1 2181 + es-abstract: 1.24.1 2182 + es-errors: 1.3.0 2183 + get-intrinsic: 1.3.0 2184 + is-array-buffer: 3.0.5 2185 + 2186 + async-function@1.0.0: {} 2187 + 2188 + atomic-sleep@1.0.0: {} 2189 + 2190 + available-typed-arrays@1.0.7: 2191 + dependencies: 2192 + possible-typed-array-names: 1.1.0 2193 + 2194 + await-lock@2.2.2: {} 2195 + 2196 + balanced-match@1.0.2: {} 2197 + 2198 + balanced-match@4.0.3: {} 2199 + 2200 + base64-js@1.5.1: {} 2201 + 2202 + brace-expansion@1.1.12: 2203 + dependencies: 2204 + balanced-match: 1.0.2 2205 + concat-map: 0.0.1 2206 + 2207 + brace-expansion@5.0.2: 2208 + dependencies: 2209 + balanced-match: 4.0.3 2210 + 2211 + buffer@6.0.3: 2212 + dependencies: 2213 + base64-js: 1.5.1 2214 + ieee754: 1.2.1 2215 + 2216 + call-bind-apply-helpers@1.0.2: 2217 + dependencies: 2218 + es-errors: 1.3.0 2219 + function-bind: 1.1.2 2220 + 2221 + call-bind@1.0.8: 2222 + dependencies: 2223 + call-bind-apply-helpers: 1.0.2 2224 + es-define-property: 1.0.1 2225 + get-intrinsic: 1.3.0 2226 + set-function-length: 1.2.2 2227 + 2228 + call-bound@1.0.4: 2229 + dependencies: 2230 + call-bind-apply-helpers: 1.0.2 2231 + get-intrinsic: 1.3.0 2232 + 2233 + callsites@3.1.0: {} 2234 + 2235 + cborg@1.10.2: {} 2236 + 2237 + chalk@4.1.2: 2238 + dependencies: 2239 + ansi-styles: 4.3.0 2240 + supports-color: 7.2.0 2241 + 2242 + cliui@8.0.1: 2243 + dependencies: 2244 + string-width: 4.2.3 2245 + strip-ansi: 6.0.1 2246 + wrap-ansi: 7.0.0 2247 + 2248 + code-block-writer@13.0.3: {} 2249 + 2250 + color-convert@2.0.1: 2251 + dependencies: 2252 + color-name: 1.1.4 2253 + 2254 + color-name@1.1.4: {} 2255 + 2256 + concat-map@0.0.1: {} 2257 + 2258 + core-js@3.48.0: {} 2259 + 2260 + cross-spawn@7.0.6: 2261 + dependencies: 2262 + path-key: 3.1.1 2263 + shebang-command: 2.0.0 2264 + which: 2.0.2 2265 + 2266 + data-view-buffer@1.0.2: 2267 + dependencies: 2268 + call-bound: 1.0.4 2269 + es-errors: 1.3.0 2270 + is-data-view: 1.0.2 2271 + 2272 + data-view-byte-length@1.0.2: 2273 + dependencies: 2274 + call-bound: 1.0.4 2275 + es-errors: 1.3.0 2276 + is-data-view: 1.0.2 2277 + 2278 + data-view-byte-offset@1.0.1: 2279 + dependencies: 2280 + call-bound: 1.0.4 2281 + es-errors: 1.3.0 2282 + is-data-view: 1.0.2 2283 + 2284 + debug@3.2.7: 2285 + dependencies: 2286 + ms: 2.1.3 2287 + 2288 + debug@4.4.3: 2289 + dependencies: 2290 + ms: 2.1.3 2291 + 2292 + deep-is@0.1.4: {} 2293 + 2294 + define-data-property@1.1.4: 2295 + dependencies: 2296 + es-define-property: 1.0.1 2297 + es-errors: 1.3.0 2298 + gopd: 1.2.0 2299 + 2300 + define-properties@1.2.1: 2301 + dependencies: 2302 + define-data-property: 1.1.4 2303 + has-property-descriptors: 1.0.2 2304 + object-keys: 1.1.1 2305 + 2306 + doctrine@2.1.0: 2307 + dependencies: 2308 + esutils: 2.0.3 2309 + 2310 + dunder-proto@1.0.1: 2311 + dependencies: 2312 + call-bind-apply-helpers: 1.0.2 2313 + es-errors: 1.3.0 2314 + gopd: 1.2.0 2315 + 2316 + emoji-regex@8.0.0: {} 2317 + 2318 + es-abstract@1.24.1: 2319 + dependencies: 2320 + array-buffer-byte-length: 1.0.2 2321 + arraybuffer.prototype.slice: 1.0.4 2322 + available-typed-arrays: 1.0.7 2323 + call-bind: 1.0.8 2324 + call-bound: 1.0.4 2325 + data-view-buffer: 1.0.2 2326 + data-view-byte-length: 1.0.2 2327 + data-view-byte-offset: 1.0.1 2328 + es-define-property: 1.0.1 2329 + es-errors: 1.3.0 2330 + es-object-atoms: 1.1.1 2331 + es-set-tostringtag: 2.1.0 2332 + es-to-primitive: 1.3.0 2333 + function.prototype.name: 1.1.8 2334 + get-intrinsic: 1.3.0 2335 + get-proto: 1.0.1 2336 + get-symbol-description: 1.1.0 2337 + globalthis: 1.0.4 2338 + gopd: 1.2.0 2339 + has-property-descriptors: 1.0.2 2340 + has-proto: 1.2.0 2341 + has-symbols: 1.1.0 2342 + hasown: 2.0.2 2343 + internal-slot: 1.1.0 2344 + is-array-buffer: 3.0.5 2345 + is-callable: 1.2.7 2346 + is-data-view: 1.0.2 2347 + is-negative-zero: 2.0.3 2348 + is-regex: 1.2.1 2349 + is-set: 2.0.3 2350 + is-shared-array-buffer: 1.0.4 2351 + is-string: 1.1.1 2352 + is-typed-array: 1.1.15 2353 + is-weakref: 1.1.1 2354 + math-intrinsics: 1.1.0 2355 + object-inspect: 1.13.4 2356 + object-keys: 1.1.1 2357 + object.assign: 4.1.7 2358 + own-keys: 1.0.1 2359 + regexp.prototype.flags: 1.5.4 2360 + safe-array-concat: 1.1.3 2361 + safe-push-apply: 1.0.0 2362 + safe-regex-test: 1.1.0 2363 + set-proto: 1.0.0 2364 + stop-iteration-iterator: 1.1.0 2365 + string.prototype.trim: 1.2.10 2366 + string.prototype.trimend: 1.0.9 2367 + string.prototype.trimstart: 1.0.8 2368 + typed-array-buffer: 1.0.3 2369 + typed-array-byte-length: 1.0.3 2370 + typed-array-byte-offset: 1.0.4 2371 + typed-array-length: 1.0.7 2372 + unbox-primitive: 1.1.0 2373 + which-typed-array: 1.1.20 2374 + 2375 + es-define-property@1.0.1: {} 2376 + 2377 + es-errors@1.3.0: {} 2378 + 2379 + es-object-atoms@1.1.1: 2380 + dependencies: 2381 + es-errors: 1.3.0 2382 + 2383 + es-set-tostringtag@2.1.0: 2384 + dependencies: 2385 + es-errors: 1.3.0 2386 + get-intrinsic: 1.3.0 2387 + has-tostringtag: 1.0.2 2388 + hasown: 2.0.2 2389 + 2390 + es-shim-unscopables@1.1.0: 2391 + dependencies: 2392 + hasown: 2.0.2 2393 + 2394 + es-to-primitive@1.3.0: 2395 + dependencies: 2396 + is-callable: 1.2.7 2397 + is-date-object: 1.1.0 2398 + is-symbol: 1.1.1 2399 + 2400 + esbuild@0.27.3: 2401 + optionalDependencies: 2402 + '@esbuild/aix-ppc64': 0.27.3 2403 + '@esbuild/android-arm': 0.27.3 2404 + '@esbuild/android-arm64': 0.27.3 2405 + '@esbuild/android-x64': 0.27.3 2406 + '@esbuild/darwin-arm64': 0.27.3 2407 + '@esbuild/darwin-x64': 0.27.3 2408 + '@esbuild/freebsd-arm64': 0.27.3 2409 + '@esbuild/freebsd-x64': 0.27.3 2410 + '@esbuild/linux-arm': 0.27.3 2411 + '@esbuild/linux-arm64': 0.27.3 2412 + '@esbuild/linux-ia32': 0.27.3 2413 + '@esbuild/linux-loong64': 0.27.3 2414 + '@esbuild/linux-mips64el': 0.27.3 2415 + '@esbuild/linux-ppc64': 0.27.3 2416 + '@esbuild/linux-riscv64': 0.27.3 2417 + '@esbuild/linux-s390x': 0.27.3 2418 + '@esbuild/linux-x64': 0.27.3 2419 + '@esbuild/netbsd-arm64': 0.27.3 2420 + '@esbuild/netbsd-x64': 0.27.3 2421 + '@esbuild/openbsd-arm64': 0.27.3 2422 + '@esbuild/openbsd-x64': 0.27.3 2423 + '@esbuild/openharmony-arm64': 0.27.3 2424 + '@esbuild/sunos-x64': 0.27.3 2425 + '@esbuild/win32-arm64': 0.27.3 2426 + '@esbuild/win32-ia32': 0.27.3 2427 + '@esbuild/win32-x64': 0.27.3 2428 + 2429 + escalade@3.2.0: {} 2430 + 2431 + escape-string-regexp@4.0.0: {} 2432 + 2433 + eslint-import-resolver-node@0.3.9: 2434 + dependencies: 2435 + debug: 3.2.7 2436 + is-core-module: 2.16.1 2437 + resolve: 1.22.11 2438 + transitivePeerDependencies: 2439 + - supports-color 2440 + 2441 + eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint@9.39.2): 2442 + dependencies: 2443 + debug: 3.2.7 2444 + optionalDependencies: 2445 + eslint: 9.39.2 2446 + eslint-import-resolver-node: 0.3.9 2447 + transitivePeerDependencies: 2448 + - supports-color 2449 + 2450 + eslint-plugin-import@2.32.0(eslint@9.39.2): 2451 + dependencies: 2452 + '@rtsao/scc': 1.1.0 2453 + array-includes: 3.1.9 2454 + array.prototype.findlastindex: 1.2.6 2455 + array.prototype.flat: 1.3.3 2456 + array.prototype.flatmap: 1.3.3 2457 + debug: 3.2.7 2458 + doctrine: 2.1.0 2459 + eslint: 9.39.2 2460 + eslint-import-resolver-node: 0.3.9 2461 + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint@9.39.2) 2462 + hasown: 2.0.2 2463 + is-core-module: 2.16.1 2464 + is-glob: 4.0.3 2465 + minimatch: 3.1.2 2466 + object.fromentries: 2.0.8 2467 + object.groupby: 1.0.3 2468 + object.values: 1.2.1 2469 + semver: 6.3.1 2470 + string.prototype.trimend: 1.0.9 2471 + tsconfig-paths: 3.15.0 2472 + transitivePeerDependencies: 2473 + - eslint-import-resolver-typescript 2474 + - eslint-import-resolver-webpack 2475 + - supports-color 2476 + 2477 + eslint-scope@8.4.0: 2478 + dependencies: 2479 + esrecurse: 4.3.0 2480 + estraverse: 5.3.0 2481 + 2482 + eslint-visitor-keys@3.4.3: {} 2483 + 2484 + eslint-visitor-keys@4.2.1: {} 2485 + 2486 + eslint@9.39.2: 2487 + dependencies: 2488 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) 2489 + '@eslint-community/regexpp': 4.12.2 2490 + '@eslint/config-array': 0.21.1 2491 + '@eslint/config-helpers': 0.4.2 2492 + '@eslint/core': 0.17.0 2493 + '@eslint/eslintrc': 3.3.3 2494 + '@eslint/js': 9.39.2 2495 + '@eslint/plugin-kit': 0.4.1 2496 + '@humanfs/node': 0.16.7 2497 + '@humanwhocodes/module-importer': 1.0.1 2498 + '@humanwhocodes/retry': 0.4.3 2499 + '@types/estree': 1.0.8 2500 + ajv: 6.12.6 2501 + chalk: 4.1.2 2502 + cross-spawn: 7.0.6 2503 + debug: 4.4.3 2504 + escape-string-regexp: 4.0.0 2505 + eslint-scope: 8.4.0 2506 + eslint-visitor-keys: 4.2.1 2507 + espree: 10.4.0 2508 + esquery: 1.7.0 2509 + esutils: 2.0.3 2510 + fast-deep-equal: 3.1.3 2511 + file-entry-cache: 8.0.0 2512 + find-up: 5.0.0 2513 + glob-parent: 6.0.2 2514 + ignore: 5.3.2 2515 + imurmurhash: 0.1.4 2516 + is-glob: 4.0.3 2517 + json-stable-stringify-without-jsonify: 1.0.1 2518 + lodash.merge: 4.6.2 2519 + minimatch: 3.1.2 2520 + natural-compare: 1.4.0 2521 + optionator: 0.9.4 2522 + transitivePeerDependencies: 2523 + - supports-color 2524 + 2525 + esm-env@1.2.2: {} 2526 + 2527 + espree@10.4.0: 2528 + dependencies: 2529 + acorn: 8.15.0 2530 + acorn-jsx: 5.3.2(acorn@8.15.0) 2531 + eslint-visitor-keys: 4.2.1 2532 + 2533 + esquery@1.7.0: 2534 + dependencies: 2535 + estraverse: 5.3.0 2536 + 2537 + esrecurse@4.3.0: 2538 + dependencies: 2539 + estraverse: 5.3.0 2540 + 2541 + estraverse@5.3.0: {} 2542 + 2543 + esutils@2.0.3: {} 2544 + 2545 + event-target-shim@5.0.1: {} 2546 + 2547 + events@3.3.0: {} 2548 + 2549 + fast-deep-equal@3.1.3: {} 2550 + 2551 + fast-json-stable-stringify@2.1.0: {} 2552 + 2553 + fast-levenshtein@2.0.6: {} 2554 + 2555 + fast-redact@3.5.0: {} 2556 + 2557 + fdir@6.5.0(picomatch@4.0.3): 2558 + optionalDependencies: 2559 + picomatch: 4.0.3 2560 + 2561 + file-entry-cache@8.0.0: 2562 + dependencies: 2563 + flat-cache: 4.0.1 2564 + 2565 + find-up@5.0.0: 2566 + dependencies: 2567 + locate-path: 6.0.0 2568 + path-exists: 4.0.0 2569 + 2570 + flat-cache@4.0.1: 2571 + dependencies: 2572 + flatted: 3.3.3 2573 + keyv: 4.5.4 2574 + 2575 + flatted@3.3.3: {} 2576 + 2577 + for-each@0.3.5: 2578 + dependencies: 2579 + is-callable: 1.2.7 2580 + 2581 + fsevents@2.3.3: 2582 + optional: true 2583 + 2584 + function-bind@1.1.2: {} 2585 + 2586 + function.prototype.name@1.1.8: 2587 + dependencies: 2588 + call-bind: 1.0.8 2589 + call-bound: 1.0.4 2590 + define-properties: 1.2.1 2591 + functions-have-names: 1.2.3 2592 + hasown: 2.0.2 2593 + is-callable: 1.2.7 2594 + 2595 + functions-have-names@1.2.3: {} 2596 + 2597 + generator-function@2.0.1: {} 2598 + 2599 + get-caller-file@2.0.5: {} 2600 + 2601 + get-intrinsic@1.3.0: 2602 + dependencies: 2603 + call-bind-apply-helpers: 1.0.2 2604 + es-define-property: 1.0.1 2605 + es-errors: 1.3.0 2606 + es-object-atoms: 1.1.1 2607 + function-bind: 1.1.2 2608 + get-proto: 1.0.1 2609 + gopd: 1.2.0 2610 + has-symbols: 1.1.0 2611 + hasown: 2.0.2 2612 + math-intrinsics: 1.1.0 2613 + 2614 + get-proto@1.0.1: 2615 + dependencies: 2616 + dunder-proto: 1.0.1 2617 + es-object-atoms: 1.1.1 2618 + 2619 + get-symbol-description@1.1.0: 2620 + dependencies: 2621 + call-bound: 1.0.4 2622 + es-errors: 1.3.0 2623 + get-intrinsic: 1.3.0 2624 + 2625 + glob-parent@6.0.2: 2626 + dependencies: 2627 + is-glob: 4.0.3 2628 + 2629 + globals@14.0.0: {} 2630 + 2631 + globalthis@1.0.4: 2632 + dependencies: 2633 + define-properties: 1.2.1 2634 + gopd: 1.2.0 2635 + 2636 + gopd@1.2.0: {} 2637 + 2638 + has-bigints@1.1.0: {} 2639 + 2640 + has-flag@4.0.0: {} 2641 + 2642 + has-property-descriptors@1.0.2: 2643 + dependencies: 2644 + es-define-property: 1.0.1 2645 + 2646 + has-proto@1.2.0: 2647 + dependencies: 2648 + dunder-proto: 1.0.1 2649 + 2650 + has-symbols@1.1.0: {} 2651 + 2652 + has-tostringtag@1.0.2: 2653 + dependencies: 2654 + has-symbols: 1.1.0 2655 + 2656 + hasown@2.0.2: 2657 + dependencies: 2658 + function-bind: 1.1.2 2659 + 2660 + ieee754@1.2.1: {} 2661 + 2662 + ignore@5.3.2: {} 2663 + 2664 + import-fresh@3.3.1: 2665 + dependencies: 2666 + parent-module: 1.0.1 2667 + resolve-from: 4.0.0 2668 + 2669 + imurmurhash@0.1.4: {} 2670 + 2671 + internal-slot@1.1.0: 2672 + dependencies: 2673 + es-errors: 1.3.0 2674 + hasown: 2.0.2 2675 + side-channel: 1.1.0 2676 + 2677 + is-array-buffer@3.0.5: 2678 + dependencies: 2679 + call-bind: 1.0.8 2680 + call-bound: 1.0.4 2681 + get-intrinsic: 1.3.0 2682 + 2683 + is-async-function@2.1.1: 2684 + dependencies: 2685 + async-function: 1.0.0 2686 + call-bound: 1.0.4 2687 + get-proto: 1.0.1 2688 + has-tostringtag: 1.0.2 2689 + safe-regex-test: 1.1.0 2690 + 2691 + is-bigint@1.1.0: 2692 + dependencies: 2693 + has-bigints: 1.1.0 2694 + 2695 + is-boolean-object@1.2.2: 2696 + dependencies: 2697 + call-bound: 1.0.4 2698 + has-tostringtag: 1.0.2 2699 + 2700 + is-callable@1.2.7: {} 2701 + 2702 + is-core-module@2.16.1: 2703 + dependencies: 2704 + hasown: 2.0.2 2705 + 2706 + is-data-view@1.0.2: 2707 + dependencies: 2708 + call-bound: 1.0.4 2709 + get-intrinsic: 1.3.0 2710 + is-typed-array: 1.1.15 2711 + 2712 + is-date-object@1.1.0: 2713 + dependencies: 2714 + call-bound: 1.0.4 2715 + has-tostringtag: 1.0.2 2716 + 2717 + is-extglob@2.1.1: {} 2718 + 2719 + is-finalizationregistry@1.1.1: 2720 + dependencies: 2721 + call-bound: 1.0.4 2722 + 2723 + is-fullwidth-code-point@3.0.0: {} 2724 + 2725 + is-generator-function@1.1.2: 2726 + dependencies: 2727 + call-bound: 1.0.4 2728 + generator-function: 2.0.1 2729 + get-proto: 1.0.1 2730 + has-tostringtag: 1.0.2 2731 + safe-regex-test: 1.1.0 2732 + 2733 + is-glob@4.0.3: 2734 + dependencies: 2735 + is-extglob: 2.1.1 2736 + 2737 + is-map@2.0.3: {} 2738 + 2739 + is-negative-zero@2.0.3: {} 2740 + 2741 + is-number-object@1.1.1: 2742 + dependencies: 2743 + call-bound: 1.0.4 2744 + has-tostringtag: 1.0.2 2745 + 2746 + is-regex@1.2.1: 2747 + dependencies: 2748 + call-bound: 1.0.4 2749 + gopd: 1.2.0 2750 + has-tostringtag: 1.0.2 2751 + hasown: 2.0.2 2752 + 2753 + is-set@2.0.3: {} 2754 + 2755 + is-shared-array-buffer@1.0.4: 2756 + dependencies: 2757 + call-bound: 1.0.4 2758 + 2759 + is-string@1.1.1: 2760 + dependencies: 2761 + call-bound: 1.0.4 2762 + has-tostringtag: 1.0.2 2763 + 2764 + is-symbol@1.1.1: 2765 + dependencies: 2766 + call-bound: 1.0.4 2767 + has-symbols: 1.1.0 2768 + safe-regex-test: 1.1.0 2769 + 2770 + is-typed-array@1.1.15: 2771 + dependencies: 2772 + which-typed-array: 1.1.20 2773 + 2774 + is-weakmap@2.0.2: {} 2775 + 2776 + is-weakref@1.1.1: 2777 + dependencies: 2778 + call-bound: 1.0.4 2779 + 2780 + is-weakset@2.0.4: 2781 + dependencies: 2782 + call-bound: 1.0.4 2783 + get-intrinsic: 1.3.0 2784 + 2785 + isarray@2.0.5: {} 2786 + 2787 + isexe@2.0.0: {} 2788 + 2789 + iso-datestring-validator@2.2.2: {} 2790 + 2791 + js-yaml@4.1.1: 2792 + dependencies: 2793 + argparse: 2.0.1 2794 + 2795 + json-buffer@3.0.1: {} 2796 + 2797 + json-schema-traverse@0.4.1: {} 2798 + 2799 + json-stable-stringify-without-jsonify@1.0.1: {} 2800 + 2801 + json5@1.0.2: 2802 + dependencies: 2803 + minimist: 1.2.8 2804 + 2805 + keyv@4.5.4: 2806 + dependencies: 2807 + json-buffer: 3.0.1 2808 + 2809 + levn@0.4.1: 2810 + dependencies: 2811 + prelude-ls: 1.2.1 2812 + type-check: 0.4.0 2813 + 2814 + locate-path@6.0.0: 2815 + dependencies: 2816 + p-locate: 5.0.0 2817 + 2818 + lodash.merge@4.6.2: {} 2819 + 2820 + lru-cache@10.4.3: {} 2821 + 2822 + math-intrinsics@1.1.0: {} 2823 + 2824 + minimatch@10.2.1: 2825 + dependencies: 2826 + brace-expansion: 5.0.2 2827 + 2828 + minimatch@3.1.2: 2829 + dependencies: 2830 + brace-expansion: 1.1.12 2831 + 2832 + minimist@1.2.8: {} 2833 + 2834 + ms@2.1.3: {} 2835 + 2836 + multiformats@9.9.0: {} 2837 + 2838 + nanoid@3.3.11: {} 2839 + 2840 + natural-compare@1.4.0: {} 2841 + 2842 + object-inspect@1.13.4: {} 2843 + 2844 + object-keys@1.1.1: {} 2845 + 2846 + object.assign@4.1.7: 2847 + dependencies: 2848 + call-bind: 1.0.8 2849 + call-bound: 1.0.4 2850 + define-properties: 1.2.1 2851 + es-object-atoms: 1.1.1 2852 + has-symbols: 1.1.0 2853 + object-keys: 1.1.1 2854 + 2855 + object.fromentries@2.0.8: 2856 + dependencies: 2857 + call-bind: 1.0.8 2858 + define-properties: 1.2.1 2859 + es-abstract: 1.24.1 2860 + es-object-atoms: 1.1.1 2861 + 2862 + object.groupby@1.0.3: 2863 + dependencies: 2864 + call-bind: 1.0.8 2865 + define-properties: 1.2.1 2866 + es-abstract: 1.24.1 2867 + 2868 + object.values@1.2.1: 2869 + dependencies: 2870 + call-bind: 1.0.8 2871 + call-bound: 1.0.4 2872 + define-properties: 1.2.1 2873 + es-object-atoms: 1.1.1 2874 + 2875 + on-exit-leak-free@2.1.2: {} 2876 + 2877 + optionator@0.9.4: 2878 + dependencies: 2879 + deep-is: 0.1.4 2880 + fast-levenshtein: 2.0.6 2881 + levn: 0.4.1 2882 + prelude-ls: 1.2.1 2883 + type-check: 0.4.0 2884 + word-wrap: 1.2.5 2885 + 2886 + own-keys@1.0.1: 2887 + dependencies: 2888 + get-intrinsic: 1.3.0 2889 + object-keys: 1.1.1 2890 + safe-push-apply: 1.0.0 2891 + 2892 + p-limit@3.1.0: 2893 + dependencies: 2894 + yocto-queue: 0.1.0 2895 + 2896 + p-locate@5.0.0: 2897 + dependencies: 2898 + p-limit: 3.1.0 2899 + 2900 + parent-module@1.0.1: 2901 + dependencies: 2902 + callsites: 3.1.0 2903 + 2904 + path-browserify@1.0.1: {} 2905 + 2906 + path-exists@4.0.0: {} 2907 + 2908 + path-key@3.1.1: {} 2909 + 2910 + path-parse@1.0.7: {} 2911 + 2912 + picocolors@1.1.1: {} 2913 + 2914 + picomatch@2.3.1: {} 2915 + 2916 + picomatch@4.0.3: {} 2917 + 2918 + pino-abstract-transport@1.2.0: 2919 + dependencies: 2920 + readable-stream: 4.7.0 2921 + split2: 4.2.0 2922 + 2923 + pino-std-serializers@6.2.2: {} 2924 + 2925 + pino@8.21.0: 2926 + dependencies: 2927 + atomic-sleep: 1.0.0 2928 + fast-redact: 3.5.0 2929 + on-exit-leak-free: 2.1.2 2930 + pino-abstract-transport: 1.2.0 2931 + pino-std-serializers: 6.2.2 2932 + process-warning: 3.0.0 2933 + quick-format-unescaped: 4.0.4 2934 + real-require: 0.2.0 2935 + safe-stable-stringify: 2.5.0 2936 + sonic-boom: 3.8.1 2937 + thread-stream: 2.7.0 2938 + 2939 + possible-typed-array-names@1.1.0: {} 2940 + 2941 + postcss@8.5.6: 2942 + dependencies: 2943 + nanoid: 3.3.11 2944 + picocolors: 1.1.1 2945 + source-map-js: 1.2.1 2946 + 2947 + prelude-ls@1.2.1: {} 2948 + 2949 + prettier@3.8.1: {} 2950 + 2951 + process-warning@3.0.0: {} 2952 + 2953 + process@0.11.10: {} 2954 + 2955 + punycode@2.3.1: {} 2956 + 2957 + quick-format-unescaped@4.0.4: {} 2958 + 2959 + readable-stream@4.7.0: 2960 + dependencies: 2961 + abort-controller: 3.0.0 2962 + buffer: 6.0.3 2963 + events: 3.3.0 2964 + process: 0.11.10 2965 + string_decoder: 1.3.0 2966 + 2967 + real-require@0.2.0: {} 2968 + 2969 + reflect.getprototypeof@1.0.10: 2970 + dependencies: 2971 + call-bind: 1.0.8 2972 + define-properties: 1.2.1 2973 + es-abstract: 1.24.1 2974 + es-errors: 1.3.0 2975 + es-object-atoms: 1.1.1 2976 + get-intrinsic: 1.3.0 2977 + get-proto: 1.0.1 2978 + which-builtin-type: 1.2.1 2979 + 2980 + regexp.prototype.flags@1.5.4: 2981 + dependencies: 2982 + call-bind: 1.0.8 2983 + define-properties: 1.2.1 2984 + es-errors: 1.3.0 2985 + get-proto: 1.0.1 2986 + gopd: 1.2.0 2987 + set-function-name: 2.0.2 2988 + 2989 + require-directory@2.1.1: {} 2990 + 2991 + resolve-from@4.0.0: {} 2992 + 2993 + resolve@1.22.11: 2994 + dependencies: 2995 + is-core-module: 2.16.1 2996 + path-parse: 1.0.7 2997 + supports-preserve-symlinks-flag: 1.0.0 2998 + 2999 + rollup@4.57.1: 3000 + dependencies: 3001 + '@types/estree': 1.0.8 3002 + optionalDependencies: 3003 + '@rollup/rollup-android-arm-eabi': 4.57.1 3004 + '@rollup/rollup-android-arm64': 4.57.1 3005 + '@rollup/rollup-darwin-arm64': 4.57.1 3006 + '@rollup/rollup-darwin-x64': 4.57.1 3007 + '@rollup/rollup-freebsd-arm64': 4.57.1 3008 + '@rollup/rollup-freebsd-x64': 4.57.1 3009 + '@rollup/rollup-linux-arm-gnueabihf': 4.57.1 3010 + '@rollup/rollup-linux-arm-musleabihf': 4.57.1 3011 + '@rollup/rollup-linux-arm64-gnu': 4.57.1 3012 + '@rollup/rollup-linux-arm64-musl': 4.57.1 3013 + '@rollup/rollup-linux-loong64-gnu': 4.57.1 3014 + '@rollup/rollup-linux-loong64-musl': 4.57.1 3015 + '@rollup/rollup-linux-ppc64-gnu': 4.57.1 3016 + '@rollup/rollup-linux-ppc64-musl': 4.57.1 3017 + '@rollup/rollup-linux-riscv64-gnu': 4.57.1 3018 + '@rollup/rollup-linux-riscv64-musl': 4.57.1 3019 + '@rollup/rollup-linux-s390x-gnu': 4.57.1 3020 + '@rollup/rollup-linux-x64-gnu': 4.57.1 3021 + '@rollup/rollup-linux-x64-musl': 4.57.1 3022 + '@rollup/rollup-openbsd-x64': 4.57.1 3023 + '@rollup/rollup-openharmony-arm64': 4.57.1 3024 + '@rollup/rollup-win32-arm64-msvc': 4.57.1 3025 + '@rollup/rollup-win32-ia32-msvc': 4.57.1 3026 + '@rollup/rollup-win32-x64-gnu': 4.57.1 3027 + '@rollup/rollup-win32-x64-msvc': 4.57.1 3028 + fsevents: 2.3.3 3029 + 3030 + safe-array-concat@1.1.3: 3031 + dependencies: 3032 + call-bind: 1.0.8 3033 + call-bound: 1.0.4 3034 + get-intrinsic: 1.3.0 3035 + has-symbols: 1.1.0 3036 + isarray: 2.0.5 3037 + 3038 + safe-buffer@5.2.1: {} 3039 + 3040 + safe-push-apply@1.0.0: 3041 + dependencies: 3042 + es-errors: 1.3.0 3043 + isarray: 2.0.5 3044 + 3045 + safe-regex-test@1.1.0: 3046 + dependencies: 3047 + call-bound: 1.0.4 3048 + es-errors: 1.3.0 3049 + is-regex: 1.2.1 3050 + 3051 + safe-stable-stringify@2.5.0: {} 3052 + 3053 + semver@6.3.1: {} 3054 + 3055 + set-function-length@1.2.2: 3056 + dependencies: 3057 + define-data-property: 1.1.4 3058 + es-errors: 1.3.0 3059 + function-bind: 1.1.2 3060 + get-intrinsic: 1.3.0 3061 + gopd: 1.2.0 3062 + has-property-descriptors: 1.0.2 3063 + 3064 + set-function-name@2.0.2: 3065 + dependencies: 3066 + define-data-property: 1.1.4 3067 + es-errors: 1.3.0 3068 + functions-have-names: 1.2.3 3069 + has-property-descriptors: 1.0.2 3070 + 3071 + set-proto@1.0.0: 3072 + dependencies: 3073 + dunder-proto: 1.0.1 3074 + es-errors: 1.3.0 3075 + es-object-atoms: 1.1.1 3076 + 3077 + shebang-command@2.0.0: 3078 + dependencies: 3079 + shebang-regex: 3.0.0 3080 + 3081 + shebang-regex@3.0.0: {} 3082 + 3083 + side-channel-list@1.0.0: 3084 + dependencies: 3085 + es-errors: 1.3.0 3086 + object-inspect: 1.13.4 3087 + 3088 + side-channel-map@1.0.1: 3089 + dependencies: 3090 + call-bound: 1.0.4 3091 + es-errors: 1.3.0 3092 + get-intrinsic: 1.3.0 3093 + object-inspect: 1.13.4 3094 + 3095 + side-channel-weakmap@1.0.2: 3096 + dependencies: 3097 + call-bound: 1.0.4 3098 + es-errors: 1.3.0 3099 + get-intrinsic: 1.3.0 3100 + object-inspect: 1.13.4 3101 + side-channel-map: 1.0.1 3102 + 3103 + side-channel@1.1.0: 3104 + dependencies: 3105 + es-errors: 1.3.0 3106 + object-inspect: 1.13.4 3107 + side-channel-list: 1.0.0 3108 + side-channel-map: 1.0.1 3109 + side-channel-weakmap: 1.0.2 3110 + 3111 + sonic-boom@3.8.1: 3112 + dependencies: 3113 + atomic-sleep: 1.0.0 3114 + 3115 + source-map-js@1.2.1: {} 3116 + 3117 + split2@4.2.0: {} 3118 + 3119 + stop-iteration-iterator@1.1.0: 3120 + dependencies: 3121 + es-errors: 1.3.0 3122 + internal-slot: 1.1.0 3123 + 3124 + string-width@4.2.3: 3125 + dependencies: 3126 + emoji-regex: 8.0.0 3127 + is-fullwidth-code-point: 3.0.0 3128 + strip-ansi: 6.0.1 3129 + 3130 + string.prototype.trim@1.2.10: 3131 + dependencies: 3132 + call-bind: 1.0.8 3133 + call-bound: 1.0.4 3134 + define-data-property: 1.1.4 3135 + define-properties: 1.2.1 3136 + es-abstract: 1.24.1 3137 + es-object-atoms: 1.1.1 3138 + has-property-descriptors: 1.0.2 3139 + 3140 + string.prototype.trimend@1.0.9: 3141 + dependencies: 3142 + call-bind: 1.0.8 3143 + call-bound: 1.0.4 3144 + define-properties: 1.2.1 3145 + es-object-atoms: 1.1.1 3146 + 3147 + string.prototype.trimstart@1.0.8: 3148 + dependencies: 3149 + call-bind: 1.0.8 3150 + define-properties: 1.2.1 3151 + es-object-atoms: 1.1.1 3152 + 3153 + string_decoder@1.3.0: 3154 + dependencies: 3155 + safe-buffer: 5.2.1 3156 + 3157 + strip-ansi@6.0.1: 3158 + dependencies: 3159 + ansi-regex: 5.0.1 3160 + 3161 + strip-bom@3.0.0: {} 3162 + 3163 + strip-json-comments@3.1.1: {} 3164 + 3165 + supports-color@7.2.0: 3166 + dependencies: 3167 + has-flag: 4.0.0 3168 + 3169 + supports-preserve-symlinks-flag@1.0.0: {} 3170 + 3171 + thread-stream@2.7.0: 3172 + dependencies: 3173 + real-require: 0.2.0 3174 + 3175 + tinyglobby@0.2.15: 3176 + dependencies: 3177 + fdir: 6.5.0(picomatch@4.0.3) 3178 + picomatch: 4.0.3 3179 + 3180 + tlds@1.261.0: {} 3181 + 3182 + ts-morph@27.0.2: 3183 + dependencies: 3184 + '@ts-morph/common': 0.28.1 3185 + code-block-writer: 13.0.3 3186 + 3187 + tsconfig-paths@3.15.0: 3188 + dependencies: 3189 + '@types/json5': 0.0.29 3190 + json5: 1.0.2 3191 + minimist: 1.2.8 3192 + strip-bom: 3.0.0 3193 + 3194 + tslib@2.8.1: {} 3195 + 3196 + type-check@0.4.0: 3197 + dependencies: 3198 + prelude-ls: 1.2.1 3199 + 3200 + typed-array-buffer@1.0.3: 3201 + dependencies: 3202 + call-bound: 1.0.4 3203 + es-errors: 1.3.0 3204 + is-typed-array: 1.1.15 3205 + 3206 + typed-array-byte-length@1.0.3: 3207 + dependencies: 3208 + call-bind: 1.0.8 3209 + for-each: 0.3.5 3210 + gopd: 1.2.0 3211 + has-proto: 1.2.0 3212 + is-typed-array: 1.1.15 3213 + 3214 + typed-array-byte-offset@1.0.4: 3215 + dependencies: 3216 + available-typed-arrays: 1.0.7 3217 + call-bind: 1.0.8 3218 + for-each: 0.3.5 3219 + gopd: 1.2.0 3220 + has-proto: 1.2.0 3221 + is-typed-array: 1.1.15 3222 + reflect.getprototypeof: 1.0.10 3223 + 3224 + typed-array-length@1.0.7: 3225 + dependencies: 3226 + call-bind: 1.0.8 3227 + for-each: 0.3.5 3228 + gopd: 1.2.0 3229 + is-typed-array: 1.1.15 3230 + possible-typed-array-names: 1.1.0 3231 + reflect.getprototypeof: 1.0.10 3232 + 3233 + uint8arrays@3.0.0: 3234 + dependencies: 3235 + multiformats: 9.9.0 3236 + 3237 + unbox-primitive@1.1.0: 3238 + dependencies: 3239 + call-bound: 1.0.4 3240 + has-bigints: 1.1.0 3241 + has-symbols: 1.1.0 3242 + which-boxed-primitive: 1.1.1 3243 + 3244 + unicode-segmenter@0.14.5: {} 3245 + 3246 + uri-js@4.4.1: 3247 + dependencies: 3248 + punycode: 2.3.1 3249 + 3250 + varint@6.0.0: {} 3251 + 3252 + vite-plugin-full-reload@1.2.0: 3253 + dependencies: 3254 + picocolors: 1.1.1 3255 + picomatch: 2.3.1 3256 + 3257 + vite-rs-plugin@1.0.1(vite@7.3.1): 3258 + dependencies: 3259 + vite: 7.3.1 3260 + vite-plugin-full-reload: 1.2.0 3261 + 3262 + vite@7.3.1: 3263 + dependencies: 3264 + esbuild: 0.27.3 3265 + fdir: 6.5.0(picomatch@4.0.3) 3266 + picomatch: 4.0.3 3267 + postcss: 8.5.6 3268 + rollup: 4.57.1 3269 + tinyglobby: 0.2.15 3270 + optionalDependencies: 3271 + fsevents: 2.3.3 3272 + 3273 + which-boxed-primitive@1.1.1: 3274 + dependencies: 3275 + is-bigint: 1.1.0 3276 + is-boolean-object: 1.2.2 3277 + is-number-object: 1.1.1 3278 + is-string: 1.1.1 3279 + is-symbol: 1.1.1 3280 + 3281 + which-builtin-type@1.2.1: 3282 + dependencies: 3283 + call-bound: 1.0.4 3284 + function.prototype.name: 1.1.8 3285 + has-tostringtag: 1.0.2 3286 + is-async-function: 2.1.1 3287 + is-date-object: 1.1.0 3288 + is-finalizationregistry: 1.1.1 3289 + is-generator-function: 1.1.2 3290 + is-regex: 1.2.1 3291 + is-weakref: 1.1.1 3292 + isarray: 2.0.5 3293 + which-boxed-primitive: 1.1.1 3294 + which-collection: 1.0.2 3295 + which-typed-array: 1.1.20 3296 + 3297 + which-collection@1.0.2: 3298 + dependencies: 3299 + is-map: 2.0.3 3300 + is-set: 2.0.3 3301 + is-weakmap: 2.0.2 3302 + is-weakset: 2.0.4 3303 + 3304 + which-typed-array@1.1.20: 3305 + dependencies: 3306 + available-typed-arrays: 1.0.7 3307 + call-bind: 1.0.8 3308 + call-bound: 1.0.4 3309 + for-each: 0.3.5 3310 + get-proto: 1.0.1 3311 + gopd: 1.2.0 3312 + has-tostringtag: 1.0.2 3313 + 3314 + which@2.0.2: 3315 + dependencies: 3316 + isexe: 2.0.0 3317 + 3318 + word-wrap@1.2.5: {} 3319 + 3320 + wrap-ansi@7.0.0: 3321 + dependencies: 3322 + ansi-styles: 4.3.0 3323 + string-width: 4.2.3 3324 + strip-ansi: 6.0.1 3325 + 3326 + y18n@5.0.8: {} 3327 + 3328 + yargs-parser@21.1.1: {} 3329 + 3330 + yargs@17.7.2: 3331 + dependencies: 3332 + cliui: 8.0.1 3333 + escalade: 3.2.0 3334 + get-caller-file: 2.0.5 3335 + require-directory: 2.1.1 3336 + string-width: 4.2.3 3337 + y18n: 5.0.8 3338 + yargs-parser: 21.1.1 3339 + 3340 + yocto-queue@0.1.0: {} 3341 + 3342 + zod@3.25.76: {}
+2
packages/moover/pnpm-workspace.yaml
··· 1 + onlyBuiltDependencies: 2 + - core-js
+4 -12
packages/moover/tsconfig.json
··· 1 1 { 2 - "include": [ 3 - "lib/**/*" 4 - ], 5 - "exclude": [ 6 - "node_modules", 7 - "dist", 8 - "types" 9 - ], 2 + "include": ["lib/**/*"], 3 + "exclude": ["node_modules", "dist", "types"], 10 4 "compilerOptions": { 11 5 "allowJs": true, 12 6 "checkJs": false, ··· 17 11 // or use "declarationDir": "types" 18 12 19 13 "target": "ES2022", 20 - "lib": [ 21 - "ES2022" 22 - ], 14 + "lib": ["ES2022"], 23 15 // add "DOM" if you use browser APIs 24 16 "skipLibCheck": true, 25 17 "module": "ESNext", ··· 27 19 // or "NodeNext" depending on your setup 28 20 "rootDir": "lib" 29 21 } 30 - } 22 + }
+10 -10
packages/moover/types/atprotoUtils.d.ts
··· 1 - export const handleResolver: CompositeHandleResolver; 2 - export const docResolver: CompositeDidDocumentResolver<"plc" | "web">; 1 + export const handleResolver: CompositeHandleResolver 2 + export const docResolver: CompositeDidDocumentResolver<'plc' | 'web'> 3 3 /** 4 4 * Cleans the handle of @ and some other unicode characters that used to show up when copied from the profile 5 5 * @param handle {string} 6 6 * @returns {string} 7 7 */ 8 - export function cleanHandle(handle: string): string; 8 + export function cleanHandle(handle: string): string 9 9 /** 10 10 * Convince helper to resolve a handle to a did and then find the PDS url from the did document. 11 11 * ··· 13 13 * @returns {Promise<{usersDid: string, pds: string}>} 14 14 */ 15 15 export function handleAndPDSResolver(handle: any): Promise<{ 16 - usersDid: string; 17 - pds: string; 18 - }>; 16 + usersDid: string 17 + pds: string 18 + }> 19 19 /** 20 20 * Fetches the DID Web from the .well-known/did.json endpoint of the server. 21 21 * Legacy and was helpful if the web ui and server are on the same domain, not as useful now 22 22 * @param baseUrl 23 23 * @returns {Promise<*>} 24 24 */ 25 - export function fetchPDSMooverDIDWeb(baseUrl: any): Promise<any>; 26 - import { CompositeHandleResolver } from '@atcute/identity-resolver'; 27 - import { CompositeDidDocumentResolver } from '@atcute/identity-resolver'; 28 - //# sourceMappingURL=atprotoUtils.d.ts.map 25 + export function fetchPDSMooverDIDWeb(baseUrl: any): Promise<any> 26 + import { CompositeHandleResolver } from '@atcute/identity-resolver' 27 + import { CompositeDidDocumentResolver } from '@atcute/identity-resolver' 28 + //# sourceMappingURL=atprotoUtils.d.ts.map
+89 -82
packages/moover/types/backup.d.ts
··· 1 1 /** 2 2 * JSDoc type-only import to avoid runtime import errors in the browser. 3 3 */ 4 - export type InferXRPCBodyOutput = any; 4 + export type InferXRPCBodyOutput = any 5 5 /** 6 6 * JSDoc type-only import to avoid runtime import errors in the browser. 7 7 * @typedef {import('@atcute/lexicons').InferXRPCBodyOutput} InferXRPCBodyOutput ··· 10 10 * Logic to sign up and manage backups for pdsmoover.com (or your own selfhosted instance) 11 11 */ 12 12 export class BackupService { 13 - /** 14 - * 15 - * @param backupDidWeb {string} - The did:web for the xrpc service for backups, defaults to did:web:pdsmoover.com 16 - */ 17 - constructor(backupDidWeb?: string); 18 - /** 19 - * 20 - * @type {Client} 21 - */ 22 - atCuteClient: Client; 23 - /** 24 - * 25 - * @type {CredentialManager} 26 - */ 27 - atCuteCredentialManager: CredentialManager; 28 - /** 29 - * The did:web for the xrpc service for backups, defaults to pdsmoover.com 30 - * @type {string} 31 - */ 32 - backupDidWeb: string; 33 - /** 34 - * Logs in and returns the backup status. 35 - * To use the rest of the BackupService, it is assumed that this has ran first, 36 - * and the user has successfully signed up. A successful login is a returned null if the user has not signed up. 37 - * or the backup status if they are 38 - * 39 - * If the server requires 2FA, 40 - * it will throw with error.error === 'AuthFactorTokenRequired'. 41 - * @param identifier {string} handle or did 42 - * @param password {string} 43 - * @param {function|null} onStatus - a function that takes a string used to update the UI. 44 - * Like (status) => console.log(status) 45 - * @param twoFactorCode {string|null} 46 - * 47 - * @returns {Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>|null>} 48 - */ 49 - loginAndStatus(identifier: string, password: string, onStatus?: Function | null, twoFactorCode?: string | null): Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema["output"]> | null>; 50 - /** 51 - * Signs the user up for backups with the service 52 - * @param onStatus 53 - * @returns {Promise<void>} 54 - */ 55 - signUp(onStatus?: any): Promise<void>; 56 - /** 57 - * Requests a PLC token to be sent to the user's email, needed to add a new rotation key 58 - * @returns {Promise<void>} 59 - */ 60 - requestAPlcToken(): Promise<void>; 61 - /** 62 - * Adds a new rotation to the users did document. Assumes you are already signed in. 63 - * 64 - * WARNING: This will overwrite any existing rotation keys with the new one at the top, and the PDS key as the second one 65 - * @param plcToken {string} - PLC token from the user's email that was sent from requestAPlcToken 66 - * @param rotationKey {string} - The new rotation key to add to the user's did document 67 - * @returns {Promise<void>} 68 - */ 69 - addANewRotationKey(plcToken: string, rotationKey: string): Promise<void>; 70 - /** 71 - * 72 - * Gets the current status of the user's backup repository. 73 - * 74 - * @param onStatus {function|null} - a function that takes a string used to update the UI. 75 - * @returns {Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>>} 76 - */ 77 - getUsersRepoStatus(onStatus?: Function | null): Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema["output"]>>; 78 - /** 79 - * Requests a backup to be run immediately for the signed-in user. Usually does, depend on the server's backup queue 80 - * @param onStatus 81 - * @returns {Promise<boolean>} 82 - */ 83 - runBackupNow(onStatus?: any): Promise<boolean>; 84 - /** 85 - * Remove (delete) the signed-in user's backup repository. this also deletes all the user's backup data. 86 - * @param onStatus 87 - * @returns {Promise<boolean>} 88 - */ 89 - removeRepo(onStatus?: any): Promise<boolean>; 13 + /** 14 + * 15 + * @param backupDidWeb {string} - The did:web for the xrpc service for backups, defaults to did:web:pdsmoover.com 16 + */ 17 + constructor(backupDidWeb?: string) 18 + /** 19 + * 20 + * @type {Client} 21 + */ 22 + atCuteClient: Client 23 + /** 24 + * 25 + * @type {CredentialManager} 26 + */ 27 + atCuteCredentialManager: CredentialManager 28 + /** 29 + * The did:web for the xrpc service for backups, defaults to pdsmoover.com 30 + * @type {string} 31 + */ 32 + backupDidWeb: string 33 + /** 34 + * Logs in and returns the backup status. 35 + * To use the rest of the BackupService, it is assumed that this has ran first, 36 + * and the user has successfully signed up. A successful login is a returned null if the user has not signed up. 37 + * or the backup status if they are 38 + * 39 + * If the server requires 2FA, 40 + * it will throw with error.error === 'AuthFactorTokenRequired'. 41 + * @param identifier {string} handle or did 42 + * @param password {string} 43 + * @param {function|null} onStatus - a function that takes a string used to update the UI. 44 + * Like (status) => console.log(status) 45 + * @param twoFactorCode {string|null} 46 + * 47 + * @returns {Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>|null>} 48 + */ 49 + loginAndStatus( 50 + identifier: string, 51 + password: string, 52 + onStatus?: Function | null, 53 + twoFactorCode?: string | null, 54 + ): Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']> | null> 55 + /** 56 + * Signs the user up for backups with the service 57 + * @param onStatus 58 + * @returns {Promise<void>} 59 + */ 60 + signUp(onStatus?: any): Promise<void> 61 + /** 62 + * Requests a PLC token to be sent to the user's email, needed to add a new rotation key 63 + * @returns {Promise<void>} 64 + */ 65 + requestAPlcToken(): Promise<void> 66 + /** 67 + * Adds a new rotation to the users did document. Assumes you are already signed in. 68 + * 69 + * WARNING: This will overwrite any existing rotation keys with the new one at the top, and the PDS key as the second one 70 + * @param plcToken {string} - PLC token from the user's email that was sent from requestAPlcToken 71 + * @param rotationKey {string} - The new rotation key to add to the user's did document 72 + * @returns {Promise<void>} 73 + */ 74 + addANewRotationKey(plcToken: string, rotationKey: string): Promise<void> 75 + /** 76 + * 77 + * Gets the current status of the user's backup repository. 78 + * 79 + * @param onStatus {function|null} - a function that takes a string used to update the UI. 80 + * @returns {Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>>} 81 + */ 82 + getUsersRepoStatus( 83 + onStatus?: Function | null, 84 + ): Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>> 85 + /** 86 + * Requests a backup to be run immediately for the signed-in user. Usually does, depend on the server's backup queue 87 + * @param onStatus 88 + * @returns {Promise<boolean>} 89 + */ 90 + runBackupNow(onStatus?: any): Promise<boolean> 91 + /** 92 + * Remove (delete) the signed-in user's backup repository. this also deletes all the user's backup data. 93 + * @param onStatus 94 + * @returns {Promise<boolean>} 95 + */ 96 + removeRepo(onStatus?: any): Promise<boolean> 90 97 } 91 - import { Client } from '@atcute/client'; 92 - import { CredentialManager } from '@atcute/client'; 93 - import { ComPdsmooverBackupDescribeServer } from '@pds-moover/lexicons'; 94 - //# sourceMappingURL=backup.d.ts.map 98 + import { Client } from '@atcute/client' 99 + import { CredentialManager } from '@atcute/client' 100 + import { ComPdsmooverBackupDescribeServer } from '@pds-moover/lexicons' 101 + //# sourceMappingURL=backup.d.ts.map
+8 -8
packages/moover/types/main.d.ts
··· 1 - import { Migrator } from './pdsmoover.js'; 2 - import { MissingBlobs } from './missingBlobs.js'; 3 - import { BackupService } from './backup.js'; 4 - import { PlcOps } from './plc-ops.js'; 5 - import { Restore } from './restore.js'; 6 - import { handleAndPDSResolver } from './atprotoUtils.js'; 7 - export { Migrator, MissingBlobs, BackupService, PlcOps, Restore, handleAndPDSResolver }; 8 - //# sourceMappingURL=main.d.ts.map 1 + import { Migrator } from './pdsmoover.js' 2 + import { MissingBlobs } from './missingBlobs.js' 3 + import { BackupService } from './backup.js' 4 + import { PlcOps } from './plc-ops.js' 5 + import { Restore } from './restore.js' 6 + import { handleAndPDSResolver } from './atprotoUtils.js' 7 + export { Migrator, MissingBlobs, BackupService, PlcOps, Restore, handleAndPDSResolver } 8 + //# sourceMappingURL=main.d.ts.map
+65 -57
packages/moover/types/missingBlobs.d.ts
··· 2 2 * Class to help find missing blobs from the did's previous PDS and import them into the current PDS 3 3 */ 4 4 export class MissingBlobs { 5 - /** 6 - * The user's current PDS agent 7 - * @type {AtpAgent} 8 - */ 9 - currentPdsAgent: AtpAgent; 10 - /** 11 - * The user's old PDS agent 12 - * @type {AtpAgent} 13 - */ 14 - oldPdsAgent: AtpAgent; 15 - /** 16 - * the user's did 17 - * @type {string|null} 18 - */ 19 - did: string | null; 20 - /** 21 - * The user's current PDS url 22 - * @type {null} 23 - */ 24 - currentPdsUrl: any; 25 - /** 26 - * A list of the missing cids blobs from the old PDS. In this case if a retry upload fails it gets put in this array for the ui 27 - * @type {string[]} 28 - */ 29 - missingBlobs: string[]; 30 - /** 31 - * Logs the user into the current PDS and gets the account status 32 - * @param handle {string} 33 - * @param password {string} 34 - * @param twoFactorCode {string|null} 35 - * @returns {Promise<{accountStatus: OutputSchema, missingBlobsCount: number}>} 36 - */ 37 - currentAgentLogin(handle: string, password: string, twoFactorCode?: string | null): Promise<{ 38 - accountStatus: OutputSchema; 39 - missingBlobsCount: number; 40 - }>; 41 - /** 42 - * Logs into the old PDS and gets the account status. 43 - * Does not need a handle 44 - * since it is assumed the user has already logged in with the current PDS and we are using their did 45 - * @param password {string} 46 - * @param twoFactorCode {string|null} 47 - * @param pdsUrl {string|null} - If you know the url of the old PDS you can pass it in here. If not it will be guessed at from plc ops 48 - * @returns {Promise<void>} 49 - */ 50 - oldAgentLogin(password: string, twoFactorCode?: string | null, pdsUrl?: string | null): Promise<void>; 51 - /** 52 - * Gets the missing blobs from the old PDS and uploads them to the current PDS 53 - * @param statusUpdateHandler {function} - A function to update the status of the migration. This is useful for showing the user the progress of the migration 54 - * @returns {Promise<{accountStatus: OutputSchema, missingBlobsCount: number}>} 55 - */ 56 - migrateMissingBlobs(statusUpdateHandler: Function): Promise<{ 57 - accountStatus: OutputSchema; 58 - missingBlobsCount: number; 59 - }>; 5 + /** 6 + * The user's current PDS agent 7 + * @type {AtpAgent} 8 + */ 9 + currentPdsAgent: AtpAgent 10 + /** 11 + * The user's old PDS agent 12 + * @type {AtpAgent} 13 + */ 14 + oldPdsAgent: AtpAgent 15 + /** 16 + * the user's did 17 + * @type {string|null} 18 + */ 19 + did: string | null 20 + /** 21 + * The user's current PDS url 22 + * @type {null} 23 + */ 24 + currentPdsUrl: any 25 + /** 26 + * A list of the missing cids blobs from the old PDS. In this case if a retry upload fails it gets put in this array for the ui 27 + * @type {string[]} 28 + */ 29 + missingBlobs: string[] 30 + /** 31 + * Logs the user into the current PDS and gets the account status 32 + * @param handle {string} 33 + * @param password {string} 34 + * @param twoFactorCode {string|null} 35 + * @returns {Promise<{accountStatus: OutputSchema, missingBlobsCount: number}>} 36 + */ 37 + currentAgentLogin( 38 + handle: string, 39 + password: string, 40 + twoFactorCode?: string | null, 41 + ): Promise<{ 42 + accountStatus: OutputSchema 43 + missingBlobsCount: number 44 + }> 45 + /** 46 + * Logs into the old PDS and gets the account status. 47 + * Does not need a handle 48 + * since it is assumed the user has already logged in with the current PDS and we are using their did 49 + * @param password {string} 50 + * @param twoFactorCode {string|null} 51 + * @param pdsUrl {string|null} - If you know the url of the old PDS you can pass it in here. If not it will be guessed at from plc ops 52 + * @returns {Promise<void>} 53 + */ 54 + oldAgentLogin( 55 + password: string, 56 + twoFactorCode?: string | null, 57 + pdsUrl?: string | null, 58 + ): Promise<void> 59 + /** 60 + * Gets the missing blobs from the old PDS and uploads them to the current PDS 61 + * @param statusUpdateHandler {function} - A function to update the status of the migration. This is useful for showing the user the progress of the migration 62 + * @returns {Promise<{accountStatus: OutputSchema, missingBlobsCount: number}>} 63 + */ 64 + migrateMissingBlobs(statusUpdateHandler: Function): Promise<{ 65 + accountStatus: OutputSchema 66 + missingBlobsCount: number 67 + }> 60 68 } 61 - import { AtpAgent } from '@atproto/api'; 62 - //# sourceMappingURL=missingBlobs.d.ts.map 69 + import { AtpAgent } from '@atproto/api' 70 + //# sourceMappingURL=missingBlobs.d.ts.map
+79 -64
packages/moover/types/pdsmoover.d.ts
··· 3 3 * On pdsmoover.com this is the logic for the MOOver 4 4 */ 5 5 export class Migrator { 6 - /** @type {AtpAgent} */ 7 - oldAgent: AtpAgent; 8 - /** @type {AtpAgent} */ 9 - newAgent: AtpAgent; 10 - /** @type {[string]} */ 11 - missingBlobs: [string]; 12 - /** @type {boolean} */ 13 - createNewAccount: boolean; 14 - /** @type {boolean} */ 15 - migrateRepo: boolean; 16 - /** @type {boolean} */ 17 - migrateBlobs: boolean; 18 - /** @type {boolean} */ 19 - migrateMissingBlobs: boolean; 20 - /** @type {boolean} */ 21 - migratePrefs: boolean; 22 - /** @type {boolean} */ 23 - migratePlcRecord: boolean; 24 - /** 25 - * This migrator is pretty cut and dry and makes a few assumptions 26 - * 1. You are using the same password between each account 27 - * 2. If this command fails for something like oauth 2fa code it throws an error and expects the same values when ran again. 28 - * 3. You can control which "actions" happen by setting the class variables to false. 29 - * 4. Each instance of the class is assumed to be for a single migration 30 - * @param {string} oldHandle - The handle you use on your old pds, something like alice.bsky.social 31 - * @param {string} password - Your password for your current login. Has to be your real password, no app password. When setting up a new account we reuse it as well for that account 32 - * @param {string} newPdsUrl - The new URL for your pds. Like https://coolnewpds.com 33 - * @param {string} newEmail - The email you want to use on the new pds (can be the same as the previous one as long as it's not already being used on the new pds) 34 - * @param {string} newHandle - The new handle you want, like alice.bsky.social, or if you already have a domain name set as a handle can use it myname.com. 35 - * @param {string|null} inviteCode - The invite code you got from the PDS you are migrating to. If null does not include one 36 - * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. Like (status) => console.log(status) 37 - * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 38 - * @param verificationCode - Optional verification captcha code for account creation if the PDS requires it 39 - */ 40 - migrate(oldHandle: string, password: string, newPdsUrl: string, newEmail: string, newHandle: string, inviteCode: string | null, statusUpdateHandler?: Function | null, twoFactorCode?: string | null, verificationCode?: any): Promise<void>; 41 - /** 42 - * Sign and submits the PLC operation to officially migrate the account 43 - * @param {string} token - the PLC token sent in the email. If you're just wanting to run this rerun migrate with all the flags set as false except for migratePlcRecord 44 - * @param additionalRotationKeysToAdd {string[]} - additional rotation keys to add in addition to the ones provided by the new PDS. 45 - * @returns {Promise<void>} 46 - */ 47 - signPlcOperation(token: string, additionalRotationKeysToAdd?: string[]): Promise<void>; 48 - /** 49 - * Using this method assumes the Migrator class was constructed new and this was called. 50 - * Find the user's previous PDS from the PLC op logs, 51 - * logs in and deactivates their old account if it was found still active. 52 - * 53 - * @param oldHandle {string} 54 - * @param oldPassword {string} 55 - * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. 56 - * Like (status) => console.log(status) 57 - * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 58 - * @returns {Promise<void>} 59 - */ 60 - deactivateOldAccount(oldHandle: string, oldPassword: string, statusUpdateHandler?: Function | null, twoFactorCode?: string | null): Promise<void>; 61 - /** 62 - * Signs the logged-in user in this.newAgent for backups with PDS MOOver. This is usually called after migrate and signPlcOperation are successful 63 - * 64 - * @param {string} didWeb 65 - * @returns {Promise<void>} 66 - */ 67 - signUpForBackupsFromMigration(didWeb?: string): Promise<void>; 6 + /** @type {AtpAgent} */ 7 + oldAgent: AtpAgent 8 + /** @type {AtpAgent} */ 9 + newAgent: AtpAgent 10 + /** @type {[string]} */ 11 + missingBlobs: [string] 12 + /** @type {boolean} */ 13 + createNewAccount: boolean 14 + /** @type {boolean} */ 15 + migrateRepo: boolean 16 + /** @type {boolean} */ 17 + migrateBlobs: boolean 18 + /** @type {boolean} */ 19 + migrateMissingBlobs: boolean 20 + /** @type {boolean} */ 21 + migratePrefs: boolean 22 + /** @type {boolean} */ 23 + migratePlcRecord: boolean 24 + /** 25 + * This migrator is pretty cut and dry and makes a few assumptions 26 + * 1. You are using the same password between each account 27 + * 2. If this command fails for something like oauth 2fa code it throws an error and expects the same values when ran again. 28 + * 3. You can control which "actions" happen by setting the class variables to false. 29 + * 4. Each instance of the class is assumed to be for a single migration 30 + * @param {string} oldHandle - The handle you use on your old pds, something like alice.bsky.social 31 + * @param {string} password - Your password for your current login. Has to be your real password, no app password. When setting up a new account we reuse it as well for that account 32 + * @param {string} newPdsUrl - The new URL for your pds. Like https://coolnewpds.com 33 + * @param {string} newEmail - The email you want to use on the new pds (can be the same as the previous one as long as it's not already being used on the new pds) 34 + * @param {string} newHandle - The new handle you want, like alice.bsky.social, or if you already have a domain name set as a handle can use it myname.com. 35 + * @param {string|null} inviteCode - The invite code you got from the PDS you are migrating to. If null does not include one 36 + * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. Like (status) => console.log(status) 37 + * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 38 + * @param verificationCode - Optional verification captcha code for account creation if the PDS requires it 39 + */ 40 + migrate( 41 + oldHandle: string, 42 + password: string, 43 + newPdsUrl: string, 44 + newEmail: string, 45 + newHandle: string, 46 + inviteCode: string | null, 47 + statusUpdateHandler?: Function | null, 48 + twoFactorCode?: string | null, 49 + verificationCode?: any, 50 + ): Promise<void> 51 + /** 52 + * Sign and submits the PLC operation to officially migrate the account 53 + * @param {string} token - the PLC token sent in the email. If you're just wanting to run this rerun migrate with all the flags set as false except for migratePlcRecord 54 + * @param additionalRotationKeysToAdd {string[]} - additional rotation keys to add in addition to the ones provided by the new PDS. 55 + * @returns {Promise<void>} 56 + */ 57 + signPlcOperation(token: string, additionalRotationKeysToAdd?: string[]): Promise<void> 58 + /** 59 + * Using this method assumes the Migrator class was constructed new and this was called. 60 + * Find the user's previous PDS from the PLC op logs, 61 + * logs in and deactivates their old account if it was found still active. 62 + * 63 + * @param oldHandle {string} 64 + * @param oldPassword {string} 65 + * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. 66 + * Like (status) => console.log(status) 67 + * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 68 + * @returns {Promise<void>} 69 + */ 70 + deactivateOldAccount( 71 + oldHandle: string, 72 + oldPassword: string, 73 + statusUpdateHandler?: Function | null, 74 + twoFactorCode?: string | null, 75 + ): Promise<void> 76 + /** 77 + * Signs the logged-in user in this.newAgent for backups with PDS MOOver. This is usually called after migrate and signPlcOperation are successful 78 + * 79 + * @param {string} didWeb 80 + * @returns {Promise<void>} 81 + */ 82 + signUpForBackupsFromMigration(didWeb?: string): Promise<void> 68 83 } 69 - import { AtpAgent } from '@atproto/api'; 70 - //# sourceMappingURL=pdsmoover.d.ts.map 84 + import { AtpAgent } from '@atproto/api' 85 + //# sourceMappingURL=pdsmoover.d.ts.map
+109 -98
packages/moover/types/plc-ops.d.ts
··· 1 1 /** 2 2 * JSDoc type-only import to avoid runtime import errors in the browser. 3 3 */ 4 - export type defs = typeof defs; 4 + export type defs = typeof defs 5 5 /** 6 6 * JSDoc type-only import to avoid runtime import errors in the browser. 7 7 */ 8 - export type normalizeOp = any; 8 + export type normalizeOp = any 9 9 /** 10 10 * JSDoc type-only import to avoid runtime import errors in the browser. 11 11 */ 12 - export type Operation = import("@atcute/did-plc").Operation; 12 + export type Operation = import('@atcute/did-plc').Operation 13 13 /** 14 14 * JSDoc type-only import to avoid runtime import errors in the browser. 15 15 */ 16 - export type CompatibleOperation = import("@atcute/did-plc").CompatibleOperation; 16 + export type CompatibleOperation = import('@atcute/did-plc').CompatibleOperation 17 17 /** 18 18 * JSDoc type-only import to avoid runtime import errors in the browser. 19 19 */ 20 - export type IndexedEntryLog = import("@atcute/did-plc").IndexedEntryLog; 20 + export type IndexedEntryLog = import('@atcute/did-plc').IndexedEntryLog 21 21 /** 22 22 * JSDoc type-only import to avoid runtime import errors in the browser. 23 23 */ 24 - export type IndexedEntry = import("@atcute/did-plc").IndexedEntry; 24 + export type IndexedEntry = import('@atcute/did-plc').IndexedEntry 25 25 /** 26 26 * Class to help with various PLC operations 27 27 */ 28 28 export class PlcOps { 29 - /** 30 - * 31 - * @param plcDirectoryUrl {string} - The url of the plc directory, defaults to https://plc.directory 32 - */ 33 - constructor(plcDirectoryUrl?: string); 34 - /** 35 - * The url of the plc directory 36 - * @type {string} 37 - */ 38 - plcDirectoryUrl: string; 39 - /** 40 - * Gets the current rotation keys for a user via their last PlC operation 41 - * @param did 42 - * @returns {Promise<string[]>} 43 - */ 44 - getCurrentRotationKeysForUser(did: any): Promise<string[]>; 45 - /** 46 - * Gets the last PlC operation for a user from the plc directory 47 - * @param did 48 - * @returns {Promise<{lastOperation: Operation, base: any}>} 49 - */ 50 - getLastPlcOpFromPlc(did: any): Promise<{ 51 - lastOperation: Operation; 52 - base: any; 53 - }>; 54 - /** 55 - * 56 - * @param logs {IndexedEntryLog} 57 - * @returns {{lastOperation: Operation, base: IndexedEntry}} 58 - */ 59 - getLastPlcOp(logs: IndexedEntryLog): { 60 - lastOperation: Operation; 61 - base: IndexedEntry; 62 - }; 63 - /** 64 - * Gets the plc audit logs for a user from the plc directory 65 - * @param did 66 - * @returns {Promise<IndexedEntryLog>} 67 - */ 68 - getPlcAuditLogs(did: any): Promise<IndexedEntryLog>; 69 - /** 70 - * Creates a new secp256k1 key that can be used for either rotation or verification key 71 - * @returns {Promise<{privateKey: string, publicKey: `did:key:${string}`}>} 72 - */ 73 - createANewSecp256k1(): Promise<{ 74 - privateKey: string; 75 - publicKey: `did:key:${string}`; 76 - }>; 77 - /** 78 - * Signs a new operation with the provided signing key, and information and submits it to the plc directory 79 - * @param did {string} - The user's did 80 - * @param signingRotationKey { P256PrivateKey|Secp256k1PrivateKey} - The keypair to sign the op with 81 - * @param alsoKnownAs {string[]} 82 - * @param rotationKeys {string[]} 83 - * @param pds {string} 84 - * @param verificationKey {string} - The public verification key 85 - * @param prev {string} - The previous valid operation's cid. 86 - * @returns {Promise<void>} 87 - */ 88 - signAndPublishNewOp(did: string, signingRotationKey: P256PrivateKey | Secp256k1PrivateKey, alsoKnownAs: string[], rotationKeys: string[], pds: string, verificationKey: string, prev: string): Promise<void>; 89 - /** 90 - * Takes a multi or hex based private key and returns a keypair 91 - * @param privateKeyString {string} 92 - * @param type {string} - secp256k1 or p256, needed if the private key is hex based, can be assumed if it's a multikey 93 - * @returns {Promise<{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}>} 94 - */ 95 - getKeyPair(privateKeyString: string, type?: string): Promise<{ 96 - type: string; 97 - didPublicKey: `did:key:${string}`; 98 - keypair: P256PrivateKey | Secp256k1PrivateKey; 99 - }>; 100 - /** 101 - * Submits a new operation to the plc directory 102 - * @param did {string} - The user's did 103 - * @param operation 104 - * @returns {Promise<void>} 105 - */ 106 - pushPlcOperation(did: string, operation: any): Promise<void>; 107 - /** 108 - * Creates a new service auth token for a user. This is what is used to create a new account on a PDS for your did 109 - * 110 - * @param iss The user's did 111 - * @param aud The did:web, if it's a PDS it's usually from /xrpc/com.atproto.server.describeServer 112 - * @param keypair The keypair to sign with only supporting ES256K atm 113 - * @param lxm The lxm which is usually com.atproto.server.createAccount for creating a new account 114 - * @returns {Promise<string>} 115 - */ 116 - createANewServiceAuthToken(iss: any, aud: any, keypair: any, lxm: any): Promise<string>; 29 + /** 30 + * 31 + * @param plcDirectoryUrl {string} - The url of the plc directory, defaults to https://plc.directory 32 + */ 33 + constructor(plcDirectoryUrl?: string) 34 + /** 35 + * The url of the plc directory 36 + * @type {string} 37 + */ 38 + plcDirectoryUrl: string 39 + /** 40 + * Gets the current rotation keys for a user via their last PlC operation 41 + * @param did 42 + * @returns {Promise<string[]>} 43 + */ 44 + getCurrentRotationKeysForUser(did: any): Promise<string[]> 45 + /** 46 + * Gets the last PlC operation for a user from the plc directory 47 + * @param did 48 + * @returns {Promise<{lastOperation: Operation, base: any}>} 49 + */ 50 + getLastPlcOpFromPlc(did: any): Promise<{ 51 + lastOperation: Operation 52 + base: any 53 + }> 54 + /** 55 + * 56 + * @param logs {IndexedEntryLog} 57 + * @returns {{lastOperation: Operation, base: IndexedEntry}} 58 + */ 59 + getLastPlcOp(logs: IndexedEntryLog): { 60 + lastOperation: Operation 61 + base: IndexedEntry 62 + } 63 + /** 64 + * Gets the plc audit logs for a user from the plc directory 65 + * @param did 66 + * @returns {Promise<IndexedEntryLog>} 67 + */ 68 + getPlcAuditLogs(did: any): Promise<IndexedEntryLog> 69 + /** 70 + * Creates a new secp256k1 key that can be used for either rotation or verification key 71 + * @returns {Promise<{privateKey: string, publicKey: `did:key:${string}`}>} 72 + */ 73 + createANewSecp256k1(): Promise<{ 74 + privateKey: string 75 + publicKey: `did:key:${string}` 76 + }> 77 + /** 78 + * Signs a new operation with the provided signing key, and information and submits it to the plc directory 79 + * @param did {string} - The user's did 80 + * @param signingRotationKey { P256PrivateKey|Secp256k1PrivateKey} - The keypair to sign the op with 81 + * @param alsoKnownAs {string[]} 82 + * @param rotationKeys {string[]} 83 + * @param pds {string} 84 + * @param verificationKey {string} - The public verification key 85 + * @param prev {string} - The previous valid operation's cid. 86 + * @returns {Promise<void>} 87 + */ 88 + signAndPublishNewOp( 89 + did: string, 90 + signingRotationKey: P256PrivateKey | Secp256k1PrivateKey, 91 + alsoKnownAs: string[], 92 + rotationKeys: string[], 93 + pds: string, 94 + verificationKey: string, 95 + prev: string, 96 + ): Promise<void> 97 + /** 98 + * Takes a multi or hex based private key and returns a keypair 99 + * @param privateKeyString {string} 100 + * @param type {string} - secp256k1 or p256, needed if the private key is hex based, can be assumed if it's a multikey 101 + * @returns {Promise<{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}>} 102 + */ 103 + getKeyPair( 104 + privateKeyString: string, 105 + type?: string, 106 + ): Promise<{ 107 + type: string 108 + didPublicKey: `did:key:${string}` 109 + keypair: P256PrivateKey | Secp256k1PrivateKey 110 + }> 111 + /** 112 + * Submits a new operation to the plc directory 113 + * @param did {string} - The user's did 114 + * @param operation 115 + * @returns {Promise<void>} 116 + */ 117 + pushPlcOperation(did: string, operation: any): Promise<void> 118 + /** 119 + * Creates a new service auth token for a user. This is what is used to create a new account on a PDS for your did 120 + * 121 + * @param iss The user's did 122 + * @param aud The did:web, if it's a PDS it's usually from /xrpc/com.atproto.server.describeServer 123 + * @param keypair The keypair to sign with only supporting ES256K atm 124 + * @param lxm The lxm which is usually com.atproto.server.createAccount for creating a new account 125 + * @returns {Promise<string>} 126 + */ 127 + createANewServiceAuthToken(iss: any, aud: any, keypair: any, lxm: any): Promise<string> 117 128 } 118 - import { defs } from '@atcute/did-plc'; 119 - import { P256PrivateKey } from '@atcute/crypto'; 120 - import { Secp256k1PrivateKey } from '@atcute/crypto'; 121 - //# sourceMappingURL=plc-ops.d.ts.map 129 + import { defs } from '@atcute/did-plc' 130 + import { P256PrivateKey } from '@atcute/crypto' 131 + import { Secp256k1PrivateKey } from '@atcute/crypto' 132 + //# sourceMappingURL=plc-ops.d.ts.map
+92 -77
packages/moover/types/restore.d.ts
··· 1 - export type Operation = import("@atcute/did-plc").Operation; 1 + export type Operation = import('@atcute/did-plc').Operation 2 2 export class Restore { 3 - /** 4 - * 5 - * @param pdsMooverInstance {string} - The url of the pds moover instance to restore from. Defaults to https://pdsmover.com 6 - */ 7 - constructor(pdsMooverInstance?: string); 8 - /** 9 - * If you want to use a different plc directory create your own instance of the plc ops class and pass it in here 10 - * @type {PlcOps} */ 11 - plcOps: PlcOps; 12 - /** 13 - * This is the base url for the pds moover instance used to restore the files from a backup. 14 - * @type {string} 15 - */ 16 - pdsMooverInstance: string; 17 - /** 18 - * To keep it simple, only uses secp256k for the temp verification key that is used to create the new account on the new PDS 19 - * and is temporarily assigned to the user's account on PLC 20 - * @type {null|Secp256k1PrivateKeyExportable} 21 - */ 22 - tempVerificationKeypair: null | Secp256k1PrivateKeyExportable; 23 - /** @type {AtpAgent} */ 24 - atpAgent: AtpAgent; 25 - /** 26 - * The keypair that is used to sign the plc operation 27 - * @type {null|{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}} 28 - */ 29 - recoveryRotationKeyPair: null | { 30 - type: string; 31 - didPublicKey: `did:key:${string}`; 32 - keypair: P256PrivateKey | Secp256k1PrivateKey; 33 - }; 34 - /** 35 - * If this is true we are just restoring the repo and blobs. Ideally for rerunning a restore process after account recovery 36 - * @type {boolean} 37 - */ 38 - RestoreFromBackup: boolean; 39 - /** 40 - * If set to true then it will do the account recovery. Writes a temp key to the did doc, 41 - * create a new account on the new pds, and then submit a new plc op for the pds to have control (finishes the migration, can always restore the backup later) 42 - * @type {boolean} 43 - */ 44 - AccountRecovery: boolean; 45 - /** 46 - * Recovers an account with the users rotation key and restores the repo from a PDS MOOver backup 47 - * This method can fail, and the account was still recovered, it's best to check the PLC logs to see where an account stands before reruns 48 - * @param rotationKey {string} - The users private rotation key, can be a multi key or hex key 49 - * @param rotationKeyType {string} - The type of the key, secp256k1 or p256. Required if the key is in hex format, defaults to secp256k1 50 - * @param currentHandleOrDid {string} - The users current handle or did, if they don't have a DNS record it will have to be their did for success 51 - * @param newPDS {string} - The new PDS url, like https://coolnewpds.com 52 - * @param newHandle {string} - Can be the users DNS handle if it is already setup with their did, if not it's bob.mypds.com 53 - * @param newPassword {string} - The new password for the new account 54 - * @param newEmail {string} - The new email for the new account 55 - * @param inviteCode {string|null} - The invite code for the new PDS if it requires one 56 - * @param cidToRestoreTo {string|null} - The cid of the plc op to restore to, used mostly to revert a fraudulent plc op. Want to give it the last valid operations cid 57 - * @param onStatus {function|null} - A function that takes a string used to update the UI. Like (status) => console.log(status) 58 - * @returns {Promise<void>} If there is a failure during restoring the back up (after the status Success! Restoring your repo...) then your account is most likely 59 - * recovered and future runs need to have the RestoreFromBackup flag set to true and AccountRecovery set to false. 60 - */ 61 - recover(rotationKey: string, rotationKeyType: string, currentHandleOrDid: string, newPDS: string, newHandle: string, newPassword: string, newEmail: string, inviteCode: string | null, cidToRestoreTo?: string | null, onStatus?: Function | null): Promise<void>; 62 - /** 63 - * This method signs the plc operation over to the new PDS and activates the account 64 - * Assumes you have already created a new account during the recovery process and logged in 65 - * Uses the recommended did doc from the PDS as a base and adds the users rotation key to the rotation keys array 66 - * 67 - * @param usersDid 68 - * @param additionalRotationKeysToAdd 69 - * @param prevCid 70 - * @returns {Promise<void>} 71 - */ 72 - signRestorePlcOperation(usersDid: any, additionalRotationKeysToAdd: any[], prevCid: any): Promise<void>; 3 + /** 4 + * 5 + * @param pdsMooverInstance {string} - The url of the pds moover instance to restore from. Defaults to https://pdsmover.com 6 + */ 7 + constructor(pdsMooverInstance?: string) 8 + /** 9 + * If you want to use a different plc directory create your own instance of the plc ops class and pass it in here 10 + * @type {PlcOps} */ 11 + plcOps: PlcOps 12 + /** 13 + * This is the base url for the pds moover instance used to restore the files from a backup. 14 + * @type {string} 15 + */ 16 + pdsMooverInstance: string 17 + /** 18 + * To keep it simple, only uses secp256k for the temp verification key that is used to create the new account on the new PDS 19 + * and is temporarily assigned to the user's account on PLC 20 + * @type {null|Secp256k1PrivateKeyExportable} 21 + */ 22 + tempVerificationKeypair: null | Secp256k1PrivateKeyExportable 23 + /** @type {AtpAgent} */ 24 + atpAgent: AtpAgent 25 + /** 26 + * The keypair that is used to sign the plc operation 27 + * @type {null|{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}} 28 + */ 29 + recoveryRotationKeyPair: null | { 30 + type: string 31 + didPublicKey: `did:key:${string}` 32 + keypair: P256PrivateKey | Secp256k1PrivateKey 33 + } 34 + /** 35 + * If this is true we are just restoring the repo and blobs. Ideally for rerunning a restore process after account recovery 36 + * @type {boolean} 37 + */ 38 + RestoreFromBackup: boolean 39 + /** 40 + * If set to true then it will do the account recovery. Writes a temp key to the did doc, 41 + * create a new account on the new pds, and then submit a new plc op for the pds to have control (finishes the migration, can always restore the backup later) 42 + * @type {boolean} 43 + */ 44 + AccountRecovery: boolean 45 + /** 46 + * Recovers an account with the users rotation key and restores the repo from a PDS MOOver backup 47 + * This method can fail, and the account was still recovered, it's best to check the PLC logs to see where an account stands before reruns 48 + * @param rotationKey {string} - The users private rotation key, can be a multi key or hex key 49 + * @param rotationKeyType {string} - The type of the key, secp256k1 or p256. Required if the key is in hex format, defaults to secp256k1 50 + * @param currentHandleOrDid {string} - The users current handle or did, if they don't have a DNS record it will have to be their did for success 51 + * @param newPDS {string} - The new PDS url, like https://coolnewpds.com 52 + * @param newHandle {string} - Can be the users DNS handle if it is already setup with their did, if not it's bob.mypds.com 53 + * @param newPassword {string} - The new password for the new account 54 + * @param newEmail {string} - The new email for the new account 55 + * @param inviteCode {string|null} - The invite code for the new PDS if it requires one 56 + * @param cidToRestoreTo {string|null} - The cid of the plc op to restore to, used mostly to revert a fraudulent plc op. Want to give it the last valid operations cid 57 + * @param onStatus {function|null} - A function that takes a string used to update the UI. Like (status) => console.log(status) 58 + * @returns {Promise<void>} If there is a failure during restoring the back up (after the status Success! Restoring your repo...) then your account is most likely 59 + * recovered and future runs need to have the RestoreFromBackup flag set to true and AccountRecovery set to false. 60 + */ 61 + recover( 62 + rotationKey: string, 63 + rotationKeyType: string, 64 + currentHandleOrDid: string, 65 + newPDS: string, 66 + newHandle: string, 67 + newPassword: string, 68 + newEmail: string, 69 + inviteCode: string | null, 70 + cidToRestoreTo?: string | null, 71 + onStatus?: Function | null, 72 + ): Promise<void> 73 + /** 74 + * This method signs the plc operation over to the new PDS and activates the account 75 + * Assumes you have already created a new account during the recovery process and logged in 76 + * Uses the recommended did doc from the PDS as a base and adds the users rotation key to the rotation keys array 77 + * 78 + * @param usersDid 79 + * @param additionalRotationKeysToAdd 80 + * @param prevCid 81 + * @returns {Promise<void>} 82 + */ 83 + signRestorePlcOperation( 84 + usersDid: any, 85 + additionalRotationKeysToAdd: any[], 86 + prevCid: any, 87 + ): Promise<void> 73 88 } 74 - import { PlcOps } from './plc-ops.js'; 75 - import { Secp256k1PrivateKeyExportable } from '@atcute/crypto'; 76 - import { AtpAgent } from '@atproto/api'; 77 - import { P256PrivateKey } from '@atcute/crypto'; 78 - import { Secp256k1PrivateKey } from '@atcute/crypto'; 79 - //# sourceMappingURL=restore.d.ts.map 89 + import { PlcOps } from './plc-ops.js' 90 + import { Secp256k1PrivateKeyExportable } from '@atcute/crypto' 91 + import { AtpAgent } from '@atproto/api' 92 + import { P256PrivateKey } from '@atcute/crypto' 93 + import { Secp256k1PrivateKey } from '@atcute/crypto' 94 + //# sourceMappingURL=restore.d.ts.map
+23 -23
packages/moover/vite.config.js
··· 1 - import {dirname, resolve} from 'node:path' 2 - import {fileURLToPath} from 'node:url' 3 - import {defineConfig} from 'vite' 1 + import { dirname, resolve } from 'node:path' 2 + import { fileURLToPath } from 'node:url' 3 + import { defineConfig } from 'vite' 4 4 5 5 const __dirname = dirname(fileURLToPath(import.meta.url)) 6 6 7 7 export default defineConfig({ 8 - build: { 9 - lib: { 10 - entry: resolve(__dirname, 'lib/main.js'), 11 - name: '@pds-moover/moover', 12 - // the proper extensions will be added 13 - fileName: 'pds-moover', 14 - }, 15 - rollupOptions: { 16 - // // make sure to externalize deps that shouldn't be bundled 17 - // // into your library 18 - // external: ['vue'], 19 - // output: { 20 - // // Provide global variables to use in the UMD build 21 - // // for externalized deps 22 - // globals: { 23 - // vue: 'Vue', 24 - // }, 25 - // }, 26 - }, 8 + build: { 9 + lib: { 10 + entry: resolve(__dirname, 'lib/main.js'), 11 + name: '@pds-moover/moover', 12 + // the proper extensions will be added 13 + fileName: 'pds-moover', 14 + }, 15 + rollupOptions: { 16 + // // make sure to externalize deps that shouldn't be bundled 17 + // // into your library 18 + // external: ['vue'], 19 + // output: { 20 + // // Provide global variables to use in the UMD build 21 + // // for externalized deps 22 + // globals: { 23 + // vue: 'Vue', 24 + // }, 25 + // }, 27 26 }, 28 - }) 27 + }, 28 + })
-1
shared/src/jobs/mod.rs
··· 9 9 use crate::db::models; 10 10 use crate::db::models::BlobModel; 11 11 use apalis::prelude::*; 12 - use log::info; 13 12 use serde::{Deserialize, Serialize}; 14 13 use serde_json::{self}; 15 14 use sqlx::{Pool, Postgres, query};