notifkit 0.1.5 → 0.1.7
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/README.md +5 -17
- package/dist/index.d.mts +3012 -550
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{main-DeNFQ-UL.mjs → main-Bgi2wWck.mjs} +2 -2
- package/dist/{main-DeNFQ-UL.mjs.map → main-Bgi2wWck.mjs.map} +1 -1
- package/dist/{main-40zwq6b0.mjs → main-CFSukWm8.mjs} +73 -39
- package/dist/main-CFSukWm8.mjs.map +1 -0
- package/dist/{main-DmCPcxOc.mjs → main-CRPxM-O3.mjs} +2 -2
- package/dist/{main-DmCPcxOc.mjs.map → main-CRPxM-O3.mjs.map} +1 -1
- package/dist/{main-BFre2-HQ.mjs → main-CjbVdUqa.mjs} +2 -2
- package/dist/{main-BFre2-HQ.mjs.map → main-CjbVdUqa.mjs.map} +1 -1
- package/dist/{main-BNJtzY61.mjs → main-D6SG3Isk.mjs} +2 -2
- package/dist/{main-BNJtzY61.mjs.map → main-D6SG3Isk.mjs.map} +1 -1
- package/dist/{main-DvgJSm11.mjs → main-DFMHcN_d.mjs} +2 -2
- package/dist/{main-DvgJSm11.mjs.map → main-DFMHcN_d.mjs.map} +1 -1
- package/dist/{main-BOPMYqsW.mjs → main-Yorxzw0Z.mjs} +2 -2
- package/dist/{main-BOPMYqsW.mjs.map → main-Yorxzw0Z.mjs.map} +1 -1
- package/dist/{main-CiigNpsP.mjs → main-pFAfCkVX.mjs} +2 -2
- package/dist/{main-CiigNpsP.mjs.map → main-pFAfCkVX.mjs.map} +1 -1
- package/dist/{src-vG79L-8m.mjs → src-CPMwsUCJ.mjs} +70 -41
- package/dist/src-CPMwsUCJ.mjs.map +1 -0
- package/package.json +1 -1
- package/src/client.ts +111 -40
- package/src/config/index.ts +5 -1
- package/src/contracts/sdk.ts +143 -3
- package/src/services/api/handlers.ts +94 -30
- package/dist/main-40zwq6b0.mjs.map +0 -1
- package/dist/src-vG79L-8m.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notifkit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Self-hosted notification infrastructure. One call delivers to email, SMS, push, and webhook — routed by preference, quiet hours, and consent.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "devkitshq",
|
package/src/client.ts
CHANGED
|
@@ -2,12 +2,29 @@ import type {
|
|
|
2
2
|
AddUserInput,
|
|
3
3
|
UpdateUserInput,
|
|
4
4
|
AddContactInput,
|
|
5
|
+
UserContactInput,
|
|
5
6
|
SyncTemplatesInput,
|
|
7
|
+
TemplateInput,
|
|
6
8
|
NotifyRequestInput,
|
|
7
9
|
TriggerWorkflowInput,
|
|
8
10
|
CreateWorkflowInput,
|
|
9
11
|
IngestEventInput,
|
|
10
12
|
UpdateProjectInput,
|
|
13
|
+
Preferences,
|
|
14
|
+
UserContactResponse,
|
|
15
|
+
UserResponse,
|
|
16
|
+
UserDetailResponse,
|
|
17
|
+
UserProfileResponse,
|
|
18
|
+
WorkflowDefinitionRecord,
|
|
19
|
+
WorkflowInstanceRecord,
|
|
20
|
+
NotificationLogRecord,
|
|
21
|
+
TemplateRecordResponse,
|
|
22
|
+
SuppressionRecord,
|
|
23
|
+
ProjectRecord,
|
|
24
|
+
ProjectKeyRecord,
|
|
25
|
+
SystemHealthRecord,
|
|
26
|
+
SystemMetricsRecord,
|
|
27
|
+
DLQMessageRecord,
|
|
11
28
|
} from "./contracts/sdk.js";
|
|
12
29
|
|
|
13
30
|
export interface NotifkitClientOptions {
|
|
@@ -53,37 +70,86 @@ export class NotifkitClient {
|
|
|
53
70
|
return data as T;
|
|
54
71
|
}
|
|
55
72
|
|
|
56
|
-
/** Sync templates with the server. */
|
|
57
|
-
async syncTemplates(input: SyncTemplatesInput): Promise<{ synced: number }> {
|
|
58
|
-
|
|
73
|
+
/** Sync templates with the server. Accepts either { templates: [...] } or an array of templates directly. */
|
|
74
|
+
async syncTemplates(input: SyncTemplatesInput | TemplateInput[]): Promise<{ synced: number }> {
|
|
75
|
+
const payload = Array.isArray(input) ? { templates: input } : input;
|
|
76
|
+
return this.request("/v1/templates", "PUT", payload);
|
|
59
77
|
}
|
|
60
78
|
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Create/upsert a user profile and contacts.
|
|
81
|
+
* Can be called with (id, contacts, options) or (inputObject).
|
|
82
|
+
*/
|
|
83
|
+
async addUser(
|
|
84
|
+
id: string,
|
|
85
|
+
contacts?: UserContactInput[],
|
|
86
|
+
options?: Omit<AddUserInput, "id" | "contacts">,
|
|
87
|
+
): Promise<{ id: string }>;
|
|
88
|
+
async addUser(input: AddUserInput): Promise<{ id: string }>;
|
|
89
|
+
async addUser(
|
|
90
|
+
idOrInput: string | AddUserInput,
|
|
91
|
+
contacts?: UserContactInput[],
|
|
92
|
+
options?: Omit<AddUserInput, "id" | "contacts">,
|
|
93
|
+
): Promise<{ id: string }> {
|
|
94
|
+
if (typeof idOrInput === "string") {
|
|
95
|
+
return this.request("/v1/users", "POST", {
|
|
96
|
+
id: idOrInput,
|
|
97
|
+
contacts: contacts ?? [],
|
|
98
|
+
...options,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return this.request("/v1/users", "POST", idOrInput);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Convenient alias for `addUser` matching standard CDP/notification SDK conventions.
|
|
106
|
+
*/
|
|
107
|
+
async identify(
|
|
108
|
+
id: string,
|
|
109
|
+
contacts?: UserContactInput[],
|
|
110
|
+
options?: Omit<AddUserInput, "id" | "contacts">,
|
|
111
|
+
): Promise<{ id: string }>;
|
|
112
|
+
async identify(input: AddUserInput): Promise<{ id: string }>;
|
|
113
|
+
async identify(
|
|
114
|
+
idOrInput: string | AddUserInput,
|
|
115
|
+
contacts?: UserContactInput[],
|
|
116
|
+
options?: Omit<AddUserInput, "id" | "contacts">,
|
|
117
|
+
): Promise<{ id: string }> {
|
|
118
|
+
return (this.addUser as any)(idOrInput, contacts, options);
|
|
64
119
|
}
|
|
65
120
|
|
|
66
121
|
/** Update user profile. */
|
|
67
122
|
async updateUser(id: string, input: UpdateUserInput): Promise<{ id: string }> {
|
|
68
|
-
return this.request(`/v1/users/${id}`, "PATCH", input);
|
|
123
|
+
return this.request(`/v1/users/${encodeURIComponent(id)}`, "PATCH", input);
|
|
69
124
|
}
|
|
70
125
|
|
|
71
126
|
/** Delete user profile. */
|
|
72
127
|
async deleteUser(id: string): Promise<void> {
|
|
73
|
-
return this.request(`/v1/users/${id}`, "DELETE");
|
|
128
|
+
return this.request(`/v1/users/${encodeURIComponent(id)}`, "DELETE");
|
|
74
129
|
}
|
|
75
130
|
|
|
76
|
-
/** Add contact
|
|
131
|
+
/** Add contact target to user profile. */
|
|
77
132
|
async addContact(
|
|
78
133
|
userId: string,
|
|
79
134
|
input: AddContactInput,
|
|
80
135
|
): Promise<{ userId: string; channel: string; target: string }> {
|
|
81
|
-
return this.request(`/v1/users/${userId}/contacts`, "POST", input);
|
|
136
|
+
return this.request(`/v1/users/${encodeURIComponent(userId)}/contacts`, "POST", input);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Add multiple contact targets to user profile in a batch. */
|
|
140
|
+
async addContacts(
|
|
141
|
+
userId: string,
|
|
142
|
+
contacts: AddContactInput[],
|
|
143
|
+
): Promise<{ userId: string; contacts: Array<{ channel: string; target: string }> }> {
|
|
144
|
+
return this.request(`/v1/users/${encodeURIComponent(userId)}/contacts`, "POST", contacts);
|
|
82
145
|
}
|
|
83
146
|
|
|
84
147
|
/** Delete a specific contact channel target. */
|
|
85
148
|
async deleteContact(userId: string, channel: string, target: string): Promise<void> {
|
|
86
|
-
return this.request(
|
|
149
|
+
return this.request(
|
|
150
|
+
`/v1/users/${encodeURIComponent(userId)}/contacts/${encodeURIComponent(channel)}/${encodeURIComponent(target)}`,
|
|
151
|
+
"DELETE",
|
|
152
|
+
);
|
|
87
153
|
}
|
|
88
154
|
|
|
89
155
|
/** Request a notification dispatch. */
|
|
@@ -124,7 +190,7 @@ export class NotifkitClient {
|
|
|
124
190
|
async listWorkflows(options?: {
|
|
125
191
|
limit?: number;
|
|
126
192
|
search?: string;
|
|
127
|
-
}): Promise<{ workflows:
|
|
193
|
+
}): Promise<{ workflows: WorkflowDefinitionRecord[] }> {
|
|
128
194
|
const params = new URLSearchParams();
|
|
129
195
|
if (options?.limit) params.set("limit", options.limit.toString());
|
|
130
196
|
if (options?.search) params.set("search", options.search);
|
|
@@ -133,13 +199,13 @@ export class NotifkitClient {
|
|
|
133
199
|
}
|
|
134
200
|
|
|
135
201
|
/** Get a workflow instance by ID. */
|
|
136
|
-
async getWorkflow(instanceId: string): Promise<
|
|
137
|
-
return this.request(`/v1/workflows/instances/${instanceId}`, "GET");
|
|
202
|
+
async getWorkflow(instanceId: string): Promise<WorkflowInstanceRecord> {
|
|
203
|
+
return this.request(`/v1/workflows/instances/${encodeURIComponent(instanceId)}`, "GET");
|
|
138
204
|
}
|
|
139
205
|
|
|
140
206
|
/** Cancel a running/suspended workflow instance. */
|
|
141
207
|
async cancelWorkflow(instanceId: string): Promise<void> {
|
|
142
|
-
return this.request(`/v1/workflows/instances/${instanceId}`, "DELETE");
|
|
208
|
+
return this.request(`/v1/workflows/instances/${encodeURIComponent(instanceId)}`, "DELETE");
|
|
143
209
|
}
|
|
144
210
|
|
|
145
211
|
/** Get notification logs for the project. */
|
|
@@ -153,7 +219,7 @@ export class NotifkitClient {
|
|
|
153
219
|
taskId?: string;
|
|
154
220
|
campaign?: string;
|
|
155
221
|
search?: string;
|
|
156
|
-
}): Promise<{ logs:
|
|
222
|
+
}): Promise<{ logs: NotificationLogRecord[]; nextCursor: string | null }> {
|
|
157
223
|
let url = "/v1/notifications/logs";
|
|
158
224
|
if (options) {
|
|
159
225
|
const params = new URLSearchParams();
|
|
@@ -182,7 +248,7 @@ export class NotifkitClient {
|
|
|
182
248
|
language?: string;
|
|
183
249
|
timezone?: string;
|
|
184
250
|
channel?: string;
|
|
185
|
-
}): Promise<{ users:
|
|
251
|
+
}): Promise<{ users: UserProfileResponse[]; nextCursor: string | null }> {
|
|
186
252
|
const params = new URLSearchParams();
|
|
187
253
|
if (options?.limit) params.set("limit", options.limit.toString());
|
|
188
254
|
if (options?.cursor) params.set("cursor", options.cursor);
|
|
@@ -197,45 +263,48 @@ export class NotifkitClient {
|
|
|
197
263
|
|
|
198
264
|
/** Delete a template. */
|
|
199
265
|
async deleteTemplate(id: string): Promise<void> {
|
|
200
|
-
return this.request(`/v1/templates/${id}`, "DELETE");
|
|
266
|
+
return this.request(`/v1/templates/${encodeURIComponent(id)}`, "DELETE");
|
|
201
267
|
}
|
|
202
268
|
|
|
203
269
|
/** Get a user's contacts. */
|
|
204
|
-
async getUserContacts(userId: string): Promise<{ contacts:
|
|
205
|
-
return this.request(`/v1/users/${userId}/contacts`, "GET");
|
|
270
|
+
async getUserContacts(userId: string): Promise<{ contacts: UserContactResponse[] }> {
|
|
271
|
+
return this.request(`/v1/users/${encodeURIComponent(userId)}/contacts`, "GET");
|
|
206
272
|
}
|
|
207
273
|
|
|
208
274
|
/** List projects (Admin only). */
|
|
209
|
-
async listProjects(): Promise<{ projects:
|
|
275
|
+
async listProjects(): Promise<{ projects: ProjectRecord[] }> {
|
|
210
276
|
return this.request("/v1/projects", "GET");
|
|
211
277
|
}
|
|
212
278
|
|
|
213
279
|
/** Delete a project (Admin only). */
|
|
214
280
|
async deleteProject(id: string): Promise<void> {
|
|
215
|
-
return this.request(`/v1/projects/${id}`, "DELETE");
|
|
281
|
+
return this.request(`/v1/projects/${encodeURIComponent(id)}`, "DELETE");
|
|
216
282
|
}
|
|
217
283
|
|
|
218
284
|
/** Create a new project API key (Admin only). */
|
|
219
285
|
async createProjectKey(
|
|
220
286
|
id: string,
|
|
221
287
|
input?: { role?: "admin" | "read_only" },
|
|
222
|
-
): Promise<
|
|
223
|
-
return this.request(`/v1/projects/${id}/keys`, "POST", input || {});
|
|
288
|
+
): Promise<ProjectKeyRecord> {
|
|
289
|
+
return this.request(`/v1/projects/${encodeURIComponent(id)}/keys`, "POST", input || {});
|
|
224
290
|
}
|
|
225
291
|
|
|
226
292
|
/** List project API keys (Admin only). */
|
|
227
|
-
async listProjectKeys(id: string): Promise<{ keys:
|
|
228
|
-
return this.request(`/v1/projects/${id}/keys`, "GET");
|
|
293
|
+
async listProjectKeys(id: string): Promise<{ keys: ProjectKeyRecord[] }> {
|
|
294
|
+
return this.request(`/v1/projects/${encodeURIComponent(id)}/keys`, "GET");
|
|
229
295
|
}
|
|
230
296
|
|
|
231
297
|
/** Delete a project API key (Admin only). */
|
|
232
298
|
async deleteProjectKey(id: string, keyId: string): Promise<void> {
|
|
233
|
-
return this.request(
|
|
299
|
+
return this.request(
|
|
300
|
+
`/v1/projects/${encodeURIComponent(id)}/keys/${encodeURIComponent(keyId)}`,
|
|
301
|
+
"DELETE",
|
|
302
|
+
);
|
|
234
303
|
}
|
|
235
304
|
|
|
236
305
|
/** Update project settings (Admin only). */
|
|
237
306
|
async updateProject(id: string, input: UpdateProjectInput): Promise<{ id: string }> {
|
|
238
|
-
return this.request(`/v1/projects/${id}`, "PATCH", input);
|
|
307
|
+
return this.request(`/v1/projects/${encodeURIComponent(id)}`, "PATCH", input);
|
|
239
308
|
}
|
|
240
309
|
|
|
241
310
|
/** List unique segment tags. */
|
|
@@ -302,7 +371,7 @@ export class NotifkitClient {
|
|
|
302
371
|
channel?: string;
|
|
303
372
|
reason?: string;
|
|
304
373
|
target?: string;
|
|
305
|
-
}): Promise<{ suppressions:
|
|
374
|
+
}): Promise<{ suppressions: SuppressionRecord[] }> {
|
|
306
375
|
const params = new URLSearchParams();
|
|
307
376
|
if (options?.limit) params.set("limit", options.limit.toString());
|
|
308
377
|
if (options?.channel) params.set("channel", options.channel);
|
|
@@ -332,7 +401,9 @@ export class NotifkitClient {
|
|
|
332
401
|
// ─── Notification Status & Cancellation ─────────────────────────────────────
|
|
333
402
|
|
|
334
403
|
/** Get real-time status and delivery logs for a specific notification task. */
|
|
335
|
-
async getNotificationStatus(
|
|
404
|
+
async getNotificationStatus(
|
|
405
|
+
taskId: string,
|
|
406
|
+
): Promise<{ status: string; logs: NotificationLogRecord[] }> {
|
|
336
407
|
return this.request(`/v1/notifications/${encodeURIComponent(taskId)}`, "GET");
|
|
337
408
|
}
|
|
338
409
|
|
|
@@ -349,54 +420,54 @@ export class NotifkitClient {
|
|
|
349
420
|
// ─── User Profile & Preferences ─────────────────────────────────────────────
|
|
350
421
|
|
|
351
422
|
/** Get user profile and contacts by ID. */
|
|
352
|
-
async getUser(id: string): Promise<
|
|
423
|
+
async getUser(id: string): Promise<UserResponse> {
|
|
353
424
|
return this.request(`/v1/users/${encodeURIComponent(id)}`, "GET");
|
|
354
425
|
}
|
|
355
426
|
|
|
356
427
|
/** Get user details including contacts and recent message logs. */
|
|
357
|
-
async getUserDetails(id: string): Promise<
|
|
428
|
+
async getUserDetails(id: string): Promise<UserDetailResponse> {
|
|
358
429
|
return this.request(`/v1/users/${encodeURIComponent(id)}/details`, "GET");
|
|
359
430
|
}
|
|
360
431
|
|
|
361
432
|
/** Get user preferences. */
|
|
362
|
-
async getUserPreferences(id: string): Promise<
|
|
433
|
+
async getUserPreferences(id: string): Promise<Preferences> {
|
|
363
434
|
return this.request(`/v1/users/${encodeURIComponent(id)}/preferences`, "GET");
|
|
364
435
|
}
|
|
365
436
|
|
|
366
437
|
/** Update user preferences. */
|
|
367
438
|
async updateUserPreferences(
|
|
368
439
|
id: string,
|
|
369
|
-
preferences:
|
|
370
|
-
): Promise<{ id: string; preferences:
|
|
440
|
+
preferences: Preferences,
|
|
441
|
+
): Promise<{ id: string; preferences: Preferences }> {
|
|
371
442
|
return this.request(`/v1/users/${encodeURIComponent(id)}/preferences`, "PATCH", preferences);
|
|
372
443
|
}
|
|
373
444
|
|
|
374
445
|
// ─── Templates Querying ─────────────────────────────────────────────────────
|
|
375
446
|
|
|
376
447
|
/** List all templates for the project. */
|
|
377
|
-
async listTemplates(): Promise<{ templates:
|
|
448
|
+
async listTemplates(): Promise<{ templates: TemplateRecordResponse[] }> {
|
|
378
449
|
return this.request("/v1/templates", "GET");
|
|
379
450
|
}
|
|
380
451
|
|
|
381
452
|
/** Get a template by ID. */
|
|
382
|
-
async getTemplate(id: string): Promise<
|
|
453
|
+
async getTemplate(id: string): Promise<TemplateRecordResponse> {
|
|
383
454
|
return this.request(`/v1/templates/${encodeURIComponent(id)}`, "GET");
|
|
384
455
|
}
|
|
385
456
|
|
|
386
457
|
// ─── System Health, Metrics & DLQ ───────────────────────────────────────────
|
|
387
458
|
|
|
388
459
|
/** Get system health and worker status. */
|
|
389
|
-
async getSystemHealth(): Promise<
|
|
460
|
+
async getSystemHealth(): Promise<SystemHealthRecord> {
|
|
390
461
|
return this.request("/v1/system/health", "GET");
|
|
391
462
|
}
|
|
392
463
|
|
|
393
464
|
/** Get system metrics and queue lengths. */
|
|
394
|
-
async getSystemMetrics(): Promise<
|
|
465
|
+
async getSystemMetrics(): Promise<SystemMetricsRecord> {
|
|
395
466
|
return this.request("/v1/system/metrics", "GET");
|
|
396
467
|
}
|
|
397
468
|
|
|
398
469
|
/** Get dead-letter queue messages. */
|
|
399
|
-
async getDLQMessages(): Promise<{ messages:
|
|
470
|
+
async getDLQMessages(): Promise<{ messages: DLQMessageRecord[] }> {
|
|
400
471
|
return this.request("/v1/dlq", "GET");
|
|
401
472
|
}
|
|
402
473
|
|
package/src/config/index.ts
CHANGED
|
@@ -38,7 +38,11 @@ export const baseConfigSchema = z.object({
|
|
|
38
38
|
HOST: z.string().default("127.0.0.1"),
|
|
39
39
|
REDIS_URL: z.string().url().default("redis://localhost:6379"),
|
|
40
40
|
DATABASE_URL: z.string().url().default("postgres://platform:platform@localhost:5432/notifkit"),
|
|
41
|
-
|
|
41
|
+
// Trimmed because the incoming bearer token is trimmed before comparison, so
|
|
42
|
+
// an untrimmed value here could never match it. `set KEY=value && cmd` on
|
|
43
|
+
// Windows puts a trailing space in the variable, which otherwise turns every
|
|
44
|
+
// admin request into a 401 that reads like a wrong key.
|
|
45
|
+
ADMIN_API_KEY: z.string().trim().optional(),
|
|
42
46
|
WORKER_CONCURRENCY: z.coerce.number().int().min(1).default(10),
|
|
43
47
|
QUEUE_MAX_LEN: z.coerce.number().int().min(1).default(10000000),
|
|
44
48
|
DB_MAX_CONNECTIONS: z.coerce.number().int().min(1).default(2),
|
package/src/contracts/sdk.ts
CHANGED
|
@@ -28,6 +28,7 @@ export const ContactChannelSchema = z.enum([
|
|
|
28
28
|
"sms",
|
|
29
29
|
"push",
|
|
30
30
|
"webhook",
|
|
31
|
+
"in-app",
|
|
31
32
|
"telegram",
|
|
32
33
|
"discord",
|
|
33
34
|
"whatsapp",
|
|
@@ -40,16 +41,28 @@ const stringOrArray = z
|
|
|
40
41
|
.union([z.string().min(1), z.array(z.string().min(1))])
|
|
41
42
|
.transform((v) => (Array.isArray(v) ? v : [v]));
|
|
42
43
|
|
|
44
|
+
export const UserContactInputSchema = z.object({
|
|
45
|
+
channel: ContactChannelSchema,
|
|
46
|
+
target: z.string().min(1),
|
|
47
|
+
label: z.string().optional(),
|
|
48
|
+
isPrimary: z.boolean().optional(),
|
|
49
|
+
enabled: z.boolean().optional(),
|
|
50
|
+
preferences: PreferencesSchema.optional(),
|
|
51
|
+
});
|
|
52
|
+
export type UserContactInput = z.infer<typeof UserContactInputSchema>;
|
|
53
|
+
|
|
43
54
|
// ─── Users ────────────────────────────────────────────────────────────────────
|
|
44
55
|
|
|
45
56
|
/**
|
|
46
|
-
* addUser({ id, email, phone, pushToken, segments, preferences })
|
|
47
|
-
* email / phone / pushToken accept a single string or an array.
|
|
57
|
+
* addUser({ id, contacts, email, phone, pushToken, segments, preferences })
|
|
58
|
+
* email / phone / pushToken accept a single string or an array for backwards compatibility.
|
|
59
|
+
* Prefer `contacts: [{ channel, target }]`.
|
|
48
60
|
*/
|
|
49
61
|
export const AddUserSchema = z.object({
|
|
50
62
|
id: z.string().min(1),
|
|
51
63
|
language: z.string().optional(),
|
|
52
64
|
timezone: z.string().optional(),
|
|
65
|
+
contacts: z.array(UserContactInputSchema).optional(),
|
|
53
66
|
email: stringOrArray.optional(),
|
|
54
67
|
phone: stringOrArray.optional(),
|
|
55
68
|
pushToken: stringOrArray.optional(),
|
|
@@ -62,6 +75,7 @@ export type AddUserInput = z.input<typeof AddUserSchema>;
|
|
|
62
75
|
export const UpdateUserSchema = z.object({
|
|
63
76
|
language: z.string().optional(),
|
|
64
77
|
timezone: z.string().optional(),
|
|
78
|
+
contacts: z.array(UserContactInputSchema).optional(),
|
|
65
79
|
email: stringOrArray.optional(),
|
|
66
80
|
phone: stringOrArray.optional(),
|
|
67
81
|
pushToken: stringOrArray.optional(),
|
|
@@ -70,14 +84,20 @@ export const UpdateUserSchema = z.object({
|
|
|
70
84
|
});
|
|
71
85
|
export type UpdateUserInput = z.input<typeof UpdateUserSchema>;
|
|
72
86
|
|
|
73
|
-
/** addUserContact(userId, channel, { target, preferences
|
|
87
|
+
/** addUserContact(userId, channel, { target, preferences, label, isPrimary, enabled }) */
|
|
74
88
|
export const AddContactSchema = z.object({
|
|
75
89
|
channel: ContactChannelSchema,
|
|
76
90
|
target: z.string().min(1),
|
|
91
|
+
label: z.string().optional(),
|
|
92
|
+
isPrimary: z.boolean().optional(),
|
|
93
|
+
enabled: z.boolean().optional(),
|
|
77
94
|
preferences: PreferencesSchema.optional(),
|
|
78
95
|
});
|
|
79
96
|
export type AddContactInput = z.infer<typeof AddContactSchema>;
|
|
80
97
|
|
|
98
|
+
export const BatchAddContactsSchema = z.union([AddContactSchema, z.array(AddContactSchema).min(1)]);
|
|
99
|
+
export type BatchAddContactsInput = z.infer<typeof BatchAddContactsSchema>;
|
|
100
|
+
|
|
81
101
|
// ─── Templates ────────────────────────────────────────────────────────────────
|
|
82
102
|
|
|
83
103
|
export const TemplateSchema = z.object({
|
|
@@ -104,6 +124,7 @@ export const InlineUserSchema = z.object({
|
|
|
104
124
|
id: z.string().min(1),
|
|
105
125
|
language: z.string().optional(),
|
|
106
126
|
timezone: z.string().optional(),
|
|
127
|
+
contacts: z.array(UserContactInputSchema).optional(),
|
|
107
128
|
email: stringOrArray.optional(),
|
|
108
129
|
phone: stringOrArray.optional(),
|
|
109
130
|
pushToken: stringOrArray.optional(),
|
|
@@ -240,3 +261,122 @@ export const UpdateProjectSchema = z.object({
|
|
|
240
261
|
throttleWindowHours: z.number().nullable().optional(),
|
|
241
262
|
});
|
|
242
263
|
export type UpdateProjectInput = z.infer<typeof UpdateProjectSchema>;
|
|
264
|
+
|
|
265
|
+
// ─── Response Entities ────────────────────────────────────────────────────────
|
|
266
|
+
|
|
267
|
+
export interface UserContactResponse {
|
|
268
|
+
id: string;
|
|
269
|
+
userId: string;
|
|
270
|
+
channel: ContactChannel;
|
|
271
|
+
target: string;
|
|
272
|
+
label?: string | null;
|
|
273
|
+
isPrimary?: boolean;
|
|
274
|
+
active?: boolean;
|
|
275
|
+
enabled?: boolean;
|
|
276
|
+
preferences?: Preferences;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export interface UserProfileResponse {
|
|
280
|
+
userId: string;
|
|
281
|
+
language?: string;
|
|
282
|
+
timezone?: string;
|
|
283
|
+
email?: string | null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export interface UserResponse extends UserProfileResponse {
|
|
287
|
+
segments: string[];
|
|
288
|
+
preferences: Preferences;
|
|
289
|
+
contacts?: UserContactResponse[];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export interface UserDetailResponse extends UserResponse {
|
|
293
|
+
contacts: UserContactResponse[];
|
|
294
|
+
recentLogs?: unknown[];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export interface WorkflowDefinitionRecord {
|
|
298
|
+
id: string;
|
|
299
|
+
name: string;
|
|
300
|
+
steps: unknown[];
|
|
301
|
+
createdAt?: string;
|
|
302
|
+
updatedAt?: string;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export interface WorkflowInstanceRecord {
|
|
306
|
+
id: string;
|
|
307
|
+
name: string;
|
|
308
|
+
status: string;
|
|
309
|
+
currentStepIndex: number;
|
|
310
|
+
input?: Record<string, unknown>;
|
|
311
|
+
output?: Record<string, unknown>;
|
|
312
|
+
createdAt: string;
|
|
313
|
+
updatedAt: string;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export interface NotificationLogRecord {
|
|
317
|
+
id: string;
|
|
318
|
+
notificationId: string;
|
|
319
|
+
taskId?: string;
|
|
320
|
+
channel: string;
|
|
321
|
+
target?: string;
|
|
322
|
+
status: string;
|
|
323
|
+
templateId?: string;
|
|
324
|
+
campaign?: string;
|
|
325
|
+
createdAt: string;
|
|
326
|
+
dispatchedAt?: string;
|
|
327
|
+
error?: string;
|
|
328
|
+
metadata?: Record<string, unknown>;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export interface TemplateRecordResponse {
|
|
332
|
+
id: string;
|
|
333
|
+
channel: string;
|
|
334
|
+
topics: string[];
|
|
335
|
+
content: Record<string, unknown>;
|
|
336
|
+
aiPrompts?: Record<string, string> | null;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export interface SuppressionRecord {
|
|
340
|
+
id: string;
|
|
341
|
+
channel: string;
|
|
342
|
+
target: string;
|
|
343
|
+
reason: string;
|
|
344
|
+
createdAt: string;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export interface ProjectRecord {
|
|
348
|
+
id: string;
|
|
349
|
+
name: string;
|
|
350
|
+
rateLimitRpm?: number | null;
|
|
351
|
+
throttleLimit?: number | null;
|
|
352
|
+
throttleWindowHours?: number | null;
|
|
353
|
+
createdAt: string;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export interface ProjectKeyRecord {
|
|
357
|
+
id: string;
|
|
358
|
+
apiKey: string;
|
|
359
|
+
role: string;
|
|
360
|
+
createdAt: string;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export interface SystemHealthRecord {
|
|
364
|
+
status: string;
|
|
365
|
+
timestamp: string;
|
|
366
|
+
redis?: { status: string };
|
|
367
|
+
db?: { status: string };
|
|
368
|
+
workers?: Record<string, unknown>;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export interface SystemMetricsRecord {
|
|
372
|
+
queues: Record<string, number>;
|
|
373
|
+
throughput?: Record<string, unknown>;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export interface DLQMessageRecord {
|
|
377
|
+
id: string;
|
|
378
|
+
stream: string;
|
|
379
|
+
payload: Record<string, unknown>;
|
|
380
|
+
error?: string;
|
|
381
|
+
timestamp: string;
|
|
382
|
+
}
|