everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
192 lines (190 loc) • 6.04 kB
JavaScript
import { Data, Effect } from "effect";
import { generateKeyPairSync } from "node:crypto";
import { execa } from "execa";
//#region src/near-cli.ts
const INSTALLER_URL = `https://github.com/near/near-cli-rs/releases/download/v0.23.5/near-cli-rs-installer.sh`;
const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
var NearCliNotFoundError = class extends Data.TaggedError("NearCliNotFoundError") {
get message() {
return "NEAR CLI not found";
}
};
var NearTransactionError = class extends Data.TaggedError("NearTransactionError") {
constructor(detail) {
super({ detail });
}
get message() {
return this.detail;
}
};
function base64UrlToBytes(input) {
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
return new Uint8Array(Buffer.from(normalized, "base64"));
}
function base58Encode(input) {
if (input.length === 0) return "";
const digits = [0];
for (const byte of input) {
let carry = byte;
for (let i = 0; i < digits.length; i++) {
carry += digits[i] << 8;
digits[i] = carry % 58;
carry = Math.floor(carry / 58);
}
while (carry > 0) {
digits.push(carry % 58);
carry = Math.floor(carry / 58);
}
}
let output = "";
for (const byte of input) if (byte === 0) output += BASE58_ALPHABET[0];
else break;
for (let i = digits.length - 1; i >= 0; i--) output += BASE58_ALPHABET[digits[i]];
return output;
}
function generateNearKeyPair() {
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
const publicJwk = publicKey.export({ format: "jwk" });
const privateJwk = privateKey.export({ format: "jwk" });
if (!publicJwk.x || !privateJwk.d) throw new Error("Failed to generate NEAR keypair");
const publicBytes = base64UrlToBytes(publicJwk.x);
const privateSeed = base64UrlToBytes(privateJwk.d);
const secretBytes = new Uint8Array(privateSeed.length + publicBytes.length);
secretBytes.set(privateSeed, 0);
secretBytes.set(publicBytes, privateSeed.length);
return {
publicKey: `ed25519:${base58Encode(publicBytes)}`,
privateKey: `ed25519:${base58Encode(secretBytes)}`
};
}
async function isNearCliInstalled() {
try {
await execa("near", ["--version"], { stdio: "pipe" });
return true;
} catch {
return false;
}
}
function combineNearOutput(stdout, stderr) {
return [stdout, stderr].filter((value) => value && value.trim().length > 0).join("\n");
}
function extractTransactionHash(output) {
return output.match(/Transaction ID:\s*([A-Za-z0-9]+)/i)?.[1];
}
async function executeKeychainTransaction(config) {
if (!process.stdin.isTTY) throw new NearTransactionError("No TTY available for keychain signing. Set NEAR_PRIVATE_KEY environment variable to sign locally.");
const gas = "300Tgas";
const deposit = "0NEAR";
const argsBase64 = Buffer.from(JSON.stringify(config.args)).toString("base64");
const args = [
"contract",
"call-function",
"as-transaction",
config.contract,
config.method,
"base64-args",
argsBase64,
"prepaid-gas",
gas,
"attached-deposit",
deposit,
"sign-as",
config.account,
"network-config",
config.network,
"sign-with-keychain",
"send"
];
const result = await execa("near", args, {
stdin: "inherit",
stdout: "pipe",
stderr: "pipe",
reject: false,
timeout: 3e5
});
const combined = combineNearOutput(result.stdout, result.stderr);
const txHash = extractTransactionHash(combined);
const hasCodeDoesNotExist = /CodeDoesNotExist/i.test(combined);
if (result.exitCode === 0 || hasCodeDoesNotExist) return {
success: true,
txHash
};
throw new NearTransactionError(combined || `Transaction failed with code ${result.exitCode}`);
}
async function runNearCommand(args) {
if (!process.stdin.isTTY) throw new NearTransactionError("No TTY available for keychain signing. Set NEAR_PRIVATE_KEY environment variable to sign locally.");
await execa("near", args, {
stdin: "inherit",
stdout: "inherit",
stderr: "inherit"
});
}
const ensureNearCli = Effect.gen(function* () {
if (yield* Effect.promise(() => isNearCliInstalled())) return;
yield* Effect.log("");
yield* Effect.log(" NEAR CLI not found");
yield* Effect.log("");
yield* Effect.log(` To install manually: curl --proto '=https' --tlsv1.2 -LsSf ${INSTALLER_URL} | sh`);
yield* Effect.log("");
return yield* new NearCliNotFoundError({});
});
async function listPublishKeys(config) {
const listResult = await execa("near", [
"account",
"list-keys",
config.account,
"network-config",
config.network,
"now"
], {
stdout: "pipe",
stderr: "pipe",
reject: false
});
if (listResult.exitCode !== 0) throw new NearTransactionError(`Failed to list account keys: ${listResult.stderr || listResult.stdout || "unknown error"}`);
const output = listResult.stdout ?? "";
const oldKeys = [];
for (const line of output.split("\n")) if (line.includes(config.contract) && line.includes("__fastdata_kv")) {
const match = line.match(/ed25519:[A-Za-z0-9]+/);
if (match) oldKeys.push(match[0]);
}
return oldKeys;
}
async function deleteAccessKeys(account, publicKeys, network) {
await runNearCommand([
"account",
"delete-keys",
account,
"public-keys",
publicKeys.join(","),
"network-config",
network,
"sign-with-keychain",
"send"
]);
}
async function addFunctionCallAccessKey(config) {
const keyPair = config.keyPair ?? generateNearKeyPair();
await runNearCommand([
"account",
"add-key",
config.account,
"grant-function-call-access",
"--allowance",
config.allowance,
"--contract-account-id",
config.contract,
"--function-names",
config.functionNames.join(", "),
"use-manually-provided-public-key",
keyPair.publicKey,
"network-config",
config.network || (config.account.endsWith(".testnet") ? "testnet" : "mainnet"),
"sign-with-keychain",
"send"
]);
return keyPair;
}
//#endregion
export { NearCliNotFoundError, NearTransactionError, addFunctionCallAccessKey, deleteAccessKeys, ensureNearCli, executeKeychainTransaction, generateNearKeyPair, isNearCliInstalled, listPublishKeys };
//# sourceMappingURL=near-cli.mjs.map