Skip to content

Commit e4434f7

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-x004t9
2 parents 96a92ca + e677770 commit e4434f7

287 files changed

Lines changed: 5410 additions & 8254 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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/plugin-email': patch
3+
---
4+
5+
fix(plugin-email)!: retire `renderTemplate`, the render export that takes an email template body with no save moment
6+
7+
The hole-placement rule for mail templates is judged where a template is authored or published: `os validate`, `os build`, `os lint` and the runtime publish gate. The `renderTemplate` function on the package entry took a template body inline and rendered its html face at once, with no save moment, so the rule could not see it. It leaves the published entry of `@objectstack/plugin-email`. Nothing in this repository imported it from that entry. The renderer stays inside the package: `services.email` still renders every stored `email_template` through it, unchanged.
8+
9+
Clause-②: no (narrowing)
10+
Metadata-protocol: none
11+
12+
<!-- adr-0087: not-required (runtime-export-retired @objectstack/plugin-email#renderTemplate) One runtime function leaves a published package entry; no key of any metadata schema, no object definition and no stored shape is added, removed, renamed or re-shaped, so there is nothing for `objectstack migrate meta` to rewrite and no tombstone. A TypeScript consumer meets the break at its own import, and the line below names the door to use instead. -->
13+
14+
**BREAKING** (a runtime export is removed). It ships as `patch` because the metadata protocol does not move. There is no staged window.
15+
16+
- FROM `renderTemplate` TO an `email_template` item for the name and locale, rendered by `services.email` through `renderTemplate({ template, data, locale })` (render only) or `sendTemplate` (render and send).
17+
18+
Unchanged: `IEmailService.renderTemplate` (the render-by-name method of the same name), `requireVars`, `htmlToText` and the template loader; and how every stored template renders.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
'@object-ui/components': patch
3+
'@object-ui/console': patch
4+
'@object-ui/layout': patch
5+
'@object-ui/plugin-calendar': patch
6+
'@object-ui/plugin-dashboard': patch
7+
'@object-ui/plugin-detail': patch
8+
'@object-ui/plugin-gantt': patch
9+
'@object-ui/plugin-grid': patch
10+
'@object-ui/plugin-kanban': patch
11+
'@object-ui/plugin-map': patch
12+
'@object-ui/plugin-timeline': patch
13+
'@object-ui/plugin-tree': patch
14+
'@object-ui/react': patch
15+
---
16+
17+
refactor(ui)!: the renderer runs `@objectstack/sdui-parser`, and `@object-ui/sdui-parser` is deleted (#25712)
18+
19+
Clause-②: yes (narrowing: a `type-mismatch` or `member-type-mismatch` the html-tier compile used to grade `warning` is now an `error`, so a page that carried one now refuses to compile instead of rendering with the value dropped)
20+
Metadata-protocol: none
21+
22+
The renderer and the save gate each ran their own copy of the SDUI parser. The previous change under this card made `@objectstack/sdui-parser` the one body. This change moves every `@object-ui/*` package, the console and the examples onto it, and deletes `ui/packages/sdui-parser`. There is no forwarding release of `@object-ui/sdui-parser`: it leaves the fixed version group, and the 18.1 line on npm is its last.
23+
24+
- **Dependencies.** Each package above now depends on `@objectstack/sdui-parser` where it depended on `@object-ui/sdui-parser`. The console's `gen-manifest.ts` moved from the deleted package to `ui/apps/console/scripts/`.
25+
- **Severity, the one narrowing.** The renderer's copy graded a `type-mismatch` as an `error` only beside an `enum` arm, and as a `warning` otherwise. The kept rule grades every `type-mismatch` and `member-type-mismatch` an `error`, which is what the save gate already did. So an html-tier page whose attribute does not fit the declared type now shows the compile-error block naming the prop, where it rendered the block with that value dropped. Example: `<list-view columns='["name","amount"]' />` (a string where `columns` is declared `array`). A page the save gate accepted is not affected, because the save gate already refused it.
26+
- **Body-dialect messages.** The retired `body` child-list key's `unknown-prop` message now ends `("body" is its retired spelling)`, and its `not-a-container` message drops the `(objectui#6771)` parenthetical.
27+
28+
Migration: none for a page the save gate accepted. A page that relied on the warning has a value the declared type does not take: write it in the declared type, for example `columns={['name','amount']}`.
29+
30+
<!-- adr-0087: not-required (no-migration-prescription) No metadata moves: no spec key, authorable spelling, object definition, stored row or stored shape is added, removed, renamed or re-shaped, so there is nothing for `objectstack migrate meta` to rewrite. What narrows is the renderer's grading of a value that does not fit its declared type, which the save gate already refused, so no page saved through the gate changes verdict. The repair of a page written around the gate is the author's per diagnostic, which no ledger entry can derive. The other categories are closed on facts: no ADR-0087 id is named or touched (not registered or already-registered); the deleted `@object-ui/sdui-parser` stays published on npm at 18.1 and is deprecated there by the maintainer after 18.2, and no package in this change is unpublished (not unpublished); nothing exported by a package above is removed or narrowed (not runtime-interface-only, type-surface-only or runtime-export-retired). -->

‎.changeset/25831-object-ui-18-1-to-merge.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
"@object-ui/providers": minor
3636
"@object-ui/react": minor
3737
"@object-ui/react-runtime": minor
38-
"@object-ui/sdui-parser": minor
3938
"@object-ui/types": minor
4039
---
4140

‎.changeset/25926-retire-node-visibility-dialects.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
'@object-ui/types': minor
33
'@object-ui/react': minor
4-
'@object-ui/sdui-parser': minor
4+
'@objectstack/sdui-parser': patch
55
'@object-ui/app-shell': patch
66
---
77

@@ -16,7 +16,7 @@ Metadata-protocol: narrowed
1616

1717
- **`@object-ui/types`**: `BaseSchema.visibleOn`, `hiddenOn` and `disabledOn` are `never` on the TypeScript face and alias refusals on the Zod mirror. A document that writes one is refused at that key, and the message names the key that survives (`visibleWhen`, `hidden`, `disabled`), says the polarity is the same, and says the value is unchanged. `objectui validate` reads the same mirror, so it prints the same refusal. The `object-view` table slot's refusal of the same three keys now names the surviving node key too (write `visibleWhen` / `hidden` / `disabled` on the object-view node itself) instead of sending the author to a retired spelling. Boolean `visible` stays: the spec declares it on actions, navigation entries, tabs, alerts and settings, and the renderer keeps evaluating it.
1818
- **`@object-ui/react`**: `SchemaRenderer`'s visibility chain has three legs, `visibleWhen` and `visible` (shown when truthy) and `hidden` (hidden when truthy), and the enablement gate has one, `disabled`. A stale retired key that reaches the renderer unvalidated is not consulted and is held out of the component's props, so it neither gates the node nor lands in the DOM.
19-
- **`@object-ui/sdui-parser`**: the three keys are no longer base props. `validateTree` reports each as an `unknown-prop` diagnostic that names the surviving key, so a TSX tree is told the same thing a JSON document is. `checkRetiredVisibilityDialect` and `RETIRED_VISIBILITY_DIALECTS` are exported for that reading.
19+
- **`@objectstack/sdui-parser`**: the three keys are no longer base props. `validateTree` reports each as an `unknown-prop` diagnostic that names the surviving key, so a TSX tree is told the same thing a JSON document is. `checkRetiredVisibilityDialect` and `RETIRED_VISIBILITY_DIALECTS` are exported for that reading.
2020
- **`@object-ui/app-shell`**: the command palette reads a navigation entry's `visible` only; the `visibleOn` fallback it carried read a key `NavigationItem` never declared.
2121

2222
**BREAKING** (an accept-set narrowing) for any page, view or component document that writes `visibleOn`, `hiddenOn` or `disabledOn` on a node. No shipped example, fixture or skill example in this repository wrote one; the ones in the documentation and the `objectui` skill are rewritten in this change.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@objectstack/lint': patch
3+
---
4+
5+
fix(lint): the liveness author warnings and their status census read ledger rows at every depth of `children`
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The liveness rule built its warn map, and its census of the status words the ledgers use, from the top-level rows and one level of `children`. The ledgers have rows two and three levels down, and those were never read. On the tree this was measured on, 41 rows two levels down and 2 rows three levels down carry a status.
11+
12+
- **Warnings.** A row at any depth now warns under the same rules as a shallower one. Its verdict warns on its own (`dead`, `live-elsewhere`, `experimental`), or the row opts in with `authorWarn`. The only rows this reaches today are the dashboard's `widgets[].chartConfig.type`, `.xAxis`, `.yAxis`, `.series` and `.aria`, all `dead`. Each of them is already a retired key that the schema refuses, so `os lint` handed an unparsed stack now also reports `liveness-dead-property` for them. No row at depth 2 or deeper carries `authorWarn`, so no other warning appears.
13+
- **Status census.** A status word used only at depth 2 or deeper now reaches the vocabulary check that makes sure every status has an author-facing branch.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'@objectstack/plugin-approvals': patch
3+
'@objectstack/plugin-sharing': patch
4+
'@objectstack/plugin-security': patch
5+
---
6+
7+
fix(plugin-approvals,plugin-sharing): the approver a pending step waits on can read the record the request is about
8+
9+
Clause-②: no
10+
Metadata-protocol: none
11+
12+
An approver was asked to decide on a record they could not open. Unless the app happened to grant them read of that exact row, the request page showed only the snapshot and the record card read "unable to open this record", so a contract was approved without its document being read.
13+
14+
- **Who reads.** While an approval request is pending, each person on its current step's slate (a slot naming their user id, or the email their own account carries) and the override holder (the administrator who may decide any pending request of their organization) may READ the one record the request is about. A slot left as a `position:` or `team:` literal names no person and grants no read.
15+
- **How far.** Read only: the write gates never read this grant, so the approver still cannot edit or delete the record. Only the record itself, as a reader of it reads it: attachments follow the record read, as they do for any reader. Only while the step is pending: a decision that resolves the step, or advances the request to its next step, ends the grant at the next read. Never across organizations, the platform administrator's override included. An on-behalf-of (agent) principal gets no approval grant.
16+
- **Where it is decided.** `@objectstack/plugin-approvals` derives the records from the slate it already resolves (`ApprovalService.pendingApproverReadGrants`, one indexed read of the pending-approver index per request context, plus one request read per object, memoized per request and retired by any write). `@objectstack/plugin-sharing` ORs those record ids into its read filter beside the caller's record shares, so every read door that composes the sharing filter serves them; it grants nothing when the approvals service is absent or its read fails.
17+
- **Explain.** `@objectstack/plugin-security`'s access explanation names the grant on the sharing layer as a read share "via approval request N".
18+
19+
The grant is the row-level reach of a read share; the approver still needs read on the object itself. Nothing is declared: no metadata key, no app setting.
20+
21+
What to do: nothing. An app that wrote sharing rules mirroring its approval routing only so approvers could open the record may drop them.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/plugin-approvals': patch
3+
---
4+
5+
fix(plugin-approvals): an approval notification names the record by its title, as the recipient may read it
6+
7+
Every approval notification body named its record by the object's label and the record id (`Your Contract C8Mp7TjRYta8mOWx was approved.`), so a recipient could not tell which record it was without opening it. Each notification is delivered to one named recipient, so it now names the record by its display title (ADR-0079 `nameField`) as that recipient may read it: the inbox's own rule (the record read under the system context, the title picked from it as served to the reader, narrowed by field-level security), asked as the recipient, whose envelope is resolved through `resolveUserAuthzGrants` in the request's organization. It falls back to the record id when the title is empty, when the recipient may not read the title field or is served it masked, and when anything on the way fails. A notification that names one record to several recipients goes out once per name they read, so no recipient is handed another recipient's title. The message templates keep their `{{object}} {{record}}` shape. The token-bearing one-tap action pages keep naming the record by its id: they have no reader to narrow for.
8+
9+
Clause-②: no
10+
Metadata-protocol: none
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
'@object-ui/react': patch
3+
'@object-ui/components': patch
4+
'@object-ui/app-shell': patch
5+
---
6+
7+
fix(react,components,app-shell): a delegated admin's out-of-scope save shows the server's reason instead of the fixed no-permission line
8+
9+
Clause-②: yes (widening: `@object-ui/react` exports a new `delegationScopeMessage`)
10+
Metadata-protocol: none
11+
12+
When a delegated administrator saved something outside their own scope, the server answered `403 DELEGATION_SCOPE_VIOLATION` with the boundary that refused it (a business unit outside the delegated subtree, a permission set not in the scope's allowlist, a target user outside the subtree). The console read every 403 as a plain denial, so the toast and the in-form banner said only "You don't have permission to save this record."
13+
14+
- **`@object-ui/react`.** New `delegationScopeMessage(err)`: the server's reason, without the `[Security] ` machine prefix, when the error's code (`code` or `details.code`) is `DELEGATION_SCOPE_VIOLATION`; `null` for everything else. It is read before `isPermissionError`, which still answers true for any 403.
15+
- **`@object-ui/components`.** The record form's toast and banner show that reason. A producer-marked `userMessage` still comes first.
16+
- **`@object-ui/app-shell`.** A flow run whose write node met the same refusal (`innerRefusal`) shows the same reason.
17+
18+
Every other denial is unchanged: `PERMISSION_DENIED`, `FORBIDDEN`, a bare 403 and a row-level-security refusal still show the fixed localized line and never the server's text.
19+
20+
What to do: nothing.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
'@objectstack/spec': patch
3+
'@objectstack/cli': patch
4+
'@objectstack/connector-imap': patch
5+
'@objectstack/core': patch
6+
'create-objectstack': patch
7+
'@objectstack/lint': patch
8+
'@objectstack/metadata': patch
9+
'@objectstack/metadata-core': patch
10+
'@objectstack/objectql': patch
11+
'@objectstack/platform-objects': patch
12+
'@objectstack/plugin-auth': patch
13+
'@objectstack/plugin-webhooks': patch
14+
'@objectstack/service-cluster': patch
15+
'@objectstack/service-datasource': patch
16+
'@objectstack/types': patch
17+
---
18+
19+
docs: TypeScript's legacy `node10` module resolution is unsupported for packages with subpath exports
20+
21+
Clause-②: no
22+
Metadata-protocol: none
23+
24+
These fifteen packages declare their entry points only through `exports`, including subpaths such as `@objectstack/spec/kernel`. TypeScript's legacy `node10` resolution (`moduleResolution: "node"`, the default when `module` is `commonjs`) ignores `exports`, so a project configured that way cannot resolve the types of any subpath import, while the root entry still appears to work. Each package's README now says so in one line, and the plugin development guide gains a "Module resolution" paragraph. No `exports` map changes and no `typesVersions` fallback is added.
25+
26+
What to do: set `moduleResolution` to `node16`, `nodenext` or `bundler` in the consuming project's `tsconfig.json`.

‎.changeset/26276-ui-package-test-vitest.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
'@object-ui/providers': patch
2020
'@object-ui/react': patch
2121
'@object-ui/react-runtime': patch
22-
'@object-ui/sdui-parser': patch
2322
'@object-ui/types': patch
2423
---
2524

0 commit comments

Comments
 (0)