@classytic/crm 0.3.0 → 0.5.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/events.js ADDED
@@ -0,0 +1,716 @@
1
+ import { z } from "zod";
2
+ import { createEvent as createEvent$1, matchEventPattern } from "@classytic/primitives/events";
3
+ //#region src/events/event-constants.ts
4
+ /** PACKAGE_RULES rule 12: `package:resource.verb`. */
5
+ const CRM_EVENTS = {
6
+ LEAD_CREATED: "crm:lead.created",
7
+ LEAD_STATUS_CHANGED: "crm:lead.status_changed",
8
+ LEAD_SCORED: "crm:lead.scored",
9
+ LEAD_CONVERTED: "crm:lead.converted",
10
+ LEAD_DISQUALIFIED: "crm:lead.disqualified",
11
+ ACCOUNT_CREATED: "crm:account.created",
12
+ ACCOUNT_UPDATED: "crm:account.updated",
13
+ CONTACT_CREATED: "crm:contact.created",
14
+ CONTACT_UPDATED: "crm:contact.updated",
15
+ OPPORTUNITY_CREATED: "crm:opportunity.created",
16
+ OPPORTUNITY_STAGE_ADVANCED: "crm:opportunity.stage_advanced",
17
+ OPPORTUNITY_STAGE_REGRESSED: "crm:opportunity.stage_regressed",
18
+ OPPORTUNITY_WON: "crm:opportunity.won",
19
+ OPPORTUNITY_LOST: "crm:opportunity.lost",
20
+ OPPORTUNITY_ABANDONED: "crm:opportunity.abandoned",
21
+ PIPELINE_CREATED: "crm:pipeline.created",
22
+ PIPELINE_ARCHIVED: "crm:pipeline.archived",
23
+ ACTIVITY_PLANNED: "crm:activity.planned",
24
+ ACTIVITY_COMPLETED: "crm:activity.completed",
25
+ ACTIVITY_CANCELLED: "crm:activity.cancelled",
26
+ NOTE_ADDED: "crm:note.added",
27
+ CAMPAIGN_CREATED: "crm:campaign.created",
28
+ CAMPAIGN_ACTIVATED: "crm:campaign.activated",
29
+ CAMPAIGN_PAUSED: "crm:campaign.paused",
30
+ CAMPAIGN_RESUMED: "crm:campaign.resumed",
31
+ CAMPAIGN_COMPLETED: "crm:campaign.completed",
32
+ CAMPAIGN_ARCHIVED: "crm:campaign.archived",
33
+ SUBSCRIPTION_CREATED: "crm:subscription.created",
34
+ SUBSCRIPTION_CONFIRMED: "crm:subscription.confirmed",
35
+ SUBSCRIPTION_UNSUBSCRIBED: "crm:subscription.unsubscribed",
36
+ SUBSCRIPTION_BOUNCED: "crm:subscription.bounced",
37
+ SUBSCRIPTION_COMPLAINED: "crm:subscription.complained",
38
+ EMAIL_EVENT_OPENED: "crm:email_event.opened",
39
+ EMAIL_EVENT_CLICKED: "crm:email_event.clicked",
40
+ EMAIL_EVENT_BOUNCED: "crm:email_event.bounced",
41
+ EMAIL_EVENT_COMPLAINED: "crm:email_event.complained",
42
+ EMAIL_EVENT_UNSUBSCRIBED: "crm:email_event.unsubscribed",
43
+ LEAD_ENRICHED: "crm:lead.enriched",
44
+ LEAD_ASSIGNED: "crm:lead.assigned",
45
+ CONTACT_ENRICHED: "crm:contact.enriched",
46
+ CONTACT_ASSIGNED: "crm:contact.assigned",
47
+ CONTACT_MERGED: "crm:contact.merged",
48
+ ACCOUNT_ENRICHED: "crm:account.enriched",
49
+ ACCOUNT_ASSIGNED: "crm:account.assigned",
50
+ ACCOUNT_MERGED: "crm:account.merged",
51
+ OPPORTUNITY_ASSIGNED: "crm:opportunity.assigned",
52
+ OPPORTUNITY_AMOUNT_CHANGED: "crm:opportunity.amount_changed",
53
+ OPPORTUNITY_EXTENDED: "crm:opportunity.extended",
54
+ ACTIVITY_RESCHEDULED: "crm:activity.rescheduled",
55
+ ACTIVITY_OUTCOME_RECORDED: "crm:activity.outcome_recorded",
56
+ CONVERSATION_CREATED: "crm:conversation.created",
57
+ CONVERSATION_ASSIGNED: "crm:conversation.assigned",
58
+ CONVERSATION_RESOLVED: "crm:conversation.resolved",
59
+ CONVERSATION_REOPENED: "crm:conversation.reopened",
60
+ MESSAGE_RECEIVED: "crm:message.received",
61
+ MESSAGE_SENT: "crm:message.sent"
62
+ };
63
+ //#endregion
64
+ //#region src/events/event-catalog.ts
65
+ /**
66
+ * Zod-source event catalog (PACKAGE_RULES rule 18).
67
+ *
68
+ * One Zod schema per event is the source of truth — runtime validation,
69
+ * static types, and Arc introspection all derive from it. Hosts wire the
70
+ * definitions into arc's `EventRegistry`:
71
+ *
72
+ * ```ts
73
+ * for (const def of crmEventDefinitions) registry.register(def);
74
+ * ```
75
+ *
76
+ * which lights up publish-time validation (`eventPlugin({ registry,
77
+ * validateMode: 'reject' })`), OpenAPI event introspection, and
78
+ * `arc describe` output — with zero arc imports here (the definition shape
79
+ * is structurally identical to arc's `EventDefinitionOutput`).
80
+ *
81
+ * Timestamps in payloads are `z.iso.datetime()` (NEVER `z.coerce.date()`):
82
+ * `z.toJSONSchema()` throws on coerce.date, and events are ISO strings on
83
+ * the wire (rule 25).
84
+ */
85
+ function defineCrmEvent(input) {
86
+ const { name, resource, description, version = 1, zodSchema } = input;
87
+ return {
88
+ name,
89
+ version,
90
+ description,
91
+ zodSchema,
92
+ schema: z.toJSONSchema(zodSchema),
93
+ create(payload, meta) {
94
+ return createEvent$1(name, payload, {
95
+ resource,
96
+ source: "crm",
97
+ ...meta
98
+ });
99
+ }
100
+ };
101
+ }
102
+ const ownerChangeSchema = z.object({
103
+ ownerId: z.string(),
104
+ previousOwnerId: z.string().optional()
105
+ });
106
+ const enrichSchema = z.object({
107
+ fields: z.array(z.string()),
108
+ provider: z.string().optional()
109
+ });
110
+ const mergeSchema = z.object({
111
+ primaryId: z.string(),
112
+ secondaryId: z.string(),
113
+ strategy: z.enum(["preferPrimary", "preferSecondary"])
114
+ });
115
+ const stageRefSchema = z.object({
116
+ stageId: z.string(),
117
+ sequence: z.number()
118
+ });
119
+ const LeadCreated = defineCrmEvent({
120
+ name: CRM_EVENTS.LEAD_CREATED,
121
+ resource: "lead",
122
+ description: "createLead() inserted a scored lead.",
123
+ zodSchema: z.object({
124
+ leadId: z.string(),
125
+ email: z.string().optional(),
126
+ score: z.number()
127
+ })
128
+ });
129
+ const LeadStatusChanged = defineCrmEvent({
130
+ name: CRM_EVENTS.LEAD_STATUS_CHANGED,
131
+ resource: "lead",
132
+ description: "A lead lifecycle transition committed (qualify/disqualify/nurture/...).",
133
+ zodSchema: z.object({
134
+ leadId: z.string(),
135
+ from: z.string(),
136
+ to: z.string()
137
+ })
138
+ });
139
+ const LeadScored = defineCrmEvent({
140
+ name: CRM_EVENTS.LEAD_SCORED,
141
+ resource: "lead",
142
+ description: "A lead score was (re)computed and persisted.",
143
+ zodSchema: z.object({
144
+ leadId: z.string(),
145
+ score: z.number()
146
+ })
147
+ });
148
+ const LeadConverted = defineCrmEvent({
149
+ name: CRM_EVENTS.LEAD_CONVERTED,
150
+ resource: "lead",
151
+ description: "convert() spawned Contact (+ Account) + Opportunity and closed the lead.",
152
+ zodSchema: z.object({
153
+ leadId: z.string(),
154
+ contactId: z.string(),
155
+ accountId: z.string().optional(),
156
+ opportunityId: z.string()
157
+ })
158
+ });
159
+ const LeadDisqualified = defineCrmEvent({
160
+ name: CRM_EVENTS.LEAD_DISQUALIFIED,
161
+ resource: "lead",
162
+ description: "disqualify() rejected a lead with a reason.",
163
+ zodSchema: z.object({
164
+ leadId: z.string(),
165
+ reason: z.string()
166
+ })
167
+ });
168
+ const LeadEnriched = defineCrmEvent({
169
+ name: CRM_EVENTS.LEAD_ENRICHED,
170
+ resource: "lead",
171
+ description: "enrich() merged a provider/manual patch into the lead.",
172
+ zodSchema: z.object({ leadId: z.string() }).extend(enrichSchema.shape)
173
+ });
174
+ const LeadAssigned = defineCrmEvent({
175
+ name: CRM_EVENTS.LEAD_ASSIGNED,
176
+ resource: "lead",
177
+ description: "assign() (re)assigned the lead owner.",
178
+ zodSchema: z.object({ leadId: z.string() }).extend(ownerChangeSchema.shape)
179
+ });
180
+ const AccountCreated = defineCrmEvent({
181
+ name: CRM_EVENTS.ACCOUNT_CREATED,
182
+ resource: "account",
183
+ description: "createAccount() inserted an account.",
184
+ zodSchema: z.object({
185
+ accountId: z.string(),
186
+ name: z.string()
187
+ })
188
+ });
189
+ const AccountUpdated = defineCrmEvent({
190
+ name: CRM_EVENTS.ACCOUNT_UPDATED,
191
+ resource: "account",
192
+ description: "An account record was updated.",
193
+ zodSchema: z.object({ accountId: z.string() })
194
+ });
195
+ const AccountEnriched = defineCrmEvent({
196
+ name: CRM_EVENTS.ACCOUNT_ENRICHED,
197
+ resource: "account",
198
+ description: "enrich() merged a patch into the account.",
199
+ zodSchema: z.object({ accountId: z.string() }).extend(enrichSchema.shape)
200
+ });
201
+ const AccountAssigned = defineCrmEvent({
202
+ name: CRM_EVENTS.ACCOUNT_ASSIGNED,
203
+ resource: "account",
204
+ description: "assign() (re)assigned the account owner.",
205
+ zodSchema: z.object({ accountId: z.string() }).extend(ownerChangeSchema.shape)
206
+ });
207
+ const AccountMerged = defineCrmEvent({
208
+ name: CRM_EVENTS.ACCOUNT_MERGED,
209
+ resource: "account",
210
+ description: "merge() folded a secondary account into the primary; secondary deleted.",
211
+ zodSchema: mergeSchema
212
+ });
213
+ const ContactCreated = defineCrmEvent({
214
+ name: CRM_EVENTS.CONTACT_CREATED,
215
+ resource: "contact",
216
+ description: "createContact() inserted a contact.",
217
+ zodSchema: z.object({
218
+ contactId: z.string(),
219
+ email: z.string().optional()
220
+ })
221
+ });
222
+ const ContactUpdated = defineCrmEvent({
223
+ name: CRM_EVENTS.CONTACT_UPDATED,
224
+ resource: "contact",
225
+ description: "A contact record was updated.",
226
+ zodSchema: z.object({ contactId: z.string() })
227
+ });
228
+ const ContactEnriched = defineCrmEvent({
229
+ name: CRM_EVENTS.CONTACT_ENRICHED,
230
+ resource: "contact",
231
+ description: "enrich() merged a patch into the contact.",
232
+ zodSchema: z.object({ contactId: z.string() }).extend(enrichSchema.shape)
233
+ });
234
+ const ContactAssigned = defineCrmEvent({
235
+ name: CRM_EVENTS.CONTACT_ASSIGNED,
236
+ resource: "contact",
237
+ description: "assign() (re)assigned the contact owner.",
238
+ zodSchema: z.object({ contactId: z.string() }).extend(ownerChangeSchema.shape)
239
+ });
240
+ const ContactMerged = defineCrmEvent({
241
+ name: CRM_EVENTS.CONTACT_MERGED,
242
+ resource: "contact",
243
+ description: "merge() folded a secondary contact into the primary; secondary deleted.",
244
+ zodSchema: mergeSchema
245
+ });
246
+ const OpportunityCreated = defineCrmEvent({
247
+ name: CRM_EVENTS.OPPORTUNITY_CREATED,
248
+ resource: "opportunity",
249
+ description: "openOpportunity() created a deal in a pipeline stage.",
250
+ zodSchema: z.object({
251
+ opportunityId: z.string(),
252
+ pipelineId: z.string(),
253
+ stageId: z.string()
254
+ })
255
+ });
256
+ const OpportunityStageAdvanced = defineCrmEvent({
257
+ name: CRM_EVENTS.OPPORTUNITY_STAGE_ADVANCED,
258
+ resource: "opportunity",
259
+ description: "moveToStage() moved the deal forward (higher stage sequence).",
260
+ zodSchema: z.object({
261
+ opportunityId: z.string(),
262
+ from: stageRefSchema,
263
+ to: stageRefSchema
264
+ })
265
+ });
266
+ const OpportunityStageRegressed = defineCrmEvent({
267
+ name: CRM_EVENTS.OPPORTUNITY_STAGE_REGRESSED,
268
+ resource: "opportunity",
269
+ description: "moveToStage() moved the deal backward (lower stage sequence).",
270
+ zodSchema: z.object({
271
+ opportunityId: z.string(),
272
+ from: stageRefSchema,
273
+ to: stageRefSchema
274
+ })
275
+ });
276
+ const OpportunityWon = defineCrmEvent({
277
+ name: CRM_EVENTS.OPPORTUNITY_WON,
278
+ resource: "opportunity",
279
+ description: "win() closed the deal as won (terminal).",
280
+ zodSchema: z.object({
281
+ opportunityId: z.string(),
282
+ amount: z.number().optional()
283
+ })
284
+ });
285
+ const OpportunityLost = defineCrmEvent({
286
+ name: CRM_EVENTS.OPPORTUNITY_LOST,
287
+ resource: "opportunity",
288
+ description: "lose() closed the deal as lost (terminal).",
289
+ zodSchema: z.object({
290
+ opportunityId: z.string(),
291
+ lostReasonId: z.string().optional()
292
+ })
293
+ });
294
+ const OpportunityAbandoned = defineCrmEvent({
295
+ name: CRM_EVENTS.OPPORTUNITY_ABANDONED,
296
+ resource: "opportunity",
297
+ description: "abandon() closed the deal as abandoned (terminal).",
298
+ zodSchema: z.object({ opportunityId: z.string() })
299
+ });
300
+ const OpportunityAssigned = defineCrmEvent({
301
+ name: CRM_EVENTS.OPPORTUNITY_ASSIGNED,
302
+ resource: "opportunity",
303
+ description: "assign() (re)assigned the opportunity owner.",
304
+ zodSchema: z.object({ opportunityId: z.string() }).extend(ownerChangeSchema.shape)
305
+ });
306
+ const OpportunityAmountChanged = defineCrmEvent({
307
+ name: CRM_EVENTS.OPPORTUNITY_AMOUNT_CHANGED,
308
+ resource: "opportunity",
309
+ description: "updateAmount() changed an open deal value.",
310
+ zodSchema: z.object({
311
+ opportunityId: z.string(),
312
+ amount: z.number(),
313
+ previousAmount: z.number().optional(),
314
+ reason: z.string().optional(),
315
+ by: z.string().optional()
316
+ })
317
+ });
318
+ const OpportunityExtended = defineCrmEvent({
319
+ name: CRM_EVENTS.OPPORTUNITY_EXTENDED,
320
+ resource: "opportunity",
321
+ description: "extendCloseDate() pushed expectedCloseAt further out.",
322
+ zodSchema: z.object({
323
+ opportunityId: z.string(),
324
+ expectedCloseAt: z.iso.datetime(),
325
+ previousExpectedCloseAt: z.iso.datetime().optional(),
326
+ reason: z.string().optional(),
327
+ by: z.string().optional()
328
+ })
329
+ });
330
+ const PipelineCreated = defineCrmEvent({
331
+ name: CRM_EVENTS.PIPELINE_CREATED,
332
+ resource: "pipeline",
333
+ description: "A pipeline was created.",
334
+ zodSchema: z.object({ pipelineId: z.string() })
335
+ });
336
+ const PipelineArchived = defineCrmEvent({
337
+ name: CRM_EVENTS.PIPELINE_ARCHIVED,
338
+ resource: "pipeline",
339
+ description: "archive() soft-deprecated a pipeline.",
340
+ zodSchema: z.object({ pipelineId: z.string() })
341
+ });
342
+ const ActivityPlanned = defineCrmEvent({
343
+ name: CRM_EVENTS.ACTIVITY_PLANNED,
344
+ resource: "activity",
345
+ description: "plan() created a planned activity against a subject.",
346
+ zodSchema: z.object({
347
+ activityId: z.string(),
348
+ type: z.string(),
349
+ subjectKind: z.string(),
350
+ subjectId: z.string()
351
+ })
352
+ });
353
+ const ActivityCompleted = defineCrmEvent({
354
+ name: CRM_EVENTS.ACTIVITY_COMPLETED,
355
+ resource: "activity",
356
+ description: "complete() finished an activity.",
357
+ zodSchema: z.object({
358
+ activityId: z.string(),
359
+ completedAt: z.iso.datetime()
360
+ })
361
+ });
362
+ const ActivityCancelled = defineCrmEvent({
363
+ name: CRM_EVENTS.ACTIVITY_CANCELLED,
364
+ resource: "activity",
365
+ description: "cancel() cancelled an activity.",
366
+ zodSchema: z.object({
367
+ activityId: z.string(),
368
+ reason: z.string().optional()
369
+ })
370
+ });
371
+ const ActivityRescheduled = defineCrmEvent({
372
+ name: CRM_EVENTS.ACTIVITY_RESCHEDULED,
373
+ resource: "activity",
374
+ description: "reschedule() moved an activity due date.",
375
+ zodSchema: z.object({
376
+ activityId: z.string(),
377
+ dueDate: z.iso.datetime(),
378
+ previousDueDate: z.iso.datetime().optional(),
379
+ reason: z.string().optional(),
380
+ by: z.string().optional()
381
+ })
382
+ });
383
+ const ActivityOutcomeRecorded = defineCrmEvent({
384
+ name: CRM_EVENTS.ACTIVITY_OUTCOME_RECORDED,
385
+ resource: "activity",
386
+ description: "recordOutcome() logged an activity outcome (no status change).",
387
+ zodSchema: z.object({
388
+ activityId: z.string(),
389
+ outcome: z.record(z.string(), z.unknown())
390
+ })
391
+ });
392
+ const NoteAdded = defineCrmEvent({
393
+ name: CRM_EVENTS.NOTE_ADDED,
394
+ resource: "note",
395
+ description: "A note was appended to a subject timeline.",
396
+ zodSchema: z.object({
397
+ noteId: z.string(),
398
+ subjectKind: z.string(),
399
+ subjectId: z.string()
400
+ })
401
+ });
402
+ const CampaignCreated = defineCrmEvent({
403
+ name: CRM_EVENTS.CAMPAIGN_CREATED,
404
+ resource: "campaign",
405
+ description: "createCampaign() inserted a campaign.",
406
+ zodSchema: z.object({
407
+ campaignId: z.string(),
408
+ name: z.string()
409
+ })
410
+ });
411
+ const CampaignActivated = defineCrmEvent({
412
+ name: CRM_EVENTS.CAMPAIGN_ACTIVATED,
413
+ resource: "campaign",
414
+ description: "activate() moved a campaign to active.",
415
+ zodSchema: z.object({ campaignId: z.string() })
416
+ });
417
+ const CampaignPaused = defineCrmEvent({
418
+ name: CRM_EVENTS.CAMPAIGN_PAUSED,
419
+ resource: "campaign",
420
+ description: "pause() suspended an active campaign.",
421
+ zodSchema: z.object({
422
+ campaignId: z.string(),
423
+ reason: z.string().optional()
424
+ })
425
+ });
426
+ const CampaignResumed = defineCrmEvent({
427
+ name: CRM_EVENTS.CAMPAIGN_RESUMED,
428
+ resource: "campaign",
429
+ description: "resume() reactivated a paused campaign.",
430
+ zodSchema: z.object({ campaignId: z.string() })
431
+ });
432
+ const CampaignCompleted = defineCrmEvent({
433
+ name: CRM_EVENTS.CAMPAIGN_COMPLETED,
434
+ resource: "campaign",
435
+ description: "complete() closed a campaign.",
436
+ zodSchema: z.object({
437
+ campaignId: z.string(),
438
+ actualRevenueAmount: z.number().optional()
439
+ })
440
+ });
441
+ const CampaignArchived = defineCrmEvent({
442
+ name: CRM_EVENTS.CAMPAIGN_ARCHIVED,
443
+ resource: "campaign",
444
+ description: "archive() archived a campaign (terminal).",
445
+ zodSchema: z.object({ campaignId: z.string() })
446
+ });
447
+ const SubscriptionCreated = defineCrmEvent({
448
+ name: CRM_EVENTS.SUBSCRIPTION_CREATED,
449
+ resource: "subscription",
450
+ description: "subscribe() created an opt-in (pending or confirmed).",
451
+ zodSchema: z.object({
452
+ subscriptionId: z.string(),
453
+ contactId: z.string(),
454
+ campaignId: z.string(),
455
+ requiresConfirmation: z.boolean()
456
+ })
457
+ });
458
+ const SubscriptionConfirmed = defineCrmEvent({
459
+ name: CRM_EVENTS.SUBSCRIPTION_CONFIRMED,
460
+ resource: "subscription",
461
+ description: "confirm() validated a double-opt-in token.",
462
+ zodSchema: z.object({
463
+ subscriptionId: z.string(),
464
+ contactId: z.string(),
465
+ campaignId: z.string(),
466
+ confirmedAt: z.iso.datetime()
467
+ })
468
+ });
469
+ const SubscriptionUnsubscribed = defineCrmEvent({
470
+ name: CRM_EVENTS.SUBSCRIPTION_UNSUBSCRIBED,
471
+ resource: "subscription",
472
+ description: "unsubscribe() opted a contact out (terminal).",
473
+ zodSchema: z.object({
474
+ subscriptionId: z.string(),
475
+ contactId: z.string(),
476
+ campaignId: z.string(),
477
+ reason: z.string().optional()
478
+ })
479
+ });
480
+ const SubscriptionBounced = defineCrmEvent({
481
+ name: CRM_EVENTS.SUBSCRIPTION_BOUNCED,
482
+ resource: "subscription",
483
+ description: "markBounced() recorded a hard bounce (terminal).",
484
+ zodSchema: z.object({
485
+ subscriptionId: z.string(),
486
+ contactId: z.string(),
487
+ campaignId: z.string(),
488
+ reason: z.string().optional()
489
+ })
490
+ });
491
+ const SubscriptionComplained = defineCrmEvent({
492
+ name: CRM_EVENTS.SUBSCRIPTION_COMPLAINED,
493
+ resource: "subscription",
494
+ description: "markComplained() recorded a spam complaint (terminal).",
495
+ zodSchema: z.object({
496
+ subscriptionId: z.string(),
497
+ contactId: z.string(),
498
+ campaignId: z.string()
499
+ })
500
+ });
501
+ const emailEventSchema = z.object({
502
+ eventId: z.string(),
503
+ kind: z.string(),
504
+ subjectKind: z.string(),
505
+ subjectId: z.string(),
506
+ campaignId: z.string().optional()
507
+ });
508
+ const EmailEventOpened = defineCrmEvent({
509
+ name: CRM_EVENTS.EMAIL_EVENT_OPENED,
510
+ resource: "email_event",
511
+ description: "recordOpen() logged an email open.",
512
+ zodSchema: emailEventSchema
513
+ });
514
+ const EmailEventClicked = defineCrmEvent({
515
+ name: CRM_EVENTS.EMAIL_EVENT_CLICKED,
516
+ resource: "email_event",
517
+ description: "recordClick() logged a link click.",
518
+ zodSchema: emailEventSchema
519
+ });
520
+ const EmailEventBounced = defineCrmEvent({
521
+ name: CRM_EVENTS.EMAIL_EVENT_BOUNCED,
522
+ resource: "email_event",
523
+ description: "recordBounce() logged a bounce.",
524
+ zodSchema: emailEventSchema
525
+ });
526
+ const EmailEventComplained = defineCrmEvent({
527
+ name: CRM_EVENTS.EMAIL_EVENT_COMPLAINED,
528
+ resource: "email_event",
529
+ description: "recordComplaint() logged a spam complaint.",
530
+ zodSchema: emailEventSchema
531
+ });
532
+ const EmailEventUnsubscribed = defineCrmEvent({
533
+ name: CRM_EVENTS.EMAIL_EVENT_UNSUBSCRIBED,
534
+ resource: "email_event",
535
+ description: "recordUnsubscribe() logged a one-click unsubscribe.",
536
+ zodSchema: emailEventSchema
537
+ });
538
+ const ConversationCreated = defineCrmEvent({
539
+ name: CRM_EVENTS.CONVERSATION_CREATED,
540
+ resource: "conversation",
541
+ description: "openConversation() started a conversation thread.",
542
+ zodSchema: z.object({
543
+ conversationId: z.string(),
544
+ channel: z.string(),
545
+ participantKind: z.string(),
546
+ participantId: z.string()
547
+ })
548
+ });
549
+ const ConversationAssigned = defineCrmEvent({
550
+ name: CRM_EVENTS.CONVERSATION_ASSIGNED,
551
+ resource: "conversation",
552
+ description: "assign() (re)assigned a conversation owner.",
553
+ zodSchema: z.object({
554
+ conversationId: z.string(),
555
+ assigneeId: z.string(),
556
+ previousAssigneeId: z.string().optional()
557
+ })
558
+ });
559
+ const ConversationResolved = defineCrmEvent({
560
+ name: CRM_EVENTS.CONVERSATION_RESOLVED,
561
+ resource: "conversation",
562
+ description: "resolve() closed a conversation.",
563
+ zodSchema: z.object({
564
+ conversationId: z.string(),
565
+ status: z.string(),
566
+ previousStatus: z.string()
567
+ })
568
+ });
569
+ const ConversationReopened = defineCrmEvent({
570
+ name: CRM_EVENTS.CONVERSATION_REOPENED,
571
+ resource: "conversation",
572
+ description: "reopen() reopened a resolved conversation.",
573
+ zodSchema: z.object({
574
+ conversationId: z.string(),
575
+ status: z.string(),
576
+ previousStatus: z.string()
577
+ })
578
+ });
579
+ const MessageReceived = defineCrmEvent({
580
+ name: CRM_EVENTS.MESSAGE_RECEIVED,
581
+ resource: "message",
582
+ description: "recordInbound() logged an inbound message.",
583
+ zodSchema: z.object({
584
+ messageId: z.string(),
585
+ conversationId: z.string(),
586
+ direction: z.string(),
587
+ externalMessageId: z.string().optional()
588
+ })
589
+ });
590
+ const MessageSent = defineCrmEvent({
591
+ name: CRM_EVENTS.MESSAGE_SENT,
592
+ resource: "message",
593
+ description: "recordOutbound() logged an outbound message.",
594
+ zodSchema: z.object({
595
+ messageId: z.string(),
596
+ conversationId: z.string(),
597
+ direction: z.string(),
598
+ externalMessageId: z.string().optional()
599
+ })
600
+ });
601
+ const crmEventDefinitions = [
602
+ LeadCreated,
603
+ LeadStatusChanged,
604
+ LeadScored,
605
+ LeadConverted,
606
+ LeadDisqualified,
607
+ LeadEnriched,
608
+ LeadAssigned,
609
+ AccountCreated,
610
+ AccountUpdated,
611
+ AccountEnriched,
612
+ AccountAssigned,
613
+ AccountMerged,
614
+ ContactCreated,
615
+ ContactUpdated,
616
+ ContactEnriched,
617
+ ContactAssigned,
618
+ ContactMerged,
619
+ OpportunityCreated,
620
+ OpportunityStageAdvanced,
621
+ OpportunityStageRegressed,
622
+ OpportunityWon,
623
+ OpportunityLost,
624
+ OpportunityAbandoned,
625
+ OpportunityAssigned,
626
+ OpportunityAmountChanged,
627
+ OpportunityExtended,
628
+ PipelineCreated,
629
+ PipelineArchived,
630
+ ActivityPlanned,
631
+ ActivityCompleted,
632
+ ActivityCancelled,
633
+ ActivityRescheduled,
634
+ ActivityOutcomeRecorded,
635
+ NoteAdded,
636
+ CampaignCreated,
637
+ CampaignActivated,
638
+ CampaignPaused,
639
+ CampaignResumed,
640
+ CampaignCompleted,
641
+ CampaignArchived,
642
+ SubscriptionCreated,
643
+ SubscriptionConfirmed,
644
+ SubscriptionUnsubscribed,
645
+ SubscriptionBounced,
646
+ SubscriptionComplained,
647
+ EmailEventOpened,
648
+ EmailEventClicked,
649
+ EmailEventBounced,
650
+ EmailEventComplained,
651
+ EmailEventUnsubscribed,
652
+ ConversationCreated,
653
+ ConversationAssigned,
654
+ ConversationResolved,
655
+ ConversationReopened,
656
+ MessageReceived,
657
+ MessageSent
658
+ ];
659
+ //#endregion
660
+ //#region src/events/helpers.ts
661
+ /**
662
+ * Event creation helper.
663
+ *
664
+ * Thin wrapper around `@classytic/primitives`' `createEvent` that fills the
665
+ * shared `EventMeta` from a `CrmContext` (userId/actorId, organizationId,
666
+ * correlationId). Use this instead of importing `createEvent` directly so
667
+ * the `source: 'crm'` stamp + tenant scoping stay consistent.
668
+ */
669
+ function createEvent(type, payload, ctx, meta) {
670
+ return createEvent$1(type, payload, {
671
+ source: "crm",
672
+ ...ctx?.actorId !== void 0 ? { actorId: String(ctx.actorId) } : {},
673
+ ...ctx?.organizationId !== void 0 ? { organizationId: String(ctx.organizationId) } : {},
674
+ ...ctx?.correlationId !== void 0 ? { correlationId: ctx.correlationId } : {},
675
+ ...meta
676
+ });
677
+ }
678
+ //#endregion
679
+ //#region src/events/in-process-bus.ts
680
+ /**
681
+ * In-process event bus — default fallback when no `eventTransport` is
682
+ * provided to `createCrm`. Structurally identical to arc's
683
+ * `MemoryEventTransport`; pattern matching is delegated to primitives.
684
+ * Handler errors are swallowed — event emission never blocks writes.
685
+ */
686
+ var InProcessCrmBus = class {
687
+ name = "in-process-crm";
688
+ subs = [];
689
+ logger;
690
+ constructor(options) {
691
+ if (options?.logger) this.logger = options.logger;
692
+ }
693
+ async publish(event) {
694
+ const matching = this.subs.filter((s) => matchEventPattern(s.pattern, event.type));
695
+ for (const sub of matching) try {
696
+ await sub.handler(event);
697
+ } catch (err) {
698
+ this.logger?.error?.(`[crm] event handler error [${event.type}]`, { error: err instanceof Error ? err.message : String(err) });
699
+ }
700
+ }
701
+ async subscribe(pattern, handler) {
702
+ const sub = {
703
+ pattern,
704
+ handler
705
+ };
706
+ this.subs.push(sub);
707
+ return () => {
708
+ this.subs = this.subs.filter((s) => s !== sub);
709
+ };
710
+ }
711
+ async close() {
712
+ this.subs = [];
713
+ }
714
+ };
715
+ //#endregion
716
+ export { AccountAssigned, AccountCreated, AccountEnriched, AccountMerged, AccountUpdated, ActivityCancelled, ActivityCompleted, ActivityOutcomeRecorded, ActivityPlanned, ActivityRescheduled, CRM_EVENTS, CampaignActivated, CampaignArchived, CampaignCompleted, CampaignCreated, CampaignPaused, CampaignResumed, ContactAssigned, ContactCreated, ContactEnriched, ContactMerged, ContactUpdated, ConversationAssigned, ConversationCreated, ConversationReopened, ConversationResolved, EmailEventBounced, EmailEventClicked, EmailEventComplained, EmailEventOpened, EmailEventUnsubscribed, InProcessCrmBus, LeadAssigned, LeadConverted, LeadCreated, LeadDisqualified, LeadEnriched, LeadScored, LeadStatusChanged, MessageReceived, MessageSent, NoteAdded, OpportunityAbandoned, OpportunityAmountChanged, OpportunityAssigned, OpportunityCreated, OpportunityExtended, OpportunityLost, OpportunityStageAdvanced, OpportunityStageRegressed, OpportunityWon, PipelineArchived, PipelineCreated, SubscriptionBounced, SubscriptionComplained, SubscriptionConfirmed, SubscriptionCreated, SubscriptionUnsubscribed, createEvent, crmEventDefinitions };