Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
239 changes: 105 additions & 134 deletions README.md

Large diffs are not rendered by default.

25,917 changes: 13,283 additions & 12,634 deletions package-lock.json

Large diffs are not rendered by default.

27 changes: 12 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,19 @@
"scripts": {
"build": "npm run prepack && npm run postpack",
"postpack": "rm -f oclif.manifest.json",
"posttest": "eslint . --ext .ts --config .eslintrc",
"lint": "eslint . --ext .ts --config .eslintrc",
"prepack": "rm -rf lib && tsc -b && npx oclif manifest && npx oclif readme && shx cp -r src/templates lib",
"testbase": "nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"",
"test": "npm run testbase -- \"test/**/*.test.ts\"",
"testsome": "nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\" --exclude \"**/boilerplate.test.ts\"",
"pretest": "tsc -b",
"test": "mocha --config test/.mocharc.json --forbid-only",
"test:coverage": "nyc --extension .ts mocha --config test/.mocharc.json --forbid-only",
"version": "npx oclif readme && git add README.md"
},
"types": "lib/index.d.ts",
"dependencies": {
"@bloks/numbers": "^26.1.9",
"@greymass/eosio": "^0.5.5",
"@inquirer/input": "^0.0.18-alpha.0",
"@oclif/command": "^1.8.16",
"@oclif/config": "^1.18.3",
"@oclif/core": "^1.7.0",
"@oclif/parser": "^3.8.17",
"@oclif/plugin-help": "^3",
"@oclif/core": "^4.11.0",
"@proton/api": "^28.7.1",
"@proton/js": "^27.5.1",
"@proton/light-api": "^3.3.4",
Expand Down Expand Up @@ -89,15 +85,16 @@
"ws": "^8.5.0"
},
"devDependencies": {
"@oclif/test": "^1",
"@oclif/plugin-help": "^6.2.46",
"@oclif/test": "^4.1.18",
"@types/chai": "^4.2.14",
"@types/debug": "^4.1.5",
"@types/ejs": "^3.1.0",
"@types/elliptic": "^6.4.12",
"@types/ini": "^1.3.30",
"@types/inquirer": "^8.2.1",
"@types/lodash.isequal": "^4.5.6",
"@types/mocha": "^9.1.0",
"@types/mocha": "^10.0.10",
"@types/node": "^14",
"@types/pako": "^1.0.3",
"@types/rimraf": "^3.0.0",
Expand All @@ -110,13 +107,13 @@
"eslint-config-oclif-typescript": "^1.0.2",
"globby": "^10",
"ini": "^2.0.0",
"mocha": "^9.2.2",
"mocha": "^10.8.2",
"nyc": "^15.1.0",
"oclif": "^3.0.0",
"oclif": "^4.23.0",
"rxjs": "^7.5.5",
"shx": "^0.3.4",
"ts-node": "^8",
"tslib": "^1"
"tslib": "^1",
"tsx": "^4.21.0"
},
"overrides": {
"@oclif/core": {
Expand Down
140 changes: 140 additions & 0 deletions scripts/migrate-args.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/usr/bin/env node
/**
* Second-pass codemod: convert legacy `static args = [{ name: 'X', ... }]`
* array syntax to v4 object syntax `static args = { X: Args.string({...}) }`,
* and clean up remaining `flags.X(...)` references.
*
* Run from repo root: `node scripts/migrate-args.mjs`.
*/

import { readFileSync, writeFileSync } from 'node:fs'
import { execSync } from 'node:child_process'

const FILES = execSync('find src -type f -name "*.ts"', { cwd: process.cwd() })
.toString().trim().split('\n').filter(Boolean)

let touched = 0

function convertArgsBlock(body) {
// body is the text inside `static args = [ ... ]`. Returns the replacement
// (the inside of the new object form). We split on top-level commas between
// {...} items.
const items = []
let depth = 0
let start = 0
for (let i = 0; i < body.length; i++) {
const c = body[i]
if (c === '{') depth++
else if (c === '}') depth--
else if (c === ',' && depth === 0) {
const piece = body.slice(start, i).trim()
if (piece) items.push(piece)
start = i + 1
}
}
const tail = body.slice(start).trim()
if (tail) items.push(tail)

const converted = items
.map((item) => {
// item is like `{ name: 'foo', required: true, description: 'bar' }`
const m = item.match(/\{\s*([\s\S]*)\s*\}/)
if (!m) return null
const inner = m[1]

// Parse property pairs (very simple — works for this codebase's args).
const props = {}
// Split on top-level commas, ignoring those inside strings or braces
let d = 0
let s = 0
const parts = []
for (let i = 0; i < inner.length; i++) {
const c = inner[i]
if (c === '{' || c === '[') d++
else if (c === '}' || c === ']') d--
else if (c === ',' && d === 0) {
parts.push(inner.slice(s, i))
s = i + 1
}
}
parts.push(inner.slice(s))

for (const p of parts) {
const kv = p.match(/^\s*([a-zA-Z_]+)\s*:\s*([\s\S]*)$/)
if (kv) props[kv[1]] = kv[2].trim().replace(/,$/, '')
}

const name = props.name?.replace(/^['"]|['"]$/g, '')
if (!name) return null

const otherProps = Object.entries(props)
.filter(([k]) => k !== 'name')
.map(([k, v]) => ` ${k}: ${v},`)

if (otherProps.length === 0) {
return ` ${name}: Args.string({})`
}
return ` ${name}: Args.string({\n${otherProps.join('\n')}\n })`
})
.filter(Boolean)

return converted.join(',\n')
}

for (const file of FILES) {
let src = readFileSync(file, 'utf8')
const original = src

// 1. Convert static args = [...] to static args = {...}
// Match the array form, capturing whatever is between [ and the matching ]
src = src.replace(
/static\s+args\s*=\s*\[\s*([\s\S]*?)\s*\]/g,
(match, body) => {
try {
const converted = convertArgsBlock(body)
return `static args = {\n${converted},\n }`
} catch {
return match
}
},
)

// 2. Bare `flags.X(` -> `Flags.X(`
src = src.replace(/\bflags\.(boolean|string|integer|enum|build|option|help|version)\(/g, 'Flags.$1(')

// 3. Drop `help: Flags.help({...})` — v4 commands have help built in
src = src.replace(/\s*help:\s*Flags\.help\([^)]*\)\s*,?\s*\n?/g, '\n')

// 4. If we now use Args.X(...), import Args from @oclif/core
if (/\bArgs\.\w+\(/.test(src) && !/\bArgs\b[^,}]*\}\s*from\s*["']@oclif\/core["']/m.test(src)) {
const coreImportRe = /^import\s*\{([^}]+)\}\s*from\s*["']@oclif\/core["'];?$/m
const m = src.match(coreImportRe)
if (m) {
const existing = m[1].split(',').map((n) => n.trim()).filter(Boolean)
if (!existing.includes('Args')) existing.push('Args')
src = src.replace(coreImportRe, `import { ${existing.join(', ')} } from '@oclif/core'`)
} else {
src = `import { Args } from '@oclif/core'\n` + src
}
}

// 5. If we now use Flags.X(...), ensure Flags is imported
if (/\bFlags\.\w+\(/.test(src) && !/\bFlags\b[^,}]*\}\s*from\s*["']@oclif\/core["']/m.test(src)) {
const coreImportRe = /^import\s*\{([^}]+)\}\s*from\s*["']@oclif\/core["'];?$/m
const m = src.match(coreImportRe)
if (m) {
const existing = m[1].split(',').map((n) => n.trim()).filter(Boolean)
if (!existing.includes('Flags')) existing.push('Flags')
src = src.replace(coreImportRe, `import { ${existing.join(', ')} } from '@oclif/core'`)
} else {
src = `import { Flags } from '@oclif/core'\n` + src
}
}

if (src !== original) {
writeFileSync(file, src)
touched++
}
}

console.log(`migrate-args: rewrote ${touched} files`)
118 changes: 118 additions & 0 deletions scripts/migrate-oclif.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#!/usr/bin/env node
/**
* One-time codemod: migrate proton-cli command files off @oclif/command (v1)
* to @oclif/core (v4) and the local ux shim at src/utils/ux.ts.
*
* Run from the repo root: `node scripts/migrate-oclif.mjs`.
*/

import { readFileSync, writeFileSync } from 'node:fs'
import { execSync } from 'node:child_process'
import { relative, dirname, posix } from 'node:path'

const ROOT = process.cwd()
const FILES = execSync('find src -type f -name "*.ts"', { cwd: ROOT })
.toString()
.trim()
.split('\n')
.filter(Boolean)

let touched = 0

for (const file of FILES) {
if (file === 'src/utils/ux.ts') continue
let src = readFileSync(file, 'utf8')
const original = src

// 1. Drop legacy @oclif/command imports — covers Command, flags, both, either order
src = src.replace(
/^import\s*\{[^}]*\}\s*from\s*["']@oclif\/command["'];?\s*$/gm,
'',
)

// 2. Drop @oclif/core imports of CliUx (we use the shim instead).
// Preserve other named exports from the same import.
src = src.replace(
/^import\s*\{([^}]+)\}\s*from\s*["']@oclif\/core["'];?\s*$/gm,
(line, names) => {
const filtered = names
.split(',')
.map((n) => n.trim())
.filter((n) => n && n !== 'CliUx')
if (filtered.length === 0) return ''
return `import { ${filtered.join(', ')} } from '@oclif/core'`
},
)

// 3. Body: CliUx.ux.X -> ux.X, bare CliUx -> ux
src = src.replace(/\bCliUx\.ux\b/g, 'ux')
src = src.replace(/\bCliUx\b/g, 'ux')

// 4. await this.parse(...) — only outside of test or non-async contexts.
// Commands have async run(). this.parse(...) should be awaited.
// Be conservative: only rewrite if not already awaited and the surrounding
// function is async.
// Heuristic: replace `const ... = this.parse(` with `const ... = await this.parse(`.
src = src.replace(
/(const\s+\{[^}]*\}\s*=\s*)this\.parse\(/g,
'$1await this.parse(',
)

// 5. If the file uses `ux.something(`, ensure it imports the shim.
const usesUx = /\bux\.\w+/.test(src)
const hasUxImport = /from\s+["'][^"']*\/utils\/ux["']/.test(src)
if (usesUx && !hasUxImport) {
const fileDir = dirname(file)
let importPath = relative(fileDir, 'src/utils/ux').replaceAll('\\', '/')
if (!importPath.startsWith('.')) importPath = './' + importPath
src = `import { ux } from '${importPath}'\n` + src
}

// 6. If we removed the @oclif/command import but still have `extends Command`,
// ensure Command is imported from @oclif/core.
const usesCommandClass = /\bextends\s+Command\b/.test(src)
const usesFlagsHelpers = /\bflags\.(boolean|string|integer|enum|build|option)\(/.test(src)
const usesFlagsCapital = /\bFlags\.(boolean|string|integer|enum|build|option)\(/.test(src)

// Replace lower-case `flags.X({...})` (legacy) with `Flags.X({...})` (v4)
if (usesFlagsHelpers && !usesFlagsCapital) {
src = src.replace(/\bflags\.(boolean|string|integer|enum|build|option)\(/g, 'Flags.$1(')
}

if (usesCommandClass || usesFlagsCapital || /\bFlags\.\w+\(/.test(src)) {
const hasCommandImport = /from\s+["']@oclif\/core["']/.test(src) && /\bCommand\b/.test(
src.match(/import\s*\{([^}]+)\}\s*from\s*["']@oclif\/core["']/m)?.[1] ?? '',
)
const hasFlagsImport = /import\s*\{[^}]*\bFlags\b[^}]*\}\s*from\s*["']@oclif\/core["']/.test(src)
const needCommand = usesCommandClass && !hasCommandImport
const needFlags = /\bFlags\.\w+\(/.test(src) && !hasFlagsImport

if (needCommand || needFlags) {
// Build or update the @oclif/core import line
const coreImportRe = /^import\s*\{([^}]+)\}\s*from\s*["']@oclif\/core["'];?$/m
const m = src.match(coreImportRe)
if (m) {
const existing = m[1].split(',').map((n) => n.trim()).filter(Boolean)
if (needCommand && !existing.includes('Command')) existing.push('Command')
if (needFlags && !existing.includes('Flags')) existing.push('Flags')
const newLine = `import { ${existing.join(', ')} } from '@oclif/core'`
src = src.replace(coreImportRe, newLine)
} else {
const parts = []
if (needCommand) parts.push('Command')
if (needFlags) parts.push('Flags')
src = `import { ${parts.join(', ')} } from '@oclif/core'\n` + src
}
}
}

// 7. Tidy: collapse multiple blank lines
src = src.replace(/\n{3,}/g, '\n\n')

if (src !== original) {
writeFileSync(file, src)
touched++
}
}

console.log(`migrate-oclif: rewrote ${touched} files`)
9 changes: 5 additions & 4 deletions src/apis/uri/signUri.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { ux } from '../../utils/ux'
import { Authority } from "@proton/api"
import { CliUx } from "@oclif/core"

import { green } from "colors"
import { IProtonLinkSessionManagerSessionExtended, ProtonLinkSessionManager } from "../esr"
import { handleURI } from "./handleUri"
import { signRequest } from "./parseUri"

export const signUri = async (uri: string, auth: Authority, sessionManager: ProtonLinkSessionManager, session?: IProtonLinkSessionManagerSessionExtended) => {
const res = await handleURI(uri, auth, session)
await CliUx.ux.log(green('Transaction Request:'))
await CliUx.ux.styledJSON(res!.resolved.resolvedTransaction)
const accept = await CliUx.ux.confirm('Would you like to sign this transaction?')
await ux.log(green('Transaction Request:'))
await ux.styledJSON(res!.resolved.resolvedTransaction)
const accept = await ux.confirm('Would you like to sign this transaction?')
if (accept) {
await signRequest(
res!.chainId,
Expand Down
Loading