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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ export class KeeplyApi {
headers: { ...this.headers, ...init?.headers },
});

if (res.status === 409) {
const body = (await res.json().catch(() => ({}))) as {
message?: string;
existingTitle?: string;
};
const title = body.existingTitle ? ` as "${body.existingTitle}"` : "";
throw new Error(
`This URL is already saved${title}. Use --force to save anyway.`,
);
}

if (!res.ok) {
const body = (await res.json().catch(() => ({}))) as { message?: string };
const msg = Array.isArray(body.message)
Expand Down
47 changes: 30 additions & 17 deletions src/commands/add.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import { Command } from 'commander';
import chalk from 'chalk';
import { requireConfig } from '../config.js';
import { KeeplyApi } from '../api.js';
import { printError, printSuccess } from '../format.js';
import { Command } from "commander";
import chalk from "chalk";
import { requireConfig } from "../config.js";
import { KeeplyApi } from "../api.js";
import { printError, printSuccess } from "../format.js";

interface AddOptions {
title?: string;
note?: string;
folder?: string;
tags?: string;
force?: boolean;
json: boolean;
}

export function registerAddCommand(program: Command): void {
program
.command('add <url>')
.description('Save a new bookmark')
.option('-t, --title <title>', 'Bookmark title')
.option('-n, --note <note>', 'Personal note')
.option('-f, --folder <name|id>', 'Folder name or ID')
.option('--tags <tags>', 'Comma-separated tag names (must already exist)')
.option('--json', 'Output created bookmark as JSON')
.command("add <url>")
.description("Save a new bookmark")
.option("-t, --title <title>", "Bookmark title")
.option("-n, --note <note>", "Personal note")
.option("-f, --folder <name|id>", "Folder name or ID")
.option("--tags <tags>", "Comma-separated tag names (must already exist)")
.option("--force", "Save even if this URL is already bookmarked")
.option("--json", "Output created bookmark as JSON")
.action(async (url: string, opts: AddOptions) => {
const { apiKey, apiUrl } = requireConfig();
const api = new KeeplyApi(apiKey, apiUrl);
Expand All @@ -32,23 +34,31 @@ export function registerAddCommand(program: Command): void {
if (opts.folder) {
const folders = await api.listFolders();
const match = folders.find(
(f) => f.name.toLowerCase() === opts.folder!.toLowerCase() || f.id === opts.folder,
(f) =>
f.name.toLowerCase() === opts.folder!.toLowerCase() ||
f.id === opts.folder,
);
if (!match) {
printError(`Folder "${opts.folder}" not found. Create it in the web app first.`);
printError(
`Folder "${opts.folder}" not found. Create it in the web app first.`,
);
process.exit(1);
}
folderId = match.id;
}

if (opts.tags) {
const tagNames = opts.tags.split(',').map((t) => t.trim().toLowerCase());
const tagNames = opts.tags
.split(",")
.map((t) => t.trim().toLowerCase());
const allTags = await api.listTags();
tagIds = [];
for (const name of tagNames) {
const found = allTags.find((t) => t.name === name);
if (!found) {
printError(`Tag "${name}" not found. Create it in the web app first.`);
printError(
`Tag "${name}" not found. Create it in the web app first.`,
);
process.exit(1);
}
tagIds.push(found.id);
Expand All @@ -61,6 +71,7 @@ export function registerAddCommand(program: Command): void {
note: opts.note,
folderId,
tagIds,
force: opts.force,
});

if (opts.json) {
Expand All @@ -71,7 +82,9 @@ export function registerAddCommand(program: Command): void {
printSuccess(`Saved ${chalk.bold(bookmark.title ?? url)}`);
console.log(chalk.dim(`id: ${bookmark.id}`));
} catch (err) {
printError(err instanceof Error ? err.message : 'Failed to save bookmark');
printError(
err instanceof Error ? err.message : "Failed to save bookmark",
);
process.exit(1);
}
});
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface CreateBookmarkPayload {
note?: string;
folderId?: string;
tagIds?: string[];
force?: boolean;
}

export interface UpdateBookmarkPayload {
Expand Down
Loading