### Get Latest Snapshot URL via CDX API Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Queries the Wayback CDX server for the most recent successful snapshot and constructs its URL. Returns null if no snapshots are found. Used as a fallback in `archiveUrl`. ```typescript const service = new ArchiverService(plugin); // Used automatically as a fallback in archiveUrl(), but also callable directly: const snapshotUrl = await service.getLatestSnapshotUrl("https://example.com"); if (snapshotUrl) { // "https://web.archive.org/web/20250410183042/https://example.com" console.log("Latest snapshot:", snapshotUrl); } else { console.log("No snapshots found for this URL."); } ``` -------------------------------- ### Obsidian Plugin Initialization in TypeScript Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt This snippet shows the entry point for the WaybackArchiverPlugin, extending Obsidian's Plugin class. It covers data loading, validation, accessing settings, and persisting changes. ```typescript // Plugin data is loaded and validated on startup: // - Falls back to default profile if activeProfileId is missing or invalid // - Initializes failedArchives array if absent // Accessing the active profile's settings at runtime: const plugin: WaybackArchiverPlugin = ...; // injected by Obsidian console.log(plugin.activeSettings.apiDelay); // e.g. 2000 console.log(plugin.data.activeProfileId); // e.g. "research" console.log(plugin.data.spnAccessKey); // "AKIAIOSFODNN7EXAMPLE" // Persisting settings after mutation: plugin.data.profiles["research"].archiveFreshnessDays = 14; await plugin.saveSettings(); // Programmatically switch active profile: plugin.data.activeProfileId = "social"; await plugin.saveSettings(); // Now plugin.activeSettings reflects the "social" profile ``` -------------------------------- ### Apply URL Substitution Rules Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Applies a series of find/replace rules to a URL. Use regex: true for global regex replacement, otherwise uses split/join for all occurrences. ```typescript import { applySubstitutionRules } from "./src/utils/LinkUtils"; const rules = [ { find: "reddit.com", replace: "old.reddit.com", regex: false }, { find: "^https://m\.", replace: "https://", regex: true }, { find: "?amp=1", replace: "", regex: false }, ]; console.log(applySubstitutionRules("https://www.reddit.com/r/obsidian/", rules)); // → "https://www.old.reddit.com/r/obsidian/" console.log(applySubstitutionRules("https://m.example.com/page?amp=1", rules)); // → "https://example.com/page" ``` -------------------------------- ### archiveAllLinksVaultAction() Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Archives every link across the entire vault. It iterates through all markdown files, applying path and word pattern filters before processing each file. A running notice displays progress, and a final summary is shown upon completion. ```APIDOC ## `archiveAllLinksVaultAction()` — Archive every link across the entire vault Iterates over all markdown files returned by `vault.getMarkdownFiles()`, applying path and word pattern filters before processing each file with `processFileWithContext`. Displays a running notice and a final summary. ```typescript // Triggered via command palette: "Archive all links in vault" // A ConfirmationModal is shown first; on confirm: await plugin.archiveAllLinksVaultAction(); // Notice: "Vault archival complete. Archived: 42, Failed: 3, Skipped: 15." // Only files matching BOTH pathPatterns AND wordPatterns are processed: // Profile settings example: // pathPatterns: ["Sources/"] → only Notes/Sources/** // wordPatterns: ["#to-archive"] → only notes containing the tag // Vault structure: // Sources/paper1.md ← contains "#to-archive" → processed ✓ // Sources/paper2.md ← does NOT contain tag → skipped // Daily/2025-04-15.md → skipped (wrong path) ``` ``` -------------------------------- ### createArchiveLink Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Formats the archive link string to be inserted after the original link, matching the source format (markdown or HTML). It uses a provided template and date format. ```APIDOC ## createArchiveLink(match, archiveUrl, settings) ### Description Produces the correctly-formatted archive link string to insert after the original link, matching the source format (markdown or HTML). Uses the profile's `archiveLinkText` template and `dateFormat`. ### Parameters - **match** (object) - The result of a regex match for a link. - **archiveUrl** (string) - The URL of the archived version. - **settings** (object) - An object containing settings like `archiveLinkText` and `dateFormat`. ### Request Example ```typescript const noteSnippet = "[Example](https://example.com)"; const [match] = Array.from(noteSnippet.matchAll(LINK_REGEX)); const archiveUrl = "https://web.archive.org/web/20250415120000/https://example.com"; const link = createArchiveLink(match, archiveUrl, { archiveLinkText: "(Archived on {date})", dateFormat: "yyyy-MM-dd", }); console.log(link); // → " [(Archived on 2025-04-15)](https://web.archive.org/web/20250415120000/https://example.com)" // HTML source link produces an HTML archive link: const htmlSnippet = 'Example'; const [htmlMatch] = Array.from(htmlSnippet.matchAll(LINK_REGEX)); const htmlLink = createArchiveLink(htmlMatch, archiveUrl, DEFAULT_SETTINGS); console.log(htmlLink); // → ' (Archived on 2025-04-15)' ``` ``` -------------------------------- ### applySubstitutionRules Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Applies an ordered array of find/replace rules to a URL to transform it before archiving. Rules can use plain string replacement or global regular expressions. ```APIDOC ## applySubstitutionRules(url, rules) ### Description Applies an ordered array of find/replace rules to a URL. Rules with `regex: true` compile to a global regex; plain rules use `split().join()` for all-occurrence replacement. ### Parameters - **url** (string) - The URL to transform. - **rules** (Array) - An array of rule objects, where each object has `find` (string), `replace` (string), and optionally `regex` (boolean) properties. ### Request Example ```typescript const rules = [ { find: "reddit.com", replace: "old.reddit.com", regex: false }, { find: "^https://m\.", replace: "https://", regex: true }, { find: "?amp=1", replace: "", regex: false }, ]; console.log(applySubstitutionRules("https://www.reddit.com/r/obsidian/", rules)); // → "https://www.old.reddit.com/r/obsidian/" console.log(applySubstitutionRules("https://m.example.com/page?amp=1", rules)); // → "https://example.com/page" ``` ``` -------------------------------- ### getLatestSnapshotUrl(targetUrl) Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Queries the Wayback CDX server for the most recent successful snapshot (HTTP 200) and constructs the direct snapshot URL. Returns `null` if no snapshots are found for the given URL. ```APIDOC ## `getLatestSnapshotUrl(targetUrl)` — Query CDX API for the most recent snapshot Queries the Wayback CDX server for the latest successful (HTTP 200) snapshot timestamp and constructs the direct snapshot URL. Returns `null` if no snapshots exist. ```typescript const service = new ArchiverService(plugin); // Used automatically as a fallback in archiveUrl(), but also callable directly: const snapshotUrl = await service.getLatestSnapshotUrl("https://example.com"); if (snapshotUrl) { // "https://web.archive.org/web/20250410183042/https://example.com" console.log("Latest snapshot:", snapshotUrl); } else { console.log("No snapshots found for this URL."); } // CDX API query built internally: // GET https://web.archive.org/cdx/search/cdx // ?url=https%3A%2F%2Fexample.com // &output=json&fl=timestamp&filter=statuscode:200&limit=1&sort=reverse // // Response shape: [["timestamp"], ["20250410183042"]] ``` ``` -------------------------------- ### archiveLinksAction(editor, ctx) Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Handles the command to archive links within the active note. It processes either the selected text or the entire file, respecting freshness settings and skipping already archived links. Failures are logged, and atomic writes are performed using `vault.process`. ```APIDOC ## `archiveLinksAction(editor, ctx)` — Archive links in the active note Command handler for "Archive links in current note". Processes only the selected text if a selection exists, otherwise the full file. Respects freshness settings, skips links already followed by an up-to-date archive link, and logs failures. Uses `vault.process` for atomic per-link file writes. ```typescript // Registered automatically via registerCommands(); called by the ribbon icon and command palette. // Direct invocation (e.g. in a custom script): plugin.addCommand({ id: "my-trigger", name: "Archive current note links", editorCallback: async (editor, ctx) => { await plugin.archiveLinksAction(editor, ctx); // Obsidian Notice displayed: "Archival complete. Archived: 3, Failed: 1, Skipped: 2" }, }); // Before archiving (note content): // [Wikipedia](https://en.wikipedia.org/wiki/Obsidian) // [GitHub](https://github.com/obsidianmd) // After archiving (note content): // [Wikipedia](https://en.wikipedia.org/wiki/Obsidian) [(Archived on 2025-04-15)](https://web.archive.org/web/20250415110000/https://en.wikipedia.org/wiki/Obsidian) // [GitHub](https://github.com/obsidianmd) [(Archived on 2025-04-15)](https://web.archive.org/web/20250415110001/https://github.com/obsidianmd) ``` ``` -------------------------------- ### WaybackArchiverSettings Interface and Defaults Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Defines the structure for per-profile settings, including date formatting, link text, URL filtering patterns, substitution rules, rate limiting, and SPN API capture flags. Access default settings using the `DEFAULT_SETTINGS` constant. ```typescript import { DEFAULT_SETTINGS, WaybackArchiverSettings } from "./src/core/settings"; // Shape of a single profile's settings const myProfile: WaybackArchiverSettings = { // Display / link format dateFormat: "yyyy-MM-dd", // date-fns token string archiveLinkText: "(Archived on {date})", // {date} is replaced at insertion time // Filtering (one pattern per array entry; supports plain text or regex strings) ignorePatterns: ["web.archive.org/", "localhost"], // always skip these URLs urlPatterns: ["github.com", "wikipedia.org"], // only archive these URLs (empty = all) pathPatterns: ["Research/"], // vault-wide: only process notes in this path wordPatterns: ["#archive-me"], // vault-wide: only process notes containing this text // URL substitution (applied in order before sending to SPN) substitutionRules: [ { find: "reddit.com", replace: "old.reddit.com", regex: false }, { find: "^https://mobile\.", replace: "https://", regex: true }, ], // Rate limiting & polling apiDelay: 2000, // ms between each API call maxRetries: 3, // status-check polling attempts before giving up archiveFreshnessDays: 7, // skip re-archive if existing link is < 7 days old (0 = always archive) // SPN API v2 capture flags captureScreenshot: false, captureAll: false, jsBehaviorTimeout: 0, // 0 = API default forceGet: false, captureOutlinks: false, // Housekeeping autoClearFailedLogs: false, }; // Access default settings console.log(DEFAULT_SETTINGS.apiDelay); // 2000 console.log(DEFAULT_SETTINGS.dateFormat); // "yyyy-MM-dd" ``` -------------------------------- ### Create Archive Link String Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Formats an archive link string to be inserted after an original link. Supports markdown and HTML formats based on the source link and uses configurable templates and date formats. ```typescript import { createArchiveLink, LINK_REGEX, getUrlFromMatch } from "./src/utils/LinkUtils"; import { DEFAULT_SETTINGS } from "./src/core/settings"; const noteSnippet = "[Example](https://example.com)"; const [match] = Array.from(noteSnippet.matchAll(LINK_REGEX)); const archiveUrl = "https://web.archive.org/web/20250415120000/https://example.com"; const link = createArchiveLink(match, archiveUrl, { ...DEFAULT_SETTINGS, archiveLinkText: "(Archived on {date})", dateFormat: "yyyy-MM-dd", }); console.log(link); // → " [(Archived on 2025-04-15)](https://web.archive.org/web/20250415120000/https://example.com)" // HTML source link produces an HTML archive link: const htmlSnippet = 'Example'; const [htmlMatch] = Array.from(htmlSnippet.matchAll(LINK_REGEX)); const htmlLink = createArchiveLink(htmlMatch, archiveUrl, DEFAULT_SETTINGS); console.log(htmlLink); // → ' (Archived on 2025-04-15)' ``` -------------------------------- ### Aggressive Profile Settings for Wayback Archiver Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Defines a comprehensive settings object for the Wayback Archiver plugin, enabling all SPN API v2 options. This includes detailed configuration for date formatting, URL/path/word patterns, API behavior, and capture settings. ```typescript import { WaybackArchiverSettings } from "./src/core/settings"; const aggressiveProfile: WaybackArchiverSettings = { dateFormat: "dd MMM yyyy", // e.g. "15 Apr 2025" archiveLinkText: "[archived {date}]", // custom bracket syntax ignorePatterns: [ "web\.archive\.org", "localhost", "^file://", ], urlPatterns: [], // empty = archive all URLs pathPatterns: [], // empty = all notes in vault wordPatterns: [], substitutionRules: [ { find: "twitter\.com", replace: "nitter.net", regex: true }, { find: "reddit.com", replace: "old.reddit.com", regex: false }, ], apiDelay: 5000, // 5 s between calls (conservative for rate limits) maxRetries: 5, archiveFreshnessDays: 0, // always archive if no adjacent link present // SPN API v2 capture options captureScreenshot: true, // capture_screenshot=1 captureAll: true, // capture_all=1 (captures JS/CSS, better error recovery) jsBehaviorTimeout: 10000, // js_behavior_timeout=10000 forceGet: true, // force_get=1 captureOutlinks: false, // capture_outlinks=0 autoClearFailedLogs: true, // remove retried entries without confirmation }; ``` -------------------------------- ### File I/O Helpers in TypeScript Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Utilize these functions for safe file reading and writing within the Obsidian vault. They handle errors by showing notices and provide a mechanism for logging failed archive operations. ```typescript import { safeReadFile, safeWriteFile, logFailedArchive, showAndLogError } from "./src/utils/FileUtils"; // Read a file without throwing on error const content = await safeReadFile(plugin.app.vault, someFile); // On error: shows "Error reading file: path/to/file.md" notice, returns "" // Write a file without throwing on error await safeWriteFile(plugin.app.vault, someFile, updatedContent); // On error: shows "Error saving file: path/to/file.md" notice // Log a failed archive entry and persist it await logFailedArchive(plugin, { url: "https://example.com/blocked", filePath: "Notes/research.md", timestamp: Date.now(), error: "error:blocked-client-error", retryCount: 0, }); // Show a notice AND log to console.error atomically showAndLogError("Unexpected error processing vault", new Error("ENOENT")); ``` -------------------------------- ### Archive URL with SPN API v2 Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Submits a URL to the SPN API v2 for archiving and polls for the result. Falls back to `getLatestSnapshotUrl` on certain responses. Requires plugin instance with API keys. ```typescript const service = new ArchiverService(plugin); const result = await service.archiveUrl("https://example.com/article"); switch (result.status) { case "success": // result.url = "https://web.archive.org/web/20250415120000/https://example.com/article" console.log("Archived at:", result.url); break; case "too_many_captures": // Daily limit hit; result.url points to the latest available snapshot (or wildcard URL) console.log("Using existing snapshot:", result.url); break; case "failed": // result.status_ext contains the reason: "Timeout", "error:blocked", "Initiation failed (503)", … console.error("Archive failed:", result.status_ext); break; } ``` -------------------------------- ### Archive All Links in Vault Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Iterates through all markdown files in the vault, applying path and word pattern filters before archiving links. Displays a running and final summary notice. A confirmation modal is shown before execution. ```typescript // Triggered via command palette: "Archive all links in vault" // A ConfirmationModal is shown first; on confirm: await plugin.archiveAllLinksVaultAction(); // Notice: "Vault archival complete. Archived: 42, Failed: 3, Skipped: 15." // Only files matching BOTH pathPatterns AND wordPatterns are processed: // Profile settings example: // pathPatterns: ["Sources/"] → only Notes/Sources/** // wordPatterns: ["#to-archive"] → only notes containing the tag // Vault structure: // Sources/paper1.md ← contains "#to-archive" → processed ✓ // Sources/paper2.md ← does NOT contain tag → skipped // Daily/2025-04-15.md → skipped (wrong path) ``` -------------------------------- ### LINK_REGEX Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt A unified regular expression for detecting various link and image formats, including Markdown, HTML, and bare URLs. ```APIDOC ## LINK_REGEX ### Description A single compiled `RegExp` (flags `img`) that matches markdown links/images, HTML `` / `` tags (double- and single-quoted), and bare `https?://` URLs. Group indices are consistent: group 1 = markdown URL, groups 2–3 = HTML anchor, groups 4–5 = HTML img, group 6 = plain URL. ### Example ```typescript import { LINK_REGEX, getUrlFromMatch } from "./src/utils/LinkUtils"; const noteContent = ` Check [Wikipedia](https://en.wikipedia.org/wiki/Erica_(plant)) for details. Also see GitHub. Plain URL: https://obsidian.md `; const matches = Array.from(noteContent.matchAll(LINK_REGEX)); matches.forEach((m) => { console.log(getUrlFromMatch(m)); }); // Output: // https://en.wikipedia.org/wiki/Erica_(plant) ← markdown (balanced parens handled) // https://github.com ← HTML anchor // https://obsidian.md ← plain URL ``` ``` -------------------------------- ### matchesAnyPattern Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Tests a string against an array of patterns using case-insensitive regex, with a fallback to plain string inclusion for invalid regex patterns. ```APIDOC ## matchesAnyPattern(text, patterns) ### Description Tests a string against an array of pattern strings using case-insensitive regex (`/iu` flags). Falls back to plain string inclusion if a pattern is invalid regex. ### Parameters - **text** (string) - The text to test. - **patterns** (string[]) - An array of pattern strings to match against. ### Example ```typescript import { matchesAnyPattern } from "./src/utils/LinkUtils"; // Ignore patterns check const ignorePatterns = ["web\.archive\.org", "localhost", "127\.0\.0\.1"]; console.log(matchesAnyPattern("https://web.archive.org/web/123/", ignorePatterns)); // true console.log(matchesAnyPattern("https://example.com", ignorePatterns)); // false // Include URL patterns check const includePatterns = ["github\.com", "stackoverflow\.com"]; console.log(matchesAnyPattern("https://github.com/obsidianmd", includePatterns)); // true console.log(matchesAnyPattern("https://random-site.com", includePatterns)); // false // Invalid regex gracefully falls back to string inclusion const badPatterns = ["(unclosed"]; console.log(matchesAnyPattern("(unclosed bracket url", badPatterns)); // true (string inclusion) ``` ``` -------------------------------- ### WaybackArchiverData Structure for Plugin Data Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Represents the top-level data object stored by the plugin, including API keys, active profile, multiple named profiles with their settings, and a log of failed archives. Initialize with `DEFAULT_SETTINGS` for new profiles. ```typescript import { WaybackArchiverData, DEFAULT_SETTINGS } from "./src/core/settings"; const pluginData: WaybackArchiverData = { spnAccessKey: "YOUR_ARCHIVE_ORG_ACCESS_KEY", spnSecretKey: "YOUR_ARCHIVE_ORG_SECRET_KEY", activeProfileId: "research", profiles: { default: { ...DEFAULT_SETTINGS }, research: { ...DEFAULT_SETTINGS, urlPatterns: ["arxiv.org", "scholar.google.com"], pathPatterns: ["Research/"], apiDelay: 3000, archiveFreshnessDays: 30, }, social: { ...DEFAULT_SETTINGS, substitutionRules: [{ find: "reddit.com", replace: "old.reddit.com", regex: false }], }, }, failedArchives: [ { url: "https://example.com/gone", filePath: "Notes/my-note.md", timestamp: 1714000000000, error: "Archiving failed (error:too-many-requests)", retryCount: 1, }, ], }; ``` -------------------------------- ### forceReArchiveLinksAction Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Force-replaces archive links in the active note, bypassing freshness and existing link checks. Existing archive links are immediately replaced. ```APIDOC ## forceReArchiveLinksAction(editor, ctx) ### Description Force-replaces archive links in the active note. This action bypasses freshness checks and existing-adjacent-link checks, replacing any existing archive link immediately regardless of its age. If the underlying service call fails, the note remains unchanged. ### Parameters - **editor**: The active editor instance. - **ctx**: The context object provided by Obsidian. ### Example ```typescript plugin.addCommand({ id: "force-rearchive-current", name: "Force re-archive current note", editorCallback: async (editor, ctx) => { await plugin.forceReArchiveLinksAction(editor, ctx); }, }); ``` ``` -------------------------------- ### Force Re-archive Links in Current Note Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Use this command to bypass freshness checks and immediately replace any existing archive link following the original URL. If the archiving process fails, the note remains unchanged. ```typescript plugin.addCommand({ id: "force-rearchive-current", name: "Force re-archive current note", editorCallback: async (editor, ctx) => { await plugin.forceReArchiveLinksAction(editor, ctx); }, }); ``` -------------------------------- ### forceReArchiveAllLinksAction Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Force-replaces all archive links across the entire Obsidian vault. This is a vault-wide operation equivalent to forceReArchiveLinksAction. ```APIDOC ## forceReArchiveAllLinksAction() ### Description Force-replaces all archive links in the entire vault. This function processes every eligible note with the `isForce = true` option, performing a vault-wide re-archival. ### Example ```typescript await plugin.forceReArchiveAllLinksAction(); // Notice: "Vault force re-Archival complete. Archived: 38, Failed: 2, Skipped: 10." ``` ``` -------------------------------- ### Archive Links in Active Note Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Command handler to archive links within the active note, processing selected text or the entire file. Respects freshness settings and skips existing archives. Uses `vault.process` for atomic writes. ```typescript // Registered automatically via registerCommands(); called by the ribbon icon and command palette. // Direct invocation (e.g. in a custom script): plugin.addCommand({ id: "my-trigger", name: "Archive current note links", editorCallback: async (editor, ctx) => { await plugin.archiveLinksAction(editor, ctx); // Obsidian Notice displayed: "Archival complete. Archived: 3, Failed: 1, Skipped: 2" }, }); // Before archiving (note content): // [Wikipedia](https://en.wikipedia.org/wiki/Obsidian) // [GitHub](https://github.com/obsidianmd) // After archiving (note content): // [Wikipedia](https://en.wikipedia.org/wiki/Obsidian) [(Archived on 2025-04-15)](https://web.archive.org/web/20250415110000/https://en.wikipedia.org/wiki/Obsidian) // [GitHub](https://github.com/obsidianmd) [(Archived on 2025-04-15)](https://web.archive.org/web/20250415110001/https://github.com/obsidianmd) ``` -------------------------------- ### isFollowedByArchiveLink and checkAdjacentLinkFreshness Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Utility functions to check if a link is already followed by an archive link and to determine if an existing archive link is still considered fresh based on a freshness window. ```APIDOC ## isFollowedByArchiveLink(textFollowingLink) & checkAdjacentLinkFreshness(timestamp, settings) ### Description `isFollowedByArchiveLink` tests whether the text immediately after a link is already a Wayback Machine archive link (markdown or HTML, supporting wildcard `*` timestamps). `checkAdjacentLinkFreshness` parses the `YYYYMMDDHHmmss` timestamp and determines whether it falls within the freshness window. ### Parameters - **textFollowingLink** (string) - The text immediately following a link. - **timestamp** (string) - The timestamp of the archive link in `YYYYMMDDHHmmss` format. - **settings** (object) - An object containing settings, specifically `archiveFreshnessDays`. ### Request Example ```typescript // Check if an archive link already follows a link const textAfter = " [(Archived on 2025-04-15)](https://web.archive.org/web/20250415120000/https://example.com)"; console.log(isFollowedByArchiveLink(textAfter)); // true console.log(isFollowedByArchiveLink(" Some other text")); // false // Check freshness: settings.archiveFreshnessDays = 7 const freshSettings = { archiveFreshnessDays: 7 }; // Timestamp from yesterday → fresh → skip re-archive const { shouldProcess, replaceExisting } = checkAdjacentLinkFreshness("20250414120000", freshSettings); console.log(shouldProcess); // false (within 7-day window, do not re-archive) console.log(replaceExisting); // false // Timestamp from 30 days ago → stale → replace const stale = checkAdjacentLinkFreshness("20250315120000", freshSettings); console.log(stale.shouldProcess); // true (older than 7 days, should re-archive) console.log(stale.replaceExisting); // true ``` ``` -------------------------------- ### Check for Existing Archive Link and Freshness Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Determines if a link is immediately followed by an archive link and checks if the archive's timestamp is within a configurable freshness window. ```typescript import { isFollowedByArchiveLink, checkAdjacentLinkFreshness, } from "./src/utils/LinkUtils"; import { DEFAULT_SETTINGS } from "./src/core/settings"; // Check if an archive link already follows a link const textAfter = " [(Archived on 2025-04-15)](https://web.archive.org/web/20250415120000/https://example.com)"; console.log(isFollowedByArchiveLink(textAfter)); // true console.log(isFollowedByArchiveLink(" Some other text")); // false // Check freshness: settings.archiveFreshnessDays = 7 const freshSettings = { ...DEFAULT_SETTINGS, archiveFreshnessDays: 7 }; // Timestamp from yesterday → fresh → skip re-archive const { shouldProcess, replaceExisting } = checkAdjacentLinkFreshness("20250414120000", freshSettings); console.log(shouldProcess); // false (within 7-day window, do not re-archive) console.log(replaceExisting); // false // Timestamp from 30 days ago → stale → replace const stale = checkAdjacentLinkFreshness("20250315120000", freshSettings); console.log(stale.shouldProcess); // true (older than 7 days, should re-archive) console.log(stale.replaceExisting); // true ``` -------------------------------- ### archiveUrl(url) Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Submits a URL to the SPN API v2 for archiving and polls for the result. It handles substitution rules, posts to the Wayback Machine, and falls back to retrieving the latest snapshot if necessary. Returns a typed outcome indicating success, too many captures, or failure. ```APIDOC ## `archiveUrl(url)` — Submit a URL to SPN API v2 and poll for result Applies substitution rules to the URL, then POSTs to `https://web.archive.org/save`, polls the status endpoint until the job succeeds or times out, and returns a typed outcome. On HTTP 429 or a "same snapshot" response it falls back to `getLatestSnapshotUrl`. ```typescript // Internal usage within ArchiverService (requires plugin instance with API keys set) const service = new ArchiverService(plugin); const result = await service.archiveUrl("https://example.com/article"); switch (result.status) { case "success": // result.url = "https://web.archive.org/web/20250415120000/https://example.com/article" console.log("Archived at:", result.url); break; case "too_many_captures": // Daily limit hit; result.url points to the latest available snapshot (or wildcard URL) console.log("Using existing snapshot:", result.url); break; case "failed": // result.status_ext contains the reason: "Timeout", "error:blocked", "Initiation failed (503)", … console.error("Archive failed:", result.status_ext); break; } // SPN API POST body constructed internally: // url=https%3A%2F%2Fexample.com%2Farticle // &capture_outlinks=0&capture_screenshot=0&force_get=0&capture_all=0 // &skip_first_archive=1 // &if_not_archived_within=604800s ← only when archiveFreshnessDays > 0 // // Authorization header: "LOW :" ``` ``` -------------------------------- ### Unified Link Detection with Regex Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Utilizes a comprehensive regular expression to identify various link formats including Markdown, HTML anchor/image tags, and bare URLs. The `getUrlFromMatch` function extracts the URL from the regex match object. ```typescript import { LINK_REGEX, getUrlFromMatch } from "./src/utils/LinkUtils"; const noteContent = ` Check [Wikipedia](https://en.wikipedia.org/wiki/Erica_(plant)) for details. Also see GitHub. Plain URL: https://obsidian.md `; const matches = Array.from(noteContent.matchAll(LINK_REGEX)); matches.forEach((m) => { console.log(getUrlFromMatch(m)); }); // Output: // https://en.wikipedia.org/wiki/Erica_(plant) ← markdown (balanced parens handled) // https://github.com ← HTML anchor // https://obsidian.md ← plain URL ``` -------------------------------- ### Pattern Matching with Regex Fallback Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Tests strings against an array of patterns using case-insensitive regex. If a pattern is invalid regex, it falls back to simple string inclusion for matching. ```typescript import { matchesAnyPattern } from "./src/utils/LinkUtils"; // Ignore patterns check const ignorePatterns = ["web\.archive\.org", "localhost", "127\.0\.0\.1"]; console.log(matchesAnyPattern("https://web.archive.org/web/123/", ignorePatterns)); // true console.log(matchesAnyPattern("https://example.com", ignorePatterns)); // false // Include URL patterns check const includePatterns = ["github\.com", "stackoverflow\.com"]; console.log(matchesAnyPattern("https://github.com/obsidianmd", includePatterns)); // true console.log(matchesAnyPattern("https://random-site.com", includePatterns)); // false // Invalid regex gracefully falls back to string inclusion const badPatterns = ["(unclosed"]; console.log(matchesAnyPattern("(unclosed bracket url", badPatterns)); // true (string inclusion) ``` -------------------------------- ### Retry Failed Archives Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Re-attempts archiving for entries listed in failed log files. Successful retries update the source note, and completed log files are deleted. The `forceReplace` parameter determines if existing adjacent archive links should be overwritten. ```typescript // Retry without overwriting existing adjacent archive links: await plugin.retryFailedArchives(false); // Force-replace existing adjacent archive links on success: await plugin.retryFailedArchives(true); ``` ```json [ { "url": "https://example.com/article", "filePath": "Sources/paper1.md", "timestamp": 1744718400000, "error": "Archiving failed (Timeout)", "retryCount": 0 }, { "url": "https://another.example.com", "filePath": "Daily/2025-04-10.md", "timestamp": 1744200000000, "error": "Archiving failed (error:blocked-client-error)", "retryCount": 2 } ] ``` ```text url,filePath,timestamp,error,retryCount https://example.com/article,Sources/paper1.md,1744718400000,Archiving failed (Timeout),0 https://another.example.com,Daily/2025-04-10.md,1744200000000,Archiving failed (error:blocked-client-error),2 ``` -------------------------------- ### Force Re-archive All Links in Vault Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt This function performs a vault-wide re-archival, applying the same force-replacement logic as `forceReArchiveLinksAction` to every eligible note. ```typescript await plugin.forceReArchiveAllLinksAction(); // Notice: "Vault force re-Archival complete. Archived: 38, Failed: 2, Skipped: 10." ``` -------------------------------- ### Find Latest Link Index in Content Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Locates the index of a specific URL within content, prioritizing the occurrence closest to a given approximate index. Handles content shifts and returns null if the link is not found. ```typescript import { findLatestLinkIndex } from "./src/utils/contentManipulator"; // Normal case const content = "Check this [Link](https://example.com) out."; console.log(findLatestLinkIndex(content, "https://example.com", 11)); // 11 // Content shifted (user prepended a header while archive was running) const shifted = "# New Header\n\nCheck this [Link](https://example.com) out."; console.log(findLatestLinkIndex(shifted, "https://example.com", 11)); // 25 // Link deleted by user → returns null console.log(findLatestLinkIndex("No links here.", "https://example.com", 0)); // null // Multiple identical URLs → picks the one closest to approximateIndex const multi = "[A](https://example.com) and [B](https://example.com)"; console.log(findLatestLinkIndex(multi, "https://example.com", 0)); // 0 (first) console.log(findLatestLinkIndex(multi, "https://example.com", 32)); // 30 (second) ``` -------------------------------- ### retryFailedArchives Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Retries previously failed archive attempts by re-processing entries from failed log files. Supports an option to force-replace existing adjacent links. ```APIDOC ## retryFailedArchives(forceReplace) ### Description Retries previously failed archive attempts. This function opens a modal to select failed log files (`wayback-archiver-failed-log-*.{json,csv}`). Upon selection, it re-attempts each entry. Successful retries update the source note and remove the entry from the log file. If all entries succeed, the log file is deleted. ### Parameters - **forceReplace** (boolean) - Required - If true, existing adjacent archive links will be force-replaced upon successful retry. If false, existing links are preserved. ### Example ```typescript // Retry without overwriting existing adjacent archive links: await plugin.retryFailedArchives(false); // Force-replace existing adjacent archive links on success: await plugin.retryFailedArchives(true); ``` ### Failed Log JSON Schema ```json [ { "url": "https://example.com/article", "filePath": "Sources/paper1.md", "timestamp": 1744718400000, "error": "Archiving failed (Timeout)", "retryCount": 0 } ] ``` ### Failed Log CSV Schema ```csv url,filePath,timestamp,error,retryCount https://example.com/article,Sources/paper1.md,1744718400000,Archiving failed (Timeout),0 ``` ``` -------------------------------- ### findLatestLinkIndex Source: https://context7.com/ishizueitaro/obsidian-wayback-archiver/llms.txt Re-scans content for a link matching a given URL and returns the index of the occurrence closest to a specified approximate index. This is resilient to content shifts. ```APIDOC ## findLatestLinkIndex(content, originalUrl, approximateIndex) ### Description Re-scans `content` for a link matching `originalUrl` and returns the index of the occurrence closest to `approximateIndex`. This handles cases where the user edits the note while an async archive API call is in flight. ### Parameters - **content** (string) - The content to search within. - **originalUrl** (string) - The URL of the link to find. - **approximateIndex** (number) - The approximate index of the link in the content. ### Request Example ```typescript // Normal case const content = "Check this [Link](https://example.com) out."; console.log(findLatestLinkIndex(content, "https://example.com", 11)); // 11 // Content shifted (user prepended a header while archive was running) const shifted = "# New Header\n\nCheck this [Link](https://example.com) out."; console.log(findLatestLinkIndex(shifted, "https://example.com", 11)); // 25 // Link deleted by user → returns null console.log(findLatestLinkIndex("No links here.", "https://example.com", 0)); // null // Multiple identical URLs → picks the one closest to approximateIndex const multi = "[A](https://example.com) and [B](https://example.com)"; console.log(findLatestLinkIndex(multi, "https://example.com", 0)); // 0 (first) console.log(findLatestLinkIndex(multi, "https://example.com", 32)); // 30 (second) ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.