Skip to content

公開のトップページをアプリに返す - #73

Merged
snakajima merged 1 commit into
mainfrom
feat/app-owned-index
Aug 27, 2026
Merged

公開のトップページをアプリに返す#73
snakajima merged 1 commit into
mainfrom
feat/app-owned-index

Conversation

@snakajima

@snakajima snakajima commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

type: "article" は住所を 2 つ取っていた。

住所 いま描くもの 誰のものであるべきか
/a/{slug} プラットフォームの索引 アプリ
/a/{slug}/{id} プラットフォームの記事ページ プラットフォーム

索引がプラットフォームのものだったことは一度もない——ただのレコードの一覧で、手書きの公開ページは同じ datasets をすでに受け取っている。それでも viewSourceProblemspathtype を排他にし、public ビューはアプリに 1 つなので、記事を出すアプリは公開の顔を丸ごと手放していた。表紙も About もセクションのナビも置けず、記事アプリは全部おなじページになる。

記事ページのほうは本当にプラットフォームのものである。markdown は sandbox の外・host の origin で v-html されるので、articleMarkdown.tsmarkedDOMPurify)がセキュリティ境界そのもので、ページに渡せる代物ではない。

変えたこと

views[].type を撤去。article ブロック単独が宣言になる。

{ "id": "public", "audience": "public",
  "path": "views/home.html",
  "collections": ["articles"],
  "article": { "title": "title", "body": "body", "byline": "byline" } }

path はすべてのビューで必須。type名指しで断る——足すべきもの(path)を言う必要があるので、.strict() の "Unrecognized key" では足りない。

article.collection を追加。 索引が自前になった以上、雑誌の表紙が 2 本目の dataset(セクション、マストヘッド)を読む理由はいくらでもある。以前の「コレクションは 1 本だけ」は、プラットフォームが全部描いていたから安上がりだっただけ。1 本しか名指していないビューでは省ける。射影は解決済みの collection を必ず載せるので、reader が推論をやり直すことはない。

view.open(cid, id) ——新しい橋の動詞。 フレームは sandbox="allow-scripts" のみ(allow-top-navigationallow-popups も無い)ので、ページからリンクが張れない。索引がアプリのものになった以上これが要る。

  • URL ではなくレコードを名指す。 slug は親が持つので、ページは読んでいるアプリの外へ人を送れない——検査ではなく形で保証される
  • cid はそのアプリが記事を描く唯一のコレクションでなければ断られる
  • id の文法(/ も先頭の . も空も不可)で、host の encode 忘れに依存しない
  • opened: false, reason: "no-navigation"拒否ではない。navigate ポートを持たない親(著者のプレビューのペイン)の答えで、ページ側にできることは何もない。本番では通常 settle しない——遷移がこの文書を持っていくから。それがリンクの意味論

protocol は据え置き

protocolFor の判定を type から article に付け替えただけで、記事アプリは 2.0.0 のまま。古い reader は path(1.0.0 から知っている鍵)を描くので、間違えるのは /a/{slug}/{id} だけ——「索引の位置に生成フォームを出す=別のアプリを見せる」から「共有リンクが索引に落ちる」への劣化になった。major を維持したのは、これらのアプリが idFrom: "slug" で既に 2.0.0 だからタダだから。

検証

yarn typecheck / yarn lint / yarn test(545 pass)/ yarn check:apps(ALL 10 APPS PUBLISH)。

test/test_viewOpen.ts は 9 本。ブートストラップを node:vm で実際に走らせて view.open が出す線とその settle まで見ている。

手順

reader が先——mulmoserver を先に出し、このパッケージの publish 後に MT。既存のブログアプリ 2 本(ai-notes / ai-journal)は互換の受け皿を作らず、著者が type を消して索引ページを足す。

計画: mulmoterminal plans/feat-shared-app-app-owned-index.md

Summary by Sourcery

Return public index rendering to applications while keeping secure platform article pages and adding a safe navigation bridge from sandboxed views.

New Features:

  • Restore ownership of the public article index to applications while retaining platform-rendered article detail pages.
  • Add the view.open(cid, id) API for sandboxed public pages to navigate to articles safely.
  • Allow article declarations to identify their collection explicitly when a view reads multiple collections.

Bug Fixes:

  • Prevent article applications from losing their entire public-facing page to the platform's generated index.
  • Provide clear publication errors for the retired type: "article" declaration and missing view paths.

