Skip to content
The source-authoring library (@ontrove/extend/source) powers personal-source ingestion, which ships with the Trove Mac app — coming soon. Browse the API below now; for what is live today, see the official toolkits.

@ontrove/extend/source

@ontrove/extend/source — the shared vocabulary for Trove sources: scheduled adapters that fetch content into a knowledge base. A source exports a sync(ctx) that returns documents; this package owns the shapes both ends of that call agree on.

  • The invoke contract (@ontrove/extend/contract) — the request/response envelope every runtime speaks. Three of them execute it: Trove’s deployed shim, the CLI’s local shim, and the Mac harness. This is the load-bearing part, and the reason the same sync(ctx) runs unchanged in all three.
  • The typesDocument, SourceContext, Cursor, SourceManifest. Trove imports several of them directly rather than re-declaring them.
  • runSource — the local-run harness the CLI drives, with the same validation and dedup the cloud applies.
  • validateSourceManifest — what a manifest must say to be installable, including the vocabulary itself: which kind, transport, cursor and ingest values exist, and which subset is buildable today.
  • The cursor writerdateCursor, idSetCursor and their readers. The type and the code that produces it are now in one place, so the contract test asserts against the writer rather than fixtures.
  • The guarded fetch seamfetchPage / fetchBytes, with the host guard, timeout and size caps a source should never re-implement. It takes the fetch it is given, so a source called with a capability-bearing ctx.fetch uses that one.

Feed and HTML parsing — the RSS/Atom reader, HTML to text, the scrape loop — still live alongside the sources themselves. Those are the next candidates; the pieces every source needs in order to be correct rather than merely convenient now live here.

It is the symmetric sibling of @ontrove/extend/toolkit, the toolkit-authoring library (every toolkit runs as a full MCP server on Trove’s cloud): a source returns documents to be stored (defineSource + sync); a toolkit’s tools return results to be read live (defineToolkit). The two are at different stages — a toolkit is written in @ontrove/extend/toolkit, while a source is written against a contract this package defines and helpers it does not yet provide.

