### 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.