Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .changeset/notification-dual-source-c3.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
---
"@objectstack/spec": major
---

feat(spec)!: the notification vocabulary has one owner per name — `@objectstack/spec/ui` no longer exports `Notification(Schema)` / `NotificationConfig(Schema)`, and `@objectstack/spec/system` no longer exports `NotificationConfig(Schema)` (#4610)

The names `Notification` / `NotificationSchema` (`./api` vs `./ui`) and
`NotificationConfig` / `NotificationConfigSchema` (`./system` vs `./ui`)
each resolved to **two different declarations** depending on the import
path — the #4411 dual-source trap. Resolution (three-repo,
import-statement-level consumer scan: framework, cloud, objectui):

- **Removed** `NotificationSchema` / `Notification` from
`@objectstack/spec/ui`. This was a toast/banner "notification instance"
shape (`type`/`severity`/`message`/`duration`/`actions`/`position` + ARIA
props) with **zero importers** in all three repos — objectui's toaster
never adopted it. The live contract is `./api`'s `Notification(Schema)`:
the REST inbox row (`id`/`type`/`title`/`body`/`read`/`data`/`actionUrl`/
`createdAt`) embedded in `ListNotificationsResponseSchema`, served by
`/api/v1/notifications`, implemented by `@objectstack/client`, and
mirrored by `InboxNotification` in `@objectstack/spec/contracts`
(ADR-0030: the bell reads this shape).
- FROM `import { NotificationSchema, type Notification } from '@objectstack/spec/ui'` →
TO `import { NotificationSchema, type Notification } from '@objectstack/spec/api'`.
**Shape change**: the api row is an inbox record, not a presentation
config — the ui shape's `severity` / `duration` / `dismissible` /
`actions` / `position` / ARIA fields do not exist there. For the
presentation vocabulary keep using the ui enums, which are unchanged:
`NotificationTypeSchema`, `NotificationSeveritySchema`,
`NotificationPositionSchema`, `NotificationActionSchema` (+ their
types) still live in `@objectstack/spec/ui`.
- **Removed** `NotificationConfigSchema` / `NotificationConfig` from **both**
`@objectstack/spec/system` and `@objectstack/spec/ui` — the bare name left
the spec export surface entirely. Both declarations had zero importers in
all three repos and were wired into no parent schema. The system side (a
channel + template + recipients + schedule + retryPolicy + tracking
"unified notification management protocol") predates ADR-0030's accepted
delivery architecture and advertised capability the runtime does not
deliver (its channel enum's `push`/`slack`/`teams`/`webhook` dead-letter,
#3197; nothing reads `schedule`/`retryPolicy`/`tracking`). The ui side (a
toaster global config: `defaultPosition`/`defaultDuration`/`maxVisible`/
`stackDirection`/`pauseOnHover`) was never adopted by objectui.
- FROM `import { NotificationConfigSchema } from '@objectstack/spec/system'` (or `.../ui`) →
TO: no direct replacement. The live delivery vocabulary is
`NotificationService.emit` (`INotificationService`,
`@objectstack/spec/contracts`), the `notify` flow node
(`NotifyConfigSchema`, `@objectstack/spec/automation`) and the
`sys_notification*` platform objects; per-user delivery preferences are
`NotificationPreferences(Schema)` in `@objectstack/spec/api`.
- `@objectstack/spec/api`'s `Notification(Schema)` and
`NotificationPreferences(Schema)` are **unchanged**; `./api` is now the
sole owner of the bare `Notification(Schema)` names. Imports from `./api`
need no migration. `@objectstack/spec/system`'s `NotificationChannel(Schema)`,
`EmailTemplate(Schema)`, `SMSTemplate(Schema)`, `PushNotification(Schema)`
and `InAppNotification(Schema)` are **unchanged**.

