ab-ecosystem-cpass 1.0.0 → 1.0.6

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.
Files changed (86) hide show
  1. package/README.md +121 -21
  2. package/dist/client.d.ts +38 -0
  3. package/dist/client.js +49 -5
  4. package/dist/client.js.map +1 -1
  5. package/dist/http.d.ts +15 -0
  6. package/dist/http.js +21 -1
  7. package/dist/http.js.map +1 -1
  8. package/dist/index.d.ts +11 -0
  9. package/dist/index.js +24 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/internal/bytes.d.ts +38 -0
  12. package/dist/internal/bytes.js +84 -0
  13. package/dist/internal/bytes.js.map +1 -0
  14. package/dist/internal/device.d.ts +24 -0
  15. package/dist/internal/device.js +91 -0
  16. package/dist/internal/device.js.map +1 -0
  17. package/dist/internal/md5.d.ts +4 -0
  18. package/dist/internal/md5.js +109 -0
  19. package/dist/internal/md5.js.map +1 -0
  20. package/dist/internal/receiver-file.d.ts +106 -0
  21. package/dist/internal/receiver-file.js +128 -0
  22. package/dist/internal/receiver-file.js.map +1 -0
  23. package/dist/realtime/client.d.ts +85 -0
  24. package/dist/realtime/client.js +126 -0
  25. package/dist/realtime/client.js.map +1 -0
  26. package/dist/realtime/events.d.ts +147 -0
  27. package/dist/realtime/events.js +3 -0
  28. package/dist/realtime/events.js.map +1 -0
  29. package/dist/realtime/inbox.d.ts +173 -0
  30. package/dist/realtime/inbox.js +263 -0
  31. package/dist/realtime/inbox.js.map +1 -0
  32. package/dist/realtime/index.d.ts +16 -0
  33. package/dist/realtime/index.js +37 -0
  34. package/dist/realtime/index.js.map +1 -0
  35. package/dist/resources/audience/contacts.d.ts +19 -3
  36. package/dist/resources/audience/contacts.js +19 -3
  37. package/dist/resources/audience/contacts.js.map +1 -1
  38. package/dist/resources/audience/inbox-media.d.ts +92 -0
  39. package/dist/resources/audience/inbox-media.js +127 -0
  40. package/dist/resources/audience/inbox-media.js.map +1 -0
  41. package/dist/resources/audience/notifications.d.ts +171 -0
  42. package/dist/resources/audience/notifications.js +215 -0
  43. package/dist/resources/audience/notifications.js.map +1 -0
  44. package/dist/resources/audience/scheduled-messages.d.ts +234 -0
  45. package/dist/resources/audience/scheduled-messages.js +259 -0
  46. package/dist/resources/audience/scheduled-messages.js.map +1 -0
  47. package/dist/resources/campaign-audience.d.ts +132 -0
  48. package/dist/resources/campaign-audience.js +224 -0
  49. package/dist/resources/campaign-audience.js.map +1 -0
  50. package/dist/resources/campaign-report-exports.d.ts +14 -0
  51. package/dist/resources/campaign-report-exports.js +28 -0
  52. package/dist/resources/campaign-report-exports.js.map +1 -0
  53. package/dist/resources/campaign-reports.d.ts +15 -0
  54. package/dist/resources/campaign-reports.js +25 -0
  55. package/dist/resources/campaign-reports.js.map +1 -0
  56. package/dist/resources/campaigns.d.ts +239 -0
  57. package/dist/resources/campaigns.js +300 -0
  58. package/dist/resources/campaigns.js.map +1 -0
  59. package/dist/types/audience.d.ts +93 -1
  60. package/dist/types/campaign.d.ts +229 -0
  61. package/dist/types/campaign.js +3 -0
  62. package/dist/types/campaign.js.map +1 -0
  63. package/dist/types/contact-filter.d.ts +3 -2
  64. package/dist/types/contact-filter.js +3 -2
  65. package/dist/types/contact-filter.js.map +1 -1
  66. package/dist/types/inbox.d.ts +391 -0
  67. package/dist/types/inbox.js +16 -0
  68. package/dist/types/inbox.js.map +1 -0
  69. package/dist/types/index.d.ts +3 -0
  70. package/dist/types/index.js +5 -0
  71. package/dist/types/index.js.map +1 -1
  72. package/dist/types/scheduled-message.d.ts +221 -0
  73. package/dist/types/scheduled-message.js +16 -0
  74. package/dist/types/scheduled-message.js.map +1 -0
  75. package/docs/audience/contacts.md +34 -4
  76. package/docs/audience/notifications.md +263 -0
  77. package/docs/audience/scheduled-messages.md +557 -0
  78. package/docs/campaign/campaigns.md +489 -0
  79. package/docs/contact-filter.md +2 -1
  80. package/docs/inbox/chats.md +356 -0
  81. package/docs/inbox/connection.md +171 -0
  82. package/docs/inbox/media.md +202 -0
  83. package/docs/inbox/messages.md +338 -0
  84. package/docs/inbox/referrals.md +72 -0
  85. package/docs/inbox/sessions.md +74 -0
  86. package/package.json +28 -2
