Put a versions literal in front of the version detail route
What this does
Puts a versions/ literal in front of the version detail route, so it reads
/:id/:artifactId/versions/:versionId rather than /:id/:artifactId/:versionId.
Follow-up to this thread on !253776 (merged), where @rchanila preferred option 1 — literals for both the manifest and the version segment — over leaving the two asymmetric.
Why
A bare :versionId claimed every unreserved third segment under an artifact. That is why
manifests needed a literal to escape it while a version did not have one, and reading the
two routes side by side the asymmetry looked like an oversight rather than a decision. It also
meant a bare parameter was sitting on every segment a later step might want to hang there.
Both routes lead with a literal now, so neither can claim the other's segment and the order between them stays free.
What a bare segment does now
This is the question on the thread. Before:
| Path | Resolved to |
|---|---|
…/manifests/sha256:abc |
manifest detail |
…/manifests |
version detail, versionId: 'manifests' → that page's own not-found state |
…/3.2.1 |
version detail |
After:
| Path | Resolved to |
|---|---|
…/manifests/sha256:abc |
manifest detail |
…/versions/3.2.1 |
version detail |
…/manifests |
Page not found (catch-all route) |
…/versions |
Page not found (catch-all route) |
…/3.2.1 |
Page not found (catch-all route) |
So yes — Page not found, and via the real not-found route rather than a page rendering its own
empty state. All five cases are in the resolution table in index_spec.js, driven from the
path rather than by name, since a push by name cannot fail path matching.
No link changes were needed
The only version-detail link in the app is the version list's row (versions_table.vue), and
it pushes by route name, so it picks the new path up on its own. The breadcrumb trail derives
its hrefs from the resolved route. I swept every router-link, :to, and $router.push in
the feature to confirm nothing builds a version path by hand, and checked that nothing outside
the SPA deep-links into one.
A test pins that resolving by name lands on the prefixed path, since that is what actually decides the URL every caller gets.
Verification
- 1854 Jest tests pass across the artifact registry suite. Two suites
(
repository_detail_spec.js,version_list_spec.js) fail to run locally on missing generated GraphQL fixtures — pre-existing and unrelated; CI generates them. - ESLint and Prettier clean.
- Not driven in a browser. The change is entirely route matching, and the layer that decides the URL is pinned directly by the name-resolution and breadcrumb-href tests.
Breaking change, and why it is fine here
Deep links to the old shape stop resolving. Nothing outside the SPA builds one, and the surface is pre-GA behind the organization stage flags.
Stacked
Targets zcuddy-s06p2-step1-manifest-detail-page (!253776 (merged)), so review that one first. Retarget
to master once it merges.