Skip to content

Commit b8a1348

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-30073-personal-view-conditions
2 parents 5fc9c61 + 6f095b9 commit b8a1348

175 files changed

Lines changed: 9600 additions & 1062 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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@objectstack/objectql': patch
3+
---
4+
5+
fix(objectql): the lifecycle sweep no longer runs 60 s after a boot that created its datastore from empty; its first sweep runs one interval later
6+
7+
Clause-②: yes (widening) — the exported `LifecycleEngineLike` gains the optional member `wasDatastoreCreatedFromEmpty?(): boolean` (an engine without it keeps the old schedule); no accept set changes.
8+
Metadata-protocol: none
9+
10+
- `LifecycleService.start()` asks the engine, at the initial delay, whether this process created the datastore from empty (`ObjectQL.wasDatastoreCreatedFromEmpty()`, answered from every driver's schema-sync stats without a statement). When it did, the scheduled first sweep is skipped and the sweep runs on the interval, one interval later. Such a store holds no row older than the boot, and no declared window is shorter than an hour, so the skipped sweep had nothing to reap. It still read every declared object, the `lifecycle` settings and every lookup the reference audit walks: 130 statements in the first minute of a hosted tenant kernel's first boot.
11+
- A store that existed before the boot, or an engine that cannot say, sweeps at the initial delay as before. A direct `sweep()` call always sweeps. `OS_LIFECYCLE_DISABLED`, `initialDelayMs` and `sweepIntervalMs` keep their meaning.
12+
- `LifecycleEngineLike` gains the optional member `wasDatastoreCreatedFromEmpty?(): boolean`. An engine double that omits it keeps the old schedule.
13+
- A re-boot on an existing store still sweeps at the initial delay. Persisting a last-swept watermark for that case is separate work.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/components': patch
4+
'@object-ui/plugin-detail': patch
5+
---
6+
7+
fix(ui): a greyed action's `disabledReason` tooltip wraps inside a phone-width viewport instead of running off-screen (#29954)
8+
9+
Clause-②: no
10+
Metadata-protocol: none
11+
12+
The record action bar, the `record:quick_actions` button and the schema-driven `action:*` controls (button, icon, group, menu, through the shared disabled-reason trigger) rendered the authored `disabledReason` in a tooltip with no width bound, so a long reason sat on one line and was cut at the right edge of a 390px viewport. All three tooltips now carry the same `max-w-xs` bound the home page's disabled-reason tooltip uses, so the reason wraps.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@objectstack/plugin-auth': patch
3+
'@objectstack/spec': patch
4+
'@object-ui/console': patch
5+
---
6+
7+
feat(plugin-auth): a page navigation to the mobile sign-in hand-off that has to sign in first goes to the sign-in page, so the app opens only the hand-off
8+
9+
Clause-②: no
10+
Metadata-protocol: none
11+
12+
`GET /api/v1/auth/one-time-token/handoff` answered a missing or old session with `401` as raw JSON, so the mobile app had to open the Console's `/login` route itself and know how that page treats `redirect`. An installed app stays in use for months, so a Console upgrade could strand it. Now the app opens only the hand-off:
13+
14+
- A page navigation with no session, or with one older than five minutes, answers `302` to this deployment's Console sign-in page, with `redirect` naming the same request. `Location` is root-relative, so the browser stays on the origin the request arrived on, and `uiBasePath` is honoured.
15+
- Any other request still answers `401`, so the app's anonymous probe keeps reading "this server registered me". A navigation reporting `Sec-Fetch-Site: same-origin` answers `401` too: it is the sign-in page forwarding a session the route refuses. A deployment that does not serve the Console keeps the `401` too, and says so once at `warn`.
16+
- A token is still minted only for a session under five minutes old, and every `400` (a cross-site navigation, an unknown parameter, an unregistered scheme, a bad challenge) is still decided first, with no redirect.
17+
- A device whose clock is behind does not bounce between the route and the sign-in page. The redirect carries `handoff_refused_at`, the server's time of the refusal, and the page holds its form for any session not created after it, whatever the device's clock says. A session created after it forwards, so a provider's sign-in that returns to the same URL still ends at the app. The links between the sign-in and sign-up pages carry the mark beside a hand-off `redirect`, and every other link is unchanged.
18+
19+
An app can change its entry only once its server runs this release: an older server answers the new entry with JSON.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/i18n': patch
4+
---
5+
6+
fix(app-shell): New automation no longer saves a typed prefix as the trigger object (#30038)
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
In Studio's New automation dialog, typing part of an object name and pressing Enter committed the typed text itself, so the flow was created bound to an object that does not exist. The trigger-object picker there now takes objects only: Enter or leaving the field commits the object the text names exactly, else the only match left in the list, else no object, and the picker says no object is chosen. A record preset then keeps the dialog open until an object is picked.
12+
13+
Every other object picker keeps its free text, because a flow reference may hold an expression.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/i18n': patch
4+
---
5+
6+
fix(app-shell): Studio on a zh-CN console reads in Chinese in the view, page and report panels, the publish sheet, the flow Problems tab and the save-notice / notify nodes, and calls a dashboard 仪表盘 everywhere
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
The view, page and report property panels render the spec's authoring forms from the console's own copy of the spec, and only the dashboard form had a zh-CN overlay, so section headings such as Basics, Columns & filters and Navigation & sharing, their descriptions and schema-titled rows such as Searchable Fields read in English. The overlay now carries every `METADATA_FORM_REGISTRY` type, worded as the platform's own zh-CN metadata-forms bundle words them: view, page and report in full, the other types by field label, which is what the Pending changes sheet names a changed property by (a flow's `~ Status` now reads `~ 状态`). The sheet's summary line counts datasets as 数据集 instead of the raw type name.
12+
13+
A flow whose trigger is not armed on this deployment showed the engine's developer sentence under 未在此运行, above 没有问题 —— 该流程结构有效. The note now says, from the engine's `unboundCause` code, what this deployment does not do and who can change it, keeps the engine's sentence as the hover detail, and the line below it says the structure checks found nothing to fix instead of "no problems". A cause the console has no row for still shows the engine's sentence.
14+
15+
The palette names the save-notice node 保存提示, and the notify node's Template and Template data fields and the save-notice Message field read in Chinese. English consoles read the same English as before. zh-CN Console and Studio text uses 仪表盘 for dashboard throughout.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/i18n': patch
4+
---
5+
6+
feat(app-shell): Studio Interfaces picks an object's list view from the header title, object / view ▾, the way the running list does, and the open view stays in the address
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
- **The view switcher is in the header's title.** An `object` entry's header reads object / view ▾. The switcher is the running list's own dropdown (`ObjectViewTitleSwitcher`): the object's list views by label, a view only drafted in this package included, a search box, and New view, which opens the Create View dialog. Studio does not offer the reader's own features there: no ☆, no 📌, no recent views, no Save as my view. It shows when the object has one list view too, so New view is always one click away. A read-only package switches views and offers no New view.
12+
- **The Properties panel no longer holds a view picker, Add view or "Back to …".** It keeps the open view's settings, and its title is the view's name as the switcher gives it (the label the object's served `listViews` carry). A view with no label is named "Default view" or by its key; the header names the object before it.
13+
- **The open view is in the address.** Switching writes `?view=<object>.<key>` beside `?surface=` and `?nav=`; the entry's own view writes none. A reload or a shared link opens that view again, a view only drafted in this package included. A link naming a view of another object is not taken. A pending edit is still sent before a switch, and an edit that cannot be sent still asks first.
14+
- **`ObjectViewTitleSwitcher`: the pin is optional.** `onPinnedViewChange`, `pinnedViewId` and `recentViewIds` may be left out; without `onPinnedViewChange` no row draws 📌 and no Pinned section is listed. The running list passes all three, so it is unchanged.
15+
- **`@object-ui/i18n` (studio namespace):** adds `engine.studio.inspector.listView.defaultView`; removes `defaultTitle`, `namedTitle`, `addView`, `backToEntry`, `switchView` and `switchViewDraft`, which only the removed panel controls read.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
---
4+
5+
fix(app-shell): the console picks a signed-in user's UI language in the server's `resolveLocale` order
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** `pickRenderableLocale` matched `sys_user.locale` against the offered locales exactly, then by base language only. A row stored mis-cased (`zh-cn`, written before the server door folded case and served as stored), or a tag the deployment offers only as a variant (`zh` or `zh-Hans-CN` against `zh-CN`), matched nothing, so the console rendered English.
11+
- **Now.** It uses the order the server's i18n routes resolve with: exact, then case-insensitive, then base language, then the first offered variant of that base language. `zh-cn`, `zh` and `zh-Hans-CN` render `zh-CN`; `en-GB` renders `en`. A tag no offered locale shares a language with still matches nothing, and the device language keeps deciding, as before.
12+
- No schema, key or server behaviour changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/data-objectstack': patch
4+
'@object-ui/i18n': patch
5+
---
6+
7+
feat(studio): customize a read-only package's dataset in the All metadata area, see what uses it before publishing, and read a refused publish at the dataset
8+
9+
Clause-②: yes (widening)
10+
Metadata-protocol: none
11+
12+
- **Customize a dataset.** On a read-only base package, Studio's All metadata area opens a dataset in its form editor with the dataset designer and live preview, and offers Customize. The first saved edit copies the whole dataset into the base's paired `<base>.custom` package, declaring `overrides: { package, basedOn }`; the header's Publish applies it, and "Restore package original" on the override mark deletes the copy. On a writable package a dataset still opens the generic item page.
13+
- **What uses it.** While a dataset is customized, the editor lists the dashboards, reports and other items that name it, read from the References read, so the person sees what a published change reaches. A failed read says it failed and offers to read again; it never reads as "nothing uses it".
14+
- **A refused publish at the item.** A publish the server refuses for a dataset is placed on its measure or dimension and the designer's input (`Not published. Check Aggregate of the measure "Total Amount"`), in the Console's language, and shown at the dataset's editor as well as on its Pending changes entry. The server's own text stays under Details.
15+
- **References read fixed.** `MetadataClient.references()` now reads the route's declared answer (`{ references: [{ type, name, label, path, kind }] }`) and maps each element to `MetadataReference` (`fromType`, `fromName`, new optional `fromLabel` and `kind`). It used to read a key the route never serves, so Setup's References tab and Studio's item tools showed "Nothing in the metadata graph points at this item. Safe to delete." for every item. An answer the declaration refuses is now a failed read.
16+
- **Published surface.** `MetadataReference`, exported from `@object-ui/data-objectstack`, gains two optional fields, `fromLabel` (the referring item's label, when the server serves one) and `kind` (the served reference kind); existing fields are unchanged. `MetadataClient.references()` now accepts less: a `200` answer that `FindReferencesToMetaResponseSchema` refuses throws instead of resolving `[]`, so a caller must handle the rejection as it already does for a non-2xx answer; in this repository every caller reads it through `useReferencesScan`, which catches it and shows the failed-check state.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@object-ui/plugin-detail': patch
3+
'@object-ui/plugin-list': patch
4+
'@object-ui/plugin-grid': patch
5+
'@object-ui/i18n': patch
6+
---
7+
8+
fix(plugin-detail): a record page's related table offers Export only to a reader the list page offers it to, and an export refusal reads in the Console's language
9+
10+
Clause-②: no
11+
Metadata-protocol: none
12+
13+
- **`@object-ui/plugin-detail`: the related table asks the list page's question.** The enlarged related list (the record page's active tab) drew Export whenever its data source could export. It now also reads the child object's effective API operation set (`/me/permissions` `apiOperations`) through `usePermissions()`, the source the list page's `exportPermitted` reads, and draws no Export when that set leaves `export` out. A missing set keeps the button, as on the list page. A reader who may export still exports this relation's rows alone.
14+
- **Export refusals read in the Console's language.** When the export route still refuses (a 403, such as `EXPORT_NOT_PERMITTED`), the related table, the list page and the grid show the new `view.exportNotPermitted` sentence in the reader's language instead of the route's English text. Other export failures keep the server's sentence.
15+
- **`@object-ui/i18n`: `view.exportNotPermitted`** in all ten locale packs.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@object-ui/plugin-detail': patch
3+
---
4+
5+
feat(plugin-detail): the default record page lets an editor move a record to another stage by pressing it on the stage path
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
**Visible behaviour change on a released page.** The record page the console synthesizes for an object with a stage field (no authored page) used to draw its stage path as a read-only progress rail, so moving a deal to its next stage took 编辑 → change the stage → 更新. That page's `record:path` now carries `allowStageChange: true`, the existing opt-in (no new metadata key):
11+
12+
- **Who can press.** Unchanged from the opt-in: a reader the record page lets edit this record, on a stage field that is not read-only or computed, whose field edit grant holds for the stored row. Everyone else keeps the read-only path, and so does an editor while the record's inline edit holds unsaved changes.
13+
- **Every press asks for confirmation**, then saves the stage through the record's ordinary update with the record's version, so the object's validation rules and `state_machine` decide. A refused move is shown under the path, naming any other field the move needs (for example a field made required by the new stage); nothing is written.
14+
- **Only reachable stages are offered.** On every page with the opt-in, a stage the stage field's blocking `state_machine` cannot reach from the stored value is drawn as a readout, not a button, the same moves the record form, the grid's inline editor and the board offer. A field with no such rule offers every stage, and the server still judges each move.
15+
- **Authored pages keep their own setting.** A page, or an authored `highlights` slot, that declares its own `record:path` keeps whatever it declares: `true`, `false`, or the key left out (a read-only rail).
16+
17+
To keep a default record page's path read-only, assign the object an authored page (or a `highlights` slot) whose `record:path` leaves `allowStageChange` out or sets it `false`.

0 commit comments

Comments
 (0)