Skip to content

feat(console): filter and page the marketplace catalog through the list door - #31288

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-044740
Nov 7, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-044740

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Fixes #31267

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:控制台市场页不再一次拉 100 条再在浏览器里筛。搜索词、三个维度的选择和分页窗口随列表请求一起发给 GET /api/v1/marketplace/packages:businessFunctions / industries / solutionTypes 各一个逗号拼接的参数,没选的维度不发,q 在停止输入 300 毫秒后发,limit / offset 由卡片下方的翻页器驱动(每页 24 条)。listMarketplacePackages 还接受 maturity,页面本身不选它。
  • 为什么改:feat(cloud-connection)!: the marketplace catalog list filters by taxonomy facets on the server, and retires ?category= #31234 已让服务端按这四个维度筛选并分页;前端筛只看得到前 100 条,目录超过 100 条后其余模板永远显示不出来。
  • 风险与回滚:只改私有包 @object-ui/app-shell 的市场页(随 @object-ui/console 的构建发出),patch。可见变化:维度按钮现在列出规格声明的全部取值、不再显示数量(一页筛选结果数不出全目录,列表接口也不返回计数);搜索改由服务端匹配(名称与应用 ID,与占位文字一致),不再匹配描述和本地化名称。默认配置下请求原样转发 cloud,cloud 按同名参数筛选要等 cloud#3661。回滚即 revert 本 PR。
  • 先看:ui/packages/app-shell/src/console/marketplace/MarketplacePage.tsx(catalogRequest、读取 effect、翻页器)、marketplaceApi.ts 的 listMarketplacePackages(参数拼法)、marketplaceTaxonomy.ts 的 facetListParams 与 widenServedFacets。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/console/marketplace/__tests__/MarketplacePage.facets-31011.test.tsx packages/app-shell/src/console/marketplace/__tests__/marketplaceApi.listQuery-31267.test.ts,预期 17 个用例全绿(点按钮、打字、翻页,断言列表请求收到的查询串)。
  • 需要你做的:无,席位落地。

