From 30dc9a145711f95716651947c710314c9c3ba050 Mon Sep 17 00:00:00 2001
From: Doxa Maps
Date: Sat, 3 Oct 2026 05:20:43 +0000
Subject: [PATCH 01/12] Shareable pages P1: one address helper, and the filters
write through it
library/composables/useViewAddress.js is the page's address for the view a reader is on: ?tab= &pane= &place=, with
a level rule, an id whitelist, and ONE history write (commitUrl). It keeps history.state and sets its `current` to
the new URL, so a link survives the next in-app navigation on a Vue Router host. The OUTERMOST owner writes,
named by profile-config urlOwner:
- 'app' writes history itself;
- 'host' hands its router one dxm-address;
- absent, nothing is written and links are still read.
Nested elements announce dxm-view. Back/Forward is read and never written.
useShareableView's filter write used to call replaceState(null, ...) itself, which wiped the router's state. It
now goes through commitUrl (writeFilterView). A filter write keeps tab/pane/place, and a tab/pane/place write keeps
the filters.
Level 2 is `pane`, not `view`: ?view= is already published by four of our pages.
check:view-address (gates:pre) runs on a simulated browser history. It covers:
- the whitelist and the level rule;
- one push per tab change, with state + current kept;
- no write when unchanged, on a partner page, or by a host owner;
- the filters and the view keys keeping each other through writes, Back and Forward;
- a census of every history writer in library/ and plugin-designs/ (6 today). A new writer fails until it is listed.
The self-test has 14 poisons, each caught. Real-source mutations are caught too: drop current, drop the unchanged
guard, drop the level rule, an uncomposed event, no length cap, an aliased pushState in a plugin.
---
internal/check-view-address.mjs | 237 ++++++++++++++++++++++++
library/composables/useShareableView.js | 27 ++-
library/composables/useViewAddress.js | 108 +++++++++++
package.json | 6 +-
4 files changed, 368 insertions(+), 10 deletions(-)
create mode 100644 internal/check-view-address.mjs
create mode 100644 library/composables/useViewAddress.js
diff --git a/internal/check-view-address.mjs b/internal/check-view-address.mjs
new file mode 100644
index 000000000..96a44083e
--- /dev/null
+++ b/internal/check-view-address.mjs
@@ -0,0 +1,237 @@
+#!/usr/bin/env node
+// check:view-address — the page's address (library/composables/useViewAddress.js) and its ONE history write.
+// A. the pure helpers: the id whitelist + 64 cap; an unknown or malformed value reads as absent (the default opens);
+// the level rule (a tab change drops pane and place, a pane change drops place, unless the change sets them);
+// foreign params keep their exact text.
+// B. the writes, on a simulated browser history: an 'app' owner pushes once and keeps history.state with `current`
+// and the hash; an unchanged address writes nothing; a partner page (no owner) writes nothing; a 'host' owner
+// writes nothing and hands the host one dxm-address event; nested elements announce dxm-view; Back/Forward is
+// only read, never written.
+// C. COEXISTENCE with the filters (useShareableView, which writes through the same commitUrl): a filter write keeps
+// tab/pane/place, the hash or the query it does not own, and the router's history.state; a tab/pane/place write
+// keeps the filters (query AND hash form); both survive Back and Forward, and re-applying the old tab after Back
+// writes nothing, so Forward still exists.
+// D. THE CENSUS of history writers, enumerated from the tree: every history.pushState/replaceState call in library/
+// and plugin-designs/ is the helper's or a listed writer. A new one fails until it is listed here with its reason.
+// --self-test: each poison below must FAIL its arm. Pure node: no Vite, no build, no browser.
+import { execFileSync } from 'node:child_process'
+import { readFileSync } from 'node:fs'
+import { join, dirname } from 'node:path'
+import { fileURLToPath } from 'node:url'
+const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
+
+// The history writers that exist today besides the helper, by call count. Adding one means a row here with a reason.
+const KNOWN = {
+ 'library/composables/useViewAddress.js': [1, 'THE write (commitUrl)'],
+ 'library/utils/languageRequest.js': [1, '?lang= on a generated page only (MAP_OWNS_PAGE); keeps state and every other param'],
+ 'library/layouts/app-wrapper-material/index.html': [1, "the layout page's own #/