@stnd/loader
Overview
Content ingestion loaders for the Standard Ecosystem, built as custom Astro Content Collection Loaders.
ELI5
This is how an Astro site’s content collections point at something that isn’t a local src/content/ folder — an Obsidian vault (with wikilinks and frontmatter) or a Notion database. Define the collection once with the right loader, then use getCollection() like any normal Astro content collection.
Usage
Use it (vault):
import { defineCollection } from "astro:content";
import { vaultLoader } from "@stnd/loader-vault";
export const collections = {
vault: defineCollection({ loader: vaultLoader({ path: "../../vault/Atelier" }) }),
};
Loaders
1. Vault Loader (@stnd/loader-vault)
Loads content from a local directory containing Markdown files with Obsidian-style frontmatter, wikilinks ([[link]]), and media files.
Configuration (src/content.config.ts)
import { defineCollection } from "astro:content";
import { vaultLoader } from "@stnd/loader-vault";
const vault = defineCollection({
loader: vaultLoader({
path: "../../vault/Atelier", // path to the local Obsidian vault
flattenDirectories: false, // preserve directory structure in IDs
ignore: [".obsidian", ".trash", ".agent"], // directories to ignore
}),
});
export const collections = { vault };
Astro Dev Mode Workaround (Important)
In Astro 6.x/7.x development mode (astro dev), calling getCollection("vault") inside routes injected via injectRoute() (used by plugins/integrations) returns []. This is due to virtual module hydration issues inside Vite SSR context.
To work around this, the vault loader populates a global singleton that survives HMR and SSR code-splitting. Instead of using getCollection, import the vault singleton directly:
import { vault } from "@stnd/loader-vault";
// Wait for the vault to initialize (optional, useful in edge cases)
await vault.ensureLoaded();
// Access entries and metadata
const entries = vault.entries; // Array of VaultEntry
const size = vault.size; // Number of entries
const meta = vault.meta; // { validLinks, imageIndex, vaultPath }
// Find individual entries
const note = vault.getEntry("my-permalink");
2. Notion Loader (@stnd/loader-notion)
Synchronizes content from Notion databases into the Standard Ecosystem.
Configuration (src/content.config.ts)
import { defineCollection } from "astro:content";
import { notionLoader } from "@stnd/loader-notion";
const notion = defineCollection({
loader: notionLoader({
auth: import.meta.env.NOTION_API_KEY,
databaseId: import.meta.env.NOTION_DATABASE_ID,
}),
});
export const collections = { notion };
Notes / Observations
- Reciprocal symlinks are deduped by real path. A vault folder that symlinks back into a directory containing the vault itself (e.g. this repo’s own
vault/Atelier -> ~/Documents/Atelieralongside~/Documents/Atelier/Utopie -> this repo— an intentional “codebase opens as a vault too” pattern) turnsglob({ follow: true })into a cycle: the same real file gets enumerated repeatedly at increasing nesting depth. The loader now resolves each globbed path withfs.realpathand skips repeats, so every physical note loads exactly once regardless of how many symlinked routes reach it. publish:acceptstrueor a date (matching the repo-wide publish convention used elsewhere, e.g. the D1-backed Garden notes):publish: trueor any parseablepublish: <date>loads the note;false/absent/unparseable skips it. A future date still loads (no scheduled-publish gate at this layer) — a consumer wanting to hide not-yet-due content filtersvault.entriesitself, asapps/stnd.gd/modules/rss/does for its RSS feed.
Todo
- Evaluate
experimental.collectionStorage: 'chunked'for the vault collection — Astro 7.1.6 added a chunk-size option, and@astrojs/cloudflare14.1.7 fixed chunked-collection loading on the adapter. Only worth it if the vault store is actually large enough to justify it; measure the serialized store first rather than adopting on principle. Note this is unrelated to the dev-modegetCollectionworkaround (root README #1), which chunking does not address. priority: 2 token_scale: 3 created: 2026-08-07 area: framework