UNPKG

@brianlovin/notion-skills

Version:

Sync agent skills from a Notion database to Claude Code, Codex, OpenCode, Cursor, Gemini CLI.

160 lines 6.33 kB
/** * Error translation: turn raw exceptions from ntn / Notion / our internals * into human-readable messages with optional recovery hints. * * Pattern matching is keyed off the message text rather than instanceof * checks because errors cross language and process boundaries (ntn shells * out, Notion returns JSON, we wrap with our own classes). The matchers * are dumb regexes — when they fire, the original message is shown * alongside the friendly version so we never hide context. * * Recoveries are PRINTED, not executed. Spawning a re-entrant CLI from * inside a failing command was clever; printing the command the user can * type is dumb and obviously correct. */ import chalk from "chalk"; const PATTERNS = [ { match: (t) => /API token is invalid/i.test(t) || /NtnAuthError/.test(t), build: () => ({ summary: "Notion auth has expired or `ntn` is in a stuck state.", detail: "Sometimes `ntn doctor` reports a valid token while API calls still fail. " + "The reliable fix is a full re-login.", suggest: "ntn logout && ntn login", }), }, { match: (t) => /ntn` is not installed/i.test(t) || /NtnNotInstalledError/.test(t), build: () => ({ summary: "`ntn` isn't installed.", detail: "notion-skills uses Notion's official CLI for API access.", suggest: "Install: https://github.com/makenotion/cli", }), }, { match: (t) => /is expected to be (select|rich_text|checkbox|multi_select|title)/i.test(t), build: () => ({ summary: "Notion's database schema doesn't match what notion-skills expects.", detail: "A property's type is different in Notion than in our schema. " + "This usually happens after upgrading the CLI.", suggest: "notion-skills upgrade", }), }, { match: (t) => /Could not find database/i.test(t) || /Could not find page/i.test(t), build: () => ({ summary: "Notion couldn't find the database or page you referenced.", detail: "Either the URL/ID is wrong, the page was deleted, or `ntn` is " + "logged in to a different workspace than the one containing it.", }), }, { match: (t) => /Can't edit block that is archived/i.test(t), build: () => ({ summary: "That page is in Notion's trash.", detail: "Restore it from Notion's trash or recreate the page.", }), }, { match: (t) => /ENOTFOUND|EAI_AGAIN|getaddrinfo|fetch failed/i.test(t), build: () => ({ summary: "Couldn't reach the Notion API.", detail: "Network looks unavailable. Check your connection and try again.", }), }, { match: (t) => /No scope configured/i.test(t), build: () => ({ summary: "notion-skills isn't configured yet.", suggest: "notion-skills init", }), }, { match: (t) => /Not logged in/i.test(t), build: () => ({ summary: "Not logged in to Notion.", suggest: "ntn login", }), }, { match: (t) => /GitHub rate limit hit/i.test(t), build: (t) => ({ summary: "GitHub rate limit hit while fetching the skill repo.", detail: "The hint about reset time is in the raw error below. Anonymous " + "requests cap at 60/hour; authenticated calls get 5,000/hour.", suggest: t.includes("GITHUB_TOKEN") ? "export GITHUB_TOKEN=$(gh auth token) # or set a personal token" : undefined, }), }, { match: (t) => /GitHub fetch timed out/i.test(t), build: () => ({ summary: "GitHub took too long to respond.", detail: "Default timeout is 10s. If your network is slow or the file is unusually large, raise the limit:", suggest: "NOTION_SKILLS_FETCH_TIMEOUT_MS=30000 notion-skills add ...", }), }, { match: (t) => /GitHub API returned 404/i.test(t), build: () => ({ summary: "GitHub couldn't find that repo, branch, or tag.", detail: "Check the spelling. If the repo is private, set GITHUB_TOKEN " + "(or run `gh auth login`) so we can authenticate.", }), }, { match: (t) => /Only github\.com URLs are supported/i.test(t), build: () => ({ summary: "Only github.com is supported as a source host.", detail: "GitLab and self-hosted Git providers aren't wired up yet. If " + "you have a use case for GitHub Enterprise, file an issue.", }), }, { match: (t) => /Unknown source ".+"\. Configured/i.test(t), build: () => ({ summary: "That source key isn't configured on this machine.", suggest: "notion-skills source list", }), }, { match: (t) => /Multiple sources configured and no default/i.test(t), build: () => ({ summary: "Multiple sources configured; need to know which to use.", detail: "Pass `--source <key>` for this command, or set a default with " + "`notion-skills source default <key>` so unscoped commands route there.", }), }, ]; export function translateError(err) { const raw = err instanceof Error ? err.message : String(err); for (const p of PATTERNS) { if (p.match(raw)) { return { ...p.build(raw), raw }; } } return { summary: raw, raw }; } /** * Print a translated error to stderr. Recovery commands are SUGGESTED, * never executed automatically. Returns 1 (process exit code). */ export function reportError(err) { const f = translateError(err); console.error(""); console.error(chalk.red(`✗ ${f.summary}`)); if (f.detail) { for (const line of f.detail.split("\n")) { console.error(chalk.dim(` ${line}`)); } } if (f.suggest) { console.error(chalk.dim(` → ${f.suggest}`)); } if (f.summary !== f.raw) { console.error(chalk.dim(` (raw: ${f.raw.split("\n")[0]})`)); } return 1; } //# sourceMappingURL=errors.js.map