Skip to content

Commit 0446795

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-30017-confirm-beside-params-icon-members
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgVugtCCwBs98FeR27P
2 parents 34eb15a + 5b748df commit 0446795

269 files changed

Lines changed: 7880 additions & 2707 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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@object-ui/plugin-chatbot': patch
3+
---
4+
5+
refactor(plugin-chatbot)!: retire eight zero-consumer exports (the floating chatbot context hook and its three types, the message item props, one adapter helper and two display helpers)
6+
7+
Clause-②: no (narrowing)
8+
Metadata-protocol: none
9+
10+
<!-- adr-0087: not-required (runtime-export-retired @object-ui/plugin-chatbot#useFloatingChatbot, @object-ui/plugin-chatbot#FloatingChatbotState, @object-ui/plugin-chatbot#FloatingChatbotActions, @object-ui/plugin-chatbot#FloatingChatbotContextValue, @object-ui/plugin-chatbot#ChatMessageProps, @object-ui/plugin-chatbot#toRuntimeToolInvocation, @object-ui/plugin-chatbot#unwrapToolResult, @object-ui/plugin-chatbot#summarizeChatError) the retired operands are a React context hook with its state, actions and context-value types, the props interface of an internal message row, one authored-to-runtime tool-invocation converter and two tool-call display helpers; nothing in the workspace or in hotcrm imports any of them from the package entry, and no packages/spec schema, registered node type, conversion surface or stored shape names them, so there is nothing for `objectstack migrate meta` to rewrite -->
11+
12+
**BREAKING** for a host that imports any of these eight names from `@object-ui/plugin-chatbot`. None had an importer anywhere in the workspace or in hotcrm, so the Console and every `@object-ui/*` package are unaffected. Every module stays in the package: `FloatingChatbotTrigger` and `FloatingChatbotPanel` keep reading the floating state through the hook, `toRuntimeMessages` keeps converting tool invocations, and `ChatbotEnhanced` keeps rendering tool results and error summaries with the two helpers. Only the entry stops publishing them. `FloatingChatbot`, `FloatingChatbotProvider`, `FloatingChatbotTrigger`, `FloatingChatbotPanel` and their `…Props` types, `toRuntimeMessages`, `authoredToRuntimeMessage`, `toRuntimeRole`, `toRuntimeTimestamp`, `toRuntimeToolState`, `humanizeToolName`, `TypingIndicator`, `emitAiUsageRefresh` and `AGENT_ALIAS_GROUPS` stay exported unchanged.
13+
14+
What leaves, and what to write instead:
15+
16+
- FROM `useFloatingChatbot`, `FloatingChatbotState`, `FloatingChatbotActions`, `FloatingChatbotContextValue` TO `FloatingChatbotTrigger` and `FloatingChatbotPanel` inside a `FloatingChatbotProvider`, which open, close and toggle the panel themselves; for a control of your own, hold the open state in your own component.
17+
- FROM `ChatMessageProps` TO `ChatbotProps`: `Chatbot` renders the message rows itself.
18+
- FROM `toRuntimeToolInvocation` TO `toRuntimeMessages` or `authoredToRuntimeMessage`, which convert every tool invocation of a message.
19+
- FROM `unwrapToolResult`, `summarizeChatError` TO no replacement: `ChatbotEnhanced` applies both itself; keep a local copy if your own chat surface needs them.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@object-ui/fields': patch
3+
'@object-ui/data-objectstack': patch
4+
---
5+
6+
refactor(fields, data-objectstack)!: `UploadProvider` moves into `@object-ui/fields`, the ObjectStack upload adapter into `@object-ui/data-objectstack`, and `@object-ui/providers` is retired
7+
8+
Clause-②: yes (narrowing) — `@object-ui/fields` gains `UploadProvider`, `useUpload`, `UploadProviderProps` and the three upload contract types, and `@object-ui/data-objectstack` gains `createObjectStackUploadAdapter` and `ObjectStackUploadAdapterOptions`; the whole `@object-ui/providers` package is retired, which takes `createS3Adapter`, `createAzureBlobAdapter`, their option types and the exported `createObjectUrlAdapter` with it.
9+
Metadata-protocol: none
10+
11+
<!-- adr-0087: not-required (runtime-export-retired @object-ui/providers#*) the retired operand is a published ui package whose entry held a React upload context, its hook, four upload adapter factories and their TypeScript option and result types; no packages/spec schema, registered node type, ADR-0087 conversion surface or stored shape names any of them, so there is nothing for `objectstack migrate meta` to rewrite, and a consumer meets the move at its own import, where the compiler names the missing module -->
12+
13+
**BREAKING.** `@object-ui/providers` is retired: `18.3.0` is its last published version, and it gets no further release. Its one remaining job was the upload context the file, image and avatar widgets of `@object-ui/fields` upload through, so that context now lives in `@object-ui/fields` itself, and the adapter that talks to an ObjectStack server's storage service lives in `@object-ui/data-objectstack`, beside the other fetch logic against that server. The upload contract the two share is `@object-ui/types`'.
14+
15+
What leaves, and what to do:
16+
17+
- FROM `@object-ui/providers` TO `@object-ui/fields` and `@object-ui/data-objectstack`: import `UploadProvider`, `useUpload` and the `UploadProviderProps`, `UploadAdapter`, `UploadOptions` and `UploadResult` types from `@object-ui/fields` (the three contract types also from `@object-ui/types`), and `createObjectStackUploadAdapter` with `ObjectStackUploadAdapterOptions` from `@object-ui/data-objectstack`, then drop the `@object-ui/providers` dependency. `createS3Adapter`, `createAzureBlobAdapter`, `S3AdapterOptions` and `AzureBlobAdapterOptions` had no consumer and leave with no replacement: a host that uploads to its own bucket writes an object with a `name` and an `upload(file, options)` method against the `UploadAdapter` type of `@object-ui/types` and hands it to `UploadProvider`. `createObjectUrlAdapter` is no longer exported: it stays the local `blob:` adapter `useUpload` falls back to when no `UploadProvider` is mounted.
18+
19+
What does not change: `UploadProvider`, `useUpload` and `createObjectStackUploadAdapter` keep their signatures and behaviour, the presigned upload request is the same, and the widgets read the same context.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/types': patch
3+
---
4+
5+
feat(types): `@object-ui/types` publishes the upload adapter contract, `UploadAdapter`, `UploadOptions` and `UploadResult`
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: none
9+
10+
The three interfaces the file, image and avatar widgets upload through, unchanged in shape from the `@object-ui/providers` package they leave. They live here, as `DataSource` does, because `@object-ui/fields` (whose `UploadProvider` reads an adapter) and `@object-ui/data-objectstack` (whose `createObjectStackUploadAdapter` builds one) do not depend on each other. A host that writes its own adapter types it from here. Nothing existing changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@objectstack/lint': patch
3+
---
4+
5+
fix(lint): a widget filter keyed on a dataset dimension publishes, and a filter key naming a dataset measure is refused with its own message
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: none
9+
Skills-docs: updated here — content/docs/deployment/validating-metadata.mdx
10+
11+
`widget-filter-field-unknown` and `report-filter-field-unknown` now judge a bare filter key against one accept set, held in `judgeDatasetFilterKeys`, so the two rules cannot drift: a field of the dataset's object (authored or registry-injected), then a dimension of the dataset.
12+
13+
- **Widened:** a dashboard widget's own `filter` keyed on a dataset dimension's name (`{ state: 'open' }` for a dimension `state` over the field `status`) was refused at publish as an unknown field, although the dataset query binds it through the dimension's field. It now publishes, as the same key on a report's `runtimeFilter` already did.
14+
- **Refused with its own message:** a bare key naming a dataset measure was already refused by both rules as an unknown field. It is now refused as a measure: a filter compares each row before aggregation, so the dataset query would compare the measure's raw column (`{ amount_sum: { $gt: 100 } }` compiles to `amount > 100`, not a filter on the total), or no column at all for a count, derived or duration measure. The fix line names the measure's column when it reads exactly one. A measure that shares a field's name is still the field, and accepted.
15+
16+
No key that published before is refused now.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/objectql': patch
3+
---
4+
5+
refactor(objectql): the eight `ObjectQL` methods that kept an inferred return type spell it, and five of them move into their facets
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- `registerApp`, `registerHook`, `registerDriver`, `init`, `triggerHooks`, `destroy`, `setDatasourceMapping` and `getStatus` now declare their return type, copied from the published declaration file it was inferred into. The bodies of `getStatus`, `triggerHooks`, `setDatasourceMapping`, `init` and `destroy` move out of `engine.ts` into their facet modules (the methods stay on the class as one-call stubs). The built declaration files are byte-equal before and after, and no behaviour changes; only the source maps move. Nothing to do on upgrade.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@objectstack/lint': patch
3+
---
4+
5+
fix(lint)!: the object publish door refuses an object whose own list view sorts by a field the list door refuses (`sortable: false`, a `formula`, or a name the object lacks) (#30095)
6+
7+
Clause-②: no (narrowing) — an object write the door accepted before is refused: an object whose built-in `listViews.*.sort` names a field the list door answers `400 INVALID_SORT` now saves `422 INVALID_METADATA`.
8+
Metadata-protocol: none
9+
Skills-docs: none — `skills/**` names no `sortable` field key, and `content/docs/deployment/validating-metadata.mdx` lists the doors by rule family, not by reference-integrity member, so it names no object crossing to correct; the prescription ships in the refusal text.
10+
11+
<!-- adr-0087: not-required (no-migration-prescription) No key of any schema is added, removed or renamed, and nothing an author may write changes: `os validate` / `os build` / `os lint` already refused this sort. The object publish door now gives an object's own list view sort the verdict the CLI, the view door and the page door already give it. A refused name never sorted anything (the list door answered every load 400 INVALID_SORT), so there is no rewrite that keeps what the author meant; the author sorts by a sortable field, and the refusal says which field and why. -->
12+
13+
**BREAKING** accept-set narrowing at the object publish door (`PUT /api/v1/meta/object/NAME`; Studio, REST and MCP authors).
14+
15+
- **Before.** `validateSortableFields` judged a list view's `sort` on the CLI, at the `view` door and at the `page` door, but not on an `object` write. An object whose built-in list view sorted by a field declaring `sortable: false`, by a virtual `formula`, or by a name the object does not have saved green, and that list view answered `400 INVALID_SORT` on its first load and every load after it.
16+
- **Now.** The member declares `object`, so the object door refuses that save `422 INVALID_METADATA` with `sort-field-unsortable` or `sort-field-unknown` at `error`, at the sort key's path (`objects.NAME.listViews.KEY.sort[i]`). Nothing is stored.
17+
- **Not changed.** A stored object already carrying such a sort keeps loading; only its next save is judged, and a stored sibling in violation is never charged to another object's write. A `?mode=draft` save is not judged, as before.
18+
19+
**What an author does.** Sort the list view by a sortable field, or remove `sortable: false` from the field it names. A refused sort never ordered the list, so there is nothing to keep.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@object-ui/layout': patch
3+
'@object-ui/app-shell': patch
4+
'@object-ui/plugin-detail': patch
5+
'@object-ui/plugin-charts': patch
6+
'@object-ui/console': patch
7+
---
8+
9+
fix(console): a nav entry with no icon draws its object's icon, record field labels read in one style, a count chart fits a small count, and a page whose entry failed to load retries (#30126)
10+
11+
Clause-②: yes (widening)
12+
Metadata-protocol: none
13+
14+
- **Navigation icons.** An app navigation entry of type `object` that names no `icon` now draws the icon its object declares, in the sidebar and in the `nav:menu` page block. Before, every such row drew the same generic page icon, so an app whose navigation names no icons showed one icon down the whole sidebar. The icon is read by object name from the metadata the console already holds, so no request is added and the navigation is not written to. An authored `icon` wins, and an object with no icon, a dashboard, page or report entry, and a host with no resolver keep the generic icon. `@object-ui/layout` exports `resolveNavItemIcon`, the types `NavIconTarget` and `NavTargetIconResolver`, and an optional `resolveTargetIcon` prop on `NavigationRenderer`, alongside the existing `resolveTargetLabel`.
15+
- **Record field labels.** A record page's section field labels no longer carry an uppercase transform. Only Latin text took it, so in a zh console an untranslated label (`REPAIR COST`) read in capitals beside a translated one (`负责人`). Section labels now render as authored, in the style of the highlights strip above them.
16+
- **Small count charts.** A count axis whose largest value is a small whole number (1 to 3) now tops out at that number, so a chart of one record draws its bar to the top of the axis instead of a quarter of the way up a 0–4 axis. An axis with an authored `min`, `max` or `stepSize`, fractional values, or a count of 4 or more draws as before.
17+
- **Boot retry.** When the page's entry script fails to load before anything mounts (the server went away between serving the page and serving its entry, as during an `os dev` restart), the page now reloads itself, backing off from 1 second and doubling, at most five times per tab. Before, the loading splash stayed forever. The count is cleared on the first mount, and where `sessionStorage` cannot be read nothing retries.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
feat(spec): declare the recycle bin's spec surface (ADR-0148): the `sys_recycle_bin` entry contract, the host retention resolver hook, the snapshot shape, the restore refusal codes, the `restored` event name and the two grant keys
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: added
9+
10+
A protocol addition: new spellings with their readers named, and no tombstoned key returns. Declared, not yet read. Every delete is still a physical delete, and nothing writes or reads the recycle bin until the engine slice lands it (snapshot on delete, restore, purge, empty, the TTL reap, the permission mapping and the REST doors); the Console's admin page and its undo toast follow it. This release gives both something to build on.
11+
12+
- **`@objectstack/spec` (`@objectstack/spec/data`):** `RECYCLE_BIN_OBJECT` (`'sys_recycle_bin'`) and `RECYCLE_BIN_ENTRY_FIELDS`, the entry's field list the object is built from: `object_name`, `record_id`, `record_count`, `snapshot`, `deleted_by`, `deleted_at`, `retention_days` and `purge_after` (the deadline stamped at delete time), all read-only, with `RecycleBinEntrySchema` as the entry's value shape. The object itself is registered with the engine slice, so no table and no platform-object name exist until a writer does. `RECYCLE_BIN_RETENTION_DAYS_ENV` (`'OS_RECYCLE_BIN_RETENTION_DAYS'`) and `RECYCLE_BIN_DEFAULT_RETENTION_DAYS` (30). `RecycleBinRetentionResolver`, the host hook's contract: `(token: string | undefined) => number | undefined`, called with the same opaque token `resolveFeatures` receives, answering a positive whole number of days or `undefined` to keep the default. `RecycleBinRetentionDaysSchema` and `RecycleBinRetentionDaysEnvSchema` hold the answer and the variable to that rule, and `recycleBinRetentionRefusal(source, value)` is the refusal text: zero or anything that is not a positive whole number is a configuration defect, never "purge now" or "keep forever". The framework knows days, never plans. `RecycleBinSnapshotSchema` (with `RecycleBinSnapshotRowSchema` and `RecycleBinClearedReferenceSchema`): the cascade set an entry holds, the master first and its details in cascade order, secret-channel refs left out, plus the `set_null` references the delete cleared. `RECYCLE_BIN_RESTORE_REFUSAL_CODES` and `RECYCLE_BIN_RESTORE_REFUSAL_STATUS` (409). `RECORD_RESTORED_EVENT` (`'data.record.restored'`): a restore emits it, never a create; `DataEventType` and `RealtimeEventType` list only emitted events, so the name joins them in the change that emits it. `RECYCLE_BIN_GRANT_KEYS`.
13+
- **`@objectstack/spec`, error-code ledger:** `RESTORE_UNIQUE_VALUE_TAKEN`, `RESTORE_PARENT_PURGED` and `RESTORE_PACKAGE_UNINSTALLED`, registered under `@objectstack/objectql`, which answers them.
14+
- **`@objectstack/spec`, object permissions:** two new optional keys, `allowRestoreOwnDeleted` (restore the entries of this object the caller deleted) and `manageRecycleBin` (list every entry of this object, restore any, purge; emptying purges every entry the holder manages). No default, so no parsed entry grows a key. Not read yet: a dispatched `restore` or `purge` stays denied by the permission evaluator's fail-closed backstop whatever they say, and their schema descriptions say they are not read yet. The retired `allowRestore` / `allowPurge` stay tombstoned.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/console': patch
4+
---
5+
6+
fix(console): a language picked on the register page is saved as the new person's own language when registering signs them in (#30338)
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
A language picked in the register page's menu used to apply on that browser only: once the sign-up signed the person in, their saved language stayed empty, so another device, or the language their mail is written in, did not follow the pick. The register page now saves the pick to the person's own language setting before it moves on, the way the sign-in page does, on every way it leaves after a sign-up, joining an organization from an invitation link included. `DefaultRegisterPage` does the same for a pick made earlier on another signed-out page. Nothing is saved when no language was picked, a language the person already has is kept, and a save that fails does not hold up the sign-up (the pick still applies on that browser). A pick followed by a reload of the register page, or by an identity provider's redirect, is still not saved.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/service-datasource': patch
3+
---
4+
5+
fix(service-datasource): Test connection answers `ok: false` when the driver connects but its own health check answers `false` (#30407)
6+
7+
Clause-②: no — a served answer comes to match the driver's own health answer; no accept set changes.
8+
Metadata-protocol: none
9+
10+
The datasource probe awaited the handle's `ping` (the driver's `checkHealth()`) and dropped its boolean, so a connect that only warned and then reported itself unhealthy answered Test connection `ok: true`. The probe now reads that answer: a literal `false` answers `ok: false` with a sentence saying the driver connected but its health check reported the database as unhealthy. A connect or ping that throws keeps answering the thrown message, and a handle whose `ping` answers nothing keeps answering `ok: true`.

0 commit comments

Comments
 (0)