`dual-source-exports.baseline.json` shrinks by exactly these 4 rows (28 → 24,
#4535 C3).
1 change: 0 additions & 1 deletion content/docs/references/api/meta.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,6 @@
"export",
"identity",
"metadata",
"notification",
"package-api",
"package-registry",
"plugin-rest-api",
Expand Down
37 changes: 0 additions & 37 deletions content/docs/references/api/notification.mdx

This file was deleted.

22 changes: 20 additions & 2 deletions content/docs/references/api/protocol.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,8 +20,8 @@ validation. Each entry is a canonical `ActionDescriptorSchema`.
## TypeScript Usage

```typescript
import { AiAgentCapabilitiesSchema, AiAgentChatRequestSchema, AiAgentSummarySchema, AiAgentsResponseSchema, AiChatRequestSchema, AiChatResponseSchema, AiCompleteRequestSchema, AiConversationSchema, AiMessageSchema, AiModelsResponseSchema, AiPendingActionSchema, AiPendingActionStatusSchema, AiStreamChunkSchema, ApproveAiPendingActionResponseSchema, AutomationActionsResponseSchema, AutomationTriggerRequestSchema, AutomationTriggerResponseSchema, BatchDataRequestSchema, BatchDataResponseSchema, CheckPermissionRequestSchema, CheckPermissionResponseSchema, CreateAiConversationRequestSchema, CreateDataRequestSchema, CreateDataResponseSchema, CreateManyDataRequestSchema, CreateManyDataResponseSchema, CreateViewRequestSchema, CreateViewResponseSchema, DeleteDataRequestSchema, DeleteDataResponseSchema, DeleteManyDataRequestSchema, DeleteManyDataResponseSchema, DeleteMetaItemRequestSchema, DeleteMetaItemResponseSchema, DeleteViewRequestSchema, DeleteViewResponseSchema, FindDataRequestSchema, FindDataResponseSchema, GetDataRequestSchema, GetDataResponseSchema, GetDiscoveryRequestSchema, GetDiscoveryResponseSchema, GetEffectivePermissionsRequestSchema, GetEffectivePermissionsResponseSchema, GetFieldLabelsRequestSchema, GetFieldLabelsResponseSchema, GetLocalesRequestSchema, GetLocalesResponseSchema, GetMetaItemCachedRequestSchema, GetMetaItemCachedResponseSchema, GetMetaItemRequestSchema, GetMetaItemResponseSchema, GetMetaItemsRequestSchema, GetMetaItemsResponseSchema, GetMetaTypesRequestSchema, GetMetaTypesResponseSchema, GetNotificationPreferencesRequestSchema, GetNotificationPreferencesResponseSchema, GetObjectPermissionsRequestSchema, GetObjectPermissionsResponseSchema, GetPresenceRequestSchema, GetPresenceResponseSchema, GetTranslationsRequestSchema, GetTranslationsResponseSchema, GetUiViewRequestSchema, GetUiViewResponseSchema, GetViewRequestSchema, GetViewResponseSchema, HttpFindQueryParamsSchema, ListAiConversationsRequestSchema, ListAiConversationsResponseSchema, ListAiPendingActionsRequestSchema, ListAiPendingActionsResponseSchema, ListNotificationsRequestSchema, ListNotificationsResponseSchema, ListViewsRequestSchema, ListViewsResponseSchema, MarkAllNotificationsReadRequestSchema, MarkAllNotificationsReadResponseSchema, MarkNotificationsReadRequestSchema, MarkNotificationsReadResponseSchema, NotificationPreferencesSchema, RealtimeConnectRequestSchema, RealtimeConnectResponseSchema, RealtimeDisconnectRequestSchema, RealtimeDisconnectResponseSchema, RealtimeSubscribeRequestSchema, RealtimeSubscribeResponseSchema, RealtimeUnsubscribeRequestSchema, RealtimeUnsubscribeResponseSchema, RegisterDeviceRequestSchema, RegisterDeviceResponseSchema, RejectAiPendingActionResponseSchema, SaveMetaItemRequestSchema, SaveMetaItemResponseSchema, SetPresenceRequestSchema, SetPresenceResponseSchema, UnregisterDeviceRequestSchema, UnregisterDeviceResponseSchema, UpdateAiConversationRequestSchema, UpdateDataRequestSchema, UpdateDataResponseSchema, UpdateManyDataRequestSchema, UpdateManyDataResponseSchema, UpdateNotificationPreferencesRequestSchema, UpdateNotificationPreferencesResponseSchema, UpdateViewRequestSchema, UpdateViewResponseSchema } from '@objectstack/spec/api';
import type { AiAgentCapabilities, AiAgentChatRequest, AiAgentSummary, AiAgentsResponse, AiChatRequest, AiChatResponse, AiCompleteRequest, AiConversation, AiMessage, AiModelsResponse, AiPendingAction, AiPendingActionStatus, AiStreamChunk, ApproveAiPendingActionResponse, AutomationActionsResponse, AutomationTriggerRequest, AutomationTriggerResponse, BatchDataRequest, BatchDataResponse, CheckPermissionRequest, CheckPermissionResponse, CreateAiConversationRequest, CreateDataRequest, CreateDataResponse, CreateManyDataRequest, CreateManyDataResponse, CreateViewRequest, CreateViewResponse, DeleteDataRequest, DeleteDataResponse, DeleteManyDataRequest, DeleteManyDataResponse, DeleteMetaItemRequest, DeleteMetaItemResponse, DeleteViewRequest, DeleteViewResponse, FindDataRequest, FindDataResponse, GetDataRequest, GetDataResponse, GetDiscoveryRequest, GetDiscoveryResponse, GetEffectivePermissionsRequest, GetEffectivePermissionsResponse, GetFieldLabelsRequest, GetFieldLabelsResponse, GetLocalesRequest, GetLocalesResponse, GetMetaItemCachedRequest, GetMetaItemCachedResponse, GetMetaItemRequest, GetMetaItemResponse, GetMetaItemsRequest, GetMetaItemsResponse, GetMetaTypesRequest, GetMetaTypesResponse, GetNotificationPreferencesRequest, GetNotificationPreferencesResponse, GetObjectPermissionsRequest, GetObjectPermissionsResponse, GetPresenceRequest, GetPresenceResponse, GetTranslationsRequest, GetTranslationsResponse, GetUiViewRequest, GetUiViewResponse, GetViewRequest, GetViewResponse, ListAiConversationsRequest, ListAiConversationsResponse, ListAiPendingActionsRequest, ListAiPendingActionsResponse, ListNotificationsRequest, ListNotificationsResponse, ListViewsRequest, ListViewsResponse, MarkAllNotificationsReadRequest, MarkAllNotificationsReadResponse, MarkNotificationsReadRequest, MarkNotificationsReadResponse, NotificationPreferences, RealtimeConnectRequest, RealtimeConnectResponse, RealtimeDisconnectRequest, RealtimeDisconnectResponse, RealtimeSubscribeRequest, RealtimeSubscribeResponse, RealtimeUnsubscribeRequest, RealtimeUnsubscribeResponse, RegisterDeviceRequest, RegisterDeviceResponse, RejectAiPendingActionResponse, SaveMetaItemRequest, SaveMetaItemResponse, SetPresenceRequest, SetPresenceResponse, UnregisterDeviceRequest, UnregisterDeviceResponse, UpdateAiConversationRequest, UpdateDataRequest, UpdateDataResponse, UpdateManyDataRequest, UpdateManyDataResponse, UpdateNotificationPreferencesRequest, UpdateNotificationPreferencesResponse, UpdateViewRequest, UpdateViewResponse } from '@objectstack/spec/api';
import { AiAgentCapabilitiesSchema, AiAgentChatRequestSchema, AiAgentSummarySchema, AiAgentsResponseSchema, AiChatRequestSchema, AiChatResponseSchema, AiCompleteRequestSchema, AiConversationSchema, AiMessageSchema, AiModelsResponseSchema, AiPendingActionSchema, AiPendingActionStatusSchema, AiStreamChunkSchema, ApproveAiPendingActionResponseSchema, AutomationActionsResponseSchema, AutomationTriggerRequestSchema, AutomationTriggerResponseSchema, BatchDataRequestSchema, BatchDataResponseSchema, CheckPermissionRequestSchema, CheckPermissionResponseSchema, CreateAiConversationRequestSchema, CreateDataRequestSchema, CreateDataResponseSchema, CreateManyDataRequestSchema, CreateManyDataResponseSchema, CreateViewRequestSchema, CreateViewResponseSchema, DeleteDataRequestSchema, DeleteDataResponseSchema, DeleteManyDataRequestSchema, DeleteManyDataResponseSchema, DeleteMetaItemRequestSchema, DeleteMetaItemResponseSchema, DeleteViewRequestSchema, DeleteViewResponseSchema, FindDataRequestSchema, FindDataResponseSchema, GetDataRequestSchema, GetDataResponseSchema, GetDiscoveryRequestSchema, GetDiscoveryResponseSchema, GetEffectivePermissionsRequestSchema, GetEffectivePermissionsResponseSchema, GetFieldLabelsRequestSchema, GetFieldLabelsResponseSchema, GetLocalesRequestSchema, GetLocalesResponseSchema, GetMetaItemCachedRequestSchema, GetMetaItemCachedResponseSchema, GetMetaItemRequestSchema, GetMetaItemResponseSchema, GetMetaItemsRequestSchema, GetMetaItemsResponseSchema, GetMetaTypesRequestSchema, GetMetaTypesResponseSchema, GetNotificationPreferencesRequestSchema, GetNotificationPreferencesResponseSchema, GetObjectPermissionsRequestSchema, GetObjectPermissionsResponseSchema, GetPresenceRequestSchema, GetPresenceResponseSchema, GetTranslationsRequestSchema, GetTranslationsResponseSchema, GetUiViewRequestSchema, GetUiViewResponseSchema, GetViewRequestSchema, GetViewResponseSchema, HttpFindQueryParamsSchema, ListAiConversationsRequestSchema, ListAiConversationsResponseSchema, ListAiPendingActionsRequestSchema, ListAiPendingActionsResponseSchema, ListNotificationsRequestSchema, ListNotificationsResponseSchema, ListViewsRequestSchema, ListViewsResponseSchema, MarkAllNotificationsReadRequestSchema, MarkAllNotificationsReadResponseSchema, MarkNotificationsReadRequestSchema, MarkNotificationsReadResponseSchema, NotificationSchema, NotificationPreferencesSchema, RealtimeConnectRequestSchema, RealtimeConnectResponseSchema, RealtimeDisconnectRequestSchema, RealtimeDisconnectResponseSchema, RealtimeSubscribeRequestSchema, RealtimeSubscribeResponseSchema, RealtimeUnsubscribeRequestSchema, RealtimeUnsubscribeResponseSchema, RegisterDeviceRequestSchema, RegisterDeviceResponseSchema, RejectAiPendingActionResponseSchema, SaveMetaItemRequestSchema, SaveMetaItemResponseSchema, SetPresenceRequestSchema, SetPresenceResponseSchema, UnregisterDeviceRequestSchema, UnregisterDeviceResponseSchema, UpdateAiConversationRequestSchema, UpdateDataRequestSchema, UpdateDataResponseSchema, UpdateManyDataRequestSchema, UpdateManyDataResponseSchema, UpdateNotificationPreferencesRequestSchema, UpdateNotificationPreferencesResponseSchema, UpdateViewRequestSchema, UpdateViewResponseSchema } from '@objectstack/spec/api';
import type { AiAgentCapabilities, AiAgentChatRequest, AiAgentSummary, AiAgentsResponse, AiChatRequest, AiChatResponse, AiCompleteRequest, AiConversation, AiMessage, AiModelsResponse, AiPendingAction, AiPendingActionStatus, AiStreamChunk, ApproveAiPendingActionResponse, AutomationActionsResponse, AutomationTriggerRequest, AutomationTriggerResponse, BatchDataRequest, BatchDataResponse, CheckPermissionRequest, CheckPermissionResponse, CreateAiConversationRequest, CreateDataRequest, CreateDataResponse, CreateManyDataRequest, CreateManyDataResponse, CreateViewRequest, CreateViewResponse, DeleteDataRequest, DeleteDataResponse, DeleteManyDataRequest, DeleteManyDataResponse, DeleteMetaItemRequest, DeleteMetaItemResponse, DeleteViewRequest, DeleteViewResponse, FindDataRequest, FindDataResponse, GetDataRequest, GetDataResponse, GetDiscoveryRequest, GetDiscoveryResponse, GetEffectivePermissionsRequest, GetEffectivePermissionsResponse, GetFieldLabelsRequest, GetFieldLabelsResponse, GetLocalesRequest, GetLocalesResponse, GetMetaItemCachedRequest, GetMetaItemCachedResponse, GetMetaItemRequest, GetMetaItemResponse, GetMetaItemsRequest, GetMetaItemsResponse, GetMetaTypesRequest, GetMetaTypesResponse, GetNotificationPreferencesRequest, GetNotificationPreferencesResponse, GetObjectPermissionsRequest, GetObjectPermissionsResponse, GetPresenceRequest, GetPresenceResponse, GetTranslationsRequest, GetTranslationsResponse, GetUiViewRequest, GetUiViewResponse, GetViewRequest, GetViewResponse, ListAiConversationsRequest, ListAiConversationsResponse, ListAiPendingActionsRequest, ListAiPendingActionsResponse, ListNotificationsRequest, ListNotificationsResponse, ListViewsRequest, ListViewsResponse, MarkAllNotificationsReadRequest, MarkAllNotificationsReadResponse, MarkNotificationsReadRequest, MarkNotificationsReadResponse, Notification, NotificationPreferences, RealtimeConnectRequest, RealtimeConnectResponse, RealtimeDisconnectRequest, RealtimeDisconnectResponse, RealtimeSubscribeRequest, RealtimeSubscribeResponse, RealtimeUnsubscribeRequest, RealtimeUnsubscribeResponse, RegisterDeviceRequest, RegisterDeviceResponse, RejectAiPendingActionResponse, SaveMetaItemRequest, SaveMetaItemResponse, SetPresenceRequest, SetPresenceResponse, UnregisterDeviceRequest, UnregisterDeviceResponse, UpdateAiConversationRequest, UpdateDataRequest, UpdateDataResponse, UpdateManyDataRequest, UpdateManyDataResponse, UpdateNotificationPreferencesRequest, UpdateNotificationPreferencesResponse, UpdateViewRequest, UpdateViewResponse } from '@objectstack/spec/api';

// Validate data
const result = AiAgentCapabilitiesSchema.parse(data);
Expand DownExpand Up@@ -1092,6 +1092,24 @@ const result = AiAgentCapabilitiesSchema.parse(data);
| **readCount** | `number` | ✅ | Number of notifications marked as read |


---

## Notification

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **id** | `string` | ✅ | Notification ID |
| **type** | `string` | ✅ | Notification type |
| **title** | `string` | ✅ | Notification title |
| **body** | `string` | ✅ | Notification body text |
| **read** | `boolean` | ✅ | Whether notification has been read |
| **data** | `Record<string, any>` | optional | Additional notification data |
| **actionUrl** | `string` | optional | URL to navigate to when clicked |
| **createdAt** | `string` | ✅ | When notification was created |


---

## NotificationPreferences
Expand Down
22 changes: 2 additions & 20 deletions content/docs/references/system/notification.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,8 +50,8 @@ Supports variables for personalization and file attachments.
## TypeScript Usage

```typescript
import { EmailTemplateSchema, InAppNotificationSchema, NotificationChannelSchema, NotificationConfigSchema, PushNotificationSchema, SMSTemplateSchema } from '@objectstack/spec/system';
import type { EmailTemplate, InAppNotification, NotificationChannel, NotificationConfig, PushNotification, SMSTemplate } from '@objectstack/spec/system';
import { EmailTemplateSchema, InAppNotificationSchema, NotificationChannelSchema, PushNotificationSchema, SMSTemplateSchema } from '@objectstack/spec/system';
import type { EmailTemplate, InAppNotification, NotificationChannel, PushNotification, SMSTemplate } from '@objectstack/spec/system';

// Validate data
const result = EmailTemplateSchema.parse(data);
Expand DownExpand Up@@ -106,24 +106,6 @@ Notification delivery channel (implemented today: inbox, email, sms — push/sla
* `webhook`


---

## NotificationConfig

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **id** | `string` | ✅ | Notification ID |
| **name** | `string` | ✅ | Notification name |
| **channel** | `Enum<'email' \| 'sms' \| 'push' \| 'in-app' \| 'slack' \| 'teams' \| 'webhook'>` | ✅ | Notification channel |
| **template** | `{ id: string; subject: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; body: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; bodyType?: Enum<'text' \| 'html' \| 'markdown'>; … } \| { id: string; message: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; maxLength?: number; variables?: string[] } \| { title: string; body: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; icon?: string; badge?: number; … } \| { title: string; message: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; type: Enum<'info' \| 'success' \| 'warning' \| 'error'>; actionUrl?: string; … }` | ✅ | Notification template |
| **recipients** | `{ to: string[]; cc?: string[]; bcc?: string[] }` | ✅ | Recipients |
| **schedule** | `{ type: Enum<'immediate' \| 'delayed' \| 'scheduled'>; delay?: number; scheduledAt?: number }` | optional | Scheduling |
| **retryPolicy** | `{ enabled?: boolean; maxRetries?: number; backoffStrategy: Enum<'exponential' \| 'linear' \| 'fixed'> }` | optional | Retry policy |
| **tracking** | `{ trackOpens?: boolean; trackClicks?: boolean; trackDelivery?: boolean }` | optional | Tracking configuration |


---

## PushNotification
Expand Down
Loading
Loading