Skip to content

Commit 6d80aba

Browse files
committed
Merge branch 'main' into claude/project-thread-q3olv3
Brings in the 29 commits main gained since the previous merge, among them the re-pointed ObjectSchema corpus guards (#26231) this branch's console pins were waiting on. One text conflict: the sdui core source record (scripts/sdui-manifest.sources/core.json), re-recorded on both sides, is taken from main and then re-recorded from the merged tree by the manifest generator. Generator-owned artifacts are regenerated from the merged tree with the spec generators, not by hand. Claude-Session: https://claude.ai/code/session_01H37vtTJRVBf1LkyYNW5yfo Co-authored-by: Claude <noreply@anthropic.com>
2 parents 53e1488 + 9598405 commit 6d80aba

397 files changed

Lines changed: 7844 additions & 7007 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.

‎.changeset/25610-notify-email-field-recipient.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ A `notify` node's `recipients` takes a fourth form, as the whole value: `{ email
2222
- The form is the whole `recipients` value, never a list entry.
2323
- `@objectstack/lint` refuses, at `os validate`, `os lint`, `os build` and the runtime publish door for a flow, an email-field recipient whose field the trigger object does not declare (`notify-email-field-unknown`), one whose field is not `email`-typed (`notify-email-field-not-email`), and one in a flow that hands its run no record of a known object — a schedule, an inbound api call, a record trigger that names no object (`notify-email-field-no-trigger-record`). A trigger object another package declares is not judged.
2424

25-
This change is the protocol half. Delivery — resolving the field to an address, the outbound mail configuration, the per-address rate limit and the messaging-log entry — is the messaging service's half and lands separately; until it does, a run that reaches such a node fails at the node.
25+
This change is the protocol half. Delivery — resolving the field to an address, the outbound mail configuration, the per-address rate limit and the messaging-log entry — ships in the same release in `@objectstack/service-automation`.
2626

2727
Clause-②: yes (widening)
2828

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@object-ui/components': patch
3+
'@object-ui/i18n': patch
4+
---
5+
6+
fix(components): a refused submit moves focus to the first invalid field and no longer toasts the field list over the dialog's title
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
- **Before.** A submit refused by field validation, on the client or by the server's `fields[]`, raised an error toast naming the fields ("Please check the highlighted fields: First name, Last name, Company…") on top of the message already written under each input. The toast cut its list after three names, and in a record create / edit dialog it was drawn over the title row.
12+
- **Now.** The form raises no toast for a refusal that every field carries inline. It scrolls to the first invalid field in form order and focuses it, and first activates that field's tab when it is on a background tab. The message under each input is the whole report.
13+
- **A field with nothing drawn to carry it.** When the server rejects a field that has no drawn input, for example one on a tab or section a `visibleWhen` predicate hides, the form shows the form-level banner and its toast with the server's sentence, the same path a rejection naming an unknown field already takes. The hidden tab is not activated.
14+
- **Earlier outcomes retire.** A refusal on the client now takes down the previous attempt's banner and toast, and a clear-on-hide notice, as an attempt that passes validation already did. Before, the refusal toast replaced them under the form's outcome id.
15+
- **`validation.formInvalid` removed.** The toast's sentence ("Please check the highlighted fields: {{fields}}") had no other reader, so it leaves the form's default map and all ten `@object-ui/i18n` locale packs. `validation.formInvalidJoiner` stays: the clear-on-hide notice, the faulted-`visibleWhen` refusal and the option-group hint still join field labels with it. A host bundle that overrides `validation.formInvalid` can drop the entry; nothing reads it.
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/console': patch
4+
'@object-ui/i18n': patch
5+
---
6+
7+
fix(app-shell,console): an invitee who opens the invitation link signed out is greeted for the invitation, offered "Create an account and join" first, and the accept page names the inviter
8+
9+
Clause-②: yes
10+
Metadata-protocol: none
11+
12+
- **Login page.** A signed-out visitor arriving at `/login?redirect=/accept-invitation/ID` used to see the returning-user card ("Welcome back · Sign in to continue your work") with a small "Sign up" link under the form. Both login pages (the console's own and `DefaultLoginPage`) now title the card "Accept your invitation · Use the email address the invitation was sent to." Where the server offers sign-up to this visitor, a full-width "Create an account and join" link (the new `InvitationSignUpPrompt` export) sits above the sign-in form in place of the small link. With sign-up switched off, the visitor gets the invitation header and the sign-in form only.
13+
- **Register page.** From an invitation redirect, both register pages title the form "Create an account to join" and ask for the address the invitation was sent to, because only that account can accept it.
14+
- **Accept page.** `DefaultAcceptInvitationPage` shows an "Invited by" row with the inviting member's email, which `GET /organization/get-invitation` already returns as `inviterEmail`. That route returns no inviter name.
15+
- **Not in this change.** The signed-out pages name no organization, inviter or invited address, and do not prefill the email. The only invitation read the server serves answers a signed-out caller `401`, so a preview for signed-out visitors needs a new server read first.
16+
- New locale keys in all ten packs: `auth.login.invitationTitle`, `auth.login.invitationDescription`, `auth.login.invitationSignUpButton`, `auth.login.invitationSignInDivider`, `auth.register.invitationTitle`, `auth.register.invitationDescription`, `organization.accept.invitedBy`.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@object-ui/plugin-map': patch
3+
'@object-ui/i18n': patch
4+
---
5+
6+
fix(plugin-map): a map whose style or tiles fail to load shows the record list with a translated notice, instead of a grey canvas under an English alert
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
- **`@object-ui/plugin-map`:** when MapLibre reports a style or tile load failure (an intranet or proxied deployment that cannot reach the tile host, an unreachable style URL), the map view now removes the map and lists its records in its place, the same list a browser without WebGL2 already got: searchable, and each row opens the record the way its marker did. A "Try the map again" button brings the map back. The notice above the list keeps MapLibre's reason and names a remedy that exists: a view with its own map style is told to check that URL, and a view on the public default style is told to ask an administrator to set a map style for the view. It no longer says the markers are "listed in the search box" or asks for a "custom map style for production use" setting that does not exist. That notice, the WebGL2 notice, the list's label and empty text, and the "records excluded for missing or invalid coordinates" count now read the translation bundle, so a zh-CN console shows them in Chinese.
12+
- **`@object-ui/i18n`:** eight new `element.map.*` keys in all ten packs.
13+
14+
What to do: nothing. A deployment-wide map style setting is not part of this change.
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): Browse Marketplace says the catalog could not be reached when the runtime cannot reach it, instead of "the catalog answered with an error (HTTP 502)"
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **`@object-ui/app-shell`**: the runtime's marketplace proxy answers `502 MARKETPLACE_PROXY_FAILED` only when its own fetch to the catalog throws. The load-error panel picked its class line by status alone, so that answer read "The marketplace catalog answered with an error (HTTP 502)", with the raw transport text `fetch failed` in full view below it. The panel now reads the error code as well: that code, or no answer at all, reads "The marketplace catalog could not be reached.", and the raw text sits behind a **More** / **Less** disclosure with a copy button. A failure the catalog did answer, including a 502, 503 or 504 relayed from upstream, still names its HTTP status and shows the server's own text. The online hint is unchanged.
11+
12+
What to do: nothing.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@object-ui/i18n': patch
3+
---
4+
5+
chore(i18n): delete 92 message keys nothing reads from all ten locale packs
6+
7+
Clause-②: yes (TranslationKeys narrows by 92 unread members; patch per the #4730 retirement precedent)
8+
Metadata-protocol: none
9+
10+
`ui/scripts/check-i18n-dead-keys.mjs` listed 122 keys with no call site and no textual occurrence anywhere in the repository. 92 of them are deleted from `ar`, `de`, `en`, `es`, `fr`, `ja`, `ko`, `pt`, `ru` and `zh`; no key is renamed or added, and no screen changes, because no code asked for any of them. The largest groups are `appDesigner.*` (20 keys), `report.*` (9), `publicForm.*` (8), `table.*` (7) and `layout.appSwitcher.*` (7).
11+
12+
Two groups the script also listed stay:
13+
14+
- 28 `_one` / `_other` plural forms (`preview.changes.typeCount.*`, `notifications.arrivalMany` / `arrivalRepeats`, `fields.relativeDate.overdue`). Their base keys are called with `count`, and i18next reads the plural form, so they are in use.
15+
- `console.ai.agentLabels.dataChat` and `metadataAssistant`, which the pack keeps on purpose for external overrides.
16+
17+
`TranslationKeys` (`typeof en`) and the other pack objects' types lose the 92 members. A host that reads one of them by property gets a type error; nothing in this repository does, and no `t()` call anywhere asks for one.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@object-ui/core': patch
3+
'@object-ui/plugin-form': patch
4+
'@object-ui/plugin-grid': patch
5+
---
6+
7+
feat(ui): an edit form's `state_machine` select offers only the moves the server accepts
8+
9+
Clause-②: yes
10+
Metadata-protocol: none
11+
12+
A record whose `status` is governed by an error-severity `state_machine` validation used to offer every option in the edit form, and the server refused the invalid ones only after submit, without naming the allowed next states. The form now offers the persisted value plus the states the machine declares as its next moves.
13+
14+
- **`@object-ui/core`:** new export `stateMachineNextValues(objectSchema, fieldName, currentValue)`. It returns the set of values a field may move to from its persisted value, or `null` for "don't constrain". It narrows only where the server refuses: rules with `severity: 'error'` (the default), `active` not `false`, and `events` covering `update` (the default covers it). A from-state the transition map does not declare stays unconstrained, and several applicable rules on one field intersect. It is a read of the declared transition map for choice controls, not a client-side rule engine.
15+
- **`@object-ui/plugin-form`:** `gateFormFields` takes an optional `previousValues` (the persisted record) and narrows each single-value select governed by such a machine, on both the entry's own `options` and its `field.options`. Every layout passes its persisted record: the simple, modal, drawer, tabbed, split and wizard forms. The from-state is the stored row, never the live form value. A create form is unconstrained, and the current value always stays offered. Options outside the reachable set are left out rather than drawn disabled.
16+
- **`@object-ui/plugin-grid`:** `stateMachineNextValues` is now re-exported from `@object-ui/core`. The grid's inline editor therefore stops narrowing on a `warning` / `info` machine, an inactive one, or one whose `events` leave out `update`. The server never refuses those moves, so the editor now offers every option for them.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@object-ui/fields': patch
3+
---
4+
5+
fix(fields): a `user` field's person object is always `sys_user` — the user cell and the person picker no longer read an authored `reference`
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
A `user` field's target is a constant of its type: `@objectstack/spec` refuses a `user` field whose `reference` names any object but `sys_user`, and `referenceTargetOf` resolves every `user` field to `sys_user` whatever the field carries. The console now agrees with it in two places:
11+
12+
- **`UserCellRenderer`** (read-only cell): the field-level read gate on the person's name and avatar is keyed by `sys_user`. A stored object saved before the spec refused a foreign `reference` (for example `reference: 'crm_member'`) used to gate the cell by that object's policy; it is now gated by the `sys_user` policy, the object the runtime expands the field into.
13+
- **`UserField`** (person picker): the picker targets `sys_user` even when the field carries a foreign `reference`, so it offers users, matching what the runtime stores and resolves.
14+
15+
What to do: nothing for a spec-valid `user` field (no `reference`, or `reference: 'sys_user'`). A field that links to another object is a `lookup`.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'@objectstack/service-storage': patch
3+
---
4+
5+
fix(storage)!: a record write naming a file its caller may not read is refused at the field
6+
7+
A record write that names a file its caller may not read is now refused at that field, instead of saving the id as written. The answer is one `400 VALIDATION_FAILED` whose `fields[]` names each such field with the ADR-0114 code `invalid_value`, the same envelope the public form door gives an anonymous submission that carries a file. It names the field only. An id that names no file of the acting organization gets the same answer, so the write cannot tell the two apart. Nothing is claimed, copied or written. System writes and writes with no caller context reference files as before, and an authorization-store outage still fails the write with its declared envelope.
8+
9+
A file governed by a record's rule also gets the record-governed link lifetime at the download door.
10+
11+
Clause-②: no (narrowing) — a record write that names a file its caller may not read, or an id no file of the organization has, was saved and is now refused. Nothing that was refused becomes accepted.
12+
13+
Metadata-protocol: none (a write-time refusal on file fields; nothing a metadata author writes changes)
14+
15+
<!-- adr-0087: not-required (no-migration-prescription) No metadata author's surface moves: no spec key or authorable spelling is added, removed, renamed or re-shaped, and no exported declaration is removed or narrowed, so there is nothing for `objectstack migrate meta` to rewrite. What changes is which record writes the file-reference lifecycle accepts: a write naming a file its caller may not read is refused at the field. The remedy is naming a file the caller may read, or leaving the field empty, never a rewrite of a package source or a stored row. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id is named or touched (not registered or already-registered); and nothing exported is removed or narrowed (not runtime-interface-only, type-surface-only or runtime-export-retired). -->
16+
17+
**BREAKING** for a caller that writes a file id it may not read, shipped as `patch` because the metadata protocol does not move.
18+
19+
- **Before.** The write saved, and the field kept an id its readers could not use.
20+
- **Now.** The write is refused with `400 VALIDATION_FAILED`, naming the field with `invalid_value`.
21+
- **Fix.** Name a file the caller may read (upload it, or choose one they can open), or leave the field empty.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
'@objectstack/account': patch
3+
'@objectstack/cli': patch
4+
'@objectstack/client': patch
5+
'@objectstack/client-react': patch
6+
'@objectstack/cloud-connection': patch
7+
'@objectstack/connector-imap': patch
8+
'@objectstack/connector-mcp': patch
9+
'@objectstack/connector-openapi': patch
10+
'@objectstack/connector-rest': patch
11+
'@objectstack/connector-slack': patch
12+
'@objectstack/console': patch
13+
'@objectstack/core': patch
14+
'@objectstack/driver-memory': patch
15+
'@objectstack/driver-mongodb': patch
16+
'@objectstack/driver-sql': patch
17+
'@objectstack/driver-sqlite-wasm': patch
18+
'@objectstack/driver-turso': patch
19+
'@objectstack/embedder-openai': patch
20+
'@objectstack/formula': patch
21+
'@objectstack/hono': patch
22+
'@objectstack/knowledge-memory': patch
23+
'@objectstack/knowledge-ragflow': patch
24+
'@objectstack/lint': patch
25+
'@objectstack/mcp': patch
26+
'@objectstack/metadata': patch
27+
'@objectstack/metadata-core': patch
28+
'@objectstack/metadata-fs': patch
29+
'@objectstack/metadata-protocol': patch
30+
'@objectstack/objectql': patch
31+
'@objectstack/observability': patch
32+
'@objectstack/organizations': patch
33+
'@objectstack/platform-objects': patch
34+
'@objectstack/plugin-approvals': patch
35+
'@objectstack/plugin-audit': patch
36+
'@objectstack/plugin-auth': patch
37+
'@objectstack/plugin-dev': patch
38+
'@objectstack/plugin-email': patch
39+
'@objectstack/plugin-hono-server': patch
40+
'@objectstack/plugin-pinyin-search': patch
41+
'@objectstack/plugin-security': patch
42+
'@objectstack/plugin-sharing': patch
43+
'@objectstack/plugin-webhooks': patch
44+
'@objectstack/rest': patch
45+
'@objectstack/runtime': patch
46+
'@objectstack/sdui-parser': patch
47+
'@objectstack/service-analytics': patch
48+
'@objectstack/service-automation': patch
49+
'@objectstack/service-cache': patch
50+
'@objectstack/service-cluster': patch
51+
'@objectstack/service-cluster-redis': patch
52+
'@objectstack/service-datasource': patch
53+
'@objectstack/service-i18n': patch
54+
'@objectstack/service-job': patch
55+
'@objectstack/service-knowledge': patch
56+
'@objectstack/service-mailbox': patch
57+
'@objectstack/service-messaging': patch
58+
'@objectstack/service-package': patch
59+
'@objectstack/service-print': patch
60+
'@objectstack/service-queue': patch
61+
'@objectstack/service-realtime': patch
62+
'@objectstack/service-settings': patch
63+
'@objectstack/service-sms': patch
64+
'@objectstack/service-storage': patch
65+
'@objectstack/setup': patch
66+
'@objectstack/skills': patch
67+
'@objectstack/spec': patch
68+
'@objectstack/studio': patch
69+
'@objectstack/trigger-api': patch
70+
'@objectstack/trigger-record-change': patch
71+
'@objectstack/trigger-schedule': patch
72+
'@objectstack/types': patch
73+
'@objectstack/verify': patch
74+
'create-objectstack': patch---
75+
76+
chore(packages): publishing metadata — per-format types in `exports`, declared `sideEffects`, and a README for the 18 published packages without one
77+
78+
Clause-②: no
79+
Metadata-protocol: none
80+
81+
Measured on a fresh `origin/main` build of all 73 published packages outside `ui/`, each staged as `npm pack` would ship it, with publint 0.3.25 and @arethetypeswrong/cli 0.18.5. Before: 73 of 73 packages carried at least one publint message and 48 carried an attw problem. After: 3 packages carry a publint message and 16 carry an attw problem, each remaining class needing a change this one deliberately does not make — an export-map shape change, or a dual-build fix in `@objectstack/core`.
82+
83+
- **Types per module format.** On 38 manifests one `types` entry served both formats, so a TypeScript consumer importing the ESM build was handed the CJS declarations — every one of attw's 38 `FalseCJS`. Each `{types, import, require}` entry becomes `{import: {types, default}, require: {types, default}}` pointing at the declaration file the same tsup run already emits for that format. No subpath is added or removed, and no JavaScript target moves. `@objectstack/core` keeps its single `types` entry: its `.d.ts` and `.d.cts` declare the same classes with private members, so serving them per format gives a consumer that reaches `core` through both resolution modes two incompatible `ObjectKernel` types, which is `@objectstack/plugin-dev`'s declaration build (it awaits `import('@objectstack/objectql')` inside a CommonJS build). Its `FalseESM` therefore stays, measured and unfixed here.
84+
- **`sideEffects` on the 72 manifests that declared none**, read per package rather than set in bulk: `@objectstack/service-cluster-redis` registers its `redis` cluster driver at module load and `create-objectstack`'s bin reaches its program through a bare import, so both declare their entry files side-effectful; everything else is `false`.
85+
- **`publishConfig: { access: 'public' }`** on the 17 that had none, the value the other 56 already declare.
86+
- **`type`** on the 39 that shipped `.js` without declaring a format — every `.js` under their `dist/` was read first and is CommonJS, which is what the absent field already meant — **`engines.node >= 22.0.0`** on the 22 that had no `engines`, and **`repository.url`** as the full `git+https://` git URL on 71.
87+
- **A README for 18 published packages that had none**: one paragraph, the first import a reader writes, and a link to the package's docs page. They are the pages npm renders, and for these 18 npm rendered nothing.

0 commit comments

Comments
 (0)