Skip to content

Commit 834ef50

Browse files
authored
Merge branch 'main' into claude/issue-6044-datasource-pool-flake
2 parents 29ef049 + f7f54a9 commit 834ef50

90 files changed

Lines changed: 5771 additions & 580 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
"@objectstack/plugin-auth": patch
3+
---
4+
5+
fix(plugin-auth): break-glass 不变量补上第三条路径 —— 撤销「管理员身份」的写(`sys_member` 降级/删行、`admin_full_access` 授权删/改)同样被拒 (#5978)
6+
7+
cloud ADR-0024 D5.2 的不变量是「环境永远至少留一个能登录的管理员」。此前它由两个引擎钩子守着,
8+
**都装在 `sys_user`**:`banned = true`(#5892 / PR #5939)与删 `sys_user` 行(#5941 / PR #5993)。
9+
10+
但「谁是管理员」这件事根本不存在 `sys_user` 上 —— 它由另外两张表推导(`resolveAdminUserIds`
11+
正是从这两张表反向枚举的)。于是第三条写法完全绕开两个守卫:**用户行原封不动,把他的管理员身份拿掉**
12+
13+
- 把最后一个管理员的 `sys_member.role` 降到 admin 等级之下(better-auth 的 `updateMemberRole`
14+
一次 SCIM 组映射变更、导入、脚本),或直接删掉那条 `sys_member` 行;
15+
- 删掉那条 `admin_full_access``sys_user_permission_set` 授权,或把它改到不再生效
16+
—— 改指向别的权限集、加上 `organization_id` 组织作用域、把 ADR-0091 有效期窗口改过去。
17+
18+
三者事后状态与「删掉最后一个管理员」完全等价:所有人都还在,没有任何人能管理任何东西,
19+
产品内部无恢复路径。
20+
21+
**新增的拒写语义。** 守卫现在按同一形状扩到 `sys_member``sys_user_permission_set`
22+
`beforeUpdate` / `beforeDelete`(共六个钩子,同 `packageId`、同 priority 20)。判据就是 issue 的原话
23+
——**枚举、模拟、再枚举**:先枚举当前管理员,再把这次写落地后的行拿同一个枚举函数跑一遍,
24+
若第二次为空而第一次不为空则拒写。两次枚举是同一份实现,「谁是管理员」不可能对写前问题和写后问题
25+
给出两个答案。
26+
27+
- **全覆盖,不是只拦自降级**:真正会发生的是 IdP 组映射改别人的角色,不是管理员给自己降级。
28+
- **谓词/批量写照判**:一次 `where` 命中多行的 update/delete 会先解析出整个匹配行集再做写后模拟,
29+
而不是一律拒绝;只有匹配集本身解析不出来(读失败,或超过 `maxScan`)才响亮拒写。
30+
- **fail-closed**:枚举失败或形状不确定一律拒写并点名 ADR-0024 D5.2,与既有两半同向。
31+
- 模拟是**单向**的 —— 只会拿走身份,不会授予身份(把 role 从 `member` 升到 `admin`、把授权改指向
32+
`admin_full_access` 这类写,模拟看不见新增的管理员),因此每一处取整都倒向「拒写」而非「放行」。
33+
34+
**不拦的**:降级到**另一个** admin 等级(`owner``admin`,或逗号拼写 `member,admin`)—— 等级未失;
35+
已被 ban 的管理员的身份被撤(本来就不能登录,没有东西被拿走);非管理员的 membership/授权;
36+
以及不触及 `role` / `user_id`(membership)或权限集/作用域/有效期(授权)的 payload —— 这类写
37+
静态可证不改变枚举结果,一次读都不做。
38+
39+
有效期语义按 `resolveAdminUserIds` 现有的 `isGrantActive`(ADR-0091 D2)**原样消费**,本次不新造
40+
(#5893 才是那个问题的归属单)。等级判定全程只问 `isOrgAdminGrade` 这把唯一的尺(#5939 / #5942),
41+
守卫内没有任何手抄的 role 解析。

.changeset/console-f995a452d2ca.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
"@objectstack/console": minor
3+
---
4+
5+
Console (objectui) refreshed to `f995a452d2ca`. Frontend changes in this range:
6+
7+
Derived from the changesets objectui declared over the range — 64 releasing of 65 changesets added across 98 non-merge commits; omitted: 1 release-nothing changeset, 33 commits carrying no changeset (they ship no package code).
8+
9+
- **minor** — The record detail page now shows a read-gated approval panel (#3461). A record in approval used to expose NOTHING about the running approval to anyone but the current pending appr… (objectui `5af285210`)
10+
- **minor**`DrillDownConfig` now declares only keys a renderer reads, and `target: 'navigate'` is honoured on charts too (#3354). (objectui `9e9e9a92f`)
11+
- **minor** — Console chrome i18n gaps (objectstack#5407). (objectui `3889ffb4c`)
12+
- **minor** — The console language choice now survives a reload. `I18nProvider` writes every language change to `localStorage` (`objectui-locale`, exported as `LOCALE_STORAGE_KEY`) and boots th… (objectui `0554e889c`)
13+
- **minor**`<ObjectChart>` declares `drillDown` as a registry input, so the SDUI save gate treats the segment drill as a contract prop instead of an unknown one (framework#5022). (objectui `524a6357b`)
14+
- **minor** — The environment entitlement dialog now reads its context from `error.details.*` — the single declared location — and the flat dual-dialect tolerance is deleted (objectui#3329, the… (objectui `8ec406728`)
15+
- **minor**`@object-ui/core` now ships the server-action dispatcher factory — `createServerActionHandler({ fetch, baseUrl, resolveObject, ... })` — so any consumer of the runner (standalone… (objectui `5781fb15a`)
16+
- **minor**`AppSchemaRenderer` now derives area visibility from the items inside the area, closing the visible-but-empty regression the spec 17.0.0 area-key retirement left behind (objectui#… (objectui `608669eb5`)
17+
- **minor** — Track `@objectstack/spec` 17.0.0-rc.2 (objectui#3235, #3208, #3287, #3264). (objectui `d22ae31ce`)
18+
- **minor** — Field widgets no longer spread renderer-only props — or arbitrary keys from a field config — onto the DOM element they render (objectui#3291). (objectui `19b8c9be3`)
19+
- **minor**`RichTextField` honours `mobile_fullscreen`, so `mobile.fullscreenLongText` is finally true of rich text too (objectui#3301). (objectui `30ae33a77`)
20+
- **minor**`mobile.fullscreenLongText` finally reaches auto-generated long-text fields, and `mobile_fullscreen` gets one declared carrier (objectui#3245). (objectui `f44d8727f`)
21+
- **minor** — The flow designer writes node geometry as the spec's `FlowNode.position`, not its own `ui: { x, y }` (objectui#3172). (objectui `6e794a19e`)
22+
- **minor****BREAKING (v17)** — field widgets receive their metadata on ONE key, `field`. `schema` is removed from the widget contract (objectui#3233). (objectui `042e09d77`)
23+
- **patch**`UserFilters` no longer carries its own operator table when it lowers a `ViewTab.filter` preset into an ObjectQL AST node. The private `specOperatorToAst` was the second hand-kept… (objectui `d7f350a89`)
24+
- **patch** — Name `CommentThread`'s three emoji-only buttons, and follow the session language past the 7-day mark (objectui#3441) (objectui `65516ba4a`)
25+
- **patch**`toFilterNode` now lowers a spec `ViewFilterRule[]` into ObjectQL AST nodes instead of returning the array verbatim, so a saved view's stored filter reaches `$filter` as something… (objectui `2a9513d81`)
26+
- **patch** — Localize the create / edit / view form title `ObjectView` builds itself (objectui#3462) (objectui `28b2e6571`)
27+
- **patch** — Localize the record-detail headings that `ObjectKanban`, `ObjectTree` and `ObjectView` build themselves (objectui#3459) (objectui `aa36e6073`)
28+
- **patch** — Localize the record-detail overlay heading that `ListView` and `ObjectGrid` build themselves (objectui#3426) (objectui `61958416e`)
29+
- **patch** — The close button that the `Sheet` and `Dialog` primitives auto-render now announces itself in the session locale instead of always in English. Both buttons are icon-only (a lucide… (objectui `71be40696`)
30+
- **patch** — Localize `PresenceAvatars` — the avatar stack's accessible name and tooltips follow the session language (objectui#3440) (objectui `ca0fa8fab`)
31+
- **patch**`PageRenderer` no longer renders its own `<h1>` when the page authors a titled `page:header`, so a page has exactly one level-1 heading. Every non-record page used to render the p… (objectui `06632e9d1`)
32+
- **patch** — Localize `@object-ui/collaboration``CommentThread` no longer hardcodes English (objectstack#5506) (objectui `94c5b7c4e`)
33+
- **patch**`TextAreaField`'s fullscreen edit dialog now gives screen reader users the character count it has always shown sighted ones. The dialog's footer counter was a bare `{n}/{max}` spa… (objectui `f789c3b3a`)
34+
- **patch**`ObjectDataPage`'s "Save as view" now folds the active URL drill conditions into `@objectstack/spec` `ViewFilterRule`s before persisting them, instead of writing the runtime filte… (objectui `875c5fafb`)
35+
- **patch** — Localize `RecordDetailDrawer`'s drag-resize handle (objectstack#5733) (objectui `5a24ad9cb`)
36+
- **patch** — The record picker's filter panel now sends the AUTHORED value of a picked `select` filter option instead of the control's stringified form. Radix `Select` speaks strings — options… (objectui `34d9169f6`)
37+
- **patch** — Localize the record-overlay and tab-badge chrome that #5430's sweep left behind (objectstack#5506) (objectui `5dd012776`)
38+
- **patch** — The lookup "Browse all records" Record Picker's filter panel now offers the options a `select` field declares in its schema (objectui#3336). `LookupField` turns each typed picker… (objectui `5881a2cc4`)
39+
- **patch**`TextAreaField`'s character counter no longer re-announces itself on every keystroke. Measured on `main` in a zh session with `maxLength: 500`, typing a 52-character sentence one… (objectui `789fe3ee2`)
40+
- **patch** — Fix matrix report cells showing another bucket's numbers when dimension values run together. (objectui `509104a1b`)
41+
- **patch** — Fix dataset pivot cells showing another row's numbers when a dimension value contains a space. (objectui `ce7cbe5af`)
42+
- **patch** — Give `InlineCreateRelated`'s card-header close button an accessible name (objectui#3411 — the neighbouring defect found while implementing #3381/PR #3410, in the same file and lef… (objectui `58a00f0b4`)
43+
- **patch** — Give `InlineCreateRelated`'s "Link Existing" search box a real accessible name (objectui#3381 — the neighbouring defect found while implementing #3341/PR #3380, and left out of th… (objectui `b17ce4c25`)
44+
- **patch**`TextAreaField`'s character counter now announces itself in the session locale. The counter block — rendered only when the field declares `maxLength` — carried the accessible name… (objectui `2409e1d04`)
45+
- **patch** — fix(fields): translate the registered path's fullscreen long-text dialog (objectui#3404) (objectui `6fe485bf2`)
46+
- **patch**`TextAreaField` / `RichTextField` now honour `disabled` on their fullscreen editing path. `disabled` used to reach the inline control only: `showFullscreenButton` never consulted… (objectui `7d08c3feb`)
47+
- **patch** — The form renderer's built-in `textarea` branch now honours `readonly` / `disabled` on its fullscreen exit, which previously bypassed both (objectui#3400). `renderFieldComponent` d… (objectui `fd54c3e7a`)
48+
- **patch** — 表单内置 `textarea` 的全屏编辑对话框现在能拿到字段自己的 label:对话框标题显示字段名而不是恒定的通用词「编辑文本」,同一张表单上多个长文本字段的展开按钮也终于有了互不相同的无障碍名(objectui#3393)。 (objectui `85c4c9ce0`)
49+
- **patch** — The form renderer's built-in `textarea` branch reads the fullscreen long-text flag on one spelling (objectui#3303). (objectui `9cbcbf478`)
50+
- **patch** — The form renderer's last user-visible English literals now go through i18n (#3272). The fullscreen long-text editor (`mobile_fullscreen`) was an entire untranslated dialog — title… (objectui `4eeb932aa`)
51+
- **patch** — Localize the last untranslated console-chrome accessible names (objectstack#5430) (objectui `b71fc92f3`)
52+
- **patch**`ObjectGantt`'s quick-filter bar is now localized instead of pinned to Chinese. The four `QuickFilterBar` labels (`all`, `clear`, `empty`, `resultSummary`) were hardcoded as Chine… (objectui `5c856ecb5`)
53+
- **patch** — Associate the label with its control at the two form surfaces where the two were never programmatically connected (objectui#3341 — found while implementing #3299/PR #3340, and del… (objectui `53811d179`)
54+
- **patch**`ActionEngine.getActionsForLocation` now evaluates a `{ dialect: 'cel', source }` action `visible` predicate on the canonical `@objectstack/formula` engine instead of the legacy J… (objectui `18cd43289`)
55+
- **patch** — Settings pages read the declared `{ success, data }` response envelope, so the whole Setup → Configuration section works again against a framework#3843 server (objectui#3366). (objectui `7d0c6de39`)
56+
- **patch** — The flow designer no longer seeds new `wait` nodes with the retired `waitEventConfig.onTimeout` (objectui#3316). (objectui `35da149c5`)
57+
- **patch** — Gallery covers now resolve the `coverField` value through its **file value shape** instead of assuming the field value *is* a URL string, so an ADR-0104-conforming `image` value r… (objectui `978705c8f`)
58+
- **patch** — The list toolbar's "Filter" now saves. Saving a filter from the runtime toolbar PUT the FilterBuilder's whole group object (`{ id, logic, conditions }`) into the view's `filter`,… (objectui `68b6a2855`)
59+
- **patch** — The lookup "Browse all records" Record Picker now formats its columns with the same field metadata the list view uses (objectui#3333). Previously the dialog handed cell renderers… (objectui `9bc3709b9`)
60+
- **patch** — Behavior change — **an authored display `type` can NARROW inline editability, but never WIDEN it** (objectui#3355). (objectui `bbbde1207`)
61+
- **patch**`record:highlights` now honours a `readonly: true` on an authored field entry, so a header chip for a platform-owned column no longer offers inline edit. `HeaderHighlight`'s edita… (objectui `23018cc03`)
62+
- **patch** — Deliver the required state to the control in the five renderers outside the object form that still painted it as an asterisk only (objectui#3299 — the same defect #3290/#3298 fixe… (objectui `532cf8b9e`)
63+
- **patch** — The Combobox trigger now declares `type="button"` explicitly, so it can never submit an enclosing `<form>` (objectui#3344). The current Radix `PopoverTrigger` happens to supply `t… (objectui `34595eb45`)
64+
- **patch**`TagsField` no longer ships a hardcoded Chinese input placeholder (objectui#3342, AGENTS.md Commandment #-1). The placeholder now resolves through the pinned chain: the author-dec… (objectui `c7ed4c366`)
65+
- **patch** — AddressField / GeolocationField sub-inputs now derive their DOM ids from a `useId()` prefix + sub-field name (the RadioField / CheckboxesField `groupId` paradigm) instead of hardc… (objectui `b7165ce47`)
66+
- **patch** — 20 more registered field widgets now announce a failed validation to assistive tech: `multiselect`, `radio`, `checkboxes`, `tags`, `lookup`, `master_detail`, `user`, `owner`, `fil… (objectui `8d8094a7c`)
67+
- **patch**`field:permission-facet-link` now registers through `withFieldCarrier` — the repo's only raw `field:` registration bypassed the single-metadata-carrier seam (objectui#3233), so un… (objectui `c7fba276e`)
68+
- **patch** — The console server-action wrapper's `opensInNewTab` choreography no longer ships hard-coded bilingual Chinese/English copy (objectui#3321, AGENTS.md Commandment #-1): the pre-open… (objectui `a41568462`)
69+
- **patch**`AppSidebar` and `UnifiedSidebar` area switchers now adopt the derived area visibility introduced for `AppSchemaRenderer` in objectui#3311, closing the same visible-but-empty gap… (objectui `8d9984c2e`)
70+
- **patch** — RecordDetailView's `type:'modal'` dispatch no longer falls back to the server-side action handler when the target resolves to neither a page nor an object. That fallthrough could… (objectui `94755bb88`)
71+
- **patch**`field:select` now announces its validation state to assistive tech: the widget's DOM pass-through lands on the Radix `SelectTrigger` — the focusable `<button role="combobox">` a… (objectui `49f7449f9`)
72+
- **patch** — The required state now reaches the input control as `aria-required`, instead of existing only as part of the control's accessible name (objectui#3290). (objectui `680080ada`)
73+
74+
objectui range: `f5bc4c78be76...f995a452d2ca`
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
fix(spec): the reference generator prints a `retiredKey()` tombstone as `never`, not `any` (#5606)
6+
7+
`retiredKey()` is `z.never()`, which `z.toJSONSchema` emits as `{ "not": {} }`
8+
a node with no `type`, no `$ref` and no `enum`. `formatType()` had no branch for
9+
it, so every one of the ~28 tombstones in the spec fell through to the
10+
`prop.type || 'any'` tail and the generated reference pages typed a **removed**
11+
key as **`any`**.
12+
13+
That is the worst available rendering for a retirement. These pages are the
14+
primary input for an upgrading author — very often an AI one (ADR-0033) — and
15+
`heading?: any` does not read "this key was deleted", it reads "this slot exists
16+
and nothing validates it": strictly *more* inviting than the `heading?: string`
17+
it replaced. The author writes it, the parse rejects it with the `[REMOVED]`
18+
prescription, and the prescription arrives only after a wrong metadata file
19+
already exists.
20+
21+
Two changes, both in `scripts/lib/format-type.ts`:
22+
23+
- **`{ not: {} }` now renders as `never`.** Accurate TypeScript — the key's
24+
`z.input` type *is* `never` — and, unlike `any`, self-evident with no prose
25+
to lean on.
26+
- **Tombstones are dropped from an inline shape summary before
27+
`INLINE_KEY_LIMIT` counts.** A summary cell prints `k?: type` for the first
28+
four declared keys and has no description column, so a nested tombstone had
29+
nowhere to put its prescription at all: `ui/theme.mdx` advertised
30+
`{ base?: string; heading?: any; mono?: any }` with both prescriptions
31+
appearing NOWHERE on the page. Retired keys are no longer authorable surface,
32+
so they no longer spend one of the four slots — nor push a key the author
33+
must write behind the ``. The known workaround of moving a tombstone to the
34+
bottom of the shape cannot cover this: #5248 retired `IndexSchema` down to
35+
three live keys, and with a limit of four the first tombstone is then
36+
*mathematically* guaranteed into the summary.
37+
38+
Per-key table rows are unaffected and keep carrying the full `[REMOVED]`
39+
prescription in their description column; their type cell simply now says
40+
`never` instead of `any`.

0 commit comments

Comments
 (0)