Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.

themountain_sync

themountain_sync is a Val Town script that reads bolded passages from a Notion page and writes the current quote set into Val Town blob storage for the themountain app to display.

Source Of Truth

The source of truth is the Notion page:

  • title: the mountain is you reading
  • page ID: 34ac869b-2f04-80cf-a766-ed382650df33

The sync script is run manually from the Val Town editor after that page is edited.

What The Script Does

On each run, the script:

  1. Fetches the Notion page block tree recursively.
  2. Extracts bold text runs from the block content.
  3. Normalizes and deduplicates the extracted quotes.
  4. Compares the extracted source quotes against the currently cached blob data.
  5. Rewrites blob storage so the stored quote list matches the current Notion source exactly.
  6. Writes sync metadata describing what happened.

The important design choice is that Notion is now authoritative.

This script does not keep an append-only archive anymore. If a quote disappears from the extracted Notion source, it is removed from blob storage on the next sync.

How It Runs

This val is a script, not an HTTP handler.

  • main() contains the sync logic.
  • if (import.meta.main) calls main() when you press Run in Val Town.
  • The script emits structured JSON logs during the run.

Without the import.meta.main block, loading the file would not execute the sync.

Required Environment Variables

Set these in the Val Town left sidebar:

  • NOTION_TOKEN
  • NOTION_PAGE_ID

Current page ID:

NOTION_PAGE_ID=34ac869b-2f04-80cf-a766-ed382650df33

NOTION_TOKEN must belong to the Notion integration that has access to that page.

Notion Access Failure Mode

If the page ID is correct but the integration is not shared onto the page, Notion returns:

  • 404 object_not_found

In this setup, that usually means the page exists but the integration behind NOTION_TOKEN cannot read it.

To fix that:

  1. Open the mountain is you reading in Notion.
  2. Click Share.
  3. Add the integration used by NOTION_TOKEN.

Blob Storage

The script writes to these blob keys:

  • themountain_quotes_v1
  • themountain_sync_meta_v1

themountain_quotes_v1

  • stores the current synchronized quote list
  • is what the themountain app reads on every request

themountain_sync_meta_v1

  • sourceHash
  • sourceQuoteCount
  • cachedQuoteCount
  • lastSyncedAt
  • lastChangedAt
  • lastAddedCount
  • notionPageId

Seeded Quotes

main.ts contains a SEEDED_QUOTES array.

That seeded list is only a bootstrap fallback for empty or unreadable blob storage. It is not the long-term source of truth. Once sync has written blob data, the live app should be driven by blob storage, not by the seed list.

Run Statuses

Successful runs can currently return:

  • unchanged
  • updated
  • reconciled

unchanged

  • the current Notion source hash matches the previous sync
  • the cached blob quote list already matches the current extracted source

updated

  • the Notion source hash changed
  • blob storage was rewritten to the newly extracted source quotes

reconciled

  • the current Notion source hash matches the previous sync hash
  • but blob storage did not match the current extracted source
  • blob storage was rewritten to bring it back in sync

This reconciled path matters because it repairs stale cached quote lists even when the source page itself has not changed.

Useful Logs

The script emits structured logs that are useful when debugging:

  • sync.start
  • sync.fetch.begin
  • sync.fetch.complete
  • sync.fetch.sample
  • sync.hash.computed
  • sync.cache.loaded
  • sync.diff.computed
  • sync.cache.quotes_written
  • sync.cache.meta_written
  • sync.complete
  • sync.failed

The most useful debug event is sync.fetch.sample, which logs:

  • sourceQuoteCount
  • firstFiveQuotes
  • lastFiveQuotes

That makes it easy to verify whether the extractor is seeing the beginning and end of the Notion page and whether a specific formatting edit is actually present in the API output.

Extraction Notes

The extractor walks the Notion block tree recursively and scans nested rich-text arrays inside each block payload.

This is important because Notion does not always expose useful text in a single top-level rich_text field for every block shape. Earlier versions of this script missed valid bold content because the traversal was too narrow.

Local Workflow

Useful local commands:

deno check --allow-import main.ts vt status vt push vt pull vt tail

This repo is linked to the Val Town project through .vt/state.json.

Relationship To themountain

themountain_sync writes the quote data.

themountain reads themountain_quotes_v1 and renders the quote app.

If the sync output is correct but the app still looks wrong, the next thing to check is whether blob storage was actually rewritten and whether the app is reading the same blob key.

Maintenance Rule Of Thumb

When debugging this system, reason in this order:

  1. Is Notion returning the expected bold text?
  2. Is themountain_sync extracting the expected quotes?
  3. Did the sync write the updated quote list to themountain_quotes_v1?
  4. Is themountain reading that same blob key and rendering the resulting list?

That order avoids mixing up extraction bugs, sync bugs, and app display bugs.