@opencxh/domain 1.185.0 → 1.187.0

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.
@@ -0,0 +1,114 @@
1
+ import { LocaleBundle } from '../entities/analytics/dashboard';
2
+ /**
3
+ * Role group of an app that declares its own notification kinds.
4
+ *
5
+ * A constant and not a literal at both ends: `Bridge.providers.list` answers an unknown group
6
+ * with an empty list, so a typo would read as "no app sends notifications". Same reason
7
+ * `ONBOARDING_SOURCE_PROVIDER_GROUP` is a constant.
8
+ */
9
+ export declare const NOTIFICATION_SOURCE_PROVIDER_GROUP = "notification-source";
10
+ /**
11
+ * Text a reader renders without the sender knowing what it says: an i18n key from the declaring
12
+ * app's own bundle, plus values for `{name}` placeholders.
13
+ *
14
+ * Not a rendered string, because one notification is read on a laptop in Dutch and pushed to a
15
+ * phone in English. Not a bare key either: half of these lines carry data ("Vincent mentioned you")
16
+ * and a key per person does not exist.
17
+ *
18
+ * Sources write **bare** keys; the hub stamps the namespace (see `qualifyText` in platform-api).
19
+ */
20
+ export interface NotificationText {
21
+ key: string;
22
+ vars?: Record<string, string | number>;
23
+ }
24
+ /**
25
+ * One switchable kind of notification, declared by the app that sends it.
26
+ *
27
+ * Declared rather than listed centrally, because only the owning app knows that "you were
28
+ * mentioned" and "a task was assigned to you" are different questions to a reader. A hard-coded
29
+ * list breaks at app N+1 — the map it replaces is `DEFAULT_KIND_ENABLED` in comms' push dispatch.
30
+ */
31
+ export interface NotificationKindDef {
32
+ /** Bare in the describe (`"mentioned"`); the hub stamps it to `"communication:mentioned"`. */
33
+ id: string;
34
+ title: NotificationText;
35
+ description?: NotificationText;
36
+ /** What holds until the user touches a switch. Per transport, because they differ in practice. */
37
+ defaults: {
38
+ inApp: boolean;
39
+ push: boolean;
40
+ };
41
+ /** `true` = the user may not switch it off (an incoming call). Absent = they may. */
42
+ mandatory?: boolean;
43
+ /** Sort hint within an app. Ties break on id, so the settings screen stays stable. */
44
+ order?: number;
45
+ }
46
+ /**
47
+ * Payload of `GET /provider/notification/describe`.
48
+ *
49
+ * Bare, not wrapped in `ResponseFactory` — that is how the fan-out reads it, same as
50
+ * `analytics-source` and `onboarding-source`.
51
+ */
52
+ export interface NotificationSourceDescribe {
53
+ /** The declaring app (== manifest name == `req.source.app`). */
54
+ source: string;
55
+ kinds: NotificationKindDef[];
56
+ /** Texts for every key above, in every language this app ships. `flattenLocales()` helps. */
57
+ locales?: LocaleBundle;
58
+ }
59
+ /** Where clicking a notification takes you. App-relative, like an onboarding route link. */
60
+ export interface NotificationLink {
61
+ app: string;
62
+ path: string;
63
+ }
64
+ export interface Notification {
65
+ id: string;
66
+ organizationId: string;
67
+ userId: string;
68
+ /** The app that sent it. Derived from the caller, never from the body. */
69
+ source: string;
70
+ /** Namespaced: `"communication:mentioned"`. */
71
+ kind: string;
72
+ title: NotificationText;
73
+ body?: NotificationText;
74
+ link?: NotificationLink;
75
+ /** Free-form; travels untouched into the push payload. */
76
+ data?: Record<string, unknown>;
77
+ createdAt: number;
78
+ readAt?: number;
79
+ }
80
+ /** Body of `POST notification/send`. The only thing that crosses an app boundary. */
81
+ export interface NotifyRequest {
82
+ to: {
83
+ userIds: string[];
84
+ } | {
85
+ userId: string;
86
+ };
87
+ /** Bare; stamped with the calling app's name server-side. */
88
+ kind: string;
89
+ title: NotificationText;
90
+ body?: NotificationText;
91
+ link?: NotificationLink;
92
+ data?: Record<string, unknown>;
93
+ /**
94
+ * Wake the devices without leaving a row behind. For a signal that is meaningless once it is
95
+ * over — an incoming call you already missed is not something to catch up on. Default `false`.
96
+ */
97
+ transient?: boolean;
98
+ /** Passed straight to push; a newer envelope with the same key replaces an undelivered one. */
99
+ collapseKey?: string;
100
+ }
101
+ export interface NotifyResult {
102
+ /** Rows written. `0` for a transient notification, and for a send nobody had switched on. */
103
+ created: number;
104
+ }
105
+ /** Result of the kinds fan-out, as the settings screen reads it. */
106
+ export interface NotificationCatalog {
107
+ kinds: NotificationKindDef[];
108
+ locales: LocaleBundle;
109
+ /** Sources that did not answer, so the screen can say who is missing instead of silently thinning. */
110
+ degradedApps: string[];
111
+ }
112
+ /** Per-device preference keys. Two transports, two keys — see `docs/` and the settings screen. */
113
+ export type NotificationPrefTransport = "inapp" | "push";
114
+ export declare const notificationPrefKey: (transport: NotificationPrefTransport, kind: string) => string;
@@ -17,7 +17,7 @@ export declare const APP_PERMISSIONS: {
17
17
  readonly analytics: readonly ["report.read"];
18
18
  readonly "app-store": readonly ["app.publish", "app.read", "app.write", "setting.read", "setting.write"];
19
19
  readonly automations: readonly ["sync.read", "sync.write", "webhook.read", "webhook.write"];
20
- readonly communication: readonly ["account.read", "account.write", "activity-type.read", "activity.read", "activity.write", "attachment.read", "attribute.read", "attribute.write", "calendar.read", "calendar.write", "channel.read", "channel.write", "custom-field.read", "custom-field.write", "folder.read", "folder.write", "inbox.read", "inbox.write", "interaction.read", "interaction.write", "reminder.read", "task.read", "task.write", "template.read", "template.write", "topic.read", "topic.write"];
20
+ readonly communication: readonly ["account.read", "account.write", "activity-type.read", "activity.read", "activity.write", "attachment.read", "attribute.read", "attribute.write", "calendar.read", "calendar.write", "channel.read", "channel.write", "custom-field.read", "custom-field.write", "folder.read", "folder.write", "inbox.read", "inbox.write", "interaction.read", "interaction.write", "reminder.read", "template.read", "template.write", "topic.read", "topic.write"];
21
21
  readonly context: readonly ["kind.read", "kind.write", "memory.read", "memory.write"];
22
22
  readonly crm: readonly ["company.read", "company.write", "contact.read", "contact.write"];
23
23
  readonly "eylo-voip": readonly ["account.read", "account.write", "callflow.read", "callflow.write", "channel.read", "channel.write", "contact.read", "contact.write", "device.read", "device.write", "group.read", "group.write", "interaction.read", "interaction.write", "media.read", "media.write", "menu.read", "menu.write", "phone-number.read", "phone-number.write", "recording.read", "recording.write", "sip.read", "temporal-rule.read", "temporal-rule.write", "user.read", "user.write", "vmbox.read", "vmbox.write", "webhook.read", "webhook.write"];
@@ -16,7 +16,13 @@ export interface PushSubscription {
16
16
  createdAt: number;
17
17
  lastSeenAt: number;
18
18
  }
19
- export type PushKind = "wake" | "incoming-call" | "message" | "presence" | "notification" | "custom";
19
+ /**
20
+ * The platform's own kinds, plus whatever a `notification-source` app declares
21
+ * (`"communication:mentioned"`). Open like {@link ExtensionContext}: the closed union kept
22
+ * autocomplete but made an app-declared kind unexpressible, and per-kind device preferences
23
+ * are exactly what apps must be able to declare.
24
+ */
25
+ export type PushKind = "wake" | "incoming-call" | "message" | "presence" | "notification" | "custom" | (string & {});
20
26
  export interface PushDisplay {
21
27
  title: string;
22
28
  body?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencxh/domain",
3
- "version": "1.185.0",
3
+ "version": "1.187.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -1 +0,0 @@
1
- export * from './types';
@@ -1,39 +0,0 @@
1
- import { OwnerScope } from '../scope/types';
2
- export type TaskStatus = "open" | "in_progress" | "done" | "cancelled";
3
- export type TaskPriority = "low" | "normal" | "high" | "urgent";
4
- export type TaskInitiator = "user" | "system";
5
- export interface TaskSource {
6
- initiator: TaskInitiator;
7
- /** Free-form reason when initiator is "system" (e.g. "missed_call", "stale_interaction"). */
8
- systemReason?: string;
9
- }
10
- export interface Task {
11
- id: string;
12
- organizationId: string;
13
- ownerScope: OwnerScope;
14
- /** Parent task id when this is a subtask. Subtasks are exactly one level deep. */
15
- parentId?: string;
16
- title: string;
17
- description?: string;
18
- status: TaskStatus;
19
- priority?: TaskPriority;
20
- /** Epoch ms. */
21
- dueDate?: number;
22
- /** Epoch ms — set when status moves to "done". */
23
- completedAt?: number;
24
- /** Link to a communication Interaction (follow-up on a call/message). */
25
- interactionId?: string;
26
- /** Link to a contact. */
27
- contactId?: string;
28
- /** UserIds assigned to act on this task. Empty = team pool. */
29
- assignees?: string[];
30
- /** Origin of the task — user-created or system-generated. */
31
- source: TaskSource;
32
- /** Channel used for external sync (provider-resolved). */
33
- channelId?: string;
34
- /** Provider-side IDs for dedupe (Google Tasks id, MS To Do id, …). */
35
- externalIds?: string[];
36
- createdBy: string;
37
- createdAt?: number;
38
- updatedAt?: number;
39
- }