@@ -97,6 +97,12 @@ export interface Contact {
97
97
  */
98
98
  export interface CreateContactInput {
99
99
  name?: Partial<ContactName>;
100
+ /**
101
+ * Creates the contact's WhatsApp identity with it, and writes its id to
102
+ * `profile.whatsapp`. Any shape is accepted -- `+91 98765 43210` -- and stored
103
+ * as digits only, which is the form an inbound message carries. Create-only.
104
+ */
105
+ whatsapp?: string;
100
106
  order?: string;
101
107
  /** Contact tag ids. Default `[]`. */
102
108
  tags?: ObjectId[];
@@ -115,7 +121,11 @@ export interface CreateContactInput {
115
121
  blocked?: Partial<ContactBlocked>;
116
122
  };
117
123
  }
118
- export type UpdateContactInput = Partial<CreateContactInput>;
124
+ /**
125
+ * `whatsapp` is absent: changing a channel identity is a write to the profile
126
+ * collection, which a contact patch cannot express.
127
+ */
128
+ export type UpdateContactInput = Partial<Omit<CreateContactInput, 'whatsapp'>>;
119
129
  /**
120
130
  * `search` matches the first and last name, plus the name and identifier of
121
131
  * every channel identity. `segment` names a saved filter; `filter` carries
@@ -510,3 +520,85 @@ export interface WebhookEnvelope {
510
520
  createdAt: IsoDate;
511
521
  data: unknown;
512
522
  }
523
+ /**
524
+ * Where a push notification can be delivered.
525
+ *
526
+ * It decides which half of the FCM payload the send path fills in -- `webpush`
527
+ * for a browser, `android`/`apns` for an app -- and it is the one thing about a
528
+ * device that cannot be inferred from its token. A registration token is an
529
+ * opaque string identifying a browser or app *instance*; nothing in it says what
530
+ * kind of thing that instance is.
531
+ */
532
+ export type DevicePlatform = 'web' | 'android' | 'ios';
533
+ /**
534
+ * A subscribed device, as it reads back.
535
+ *
536
+ * The registration token is **never** returned. It is a send credential --
537
+ * anything holding one can push to that browser through the agency's Firebase
538
+ * project -- and the list exists so a user can recognise and revoke their own
539
+ * devices, which needs the label and the last-seen time and nothing more.
540
+ */
541
+ export interface Device {
542
+ id: ObjectId;
543
+ platform: DevicePlatform;
544
+ /** Whatever `device_label` was sent on registration; `''` when none was. */
545
+ label: string;
546
+ /** The reseller frontend it registered from; `null` for the white-label one. */
547
+ clientId: string | null;
548
+ /**
549
+ * Last time a notification to this device succeeded, or it re-subscribed.
550
+ *
551
+ * The only signal that a device is still real: FCM does not revoke a token
552
+ * when an app is uninstalled or a browser's site data is cleared, it simply
553
+ * stops delivering to it. A device unseen for 60 days is pruned on that basis,
554
+ * which is another reason to call {@link Notifications.subscribe} on every app
555
+ * load rather than once.
556
+ */
557
+ lastSeenAt: IsoDate;
558
+ createdAt: IsoDate;
559
+ }
560
+ /**
561
+ * What `notifications.subscribe()` takes. Mirrors the wire body field for field.
562
+ *
563
+ * There is deliberately no user field. The acting user comes from the verified
564
+ * token the client was constructed with; a body that could name its own user
565
+ * would let any caller point someone else's notifications at their browser.
566
+ */
567
+ export interface SubscribeNotificationsInput {
568
+ /**
569
+ * The FCM registration token for this browser or app instance, from
570
+ * `getToken()`. 20..4096 characters -- a sanity check rather than a format,
571
+ * since Google publishes no shape for these.
572
+ */
573
+ token: string;
574
+ /**
575
+ * Defaults to `'web'` when the SDK can see a DOM, and is otherwise required:
576
+ * a React Native runtime has a `navigator` but no `document`, and guessing
577
+ * `'web'` there would send the wrong half of every payload.
578
+ */
579
+ platform?: DevicePlatform;
580
+ /**
581
+ * What the user would recognise this device as, for the device list -- up to
582
+ * 200 characters. Left out, the SDK derives something like `Chrome on Windows`
583
+ * from the user agent when it is running in a browser. Cosmetic, never matched
584
+ * on.
585
+ */
586
+ device_label?: string;
587
+ /**
588
+ * The reseller client this frontend belongs to (`tpc_…`), when there is one.
589
+ *
590
+ * Rarely needed: the client id the `CrmClient` already sends is verified and
591
+ * wins. Supplying a *different* one is refused with 403 rather than ignored.
592
+ * Omitted by the white-label frontend, which has no client.
593
+ */
594
+ tpc_id?: string;
595
+ }
596
+ /**
597
+ * `page` and `rows` only.
598
+ *
599
+ * The endpoint's schema accepts `search` because it shares the common list
600
+ * query, but the service does not apply it -- a device list is one person's few
601
+ * rows, so there is nothing to search. It is left out here rather than accepted
602
+ * and silently dropped.
603
+ */
604
+ export type ListDevicesQuery = Pick<ListQuery, 'page' | 'rows'>;
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Campaign shapes. Declared here rather than imported from `@crm/contracts` so
3
+ * the published package has no private dependencies; they are kept in step with
4
+ * the zod schemas in `packages/contracts/src/campaign/`.
5
+ */
6
+ import type { IsoDate, JsonObject } from './common';
7
+ import type { ContactFilterEntry } from './contact-filter';
8
+ export type CampaignStatus = 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled';
9
+ /** What the campaign charges for. `payment` is the collect-a-payment flow. */
10
+ export type CampaignType = 'general' | 'payment';
11
+ export type CampaignTrigger = 'immediate' | 'schedule';
12
+ export type CampaignRepeatUnit = 'minutes' | 'hours' | 'days' | 'weeks' | 'months';
13
+ export interface CampaignRepeat {
14
+ every: number;
15
+ unit: CampaignRepeatUnit;
16
+ /** Inclusive: the last occurrence is the final one at or before this moment. */
17
+ until?: IsoDate;
18
+ }
19
+ /**
20
+ * A bulk recipient list held in an uploaded file.
21
+ *
22
+ * This is what `campaigns.createBulk()` produces, and it is the only audience
23
+ * mode whose contents are not in the campaign document: the numbers live in an
24
+ * encrypted file and the campaign holds a pointer to it.
25
+ */
26
+ export interface CampaignNumbersFilter {
27
+ /**
28
+ * HTTPS URL of the encrypted receiver file. Must be a public host -- the
29
+ * service that sends the campaign fetches it.
30
+ */
31
+ numbers: string;
32
+ /**
33
+ * The secret the file was encrypted under, 32-128 hex characters.
34
+ *
35
+ * Named `hash` for historical reasons; it is a key, not a digest. Whatever
36
+ * reads the file derives the AES key from it. Treat it as a credential: anyone
37
+ * holding this and the URL can read the recipient list.
38
+ */
39
+ hash: string;
40
+ }
41
+ /** Re-target by what happened to a previous campaign's messages. */
42
+ export interface CampaignRetargetStatusFilter {
43
+ type: 'status';
44
+ /** The campaign to re-target. */
45
+ cid: string;
46
+ /** Its numeric message id. A number, not a string. */
47
+ cmid: number;
48
+ status: 'all' | 'sent' | 'delivered' | 'read' | 'replied' | 'failed';
49
+ delivered?: boolean | null;
50
+ read?: boolean | null;
51
+ replied?: boolean | null;
52
+ /** Hours within which a reply counts. */
53
+ replied_in?: number | null;
54
+ reply_type?: string | null;
55
+ reply?: string[] | null;
56
+ }
57
+ /** Re-target an explicit set of previous recipients. */
58
+ export interface CampaignRetargetSelectionFilter {
59
+ type: 'selection';
60
+ records: string[];
61
+ }
62
+ /**
63
+ * One entry in a campaign's `filters`.
64
+ *
65
+ * The audience is one field with four modes, and the mode is decided by the
66
+ * **shape of the array** rather than by a selector:
67
+ *
68
+ * | `filters` | Audience |
69
+ * |---|---|
70
+ * | `[]` | every contact in the account |
71
+ * | `ContactFilterEntry[]` | a segment, built from contact conditions |
72
+ * | `[{numbers, hash}]` | a bulk list from an uploaded file |
73
+ * | `[{type: 'status' \| 'selection', …}]` | re-target of an earlier campaign |
74
+ *
75
+ * The modes cannot be mixed, and the three non-segment modes take exactly one
76
+ * entry -- everything downstream reads `filters[0]` and nothing else. The server
77
+ * rejects a mixture rather than guessing.
78
+ */
79
+ export type CampaignFilter = CampaignNumbersFilter | CampaignRetargetStatusFilter | CampaignRetargetSelectionFilter | ContactFilterEntry;
80
+ /** Which of the four modes a `filters` array expresses. */
81
+ export type CampaignAudienceKind = 'all' | 'segment' | 'numbers' | 'retarget';
82
+ export interface CampaignMessage {
83
+ id?: string | number;
84
+ channel: {
85
+ id: string;
86
+ version?: string;
87
+ category?: string;
88
+ };
89
+ category?: string;
90
+ /** The provider payload. Shape is per channel type, so it is left open. */
91
+ message: JsonObject;
92
+ /** Save a reply to a contact field. */
93
+ replyStatus?: {
94
+ save: boolean;
95
+ fieldId: string;
96
+ };
97
+ polls?: string[];
98
+ /** Anything else the panel put here is carried through. */
99
+ [key: string]: unknown;
100
+ }
101
+ /** Who launched a campaign and from where. Stamped by the server; never sent. */
102
+ export interface CampaignTriggerInfo {
103
+ userIp?: string;
104
+ userType?: string;
105
+ psuid?: string;
106
+ userRefId?: string;
107
+ [key: string]: unknown;
108
+ }
109
+ export interface Campaign {
110
+ id: string;
111
+ version: string;
112
+ crmVersion: string;
113
+ name: string;
114
+ /**
115
+ * The dispatch route, as the sending service understands it -- `general`,
116
+ * `bulk_campaign`, `retarget`, and others. Distinct from `campaignType`.
117
+ */
118
+ type: string;
119
+ campaignType: CampaignType;
120
+ trigger: CampaignTrigger;
121
+ triggerInfo: CampaignTriggerInfo;
122
+ channelType: string;
123
+ messagingPattern: string;
124
+ status: CampaignStatus;
125
+ filters: CampaignFilter[];
126
+ messages: CampaignMessage[];
127
+ showMessageInPanel: boolean;
128
+ isRepeating: boolean;
129
+ repeat?: CampaignRepeat;
130
+ parentBroadcastId?: string;
131
+ scheduleId?: string;
132
+ hidden: boolean;
133
+ scheduledAt?: IsoDate;
134
+ processedAt?: IsoDate;
135
+ completedAt?: IsoDate;
136
+ /**
137
+ * Recipients resolved at send time, not at creation.
138
+ *
139
+ * `0` until the campaign is processed. The audience is a stored query -- even a
140
+ * bulk list is a file the sender reads later -- so a scheduled campaign's final
141
+ * count can differ from what was shown when it was built.
142
+ */
143
+ receivers: number;
144
+ totalMessages: number;
145
+ error?: string;
146
+ metaInfo: JsonObject[];
147
+ createdAt: IsoDate;
148
+ updatedAt: IsoDate;
149
+ }
150
+ export interface CreateCampaignInput {
151
+ name: string;
152
+ /** Defaults to `general`. `createBulk()` sets `bulk_campaign` itself. */
153
+ type?: string;
154
+ campaignType?: CampaignType;
155
+ trigger?: CampaignTrigger;
156
+ channelType?: string;
157
+ messagingPattern?: string;
158
+ /** The audience. `[]` means every contact. See {@link CampaignFilter}. */
159
+ filters?: CampaignFilter[];
160
+ messages: CampaignMessage[];
161
+ showMessageInPanel?: boolean;
162
+ isRepeating?: boolean;
163
+ repeat?: CampaignRepeat;
164
+ parentBroadcastId?: string;
165
+ hidden?: boolean;
166
+ /** Required when `trigger` is `schedule`. */
167
+ scheduledAt?: IsoDate;
168
+ }
169
+ export type UpdateCampaignInput = Partial<CreateCampaignInput>;
170
+ /**
171
+ * Declared as a type alias rather than an interface, and that is load-bearing:
172
+ * only an alias of an object literal gets TypeScript's implicit index signature,
173
+ * which is what lets it be passed as a request's query record.
174
+ */
175
+ export type ListCampaignsQuery = {
176
+ limit: number;
177
+ cursor?: string;
178
+ status?: CampaignStatus;
179
+ campaignType?: CampaignType;
180
+ type?: string;
181
+ hidden?: boolean;
182
+ };
183
+ /** Delivery counts for one campaign. Every field is a running total. */
184
+ export interface CampaignMetrics {
185
+ queued: number;
186
+ sent: number;
187
+ delivered: number;
188
+ failed: number;
189
+ opened: number;
190
+ clicked: number;
191
+ unsubscribed: number;
192
+ }
193
+ export interface CampaignReport {
194
+ id: string;
195
+ campaignId: string;
196
+ metrics: CampaignMetrics;
197
+ /** When the metrics were last rolled up, which may lag the campaign. */
198
+ generatedAt: IsoDate;
199
+ createdAt: IsoDate;
200
+ updatedAt: IsoDate;
201
+ }
202
+ /** A report export shares the job lifecycle used across the platform. */
203
+ export type CampaignReportExportStatus = 'queued' | 'processing' | 'completed' | 'failed';
204
+ export interface CampaignReportExport {
205
+ id: string;
206
+ campaignId: string;
207
+ format: 'csv' | 'xlsx';
208
+ status: CampaignReportExportStatus;
209
+ rowCount: number;
210
+ /** Present once `status` is `completed`. Expires; see `expiresAt`. */
211
+ fileUrl?: string;
212
+ expiresAt?: IsoDate;
213
+ errorMessage?: string;
214
+ createdAt: IsoDate;
215
+ updatedAt: IsoDate;
216
+ }
217
+ export interface CreateCampaignReportExportInput {
218
+ campaignId: string;
219
+ /** Defaults to `csv`. */
220
+ format?: 'csv' | 'xlsx';
221
+ /** Narrows the rows by the period they fall in. */
222
+ from?: IsoDate;
223
+ to?: IsoDate;
224
+ }
225
+ /** A type alias, for the same reason as {@link ListCampaignsQuery}. */
226
+ export type GetCampaignReportQuery = {
227
+ from?: IsoDate;
228
+ to?: IsoDate;
229
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=campaign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"campaign.js","sourceRoot":"","sources":["../../src/types/campaign.ts"],"names":[],"mappings":""}
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * The contact filter DSL.
3
3
  *
4
- * One vocabulary, four callers: `GET /contacts?filter=`, a saved `Segment`, a
5
- * `CrmTrigger`'s `filters`, and an export's `config.filter`. Conditions are a
4
+ * One vocabulary, five callers: `GET /contacts?filter=`, a saved `Segment`, a
5
+ * `CrmTrigger`'s `filters`, an export's `config.filter`, and a scheduled
6
+ * message's `conditions`. Conditions are a
6
7
  * flat list rather than a tree -- `and` binds tighter than `or`, so the list is
7
8
  * split on every `or`, each run is `and`ed, and the runs are `or`ed together.
8
9
  */
@@ -2,8 +2,9 @@
2
2
  /**
3
3
  * The contact filter DSL.
4
4
  *
5
- * One vocabulary, four callers: `GET /contacts?filter=`, a saved `Segment`, a
6
- * `CrmTrigger`'s `filters`, and an export's `config.filter`. Conditions are a
5
+ * One vocabulary, five callers: `GET /contacts?filter=`, a saved `Segment`, a
6
+ * `CrmTrigger`'s `filters`, an export's `config.filter`, and a scheduled
7
+ * message's `conditions`. Conditions are a
7
8
  * flat list rather than a tree -- `and` binds tighter than `or`, so the list is
8
9
  * split on every `or`, each run is `and`ed, and the runs are `or`ed together.
9
10
  */
@@ -1 +1 @@
1
- {"version":3,"file":"contact-filter.js","sourceRoot":"","sources":["../../src/types/contact-filter.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG"}
1
+ {"version":3,"file":"contact-filter.js","sourceRoot":"","sources":["../../src/types/contact-filter.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG"}