Skip to content

Commit 26b58f0

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-p301jn
# Conflicts: # packages/lint/src/lint-liveness-properties.test.ts # ui/packages/plugin-report/src/DatasetReportRenderer.tsx
2 parents 858f0fe + 3346da2 commit 26b58f0

569 files changed

Lines changed: 23669 additions & 8352 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+
'@object-ui/plugin-report': patch
3+
'@object-ui/i18n': patch
4+
---
5+
6+
feat(plugin-report): a report that declares `dateRange` draws a period chip and runs every query inside the chosen window
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
`ReportSchema.dateRange` (`{ dimension?, defaultRange, allowCustomRange }`) was accepted but read by nothing, so a report showed every period whatever it declared. The report page now reads it:
12+
13+
- **The chip.** A non-joined report that declares `dateRange` draws a date-range chip above its body: All time, the dashboard's presets under the dashboard's labels, and Custom… (two date inputs) unless `allowCustomRange: false`. It opens on `defaultRange`, or `this_month` when that is absent; `custom` opens the custom pick with no window until both ends are named.
14+
- **The window.** The active window rides every dataset query the report makes (the grouped table, the matrix cross-tab and the embedded chart) as one `selection.timeDimensions` entry `{ dimension, dateRange }` with no `granularity`: a preset goes by name, a custom pick as `[start, end]`, and All time sends no entry. The server ANDs it with `runtimeFilter` and the dataset's own filter. A new pick re-queries.
15+
- **The dimension.** A declared `dimension` is sent as written. Omitted, the renderer reads the dataset definition before any query and uses its one `type: 'date'` dimension; a dataset with none or several draws that authoring error in place of the report and queries nothing.
16+
- **Unchanged without it.** A report that does not declare `dateRange` draws no chip, reads no dataset definition and posts exactly the selection it posted before. A `joined` report (where the spec refuses `dateRange`) is not affected.
17+
18+
`@object-ui/i18n` gains `report.dateRange.from`, `report.dateRange.to`, `report.dateRange.datasetUnreadable` and `report.dateRange.noSingleDateDimension` in all ten locale packs; the preset labels reuse `dashboard.filters.*`.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/i18n': patch
3+
---
4+
5+
feat(i18n): the translation preview's strings move into the `studio` namespace
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Last rows of the move ruled on objectstack#25922** (6431032267, Q2 = A; kept by 6431234244). The 23 `engine.translationPreview.*` rows in each language, the last family left in `@object-ui/app-shell`'s private designer table, now live in `@object-ui/i18n/namespaces/studio`, text unchanged. The table is now empty; deleting it is a later slice. Studio reads the same strings; a Chinese viewer sees the translation preview's labels in English for the moment the `zh` catalogue takes to load, as with the earlier slices. No export changes.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
'@object-ui/plugin-calendar': patch
3+
'@object-ui/plugin-gantt': patch
4+
'@objectstack/spec': patch
5+
'@object-ui/types': patch
6+
'@object-ui/plugin-list': patch
7+
'@object-ui/plugin-view': patch
8+
---
9+
10+
refactor(plugin-calendar,plugin-gantt)!: the calendar and gantt renderers read their binding from the `calendar` / `gantt` block only; the flat fallback read is retired (#25923)
11+
12+
Clause-②: no (narrowing)
13+
Metadata-protocol: none
14+
15+
<!-- adr-0087: not-required (no-migration-prescription) No `@objectstack/spec` key, accepted value, stored row or stored shape moves. The spec's `ComponentPropsMap['object-calendar']` and `['object-gantt']` rows already refuse every flat key this change stops reading, `@object-ui/types` stopped declaring them in the earlier slice of #25923, and both producers in the console (`ListView` and `ObjectView`) already write the block, so no metadata the spec accepts reaches the retired read. A stored-row census over every metadata source this repository reaches (examples, apps, the console's shipped metadata, seeds, fixtures, platform-objects) found no stored view or node that writes the binding flat, so `objectstack migrate meta` has nothing to rewrite. What narrows is the drawing outcome for a host that mounts `ObjectCalendar` or `ObjectGantt` directly with a flat node: it now reaches the refusal screen. The `@objectstack/spec` half is prose only: the two refusals no longer say the renderer still reads the flat spelling. The other categories are closed on facts: the packages publish (not unpublished); no ADR-0087 id covers this change (not registered or already-registered); the narrowed type members are package-local hand-off types that neither package's entry exports (not type-surface-only or runtime-export-retired). -->
16+
17+
`ObjectCalendar` and `ObjectGantt` kept a second, flat reading of their field binding for a host that mounts them directly with the keys written on the node. The spec refuses those keys on the node, `@object-ui/types` no longer declares them, and no producer writes them, so that read was a second dialect nothing should reach. It is retired.
18+
19+
- **`@object-ui/plugin-calendar`**: `getCalendarConfig` reads the `calendar` block only. A node that writes `startDateField`, `endDateField`, `titleField`, `colorField` or `allDayField` flat, with no block, shows the "Calendar configuration required" screen, whose hint names the `calendar` block. The package-local `ObjectCalendarFlatFields` and `ObjectCalendarHandoff` types are gone, and `ObjectCalendarProps['schema']` is the published `ObjectCalendarSchema`.
20+
- **`@object-ui/plugin-gantt`**: `getGanttConfig` reads the `gantt` block only. A node that writes its `GanttConfig` keys flat, with no block, shows the "Gantt configuration required" screen, which now names the `gantt` block, and in development a warning names every flat key the chart did not read, with or without a block beside it. The legacy singular `dependencyField` is read nowhere (the spec spells it `dependenciesField`, inside the block). The package-local `ObjectGanttFlatFields` type is gone; `ObjectGanttProps['schema']` keeps the two search keys `ListView` writes at runtime, now on `ObjectGanttSearchFields`.
21+
- **`@objectstack/spec`**: the `object-calendar` and `object-gantt` flat-key refusals no longer say the renderer still reads the flat spelling. The prescription is unchanged: write the keys in the `calendar` / `gantt` block.
22+
23+
**Supersedes, in the same release:** `25923-calendar-gantt-flat-keys-handoff.md` (the earlier slice of #25923) says the calendar and gantt renderers are unchanged and still read the flat spelling a host hands them, that `ObjectCalendarProps['schema']` and `ObjectGanttProps['schema']` carry the flat keys, and that the spec refusals call the flat spelling a fallback the renderer still reads. This change retires that read, so those sentences no longer hold: the renderers read the block only, the props types carry no flat binding key, and the refusals no longer say it.
24+
25+
**BREAKING** for code that mounts `<ObjectCalendar>` or `<ObjectGantt>` with a flat node.
26+
27+
- **Before.** `<ObjectGantt schema={{ type: 'object-gantt', objectName: 'task', startDateField: 's', endDateField: 'e' }} />` drew a chart, and the flat keys compiled against `ObjectGanttProps['schema']`.
28+
- **Now.** The flat keys are a compile error on the props type, and the node shows the configuration-required screen.
29+
- **Fix.** Write the binding in the block: `{ type: 'object-gantt', objectName: 'task', gantt: { startDateField: 's', endDateField: 'e', titleField: 'name' } }` (the `gantt` block requires `titleField`; the flat branch used to floor it at `'name'`), and `{ type: 'object-calendar', calendar: { startDateField: 'start' } }` for a calendar.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
docs(spec): the `TranslationDataSchema` TSDoc counts sixteen per-app translation groups, not fifteen
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The comment on `TranslationDataSchema` said "Fifteen groups" after the `hooks` and `books` groups both landed; the per-app bundle (`appTranslationDataShape()`) has sixteen. Comment only: no schema, describe, refusal message or generated artifact changes.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@object-ui/cli': patch
3+
'@object-ui/create-plugin': patch
4+
---
5+
6+
chore(scaffold): generated apps and plugins declare the dev ranges this repository now builds with
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
The workspace moved `react-router-dom` to `^7.18.4` and `vite-plugin-dts` to `^5.1.2`. A routed app scaffolded by `@object-ui/cli` now declares `react-router-dom` `^7.18.4`, and a plugin scaffolded by `@object-ui/create-plugin` now declares `vite-plugin-dts` `^5.1.2`, so a new project starts on the same versions the in-repo packages are tested on.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/client': minor
3+
---
4+
5+
feat(client): `data.fieldAccess(object, id)` reads the field projection of one record
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: added
9+
10+
`client.data.fieldAccess(object, id)` calls `GET /data/:object/:id/field-access` and resolves to `GetRecordFieldAccessResponse`: the fields the caller may read, and the fields the write gate admits on an update of that stored row. A field permission whose `when` names only some rows is unlocked on the rows it does not name. A row that is absent or unreadable rejects with the record read's 404.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/rest': minor
3+
---
4+
5+
feat(rest): `GET /data/:object/:id/field-access` serves the field projection of one record
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: added
9+
10+
A record's edit form can ask which fields it may write on THAT record: the route answers `{ success: true, data: { object, recordId, readable, editable } }` (`GetRecordFieldAccessResponseSchema`), with `editable` judged on the stored row by the write gate through the security service's `getRecordFieldAccess`. The record is read first exactly as `GET /data/:object/:id` reads it, so an absent row or one the caller cannot read answers that route's 404 and the projection discloses nothing more. The route takes no query parameter (a located 400 names any). Without a security service that offers the method it answers 501, and `/auth/me/permissions` remains the row-less reader.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/plugin-security': minor
3+
---
4+
5+
feat(plugin-security): `getRecordFieldAccess` — a record's field projection from the write gate's own verdict
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: added
9+
10+
The `security` service answers `getRecordFieldAccess(object, recordId, context)`. It reads the row as the caller reads it (the reader the write gate's pre-image comes from) and asks the field-level write gate itself, `computeForbiddenFieldWrites`, for an update of that row naming every field, over the same permission sets, posture and delegator intersection the gate takes. A field permission whose `when` names only some rows therefore locks the field only on those rows, and the answer cannot disagree with a write of that row. A row that is absent or unreadable answers `null`. `getWritableFields` and every other row-less reader are unchanged and still apply each row-conditioned entry as its lock.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
feat(spec): a record-scoped field projection, judged on the stored row by the write gate
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: added
9+
10+
`GetRecordFieldAccessResponseSchema` (`@objectstack/spec/api`) declares the answer of `GET /api/v1/data/{object}/{id}/field-access`: `{ object, recordId, readable, editable }`, the field projection of ONE record. `ISecurityService` declares its source as the optional `getRecordFieldAccess(object, recordId, context)`: `editable` is the write gate's own verdict on an update of the stored row, so a field permission whose `when` names only some rows locks the field only on those rows, and `readable` is the row-blind read projection. `null` means the caller has no row there (absent or unreadable, deliberately the same answer); `undefined` means no answer.
11+
12+
The `FieldPermissionSchema.when` description now names that reader: the row-less readers (`/auth/me/permissions`, the object-wide field projections, the write preview on an update) keep reporting a row-conditioned entry as its lock, and the record-scoped projection judges it on the row. Nothing an author writes changes meaning.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
'@object-ui/components': patch
3+
'@object-ui/app-shell': patch
4+
'@object-ui/plugin-detail': patch
5+
'@object-ui/plugin-list': patch
6+
'@object-ui/react': patch
7+
'@object-ui/i18n': patch
8+
---
9+
10+
feat(app-shell,components): the record drawer opens a related row or a lookup in place, one level at a time, and its bar names no object
11+
12+
Clause-②: no
13+
Metadata-protocol: none
14+
15+
The console's record drawer is one drawer (objectstack#26679):
16+
17+
- **The bar.** Its left side is empty at the root: no object name and no "n / N" position. Its right side holds `↑` `↓` (previous / next through the level's sibling set, disabled at the ends; `↑` / `↓` and `k` / `j` while no text field, menu or grid has the keyboard), `⤢` (icon only) and `✕`. `NavigationOverlay` gains three optional props for it — `onBack`, `trail` and `stepper` — and keeps its title as the accessible name only, on every host.
18+
- **The root steps through the list.** `ListView` reports the rows it shows, in their order, through a new `onRowsChange` host prop (beside `onTotalChange`, answered by a paged grid; a board or a grouped grid reports no rows). The object page hands them to its drawer, so `↑` `↓` at the root move to the previous / next row, disabled at the first and the last.
19+
- **In place.** A related row, a lookup value or any record link inside the drawer opens that record in the same drawer and keeps the previous one on a stack. The bar shows `←` and the path (each ancestor returns to its level); `←` or `Esc` goes back one level, `Esc` at the root closes, `✕` closes the whole stack. `↑` `↓` one level down step through the rows the parent's related list showed, never across levels. Each level keeps its own `?tab=`. An object that declares `navigation.mode: 'page'` still opens its page.
20+
- **⤢ at any level** opens that record's page with the levels below it as its `?from=` trail, so its back link reads the record it was opened from; the page offers "Collapse to drawer", which returns to the drawer with the stack restored.
21+
- **On the record page**, a related row or a lookup opens its record in a drawer over the page, under the same rules.
22+
- **`data-table` inside a dialog.** A table drawn inside a dialog now opens its rows: the row click ignored every click with a dialog ancestor, so a related list inside a record drawer drew pointer rows that opened nothing. Only a dialog portalled out of the row (one that does not contain it) still stops the click.
23+
- **What a reload keeps.** The root stays `?recordId=` on an object list; the levels above it live in memory, so a reload shows the root record.
24+
25+
Published-type additions, all optional: `RelatedRecordHandlers.onView` receives the ids of the rows on show as a third argument, and `RelatedList`'s `onRowClick` receives those rows as its third argument. The i18n packs gain `detail.collapseToDrawer` in all ten languages. In zh, the `⤢` label (`detail.openAsFullPage` and `console.objectView.expandToPage`, kept byte-identical) reads 整页打开.

0 commit comments

Comments
 (0)