Skip to content

pages: page property events, fold and reads (#132) - #143

Merged
shovon merged 1 commit into
mainfrom
issue-132
Oct 29, 2026
Merged

shovon merged 1 commit into
mainfrom
issue-132

Conversation

@shovon

@shovon shovon commented Oct 29, 2026

Copy link
Copy Markdown
Owner

Closes #132. Part of #84.

What changed

Page properties (packages/pages/properties.ts, new): the bottom layer of #84. Nothing writes these events yet and nothing shows them; the builders and commands are #133.

  • Five events, each under NAMESPACE at v: 1: page.property.set, .unset, .item.added, .item.removed and .removed. Payload types with doc comments on what each field means.
  • The fold runs in two steps:
  • removed is several of the edits the single events make, applied in one go. It clears a seen value only if it is still the one seen, and the items it lists. What another device wrote that the remover never saw survives, in either fold order.
  • Normalizing: the fold trims path segments and puts every string in NFC. Scalars are strings, finite numbers, booleans and null, and keep their type. -0 is 0.
  • No timestamps: the fold reads { type, v, payload } like the block fold, and keeps no at.
  • Reads: get, getValue, getItems, list, children, and the path / showPath helpers. Sorted by path segment, by codepoint. Every result is a snapshot.

The pages fold (pages.ts, blocks.ts):

  • Pages gains properties beside blocks and titles.
  • pagesFold runs the block fold, then the property fold. Each hands back the state it was given for an event that is not its own.
  • HANDLES is the block table plus the property table. The block half is BLOCK_HANDLES inside the package; the public HANDLES keeps its name.
  • getPages lists a journal day that has properties and no blocks.
  • A property event spells its page's title the way block.created does; spell is exported within the package for that.

README.md: a "Page properties" section with the five events, the rules, and the reads.

For the reviewer

  • A fix the new field needed: block.created built its result from blocks and titles alone, which would have dropped properties. It now spreads the state.
  • One test narrowed: "every builder writes the v its own gate reads, and there is one for every event" now checks block events only. The property builders are Page properties: builders and commands #133, which should restore the full check.
  • Live vs reopened: the app folds a write's payload in memory before it goes through JSON. A NaN would be skipped live but written to disk as null, a value, and show after a reopen. The fold can't tell; Page properties: builders and commands #133's commands refuse non-finite numbers for that reason.
  • Export names: get, list and children are top-level package exports, as Page properties #84 names them. Happy to rename to something property-specific next to getPage if preferred.
  • The ledger is untouched. No format change; a build without this skips the property events through its own gate.

Tests

  • properties.test.ts (20 tests, new), folding through pagesFold:
    • each event's effect; unset leaves items alone and item.removed leaves the value alone
    • the later write in fold order wins for the value and for each item; two writers adding different items keep both; a re-added item moves to the end
    • emptied entries, parents and pages are gone, and list and children don't show them
    • a page with properties and no blocks is a page, under the first spelling seen
    • malformed payloads are skipped whole; the gate skips a newer v; __proto__ as a title, a segment or an item is an ordinary key
    • NFC and NFD are one segment, value and item; 1, "1", true and null are four items; trimmed segments; "" is a value
    • removed: what it lists is cleared, what it doesn't list survives in either fold order, null and an absent value
    • list and children on nested paths, the root, a scope that holds something itself, ["foo"] beside ["foobar"], codepoint order
    • reads are snapshots; the fold never changes the state it is handed; an event that changes nothing hands back the same state
  • Existing tests: blocks.test.ts builds Pages with properties and checks BLOCK_HANDLES.
  • npm test --workspaces (233 pass), npm run lint -w app and npx tsc --noEmit -p app are clean.

🤖 Generated with Claude Code

A page can carry properties: a tree keyed by path, where each node holds
a value, a set of items, both, or only what is under it. This adds the
bottom layer of #84, all in @pbnotes/pages: five events, the fold over
them, and the reads. Nothing writes these events yet and nothing shows
them; the builders and commands are #133.

properties.ts holds the events (page.property.set, .unset, .item.added,
.item.removed and .removed, each at v: 1), the fold and the reads. The
fold parses a payload into edits to single paths, or skips the event
whole if it is malformed, then applies the edits. removed is several of
the edits the single events make, applied in one go: it clears a seen
value only if it is still the one seen, and the items it lists, so what
another device wrote that the remover never saw survives. The fold trims
path segments and puts every string in NFC; scalars are strings, finite
numbers, booleans and null, and keep their type. No timestamps are kept.

Pages gains properties beside blocks and titles, and pagesFold runs the
block fold, then the property fold. block.created built its result from
blocks and titles alone, which would have dropped properties; it now
spreads the state. HANDLES is the block table plus the property table;
the block half is BLOCK_HANDLES inside the package. getPages lists a day
that has properties and no blocks. A property event spells its page's
title the way block.created does.

Closes #132.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@shovon
shovon merged commit b3395ee into main Oct 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Page properties: events, projection and reads

1 participant