Enhancements:

  • Resolve and publish the article collection in the projected view configuration.
  • Update protocol detection and article validation for the standalone article declaration model.

Tests:

  • Add coverage for article declaration validation, collection resolution, protocol compatibility, and the complete view.open message flow.

Summary by CodeRabbit

  • New Features

    • Embedded views can now request opening an article by collection and record ID.
    • Hosts can approve, decline, or report unavailable article navigation.
    • Article views can specify which collection contains their articles, including support for multiple collections.
    • Added clearer validation for article routes, collections, identifiers, and public visibility.
  • Breaking Changes

    • The previous type: "article" view declaration is no longer supported; use an article block instead.
    • Article views now require an HTML path.

`type: "article"` は住所を 2 つ取っていた——索引の `/a/{slug}` と記事の
`/a/{slug}/{id}`。前者はプラットフォームのものだったことが一度もない(索引は
レコードの一覧で、手書きの公開ページは同じ datasets をすでに受け取っている)のに、
`path` と `type` は排他で public ビューはアプリに 1 つなので、記事を出すアプリは
公開の顔を丸ごと手放していた。

- `views[].type` を撤去。`article` ブロック単独が「このコレクションのレコードは
  `/a/{slug}/{id}` で markdown として描かれる」の宣言になり、`path` はすべての
  ビューで必須。`type` は名指しで断る(足すべきものを言う必要があるので、
  strict の Unrecognized key では足りない)
- `article.collection` を追加。索引が自前になった以上、雑誌の表紙が 2 本目の
  dataset を読む理由はいくらでもある。1 本しかないビューでは省ける
- `view.open(cid, id)` ——フレームは `sandbox="allow-scripts"` のみで、
  ページからリンクが張れない。URL ではなくレコードを名指し、slug は親が持つので、
  ページは読んでいるアプリの外へ人を送れない
- protocol は据え置き(2.0.0)。判定を `type` から `article` に付け替えただけで、
  古い reader は `path` を描くので「別のアプリを見せる」ではなく劣化になった

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @snakajima, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 4 days and 7 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cef3d3d7-8e87-4afd-90a3-b736af250832

📥 Commits

Reviewing files that changed from the base of the PR and between 54768b4 and 3e8f9cd.

📒 Files selected for processing (18)
  • eslint.config.js
  • src/appProtocol.ts
  • src/appViews.ts
  • src/index.ts
  • src/publishChecks.ts
  • src/publishManifest.ts
  • src/publishProject.ts
  • src/view/bridge.ts
  • src/view/index.ts
  • src/view/message.ts
  • src/view/parent.ts
  • src/view/protocol.ts
  • src/view/srcdoc.ts
  • test/test_appProtocol.ts
  • test/test_appViews.ts
  • test/test_publishChecks.ts
  • test/test_publishProject.ts
  • test/test_viewOpen.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The PR replaces type: "article" with an article declaration, resolves article collections during publication, and adds a host-mediated view.open(cid, id) protocol for sandboxed views.

Changes

Article view declarations and publication

Layer / File(s) Summary
Article declaration contracts
src/appProtocol.ts, src/appViews.ts, src/publishManifest.ts, src/index.ts
Article views now use views[].article. The retired type declaration is refused. articleCid resolves the article collection.
Article publication flow
src/publishChecks.ts, src/publishProject.ts, test/test_appProtocol.ts, test/test_appViews.ts, test/test_publishChecks.ts, test/test_publishProject.ts
Publication checks and projections use articleCid. Published article views include a resolved collection field. Tests cover collection selection and updated article declarations.

Sandbox article navigation

Layer / File(s) Summary
Sandbox open protocol
src/view/message.ts, src/view/protocol.ts, src/view/bridge.ts, src/view/index.ts, src/view/parent.ts, src/view/srcdoc.ts
Sandboxed views send validated open requests containing a collection and record id. Hosts navigate through an optional port and return openResult when navigation does not replace the document.
Open flow validation
test/test_viewOpen.ts, eslint.config.js
Tests cover request validation, navigation outcomes, dispatch ordering, and bootstrap promise settlement. Lint thresholds include the new bootstrap tests.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🔵 Low · up to 3e8f9

