Skip to content

v10.2.1

Choose a tag to compare

@github-actions github-actions released this 05 Sep 13:04
· 51 commits to main since this release
69136a6

Patch Changes

  • 3ece586: feat(idn-regions): idn_admin_regions.region.read shipped with the module (ADR-0046) and, until now, had no caller anywhere — not awcms-astro, not even the lookup API's own route files, which described their own consumers as including "the admin browser" when none existed. 91,599 regions were reachable only by curl. /admin/idn-regions gains a region browser panel — drill-down by tier (province → regency/city → district → village) via level/parentCode, name search, keyset paging on the API's own nextCursor (never a bare limit), a resolved ancestor path on selection, dataset=<code> to browse a superseded version, and a distinct empty state for reason: "no_active_dataset" versus an ordinary empty result. The panel is gated on region.read alone — an ordinary TENANT permission, unlike the platform-scoped dataset.configure/.restore — so a viewer holding only region.read in a non-platform tenant sees it and nothing else; the page's entry authorize becomes an any-of array (dataset.read OR region.read), the same shape /admin/domain-events already uses for independently-readable panels. Browsing is entirely server-rendered GET forms and links, so it ships zero client JavaScript — the measured app bundle stays at 213,807 B, unchanged. No create/update/delete of individual regions: this is a read-only browser over a vendored third-party dataset, deliberately. Removes the now-stale DELIBERATELY_UNSCREENED entry for idn_admin_regions.region.read in admin-screen-coverage-check.ts.

  • c3bb19e: fix(e2e): grep -rn "viewport" tests/e2e/ returned nothing — the whole Playwright suite has always run at devices["Desktop Chrome"] (1280×720), so "the admin surface works on mobile" was an unverified claim with no test behind it. A new read-wave spec responsive-360.e2e.ts overrides the viewport to 360×640 (the narrowest width in real use — anything that fits 360 fits 375/390) and sweeps every STATIC discovered admin route, asserting document.documentElement.scrollWidth never exceeds the viewport by more than a pixel of rounding slack; on overflow it also names the offending elements (tag, id/class, right edge) instead of leaving a bare number. Unlike admin-screens-render.e2e.ts, the two platform-scoped screens are NOT exempted here — a refusal page must not scroll sideways either, and horizontal overflow is well-defined for both outcomes, unlike contents-vs-refusal. discoverAdminRoutes/AdminRoute/the pages-root resolution moved out of admin-screens-render.e2e.ts into a new support/admin-routes.ts so this spec could reuse the same discovery without importing one .e2e.ts file into another (which would have registered the render sweep's own tests into this file's suite, under a viewport it was never written for) — admin-screens-render.e2e.ts's behaviour is unchanged. Run against a real server and Postgres: it found two genuine overflows, /admin/account (document.documentElement.scrollWidth 613px, from #account-sessions-table) and /admin/seo (396px, from the SEO rules table and a <select>) — left unfixed and unexempted here, as real findings a gate exists to surface, not to launder.

  • 1530f59: feat(idn-regions): rejected was a dataset status the schema's CHECK admitted, DatasetStatus declared, and /admin/idn-regions's statusVariant() rendered as danger — and that no code path ever wrote. Because idn-regions:import validates the vendored dump whole and refuses to write a partial hierarchy, a failed import wrote no dataset row either, so on the console a dataset that failed to import and a dataset never attempted were indistinguishable; the failure existed only in the shell or CI log of whoever ran it. A failed validation now records its own rejected row carrying the attempted dump's provenance and the reason it failed, and still writes zero region rows. The row is written AFTER the failed attempt's transaction has rolled back — recording it inside that transaction would have rolled the evidence back along with it, silently recreating the defect. Re-running the same bad dump refreshes that row in place via ON CONFLICT … DO UPDATE rather than erroring on the deterministic dataset code or accumulating duplicates. Adds sql/149 (the rejection_reason column, with a CHECK tying it to the status by construction: a rejected row must carry a reason and no other status may). The import job's descriptor also gains the environmentNotes it never had, naming the awcms-jobs image that can actually execute it.

  • 09245a1: feat(idn-regions): activating a region dataset changes the address hierarchy every tenant is served, immediately and platform-wide, and until now the operator confirming it could see a version code, a row count and a source commit — nothing about what would actually change. A well-formed dump of the wrong year passes every check the importer makes, and rollback is a recovery that only runs after tenants have already been served the wrong data. /admin/idn-regions now compares two dataset versions before the decision: per-tier counts on both sides with the delta, plus the codes added, removed, and renamed, each keyset-paged rather than rendered whole. Both versions already live side by side in awcms_idn_admin_regions — the property that makes rollback a status flip — so the comparison is a query rather than a re-parse. Gated on the tenant-scoped dataset.read, so a non-platform tenant can audit what it was served last week against what it is served now. sql/150 adds the two supporting indexes, including a covering index on (dataset_id, code), and the integration tests assert the query plan does not fall back to a sequential scan of either dataset.

  • 150e7e3: chore(bun): the pinned Bun version moves 1.3.14 → 1.4.2 across the six places that pin it — package.json's packageManager, the three CI workflows that set up the current-pin bun-version (ci.yml's quality job, changesets.yml, release.yml), Dockerfile.production's base image, and deploy/cron/awcms.crontab's job runner image — plus awcms-family-compatibility.yaml, the manifest family:conformance:check reads to assert those six agree with each other and with itself. bun install --frozen-lockfile under 1.4.2 leaves bun.lock byte-identical, so nothing in the dependency graph moved. The engines.bun floor and the ci.yml minimum-supported job deliberately stay pinned at 1.3.0: since the lockfile didn't change, the set of code actually exercised at the floor didn't change either, so there was nothing there to re-verify. The one artifact that does depend on which Bun built it — the committed public/js/blog-preview-overlay.js bundle that build:preview-overlay:check compares byte-for-byte against a fresh build — was regenerated under 1.4.2 now that packageManager and local Bun agree again; the bytes differ only in the bundler's internal minified variable names, not in behavior. docs/awcms/family-compatibility.md (+ its Indonesian mirror), the two implementation-blueprint docs, docs/PROJECT_STATE.md (+ mirror, made version-neutral so the next pin move doesn't require a second edit), and ADR-0032 (+ mirror) were updated to match, since a stale doc naming the old pin is exactly the kind of drift family:conformance:check and tests/family-compatibility-doc-parity.test.ts exist to catch.

  • c3bb19e: fix(ui): two admin screens dragged the whole page sideways on a phone. On /admin/account, the "Where you are signed in" table was already inside the standard .table-scroll wrapper — the wrapper itself was working, but the sr-only "Actions" label in its header row (position: absolute, no offsets) had no positioned ancestor closer than the page shell, so it kept its full, unclipped table-width position instead of the wrapper's, and dragged document.documentElement's width out to 613px at a 360px viewport. On /admin/seo, the site-wide noindex <select> did the same thing for a different reason: as an unstyled flex item next to the other create-form fields, its automatic minimum width locked to its longest <option>'s un-truncated text ("noindex (hide the whole site from crawlers)") and refused to shrink, pushing scrollWidth to 396px. A visitor on a real phone could drag either screen horizontally to see content that should have just wrapped or scrolled in place.

    Fixed both at the shared rule rather than per-page: .admin-table-wrap/.table-scroll now sets position: relative, so an absolutely-positioned descendant is contained by the scroll wrapper instead of escaping to whatever ancestor happens to be positioned; .admin-create-form select now gets the same min-width: 0 + max-width: 100% treatment textarea already had, so it shrinks to its allotted space like every other field in the row. Verified against a live server and Postgres at a 360×640 viewport: both screens' document.documentElement.scrollWidth are now 360px, and the full responsive-360 and admin-screens-render E2E sweeps pass.

  • 72b49b9: fix(docs,idn-regions): six passages still described region-dataset activation/rollback as removed from HTTP with revoked permissions (ADR-0052), a state ADR-0053 reversed the next day by restoring both as PLATFORM-scoped permissions (sql/085) with live endpoints. Corrects idn-admin-regions/module.ts's own description/comments/job metadata (including the jobs[].environmentNotes mis-attribution to a reversed ADR-0052 holding), docs/ARCHITECTURE.md + its Indonesian mirror, docs/PROJECT_STATE.md + its mirror, and the module's own README + its mirror — states, once, plainly, that the HTTP path writes an audit row and the job path deliberately does not. No behavior change; no schema, API, or permission change.

  • 9306807: fix(ui,idn-regions,push): three operator-facing admin instructions named a bun run job command without naming where it can run. Dockerfile.production's runtime stage (what actually serves /admin/idn-regions and /admin/push-notifications) carries only dist/, not scripts/ — an operator who reads either screen and runs the printed command in that container gets Script not found. Only the separately published jobs image (ghcr.io/ahliweb/awcms-jobs, built by release.yml) carries scripts/ and can run it. /admin/idn-regions's empty state and its "What this data is, and is not" caveat, and /admin/push-notifications's VAPID-unconfigured notice, now name the awcms-jobs image explicitly, sharing one translated sentence across all three instead of inventing the wording three times. idn-admin-regions/module.ts's jobs[] environmentNotes for idn-regions:activate/:rollback gained the same fact. Copy and metadata only — no behavior, schema, or API change.