What changes

  • listMarketplacePackages (marketplaceApi.ts) takes MarketplaceListParams: q, limit, offset, and the four filters of MarketplaceSearchRequestSchema spelled as the published spec names them, businessFunctions, industries, solutionTypes (plural) and maturity. Each list filter goes out as ONE comma-joined parameter; a list that selects nothing is left out, never sent empty; maturity is one value; category is never sent. These are exactly the three shapes the list door refuses with 400 VALIDATION_ERROR (empty facet, repeated facet key, retired category), so the console sends none of them.
  • MarketplacePage.tsx no longer reads { limit: 100 } and filters in the browser. Its read is catalogRequest: the debounced search text as q, the selection through facetListParams, limit: CATALOG_PAGE_SIZE (24) and the page's offset. One effect reads whenever that request's spelling changes; each new read retires the pending one, so only the newest settles the page (the existing loadSeq mechanism). The first read, Refresh, Retry and the read after an install also read the installs and the org's packages; a page turn or a new selection reads the catalog alone. The search box reads once typing pauses for SEARCH_DEBOUNCE_MS (300 ms). A new search or selection reads from the first page. The cards drawn are the door's answer, unfiltered. Empty answers say "No apps match your filters." under a search or a selection and "No apps have been approved…" without one.
  • Pager. Under the cards when the door's total spans more than one page: Previous, "Page N of M", Next, disabled at either end and while a read is pending. It reuses the existing keys common.previous, common.next and table.pageInfo; no i18n key is added, removed or reworded.
  • Facets (MarketplaceFacetFilters.tsx, marketplaceTaxonomy.ts). A drawn facet offers every value the spec declares, in the spec's order, read from the schemas' .options as before. The per-value count is gone, and so are matchesFacets and facetOptions: one page of the filtered catalog cannot count the catalog, and the list door answers no counts. Which facets are drawn is ServedFacets, widened by widenServedFacets from each answer: a facet appears once some answered listing carries it (the solution type once one declares a type) and stays drawn while a selection narrows the page, so a catalog serving no taxonomy still draws no filter (the console marketplace: facet filters (business function, industry, solution type) and a Beta badge replace the single category chip row — reads the spec taxonomy (objectstack#30904) #31011 behaviour).
  • Row defaults (card item 4). The console no longer applies a default to filter: the server does (solutionTypes reads a row without the column as app, maturity as stable). The one console reading that remains, effectiveSolutionType on the package page, is DEFAULT_SOLUTION_TYPE from @objectstack/spec/marketplace, the same constant the door imports; the Beta badge reads maturity === 'beta', so an absent maturity draws as stable. No second copy of either default is added.
  • Changeset .changeset/31267-marketplace-server-facets.md, @object-ui/app-shell patch, with both declaration lines (precedent: .changeset/30330-marketplace-readme-fits-page.md, an app-shell-only change shipped through @object-ui/console's dist).

Tests

  • MarketplacePage.facets-31011.test.tsx, the page's existing harness, rewritten for the server read (15 cases). The catalog stub records each list read's query string and answers from a per-test function. Every click, keystroke and page turn goes through the rendered page; the assertions read the query the data layer sent: keys, comma-joined values in the spec order, an omitted empty facet, no category, no empty value, at most one occurrence of each key, limit / offset. One case answers a hr read with listings that carry no hr and asserts both are drawn: the console filters nothing. The pager case walks three pages by offset and checks that a selection on page 2 reads from offset 0. The console marketplace: facet filters (business function, industry, solution type) and a Beta badge replace the single category chip row — reads the spec taxonomy (objectstack#30904) #31011 cases that still hold (facets hide with no taxonomy, per-facet hiding, out-of-vocabulary values drawn nowhere, non-array columns, primary-function and Beta badges) stay, read through the server answer.
  • marketplaceApi.listQuery-31267.test.ts (new, 2 cases): the exact URL listMarketplacePackages builds for all four filters, maturity included, and that empty lists and no filters send nothing extra.
  • MarketplacePage.searchPlaceholder-10900.test.tsx: its third case pinned the browser-side search; it now asserts the box's text reaches the read as trimmed q from offset 0, and that the door's empty answer draws no card. The placeholder's promise (name or app ID) is the door's q matching, catalogRowMatches in @objectstack/cloud-connection's marketplace proxy.

Acceptance notes

  • Counts are gone from the facet buttons. console marketplace: facet filters (business function, industry, solution type) and a Beta badge replace the single category chip row — reads the spec taxonomy (objectstack#30904) #31011 drew, beside each value, the loaded listings that held it. With the server filtering and paging, no count the console could compute would be true, and GET /api/v1/marketplace/packages answers { items, total, limit, offset } with no aggregation. The spec does declare an optional facets member on MarketplaceSearchResponseSchema (one count list per facet), but no in-repo producer serves it, so the console does not read it. If counts are wanted back, the list door serving those counts is the route; noted, not filed.
  • Search moved to the server, and narrowed with it. The browser search matched the localized display name, the manifest id and the localized description. The door's q (proxy path) matches display_name and manifest_id, which is what the placeholder says ("by name or app ID"). Matching the description or a localized name is gone; noted, not filed.
  • Default configuration forwards to cloud. With OS_MARKETPLACE_PUBLIC_BASE_URL unset, the proxy validates the query and forwards it verbatim to the cloud list route, which adopts the same names on cloud#3661 (carrier: cloud). Until that lands, a deployment on the default path may answer a facet read unfiltered; the console draws what it is answered. The R2 fast path filters in-repo.
  • Maturity is accepted by listMarketplacePackages (card item 1) and pinned there; the page selects no maturity, as before (maturity is a badge, not a facet, in the spec's own words).
  • Page size 24: a whole number of card-grid rows at two, three, four and six columns; the door caps a page at 100.

Verification

All on head aad134cc unless named. Exit codes captured before any pipe.

Rendered and unit tests: 58/58 files, 515/515 tests
  • pnpm --dir ui exec vitest run packages/app-shell/src/console/marketplace/ under the verification lock: Test Files 58 passed (58), Tests 515 passed (515), VERDICT command-exit 0.
  • pnpm --filter @object-ui/app-shell run type-check (both tsconfig.json and tsconfig.test.json): exit 0.
  • eslint on the seven changed files: 0 errors (warnings only; the read effect's exhaustive-deps warning was already on main's effect, and the debounce effect adds one of the same kind).
  • pnpm --dir ui check:new-line-citations: 0 new citations, exit 0.
Ablations: six mutations, each red, each restored to the HEAD blob

node scripts/ablation-replace.mjs on f7c43530, running the three changed test files; ui vitest reads app-shell from src/, so no build sits between mutation and run. Every leg printed ok restored: blob == HEAD with git diff HEAD empty.

  • A, an empty facet sent (if (values) for if (values && values.length > 0) in listMarketplacePackages): 1 red, leaves out a filter that selects nothing, and never sends category.
  • B, the selection kept off the read (...facetParams, deleted from catalogRequest): 7 red, every case that clicks a facet. The first attempt at B used a replacement that was a substring of the anchor; the script refused it (rise 0, nothing ran), so it was redone with --delete.
  • C, repeated keys instead of one comma-joined value (usp.append per value): 3 red.
  • D, the old window (limit: 100, no offset): 6 red, including the pager and the search placeholder case.
  • E, insertion order instead of the spec order in facetListParams: 1 red, businessFunctions=sales,hr expected.
  • F, a facet that does not stay drawn (served.businessFunctions || removed in widenServedFacets): 1 red, a drawn facet stays drawn when a selection narrows the page.
Derived gates: 37 derived, 37 run, all exit 0

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, reconciled with --ran: 37 derived famil(ies) accounted for — 37 run, 0 NOT-MEASURED.

  • The 27 families the ui paths derive ran on f7c43530 (the code commit): check-closing-keyword-parity (and --self-test), check-comment-mask-corpus, check-sdui-manifest (and --self-test), pnpm --dir ui exec vitest run scripts/, spec check:duration-unit-keys, check:error-code-provenance, check:cross-package-test-inputs, check:dispatcher-error-vocabulary, check:driver-memory-census, check:engine-double-contract, check:gitlink-declared, check:nul-bytes, check:published-files, check:query-options-erasure, check:refd-timer-probe, check:registry-log-declared, check:rest-log-spy-declared, check:skill-identifier-liveness, check:test-source-alias, check:tier-file-adoption, check:type-check-coverage, check:type-check-debt, check:undeclared-dep-imports, check:watch-hint-literal, check:where-matcher: all exit 0.
  • The 10 families the changeset path adds ran on aad134cc: check-adr-0087-registration (--base origin/main and --self-test), check-changeset-no-major, check-empty-changeset (--base origin/main and --self-test), ci/snapshot-next --self-test, pm/release-rehearsal-clone --self-test, release-pending-publish --self-test, check:changeset-gate-self-tests, check:closed-ceremony-text: all exit 0.
  • f7c43530..aad134cc adds the changeset file only, and the derivation maps that path to exactly those 10 families, so the 27 were not re-run on the changeset commit.
  • Full dogfood: not owed (no ! in the title, no (narrowing) arm).

Generated by Claude Code

The marketplace page no longer reads the first 100 listings and filters
them in the browser. The search text, the facet selection and the page
window travel with the list read, spelled as GET
/api/v1/marketplace/packages reads them: businessFunctions, industries
and solutionTypes as one comma-joined parameter each, maturity as one
value, a facet with nothing selected left out, limit and offset. A
pager walks the server's total.

A drawn facet now offers every spec value without a count, since one
page of the filtered catalog cannot count the catalog, and stays drawn
once some answered listing carried it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkQ5npnoTzeRFxu1Mj26Q
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 7, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · os-project-manager · session_01FEkQ5nLA7wiVY5YWjT9Svr

席位核对 · ACCEPT · 2026-11-07T22:44Z · head aad134cc0c5367ab43ce58826542ab22fd5cb5e9

Seat domain:ui#1 reviewed the diff of its own dispatch (claim 6713333412 on #31267, card thread session_01FEkQ5npnoTzeRFxu1Mj26Q, report 6713876182). Read against GitHub, not the report.

  1. Shape: 8 files, +551 / -259, all in the private @object-ui/app-shell marketplace folder plus its changeset. listMarketplacePackages takes MarketplaceListParams and sends businessFunctions, industries, solutionTypes (each one comma-joined parameter) and maturity, leaving out a facet that selects nothing, and no category. MarketplacePage.tsx reads one page of 24 with limit / offset, sends the debounced search as q, restarts at the first page on a new search or selection, and reads the side loads (installs, org packages) only on the first read, Refresh, Retry and after an install; the browser-side search and facet filter over the first 100 rows are gone. Body says Fixes #31267; no other closing keyword.
  2. Premise: PR feat(cloud-connection)!: the marketplace catalog list filters by taxonomy facets on the server, and retires ?category= #31234 (merged) serves the four facets and paging on GET /api/v1/marketplace/packages; on main the console read one { limit: 100 } window at MarketplacePage.tsx:415 and filtered it in the browser.
  3. Gates / check-runs: 55 runs on this head, 44 success, 11 skipped, 0 red. Dev: 37/37 derived gates exit 0 (the delta between the two measured heads is the changeset file alone), marketplace suites 58 files / 515 tests, app-shell type-check 0, six ablations red then restored. mergeable_state clean.
  4. Accepted deviations / notes: facet buttons list every spec value without counts (the door serves no counts, and the spec's optional facets member has no producer); search is now the server's q, which matches name and app ID but no longer the description or a localized name. Both are named in the PR body; neither has a pull behind it yet, so no follow-up card is filed. The default deployment forwards facets to cloud, which adopts the names on cloud#3661.
  5. Out of scope / Skills-docs: Clause-②: no and Metadata-protocol: none hold as claimed (only the private @object-ui/app-shell, no i18n key). No packages/spec/src path, so Skills-docs: is not owed. Nothing filed.
  6. Landing: check-governed-merges --pr 31288: not governed, 810 changed lines, so no human approval owed. Clause-②: no, so no contract review is owed. Landing on the maintainer's standing words 「按照 pm-dispatch 规则复核过的都授权」 (2026-10-25T11:35Z) and 「你应该使用 pm-dispatch 技能审核和落地pr」 (2026-11-07T16:59Z). Relay pr_ready + automerge_enable once PR test(scenarios): give the external-driver crm run a measured 30s budget and a lighter stand-in #31284 (the fix for the queue-flake anchor Queue-flake anchor: test/runner/external-driver.test.ts #31285 that has ejected three PRs) is on main, so this PR does not ride into the same ejection. On merge console marketplace: filter and page the catalog through the server's four facets #31267 closes by Fixes; the seat removes pm:dispatched.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 7, 2026 23:12
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 7, 2026 23:12
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 7, 2026
Merged via the queue into main with commit 7c523aa Nov 7, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-044740 branch November 7, 2026 23:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

console marketplace: filter and page the catalog through the server's four facets

1 participant