import { defineSource } from '@ontrove/extend/source';
export default defineSource({
async sync(ctx) {
const res = await ctx.fetch('https://hn.algolia.com/api/v1/search?tags=front_page');
const { hits } = await res.json();
return hits.map((hit) => ({
id: hit.objectID,
title: hit.title,
text: hit.story_text ?? hit.title,
url: hit.url,
author: hit.author,
date: new Date(hit.created_at_i * 1000).toISOString(),
contentType: 'bookmark',
}));
},
});
ClassDescription
HttpStatusErrorA response that arrived intact but was not OK. Carries the status so retry logic can tell a transient 503 from a permanent 404 without parsing prose.
ResponseTooLargeErrorA response rejected by the size cap: a permanent condition (the resource is simply too big), unlike a timeout or connection error that may succeed on retry. Callers branch on isTooLargeError rather than on this class.
InterfaceDescription
DocumentA single document a source returns from sync. The fields map 1:1 onto the GraphQL IngestDocumentInput the Mac app pushes via ingestDocuments:
ExtensionCacheA run-to-run cache, where the host offers one.
ExtensionContext-
FetchedPageWhat a fetch learned about the address itself, alongside the body.
GuardedFetchOptionsOptions shared by every helper here.
HistoryReachHow much of an upstream’s history a source can actually reach.
LogChannelA log channel: callable for the common case, with levels when severity matters.
ManifestConfigFieldA single field descriptor inside a manifest config object — describes one preference input shown in the source’s setup wizard.
ManifestValidationOptionsHow strictly to read a manifest, and what the caller knows that the manifest cannot say for itself.
ManifestValidationResultThe outcome of validateSourceManifest.
RunOptionsThe options runSource builds a ctx from. The CLI passes the source’s stored config and the feed’s current cursor; tests inject fetch, log, and now for determinism.
RunResultThe outcome of runSource: the validated, deduped documents, the resolved cursor, and the captured log lines. Mirrors enough of what the cloud ingest reports for trove source test to print a useful summary.
SourceContextThe single argument to syncExtensionContext plus what a scheduled, resumable, fan-out-capable source needs on top of it.
SourceExtensionA complete source: what it is, and what it does.
SourceManifest-
SourceSyncResultThe result of a source sync: the documents fetched this run and, optionally, the cursor the feed should advance to. A source may also return a bare Document[] for convenience — runSource normalizes that to { documents } with no cursor change.
TroveSourceThe type a source’s default export satisfies. A source is an object with a sync method that fetches new content and returns documents to index.
Type AliasDescription
CursorA typed cursor describing how a feed resumes between syncs. The opaque Feed.cursor string is parsed into one of these (see the cursors guide, cursor types).
CursorStrategyOne of CURSOR_STRATEGIES.
DirectoryAuthStrategyOne of DIRECTORY_AUTH_STRATEGIES.
DirectoryModeOne of DIRECTORY_MODES.
FanOutFieldTypeOne of FAN_OUT_FIELD_TYPES.
FetchLikeThe standard fetch signature the SDK exposes on SourceContext.fetch. Matches the platform fetch so existing code ports unchanged.
FormattingPolicyOne of FORMATTING.
HistoryReachKindOne of HISTORY_REACH_KINDS.
IngestModeOne of INGEST_MODES.
RunsInOne of RUNS_IN.
SourceContentTypeThe default content type Trove assigns a document when it omits contentType. Mirrors the GraphQL ContentType enum surfaced on IngestDocumentInput.
SourceKindOne of SOURCE_KINDS.
SourceScheduleOne of VALID_SCHEDULES.
SourceTransportOne of TRANSPORTS.
VariableDescription
CLOUD_ELIGIBLE_TRANSPORTSThe transports whose sync is a pure HTTP pull — the necessary condition for a source to run in the cloud at all. A browser source drives a real browser and a local source reads the user’s disk; neither exists in a hosted runtime, so both are pinned to the client.
CURSOR_STRATEGIESThe resume strategy a source declares; the value itself lives in the feed’s cursor between runs. date, idSet and none are the three the SDK’s Cursor type carries today; the rest are declared shapes for feeds that resume by token, by row, or by whole-snapshot comparison.
DEFAULT_ID_SET_MAXDefault cap on how many entries an idSet cursor retains.
DIRECTORY_AUTH_STRATEGIESThe auth strategies Trove knows how to sign a directory lookup with. A directory provider names one and the platform applies it, so no source author ever handles the credential.
DIRECTORY_MODESThe affordances a directoried config field can ask a client to render: search (type a name, pick from results) or resolve (paste something and have it turned into the real address).
FAN_OUT_FIELD_TYPESThe config field types a fan-out source may explode into one feed per entry — a list of feed URLs, or a list of query strings. A scalar field cannot fan out, so naming one in fanOut is rejected rather than silently producing a single feed.
FETCH_TIMEOUT_MSPer-request ceiling. Without it a single slow or hung host stalls an entire sync run for minutes. A bounded request fails fast and is retried next run.
FORMATTINGWhether Trove reformats a source’s documents into clean Markdown on ingest, or stores them exactly as received.
HISTORY_REACH_KINDSHow much of an upstream’s history is reachable at all.
INGEST_MODESWhat ingest does with the documents a run returns. append adds what is new and leaves what is stored alone; upsert lets a later run replace an earlier document with the same id.
MAX_ID_SET_BYTESCap on the SERIALIZED size of an idSet cursor.
MAX_REDIRECTSRedirect hops followed before giving up. Feeds need far fewer than a browser.
MAX_RESPONSE_BYTESDefault response-size cap: large enough for a long article, small enough that one page cannot exhaust a run.
MVPThe MVP cut: the subset of each vocabulary the runtimes actually build and enforce today.
MVP_DEPLOYED_CURSORSThe cursor strategies a runtime: deployed source may additionally use.
RUNS_INDefault executor for a source’s sync. cloud = a Trove-hosted runtime; client = the user’s own device.
SOURCE_KINDSExecution contract — which entrypoint the harness invokes.
SOURCE_TYPE_FIELDSThe four type-system fields with their full vocabularies, keyed by field name. Exported so a catalog can render the taxonomy — a picker, a docs table, a test that asserts every source’s declaration is in range — from the same data the validator uses, rather than a copy that drifts.
TRANSPORTSThe mechanism by which a source reaches its data. This is what decides whether the source can run anywhere but the user’s own machine — see CLOUD_ELIGIBLE_TRANSPORTS.
TROVE_USER_AGENTDescriptive, attributable User-Agent. A site operator who wants to identify or rate-limit this traffic can, which is the difference between a bot that is welcome and one that gets blocked.
VALID_SCHEDULESThe sync cadences a manifest may name. A schedule is a human-readable phrase rather than a cron expression because it is shown to the person enabling the source; the scheduler maps each phrase to an interval.
FunctionDescription
advanceDateCursorThe date cursor to return from a run whose sub-sources (feeds, sections, tickers, channels, meeting types) may have individually failed.
assertPublicHttpUrlGuard an address before fetching it. Only public web pages are ever wanted, so require http(s) and reject private, loopback, and link-local hosts.
dateCursorBuild a typed date cursor from an ISO-8601 string.
defineSourceValidate and return a source definition unchanged.
defineSyncA source authored inline as a single sync function.
fetchBytesBinary twin of fetchPage: same guard, User-Agent, deadline, and streamed size cap, but returns the raw bytes — for document downloads (PDFs, images, audio) where decoding to text would corrupt the payload.
fetchPageFetch a page and return its text, with the honest bot User-Agent, the SSRF guard, the deadline, and the size cap. Throws on a non-200.
fetchPageWithMetafetchPage plus what the fetch learned about the address itself.
idSetCursorBuild a typed idSet cursor: deduped, bounded to max entries, and then bounded again to MAX_ID_SET_BYTES so the cursor the platform stores cannot be refused.
isCredentialConfigKeyWhether a single config key looks like a credential and must be rejected. Exposed on its own so an authoring tool can warn on the field the moment it is typed, instead of only when the whole manifest is validated.
isTooLargeErrorWhether an error from these helpers was a size-cap rejection — the one failure worth treating as permanent (skip the document) rather than transient (retry next run).
readDateCursorRead a date cursor as a Date.
readIdSetRead an idSet cursor as a string array.
runSourceRun a source’s sync against a built ctx and collect/validate the result.
stringListRead a config field as a list of strings.
toSourceManifestThe manifest half of a source, as the JSON a catalog commits.
validateSourceManifestValidate a source manifest.json in full — shape, credential lint, the four type-system fields, location and its cloud-eligibility predicate, and the optional schedule, fanOut, formatting and directory declarations.