daffy-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # daffy-cli
2
+
3
+ CLI for the [Daffy Public API](https://docs.daffy.org). Search nonprofits, make donations, check balances, send gifts, and more — straight from the terminal.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ cd daffy-cli
9
+ npm install
10
+ npm run build
11
+ ```
12
+
13
+ Set your API key (get one at https://www.daffy.org/settings/api):
14
+
15
+ ```bash
16
+ export DAFFY_API_KEY="your-key-here"
17
+ ```
18
+
19
+ Run a command:
20
+
21
+ ```bash
22
+ node dist/bin.js balance
23
+ ```
24
+
25
+ ## Commands
26
+
27
+ ### Account
28
+
29
+ ```bash
30
+ daffy-cli me # Your profile
31
+ daffy-cli balance # Your fund balance
32
+ daffy-cli causes # Your selected causes
33
+ ```
34
+
35
+ ### Non-Profits
36
+
37
+ ```bash
38
+ daffy-cli search "khan academy" # Search by name
39
+ daffy-cli search --cause 1 # Filter by cause ID
40
+ daffy-cli search "schools" --page 2 # Paginate results
41
+ daffy-cli nonprofit 261544963 # Details by EIN
42
+ ```
43
+
44
+ ### Donations
45
+
46
+ ```bash
47
+ daffy-cli donations # List your donations
48
+ daffy-cli donations --page 2 # Page through results
49
+ daffy-cli donate --ein 261544963 --amount 25
50
+ daffy-cli donate --ein 261544963 --amount 25 --note "Keep it up"
51
+ daffy-cli cancel 12345 # Cancel a pending donation
52
+ ```
53
+
54
+ ### Contributions
55
+
56
+ ```bash
57
+ daffy-cli contributions # List your contributions
58
+ daffy-cli contributions --page 3
59
+ ```
60
+
61
+ ### Gifts
62
+
63
+ ```bash
64
+ daffy-cli gifts # List your gifts
65
+ daffy-cli gift <code> # Gift details by code
66
+ daffy-cli gift create --name "Alex" --amount 25
67
+ ```
68
+
69
+ ## JSON Output
70
+
71
+ Append `--json` to any command to get raw API JSON instead of formatted output. Useful for scripting and piping:
72
+
73
+ ```bash
74
+ daffy-cli balance --json
75
+ daffy-cli search "khan academy" --json | jq '.items[].name'
76
+ daffy-cli donations --json --page 2
77
+ ```
78
+
79
+ ## Options
80
+
81
+ | Flag | Description |
82
+ |---|---|
83
+ | `--help`, `-h` | Show usage |
84
+ | `--json` | Output raw JSON (works with every command) |
85
+ | `--yes`, `-y` | Skip confirmation prompts (for `donate`, `cancel`, `gift create`) |
86
+ | `--page N` | Page number for paginated commands |
87
+
88
+ ## Environment Variables
89
+
90
+ | Variable | Description |
91
+ |---|---|
92
+ | `DAFFY_API_KEY` | **Required.** Your Daffy API key. |
93
+ | `NO_COLOR` | Set to any value to disable colored output. |
94
+
95
+ ## Publishing
96
+
97
+ ```bash
98
+ npm pack --dry-run # Preview what gets published (only dist/)
99
+ npm publish # Publish to npm
100
+ ```
101
+
102
+ After publishing:
103
+
104
+ ```bash
105
+ npx daffy-cli balance
106
+ ```
package/dist/args.js ADDED
@@ -0,0 +1,43 @@
1
+ export function getFlag(args, name) {
2
+ const prefix = `--${name}`;
3
+ for (let i = 0; i < args.length; i++) {
4
+ if (args[i] === prefix && i + 1 < args.length) {
5
+ return args[i + 1];
6
+ }
7
+ if (args[i]?.startsWith(`${prefix}=`)) {
8
+ return args[i].slice(prefix.length + 1);
9
+ }
10
+ }
11
+ return undefined;
12
+ }
13
+ export function requireFlag(args, name) {
14
+ const value = getFlag(args, name);
15
+ if (!value) {
16
+ console.error(`Error: --${name} is required.`);
17
+ process.exit(1);
18
+ }
19
+ return value;
20
+ }
21
+ const BOOLEAN_FLAGS = new Set(["--yes", "-y", "--json"]);
22
+ export function getPositional(args, index) {
23
+ let pos = 0;
24
+ for (let i = 0; i < args.length; i++) {
25
+ const arg = args[i];
26
+ if (BOOLEAN_FLAGS.has(arg))
27
+ continue;
28
+ if (arg.startsWith("--")) {
29
+ if (!arg.includes("="))
30
+ i++; // skip flag value unless --flag=value
31
+ continue;
32
+ }
33
+ if (arg.startsWith("-"))
34
+ continue;
35
+ if (pos === index)
36
+ return arg;
37
+ pos++;
38
+ }
39
+ return undefined;
40
+ }
41
+ export function hasFlag(args, name) {
42
+ return args.includes(`--${name}`);
43
+ }
package/dist/bin.js ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ import { ApiError } from "./client.js";
3
+ import { setJsonMode } from "./output.js";
4
+ const HELP = `
5
+ daffy-cli — CLI for the Daffy Public API
6
+
7
+ Usage: daffy-cli <command> [options]
8
+
9
+ Commands:
10
+ me Show your profile
11
+ user <username> Show a user's profile
12
+ balance Show your fund balance
13
+ search <query> [--cause N] Search non-profits
14
+ nonprofit <ein> Show non-profit details
15
+ donations [--page N] List your donations
16
+ donate --ein <ein> --amount <n> [--note "..."]
17
+ Create a donation
18
+ cancel <id> Cancel a donation
19
+ contributions [--page N] List your contributions
20
+ causes List your causes
21
+ gifts [--page N] List your gifts
22
+ gift <code> Show gift details
23
+ gift create --name <n> --amount <n>
24
+ Create a gift
25
+
26
+ Options:
27
+ --json Output raw JSON instead of formatted text
28
+ --yes, -y Skip confirmation prompts
29
+
30
+ Environment:
31
+ DAFFY_API_KEY Your Daffy API key (required)
32
+ Get one at https://www.daffy.org/settings/api
33
+ NO_COLOR Disable color output
34
+ `;
35
+ const commands = {
36
+ me: () => import("./commands/me.js"),
37
+ user: () => import("./commands/user.js"),
38
+ balance: () => import("./commands/balance.js"),
39
+ search: () => import("./commands/search.js"),
40
+ nonprofit: () => import("./commands/nonprofit.js"),
41
+ donations: () => import("./commands/donations.js"),
42
+ donate: () => import("./commands/donate.js"),
43
+ cancel: () => import("./commands/cancel.js"),
44
+ contributions: () => import("./commands/contributions.js"),
45
+ causes: () => import("./commands/causes.js"),
46
+ gifts: () => import("./commands/gifts.js"),
47
+ gift: () => import("./commands/gift.js"),
48
+ };
49
+ async function main() {
50
+ const args = process.argv.slice(2);
51
+ if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
52
+ console.log(HELP);
53
+ process.exit(0);
54
+ }
55
+ const name = args[0];
56
+ const loader = commands[name];
57
+ if (!loader) {
58
+ console.error(`Unknown command: ${name}\nRun "daffy-cli --help" for usage.`);
59
+ process.exit(1);
60
+ }
61
+ if (args.includes("--json"))
62
+ setJsonMode(true);
63
+ const mod = await loader();
64
+ await mod.run(args.slice(1));
65
+ }
66
+ main().catch((err) => {
67
+ if (err instanceof ApiError) {
68
+ console.error(`\nAPI Error (${err.status}): ${err.body}`);
69
+ }
70
+ else {
71
+ console.error(`\nError: ${err instanceof Error ? err.message : String(err)}`);
72
+ }
73
+ process.exit(1);
74
+ });
package/dist/client.js ADDED
@@ -0,0 +1,50 @@
1
+ const BASE_URL = "https://public.daffy.org";
2
+ export class ApiError extends Error {
3
+ status;
4
+ body;
5
+ constructor(status, body) {
6
+ super(`API error ${status}: ${body}`);
7
+ this.status = status;
8
+ this.body = body;
9
+ this.name = "ApiError";
10
+ }
11
+ }
12
+ function getApiKey() {
13
+ const key = process.env.DAFFY_API_KEY;
14
+ if (!key) {
15
+ console.error("Error: DAFFY_API_KEY environment variable is required.\n" +
16
+ "Get your API key at https://www.daffy.org/settings/api");
17
+ process.exit(1);
18
+ }
19
+ return key;
20
+ }
21
+ async function request(method, path, body) {
22
+ const url = `${BASE_URL}${path}`;
23
+ const headers = {
24
+ "X-Api-Key": getApiKey(),
25
+ Accept: "application/json",
26
+ };
27
+ const init = { method, headers };
28
+ if (body) {
29
+ headers["Content-Type"] = "application/json";
30
+ init.body = JSON.stringify(body);
31
+ }
32
+ const res = await fetch(url, init);
33
+ if (!res.ok) {
34
+ const text = await res.text();
35
+ throw new ApiError(res.status, text);
36
+ }
37
+ const text = await res.text();
38
+ if (!text)
39
+ return {};
40
+ return JSON.parse(text);
41
+ }
42
+ export function get(path) {
43
+ return request("GET", path);
44
+ }
45
+ export function post(path, body) {
46
+ return request("POST", path, body);
47
+ }
48
+ export function del(path) {
49
+ return request("DELETE", path);
50
+ }
@@ -0,0 +1,15 @@
1
+ import { get } from "../client.js";
2
+ import { renderKeyValue, formatMoney } from "../output.js";
3
+ export async function run(_args) {
4
+ const bal = (await get("/v1/users/me/balance"));
5
+ renderKeyValue({
6
+ title: "Fund Balance",
7
+ data: bal,
8
+ fields: [
9
+ ["Available", formatMoney(bal.available_balance)],
10
+ ["Portfolio", formatMoney(bal.portfolio_balance)],
11
+ ["Pending Deposits", formatMoney(bal.pending_deposit_balance)],
12
+ ["Total", formatMoney(bal.amount)],
13
+ ],
14
+ });
15
+ }
@@ -0,0 +1,18 @@
1
+ import { del } from "../client.js";
2
+ import { getPositional } from "../args.js";
3
+ import { renderMessage } from "../output.js";
4
+ import { confirm } from "../prompt.js";
5
+ export async function run(args) {
6
+ const id = getPositional(args, 0);
7
+ if (!id) {
8
+ console.error("Usage: daffy-cli cancel <donation_id>");
9
+ process.exit(1);
10
+ }
11
+ const ok = await confirm(`Cancel donation ${id}?`, args);
12
+ if (!ok) {
13
+ renderMessage("Cancelled.", { cancelled: false, id });
14
+ return;
15
+ }
16
+ await del(`/v1/donations/${encodeURIComponent(id)}`);
17
+ renderMessage(`\nDonation ${id} cancelled.`, { cancelled: true, id });
18
+ }
@@ -0,0 +1,12 @@
1
+ import { get } from "../client.js";
2
+ import { renderTable } from "../output.js";
3
+ export async function run(_args) {
4
+ const user = (await get("/v1/users/me"));
5
+ const causes = (await get(`/v1/users/${user.id}/causes`));
6
+ renderTable({
7
+ title: "Your Causes",
8
+ data: causes,
9
+ headers: ["ID", "Name"],
10
+ rows: causes.map((c) => [String(c.id), c.name]),
11
+ });
12
+ }
@@ -0,0 +1,21 @@
1
+ import { get } from "../client.js";
2
+ import { getFlag } from "../args.js";
3
+ import { renderTable, formatStatus } from "../output.js";
4
+ export async function run(args) {
5
+ const page = getFlag(args, "page");
6
+ const qs = page ? `?page=${page}` : "";
7
+ const data = (await get(`/v1/contributions${qs}`));
8
+ renderTable({
9
+ title: "Your Contributions",
10
+ data,
11
+ headers: ["Amount", "Currency", "Type", "Status", "Date"],
12
+ rows: data.items.map((c) => [
13
+ `$${(c.units * c.valuation).toFixed(2)}`,
14
+ c.currency,
15
+ c.type.replace(/_/g, " "),
16
+ formatStatus(c.status),
17
+ c.created_at.slice(0, 10),
18
+ ]),
19
+ meta: data.meta,
20
+ });
21
+ }
@@ -0,0 +1,38 @@
1
+ import { get, post } from "../client.js";
2
+ import { requireFlag, getFlag } from "../args.js";
3
+ import { renderKeyValue, formatMoney, formatStatus } from "../output.js";
4
+ import { confirm } from "../prompt.js";
5
+ export async function run(args) {
6
+ const ein = requireFlag(args, "ein");
7
+ const amountStr = requireFlag(args, "amount");
8
+ const note = getFlag(args, "note");
9
+ const amount = parseFloat(amountStr);
10
+ if (isNaN(amount) || amount <= 0) {
11
+ console.error("Error: --amount must be a positive number.");
12
+ process.exit(1);
13
+ }
14
+ const np = (await get(`/v1/non_profits/${encodeURIComponent(ein)}`));
15
+ console.log(`\nDonate ${formatMoney(amount)} to ${np.name} (${np.ein})`);
16
+ if (note)
17
+ console.log(`Note: "${note}"`);
18
+ const ok = await confirm("\nProceed?", args);
19
+ if (!ok) {
20
+ console.log("Cancelled.");
21
+ return;
22
+ }
23
+ const body = { ein, amount };
24
+ if (note)
25
+ body.note = note;
26
+ const donation = (await post("/v1/donations", body));
27
+ renderKeyValue({
28
+ title: "Donation Created",
29
+ data: donation,
30
+ fields: [
31
+ ["ID", donation.id],
32
+ ["Amount", formatMoney(donation.amount)],
33
+ ["Non-Profit", donation.non_profit.name],
34
+ ["Status", formatStatus(donation.status)],
35
+ ["Date", donation.created_at.slice(0, 10)],
36
+ ],
37
+ });
38
+ }
@@ -0,0 +1,21 @@
1
+ import { get } from "../client.js";
2
+ import { getFlag } from "../args.js";
3
+ import { renderTable, formatMoney, formatStatus } from "../output.js";
4
+ export async function run(args) {
5
+ const page = getFlag(args, "page");
6
+ const qs = page ? `?page=${page}` : "";
7
+ const data = (await get(`/v1/donations${qs}`));
8
+ renderTable({
9
+ title: "Your Donations",
10
+ data,
11
+ headers: ["ID", "Amount", "Non-Profit", "Status", "Date"],
12
+ rows: data.items.map((d) => [
13
+ String(d.id),
14
+ formatMoney(d.amount),
15
+ d.non_profit.name.slice(0, 30),
16
+ formatStatus(d.status),
17
+ d.created_at.slice(0, 10),
18
+ ]),
19
+ meta: data.meta,
20
+ });
21
+ }
@@ -0,0 +1,60 @@
1
+ import { get, post } from "../client.js";
2
+ import { getPositional, requireFlag } from "../args.js";
3
+ import { renderKeyValue, formatMoney, formatStatus } from "../output.js";
4
+ import { confirm } from "../prompt.js";
5
+ export async function run(args) {
6
+ const first = getPositional(args, 0);
7
+ if (first === "create") {
8
+ await createGift(args);
9
+ }
10
+ else if (first) {
11
+ await showGift(first);
12
+ }
13
+ else {
14
+ console.error("Usage: daffy-cli gift <code> | daffy-cli gift create --name <n> --amount <n>");
15
+ process.exit(1);
16
+ }
17
+ }
18
+ async function showGift(code) {
19
+ const gift = (await get(`/v1/gifts/${encodeURIComponent(code)}`));
20
+ renderKeyValue({
21
+ title: "Gift Details",
22
+ data: gift,
23
+ fields: [
24
+ ["Code", gift.code],
25
+ ["Name", gift.name],
26
+ ["Amount", formatMoney(gift.amount)],
27
+ ["Status", formatStatus(gift.status)],
28
+ ["Claimed", gift.claimed ? "Yes" : "No"],
29
+ ["URL", gift.url],
30
+ ["Created", gift.created_at.slice(0, 10)],
31
+ ],
32
+ });
33
+ }
34
+ async function createGift(args) {
35
+ const name = requireFlag(args, "name");
36
+ const amountStr = requireFlag(args, "amount");
37
+ const amount = parseFloat(amountStr);
38
+ if (isNaN(amount) || amount < 18) {
39
+ console.error("Error: --amount must be at least 18.");
40
+ process.exit(1);
41
+ }
42
+ console.log(`\nCreate gift "${name}" for ${formatMoney(amount)}`);
43
+ const ok = await confirm("\nProceed?", args);
44
+ if (!ok) {
45
+ console.log("Cancelled.");
46
+ return;
47
+ }
48
+ const gift = (await post("/v1/gifts", { name, amount }));
49
+ renderKeyValue({
50
+ title: "Gift Created",
51
+ data: gift,
52
+ fields: [
53
+ ["Code", gift.code],
54
+ ["Name", gift.name],
55
+ ["Amount", formatMoney(gift.amount)],
56
+ ["Status", formatStatus(gift.status)],
57
+ ["URL", gift.url],
58
+ ],
59
+ });
60
+ }
@@ -0,0 +1,21 @@
1
+ import { get } from "../client.js";
2
+ import { getFlag } from "../args.js";
3
+ import { renderTable, formatMoney, formatStatus } from "../output.js";
4
+ export async function run(args) {
5
+ const page = getFlag(args, "page");
6
+ const qs = page ? `?page=${page}` : "";
7
+ const data = (await get(`/v1/gifts${qs}`));
8
+ renderTable({
9
+ title: "Your Gifts",
10
+ data,
11
+ headers: ["Code", "Name", "Amount", "Status", "Date"],
12
+ rows: data.items.map((g) => [
13
+ g.code.slice(0, 8) + "...",
14
+ g.name.slice(0, 25),
15
+ formatMoney(g.amount),
16
+ formatStatus(g.status),
17
+ g.created_at.slice(0, 10),
18
+ ]),
19
+ meta: data.meta,
20
+ });
21
+ }
@@ -0,0 +1,16 @@
1
+ import { get } from "../client.js";
2
+ import { renderKeyValue } from "../output.js";
3
+ export async function run(_args) {
4
+ const user = (await get("/v1/users/me"));
5
+ renderKeyValue({
6
+ title: "Your Profile",
7
+ data: user,
8
+ fields: [
9
+ ["Name", user.name],
10
+ ["Username", user.slug],
11
+ ["Fund", user.fund_name],
12
+ ["Onboarding", user.onboarding_status],
13
+ ["Avatar", user.avatar],
14
+ ],
15
+ });
16
+ }
@@ -0,0 +1,22 @@
1
+ import { get } from "../client.js";
2
+ import { getPositional } from "../args.js";
3
+ import { renderKeyValue } from "../output.js";
4
+ export async function run(args) {
5
+ const ein = getPositional(args, 0);
6
+ if (!ein) {
7
+ console.error("Usage: daffy-cli nonprofit <ein>");
8
+ process.exit(1);
9
+ }
10
+ const np = (await get(`/v1/non_profits/${encodeURIComponent(ein)}`));
11
+ renderKeyValue({
12
+ title: np.name,
13
+ data: np,
14
+ fields: [
15
+ ["EIN", np.ein],
16
+ ["Website", np.website],
17
+ ["Location", [np.city, np.state].filter(Boolean).join(", ") || null],
18
+ ["Cause", np.cause?.name],
19
+ ["URL", np.public_url],
20
+ ],
21
+ });
22
+ }
@@ -0,0 +1,29 @@
1
+ import { get } from "../client.js";
2
+ import { getPositional, getFlag } from "../args.js";
3
+ import { renderTable } from "../output.js";
4
+ export async function run(args) {
5
+ const query = getPositional(args, 0);
6
+ const causeId = getFlag(args, "cause");
7
+ const page = getFlag(args, "page");
8
+ const params = new URLSearchParams();
9
+ if (query)
10
+ params.set("query", query);
11
+ if (causeId)
12
+ params.set("cause_id", causeId);
13
+ if (page)
14
+ params.set("page", page);
15
+ const qs = params.toString();
16
+ const data = (await get(`/v1/non_profits${qs ? `?${qs}` : ""}`));
17
+ renderTable({
18
+ title: "Non-Profit Search Results",
19
+ data,
20
+ headers: ["EIN", "Name", "City", "State"],
21
+ rows: data.items.map((np) => [
22
+ np.ein,
23
+ np.name.slice(0, 40),
24
+ np.city || "—",
25
+ np.state || "—",
26
+ ]),
27
+ meta: data.meta,
28
+ });
29
+ }
@@ -0,0 +1,21 @@
1
+ import { get } from "../client.js";
2
+ import { getPositional } from "../args.js";
3
+ import { renderKeyValue } from "../output.js";
4
+ export async function run(args) {
5
+ const username = getPositional(args, 0);
6
+ if (!username) {
7
+ console.error("Usage: daffy-cli user <username>");
8
+ process.exit(1);
9
+ }
10
+ const user = (await get(`/v1/users/${encodeURIComponent(username)}`));
11
+ renderKeyValue({
12
+ title: user.name,
13
+ data: user,
14
+ fields: [
15
+ ["Username", user.slug],
16
+ ["Fund", user.fund_name],
17
+ ["Onboarding", user.onboarding_status],
18
+ ["Avatar", user.avatar],
19
+ ],
20
+ });
21
+ }
package/dist/format.js ADDED
@@ -0,0 +1,59 @@
1
+ const NO_COLOR = "NO_COLOR" in process.env;
2
+ const BOLD = NO_COLOR ? "" : "\x1b[1m";
3
+ const DIM = NO_COLOR ? "" : "\x1b[2m";
4
+ const GREEN = NO_COLOR ? "" : "\x1b[32m";
5
+ const YELLOW = NO_COLOR ? "" : "\x1b[33m";
6
+ const CYAN = NO_COLOR ? "" : "\x1b[36m";
7
+ const RED = NO_COLOR ? "" : "\x1b[31m";
8
+ const RESET = NO_COLOR ? "" : "\x1b[0m";
9
+ export function printKeyValue(pairs) {
10
+ const maxKey = Math.max(...pairs.map(([k]) => k.length));
11
+ for (const [key, value] of pairs) {
12
+ const display = value === null || value === undefined ? `${DIM}—${RESET}` : String(value);
13
+ console.log(` ${BOLD}${key.padEnd(maxKey)}${RESET} ${display}`);
14
+ }
15
+ }
16
+ export function printTable(headers, rows) {
17
+ if (rows.length === 0) {
18
+ console.log(`${DIM} No results.${RESET}`);
19
+ return;
20
+ }
21
+ const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length)));
22
+ const header = headers.map((h, i) => h.padEnd(widths[i])).join(" ");
23
+ console.log(` ${DIM}${header}${RESET}`);
24
+ console.log(` ${DIM}${widths.map((w) => "─".repeat(w)).join("──")}${RESET}`);
25
+ for (const row of rows) {
26
+ const line = row.map((cell, i) => cell.padEnd(widths[i])).join(" ");
27
+ console.log(` ${line}`);
28
+ }
29
+ }
30
+ export function printMeta(meta) {
31
+ console.log(`\n ${DIM}Page ${meta.page} of ${meta.last} (${meta.count} total)${RESET}`);
32
+ }
33
+ export function formatMoney(amount) {
34
+ return `${GREEN}$${amount.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}${RESET}`;
35
+ }
36
+ export function formatStatus(status) {
37
+ switch (status) {
38
+ case "completed":
39
+ case "success":
40
+ case "approved":
41
+ case "claimed":
42
+ return `${GREEN}${status}${RESET}`;
43
+ case "pending":
44
+ case "scheduled":
45
+ case "waiting_for_funds":
46
+ case "new":
47
+ return `${YELLOW}${status}${RESET}`;
48
+ case "failed":
49
+ case "rejected":
50
+ case "not_completed":
51
+ case "denied":
52
+ return `${RED}${status}${RESET}`;
53
+ default:
54
+ return `${CYAN}${status}${RESET}`;
55
+ }
56
+ }
57
+ export function heading(text) {
58
+ console.log(`\n${BOLD}${text}${RESET}\n`);
59
+ }
package/dist/output.js ADDED
@@ -0,0 +1,31 @@
1
+ import { heading, printKeyValue, printTable, printMeta, formatMoney, formatStatus } from "./format.js";
2
+ let jsonMode = false;
3
+ export function setJsonMode(enabled) {
4
+ jsonMode = enabled;
5
+ }
6
+ export function renderKeyValue({ title, data, fields }) {
7
+ if (jsonMode) {
8
+ console.log(JSON.stringify(data, null, 2));
9
+ return;
10
+ }
11
+ heading(title);
12
+ printKeyValue(fields);
13
+ }
14
+ export function renderTable({ title, data, headers, rows, meta }) {
15
+ if (jsonMode) {
16
+ console.log(JSON.stringify(data, null, 2));
17
+ return;
18
+ }
19
+ heading(title);
20
+ printTable(headers, rows);
21
+ if (meta)
22
+ printMeta(meta);
23
+ }
24
+ export function renderMessage(message, data) {
25
+ if (jsonMode) {
26
+ console.log(JSON.stringify(data, null, 2));
27
+ return;
28
+ }
29
+ console.log(message);
30
+ }
31
+ export { formatMoney, formatStatus };
package/dist/prompt.js ADDED
@@ -0,0 +1,16 @@
1
+ import * as readline from "node:readline";
2
+ export function confirm(message, args) {
3
+ if (args.includes("--yes") || args.includes("-y")) {
4
+ return Promise.resolve(true);
5
+ }
6
+ const rl = readline.createInterface({
7
+ input: process.stdin,
8
+ output: process.stdout,
9
+ });
10
+ return new Promise((resolve) => {
11
+ rl.question(`${message} [y/N] `, (answer) => {
12
+ rl.close();
13
+ resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
14
+ });
15
+ });
16
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "daffy-cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI for the Daffy Public API — search nonprofits, donate, check balances, and more",
5
+ "type": "module",
6
+ "bin": {
7
+ "daffy-cli": "./dist/bin.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "start": "node dist/bin.js"
12
+ },
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "keywords": [
20
+ "daffy",
21
+ "donations",
22
+ "charity",
23
+ "nonprofit",
24
+ "cli"
25
+ ],
26
+ "license": "MIT",
27
+ "devDependencies": {
28
+ "typescript": "^5.3.0",
29
+ "@types/node": "^20.11.0"
30
+ }
31
+ }