The new article-linking behavior delegates final destination selection to host code; an incorrect binding or authorization check could send users to an unintended article or expose content across app or tenant boundaries. The PR is mergeable with explicit owner awareness and follow-up to verify host-side destination and authorization enforcement.

Suggested reviewers: isamu

Sequence Diagram(s)

sequenceDiagram
  participant SandboxedView
  participant ViewParent
  participant Host
  SandboxedView->>ViewParent: Send open request with cid and id
  ViewParent->>ViewParent: Validate request and article collection
  ViewParent->>Host: Call navigate(OpenAsk)
  Host-->>ViewParent: Return navigation result
  ViewParent-->>SandboxedView: Send openResult when navigation does not occur
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title describes the public-page integration added by the pull request. It is related to the new view communication and navigation flow, although it does not mention the broader article declaration…
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 18 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title describes the public-page integration added by the pull request. It is related to the new view communication and navigation flow, although it does not mention the broader article declaration changes.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/app-owned-index

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR transfers ownership of /a/{slug} to each app’s HTML view, keeps the platform’s sanitized article renderer at /a/{slug}/{id}, and adds a constrained view.open(cid, id) bridge so sandboxed indexes can navigate safely; manifest validation, projection, protocol stamping, and tests are updated accordingly.

Sequence diagram for safe article navigation from a sandboxed view

sequenceDiagram
    participant Index as App index
    participant Parent as View parent
    participant Host as Host navigate port
    participant Article as Article page
    Index->>Parent: view.open(cid, id)
    Parent->>Parent: readOpenMessage(data, articleCid)
    alt valid article collection and id
        Parent->>Host: navigate({cid, id})
        Host->>Article: navigate to /a/{slug}/{id}
    else invalid collection or id
        Parent-->>Index: openResult(opened: false, reason)
    else no navigation port
        Parent-->>Index: openResult(opened: false, reason: no-navigation)
    end
Loading

Flow diagram for article declaration validation and projection

flowchart TD
    Manifest[View manifest with path and article block]
    Normalize[normalizeViews]
    Validate[articleCollectionProblems]
    Resolve[articleCid]
    Project[publicViewProjection]
    Reader[Reader receives resolved article.collection]
    Manifest --> Normalize
    Normalize --> Validate
    Validate -->|valid| Resolve
    Resolve --> Project
    Project --> Reader
Loading

File-Level Changes

Change Details Files
Make the app own the public index while retaining platform-rendered article pages as a separate address.
  • Remove views[].type as a declaration mechanism and explicitly reject it with migration guidance.
  • Require every view to provide an HTML path, while allowing article alongside it for /a/{slug}/{id}.
  • Update normalization, manifest validation, publishing, schema checks, theme handling, and exports for the new model.
src/appViews.ts
src/publishManifest.ts
src/publishChecks.ts
src/publishProject.ts
src/index.ts
Resolve and publish the article collection explicitly for multi-dataset indexes.
  • Add optional article.collection with single-collection inference.
  • Reject article declarations on non-public views and reject collections not listed by the view.
  • Project the resolved collection into published configuration and use it for article validation and cost checks.
src/appViews.ts
src/publishManifest.ts
src/publishChecks.ts
src/publishProject.ts
src/index.ts
Add a sandbox-safe view.open(cid, id) navigation bridge for app-owned indexes.
  • Define open request/result messages, strict record-ID validation, and article-collection authorization.
  • Expose navigate ports on the parent and bridge, handling absent, declined, and failing navigation as no-navigation.
  • Expose view.open in the generated bootstrap and preserve existing submit/lookup dispatch behavior.
src/view/message.ts
src/view/protocol.ts
src/view/parent.ts
src/view/bridge.ts
src/view/srcdoc.ts
src/view/index.ts
Update protocol compatibility and verification for the new article declaration.
  • Base per-app protocol versioning on article rather than retired type, retaining the 2.0.0 article contract.
  • Revise existing view, publishing, protocol, and project tests for required paths and collection resolution.
  • Add nine bootstrap/bridge tests covering navigation safety, validation, settlement, and message routing.
src/appProtocol.ts
test/test_appProtocol.ts
test/test_appViews.ts
test/test_publishChecks.ts
test/test_publishProject.ts
test/test_viewOpen.ts
eslint.config.js

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@snakajima

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@snakajima
snakajima merged commit 8875288 into main Aug 27, 2026
8 checks 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.

1 participant