@fusebase/fusebase-gate-sdk 2.5.4 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apis/AppMagicLinksApi.d.ts +1 -1
- package/dist/apis/AppMagicLinksApi.js +1 -1
- package/dist/apis/IsolatedStoresApi.d.ts +1 -1
- package/dist/apis/IsolatedStoresApi.js +1 -1
- package/dist/apis/PortalsApi.d.ts +484 -13
- package/dist/apis/PortalsApi.js +536 -15
- package/dist/types/app-magic-link/app-magic-link.d.ts +17 -5
- package/dist/types/portals/portals.d.ts +1050 -59
- package/dist/types/portals/portals.js +51 -1
- package/package.json +1 -1
- package/release-notes/2.5.4.md +940 -2
- package/release-notes/2.6.1.md +9 -0
- package/release-notes/latest.md +3 -3
- package/release-notes/2.5.1-sdk.3.md +0 -9
package/release-notes/2.5.4.md
CHANGED
|
@@ -2,8 +2,946 @@
|
|
|
2
2
|
|
|
3
3
|
- Current ref: `HEAD`
|
|
4
4
|
- Previous tag: `v2.5.4`
|
|
5
|
-
- Generated at: 2026-08-
|
|
5
|
+
- Generated at: 2026-08-11T05:32:13.369Z
|
|
6
6
|
|
|
7
7
|
## Included Drafts
|
|
8
8
|
|
|
9
|
-
-
|
|
9
|
+
- `docs/release-notes/2026-07-09-portal-homepage.md` - Portal homepage settings (NIM-42205)
|
|
10
|
+
- `docs/release-notes/2026-08-03-isolated-sql-read-only-transaction.md` - Read-only isolated SQL is enforced by Postgres
|
|
11
|
+
- `docs/release-notes/2026-08-05-add-portal-chats-dashboard-menu-item.md` - addPortalChatsDashboardMenuItem — add a Chat dashboard navigation item (NIM-43021)
|
|
12
|
+
- `docs/release-notes/2026-08-05-add-portal-files-dashboard-menu-item.md` - addPortalFilesDashboardMenuItem — add a Files dashboard navigation item (NIM-43020)
|
|
13
|
+
- `docs/release-notes/2026-08-05-add-portal-tasks-dashboard-menu-item.md` - addPortalTasksDashboardMenuItem — add a Tasks dashboard navigation item (NIM-43022)
|
|
14
|
+
- `docs/release-notes/2026-08-05-remove-add-portal-block.md` - addPortalBlock removed from Fusebase Gate (NIM-43018)
|
|
15
|
+
- `docs/release-notes/2026-08-06-chats-dashboard-menu-item-duplicates.md` - addPortalChatsDashboardMenuItem — duplicate items allowed (NIM-43021)
|
|
16
|
+
- `docs/release-notes/2026-08-06-portal-block-discovery-move-delete.md` - Portal block discovery, move and delete (NIM-43062)
|
|
17
|
+
- `docs/release-notes/2026-08-06-portal-block-read-and-note-update.md` - Read a portal block, and edit a note-backed one (NIM-43065)
|
|
18
|
+
- `docs/release-notes/2026-08-06-portal-content-permissions.md` - Portal page and folder permissions (NIM-43064)
|
|
19
|
+
- `docs/release-notes/2026-08-06-portal-text-media-block-update.md` - Edit portal Heading, Simple text and Image blocks (NIM-43065)
|
|
20
|
+
- `docs/release-notes/2026-08-10-portal-ai-agent-block-update.md` - Summary
|
|
21
|
+
- `docs/release-notes/2026-08-10-portal-app-block-update.md` - Summary
|
|
22
|
+
- `docs/release-notes/2026-08-10-portal-bpmn-diagram-block-update.md` - Summary
|
|
23
|
+
- `docs/release-notes/2026-08-10-portal-card-group-block-update.md` - Card group block update
|
|
24
|
+
- `docs/release-notes/2026-08-10-portal-carousel-block-update.md` - Edit portal Carousel blocks (NIM-43065)
|
|
25
|
+
- `docs/release-notes/2026-08-10-portal-chat-block-update.md` - Summary
|
|
26
|
+
- `docs/release-notes/2026-08-10-portal-countdown-block-update.md` - Summary
|
|
27
|
+
- `docs/release-notes/2026-08-10-portal-custom-widget-block-update.md` - Custom widget block update
|
|
28
|
+
- `docs/release-notes/2026-08-10-portal-dynamic-table-block-update.md` - Dynamic Table block update
|
|
29
|
+
- `docs/release-notes/2026-08-10-portal-file-uploader-block-update.md` - Summary
|
|
30
|
+
- `docs/release-notes/2026-08-10-portal-form-block-update.md` - Form block update
|
|
31
|
+
- `docs/release-notes/2026-08-10-portal-html-css-block-update.md` - Summary
|
|
32
|
+
- `docs/release-notes/2026-08-10-portal-info-block-update.md` - Info block update
|
|
33
|
+
- `docs/release-notes/2026-08-10-portal-linked-block-update.md` - Linked block update
|
|
34
|
+
- `docs/release-notes/2026-08-10-portal-recent-files-block-update.md` - Recent files block update
|
|
35
|
+
- `docs/release-notes/2026-08-10-portal-timeline-block-update.md` - Summary
|
|
36
|
+
- `docs/release-notes/2026-08-11-portal-style.md` - Portal branding (NIM-42204)
|
|
37
|
+
|
|
38
|
+
## Summary
|
|
39
|
+
|
|
40
|
+
### Portal homepage settings (NIM-42205)
|
|
41
|
+
|
|
42
|
+
Gate now exposes `updatePortalHomepage`, letting CLI/SDK/MCP agents configure a portal's Homepage settings — the same panel the customizer shows. The change is staged in the portal's customizer draft (not published immediately), so the owner reviews it under Homepage settings before publishing.
|
|
43
|
+
|
|
44
|
+
### Read-only isolated SQL is enforced by Postgres
|
|
45
|
+
|
|
46
|
+
`queryIsolatedStoreSql` decided "read-only" from the SQL text: the leading command plus a keyword scan for data-modifying CTEs. A `SELECT` over a volatile function that writes passed that check, and the scanner's lexer disagreed with Postgres about where an escape-string literal ends, which let a real data-modifying CTE through. The read path now opens its transaction with `BEGIN READ ONLY`, so the server rejects every write, including one made inside a function body. The keyword scan stays as an early, friendlier error.
|
|
47
|
+
|
|
48
|
+
### addPortalChatsDashboardMenuItem — add a Chat dashboard navigation item (NIM-43021)
|
|
49
|
+
|
|
50
|
+
New public operation `addPortalChatsDashboardMenuItem`
|
|
51
|
+
(`POST /:orgId/portals/:portalId/chats-dashboard`, `portals.write`) adds a root
|
|
52
|
+
navigation item pointing at the portal's system Chat dashboard — the
|
|
53
|
+
programmatic equivalent of the Customizer's root **Dashboards > Chat dashboard**
|
|
54
|
+
action.
|
|
55
|
+
|
|
56
|
+
The dashboard page at `/chats-dashboard` and its canonical `chats-dashboard`
|
|
57
|
+
block are created with the portal (and backfilled on older portals by
|
|
58
|
+
`chatsDashboardMigration`), so this operation **never creates a page or a
|
|
59
|
+
block**. A portal without that page is inconsistent, not a portal that needs a
|
|
60
|
+
second dashboard: the request returns 409 and stages nothing.
|
|
61
|
+
|
|
62
|
+
`addPortalChatBlock` still owns the chat WIDGET (a channel or DM block on an
|
|
63
|
+
existing page). The dashboard selects no chat entity, so no channel or user
|
|
64
|
+
discovery is involved.
|
|
65
|
+
|
|
66
|
+
Gate is a thin semantic passthrough: exactly one portal-service call, and it
|
|
67
|
+
builds no menu item, id, index or change event. The page lookup, the per-bar
|
|
68
|
+
duplicate check, the canonical item shape, the atomic append and its optimistic
|
|
69
|
+
concurrency retry all live in portal-service.
|
|
70
|
+
|
|
71
|
+
### addPortalFilesDashboardMenuItem — add a Files dashboard navigation item (NIM-43020)
|
|
72
|
+
|
|
73
|
+
New public operation `addPortalFilesDashboardMenuItem`
|
|
74
|
+
(`POST /:orgId/portals/:portalId/files-dashboard`, `portals.write`) adds a root
|
|
75
|
+
navigation item for the portal's system Files dashboard, staged in the
|
|
76
|
+
Customizer draft. If the portal has no `/files-dashboard` page yet, the page and
|
|
77
|
+
its canonical `files-dashboard` block are created in the same draft batch; if it
|
|
78
|
+
already has one, only the item is added, so several bars can link to the same
|
|
79
|
+
dashboard. The dashboard renders the portal's full File Manager, so it is a
|
|
80
|
+
system page — not a block an agent can place on a page of its choice.
|
|
81
|
+
`addPortalRecentFilesBlock` and `addPortalFileUploaderBlock` still own the
|
|
82
|
+
latest-files list and the upload area on existing pages.
|
|
83
|
+
|
|
84
|
+
This replaces the unreleased `createPortalFileDashboard` operation, which
|
|
85
|
+
answered `409 alreadyExists` for a portal that already had the page and created
|
|
86
|
+
no navigation item. Every portal gets its `/files-dashboard` page at setup, so
|
|
87
|
+
that op was unusable for its actual purpose: putting a Files entry in a bar.
|
|
88
|
+
|
|
89
|
+
Gate is a thin semantic passthrough: exactly one portal-service call, no draft
|
|
90
|
+
read, no page/block/id/change-event construction. The dashboard lookup, the
|
|
91
|
+
canonical page, item and block, the atomic batch and its optimistic-concurrency
|
|
92
|
+
retry all live in portal-service.
|
|
93
|
+
|
|
94
|
+
### addPortalTasksDashboardMenuItem — add a Tasks dashboard navigation item (NIM-43022)
|
|
95
|
+
|
|
96
|
+
New public operation `addPortalTasksDashboardMenuItem`
|
|
97
|
+
(`POST /:orgId/portals/:portalId/tasks-dashboard`, `portals.write`) adds a root
|
|
98
|
+
navigation item for the portal's system Tasks dashboard, staged in the
|
|
99
|
+
Customizer draft. If the portal has no `/tasks-dashboard` page yet, the page and
|
|
100
|
+
its canonical `tasks-dashboard` block are created in the same draft batch; if it
|
|
101
|
+
already has one, only the item is added, so several bars can link to the same
|
|
102
|
+
dashboard. The dashboard shows each viewer the portal tasks they are allowed to
|
|
103
|
+
access, so it is a system page — not a block an agent can place on a page of its
|
|
104
|
+
choice. `addPortalTaskListBlock` and `addPortalKanbanBlock` still own the task
|
|
105
|
+
list and board blocks on existing pages.
|
|
106
|
+
|
|
107
|
+
Gate is a thin semantic passthrough: exactly one portal-service call, no draft
|
|
108
|
+
read, no page/block/id/change-event construction. The dashboard lookup, the
|
|
109
|
+
canonical page, item and block, the atomic batch and its optimistic-concurrency
|
|
110
|
+
retry all live in portal-service.
|
|
111
|
+
|
|
112
|
+
### addPortalBlock removed from Fusebase Gate (NIM-43018)
|
|
113
|
+
|
|
114
|
+
The generic operation `addPortalBlock`
|
|
115
|
+
(`POST /:orgId/portals/:portalId/blocks`, `portals.write`) is **removed**: the
|
|
116
|
+
contract, the controller, its `portals` MCP prompt section, the generated
|
|
117
|
+
OpenAPI/SDK artifacts and its tests.
|
|
118
|
+
|
|
119
|
+
It was the universal escape hatch behind the customizer's "Add new block"
|
|
120
|
+
palette. Supported semantic workflows now use dedicated operations that own
|
|
121
|
+
their canonical blocks: `addPortalNoteBlock`, `addPortalBlankNoteBlock`,
|
|
122
|
+
`addPortalTaskListBlock`, `addPortalKanbanBlock`, `addPortalEmbedBlock`,
|
|
123
|
+
`addPortalChatBlock`, `addPortalAiAgentBlock`, `addPortalDatabaseBlock`,
|
|
124
|
+
`addPortalDynamicTableBlock`, `addPortalBpmnDiagramBlock`,
|
|
125
|
+
`addPortalHeadingBlock`, `addPortalTextBlock`, `addPortalImageBlock`,
|
|
126
|
+
`addPortalCardBlock`, `addPortalCardGroupBlock`, `addPortalInfoBlock`,
|
|
127
|
+
`addPortalCarouselBlock`, `addPortalCountdownBlock`, `addPortalFormBlock`,
|
|
128
|
+
`addPortalCustomWidgetBlock`, `addPortalHtmlCssBlock`, `addPortalLinkedBlock`,
|
|
129
|
+
`addPortalTimelineBlock`, `addPortalFileUploaderBlock` and
|
|
130
|
+
`addPortalRecentFilesBlock`. The generic path already answered 400 naming **20**
|
|
131
|
+
of those operations by hand, so its guard chain was most of what remained of it.
|
|
132
|
+
|
|
133
|
+
Gate no longer builds any portal block shape by hand. `buildBlockChangeEvent`,
|
|
134
|
+
`SUPPORTED_BLOCK_TYPES` and the `properties` type validator existed only for the
|
|
135
|
+
generic path and went with it; the note-block builder used by
|
|
136
|
+
`addPortalNoteBlock` is untouched.
|
|
137
|
+
|
|
138
|
+
### addPortalChatsDashboardMenuItem — duplicate items allowed (NIM-43021)
|
|
139
|
+
|
|
140
|
+
The Customizer lets a menu bar carry any number of Chat dashboard items, so
|
|
141
|
+
`addPortalChatsDashboardMenuItem` no longer refuses one. The per-bar duplicate
|
|
142
|
+
check and its 409 `alreadyExists` reason are removed in portal-service and gate;
|
|
143
|
+
every item still points at the portal's system `/chats-dashboard` page.
|
|
144
|
+
|
|
145
|
+
### Portal block discovery, move and delete (NIM-43062)
|
|
146
|
+
|
|
147
|
+
Gate can now find, reorder and remove an existing block on a portal page. Until
|
|
148
|
+
now it could only add blocks; changing one meant sending an internal block DTO
|
|
149
|
+
or raw draft change events, which the public API does not accept.
|
|
150
|
+
|
|
151
|
+
Three new operations, all staged in the active Customizer draft and never
|
|
152
|
+
published automatically:
|
|
153
|
+
|
|
154
|
+
- `listPortalPageBlocks` — every block of one page in display order, over the
|
|
155
|
+
published portal plus its active draft. The discovery step before a move or a
|
|
156
|
+
delete, and the order a move anchor refers to.
|
|
157
|
+
- `movePortalBlock` — reorders a block within its own page via a semantic
|
|
158
|
+
position: `first`, `last`, `beforeBlockId` or `afterBlockId`.
|
|
159
|
+
- `deletePortalBlock` — removes one removable block, keeping every backing
|
|
160
|
+
resource.
|
|
161
|
+
|
|
162
|
+
### Read a portal block, and edit a note-backed one (NIM-43065)
|
|
163
|
+
|
|
164
|
+
NIM-43062 gave Gate block discovery, move and delete. This adds the read half
|
|
165
|
+
and the first typed edit, so an agent can go list → read → patch without ever
|
|
166
|
+
touching an internal block DTO:
|
|
167
|
+
|
|
168
|
+
- `getPortalBlock` — one block's semantic `kind` and current public settings,
|
|
169
|
+
over the published portal plus its active Customizer draft. The inverse of
|
|
170
|
+
the add operations and the step that lets an agent show the user what is set
|
|
171
|
+
today before changing it.
|
|
172
|
+
- `updatePortalNoteBlock` — a partial update of a note-backed block: the kind
|
|
173
|
+
behind `addPortalNoteBlock`, `addPortalBlankNoteBlock`, `addPortalKanbanBlock`
|
|
174
|
+
and `addPortalTaskListBlock`, which all persist as the same block kind.
|
|
175
|
+
|
|
176
|
+
`listPortalPageBlocks` now fills in `editOperation` for a block whose typed
|
|
177
|
+
update operation exists — today that is the note kind; it stays `null` for the
|
|
178
|
+
other kinds until their operations land.
|
|
179
|
+
|
|
180
|
+
The change is staged in the active Customizer draft and never published
|
|
181
|
+
automatically.
|
|
182
|
+
|
|
183
|
+
### Portal page and folder permissions (NIM-43064)
|
|
184
|
+
|
|
185
|
+
Gate can now set who may open one published portal page or folder, by path and
|
|
186
|
+
email, in a single call. Until now an agent had to find the internal
|
|
187
|
+
`menuItemId`, resolve numeric `userId`s and post a raw permissions payload — a
|
|
188
|
+
multi-phase flow the public API does not expose.
|
|
189
|
+
|
|
190
|
+
`updatePortalContentPermissions` takes the item's exact full path and a list of
|
|
191
|
+
emails:
|
|
192
|
+
|
|
193
|
+
- `mode: "replace"` — the previous item ACL is dropped and access is left to the
|
|
194
|
+
supplied emails ("make this page only for X").
|
|
195
|
+
- `mode: "add"` — everyone who already has access keeps it and the supplied
|
|
196
|
+
emails are added ("also give X access").
|
|
197
|
+
|
|
198
|
+
Emails that are not portal members yet receive a targeted client/editor portal
|
|
199
|
+
invitation. Unlike `inviteToPortal`, the new members are **not** added to the
|
|
200
|
+
portal's private resources, so they reach only the item they were named for.
|
|
201
|
+
|
|
202
|
+
The target is resolved before any invitation is created, so a wrong path, an
|
|
203
|
+
unsupported item or a caller without portal console access fails without a side
|
|
204
|
+
effect. A `client`-role caller goes through the same client-invite policy
|
|
205
|
+
`inviteToPortal` applies (NIM-40541: the feature kill-switch, the portal's
|
|
206
|
+
`allowClientsToInvite` setting, and the caller's own portal access) — holding
|
|
207
|
+
`portals.manage` is not enough to invite.
|
|
208
|
+
|
|
209
|
+
The change is applied to the live portal, not staged in the Customizer draft:
|
|
210
|
+
it must **not** be followed by `publishPortalDraft`.
|
|
211
|
+
|
|
212
|
+
### Edit portal Heading, Simple text and Image blocks (NIM-43065)
|
|
213
|
+
|
|
214
|
+
The second block family of the portal edit operations, on the seam
|
|
215
|
+
`getPortalBlock` / `updatePortalNoteBlock` established. Three typed updates for
|
|
216
|
+
the Customizer's Text & Media tiles, plus their `getPortalBlock` settings:
|
|
217
|
+
|
|
218
|
+
- `updatePortalHeadingBlock` and `updatePortalTextBlock` — partial updates of
|
|
219
|
+
`content` (text, colour, size, styles, alignment), `appearance` and `layout`.
|
|
220
|
+
- `updatePortalImageBlock` — `layout` only. The picture stays the Customizer's
|
|
221
|
+
built-in image.
|
|
222
|
+
|
|
223
|
+
`getPortalBlock` now reports these kinds' current `content` and `appearance`, so
|
|
224
|
+
an agent can show what is set today and preserve the fields the user did not ask
|
|
225
|
+
to change. `listPortalPageBlocks` fills in `editOperation` for them.
|
|
226
|
+
|
|
227
|
+
The change is staged in the active Customizer draft and never published
|
|
228
|
+
automatically.
|
|
229
|
+
|
|
230
|
+
### Card group block update
|
|
231
|
+
|
|
232
|
+
Added support for updating an existing portal Card group block.
|
|
233
|
+
|
|
234
|
+
### Edit portal Carousel blocks (NIM-43065)
|
|
235
|
+
|
|
236
|
+
Agents can now read and update an existing Carousel block. Changes are staged
|
|
237
|
+
in the active Customizer draft and are not published automatically.
|
|
238
|
+
|
|
239
|
+
### Custom widget block update
|
|
240
|
+
|
|
241
|
+
Added support for updating an existing portal Custom widget block.
|
|
242
|
+
|
|
243
|
+
### Dynamic Table block update
|
|
244
|
+
|
|
245
|
+
Added support for updating an existing portal Dynamic Table block.
|
|
246
|
+
|
|
247
|
+
### Form block update
|
|
248
|
+
|
|
249
|
+
Added support for updating an existing portal Form block.
|
|
250
|
+
|
|
251
|
+
### Info block update
|
|
252
|
+
|
|
253
|
+
Added support for updating an existing portal Info block.
|
|
254
|
+
|
|
255
|
+
### Linked block update
|
|
256
|
+
|
|
257
|
+
Added support for changing the exact source of an existing Linked block.
|
|
258
|
+
|
|
259
|
+
### Recent files block update
|
|
260
|
+
|
|
261
|
+
Added support for updating an existing portal Recent files block.
|
|
262
|
+
|
|
263
|
+
### Portal branding (NIM-42204)
|
|
264
|
+
|
|
265
|
+
Gate now exposes `updatePortalStyle`, letting CLI/SDK/MCP agents set a portal's branding — the color theme, the header logo and the favicon. These are the same three fields the customizer's Portal style panel writes. The change is staged in the portal's customizer draft (not published immediately), so the owner reviews it before publishing. `getPortal` gained the matching read side.
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
## API / SDK Changes
|
|
269
|
+
|
|
270
|
+
### Portal homepage settings (NIM-42205)
|
|
271
|
+
|
|
272
|
+
- Added `updatePortalHomepage({ orgId, portalId }, body)` — `POST /:orgId/portals/:portalId/homepage`.
|
|
273
|
+
- Body fields (all optional, patch semantics; at least one is required):
|
|
274
|
+
- `title` — homepage title. Renames the Home menu item and, with it, the linked homepage.
|
|
275
|
+
- `showSidebarOnOtherPages`, `showSidebarOnHomePage`, `expandSidebarByDefault` — sidebar toggles.
|
|
276
|
+
- Omitted fields keep their current value. One call stages up to two change events — `updateThemeSettings` and `updateMenuItem` — in a single draft write, so a mixed update is all-or-nothing. Returns `{ branchId, seqs, staged: true }`. Requires `portals.write` access.
|
|
277
|
+
|
|
278
|
+
### Read-only isolated SQL is enforced by Postgres
|
|
279
|
+
|
|
280
|
+
No signature changes. `queryIsolatedStoreSql`, and a `query` operation in a batch where **every** operation is a read (`query`, `count`, `select`), run inside a Postgres `READ ONLY` transaction.
|
|
281
|
+
|
|
282
|
+
A batch that also carries a write operation cannot be read-only — a transaction's access mode cannot be relaxed once set — so there a `query` operation is a raw write channel and now requires `isolated_store.execute` instead of `isolated_store.read`. This closes an escalation: the `client` role holds `isolated_store.data.write` but not `isolated_store.execute`, so an app token could previously run arbitrary DML/DDL through a `query` op smuggled into a write batch.
|
|
283
|
+
|
|
284
|
+
### addPortalChatsDashboardMenuItem — add a Chat dashboard navigation item (NIM-43021)
|
|
285
|
+
|
|
286
|
+
- New op `addPortalChatsDashboardMenuItem`, MCP-enabled, prompt groups `authz`,
|
|
287
|
+
`sdk`, `portals`. Portals MCP prompt bumped to 1.66.0.
|
|
288
|
+
- New schemas `AddPortalChatsDashboardMenuItemRequest` and
|
|
289
|
+
`AddPortalChatsDashboardMenuItemResponse`.
|
|
290
|
+
- The body is optional. `title` (1..100 plain-text characters, default
|
|
291
|
+
`Chat Dashboard`) names the navigation item only — the page title, icon, slug
|
|
292
|
+
and route are fixed by the service. `positionType` (`sidebar` default, `top`,
|
|
293
|
+
`footer`) picks the root menu bar.
|
|
294
|
+
- There is no `pageId` and no `parentId`: the Customizer offers dashboards on
|
|
295
|
+
root only, never inside a folder or process.
|
|
296
|
+
- Response is `menuItemId`, `pageId` (the system page the item points at), `url`
|
|
297
|
+
(`/chats-dashboard`), `positionType`, `branchId`, `seqs` and `staged: true`.
|
|
298
|
+
Nothing is published.
|
|
299
|
+
- Duplicates are allowed, matching the Customizer: any bar may carry several
|
|
300
|
+
items, and every one of them points at the same dashboard. A portal with no
|
|
301
|
+
system dashboard page returns the terminal 409 `dashboardPageMissing`, unlike
|
|
302
|
+
the retryable `draftConflict`, so an agent is told whether a retry can help.
|
|
303
|
+
|
|
304
|
+
### addPortalFilesDashboardMenuItem — add a Files dashboard navigation item (NIM-43020)
|
|
305
|
+
|
|
306
|
+
- New op `addPortalFilesDashboardMenuItem`, MCP-enabled, prompt groups `authz`,
|
|
307
|
+
`sdk`, `portals`.
|
|
308
|
+
- The earlier `createPortalFileDashboard` proposal is dropped rather than kept
|
|
309
|
+
alongside; it never reached `main`, so no consumer can be calling it.
|
|
310
|
+
- New schemas `AddPortalFilesDashboardMenuItemRequest` and
|
|
311
|
+
`AddPortalFilesDashboardMenuItemResponse`.
|
|
312
|
+
- The request body may be empty (`requestBody.required: false`). Optional
|
|
313
|
+
`title` (trimmed plain text, 1..100) names the navigation item and, when the
|
|
314
|
+
page is created, the dashboard block; optional `positionType` (`sidebar`
|
|
315
|
+
default, `top`, `footer`) chooses the bar. The page title `Files`, the
|
|
316
|
+
`/files-dashboard` path and the Files icon are fixed by the service. No
|
|
317
|
+
`pageId`, `parentId`, file/bucket ids, appearance, layout or raw persisted
|
|
318
|
+
fields are accepted.
|
|
319
|
+
- Response is `menuItemId`, `pageId`, `blockId` (only when this call created the
|
|
320
|
+
page), `url`, `pageCreated`, `branchId`, `seqs` and `staged: true` — nothing is
|
|
321
|
+
published automatically.
|
|
322
|
+
- `409` means only "the draft moved concurrently" and is worth a retry. An
|
|
323
|
+
existing dashboard is not a conflict.
|
|
324
|
+
- Portals MCP prompt bumped to 1.65.0.
|
|
325
|
+
|
|
326
|
+
### addPortalTasksDashboardMenuItem — add a Tasks dashboard navigation item (NIM-43022)
|
|
327
|
+
|
|
328
|
+
- New op `addPortalTasksDashboardMenuItem`, MCP-enabled, prompt groups `authz`,
|
|
329
|
+
`sdk`, `portals`.
|
|
330
|
+
- New schemas `AddPortalTasksDashboardMenuItemRequest` and
|
|
331
|
+
`AddPortalTasksDashboardMenuItemResponse`.
|
|
332
|
+
- The request body may be empty. Optional `title` (trimmed plain text, 1..100)
|
|
333
|
+
names the navigation item and, when the page is created, the dashboard block;
|
|
334
|
+
optional `positionType` (`sidebar` default, `top`, `footer`) chooses the bar.
|
|
335
|
+
The page title `Task Dashboard`, the `/tasks-dashboard` path and the Tasks
|
|
336
|
+
icon are fixed by the service. No `pageId`, `parentId`, task/task-list ids,
|
|
337
|
+
appearance, layout or raw persisted fields are accepted.
|
|
338
|
+
- Response is `menuItemId`, `pageId`, `blockId` (only when this call created the
|
|
339
|
+
page), `url`, `pageCreated`, `branchId`, `seqs` and `staged: true` — nothing is
|
|
340
|
+
published automatically.
|
|
341
|
+
- `409` means only "the draft moved concurrently" and is worth a retry. An
|
|
342
|
+
existing dashboard is not a conflict.
|
|
343
|
+
- Portals MCP prompt bumped to 1.67.0.
|
|
344
|
+
|
|
345
|
+
### addPortalBlock removed from Fusebase Gate (NIM-43018)
|
|
346
|
+
|
|
347
|
+
- Op `addPortalBlock` removed. The route answers 404 from this release.
|
|
348
|
+
- Schemas `AddPortalBlockRequest`, `AddPortalBlockResponse` and the
|
|
349
|
+
`PortalBlockType` enum removed from the runtime schema defs, the OpenAPI
|
|
350
|
+
document and the SDK types.
|
|
351
|
+
- `PortalsApi.addPortalBlock()` removed from the generated SDK client, and
|
|
352
|
+
`addPortalBlock` from the manifest's operation list.
|
|
353
|
+
- The `portals` MCP prompt (bumped to 1.63.0) loses the op's menu entry and its
|
|
354
|
+
68-line detail section. The 17 "do not use generic `addPortalBlock` for X"
|
|
355
|
+
sentences in the dedicated ops' guidance are gone too: with no generic op there
|
|
356
|
+
is no detour left to warn about.
|
|
357
|
+
|
|
358
|
+
### addPortalChatsDashboardMenuItem — duplicate items allowed (NIM-43021)
|
|
359
|
+
|
|
360
|
+
- 409 now carries only `dashboardPageMissing` (terminal) or `draftConflict`
|
|
361
|
+
(retryable). `alreadyExists` is gone — no caller can receive it any more.
|
|
362
|
+
- The documented default item title is `Chat Dashboard`, the name portal-service
|
|
363
|
+
actually gives the item (it was documented as `Chat dashboard`).
|
|
364
|
+
- Portals MCP prompt bumped to 1.68.0.
|
|
365
|
+
|
|
366
|
+
### Portal block discovery, move and delete (NIM-43062)
|
|
367
|
+
|
|
368
|
+
- `GET /:orgId/portals/:portalId/pages/:pageId/blocks` → `{ blocks }` of
|
|
369
|
+
`blockId`, `type`, `templateType`, `title`, `editOperation`, `place`,
|
|
370
|
+
`draftOnly`. The order is the page area (`place`) first, then the position
|
|
371
|
+
inside it — a block's index is only unique within its own area.
|
|
372
|
+
- `PATCH /:orgId/portals/:portalId/pages/:pageId/blocks/:blockId/position` →
|
|
373
|
+
the shared staged result `{ blockId, pageId, branchId, seqs, staged: true }`.
|
|
374
|
+
- `DELETE /:orgId/portals/:portalId/pages/:pageId/blocks/:blockId` → the same
|
|
375
|
+
staged result. No request body.
|
|
376
|
+
- All three require `portals.write` with the org taken from the path.
|
|
377
|
+
- No raw block index is accepted anywhere. The internal append sentinel `999`
|
|
378
|
+
stays internal: the target index is resolved server-side against the effective
|
|
379
|
+
state, so a concurrent draft change cannot land the block in the wrong slot.
|
|
380
|
+
- `editOperation` is `null` for every block today. It names the typed
|
|
381
|
+
`updatePortal*Block` operation for that block once those land; a `null` does
|
|
382
|
+
not mean the block cannot be moved or deleted.
|
|
383
|
+
- A move anchor must carry the same `place` as the block being moved; anything
|
|
384
|
+
else is a 400 whose message names the reason.
|
|
385
|
+
- Portals MCP prompt bumped to 1.70.0.
|
|
386
|
+
|
|
387
|
+
### Read a portal block, and edit a note-backed one (NIM-43065)
|
|
388
|
+
|
|
389
|
+
- `GET /:orgId/portals/:portalId/pages/:pageId/blocks/:blockId` →
|
|
390
|
+
`blockId`, `kind`, `type`, `templateType`, `title`, `place`, `draftOnly`,
|
|
391
|
+
`editOperation`, `layout` (`colspan`, `rowspan`, `height`) and the
|
|
392
|
+
kind-specific `settings`. `kind` is the vocabulary the typed updates use;
|
|
393
|
+
neither `type` nor `templateType` identifies a block on its own, because every
|
|
394
|
+
`flexible` block shares one type and is told apart by its template.
|
|
395
|
+
`settings` is `null` for a kind with no typed update operation yet.
|
|
396
|
+
- `PATCH /:orgId/portals/:portalId/pages/:pageId/blocks/:blockId/note` →
|
|
397
|
+
the shared staged result `{ blockId, pageId, branchId, seqs, staged: true }`.
|
|
398
|
+
Body: `title`, `layout.colspan` (3–6) and `layout.height` (≥ 50, or `null` for
|
|
399
|
+
the automatic height). Every field is optional; an omitted one keeps its
|
|
400
|
+
current value and the add operation's defaults are not applied.
|
|
401
|
+
- Both require `portals.write` with the org taken from the path.
|
|
402
|
+
- An empty patch, and one the block already satisfies, are 400s — staging
|
|
403
|
+
nothing would answer 200 and read as "applied".
|
|
404
|
+
- The block's content is not editable here: the note's text, the Kanban board
|
|
405
|
+
and the task list belong to their own operations, and `noteId` / `workspaceId`
|
|
406
|
+
are the block's identity.
|
|
407
|
+
- Portals MCP prompt bumped to 1.71.0.
|
|
408
|
+
|
|
409
|
+
### Portal page and folder permissions (NIM-43064)
|
|
410
|
+
|
|
411
|
+
- `POST /:orgId/portals/:portalId/content/permissions` →
|
|
412
|
+
`{ item, mode, permissions, users, affectedItemIds, published: true }`.
|
|
413
|
+
Requires `portals.manage` with the org taken from the path — the same scope as
|
|
414
|
+
`inviteToPortal`, because the operation can create a portal invitation.
|
|
415
|
+
- Request is `{ path, emails, mode }` only. `menuItemId`, `pageId`, `userId` and
|
|
416
|
+
raw permissions are not accepted: they are implementation details that would
|
|
417
|
+
make the call two-phase for no benefit.
|
|
418
|
+
- `listPortalContent` items now carry `path` — the full portal path
|
|
419
|
+
(`/docs/start`), which is the selector the new operation takes. `slug` is
|
|
420
|
+
unchanged for backwards compatibility. `path` is absent for menu items that
|
|
421
|
+
have no path of their own (external links).
|
|
422
|
+
- For a folder the ACL cascades to every descendant folder and page, matching
|
|
423
|
+
the Customizer. `affectedItemIds` lists the root plus everything the cascade
|
|
424
|
+
touched.
|
|
425
|
+
- `add` against an item that is already open to everyone is a successful no-op:
|
|
426
|
+
the users already have access, and silently turning a public page private is
|
|
427
|
+
not what "add a user" asks for.
|
|
428
|
+
- A path that exists only in the Customizer draft answers `409` naming
|
|
429
|
+
`publishPortalDraft`, instead of a bare "not found".
|
|
430
|
+
- A path matching more than one published item answers `409` rather than picking
|
|
431
|
+
one.
|
|
432
|
+
- Portals MCP prompt bumped to 1.74.0.
|
|
433
|
+
|
|
434
|
+
### Edit portal Heading, Simple text and Image blocks (NIM-43065)
|
|
435
|
+
|
|
436
|
+
- `PATCH /:orgId/portals/:portalId/pages/:pageId/blocks/:blockId/heading`
|
|
437
|
+
- `PATCH /:orgId/portals/:portalId/pages/:pageId/blocks/:blockId/text`
|
|
438
|
+
- `PATCH /:orgId/portals/:portalId/pages/:pageId/blocks/:blockId/image`
|
|
439
|
+
|
|
440
|
+
All three return the shared staged result
|
|
441
|
+
`{ blockId, pageId, branchId, seqs, staged: true }` and require
|
|
442
|
+
`portals.write` with the org taken from the path.
|
|
443
|
+
|
|
444
|
+
- Body — `heading` and `text`: `content`, `appearance`, `layout`; `image`:
|
|
445
|
+
`layout` only. Every field is optional; an omitted one keeps its current
|
|
446
|
+
value and the add operation's defaults are not applied.
|
|
447
|
+
- `layout.colspan` is 2–6 (wider than the note family's 3–6) and
|
|
448
|
+
`layout.height` is ≥ 50, or `null` for the automatic height.
|
|
449
|
+
- `getPortalBlock`'s `settings` gains three members:
|
|
450
|
+
`{ kind: "heading" | "text", content, appearance }` and `{ kind: "image" }`.
|
|
451
|
+
|
|
452
|
+
### Card group block update
|
|
453
|
+
|
|
454
|
+
- Added `updatePortalCardGroupBlock` to the HTTP API, SDK and MCP surface.
|
|
455
|
+
- `content.items` replaces the complete visual card list while preserving omitted stored image, destination and per-card theme values by position.
|
|
456
|
+
|
|
457
|
+
### Edit portal Carousel blocks (NIM-43065)
|
|
458
|
+
|
|
459
|
+
- Added `updatePortalCarouselBlock`.
|
|
460
|
+
- `getPortalBlock` now returns Carousel `settings.content.items` and
|
|
461
|
+
`editOperation: updatePortalCarouselBlock`.
|
|
462
|
+
- `content.items` is a full replacement list of 1–6 slides; `layout` remains a
|
|
463
|
+
field-level patch.
|
|
464
|
+
- Regenerated OpenAPI, SDK and MCP skill artifacts include the new operation.
|
|
465
|
+
|
|
466
|
+
### Custom widget block update
|
|
467
|
+
|
|
468
|
+
- Added `updatePortalCustomWidgetBlock` to the HTTP API, SDK and MCP surface.
|
|
469
|
+
- `content.bricks` is the full desired semantic brick list; raw ids, indexes, classes and image sources remain internal.
|
|
470
|
+
|
|
471
|
+
### Dynamic Table block update
|
|
472
|
+
|
|
473
|
+
- Added `updatePortalDynamicTableBlock` to the HTTP API, generated SDK and MCP surface.
|
|
474
|
+
- The operation updates title, appearance and layout only.
|
|
475
|
+
- Integration provider, OAuth connection, data source, columns and rows remain unchanged.
|
|
476
|
+
|
|
477
|
+
### Form block update
|
|
478
|
+
|
|
479
|
+
- Added `updatePortalFormBlock` to the HTTP API, SDK and MCP surface.
|
|
480
|
+
- Existing fields preserve exact `fieldId` values; new fields omit them.
|
|
481
|
+
|
|
482
|
+
### Info block update
|
|
483
|
+
|
|
484
|
+
- Added `updatePortalInfoBlock` to the HTTP API, SDK and MCP surface.
|
|
485
|
+
- Images and destinations use semantic variants; persisted image and link encodings remain internal.
|
|
486
|
+
|
|
487
|
+
### Linked block update
|
|
488
|
+
|
|
489
|
+
- Added `updatePortalLinkedBlock` to the HTTP API, generated SDK and MCP surface.
|
|
490
|
+
- The operation accepts only an exact source returned by Linked block discovery.
|
|
491
|
+
|
|
492
|
+
### Recent files block update
|
|
493
|
+
|
|
494
|
+
- Added `updatePortalRecentFilesBlock` to the HTTP API, generated SDK and MCP surface.
|
|
495
|
+
- The operation updates title, same-portal source and layout only.
|
|
496
|
+
- Sort order, item limit, offset, file type, other query data and appearance remain unchanged.
|
|
497
|
+
|
|
498
|
+
### Portal branding (NIM-42204)
|
|
499
|
+
|
|
500
|
+
- Added `updatePortalStyle({ orgId, portalId }, body)` — `POST /:orgId/portals/:portalId/style`.
|
|
501
|
+
- Body fields (all optional, patch semantics; at least one is required):
|
|
502
|
+
- `theme` — color theme key, the same 13 named themes `createPortal` accepts.
|
|
503
|
+
- `logo` — `{ storedFileUUID }` of an uploaded image, shown in the portal header.
|
|
504
|
+
- `favicon` — `{ storedFileUUID }` of an uploaded image, the browser tab icon.
|
|
505
|
+
- Omitted fields keep their current value. One call stages one `updateThemeSettings` event. Returns `{ branchId, seq, staged: true }`. Requires `portals.write` access.
|
|
506
|
+
- `getPortal` now returns `style` — `{ theme, logoUrl, faviconUrl }` — read from the published theme.
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
## Consumer Impact
|
|
510
|
+
|
|
511
|
+
### Portal homepage settings (NIM-42205)
|
|
512
|
+
|
|
513
|
+
Agents can name a portal's homepage and control sidebar visibility as part of portal generation. The op covers exactly the two field groups both "Homepage settings" customizer screens show (`settings-homepage-screen.tsx` and `page-main-screen.tsx` for the Home item) — nothing else.
|
|
514
|
+
|
|
515
|
+
Three field groups from the original ticket body are deliberately **not** exposed:
|
|
516
|
+
|
|
517
|
+
- `heading`, `showGreeting`, `showSearch` — no customizer control produces `UPDATE_HERO_SETTING`, and the published portal stops rendering these entirely once the owner turns on the block-based Hero header (`theme.hero.show`). A field that silently loses its effect is worse than an absent one. Same reasoning as the header-blocks toggle below.
|
|
518
|
+
- `headerBlocksEnabled` — stored by portal-service, but no portal component renders it.
|
|
519
|
+
- `hero.subheading`, `hero.backgroundImage` — no such field exists anywhere in the customizer.
|
|
520
|
+
|
|
521
|
+
Page content is out of scope: to add blocks to the homepage, resolve its `pageId` from `listPortalContent` (menu item `type: "home"`, already returned today) and call the normal semantic block operations, exactly as for any other page.
|
|
522
|
+
|
|
523
|
+
Sidebar toggles live in the portal theme, which portal-service replaces wholesale on every `updateThemeSettings`. The op therefore reads the effective theme — published settings plus any theme change already staged in the draft — and writes it back with only the requested keys changed.
|
|
524
|
+
|
|
525
|
+
**Depends on portal-service:** the sidebar `minimized` field. portal-service !217 is merged and deployed.
|
|
526
|
+
|
|
527
|
+
### Read-only isolated SQL is enforced by Postgres
|
|
528
|
+
|
|
529
|
+
SQL that used to pass the read endpoint and now fails with `cannot execute … in a read-only transaction`:
|
|
530
|
+
|
|
531
|
+
- `SELECT nextval('seq')` / `setval(...)` — pre-allocate ids with `insertIsolatedStoreSqlRow ... returning` or generate them in app code
|
|
532
|
+
- `SELECT … FOR UPDATE` / `FOR SHARE` — the read path commits immediately, so these locks were already released before the response
|
|
533
|
+
- `SELECT … INTO new_table` and `EXPLAIN ANALYZE <INSERT|UPDATE|DELETE>` — these write; use `executeIsolatedStoreSql` or a migration bundle
|
|
534
|
+
- any `SELECT` over a function that writes — the point of the change
|
|
535
|
+
|
|
536
|
+
Write paths are unaffected: `executeIsolatedStoreSql`, the structured row APIs, migrations and mixed batches all still run read-write.
|
|
537
|
+
|
|
538
|
+
### addPortalChatsDashboardMenuItem — add a Chat dashboard navigation item (NIM-43021)
|
|
539
|
+
|
|
540
|
+
None.
|
|
541
|
+
|
|
542
|
+
### addPortalFilesDashboardMenuItem — add a Files dashboard navigation item (NIM-43020)
|
|
543
|
+
|
|
544
|
+
None for existing callers.
|
|
545
|
+
|
|
546
|
+
### addPortalTasksDashboardMenuItem — add a Tasks dashboard navigation item (NIM-43022)
|
|
547
|
+
|
|
548
|
+
None for existing callers.
|
|
549
|
+
|
|
550
|
+
### addPortalBlock removed from Fusebase Gate (NIM-43018)
|
|
551
|
+
|
|
552
|
+
A vendored-SDK consumer that calls `addPortalBlock` fails to compile after
|
|
553
|
+
updating the SDK (the method is gone) and gets a 404 if it calls the route
|
|
554
|
+
directly with an older client. Use a dedicated operation where one is available;
|
|
555
|
+
each dedicated op takes semantic fields instead of a hand-written `properties`
|
|
556
|
+
bag. Legacy link cards, raw `flexible` payloads and `hero-*` templates have no
|
|
557
|
+
replacement.
|
|
558
|
+
|
|
559
|
+
### Card group block update
|
|
560
|
+
|
|
561
|
+
Consumers can update Card group title, description, cards, appearance and layout without raw bricks or links.
|
|
562
|
+
|
|
563
|
+
### Edit portal Carousel blocks (NIM-43065)
|
|
564
|
+
|
|
565
|
+
Call `getPortalBlock` before editing, then send the complete desired slide list.
|
|
566
|
+
Built-in images and internal Carousel fields are preserved by the service and
|
|
567
|
+
must not be sent by consumers.
|
|
568
|
+
|
|
569
|
+
### Custom widget block update
|
|
570
|
+
|
|
571
|
+
Consumers can update Custom widget title, bricks, appearance and layout.
|
|
572
|
+
|
|
573
|
+
### Dynamic Table block update
|
|
574
|
+
|
|
575
|
+
Consumers can update Dynamic Table presentation without replacing its integration configuration.
|
|
576
|
+
|
|
577
|
+
### Form block update
|
|
578
|
+
|
|
579
|
+
Consumers can update Form content and presentation without raw bricks or response data.
|
|
580
|
+
|
|
581
|
+
### Info block update
|
|
582
|
+
|
|
583
|
+
Consumers can update Info title, image, destination, new-tab behavior and layout.
|
|
584
|
+
|
|
585
|
+
### Linked block update
|
|
586
|
+
|
|
587
|
+
Consumers can repoint a Linked block while its presentation remains mirrored from the source.
|
|
588
|
+
|
|
589
|
+
### Recent files block update
|
|
590
|
+
|
|
591
|
+
Consumers can edit Recent files presentation and switch between the entire portal and one exact portal page.
|
|
592
|
+
|
|
593
|
+
### Portal branding (NIM-42204)
|
|
594
|
+
|
|
595
|
+
Agents can re-brand a portal as part of portal generation without opening the customizer: pick a theme, attach a logo, attach a favicon.
|
|
596
|
+
|
|
597
|
+
Two field groups from the original ticket body are deliberately **not** exposed, because the portal theme model has nowhere to store them:
|
|
598
|
+
|
|
599
|
+
- **Semantic hex colors** (`colors.primary`, `background`, `surface`, `text`, `accent`, mapped onto daisyUI `--p`/`--b1`/`--bc`/`--a`). Portals are not daisyUI-themed. Theme color slots hold arrays of **Tailwind palette tokens** (`'neutral-900'`) and render as Tailwind classes (`bg-neutral-900`), safelisted by palette name in `apps/portals/tailwind.config.js`. A raw `#2563eb` would render as the class `bg-#2563eb` and style nothing. This is the same conclusion NIM-42208 published: overriding daisyUI variables re-brands nothing, because only ~7 cosmetic widgets read them.
|
|
600
|
+
- **`font: { family, source }`.** There is no font field anywhere in `PortalServiceThemeDto`, no customizer control that writes one, and no renderer that reads one.
|
|
601
|
+
|
|
602
|
+
For anything beyond the named themes, `updatePortalCustomCode` is the supported path: it targets the `data-portal` structural hooks published by NIM-42208 (see reference `portal-theme-variables`), where hex colors and `@font-face` do work. That is CNAME-domain portals only.
|
|
603
|
+
|
|
604
|
+
Removing a logo or favicon is also not exposed — the customizer's remove button is the way back. Add a nullable form if agents turn out to need it.
|
|
605
|
+
|
|
606
|
+
Images are stored as `/box/file/{storedFileUUID}`, the same relative URL the customizer writes (`STATIC_ASSET_PREFIX` is `/box/file/` on both dev and prod), so it resolves on whatever domain serves the portal.
|
|
607
|
+
|
|
608
|
+
The theme is replaced wholesale by portal-service on every `updateThemeSettings`, so the op reads the effective theme — published settings plus any theme change already staged in the draft — and writes it back with only `key` / `logo` / `icon` changed, on both the light and dark schemas. Both schemas are written together, exactly as the customizer does, so branding does not shift when dark mode is re-enabled.
|
|
609
|
+
|
|
610
|
+
`getPortal` reads branding with a second portal-service call; if that call fails or the portal has no settings entry, `style` is simply omitted rather than failing the read.
|
|
611
|
+
|
|
612
|
+
**No portal-service change:** the op writes through the existing draft-append endpoint and reads through the existing `/portals/settings`.
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
## Verification
|
|
616
|
+
|
|
617
|
+
### Portal homepage settings (NIM-42205)
|
|
618
|
+
|
|
619
|
+
- `npm test -- --runTestsByPath tests/unit/portals-controller.test.ts`
|
|
620
|
+
- `npm run build`
|
|
621
|
+
- `FEATURE_FLAGS=isolated_sql_stores,isolated_nosql_stores npm run build:sdk`
|
|
622
|
+
|
|
623
|
+
### Read-only isolated SQL is enforced by Postgres
|
|
624
|
+
|
|
625
|
+
- Unit tests: `BEGIN READ ONLY` on the read path and plain `BEGIN` on the write path, all-read vs. mixed batch, and the mixed-batch `query` permission
|
|
626
|
+
- Against a real Postgres 16: a `SELECT` calling a writing volatile function, the escape-string CTE bypass, and an all-read batch calling the same function are all rejected, while plain reads, the write path and mixed batches keep working. Each of the three was accepted (and did write) on the pre-change code.
|
|
627
|
+
- `npm test`, `npm run lint`, `npm run build`
|
|
628
|
+
|
|
629
|
+
### addPortalChatsDashboardMenuItem — add a Chat dashboard navigation item (NIM-43021)
|
|
630
|
+
|
|
631
|
+
- `npm run test:unit` — including the reworked `portals-chat-dashboard-contract`
|
|
632
|
+
and `portals-chat-dashboard-seam` suites and the `mcp-prompts` pins for the new
|
|
633
|
+
guidance.
|
|
634
|
+
- `npm run build`, `npm run build:sdk`, `npm run mcp:skills:validate` — clean.
|
|
635
|
+
- portal-service: `addPortalChatsDashboardMenuItem.spec.ts` covering duplicate
|
|
636
|
+
items in the same and other bars, the tombstoned-page exception, the
|
|
637
|
+
missing-page path, the three bars, the root index, the retry and a reducer
|
|
638
|
+
round-trip of the staged event.
|
|
639
|
+
|
|
640
|
+
### addPortalFilesDashboardMenuItem — add a Files dashboard navigation item (NIM-43020)
|
|
641
|
+
|
|
642
|
+
- `npm test` — unit tests including the new
|
|
643
|
+
`portals-files-dashboard-contract` and `portals-files-dashboard-seam` suites
|
|
644
|
+
and the `mcp-prompts` pins.
|
|
645
|
+
- `npm run build`, `npm run build:sdk`, `npm run mcp:skills:generate` and
|
|
646
|
+
`npm run mcp:skills:validate`.
|
|
647
|
+
- portal-service: `addPortalFilesDashboardMenuItem` use-case, reducer round-trip
|
|
648
|
+
and swagger-contract specs.
|
|
649
|
+
|
|
650
|
+
### addPortalTasksDashboardMenuItem — add a Tasks dashboard navigation item (NIM-43022)
|
|
651
|
+
|
|
652
|
+
- `npm test` — unit tests including the new
|
|
653
|
+
`portals-tasks-dashboard-contract` and `portals-tasks-dashboard-seam` suites
|
|
654
|
+
and the `mcp-prompts` pins.
|
|
655
|
+
- `npm run build`, `npm run build:sdk`, `npm run mcp:skills:generate` and
|
|
656
|
+
`npm run mcp:skills:validate`.
|
|
657
|
+
- portal-service: `addPortalTasksDashboardMenuItem` use-case, reducer round-trip
|
|
658
|
+
and swagger-contract specs.
|
|
659
|
+
|
|
660
|
+
### addPortalBlock removed from Fusebase Gate (NIM-43018)
|
|
661
|
+
|
|
662
|
+
- `npm test` — 1018 unit tests green. The `PortalsController.addPortalBlock`
|
|
663
|
+
suite and the 21 sibling "generic op rejects X" suites go with the op (28 test
|
|
664
|
+
files touched, 22 describe blocks removed); the pins the removed prompt lines
|
|
665
|
+
carried are dropped from `mcp-prompts` and the sibling contract specs.
|
|
666
|
+
- `npm run build`, `npm run lint`, `npm run mcp:skills:validate`.
|
|
667
|
+
- `npm run build:sdk` — OpenAPI operation, SDK method, manifest entry and types
|
|
668
|
+
dropped.
|
|
669
|
+
|
|
670
|
+
### addPortalChatsDashboardMenuItem — duplicate items allowed (NIM-43021)
|
|
671
|
+
|
|
672
|
+
- `npm run test:unit`, `npm run build`, `npm run build:sdk`,
|
|
673
|
+
`npm run mcp:skills:validate` — clean.
|
|
674
|
+
- portal-service: `addPortalChatsDashboardMenuItem.spec.ts` covers a second item
|
|
675
|
+
in the same bar, an item in another bar and a concurrent duplicate during the
|
|
676
|
+
retry.
|
|
677
|
+
|
|
678
|
+
### Portal block discovery, move and delete (NIM-43062)
|
|
679
|
+
|
|
680
|
+
- `npm run test:unit` (1090 passing), `npm run build`, `npm run lint`,
|
|
681
|
+
`npm run build:sdk`, `npm run mcp:skills:generate`,
|
|
682
|
+
`npm run mcp:skills:validate` — clean.
|
|
683
|
+
- portal-service `portalBlockOperation.spec.ts`: 24 cases covering every move
|
|
684
|
+
position, self/foreign/other-area anchors, a draft-only block, the
|
|
685
|
+
not-removable guard, a block already staged for deletion, and the single
|
|
686
|
+
draft-conflict retry re-deriving the index from fresh state.
|
|
687
|
+
|
|
688
|
+
### Read a portal block, and edit a note-backed one (NIM-43065)
|
|
689
|
+
|
|
690
|
+
- `npm run test:unit` (1138 passing), `npm run build`, `npm run lint` (0
|
|
691
|
+
errors), `npm run build:sdk`, `npm run mcp:skills:validate` — clean.
|
|
692
|
+
- portal-service `portalBlockUpdate.spec.ts`: 15 cases covering the kind map,
|
|
693
|
+
the note settings, `editOperation`, both draft events, the explicit-null
|
|
694
|
+
height, the wrong-kind guard, every validation bound and the draft-conflict
|
|
695
|
+
retry.
|
|
696
|
+
- Both guards were probed rather than trusted: reverting the full-DTO merge to a
|
|
697
|
+
changed-fields-only payload reds exactly the "stages the full current block"
|
|
698
|
+
case, and cutting the update-semantics line out of the prompt reds exactly the
|
|
699
|
+
new prompt pin.
|
|
700
|
+
|
|
701
|
+
### Portal page and folder permissions (NIM-43064)
|
|
702
|
+
|
|
703
|
+
- portal-service `updatePortalContentPermissions.spec.ts`: 22 cases covering
|
|
704
|
+
nested-path resolution, trailing-slash and absolute-URL handling, the page and
|
|
705
|
+
its backing node, the folder cascade running once per item, `replace` dropping
|
|
706
|
+
the old lists, `add` preserving them and lifting the user out of `rejectUserIds`,
|
|
707
|
+
the public-item no-op, a system item, a foreign org, a missing path, an
|
|
708
|
+
ambiguous path, the permissions-service sync payload, a permissions-service
|
|
709
|
+
failure not being reported as success, the `mode` and empty-`userIds` guards,
|
|
710
|
+
`affectedItemIds` skipping items that were already identical, and `dryRun`
|
|
711
|
+
validating without writing.
|
|
712
|
+
- fusebase-gate `portal-content-permissions.spec.ts`: 25 cases covering email
|
|
713
|
+
normalization, deduplication and the 50 limit, an existing workspace member not
|
|
714
|
+
being re-invited, a new email getting a client/editor invite without full portal
|
|
715
|
+
access, every email resolved before the ACL write, the downstream 403/404/409
|
|
716
|
+
mapping, the dry run running before the first invitation, a `client` caller
|
|
717
|
+
refused by the invite policy, and one member-list fetch per batch.
|
|
718
|
+
|
|
719
|
+
### Card group block update
|
|
720
|
+
|
|
721
|
+
- Portal-service typecheck, source lint and focused tests passed: 3 suites, 26 tests.
|
|
722
|
+
- Gate build, lint and focused tests passed: 3 suites, 135 tests.
|
|
723
|
+
- SDK and MCP artifacts were generated; MCP fallback validation passed.
|
|
724
|
+
|
|
725
|
+
### Edit portal Carousel blocks (NIM-43065)
|
|
726
|
+
|
|
727
|
+
- Portal-service TypeScript and 36 focused block/update/schema/route tests pass.
|
|
728
|
+
The broader serial suite exceeded the 4 GB Node heap after its earlier tests
|
|
729
|
+
passed; its one stale Carousel expectation was updated and re-run successfully.
|
|
730
|
+
- Gate build, lint, SDK generation, MCP skill validation and 113 focused unit
|
|
731
|
+
tests pass. The full suite reached 1182 passing tests; only the existing
|
|
732
|
+
framework-link check failed because installed platform packages are 1.7.1
|
|
733
|
+
while package.json pins 1.7.3.
|
|
734
|
+
|
|
735
|
+
### Custom widget block update
|
|
736
|
+
|
|
737
|
+
- Portal-service typecheck, source lint and focused tests passed: 3 suites, 25 tests.
|
|
738
|
+
- Gate build, lint and focused tests passed: 3 suites, 143 tests.
|
|
739
|
+
- SDK and MCP artifacts were generated; MCP fallback validation passed.
|
|
740
|
+
|
|
741
|
+
### Dynamic Table block update
|
|
742
|
+
|
|
743
|
+
- Portal-service typecheck and focused tests passed: 5 suites, 41 tests.
|
|
744
|
+
- Gate build and focused tests passed: 3 suites, 122 tests.
|
|
745
|
+
- Gate lint passed with 12 existing warnings.
|
|
746
|
+
- SDK and MCP artifacts were generated; MCP fallback validation passed.
|
|
747
|
+
- Full Gate validation remains blocked by the existing platform version mismatch (installed 1.7.1, package requires 1.7.3).
|
|
748
|
+
|
|
749
|
+
### Form block update
|
|
750
|
+
|
|
751
|
+
- Portal-service typecheck and focused tests passed: 3 suites, 25 tests.
|
|
752
|
+
- Gate build and focused tests passed: 3 suites, 131 tests.
|
|
753
|
+
- SDK and MCP artifacts were generated; MCP fallback validation passed.
|
|
754
|
+
- Full Gate validation remains blocked by the existing platform version mismatch (installed 1.7.1, package requires 1.7.3).
|
|
755
|
+
|
|
756
|
+
### Info block update
|
|
757
|
+
|
|
758
|
+
- Portal-service typecheck, source lint and focused tests passed: 3 suites, 26 tests.
|
|
759
|
+
- Gate build, lint and focused tests passed: 3 suites, 139 tests.
|
|
760
|
+
- SDK and MCP artifacts were generated; MCP fallback validation passed.
|
|
761
|
+
|
|
762
|
+
### Linked block update
|
|
763
|
+
|
|
764
|
+
- Portal-service typecheck and focused tests passed: 4 suites, 48 tests.
|
|
765
|
+
- Gate build and focused tests passed: 3 suites, 128 tests.
|
|
766
|
+
- Gate lint passed with 12 existing warnings.
|
|
767
|
+
- SDK and MCP artifacts were generated; MCP fallback validation passed.
|
|
768
|
+
- Full Gate validation remains blocked by the existing platform version mismatch (installed 1.7.1, package requires 1.7.3).
|
|
769
|
+
|
|
770
|
+
### Recent files block update
|
|
771
|
+
|
|
772
|
+
- Portal-service typecheck and focused tests passed: 3 suites, 26 tests.
|
|
773
|
+
- Gate build and focused tests passed: 3 suites, 125 tests.
|
|
774
|
+
- Gate lint passed with 12 existing warnings.
|
|
775
|
+
- SDK and MCP artifacts were generated; MCP fallback validation passed.
|
|
776
|
+
- Full Gate validation remains blocked by the existing platform version mismatch (installed 1.7.1, package requires 1.7.3).
|
|
777
|
+
|
|
778
|
+
### Portal branding (NIM-42204)
|
|
779
|
+
|
|
780
|
+
- `npm test -- --runTestsByPath tests/unit/portals-controller.test.ts tests/unit/mcp-prompts.test.ts`
|
|
781
|
+
- `npm run build`
|
|
782
|
+
- `FEATURE_FLAGS=isolated_sql_stores,isolated_nosql_stores npm run build:sdk`
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
## Follow-ups
|
|
786
|
+
|
|
787
|
+
### addPortalFilesDashboardMenuItem — add a Files dashboard navigation item (NIM-43020)
|
|
788
|
+
|
|
789
|
+
Older portals created by `filesDashboardMigration` keep the migration-era block
|
|
790
|
+
defaults; unifying them is a separate migration.
|
|
791
|
+
|
|
792
|
+
### addPortalTasksDashboardMenuItem — add a Tasks dashboard navigation item (NIM-43022)
|
|
793
|
+
|
|
794
|
+
Older portals created by `tasksDashboardMigration` keep the migration-era block
|
|
795
|
+
defaults (`rowspan: 1`, `isOrigin`); unifying them is a separate migration.
|
|
796
|
+
|
|
797
|
+
### addPortalBlock removed from Fusebase Gate (NIM-43018)
|
|
798
|
+
|
|
799
|
+
None within NIM-43018. The legacy link cards and raw `flexible` support are
|
|
800
|
+
intentionally gone. Replacement operations for `content` and the three system
|
|
801
|
+
dashboards are tracked by their own tasks (see Breaking).
|
|
802
|
+
|
|
803
|
+
### Card group block update
|
|
804
|
+
|
|
805
|
+
None.
|
|
806
|
+
|
|
807
|
+
### Edit portal Carousel blocks (NIM-43065)
|
|
808
|
+
|
|
809
|
+
Implement the remaining 15 typed portal block update families one at a time.
|
|
810
|
+
|
|
811
|
+
### Custom widget block update
|
|
812
|
+
|
|
813
|
+
None.
|
|
814
|
+
|
|
815
|
+
### Dynamic Table block update
|
|
816
|
+
|
|
817
|
+
None.
|
|
818
|
+
|
|
819
|
+
### Form block update
|
|
820
|
+
|
|
821
|
+
None.
|
|
822
|
+
|
|
823
|
+
### Info block update
|
|
824
|
+
|
|
825
|
+
None.
|
|
826
|
+
|
|
827
|
+
### Linked block update
|
|
828
|
+
|
|
829
|
+
None.
|
|
830
|
+
|
|
831
|
+
### Recent files block update
|
|
832
|
+
|
|
833
|
+
None.
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
## Breaking
|
|
837
|
+
|
|
838
|
+
### addPortalChatsDashboardMenuItem — add a Chat dashboard navigation item (NIM-43021)
|
|
839
|
+
|
|
840
|
+
None. `createPortalChatDashboardPage` never reached gate's contracts, SDK or
|
|
841
|
+
OpenAPI — only portal-service's internal `POST /portals/:id/pages/chat-dashboard`
|
|
842
|
+
route existed, and the paired MR removes it. It staged a second dashboard page on
|
|
843
|
+
the singular `/chat-dashboard` route, while the system page every portal actually
|
|
844
|
+
has is the plural `/chats-dashboard`.
|
|
845
|
+
|
|
846
|
+
### addPortalFilesDashboardMenuItem — add a Files dashboard navigation item (NIM-43020)
|
|
847
|
+
|
|
848
|
+
None. `createPortalFileDashboard` never reached `main`.
|
|
849
|
+
|
|
850
|
+
### addPortalTasksDashboardMenuItem — add a Tasks dashboard navigation item (NIM-43022)
|
|
851
|
+
|
|
852
|
+
None. The operation is additive.
|
|
853
|
+
|
|
854
|
+
### addPortalBlock removed from Fusebase Gate (NIM-43018)
|
|
855
|
+
|
|
856
|
+
Any caller of `addPortalBlock` must stop using it and, where available, move to
|
|
857
|
+
the dedicated operation for the tile it wants. There is no deprecation window
|
|
858
|
+
and no server-side redirect.
|
|
859
|
+
|
|
860
|
+
The following generic paths are **dropped without replacement**, on purpose:
|
|
861
|
+
|
|
862
|
+
- the legacy link cards `docs`, `help`, `request`, `meetings`, `links`;
|
|
863
|
+
- raw `flexible` payloads and the `hero-*` templates.
|
|
864
|
+
|
|
865
|
+
Apps remain supported through `addPortalAppPage` for a new sidebar page and
|
|
866
|
+
`addPortalAppBlock` for an existing page; callers must not construct the
|
|
867
|
+
underlying `app-feature` template themselves.
|
|
868
|
+
|
|
869
|
+
The remaining blocks are **handled separately**:
|
|
870
|
+
|
|
871
|
+
- `content` — NIM-43019;
|
|
872
|
+
- `files-dashboard` — NIM-43020;
|
|
873
|
+
- `tasks-dashboard` — NIM-43022;
|
|
874
|
+
- `chats-dashboard` — NIM-43021.
|
|
875
|
+
|
|
876
|
+
This change does not implement those replacements; their availability depends
|
|
877
|
+
on the separate tasks shipping.
|
|
878
|
+
|
|
879
|
+
### addPortalChatsDashboardMenuItem — duplicate items allowed (NIM-43021)
|
|
880
|
+
|
|
881
|
+
None for callers that treat 409 by `reason`: the removed reason was a rejection,
|
|
882
|
+
and those requests now succeed with a 201.
|
|
883
|
+
|
|
884
|
+
### Portal block discovery, move and delete (NIM-43062)
|
|
885
|
+
|
|
886
|
+
None — three additive operations.
|
|
887
|
+
|
|
888
|
+
### Read a portal block, and edit a note-backed one (NIM-43065)
|
|
889
|
+
|
|
890
|
+
None — two additive operations. `editOperation` was already in the
|
|
891
|
+
`listPortalPageBlocks` response; it now carries a value for note-backed blocks
|
|
892
|
+
instead of always `null`.
|
|
893
|
+
|
|
894
|
+
### Portal page and folder permissions (NIM-43064)
|
|
895
|
+
|
|
896
|
+
None — one additive operation plus an additive `path` field on
|
|
897
|
+
`listPortalContent`.
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
## Note for the next regeneration
|
|
901
|
+
|
|
902
|
+
### Portal block discovery, move and delete (NIM-43062)
|
|
903
|
+
|
|
904
|
+
`npm run build:sdk` must run with
|
|
905
|
+
`FEATURE_FLAGS=isolated_sql_stores,isolated_nosql_stores`. Without it the
|
|
906
|
+
isolated-store ops are not registered, the generator silently drops that whole
|
|
907
|
+
SDK domain and the run then dies in `tsc` before writing the OpenAPI spec. This
|
|
908
|
+
is pre-existing on `main`, not introduced here.
|
|
909
|
+
|
|
910
|
+
### Read a portal block, and edit a note-backed one (NIM-43065)
|
|
911
|
+
|
|
912
|
+
`npm run build:sdk` must run with
|
|
913
|
+
`FEATURE_FLAGS=isolated_sql_stores,isolated_nosql_stores`, as recorded on the
|
|
914
|
+
NIM-43062 note.
|
|
915
|
+
|
|
916
|
+
### Portal page and folder permissions (NIM-43064)
|
|
917
|
+
|
|
918
|
+
`npm run build:sdk` must run with
|
|
919
|
+
`FEATURE_FLAGS=isolated_sql_stores,isolated_nosql_stores` (see
|
|
920
|
+
`2026-08-06-portal-block-discovery-move-delete.md`).
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
## Notes
|
|
924
|
+
|
|
925
|
+
### Edit portal Heading, Simple text and Image blocks (NIM-43065)
|
|
926
|
+
|
|
927
|
+
**`content` is a field-level patch; `appearance` is not.** A size, style or
|
|
928
|
+
alignment left out of `content` keeps what the block has today. `appearance` is
|
|
929
|
+
a discriminated variant and has to be sent complete — read the current one from
|
|
930
|
+
`getPortalBlock` first, or the fields left out revert to the variant's defaults.
|
|
931
|
+
|
|
932
|
+
**A `custom` backdrop keeps its background when the patch omits one.** A block
|
|
933
|
+
the Customizer left on the portal theme's surface colour has no background to
|
|
934
|
+
report, so `getPortalBlock` omits it and sending that appearance back is legal.
|
|
935
|
+
|
|
936
|
+
**An empty patch, and one the block already satisfies, are 400s** — staging
|
|
937
|
+
nothing would answer 200 and read as "applied".
|
|
938
|
+
|
|
939
|
+
|
|
940
|
+
## Still to come
|
|
941
|
+
|
|
942
|
+
### Read a portal block, and edit a note-backed one (NIM-43065)
|
|
943
|
+
|
|
944
|
+
The other 22 typed `updatePortal…Block` operations from the
|
|
945
|
+
`NIM-40621-edit-blocks.md` spec, and their `getPortalBlock` settings members.
|
|
946
|
+
The engine seam they plug into (`updatePortalBlock` in portal-service) ships
|
|
947
|
+
here.
|