Skip to content

Commit d2801b8

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-k8l0qs
2 parents 4cb7cd3 + 6294157 commit d2801b8

146 files changed

Lines changed: 5284 additions & 3839 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: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
'@objectstack/sdui-parser': patch
3+
---
4+
5+
feat(sdui-parser)!: one parser body: the renderer copy's additions join `@objectstack/sdui-parser` (#25712)
6+
7+
Clause-②: yes (narrowing: `validateTree` now also judges the nodes held in a manifest entry's declared `slots`, so a caller whose manifest carries `slots` gets diagnostics on slot-held nodes it did not get before)
8+
Metadata-protocol: none
9+
10+
Two copies of this parser existed: this package, which the save gate runs, and `@object-ui/sdui-parser`, which the renderer runs. `check:sdui-lockstep` held them to one grammar and one code set, and they drifted everywhere else. This change makes this package the one body. The renderer moves onto it and the second copy is deleted in follow-up changes under the same card.
11+
12+
**Added (from the renderer's copy).**
13+
14+
- Node-slot validation (objectui#11170). `ManifestComponent.slots` lists the positions besides `children` where a renderer reads nodes, such as `trigger`, `items[].content` and `regions[].components`. `validateTree` descends each one the way it descends `children`. `manifestFromConfigs` gains a `slotsFor` option that writes the list, and a manifest built without it carries none, so the walk follows `children` alone.
15+
- The retired `body` child-list key (objectui#6771). Before, `body` drew the bare `unknown-prop`. Now the message names `children`. A `body` child list under a component that takes no children draws `not-a-container`, the code the `children` spelling draws. Both remain warnings. Exported as `checkRetiredBodyDialect` and `RETIRED_CHILD_LIST_KEY`.
16+
- html-tier provenance (objectui#4000). `parseJsx` stamps every element it builds with an enumerable symbol-keyed marker, `Symbol.for('@object-ui/sdui-parser.html-tier-node')`. `JSON.stringify` and `Object.keys` do not see it. Exported as `HTML_TIER_NODE`, `isHtmlTierNode` and `markHtmlTierNode`. The registry key keeps its original package name on purpose, so a node either copy stamped reads the same.
17+
- More exports:
18+
- `acceptsChildren`, `CHILD_LIST_KEY`, `SDUI_BASE_PROPS`, `SduiBaseProp` and `SduiBasePropScope`;
19+
- `assertFullyLoaded`, the manifest generators' guard against lazy stubs, with `RegistryConfigLike.lazy`;
20+
- `NodeSlotDeclarationLike`.
21+
- The generated `.d.ts` header gains `/* eslint-disable */` and names this package.
22+
23+
**Renamed, with the old names kept.** `ParsedSchemaNode` is the parsed node and `ManifestValidationResult` is `validateTree`'s result. These are the names the renderer's copy took so it would not collide with `@object-ui/types`' `SchemaNode` or the spec's `ValidationResult` (objectui#6349, objectui#3161). `SchemaNode` and `ValidationResult` stay exported as `@deprecated` aliases of them, so nothing exported is removed.
24+
25+
**Unchanged.**
26+
27+
- Every `type-mismatch` and `member-type-mismatch` is still an `error`. The renderer's copy graded them `error` only beside an `enum` arm; this package's rule is the one kept.
28+
- `unconsumed-widget-option` is still emitted as an inline literal.
29+
- The grammar, the 25 diagnostic codes and the containment predicate.
30+
31+
**BREAKING** (a narrowing of `validateTree` for a manifest that carries `slots`), shipped as `patch` because no metadata protocol kind moves. The save gate's verdicts do not move, and that was measured: the manifest that `objectstack validate`, `build`, `lint` and `serve` read is the tracked `sdui.manifest.json`, which `@objectstack/console` ships. It carries `slots` on 0 of its 106 entries, because its generator calls `manifestFromConfigs` without `slotsFor`. So no save that was accepted before is refused now. The walk has work only for a caller that builds its manifest with `slotsFor`, and today that is the renderer, whose own copy already walks the slots.
32+
33+
Migration, for a TypeScript caller: none is required. To move off the deprecated names, change `SchemaNode` to `ParsedSchemaNode` and `ValidationResult` to `ManifestValidationResult`.
34+
35+
`body-dialect.ts` and `provenance.ts` moved in from the MIT-licensed `@object-ui/sdui-parser` and keep that licence. Each carries the notice, and the package now ships `LICENSE-MIT`.
36+
37+
<!-- 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 `validateTree`, a function of this library, for a manifest that carries the new `slots` key: nodes held in those positions are now judged. No manifest the save gate reads carries that key (the tracked `sdui.manifest.json`: 0 of 106 entries), so no stored or authored page changes verdict there. A caller who builds a slotted manifest gets diagnostics on slot-held nodes, and the repair is the page author's per diagnostic, which no ledger entry can derive. 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, since the two renamed types stay exported as deprecated aliases (not runtime-interface-only, type-surface-only or runtime-export-retired). -->
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/auth': patch
3+
'@object-ui/console': patch
4+
---
5+
6+
fix(auth,console): the sign-up form shows a password mismatch under the confirm field, and the register page links privacy and terms
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
- **Mismatch on the field.** `RegisterForm` showed "Passwords do not match" as the error banner at the top of the form, above the name field. The message now sits under the confirm-password input, which carries `aria-invalid` and names the message through `aria-describedby`. Editing either password clears it. The banner still carries what the server refuses (an existing account, a closed registration). The text is the same `labels.passwordMismatchError`, so no locale key changes. Every page that renders `RegisterForm` gets this.
12+
- **Legal links on sign-up.** The console's `/register` now draws the same footer as `/login`: the locale switch, plus the privacy and terms links that `GET /api/v1/auth/config` serves as `features.privacyUrl` / `features.termsUrl`. A link appears only when its URL is served and is an http(s) URL or a same-origin path. Both pages now read the links through one shared reader.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/plugin-grid': patch
3+
---
4+
5+
fix(plugin-grid): a bulk action's `confirmText` fills each param hole with that param's display value
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
A bulk action's `confirmText` holes may name the action's params by `name` (`{{ tier }}`), and the protocol states what such a hole shows: the param's display value. The bulk dialog filled `{{ count }}` only, so a param hole printed empty. It now fills each param root by that rule. An option-bearing param shows the chosen option's label, and a multi-value param shows its labels joined with ', '. A `lookup` or user param shows the picked record's display name (its id until the name has resolved; the sentence updates when the name arrives). Any other param shows the entered value itself, so the template engine's formatters apply (`{{ amount | currency:EUR }}`, `{{ due | date:long }}`). An empty param prints empty, and `{{ count }}` still wins over a param named `count`. The default sentence and the place of the confirm step are unchanged.
11+
12+
What to do: nothing.
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/i18n': patch
4+
---
5+
6+
fix(app-shell): the record create and update toasts name the saved record
7+
8+
Clause-②: no (adds or rewords message keys; no key is removed, so `TranslationKeys` only widens; no exported runtime contract changes)
9+
Metadata-protocol: none
10+
11+
A user entering several records in a row saw "Task created successfully" after each save and could not tell which record was just written. The Console's record-form save toasts now carry the saved record's display name (ADR-0079), for example `Task "Quarterly review" created` and `Task "Quarterly review" updated`. This covers the record-form overlay on a list and the child-record form opened over a parent record's page.
12+
13+
- **`@object-ui/app-shell`:** the name comes from the same display-name resolver the record page title and lookups use, read through the saved object's declared name field. A record with nothing nameable (the resolver would only answer `Record #ID`) keeps the object-label toast it showed before.
14+
- **`@object-ui/i18n`:** two new keys under `form.` in all ten packs, `createdNamed` and `updatedNamed`, each with `{{object}}` (the object label) and `{{title}}` (the record's name). The existing `form.createSuccess` / `form.updateSuccess` strings are unchanged and remain the toast for a record with no name.
15+
16+
Settings saves and flow-completion toasts are unchanged.
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+
---
4+
5+
fix(app-shell): the permission matrix sets and shows each object's read and write record scope
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
ADR-0084 declares a record-scope column for the permission matrix, but Setup → Permission Sets and Studio → Access had no control for an object permission's `readScope` / `writeScope`. The only way to set them was `PUT /api/v1/meta/permission/:name`.
11+
12+
- **Pickers.** Every object row, in both the Setup table and the Studio Access matrix, now has a **Read scope** and a **Write scope** picker. Each lists "Not set" and the five `ObjectAccessScopeSchema` values: own, own and reports, my unit, my unit and below, whole org. A picked value saves and reloads unchanged. This holds at the environment door and at the package door, where the slice merge keeps it. "Not set" removes the key.
13+
- **Hierarchy values.** "Own and reports", "My unit" and "My unit and below" carry the hint "requires enterprise; otherwise owner-only" and stay selectable. The open runtime resolves them to the owner alone. No hierarchy resolver is installed there.
14+
- **View All / Modify All.** The spec refuses `readScope` beside View All or Modify All, and `writeScope` beside Modify All, because those bits already grant org-wide access. Ticking View All now disables the read-scope picker and removes `readScope`. Ticking Modify All disables both pickers and removes both scopes. The **All** bulk button does the same. Before this change, both paths saved a row the server refuses. Effective access does not change, because the resolvers answer `org` on those bits before they read a scope. "CRUD", "R" and "None" are unchanged.
15+
16+
What to do: nothing. No spec key, stored shape or API changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@object-ui/permissions': patch
3+
---
4+
5+
fix(permissions)!: the console stops offering New, Edit and Delete to a user whose `/me/permissions` row omits the Create, Edit or Delete bit, such as an auditor or a read-only finance user
6+
7+
Clause-②: yes (narrowing) — the console no longer offers Create / Edit / Delete when `/me/permissions` serves an object row that omits the bit. That is exactly when the server refuses it, because the spec grants only on `=== true` (permission.zod.ts:196, :218). The anonymous default and the no-provider fail-open are unchanged.
8+
Metadata-protocol: none
9+
10+
<!-- adr-0087: not-required (no-migration-prescription) A console permission verdict: no key of any metadata schema is added, removed, renamed or re-shaped, and no export, type or error code changes. The client now reads a served row with the spec's own objectPermissionGrants, so there is nothing for `objectstack migrate meta` to rewrite and no tombstone. -->
11+
12+
**BREAKING** (a narrowing of what the console offers) in `MePermissionsProvider` (`@object-ui/permissions`).
13+
14+
- **Before.** `check` (behind `usePermissions().can`) and `checkField`'s object-level fallback read a served object row with `bit !== false`, so a bit the row left out counted as granted. The server serves only the bits a set grants: a read-only set `'*': { allowRead: true }` is served as `{ allowRead: true }` on each object it covers, and an auditor's row as `{ allowRead: true, viewAllRecords: true }`. Those users were offered New on object lists, and Edit and Delete on records. They filled in the form, and the server refused the save with `403`.
15+
- **Now.** A served row grants an operation only through `objectPermissionGrants` from `@objectstack/spec/security`, the fold the server's permission evaluator decides from. A bit grants only when it is `true`. Modify All Records also grants edit, delete and transfer, and View All Records also grants read. Nothing grants create but `allowCreate: true`. `check` also matches the object key case-insensitively, as `checkField` already did.
16+
- **Unchanged.** An anonymous session (`authenticated` not `true`) keeps its permissive default. A tree with no permission provider keeps the fail-open `can`. An object the response does not mention is still denied for a signed-in user. A row that carries the bit as `true` grants exactly as before.
17+
- **Who sees it.** A user whose permission sets grant read but not Create, Edit or Delete on an object. The console now hides or disables those actions, matching the server's answer. A user whose sets grant the bit sees no change.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/cli': patch
3+
'@objectstack/service-storage': patch
4+
---
5+
6+
fix(cli,service-storage): the Console document's `connect-src` names the store the server presigns browser uploads to
7+
8+
Clause-②: yes (widening)
9+
Metadata-protocol: none
10+
11+
With S3 or an S3-compatible store (R2, MinIO), the Console uploads a file by `PUT`ting it straight to the URL `POST /api/v1/storage/upload/presigned` answers, on the store's host. The Console document's content-security-policy did not name that host, so the browser refused every such upload with `TypeError: Failed to fetch`.
12+
13+
- **`@objectstack/service-storage`.** `S3StorageAdapter.getPresignedUploadOrigin()` answers the origin every presigned upload URL carries. It signs a probe upload with the same client options as every real presign, so a virtual-hosted bucket (`BUCKET.ENDPOINT-HOST`), a region-templated AWS host and a path-style endpoint come out exactly as the upload URL carries them. Signing is local and uses fixed probe credentials, so it never reaches the network or the AWS credential chain. `SwappableStorageService.getPresignedUploadOrigin()` answers the active adapter's origin, or `undefined` for an adapter without one (the local adapter's uploads go to the server itself).
14+
- **`@objectstack/cli`.** The Console mount reads that origin off the kernel's `storage` service on every document and adds it to `connect-src`, after the error-reporting origin and before `OS_CONSOLE_CSP_CONNECT_SRC`. A storage settings change that swaps the adapter is followed on the next document, with no restart. Local storage, no storage service, or a store whose endpoint the presigner refuses adds nothing. The last case logs one `warn` that names the remedy. `OS_CONSOLE_CSP_CONNECT_SRC` still adds an operator's extra sources, and a deployment that named its store there as a stop-gap can drop it.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/core': patch
3+
---
4+
5+
fix(core): one declaration serves both module formats, so the export map hands each condition its own `types`
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`@objectstack/core` used to emit two separate declarations of every class, `dist/index.d.ts` and `dist/index.d.cts`, and its export map served the ESM-kind `index.d.ts` to both conditions, so a TypeScript consumer resolving `require` was told the package is ESM (attw `FalseESM` on `.` and `./logger`). Serving the `.d.cts` to `require` was not possible while the two files declared `ObjectKernel` twice: its `private plugins` member makes the two copies incompatible types for any program that reaches core through both resolution modes.
11+
12+
Core's declaration build now runs once, for the ESM format. `dist/index.d.ts` and `dist/logger.d.ts` carry the declarations, as before, and `dist/index.d.cts` and `dist/logger.d.cts` are one-line re-exports of them, so both conditions hand out the same classes. `exports` now gives types per condition, `{ import: { types, default }, require: { types, default } }`, on both subpaths. No subpath is added or removed, no JavaScript file changes, and the `dist` file names are unchanged.
13+
14+
A CommonJS consumer compiling with `module: node16` (or, measured on TypeScript 5.6, under `nodenext` too) used to get TS1479 at its own `import ... from '@objectstack/core'`. That diagnostic now sits inside core's `.d.cts`, where `skipLibCheck` silences it. Nothing else for a consumer to do.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@objectstack/cli': patch
3+
---
4+
5+
fix(cli): a direct `os serve --dev` compiles the config before its dev artifact door serves it, so the served connector `baseUrl` is the one dispatch calls
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** `os serve --dev` over an existing `dist/objectstack.json` served that file through the dev artifact door as found, while dispatch read the config module it evaluated. A config value read from the environment, such as `examples/app-showcase`'s self URL built from `OS_PORT`, then differed between them. With `OS_PORT=41302` over an artifact built without it, `GET /api/v1/meta/connector/showcase_status_api` served `http://127.0.0.1:3000` while the connector called port 41302. `os dev` had the same split until it began compiling at boot.
11+
- **Now.** Before it composes that door, a direct dev boot compiles the config into the artifact in its own environment, using the same decision `os dev` uses (`devCompilesAtBoot`), so both copies come from one evaluation. A failed compile refuses the boot, as under `os dev`. This costs about 3 s per boot on the showcase.
12+
- Under `os dev`, the `serve` child keeps its parent's decision and never compiles again, so `os dev --no-compile` still boots the artifact as built. A dev boot with no artifact on disk composes no door and compiles nothing, as before. A pinned artifact (`OS_ARTIFACT_URL`) is never rebuilt.
13+
- Production `os serve` (no `--dev`, `NODE_ENV` not `development`) and `os start` are unchanged.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/components': patch
4+
'@object-ui/fields': patch
5+
'@object-ui/plugin-calendar': patch
6+
'@object-ui/plugin-charts': patch
7+
'@object-ui/plugin-chatbot': patch
8+
'@object-ui/plugin-dashboard': patch
9+
'@object-ui/plugin-editor': patch
10+
'@object-ui/plugin-form': patch
11+
'@object-ui/plugin-gantt': patch
12+
'@object-ui/plugin-grid': patch
13+
'@object-ui/plugin-kanban': patch
14+
'@object-ui/plugin-map': patch
15+
'@object-ui/plugin-markdown': patch
16+
'@object-ui/plugin-timeline': patch
17+
'@object-ui/plugin-tree': patch
18+
'@object-ui/plugin-view': patch
19+
'@object-ui/providers': patch
20+
'@object-ui/react': patch
21+
'@object-ui/react-runtime': patch
22+
'@object-ui/sdui-parser': patch
23+
'@object-ui/types': patch
24+
---
25+
26+
chore(ui): every ui package whose `test` script calls `vitest` now declares `vitest` as a devDependency, so `pnpm --filter @object-ui/PKG test` runs from the repository root
27+
28+
Clause-②: no
29+
Metadata-protocol: none
30+
31+
The package `test` scripts run `vitest run --root ../.. packages/PKG/`. A pnpm script resolves its binaries from the package's own `node_modules/.bin` and the workspace root's, and vitest was installed only under `ui/node_modules/.bin`, so 24 of the 41 ui packages with a `test` script failed with `vitest: not found` from both the repository root and `ui/`. Each of them now declares `vitest: ^4.1.10`, the range `ui/package.json` and the other 17 packages already declare; the lockfile resolves every new entry to the snapshot those packages already use, so no package gains a copy. Nothing a consumer installs changes: only the `devDependencies` field of the published `package.json` moves.

0 commit comments

Comments
 (0)