diff --git a/bin/db.ts b/bin/db.ts new file mode 100644 index 0000000..1e287dc --- /dev/null +++ b/bin/db.ts @@ -0,0 +1,153 @@ +/** + * pinme db + * + * pinme db migrate [--dry-run] — run pending migrations against remote D1 + * pinme db migrate:create — create a new migration file + * pinme db query "" [--json] — execute SQL on remote D1 + */ + +import chalk from 'chalk'; +import path from 'path'; +import fs from 'fs-extra'; +import { createHash } from 'crypto'; +import { readWorkerConfig, readProjectData } from './utils/worker-config'; +import { runDbMigrations, queryDb, WorkerApiError } from './utils/worker-api'; +import { getAuthConfig } from './utils/auth'; + +function requireProject(): { projectId: string } { + const auth = getAuthConfig(); + if (!auth) { console.log(chalk.red('Not logged in. Run: pinme login')); process.exit(1); } + const projectData = readProjectData(); + if (!projectData) { + console.log(chalk.red('No project found in current directory. Run: pinme worker deploy')); + process.exit(1); + } + return { projectId: projectData.project_id }; +} + +export async function dbMigrate(opts: { dryRun?: boolean }): Promise { + const { projectId } = requireProject(); + + let config; + try { + config = readWorkerConfig(); + } catch (e: any) { + console.log(chalk.red(e.message)); + process.exit(1); + } + + if (!config.d1) { + console.log(chalk.red('No [d1] section in pinme.toml. Add migrations_dir to use migrations.')); + process.exit(1); + } + + const migrationsDir = path.join(process.cwd(), config.d1.migrations_dir); + if (!fs.existsSync(migrationsDir)) { + console.log(chalk.yellow(`Migrations directory not found: ${config.d1.migrations_dir}`)); + return; + } + + const files = fs.readdirSync(migrationsDir) + .filter((f: string) => f.endsWith('.sql')) + .sort(); + + if (files.length === 0) { + console.log(chalk.yellow('No migration files found.')); + return; + } + + if (opts.dryRun) { + console.log(chalk.cyan('Migrations that would run:')); + for (const f of files) console.log(` ${f}`); + return; + } + + const migrations = files.map((filename: string) => { + const sql = fs.readFileSync(path.join(migrationsDir, filename), 'utf-8'); + const checksum = createHash('sha256').update(sql).digest('hex'); + return { filename, sql, checksum }; + }); + + try { + const result = await runDbMigrations(projectId, migrations); + if (result.applied.length === 0) { + console.log(chalk.green('All migrations already applied.')); + } else { + for (const m of result.applied) console.log(chalk.green(` Applied: ${m}`)); + } + if (result.skipped.length > 0) { + console.log(chalk.gray(` Skipped (already applied): ${result.skipped.join(', ')}`)); + } + } catch (e: any) { + console.log(chalk.red(`Migration failed: ${e.message}`)); + process.exit(1); + } +} + +export function dbMigrateCreate(name: string): void { + let config; + try { + config = readWorkerConfig(); + } catch (e: any) { + console.log(chalk.red(e.message)); + process.exit(1); + } + + const migrationsDir = path.join(process.cwd(), config.d1?.migrations_dir ?? 'schema'); + fs.mkdirpSync(migrationsDir); + + const existing = fs.readdirSync(migrationsDir) + .filter((f: string) => f.endsWith('.sql')) + .sort(); + + let nextNum = 1; + if (existing.length > 0) { + const last = parseInt(existing[existing.length - 1].split('_')[0], 10); + if (!isNaN(last)) nextNum = last + 1; + } + + const numStr = String(nextNum).padStart(3, '0'); + const safeName = name.replace(/[^a-z0-9_]/gi, '_').toLowerCase(); + const filename = `${numStr}_${safeName}.sql`; + fs.writeFileSync( + path.join(migrationsDir, filename), + `-- Migration: ${filename}\n-- Created: ${new Date().toISOString()}\n\n`, + ); + + console.log(chalk.green(`Created: ${config.d1?.migrations_dir ?? 'schema'}/${filename}`)); +} + +export async function dbQuery(sql: string, opts: { json?: boolean }): Promise { + const { projectId } = requireProject(); + + try { + const result = await queryDb(projectId, sql); + const rows = result.results as Record[]; + + if (opts.json) { + console.log(JSON.stringify(rows, null, 2)); + return; + } + + if (rows.length === 0) { + console.log(chalk.gray('No results.')); + return; + } + + const keys = Object.keys(rows[0]); + console.log(' ' + keys.join('\t')); + console.log(' ' + keys.map(() => '---').join('\t')); + for (const row of rows) { + console.log(' ' + keys.map((k) => String(row[k] ?? '')).join('\t')); + } + console.log(''); + console.log(chalk.gray(`${rows.length} row${rows.length === 1 ? '' : 's'} (${result.meta.duration_ms.toFixed(1)}ms)`)); + } catch (e: any) { + if (e instanceof WorkerApiError) { + console.log(chalk.red(`Query failed: ${e.message}`)); + } else { + console.log(chalk.red(`Error: ${e.message}`)); + } + process.exit(1); + } +} diff --git a/bin/index.ts b/bin/index.ts index 3e2e50d..d494277 100644 --- a/bin/index.ts +++ b/bin/index.ts @@ -20,6 +20,22 @@ import logoutCmd from './logout'; import showAppKeyCmd from './show-appkey'; import myDomainsCmd from './my-domains'; import bindCmd from './bind'; +import loginCmd from './login'; +import { + workerInit, + workerDeploy, + workerStatus, + workerDestroy, + workerLogs, + workerDev, + workerList, + workerSecretSet, + workerSecretList, + workerSecretDelete, + workerSecretImport, +} from './worker'; +import { dbMigrate, dbMigrateCreate, dbQuery } from './db'; +import whoamiCmd from './whoami'; // display the ASCII art logo function showBanner(): void { @@ -98,6 +114,116 @@ program .description("Alias for 'my-domains' command") .action(() => myDomainsCmd()); +// ── Auth ────────────────────────────────────────────────────────────────────── + +program + .command('login') + .description('Log in with your email (sends a verification code)') + .option('--email ', 'Email address (skips the prompt)') + .action((opts: { email?: string }) => loginCmd(opts)); + +program + .command('whoami') + .description('Show current account identity and tier') + .action(() => whoamiCmd()); + +// ── Worker backend ──────────────────────────────────────────────────────────── + +const workerCmd = program + .command('worker') + .description('Manage Cloudflare Worker backends'); + +workerCmd + .command('init [name]') + .description('Initialize a new worker project') + .option('--template ', 'Template: blank (default) or rest-api') + .action((name: string | undefined, opts: { template?: string }) => workerInit(name, opts)); + +workerCmd + .command('deploy') + .description('Build and deploy the worker in the current directory') + .option('--message ', 'Deploy message') + .option('--dry-run', 'Preview without deploying') + .action((opts: { message?: string; dryRun?: boolean }) => workerDeploy(opts)); + +workerCmd + .command('status') + .description('Show worker status and usage') + .action(() => workerStatus()); + +workerCmd + .command('destroy') + .description('Permanently destroy the worker and its database') + .option('--confirm', 'Skip confirmation prompt') + .action((opts: { confirm?: boolean }) => workerDestroy(opts)); + +workerCmd + .command('logs') + .description('Stream live logs from the deployed worker') + .action(() => workerLogs()); + +workerCmd + .command('dev') + .description('Start local development server (requires wrangler)') + .option('--port ', 'Port to listen on (default 8787)') + .action((opts: { port?: string }) => workerDev(opts)); + +workerCmd + .command('list') + .description('List all your worker projects') + .action(() => workerList()); + +// ── Worker secrets ──────────────────────────────────────────────────────────── + +const workerSecretCmd = workerCmd + .command('secret') + .description('Manage worker secrets (environment variables)'); + +workerSecretCmd + .command('set [value]') + .description('Set a secret (prompts for value if not provided)') + .action((key: string, value: string | undefined) => workerSecretSet(key, value)); + +workerSecretCmd + .command('list') + .description('List secret names (values are never shown)') + .action(() => workerSecretList()); + +workerSecretCmd + .command('delete ') + .description('Delete a secret') + .action((key: string) => workerSecretDelete(key)); + +workerSecretCmd + .command('import ') + .description('Import secrets from a .env file') + .action((file: string) => workerSecretImport(file)); + +// ── Database ────────────────────────────────────────────────────────────────── + +const dbCmd = program + .command('db') + .description("Manage the worker's D1 database"); + +dbCmd + .command('migrate') + .description('Run pending SQL migrations against the remote database') + .option('--dry-run', 'Show pending migrations without applying') + .action((opts: { dryRun?: boolean }) => dbMigrate(opts)); + +dbCmd + .command('migrate:create ') + .description('Create a new migration file in the migrations directory') + .action((name: string) => dbMigrateCreate(name)); + +dbCmd + .command('query ') + .description('Execute a SQL query on the remote database') + .option('--json', 'Output results as JSON') + .action((sql: string, opts: { json?: boolean }) => dbQuery(sql, opts)); + +// ── IPFS history ────────────────────────────────────────────────────────────── + program .command('list') .description('show upload history') @@ -153,13 +279,29 @@ program.on('--help', () => { console.log(' $ pinme export --output '); console.log(' $ pinme rm '); console.log(' $ pinme set-appkey '); + console.log(' $ pinme login'); console.log(' $ pinme show-appkey'); console.log(' $ pinme logout'); + console.log(' $ pinme whoami'); console.log(' $ pinme my-domains'); console.log(' $ pinme domain'); console.log(' $ pinme list -l 5'); console.log(' $ pinme ls'); - console.log(' $ pinme help'); + console.log(''); + console.log('Worker backends (Cloudflare):'); + console.log(' $ pinme worker init my-api --template rest-api'); + console.log(' $ pinme worker deploy'); + console.log(' $ pinme worker status'); + console.log(' $ pinme worker logs'); + console.log(' $ pinme worker dev'); + console.log(' $ pinme worker destroy'); + console.log(' $ pinme worker list'); + console.log(' $ pinme worker secret set API_KEY'); + console.log(' $ pinme worker secret list'); + console.log(' $ pinme worker secret delete API_KEY'); + console.log(' $ pinme worker secret import .env'); + console.log(' $ pinme db migrate'); + console.log(' $ pinme db query "SELECT * FROM items"'); console.log(''); console.log( 'For more information, visit: https://github.com/glitternetwork/pinme', diff --git a/bin/login.ts b/bin/login.ts new file mode 100644 index 0000000..a821ca3 --- /dev/null +++ b/bin/login.ts @@ -0,0 +1,87 @@ +/** + * pinme login [--email ] + * + * Authenticate via email OTP. + * Credentials are saved to ~/.pinme/auth.json (same file used by all pinme commands). + */ + +import chalk from 'chalk'; +import inquirer from 'inquirer'; +import { getAuthConfig, setAuthTokenDirect } from './utils/auth'; +import { sendEmailCode, verifyEmailCode } from './utils/worker-api'; + +export default async function loginCmd(opts: { email?: string } = {}): Promise { + // Check if already logged in + const existing = getAuthConfig(); + if (existing) { + console.log(chalk.yellow(`Already logged in as: ${existing.address}`)); + const { reauth } = await inquirer.prompt([ + { + type: 'confirm', + name: 'reauth', + message: 'Log in with a different account?', + default: false, + }, + ]); + if (!reauth) return; + } + + // Get email + let email: string = opts.email ?? ''; + + if (!email) { + const ans = await inquirer.prompt([ + { + type: 'input', + name: 'email', + message: 'Email address:', + validate: (v: string) => + v.includes('@') ? true : 'Please enter a valid email address.', + }, + ]); + email = ans.email.trim(); + } + + if (!email || !email.includes('@')) { + console.log(chalk.red('Invalid email address.')); + process.exit(1); + } + + // Send OTP + process.stdout.write(chalk.cyan(`Sending verification code to ${email}... `)); + try { + await sendEmailCode(email); + console.log(chalk.green('Sent!')); + } catch (e: any) { + console.log(chalk.red(`\nFailed: ${e?.message || e}`)); + process.exit(1); + } + + // Get code + const { code } = await inquirer.prompt([ + { + type: 'input', + name: 'code', + message: 'Enter the 6-digit code:', + validate: (v: string) => + /^\d{6}$/.test(v.trim()) ? true : 'Expected a 6-digit number.', + }, + ]); + + // Verify + process.stdout.write(chalk.cyan('Verifying... ')); + let authData: { token: string; address: string }; + try { + authData = await verifyEmailCode(email, code.trim()); + console.log(chalk.green('Done!')); + } catch (e: any) { + console.log(chalk.red(`\nVerification failed: ${e?.message || e}`)); + process.exit(1); + } + + // Save — reuse the existing auth.json format + setAuthTokenDirect(authData.address, authData.token); + + console.log(chalk.green(`\nLogged in as: ${authData.address}`)); + console.log(chalk.gray('Credentials saved to ~/.pinme/auth.json\n')); +} diff --git a/bin/utils/auth.ts b/bin/utils/auth.ts index 8f5a663..030161e 100644 --- a/bin/utils/auth.ts +++ b/bin/utils/auth.ts @@ -59,6 +59,12 @@ export function getAuthConfig(): AuthConfig | null { } } +/** Save address + token directly (e.g. from email OTP login). */ +export function setAuthTokenDirect(address: string, token: string): void { + ensureConfigDir(); + fs.writeJsonSync(AUTH_FILE, { address, token }, { spaces: 2 }); +} + export function getAuthHeaders(): Record { const conf = getAuthConfig(); if (!conf) { diff --git a/bin/utils/worker-api.ts b/bin/utils/worker-api.ts new file mode 100644 index 0000000..6a0a27c --- /dev/null +++ b/bin/utils/worker-api.ts @@ -0,0 +1,282 @@ +/** + * HTTP client for api.pinme.pro (Worker control plane). + * Uses pinme auth headers (X-Pinme-Address + X-Pinme-Token). + */ + +import { getAuthConfig } from './auth'; + +export const WORKER_API_BASE = + process.env.PINME_WORKER_API_URL ?? 'https://api.pinme.pro'; + +export const IPFS_PROXY_BASE = + process.env.PINME_API_BASE ?? 'https://ipfs-proxy.opena.chat'; + +export class WorkerApiError extends Error { + constructor( + public readonly code: string, + message: string, + public readonly status: number, + ) { + super(message); + this.name = 'WorkerApiError'; + } +} + +function getWorkerAuthHeaders(): Record { + const conf = getAuthConfig(); + if (!conf) { + throw new WorkerApiError( + 'UNAUTHENTICATED', + 'Not logged in. Run: pinme login', + 401, + ); + } + return { + 'X-Pinme-Address': conf.address, + 'X-Pinme-Token': conf.token, + }; +} + +async function request( + path: string, + options: RequestInit = {}, +): Promise { + const headers: Record = { + ...getWorkerAuthHeaders(), + ...(options.headers as Record ?? {}), + }; + + if (!(options.body instanceof FormData) && options.body) { + headers['Content-Type'] = 'application/json'; + } + + const resp = await fetch(`${WORKER_API_BASE}${path}`, { + ...options, + headers, + }); + + const ct = resp.headers.get('content-type') ?? ''; + let body: unknown; + if (ct.includes('application/json')) { + body = await resp.json(); + } else { + body = await resp.text(); + } + + if (!resp.ok) { + const errBody = body as { error?: { code?: string; message?: string } }; + const code = errBody.error?.code ?? 'UNKNOWN_ERROR'; + const message = + errBody.error?.message ?? `Request failed with status ${resp.status}`; + throw new WorkerApiError(code, message, resp.status); + } + + return body as T; +} + +// ── Email login (ipfs-proxy) ────────────────────────────────────────────────── + +export async function sendEmailCode(email: string): Promise { + const resp = await fetch(`${IPFS_PROXY_BASE}/api/v4/email/send-code`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email }), + }); + + if (!resp.ok) { + let msg = `Failed to send verification code (${resp.status})`; + try { + const b = (await resp.json()) as { msg?: string; message?: string }; + msg = b.msg ?? b.message ?? msg; + } catch { /* ignore */ } + throw new Error(msg); + } +} + +export async function verifyEmailCode( + email: string, + code: string, +): Promise<{ token: string; address: string }> { + const resp = await fetch(`${IPFS_PROXY_BASE}/api/v4/email/verify-code`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, code }), + }); + + if (!resp.ok) { + let msg = `Verification failed (${resp.status})`; + try { + const b = (await resp.json()) as { msg?: string; message?: string }; + msg = b.msg ?? b.message ?? msg; + } catch { /* ignore */ } + throw new Error(msg); + } + + const data = (await resp.json()) as { token?: string; address?: string; data?: { token?: string; address?: string } }; + // ipfs-proxy may wrap in { code, data: { token, address } } + const token = data.token ?? data.data?.token; + const address = data.address ?? data.data?.address ?? email; + + if (!token) throw new Error('No token in verification response.'); + return { token, address }; +} + +// ── Deploy ──────────────────────────────────────────────────────────────────── + +export interface DeployOptions { + config: object; + workerCode: string; + migrations?: Array<{ filename: string; sql: string }>; + message?: string; + projectId?: string; +} + +export interface DeployResponse { + project_id: string; + url: string; + version: number; + migrations_applied: string[]; + tier: string; + limits?: { + requests_month: number; + secrets: number; + db_mb: number; + worker_kb: number; + }; + usage?: { + requests_used: number; + requests_limit: number; + }; +} + +export async function deployWorker(opts: DeployOptions): Promise { + const form = new FormData(); + form.append('config', JSON.stringify(opts.config)); + form.append( + 'worker', + new Blob([opts.workerCode], { type: 'application/javascript' }), + 'worker.js', + ); + + for (const m of opts.migrations ?? []) { + form.append( + 'migrations[]', + new Blob([m.sql], { type: 'text/plain' }), + m.filename, + ); + } + + if (opts.message) form.append('message', opts.message); + if (opts.projectId) form.append('project_id', opts.projectId); + + return request('/v1/deploy', { method: 'POST', body: form }); +} + +// ── Status ──────────────────────────────────────────────────────────────────── + +export interface StatusResponse { + project_id: string; + status: string; + url: string; + tier: string; + version: number; + last_deployed: string | null; + usage: { + requests_month: number; + requests_limit: number; + resets_at: string; + }; + resources: { + db_size_bytes: number; + worker_size_bytes: number; + secret_count: number; + secret_names: string[]; + }; + migrations: Array<{ filename: string; applied_at: string }>; +} + +export async function getWorkerStatus(projectId: string): Promise { + return request(`/v1/status/${projectId}`); +} + +// ── Destroy ─────────────────────────────────────────────────────────────────── + +export async function destroyWorker( + projectId: string, +): Promise<{ destroyed: boolean; project_id: string }> { + return request(`/v1/destroy/${projectId}`, { + method: 'POST', + body: JSON.stringify({ confirm: true }), + }); +} + +// ── Secrets ─────────────────────────────────────────────────────────────────── + +export async function setWorkerSecrets( + projectId: string, + secrets: Record, +): Promise<{ set: string[]; total: number; limit: number }> { + return request(`/v1/secrets/${projectId}`, { + method: 'PUT', + body: JSON.stringify({ secrets }), + }); +} + +export async function deleteWorkerSecret( + projectId: string, + name: string, +): Promise<{ deleted: string; remaining: number }> { + return request(`/v1/secrets/${projectId}/${name}`, { method: 'DELETE' }); +} + +// ── DB ──────────────────────────────────────────────────────────────────────── + +export async function runDbMigrations( + projectId: string, + migrations: Array<{ filename: string; sql: string; checksum: string }>, +): Promise<{ applied: string[]; skipped: string[]; total_applied: number }> { + return request(`/v1/db/migrate/${projectId}`, { + method: 'POST', + body: JSON.stringify({ migrations }), + }); +} + +export async function queryDb( + projectId: string, + sql: string, +): Promise<{ results: unknown[]; meta: { rows_read: number; duration_ms: number } }> { + return request(`/v1/db/query/${projectId}`, { + method: 'POST', + body: JSON.stringify({ sql, params: [] }), + }); +} + +// ── Projects ────────────────────────────────────────────────────────────────── + +export interface ProjectSummary { + project_id: string; + name: string; + url: string; + status: string; + tier: string; + usage: { requests_month: number; requests_limit: number }; + resources: { worker_size_bytes: number; db_size_bytes: number }; + last_deployed: string | null; +} + +export async function listWorkerProjects(): Promise<{ projects: ProjectSummary[] }> { + return request<{ projects: ProjectSummary[] }>('/v1/projects'); +} + +// ── Whoami ──────────────────────────────────────────────────────────────────── + +export interface WhoamiResponse { + uid: string; + tier: 'free' | 'premium'; + project_count: number; + member_since: string; +} + +export async function getWhoami(): Promise { + return request('/v1/whoami'); +} diff --git a/bin/utils/worker-build.ts b/bin/utils/worker-build.ts new file mode 100644 index 0000000..9ac5f56 --- /dev/null +++ b/bin/utils/worker-build.ts @@ -0,0 +1,50 @@ +/** + * Bundles the user's src/worker.ts into a single JS string + * using esbuild. The bundle is sent to api.pinme.pro for deployment. + */ + +import path from 'path'; +import fs from 'fs-extra'; +import esbuild from 'esbuild'; + +export interface BuildResult { + code: string; + sizeBytes: number; +} + +export async function buildWorker(cwd: string = process.cwd()): Promise { + const entryPoint = path.join(cwd, 'src', 'worker.ts'); + + if (!fs.existsSync(entryPoint)) { + // Also accept worker.js + const jsEntry = path.join(cwd, 'src', 'worker.js'); + if (!fs.existsSync(jsEntry)) { + throw new Error('Entry point not found. Expected src/worker.ts or src/worker.js.'); + } + } + + const result = await esbuild.build({ + entryPoints: [fs.existsSync(path.join(cwd, 'src', 'worker.ts')) + ? path.join(cwd, 'src', 'worker.ts') + : path.join(cwd, 'src', 'worker.js')], + bundle: true, + format: 'esm', + platform: 'browser', // CF Workers target + target: 'es2022', + write: false, + minify: false, + logLevel: 'silent', + define: { + 'process.env.NODE_ENV': '"production"', + }, + }); + + const code = result.outputFiles[0].text; + return { code, sizeBytes: Buffer.byteLength(code, 'utf-8') }; +} + +export function formatBytes(bytes: number): string { + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; +} diff --git a/bin/utils/worker-config.ts b/bin/utils/worker-config.ts new file mode 100644 index 0000000..8665c77 --- /dev/null +++ b/bin/utils/worker-config.ts @@ -0,0 +1,105 @@ +/** + * Read/write pinme.toml (worker project config) and + * .pinme/project (per-project ID file). + */ + +import fs from 'fs-extra'; +import path from 'path'; +import toml from '@iarna/toml'; + +// ── pinme.toml ──────────────────────────────────────────────────────────────── + +export interface WorkerConfig { + name: string; + d1?: { migrations_dir: string }; + vars?: Record; + cors?: { origins: string[] }; + secrets?: { required?: string[]; optional?: string[] }; +} + +export function readWorkerConfig(cwd: string = process.cwd()): WorkerConfig { + const configPath = path.join(cwd, 'pinme.toml'); + if (!fs.existsSync(configPath)) { + throw new Error( + `No pinme.toml found in ${cwd}. Run: pinme worker init`, + ); + } + + const raw = fs.readFileSync(configPath, 'utf-8'); + let parsed: Record; + try { + parsed = toml.parse(raw) as Record; + } catch (e: any) { + throw new Error(`Failed to parse pinme.toml: ${e.message}`); + } + + if (!parsed.name || typeof parsed.name !== 'string') { + throw new Error('pinme.toml: "name" is required and must be a string.'); + } + + const name = (parsed.name as string).trim(); + if (!/^[a-z0-9][a-z0-9-]{0,30}[a-z0-9]$/.test(name) && !/^[a-z0-9]{1}$/.test(name)) { + throw new Error( + 'pinme.toml: "name" must be lowercase alphanumeric with hyphens, 1-32 chars.', + ); + } + + const config: WorkerConfig = { name }; + + if (parsed.d1 && typeof parsed.d1 === 'object') { + const d1 = parsed.d1 as Record; + if (d1.migrations_dir && typeof d1.migrations_dir === 'string') { + config.d1 = { migrations_dir: d1.migrations_dir }; + } + } + + if (parsed.vars && typeof parsed.vars === 'object') { + config.vars = parsed.vars as Record; + } + + if (parsed.cors && typeof parsed.cors === 'object') { + const cors = parsed.cors as Record; + if (Array.isArray(cors.origins)) { + config.cors = { origins: cors.origins as string[] }; + } + } + + if (parsed.secrets && typeof parsed.secrets === 'object') { + const secrets = parsed.secrets as Record; + config.secrets = {}; + if (Array.isArray(secrets.required)) config.secrets.required = secrets.required as string[]; + if (Array.isArray(secrets.optional)) config.secrets.optional = secrets.optional as string[]; + } + + return config; +} + +// ── .pinme/project ──────────────────────────────────────────────────────────── + +const LOCAL_DIR = '.pinme'; +const PROJECT_FILE = 'project'; + +export interface ProjectData { + project_id: string; +} + +export function readProjectData(cwd: string = process.cwd()): ProjectData | null { + const p = path.join(cwd, LOCAL_DIR, PROJECT_FILE); + if (!fs.existsSync(p)) return null; + try { + return fs.readJsonSync(p) as ProjectData; + } catch { + return null; + } +} + +export function writeProjectData(data: ProjectData, cwd: string = process.cwd()): void { + const dir = path.join(cwd, LOCAL_DIR); + fs.mkdirpSync(dir); + fs.writeJsonSync(path.join(dir, PROJECT_FILE), data, { spaces: 2 }); +} + +export function deleteProjectData(cwd: string = process.cwd()): void { + const p = path.join(cwd, LOCAL_DIR, PROJECT_FILE); + if (fs.existsSync(p)) fs.removeSync(p); +} diff --git a/bin/whoami.ts b/bin/whoami.ts new file mode 100644 index 0000000..06cdbfb --- /dev/null +++ b/bin/whoami.ts @@ -0,0 +1,36 @@ +/** + * pinme whoami + * Shows current account identity and tier information. + */ + +import chalk from 'chalk'; +import { getWhoami, WorkerApiError } from './utils/worker-api'; +import { getAuthConfig } from './utils/auth'; + +export default async function whoamiCmd(): Promise { + const auth = getAuthConfig(); + if (!auth) { + console.log(chalk.red('Not logged in. Run: pinme login')); + process.exit(1); + } + + try { + const info = await getWhoami(); + + console.log(chalk.green(info.uid)); + console.log(''); + console.log(` Tier: ${info.tier === 'premium' ? chalk.yellow('premium') : 'free'}`); + console.log(` Workers: ${info.project_count}`); + console.log(` Since: ${new Date(info.member_since).toLocaleDateString('en-US', { + month: 'short', day: 'numeric', year: 'numeric', + })}`); + console.log(''); + } catch (e: any) { + if (e instanceof WorkerApiError) { + console.log(chalk.red(`Failed: ${e.message}`)); + } else { + console.log(chalk.red(`Error: ${e.message}`)); + } + process.exit(1); + } +} diff --git a/bin/worker.ts b/bin/worker.ts new file mode 100644 index 0000000..b09aaf6 --- /dev/null +++ b/bin/worker.ts @@ -0,0 +1,642 @@ +/** + * pinme worker + * + * Manage Cloudflare Worker backends via api.pinme.pro. + * + * pinme worker init [name] [--template blank|rest-api] + * pinme worker deploy [--message ] [--dry-run] + * pinme worker status + * pinme worker destroy [--confirm] + * pinme worker logs + * pinme worker dev [--port ] + * pinme worker list + * pinme worker secret set/list/delete/import + */ + +import chalk from 'chalk'; +import inquirer from 'inquirer'; +import path from 'path'; +import fs from 'fs-extra'; +import { spawn } from 'child_process'; +import { readWorkerConfig, readProjectData, writeProjectData, deleteProjectData } from './utils/worker-config'; +import { buildWorker, formatBytes } from './utils/worker-build'; +import { + deployWorker, + getWorkerStatus, + destroyWorker, + listWorkerProjects, + setWorkerSecrets, + deleteWorkerSecret, + WorkerApiError, + WORKER_API_BASE, +} from './utils/worker-api'; +import { getAuthConfig } from './utils/auth'; + +// ── Templates ───────────────────────────────────────────────────────────────── + +const BLANK_WORKER = `export interface Env {} + +export default { + async fetch(request: Request, _env: Env): Promise { + return Response.json({ message: 'Hello from pinme!' }); + }, +}; +`; + +const BLANK_TOML = (name: string) => `name = "${name}" +`; + +const REST_API_WORKER = `export interface Env { + DB: D1Database; +} + +export default { + async fetch(request: Request, env: Env): Promise { + const url = new URL(request.url); + const { pathname, method } = url; + + try { + if (pathname === '/api/items' && method === 'GET') { + const { results } = await env.DB.prepare('SELECT * FROM items ORDER BY created_at DESC').all(); + return Response.json(results); + } + if (pathname === '/api/items' && method === 'POST') { + const body = await request.json() as { name: string; value?: string }; + if (!body.name) return Response.json({ error: 'name is required' }, { status: 400 }); + const row = await env.DB.prepare( + 'INSERT INTO items (name, value) VALUES (?, ?) RETURNING *' + ).bind(body.name, body.value ?? null).first(); + return Response.json(row, { status: 201 }); + } + if (pathname.startsWith('/api/items/') && method === 'DELETE') { + const id = pathname.split('/')[3]; + await env.DB.prepare('DELETE FROM items WHERE id = ?').bind(id).run(); + return Response.json({ deleted: true }); + } + if (pathname === '/health') return Response.json({ ok: true }); + return Response.json({ error: 'Not found' }, { status: 404 }); + } catch (e) { + return Response.json({ error: String(e) }, { status: 500 }); + } + }, +}; +`; + +const REST_API_TOML = (name: string) => `name = "${name}" + +[d1] +migrations_dir = "schema" + +[cors] +origins = ["*"] +`; + +const REST_API_MIGRATION = `CREATE TABLE items ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + value TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) +); +`; + +const GITIGNORE = `.pinme/ +.env +dist/ +node_modules/ +`; + +// ── init ────────────────────────────────────────────────────────────────────── + +export async function workerInit( + name: string | undefined, + opts: { template?: string }, +): Promise { + if (!name) { + const ans = await inquirer.prompt([ + { + type: 'input', + name: 'name', + message: 'Project name:', + validate: (v: string) => + /^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(v.trim()) || v.trim().length === 1 + ? true + : 'Lowercase letters, numbers and hyphens only.', + }, + ]); + name = ans.name.trim(); + } + + const template = (opts.template ?? 'blank') as 'blank' | 'rest-api'; + const targetDir = path.resolve(process.cwd(), name); + + if (fs.existsSync(targetDir)) { + console.log(chalk.red(`Directory "${name}" already exists.`)); + process.exit(1); + } + + fs.mkdirpSync(path.join(targetDir, 'src')); + if (template === 'rest-api') { + fs.mkdirpSync(path.join(targetDir, 'schema')); + fs.writeFileSync(path.join(targetDir, 'src', 'worker.ts'), REST_API_WORKER); + fs.writeFileSync(path.join(targetDir, 'pinme.toml'), REST_API_TOML(name)); + fs.writeFileSync(path.join(targetDir, 'schema', '001_init.sql'), REST_API_MIGRATION); + console.log(chalk.green(`Created ${name}/ with rest-api template`)); + } else { + fs.writeFileSync(path.join(targetDir, 'src', 'worker.ts'), BLANK_WORKER); + fs.writeFileSync(path.join(targetDir, 'pinme.toml'), BLANK_TOML(name)); + console.log(chalk.green(`Created ${name}/`)); + } + + fs.writeFileSync(path.join(targetDir, '.gitignore'), GITIGNORE); + + console.log(''); + console.log(chalk.cyan('Next steps:')); + console.log(` cd ${name}`); + console.log(' pinme worker deploy'); + console.log(''); +} + +// ── deploy ──────────────────────────────────────────────────────────────────── + +export async function workerDeploy( + opts: { message?: string; dryRun?: boolean }, +): Promise { + const auth = getAuthConfig(); + if (!auth) { + console.log(chalk.red('Not logged in. Run: pinme login')); + process.exit(1); + } + + let config; + try { + config = readWorkerConfig(); + } catch (e: any) { + console.log(chalk.red(e.message)); + process.exit(1); + } + + if (opts.dryRun) { + console.log(chalk.cyan(`Dry run: would deploy "${config.name}"`)); + console.log(` Database: ${config.d1 ? 'yes' : 'no'}`); + return; + } + + // Build + process.stdout.write(chalk.cyan('Building src/worker.ts... ')); + let buildResult; + try { + buildResult = await buildWorker(); + console.log(chalk.green(`${formatBytes(buildResult.sizeBytes)}`)); + } catch (e: any) { + console.log(chalk.red(`\nBuild failed: ${e.message}`)); + process.exit(1); + } + + // Collect migrations + const migrations: Array<{ filename: string; sql: string }> = []; + if (config.d1) { + const migrationsDir = path.join(process.cwd(), config.d1.migrations_dir); + if (fs.existsSync(migrationsDir)) { + const files = fs.readdirSync(migrationsDir) + .filter((f: string) => f.endsWith('.sql')) + .sort(); + for (const file of files) { + migrations.push({ + filename: file, + sql: fs.readFileSync(path.join(migrationsDir, file), 'utf-8'), + }); + } + } + } + + const projectData = readProjectData(); + const isNew = !projectData; + + process.stdout.write(chalk.cyan(isNew ? 'Creating project... ' : 'Deploying... ')); + + try { + const resp = await deployWorker({ + config, + workerCode: buildResult.code, + migrations, + message: opts.message, + projectId: projectData?.project_id, + }); + + if (isNew) writeProjectData({ project_id: resp.project_id }); + + console.log(chalk.green('Done!')); + console.log(''); + console.log(chalk.bold(` ${resp.url}`)); + console.log(` Version: v${resp.version}`); + console.log(` Tier: ${resp.tier}`); + + if (resp.migrations_applied.length > 0) { + console.log(` Migrations: ${resp.migrations_applied.join(', ')}`); + } + if (resp.limits) { + console.log(chalk.gray(` Free tier: ${resp.limits.requests_month.toLocaleString()} requests/month`)); + } + if (resp.usage && resp.usage.requests_limit > 0) { + console.log(chalk.gray(` Usage: ${resp.usage.requests_used.toLocaleString()} / ${resp.usage.requests_limit.toLocaleString()} req/month`)); + } + console.log(''); + } catch (e: any) { + console.log(chalk.red('\nFailed!')); + if (e instanceof WorkerApiError) { + console.log(chalk.red(` ${e.message}`)); + if (e.code === 'CONFLICT') { + console.log(chalk.yellow(' Try a different project name in pinme.toml')); + } + } else { + console.log(chalk.red(` ${e.message}`)); + } + process.exit(1); + } +} + +// ── status ──────────────────────────────────────────────────────────────────── + +export async function workerStatus(): Promise { + const auth = getAuthConfig(); + if (!auth) { console.log(chalk.red('Not logged in. Run: pinme login')); process.exit(1); } + + const projectData = readProjectData(); + if (!projectData) { + console.log(chalk.red('No project found. Run: pinme worker deploy')); + process.exit(1); + } + + try { + const s = await getWorkerStatus(projectData.project_id); + console.log(chalk.green(s.project_id)); + console.log(''); + console.log(` URL: ${s.url}`); + console.log(` Status: ${s.status}`); + console.log(` Tier: ${s.tier}`); + console.log(` Version: v${s.version}`); + console.log(` Last deploy: ${s.last_deployed ?? 'never'}`); + console.log(''); + console.log(' Usage this month:'); + const limit = s.usage.requests_limit; + const used = s.usage.requests_month; + if (limit === 0) { + console.log(` Requests: ${used.toLocaleString()} (unlimited)`); + } else { + console.log(` Requests: ${used.toLocaleString()} / ${limit.toLocaleString()}`); + console.log(` Resets: ${new Date(s.usage.resets_at).toLocaleDateString()}`); + } + if (s.resources.secret_count > 0) { + console.log(''); + console.log(` Secrets (${s.resources.secret_count}): ${s.resources.secret_names.join(', ')}`); + } + if (s.migrations.length > 0) { + console.log(''); + console.log(` Migrations (${s.migrations.length}):`); + for (const m of s.migrations) { + console.log(` ${m.filename} ${m.applied_at}`); + } + } + console.log(''); + } catch (e: any) { + console.log(chalk.red(`Error: ${e.message}`)); + process.exit(1); + } +} + +// ── destroy ─────────────────────────────────────────────────────────────────── + +export async function workerDestroy(opts: { confirm?: boolean }): Promise { + const auth = getAuthConfig(); + if (!auth) { console.log(chalk.red('Not logged in. Run: pinme login')); process.exit(1); } + + const projectData = readProjectData(); + if (!projectData) { + console.log(chalk.red('No project found in current directory.')); + process.exit(1); + } + + if (!opts.confirm) { + console.log(chalk.yellow(`\nThis will permanently destroy "${projectData.project_id}" and all its data.\n`)); + const { yes } = await inquirer.prompt([ + { type: 'confirm', name: 'yes', message: 'Are you sure?', default: false }, + ]); + if (!yes) { console.log('Cancelled.'); return; } + } + + process.stdout.write(chalk.cyan(`Destroying ${projectData.project_id}... `)); + try { + await destroyWorker(projectData.project_id); + deleteProjectData(); + console.log(chalk.green('Done!')); + console.log(chalk.gray('You are still logged in. Run "pinme worker deploy" to create a new project.\n')); + } catch (e: any) { + console.log(chalk.red(`\nFailed: ${e.message}`)); + process.exit(1); + } +} + +// ── logs ────────────────────────────────────────────────────────────────────── + +export async function workerLogs(): Promise { + const auth = getAuthConfig(); + if (!auth) { console.log(chalk.red('Not logged in. Run: pinme login')); process.exit(1); } + + const projectData = readProjectData(); + if (!projectData) { + console.log(chalk.red('No project found. Run: pinme worker deploy')); + process.exit(1); + } + + console.log(chalk.cyan('Connecting to log stream...')); + + const url = `${WORKER_API_BASE}/v1/logs/${projectData.project_id}`; + + try { + const resp = await fetch(url, { + headers: { + Accept: 'text/event-stream', + 'X-Pinme-Address': auth.address, + 'X-Pinme-Token': auth.token, + }, + }); + + if (!resp.ok) { + const body = (await resp.json()) as { error?: { message?: string } }; + console.log(chalk.red(body.error?.message ?? `Failed to connect: ${resp.status}`)); + process.exit(1); + } + + if (!resp.body) { console.log(chalk.red('No response body.')); process.exit(1); } + + console.log(chalk.green('Connected. Press Ctrl+C to stop.\n')); + + const reader = resp.body.getReader(); + + process.on('SIGINT', () => { + reader.cancel(); + console.log('\nDisconnected.'); + process.exit(0); + }); + + const decoder = new TextDecoder(); + while (true) { + const { done, value } = await reader.read(); + if (done) break; + const text = decoder.decode(value); + for (const line of text.split('\n')) { + if (!line.startsWith('data: ')) continue; + const data = line.slice(6); + try { + const parsed = JSON.parse(data) as { + logs?: Array<{ message: string; level: string; timestamp: number }>; + exceptions?: Array<{ name: string; message: string }>; + }; + for (const log of parsed.logs ?? []) { + const time = new Date(log.timestamp).toISOString().slice(11, 23); + console.log(` [${time}] ${log.message}`); + } + for (const exc of parsed.exceptions ?? []) { + console.log(chalk.red(` [${exc.name}] ${exc.message}`)); + } + } catch { + if (data !== '[DONE]') console.log(` ${data}`); + } + } + } + } catch (e: any) { + if ((e as NodeJS.ErrnoException).code === 'ERR_USE_AFTER_CLOSE') return; + console.log(chalk.red(`Log stream error: ${e.message}`)); + process.exit(1); + } +} + +// ── dev ─────────────────────────────────────────────────────────────────────── + +export async function workerDev(opts: { port?: string }): Promise { + let config; + try { + config = readWorkerConfig(); + } catch (e: any) { + console.log(chalk.red(e.message)); + process.exit(1); + } + + const port = opts.port ? parseInt(opts.port, 10) : 8787; + const cwd = process.cwd(); + + const devDir = path.join(cwd, '.pinme'); + fs.mkdirpSync(devDir); + + const lines: string[] = [ + `name = "pinme-dev-${config.name}"`, + `main = "src/worker.ts"`, + `compatibility_date = "2024-12-01"`, + `compatibility_flags = ["nodejs_compat"]`, + '', + ]; + + if (config.vars) { + lines.push('[vars]'); + for (const [k, v] of Object.entries(config.vars)) lines.push(`${k} = "${v}"`); + lines.push(''); + } + + if (config.d1) { + lines.push('[[d1_databases]]'); + lines.push('binding = "DB"'); + lines.push(`database_name = "pinme-dev-${config.name}"`); + lines.push('database_id = "local-dev"'); + lines.push(''); + } + + const wranglerDevToml = path.join(devDir, 'wrangler.dev.toml'); + fs.writeFileSync(wranglerDevToml, lines.join('\n')); + + const envPath = path.join(cwd, '.env'); + if (fs.existsSync(envPath)) fs.copyFileSync(envPath, path.join(cwd, '.dev.vars')); + + console.log(chalk.green(`Starting dev server on http://localhost:${port}`)); + console.log(chalk.gray('Press Ctrl+C to stop\n')); + + const localWrangler = path.join(cwd, 'node_modules', '.bin', 'wrangler'); + const useNpx = !fs.existsSync(localWrangler); + const args = ['wrangler', 'dev', '--config', wranglerDevToml, '--port', String(port), '--local']; + + const proc = useNpx + ? spawn('npx', args, { stdio: 'inherit', cwd, env: process.env }) + : spawn(localWrangler, args.slice(1), { stdio: 'inherit', cwd, env: process.env }); + + proc.on('error', (err: NodeJS.ErrnoException) => { + if (err.code === 'ENOENT') { + console.log(chalk.red('wrangler not found. Install it: npm install wrangler')); + } else { + console.log(chalk.red(`Dev server error: ${err.message}`)); + } + process.exit(1); + }); + + proc.on('exit', (code: number | null) => { + if (code !== 0 && code !== null) process.exit(code); + }); + + process.on('SIGINT', () => proc.kill('SIGINT')); + process.on('SIGTERM', () => proc.kill('SIGTERM')); +} + +// ── list (worker projects) ──────────────────────────────────────────────────── + +function fmtBytes(bytes: number): string { + if (bytes === 0) return '—'; + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; +} + +export async function workerList(): Promise { + const auth = getAuthConfig(); + if (!auth) { console.log(chalk.red('Not logged in. Run: pinme login')); process.exit(1); } + + try { + const { projects } = await listWorkerProjects(); + + if (projects.length === 0) { + console.log(chalk.gray('No worker projects found. Run: pinme worker deploy')); + return; + } + + console.log(chalk.green(`${projects.length} project${projects.length !== 1 ? 's' : ''}`)); + console.log(''); + + for (const p of projects) { + const limit = p.usage.requests_limit; + const used = p.usage.requests_month; + const usageStr = limit > 0 + ? `${used.toLocaleString()} / ${limit.toLocaleString()} requests` + : `${used.toLocaleString()} requests (unlimited)`; + + console.log(` ${chalk.bold(p.project_id)}`); + console.log(` URL: ${p.url}`); + console.log(` Tier: ${p.tier}`); + console.log(` Usage: ${usageStr}`); + console.log(` Size: Worker ${fmtBytes(p.resources.worker_size_bytes)} DB ${fmtBytes(p.resources.db_size_bytes)}`); + if (p.last_deployed) { + console.log(` Deployed: ${new Date(p.last_deployed).toLocaleDateString()}`); + } + console.log(''); + } + + console.log(chalk.gray('Dashboard: https://pinme.pro/dashboard')); + } catch (e: any) { + console.log(chalk.red(`Error: ${e.message}`)); + process.exit(1); + } +} + +// ── secret ──────────────────────────────────────────────────────────────────── + +function requireProjectForSecret(): { projectId: string } { + const auth = getAuthConfig(); + if (!auth) { console.log(chalk.red('Not logged in. Run: pinme login')); process.exit(1); } + const projectData = readProjectData(); + if (!projectData) { + console.log(chalk.red('No project found in current directory. Run: pinme worker deploy')); + process.exit(1); + } + return { projectId: projectData.project_id }; +} + +export async function workerSecretSet(key: string, value: string | undefined): Promise { + const { projectId } = requireProjectForSecret(); + + if (!value) { + const ans = await inquirer.prompt([ + { type: 'password', name: 'value', message: `${key}:`, mask: '*' }, + ]); + value = ans.value; + } + + if (!value) { + console.log(chalk.red('Secret value cannot be empty.')); + process.exit(1); + } + + try { + const result = await setWorkerSecrets(projectId, { [key]: value }); + console.log(chalk.green(`Secret "${key}" set (${result.total}/${result.limit} used)`)); + } catch (e: any) { + console.log(chalk.red(`Failed: ${e.message}`)); + process.exit(1); + } +} + +export async function workerSecretList(): Promise { + const { projectId } = requireProjectForSecret(); + try { + const status = await getWorkerStatus(projectId); + const names = status.resources.secret_names; + if (names.length === 0) { + console.log(chalk.gray('No secrets set.')); + } else { + for (const name of names) console.log(` ${name}`); + } + } catch (e: any) { + console.log(chalk.red(`Failed: ${e.message}`)); + process.exit(1); + } +} + +export async function workerSecretDelete(key: string): Promise { + const { projectId } = requireProjectForSecret(); + try { + const result = await deleteWorkerSecret(projectId, key); + console.log(chalk.green(`Secret "${result.deleted}" deleted (${result.remaining} remaining)`)); + } catch (e: any) { + console.log(chalk.red(`Failed: ${e.message}`)); + process.exit(1); + } +} + +export async function workerSecretImport(file: string): Promise { + const { projectId } = requireProjectForSecret(); + + if (!fs.existsSync(file)) { + console.log(chalk.red(`File not found: ${file}`)); + process.exit(1); + } + + function parseEnvFile(content: string): Record { + const result: Record = {}; + for (const line of content.split('\n')) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#')) continue; + const eq = trimmed.indexOf('='); + if (eq === -1) continue; + const k = trimmed.slice(0, eq).trim(); + let v = trimmed.slice(eq + 1).trim(); + if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) { + v = v.slice(1, -1); + } + if (k) result[k] = v; + } + return result; + } + + const secrets = parseEnvFile(fs.readFileSync(file, 'utf-8')); + const count = Object.keys(secrets).length; + + if (count === 0) { + console.log(chalk.yellow('No KEY=VALUE pairs found in file.')); + return; + } + + process.stdout.write(chalk.cyan(`Importing ${count} secret${count > 1 ? 's' : ''}... `)); + + try { + const result = await setWorkerSecrets(projectId, secrets); + console.log(chalk.green(`${result.set.length} imported`)); + } catch (e: any) { + console.log(chalk.red(`\nFailed: ${e.message}`)); + process.exit(1); + } +} diff --git a/package-lock.json b/package-lock.json index 1d02418..5e8e175 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,16 @@ { "name": "pinme", - "version": "1.0.9", + "version": "1.2.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pinme", - "version": "1.0.9", + "version": "1.2.5", "license": "MIT", "dependencies": { + "@iarna/toml": "^2.2.5", + "archiver": "^7.0.1", "axios": "^1.3.2", "base-x": "^5.0.1", "bip39": "^3.1.0", @@ -16,6 +18,7 @@ "commander": "^11.1.0", "crypto-js": "^4.2.0", "dayjs": "^1.11.7", + "esbuild": "^0.25.2", "ethers": "5.7.2", "figlet": "^1.7.0", "form-data": "^4.0.0", @@ -32,7 +35,6 @@ "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^14.1.0", "dotenv": "^16.5.0", - "esbuild": "^0.25.2", "eslint": "^8.33.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^8.6.0", @@ -44,7 +46,7 @@ "rollup-plugin-terser": "^7.0.2" }, "engines": { - "node": ">= 14.18.0" + "node": ">= 18.0.0" } }, "node_modules/@babel/code-frame": { @@ -77,7 +79,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "aix" @@ -93,7 +94,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" @@ -109,7 +109,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -125,7 +124,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "android" @@ -141,7 +139,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -157,7 +154,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -173,7 +169,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -189,7 +184,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -205,7 +199,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -221,7 +214,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -237,7 +229,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "linux" @@ -253,7 +244,6 @@ "cpu": [ "loong64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -269,7 +259,6 @@ "cpu": [ "mips64el" ], - "dev": true, "optional": true, "os": [ "linux" @@ -285,7 +274,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -301,7 +289,6 @@ "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -317,7 +304,6 @@ "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -333,7 +319,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -349,7 +334,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "netbsd" @@ -365,7 +349,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "netbsd" @@ -381,7 +364,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "openbsd" @@ -397,7 +379,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "openbsd" @@ -413,7 +394,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "sunos" @@ -429,7 +409,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -445,7 +424,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -461,7 +439,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -1235,6 +1212,108 @@ "deprecated": "Use @eslint/object-schema instead", "dev": true }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", + "license": "ISC" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -1339,6 +1418,16 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@rollup/plugin-commonjs": { "version": "22.0.2", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz", @@ -1482,6 +1571,18 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -1568,6 +1669,167 @@ "node": ">=4" } }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/archiver-utils/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1699,6 +1961,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -1738,11 +2006,38 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/b4a": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", + "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } }, "node_modules/base-x": { "version": "5.0.1", @@ -1846,6 +2141,15 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2010,32 +2314,158 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "engines": { - "node": ">=16" + "node": ">=16" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/crc32-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2240,6 +2670,12 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -2406,7 +2842,6 @@ "version": "0.25.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", - "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -2880,6 +3315,33 @@ "@ethersproject/wordlists": "5.7.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -2905,6 +3367,12 @@ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -3073,6 +3541,34 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", @@ -3976,6 +4472,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", @@ -4087,8 +4595,22 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } }, "node_modules/jest-worker": { "version": "26.6.2", @@ -4198,6 +4720,54 @@ "json-buffer": "3.0.1" } }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4248,6 +4818,12 @@ "node": ">=4" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -4351,6 +4927,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4368,6 +4953,15 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -4646,6 +5240,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4680,7 +5280,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -4691,6 +5290,22 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -4763,6 +5378,21 @@ "node": ">=6.0.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -4819,6 +5449,36 @@ "node": ">= 6" } }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -5195,7 +5855,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -5207,7 +5866,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -5337,6 +5995,17 @@ "node": ">= 0.4" } }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -5358,6 +6027,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.10", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", @@ -5425,6 +6109,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -5469,6 +6166,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/terser": { "version": "5.42.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.42.0.tgz", @@ -5493,6 +6201,15 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5712,7 +6429,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -5830,6 +6546,57 @@ "node": ">=8" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -5897,6 +6664,60 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zip-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } } } } diff --git a/package.json b/package.json index 55228bf..3a75ec8 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "author": "Glitter Protocol", "license": "MIT", "dependencies": { + "@iarna/toml": "^2.2.5", "archiver": "^7.0.1", "axios": "^1.3.2", "base-x": "^5.0.1", @@ -34,6 +35,7 @@ "commander": "^11.1.0", "crypto-js": "^4.2.0", "dayjs": "^1.11.7", + "esbuild": "^0.25.2", "ethers": "5.7.2", "figlet": "^1.7.0", "form-data": "^4.0.0", @@ -47,7 +49,6 @@ "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^14.1.0", "dotenv": "^16.5.0", - "esbuild": "^0.25.2", "eslint": "^8.33.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^8.6.0", @@ -59,6 +60,6 @@ "rollup-plugin-terser": "^7.0.2" }, "engines": { - "node": ">= 16.13.0" + "node": ">= 18.0.0" } }