Skip to content

Commit f20d089

Browse files
committed
Merge main into the 2g PR 2 branch
Claude-Session: https://claude.ai/code/session_01Xe3LjBvLrRVA6dmFUEGskm Co-authored-by: Claude <noreply@anthropic.com>
2 parents 76d26ae + aec5f7b commit f20d089

182 files changed

Lines changed: 7255 additions & 3200 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: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
'@object-ui/react': patch
3+
'@object-ui/core': patch
4+
'@object-ui/data-objectstack': patch
5+
'@object-ui/app-shell': patch
6+
'@object-ui/plugin-designer': patch
7+
---
8+
9+
refactor(react): `AdapterCtx` / `useAdapter` are typed by `DataSource`, and `@object-ui/react` no longer depends on `@object-ui/data-objectstack`
10+
11+
Clause-②: yes (narrowing)
12+
Metadata-protocol: none
13+
14+
<!-- adr-0087: not-required (runtime-interface-only ui/packages/react/src/context/AppShellContext.tsx#ContextAdapter) No metadata moves: the narrowed type is the one `AdapterCtx` and `useAdapter()` carry, a runtime TypeScript type of a React context, with no Zod schema, spec declaration or stored row behind it, so there is nothing for objectstack migrate meta to rewrite; the compiler names the affected call site. -->
15+
16+
The runtime no longer depends on one adapter, so a bring-your-own backend installs `@object-ui/react` without the ObjectStack adapter, and learns at the type level which `DataSource` members it lacks.
17+
18+
- **`@object-ui/react`**: `AdapterCtx` and `useAdapter()` are typed by the protocol-level `DataSource` from `@object-ui/types`, not by `ObjectStackAdapter`. A new exported extension point, `AdapterContextRegister`, lets a host declare the concrete adapter its provider installs; `useAdapter()` then returns that adapter and an `AdapterCtx.Provider` value is checked against it. `ContextAdapter` names the resolved type. `@object-ui/data-objectstack` moves from `dependencies` to `devDependencies` (one boundary test still reads it).
19+
- **`@object-ui/core`**: exports `API_ACCESS_DENIED_CODES` and `isApiAccessDeniedError`, the `enable`-block API denial predicate, moved here unchanged so `@object-ui/react` can classify the denial without the adapter.
20+
- **`@object-ui/data-objectstack`**: re-exports both names from `@object-ui/core`; its published face is unchanged.
21+
- **`@object-ui/app-shell`**: declares `ObjectStackAdapter` on `AdapterContextRegister`, and the declaration ships in its published types, so a host that mounts `AdapterProvider` keeps today's `useAdapter()` type.
22+
- **`@object-ui/plugin-designer`**: declares the same adapter for its own pages, which read the ObjectStack client.
23+
24+
**BREAKING** (a type narrowing) for a TypeScript reader of `useAdapter()` / `AdapterCtx` from `@object-ui/react` that reads an `ObjectStackAdapter` member `DataSource` does not declare (`getClient`, `probeAppAccess`, `probeMetaItemAccess`, `getConnectionState`, `onConnectionStateChange`, `clearCache`, `queryDataset`, …) and does not import `@object-ui/app-shell`. Runtime behaviour is unchanged: the context holds the same adapter object as before.
25+
26+
- **Before.** `useAdapter()` returned `ObjectStackAdapter | null` whatever the host installed.
27+
- **Now.** It returns `DataSource | null` unless the host declares its adapter, and reading `getClient` off it is `error TS2339`.
28+
- **Fix.** Either declare the adapter your provider installs, once, in your own package:
29+
30+
```ts
31+
import type { ObjectStackAdapter } from '@object-ui/data-objectstack';
32+
33+
declare module '@object-ui/react' {
34+
interface AdapterContextRegister {
35+
adapter: ObjectStackAdapter;
36+
}
37+
}
38+
```
39+
40+
or narrow at the call site that needs the ObjectStack-only member, and keep reading `DataSource` members everywhere else.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
---
4+
5+
refactor(app-shell)!: retire the 54 root-entry exports that nothing outside the package names
6+
7+
`@object-ui/app-shell`'s root entry published 261 names. 57 of them appear in no file of this repository outside `ui/packages/app-shell/` (the measure covers `ui/`, `packages/` and `apps/`); three of those are taken by the package's own README examples and stay (`SignUpOffer`, `RuntimeBranding`, `studioOrgScopePath`). The other 54 leave the entry. Every module that defines them stays, and code inside the package keeps importing them from there; only the package entry stops publishing them. The entry now publishes 209 names (207, plus `InvitationSignUpPrompt` and `isDefaultProductName`, which main added meanwhile).
8+
9+
Clause-②: yes (narrowing)
10+
Metadata-protocol: none
11+
12+
<!-- adr-0087: not-required (runtime-export-retired @object-ui/app-shell#useExpressionContext, @object-ui/app-shell#UseUrlOverlayOptions, @object-ui/app-shell#UrlOverlayControls, @object-ui/app-shell#SettleSignalState, @object-ui/app-shell#isIdle, @object-ui/app-shell#ObjectUiGlobal, @object-ui/app-shell#ExpressionContextValue, @object-ui/app-shell#RecentItemInput, @object-ui/app-shell#RecentTextItem, @object-ui/app-shell#RecentItemLabelResolver, @object-ui/app-shell#isOverrideOnlyViewer, @object-ui/app-shell#actionAdmittedByOverride, @object-ui/app-shell#bypassedApproverNames, @object-ui/app-shell#DeclaredHomeApp, @object-ui/app-shell#AiSurfaceState, @object-ui/app-shell#PreviewBadgeProps, @object-ui/app-shell#RedirectWithSplashProps, @object-ui/app-shell#toastWithUndo, @object-ui/app-shell#ToastWithUndoOptions, @object-ui/app-shell#getCloudBase, @object-ui/app-shell#getBrandColor, @object-ui/app-shell#isRuntimeConfigInitialised, @object-ui/app-shell#RuntimeFeatures, @object-ui/app-shell#RecordFormPageProps, @object-ui/app-shell#DeclaredActionsBarProps, @object-ui/app-shell#addNavigationItem, @object-ui/app-shell#removeNavigationItems, @object-ui/app-shell#renameNavigationItems, @object-ui/app-shell#navigationEqual, @object-ui/app-shell#generateNavId, @object-ui/app-shell#CommandPaletteContextValue, @object-ui/app-shell#SignUpOfferContext, @object-ui/app-shell#HydratedUIMessage, @object-ui/app-shell#listAppComponents, @object-ui/app-shell#urlSegmentsToComponentRef, @object-ui/app-shell#AppComponentRegistryEntry, @object-ui/app-shell#MetadataPageShell, @object-ui/app-shell#listMetadataResources, @object-ui/app-shell#useMetadataTypes, @object-ui/app-shell#useTypesIndex, @object-ui/app-shell#matchesQuery, @object-ui/app-shell#MetadataPreviewProps, @object-ui/app-shell#MetadataInspectorProps, @object-ui/app-shell#StudioDesignSurfaceProps, @object-ui/app-shell#getStudioCanvasPreview, @object-ui/app-shell#listStudioCanvasPreviewTypes, @object-ui/app-shell#StudioObjectRecordsCanvas, @object-ui/app-shell#StudioCanvasPreview, @object-ui/app-shell#StudioCanvasPreviewProps, @object-ui/app-shell#useAssistant, @object-ui/app-shell#useRegisterAssistantEditor, @object-ui/app-shell#requestAssistantOpen, @object-ui/app-shell#AssistantEditorContext, @object-ui/app-shell#AssistantEditorField) No metadata moves: no spec key, authorable spelling, object definition or stored shape is added, removed, renamed or re-shaped, so there is nothing for `objectstack migrate meta` to rewrite. What retires is 54 names of `@object-ui/app-shell`'s published entry that no file in this repository outside the package names. The affected party is a TypeScript importer, and the compiler at that import delivers the break. -->
13+
14+
**BREAKING** (an export retirement), shipped as `patch`: no metadata protocol kind moves. An importer of a retired name gets `TS2305` ("has no exported member") at its import line.
15+
16+
Migration. A retired type that describes a name the entry still exports is derived from that name; everything else has no replacement on the entry.
17+
18+
- FROM `UseUrlOverlayOptions` TO `Parameters<typeof useUrlOverlay>[1]`.
19+
- FROM `UrlOverlayControls` TO `ReturnType<typeof useUrlOverlay>`.
20+
- FROM `SettleSignalState` TO `ReturnType<typeof useSettleSignal>`.
21+
- FROM `AiSurfaceState` TO `ReturnType<typeof useAiSurfaceEnabled>`.
22+
- FROM `CommandPaletteContextValue` TO `ReturnType<typeof useCommandPalette>`.
23+
- FROM `RecentItemInput` TO `Parameters<ReturnType<typeof useRecentItems>['addRecentItem']>[0]`.
24+
- FROM `RecentTextItem` TO `Extract<RecentItem, { label: string }>`.
25+
- FROM `RecentItemLabelResolver` TO `ReturnType<typeof useRecentItemLabel>`.
26+
- FROM `DeclaredHomeApp` TO `NonNullable<Parameters<typeof resolveDeclaredHomePath>[0]>[number]`.
27+
- FROM `RuntimeFeatures` TO `AppShellRuntimeConfig['features']`.
28+
- FROM `SignUpOfferContext` TO `Parameters<typeof decideSignUpOffer>[1]`.
29+
- FROM `HydratedUIMessage` TO `ReturnType<typeof toUIMessages>[number]`.
30+
- FROM `AppComponentRegistryEntry` TO `Parameters<typeof registerAppComponent>[0]`.
31+
- FROM `MetadataPreviewProps` TO `ComponentProps<MetadataPreview>` (`ComponentProps` from `react`).
32+
- FROM `MetadataInspectorProps` TO `ComponentProps<MetadataInspector>`.
33+
- FROM `StudioCanvasPreview` TO `Parameters<typeof registerStudioCanvasPreview>[1]`.
34+
- FROM `StudioCanvasPreviewProps` TO `ComponentProps<Parameters<typeof registerStudioCanvasPreview>[1]>`.
35+
- FROM `StudioDesignSurfaceProps` TO `ComponentProps<typeof StudioDesignSurface>`.
36+
- FROM `PreviewBadgeProps` TO `ComponentProps<typeof PreviewBadge>`.
37+
- FROM `RedirectWithSplashProps` TO `ComponentProps<typeof RedirectWithSplash>`.
38+
- FROM `RecordFormPageProps` TO `ComponentProps<typeof RecordFormPage>`.
39+
- FROM `DeclaredActionsBarProps` TO `ComponentProps<typeof DeclaredActionsBar>`.
40+
- FROM `useExpressionContext`, `ExpressionContextValue`, `isIdle`, `ObjectUiGlobal`, `isOverrideOnlyViewer`, `actionAdmittedByOverride`, `bypassedApproverNames`, `toastWithUndo`, `ToastWithUndoOptions`, `getCloudBase`, `getBrandColor`, `isRuntimeConfigInitialised` TO nothing (no replacement on the entry).
41+
- FROM `addNavigationItem`, `removeNavigationItems`, `renameNavigationItems`, `navigationEqual`, `generateNavId` TO nothing (no replacement on the entry).
42+
- FROM `listAppComponents`, `urlSegmentsToComponentRef` TO nothing (no replacement on the entry): `getAppComponent` and `componentRefToUrlSegments` stay.
43+
- FROM `MetadataPageShell`, `listMetadataResources`, `useMetadataTypes`, `useTypesIndex`, `matchesQuery` TO nothing (no replacement on the entry): `getMetadataResource`, `useMetadataClient` and `useGlobalDiagnostics` stay.
44+
- FROM `getStudioCanvasPreview`, `listStudioCanvasPreviewTypes`, `StudioObjectRecordsCanvas` TO nothing (no replacement on the entry): `registerStudioCanvasPreview` stays.
45+
- FROM `useAssistant`, `useRegisterAssistantEditor`, `requestAssistantOpen`, `AssistantEditorContext`, `AssistantEditorField` TO nothing (no replacement on the entry): `assistantBus` stays.
46+
47+
`ui/docs/app-shell-extraction-guide.md`, the package's architecture guide, is rewritten to describe the package that ships: what each directory holds, the groups of names the entry publishes, its load-time registrations, its dependencies, and its three consumers in this repository.

‎.changeset/25959-activity-fold.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@object-ui/types': patch
3+
'@object-ui/plugin-detail': patch
4+
'@object-ui/i18n': patch
5+
---
6+
7+
feat(plugin-detail): the record activity timeline folds consecutive updates by one actor into one row with a count
8+
9+
Clause-②: yes
10+
Metadata-protocol: none
11+
12+
- **Before.** Every `sys_activity` update was its own feed row. When a flow, a rollup or a seed updated a record repeatedly, the system's rows buried the people's comments: a demo campaign showed thirteen consecutive "System · Updated Campaign …" rows in its discussion feed.
13+
- **Now.** `RecordActivityTimeline` folds a run of adjacent `updated` entries by the same actor (`actor_id`, `null` for the system, and the same display name), each no more than 10 minutes after the one before it, into one row. The row shows the actor, the time of the latest entry, the entry count and the union of the changed fields, by their field labels where the page has the object schema and by machine name otherwise (`Updated Number Sent, Leads, Responses · 13 updates`). A toggle (`aria-expanded`) expands it to every entry.
14+
- **Rendering only.** No entry is dropped or merged in the data, and the panel heading still counts every entry. Create, delete, assign, share, comment, completed and milestone rows never fold, and any of them between two updates ends the run. An entry without a summary still folds and still counts. Field names come from `sys_activity.changed_fields`, never from the recorded values.
15+
- **`FeedItem` (`@object-ui/types`).** Four new optional members, set by `activityRowToFeedItem`: `activityType` (the row's own `sys_activity.type`), `activityKind` (`metadata.kind`, e.g. `'milestone'`), `actorId` and `changedFields`. A host item without them renders as before and never folds.
16+
- **Strings.** `detail.foldedUpdates`, `detail.foldedUpdateCount`, `detail.foldedMoreFields`, `detail.expandFolded` and `detail.collapseFolded`, in all ten packs.
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+
refactor(app-shell): the Console `ObjectView`'s identity import and record navigation move into sibling hooks
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
Two concerns move verbatim out of `ObjectViewInner` in `views/ObjectView.tsx`. The identity-import gate, its password-policy state and data source, and the managed-environment Users marker move into `useObjectViewIdentity` in `objectViewIdentity.tsx`. The record navigation overlay (how a row opens its record, the drawer's `?recordId=` URL state, "expand to full page" and the list's record links) moves into `useObjectViewRecordNavigation` in `objectViewRecordNavigation.ts`. The component passes every input in and reads the returned values. No logic, name or rendered output changes, and the package entry is unchanged.
11+
12+
What to do: nothing.
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+
refactor(app-shell): the Console `ObjectView`'s realtime refresh and record-count fetch move into a sibling hook
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The realtime subscription that refreshes the list when the server reports a change, the reconnection conflict resolution, and the footer's `$top: 0` record-count fetch move verbatim out of `ObjectViewInner` in `views/ObjectView.tsx` into `useObjectViewLiveData` in `objectViewLiveData.ts` beside it. The component calls the hook at the same place, so the hook order is unchanged; the record-count state stays in the component and is passed in with the other inputs. No logic, name or rendered output changes, and the package entry is unchanged.
11+
12+
What to do: nothing.
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+
refactor(plugin-grid): `ObjectGrid`'s module-level helpers move into sibling modules
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The query helpers (the held filter resolution, the record-source ladder, the find-params signature), the column helpers and column emit types, the record-link cell, the English fallback translations, the display defaults (row height, page sizes, selection default) and the row-version helpers move verbatim out of `ObjectGrid.tsx` into six modules beside it. No logic, name or rendered output changes. `ObjectGrid.tsx` re-exports every name it exported before, and the package entry is unchanged.
11+
12+
What to do: nothing.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@object-ui/plugin-grid': patch
3+
---
4+
5+
refactor(plugin-grid): `ObjectGrid`'s host-facing props types and its footer-summary reporter move into sibling modules
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`ObjectGridColumnState`, `ObjectGridFooterSummary` and `ObjectGridExternalPaginationProps` move verbatim out of `ObjectGrid.tsx` into `objectGridProps.ts`, and the renderless `FooterSummaryReport` moves into `objectGridFooterSummaryReport.tsx`. No logic, name or rendered output changes. `ObjectGrid.tsx` re-exports the three types, so every name it exported before is still importable from the same path, and the package entry is unchanged.
11+
12+
Correction to the slice 1 note: the record-source ladder (`getDataConfig`) stayed in `ObjectGrid.tsx`; it moved in neither slice.
13+
14+
What to do: nothing.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@objectstack/plugin-audit': patch
3+
---
4+
5+
fix(plugin-audit): a member removes their own comment reaction through an own-row unreact door, with no object delete grant
6+
7+
A member whose grants are the every-member ones could add a comment reaction and never take it back. The console removes a reaction through the generic data door's `DELETE /api/v1/data/sys_comment_reaction/:id`, which needs an object-level `delete` grant, and no every-member set may carry one: ADR-0090 D5 keeps `delete` off the `everyone` anchor (`security-anchor-high-privilege`), and `member_default` carries none. Measured as a plain member: add a reaction → `201`, delete it → `403`.
8+
9+
Clause-②: yes (widening)
10+
Metadata-protocol: none
11+
12+
`AuditPlugin` now mounts `DELETE /api/v1/comments/reactions/:id` on the kernel's HTTP server. It removes the caller's own `sys_comment_reaction` row and nothing else:
13+
14+
- The row is read as the caller, so every read gate decides. A reaction on a comment the caller cannot read answers `404 RECORD_NOT_FOUND`, the same answer as an id that names no row, so the door says nothing about a reaction the caller cannot see.
15+
- The row's reactor (`created_by`, the column the platform's own-record floor reads) must be the caller. Anyone else's reaction is refused with `403 PERMISSION_DENIED` and stays.
16+
- Only then is the row removed, under a system context attributed to the caller and kept in the caller's organization. Success answers `200` with `{ success: true, data: { id, deleted: true } }`.
17+
- Before any of that the door answers what every door answers: `401 UNAUTHENTICATED` for a request that names nobody, a held sign-in's own `403`, the permission store's `503` when it cannot be read, and `400 VALIDATION_ERROR` for any query parameter.
18+
19+
The grant model does not move. The generic data-door delete answers exactly what it answered before (`403` for a member with no delete grant), no permission set gains `delete`, and `member_default` and the `security-anchor-high-privilege` rule are unchanged. The console's reaction chip moves to the new route in a separate change; until then the route is reachable but nothing in the console calls it.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
---
4+
5+
fix(app-shell): the Delegated Admin Scope block lists the companion grants a delegate needs, and grants the missing ones in one click
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
A permission set's admin scope says what its holder may administer; it does not let the holder in. A delegate also needs the `setup.access` capability to enter Setup, read on `sys_user`, `sys_business_unit` and `sys_business_unit_member` so the lookups can pick people and units, and create, read, edit and delete on `sys_user_position` for the assignment rows. The scope block in the permission set editor now lists these five once the scope names a business unit, and marks each one as granted or not by the set being edited.
11+
12+
"Grant the missing ones" adds only what is missing to the same set, in one edit that the set's normal Save persists: `setup.access` joins `systemPermissions`, and each object row gains only the flags it lacks. No grant the set already carries is removed or narrowed, and no new key is written. Under a package (the Studio Access pillar) the list is shown without the action, with a note that these grants belong on an environment permission set in Setup: the package editor saves only the package's own objects.
13+
14+
What to do: nothing. A set's stored shape is unchanged.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/metadata-core': patch
3+
'@objectstack/service-cluster': patch
4+
'create-objectstack': patch
5+
---
6+
7+
fix(metadata-core,service-cluster,create-objectstack): ESM-only subpaths declare their types under `import`
8+
9+
Clause-②: no
10+
Metadata-protocol: none
11+
12+
`@objectstack/metadata-core/testing`, `@objectstack/service-cluster/testing` and `create-objectstack/created-summary` are ESM-only, and now say so in their export maps: the subpath's `types` moved inside its `import` condition. `require()` of these subpaths already failed at runtime with `ERR_PACKAGE_PATH_NOT_EXPORTED`. Before this change, TypeScript under `node16`/`nodenext` resolved their declarations for a CommonJS caller anyway (attw `CJSResolvesToESM`). Now it resolves them for `import` only, which is what Node does. ESM callers, and every resolution mode that reads the `import` condition, see no change. Each package's README now says the subpath is ESM-only.

0 commit comments

Comments
 (0)