@classytic/crm 0.1.1 → 0.5.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.
package/dist/index.d.ts CHANGED
@@ -1,374 +1,425 @@
1
- import { DomainEvent, EventHandler, EventTransport } from "@classytic/primitives/events";
1
+ import * as mongoose1 from "mongoose";
2
+ import { Connection, Model, Schema, SchemaDefinitionProperty } from "mongoose";
3
+ import { PaginationConfig, PluginType, Repository } from "@classytic/mongokit";
4
+ import { DomainEvent, EventHandler, EventMeta, EventTransport } from "@classytic/primitives/events";
2
5
  import { z } from "zod";
3
- import { OutboxStore } from "@classytic/primitives/outbox";
4
- import { ContactInfo, PersonName } from "@classytic/primitives/person";
5
- import { Money } from "@classytic/primitives/money";
6
+ import { OperationContext } from "@classytic/primitives/context";
6
7
 
7
- //#region src/domain/entities/account.d.ts
8
- /**
9
- * An account (company / organization). Contacts, Opportunities, and
10
- * Activities can reference it.
11
- */
12
- interface Account {
13
- id: string;
14
- name: string;
15
- /** Domain / website — used by host deduplication pipelines. */
16
- domain?: string;
17
- industry?: string;
18
- /** Employee-count bucket, e.g. `1-10`, `11-50`. Free-form for flexibility. */
19
- sizeBucket?: string;
20
- annualRevenue?: {
21
- amount: number;
22
- currency: string;
23
- };
24
- ownerId?: string;
25
- tags?: readonly string[];
26
- metadata?: Record<string, unknown>;
27
- createdAt: Date;
28
- updatedAt: Date;
29
- }
30
- //#endregion
31
- //#region src/domain/ports/account-repository.port.d.ts
32
- interface AccountFilter {
33
- ownerId?: string;
34
- industry?: string;
35
- domain?: string;
36
- tags?: readonly string[];
37
- }
38
- interface AccountRepository {
39
- findById(id: string): Promise<Account | null>;
40
- findByDomain(domain: string): Promise<Account | null>;
41
- list(filter?: AccountFilter): Promise<Account[]>;
42
- create(a: Omit<Account, 'createdAt' | 'updatedAt'>): Promise<Account>;
43
- update(id: string, patch: Partial<Account>): Promise<Account>;
44
- delete(id: string): Promise<void>;
45
- }
8
+ //#region src/events/event-constants.d.ts
9
+ /** PACKAGE_RULES rule 12: `package:resource.verb`. */
10
+ declare const CRM_EVENTS: {
11
+ readonly LEAD_CREATED: "crm:lead.created";
12
+ readonly LEAD_STATUS_CHANGED: "crm:lead.status_changed";
13
+ readonly LEAD_SCORED: "crm:lead.scored";
14
+ readonly LEAD_CONVERTED: "crm:lead.converted";
15
+ readonly LEAD_DISQUALIFIED: "crm:lead.disqualified";
16
+ readonly ACCOUNT_CREATED: "crm:account.created";
17
+ readonly ACCOUNT_UPDATED: "crm:account.updated";
18
+ readonly CONTACT_CREATED: "crm:contact.created";
19
+ readonly CONTACT_UPDATED: "crm:contact.updated";
20
+ readonly OPPORTUNITY_CREATED: "crm:opportunity.created";
21
+ readonly OPPORTUNITY_STAGE_ADVANCED: "crm:opportunity.stage_advanced";
22
+ readonly OPPORTUNITY_STAGE_REGRESSED: "crm:opportunity.stage_regressed";
23
+ readonly OPPORTUNITY_WON: "crm:opportunity.won";
24
+ readonly OPPORTUNITY_LOST: "crm:opportunity.lost";
25
+ readonly OPPORTUNITY_ABANDONED: "crm:opportunity.abandoned";
26
+ readonly PIPELINE_CREATED: "crm:pipeline.created";
27
+ readonly PIPELINE_ARCHIVED: "crm:pipeline.archived";
28
+ readonly ACTIVITY_PLANNED: "crm:activity.planned";
29
+ readonly ACTIVITY_COMPLETED: "crm:activity.completed";
30
+ readonly ACTIVITY_CANCELLED: "crm:activity.cancelled";
31
+ readonly NOTE_ADDED: "crm:note.added";
32
+ readonly CAMPAIGN_CREATED: "crm:campaign.created";
33
+ readonly CAMPAIGN_ACTIVATED: "crm:campaign.activated";
34
+ readonly CAMPAIGN_PAUSED: "crm:campaign.paused";
35
+ readonly CAMPAIGN_RESUMED: "crm:campaign.resumed";
36
+ readonly CAMPAIGN_COMPLETED: "crm:campaign.completed";
37
+ readonly CAMPAIGN_ARCHIVED: "crm:campaign.archived";
38
+ readonly SUBSCRIPTION_CREATED: "crm:subscription.created";
39
+ readonly SUBSCRIPTION_CONFIRMED: "crm:subscription.confirmed";
40
+ readonly SUBSCRIPTION_UNSUBSCRIBED: "crm:subscription.unsubscribed";
41
+ readonly SUBSCRIPTION_BOUNCED: "crm:subscription.bounced";
42
+ readonly SUBSCRIPTION_COMPLAINED: "crm:subscription.complained";
43
+ readonly EMAIL_EVENT_OPENED: "crm:email_event.opened";
44
+ readonly EMAIL_EVENT_CLICKED: "crm:email_event.clicked";
45
+ readonly EMAIL_EVENT_BOUNCED: "crm:email_event.bounced";
46
+ readonly EMAIL_EVENT_COMPLAINED: "crm:email_event.complained";
47
+ readonly EMAIL_EVENT_UNSUBSCRIBED: "crm:email_event.unsubscribed";
48
+ readonly LEAD_ENRICHED: "crm:lead.enriched";
49
+ readonly LEAD_ASSIGNED: "crm:lead.assigned";
50
+ readonly CONTACT_ENRICHED: "crm:contact.enriched";
51
+ readonly CONTACT_ASSIGNED: "crm:contact.assigned";
52
+ readonly CONTACT_MERGED: "crm:contact.merged";
53
+ readonly ACCOUNT_ENRICHED: "crm:account.enriched";
54
+ readonly ACCOUNT_ASSIGNED: "crm:account.assigned";
55
+ readonly ACCOUNT_MERGED: "crm:account.merged";
56
+ readonly OPPORTUNITY_ASSIGNED: "crm:opportunity.assigned";
57
+ readonly OPPORTUNITY_AMOUNT_CHANGED: "crm:opportunity.amount_changed";
58
+ readonly OPPORTUNITY_EXTENDED: "crm:opportunity.extended";
59
+ readonly ACTIVITY_RESCHEDULED: "crm:activity.rescheduled";
60
+ readonly ACTIVITY_OUTCOME_RECORDED: "crm:activity.outcome_recorded";
61
+ readonly CONVERSATION_CREATED: "crm:conversation.created";
62
+ readonly CONVERSATION_ASSIGNED: "crm:conversation.assigned";
63
+ readonly CONVERSATION_RESOLVED: "crm:conversation.resolved";
64
+ readonly CONVERSATION_REOPENED: "crm:conversation.reopened";
65
+ readonly MESSAGE_RECEIVED: "crm:message.received";
66
+ readonly MESSAGE_SENT: "crm:message.sent";
67
+ };
68
+ type CrmEventName = (typeof CRM_EVENTS)[keyof typeof CRM_EVENTS];
46
69
  //#endregion
47
- //#region src/domain/ports/clock.port.d.ts
48
- interface Clock {
49
- now(): Date;
70
+ //#region src/events/event-catalog.d.ts
71
+
72
+ /** JSON-Schema object produced by `z.toJSONSchema` — what arc's registry consumes. */
73
+ type CrmEventSchema = Record<string, unknown>;
74
+ /** Structurally identical to arc's `EventDefinitionOutput` — do NOT import arc. */
75
+ interface CrmEventDefinition<TSchema extends z.ZodType = z.ZodType> {
76
+ name: CrmEventName;
77
+ version: number;
78
+ description: string;
79
+ zodSchema: TSchema;
80
+ schema: CrmEventSchema;
81
+ create(payload: z.infer<TSchema>, meta?: Partial<EventMeta>): DomainEvent<z.infer<TSchema>>;
50
82
  }
51
- declare const systemClock: Clock;
83
+ declare const LeadCreated: CrmEventDefinition<z.ZodObject<{
84
+ leadId: z.ZodString;
85
+ email: z.ZodOptional<z.ZodString>;
86
+ score: z.ZodNumber;
87
+ }, z.core.$strip>>;
88
+ declare const LeadStatusChanged: CrmEventDefinition<z.ZodObject<{
89
+ leadId: z.ZodString;
90
+ from: z.ZodString;
91
+ to: z.ZodString;
92
+ }, z.core.$strip>>;
93
+ declare const LeadScored: CrmEventDefinition<z.ZodObject<{
94
+ leadId: z.ZodString;
95
+ score: z.ZodNumber;
96
+ }, z.core.$strip>>;
97
+ declare const LeadConverted: CrmEventDefinition<z.ZodObject<{
98
+ leadId: z.ZodString;
99
+ contactId: z.ZodString;
100
+ accountId: z.ZodOptional<z.ZodString>;
101
+ opportunityId: z.ZodString;
102
+ }, z.core.$strip>>;
103
+ declare const LeadDisqualified: CrmEventDefinition<z.ZodObject<{
104
+ leadId: z.ZodString;
105
+ reason: z.ZodString;
106
+ }, z.core.$strip>>;
107
+ declare const LeadEnriched: CrmEventDefinition<z.ZodObject<{
108
+ leadId: z.ZodString;
109
+ fields: z.ZodArray<z.ZodString>;
110
+ provider: z.ZodOptional<z.ZodString>;
111
+ }, z.core.$strip>>;
112
+ declare const LeadAssigned: CrmEventDefinition<z.ZodObject<{
113
+ leadId: z.ZodString;
114
+ ownerId: z.ZodString;
115
+ previousOwnerId: z.ZodOptional<z.ZodString>;
116
+ }, z.core.$strip>>;
117
+ declare const AccountCreated: CrmEventDefinition<z.ZodObject<{
118
+ accountId: z.ZodString;
119
+ name: z.ZodString;
120
+ }, z.core.$strip>>;
121
+ declare const AccountUpdated: CrmEventDefinition<z.ZodObject<{
122
+ accountId: z.ZodString;
123
+ }, z.core.$strip>>;
124
+ declare const AccountEnriched: CrmEventDefinition<z.ZodObject<{
125
+ accountId: z.ZodString;
126
+ fields: z.ZodArray<z.ZodString>;
127
+ provider: z.ZodOptional<z.ZodString>;
128
+ }, z.core.$strip>>;
129
+ declare const AccountAssigned: CrmEventDefinition<z.ZodObject<{
130
+ accountId: z.ZodString;
131
+ ownerId: z.ZodString;
132
+ previousOwnerId: z.ZodOptional<z.ZodString>;
133
+ }, z.core.$strip>>;
134
+ declare const AccountMerged: CrmEventDefinition<z.ZodObject<{
135
+ primaryId: z.ZodString;
136
+ secondaryId: z.ZodString;
137
+ strategy: z.ZodEnum<{
138
+ preferPrimary: "preferPrimary";
139
+ preferSecondary: "preferSecondary";
140
+ }>;
141
+ }, z.core.$strip>>;
142
+ declare const ContactCreated: CrmEventDefinition<z.ZodObject<{
143
+ contactId: z.ZodString;
144
+ email: z.ZodOptional<z.ZodString>;
145
+ }, z.core.$strip>>;
146
+ declare const ContactUpdated: CrmEventDefinition<z.ZodObject<{
147
+ contactId: z.ZodString;
148
+ }, z.core.$strip>>;
149
+ declare const ContactEnriched: CrmEventDefinition<z.ZodObject<{
150
+ contactId: z.ZodString;
151
+ fields: z.ZodArray<z.ZodString>;
152
+ provider: z.ZodOptional<z.ZodString>;
153
+ }, z.core.$strip>>;
154
+ declare const ContactAssigned: CrmEventDefinition<z.ZodObject<{
155
+ contactId: z.ZodString;
156
+ ownerId: z.ZodString;
157
+ previousOwnerId: z.ZodOptional<z.ZodString>;
158
+ }, z.core.$strip>>;
159
+ declare const ContactMerged: CrmEventDefinition<z.ZodObject<{
160
+ primaryId: z.ZodString;
161
+ secondaryId: z.ZodString;
162
+ strategy: z.ZodEnum<{
163
+ preferPrimary: "preferPrimary";
164
+ preferSecondary: "preferSecondary";
165
+ }>;
166
+ }, z.core.$strip>>;
167
+ declare const OpportunityCreated: CrmEventDefinition<z.ZodObject<{
168
+ opportunityId: z.ZodString;
169
+ pipelineId: z.ZodString;
170
+ stageId: z.ZodString;
171
+ }, z.core.$strip>>;
172
+ declare const OpportunityStageAdvanced: CrmEventDefinition<z.ZodObject<{
173
+ opportunityId: z.ZodString;
174
+ from: z.ZodObject<{
175
+ stageId: z.ZodString;
176
+ sequence: z.ZodNumber;
177
+ }, z.core.$strip>;
178
+ to: z.ZodObject<{
179
+ stageId: z.ZodString;
180
+ sequence: z.ZodNumber;
181
+ }, z.core.$strip>;
182
+ }, z.core.$strip>>;
183
+ declare const OpportunityStageRegressed: CrmEventDefinition<z.ZodObject<{
184
+ opportunityId: z.ZodString;
185
+ from: z.ZodObject<{
186
+ stageId: z.ZodString;
187
+ sequence: z.ZodNumber;
188
+ }, z.core.$strip>;
189
+ to: z.ZodObject<{
190
+ stageId: z.ZodString;
191
+ sequence: z.ZodNumber;
192
+ }, z.core.$strip>;
193
+ }, z.core.$strip>>;
194
+ declare const OpportunityWon: CrmEventDefinition<z.ZodObject<{
195
+ opportunityId: z.ZodString;
196
+ amount: z.ZodOptional<z.ZodNumber>;
197
+ }, z.core.$strip>>;
198
+ declare const OpportunityLost: CrmEventDefinition<z.ZodObject<{
199
+ opportunityId: z.ZodString;
200
+ lostReasonId: z.ZodOptional<z.ZodString>;
201
+ }, z.core.$strip>>;
202
+ declare const OpportunityAbandoned: CrmEventDefinition<z.ZodObject<{
203
+ opportunityId: z.ZodString;
204
+ }, z.core.$strip>>;
205
+ declare const OpportunityAssigned: CrmEventDefinition<z.ZodObject<{
206
+ opportunityId: z.ZodString;
207
+ ownerId: z.ZodString;
208
+ previousOwnerId: z.ZodOptional<z.ZodString>;
209
+ }, z.core.$strip>>;
210
+ declare const OpportunityAmountChanged: CrmEventDefinition<z.ZodObject<{
211
+ opportunityId: z.ZodString;
212
+ amount: z.ZodNumber;
213
+ previousAmount: z.ZodOptional<z.ZodNumber>;
214
+ reason: z.ZodOptional<z.ZodString>;
215
+ by: z.ZodOptional<z.ZodString>;
216
+ }, z.core.$strip>>;
217
+ declare const OpportunityExtended: CrmEventDefinition<z.ZodObject<{
218
+ opportunityId: z.ZodString;
219
+ expectedCloseAt: z.ZodISODateTime;
220
+ previousExpectedCloseAt: z.ZodOptional<z.ZodISODateTime>;
221
+ reason: z.ZodOptional<z.ZodString>;
222
+ by: z.ZodOptional<z.ZodString>;
223
+ }, z.core.$strip>>;
224
+ declare const PipelineCreated: CrmEventDefinition<z.ZodObject<{
225
+ pipelineId: z.ZodString;
226
+ }, z.core.$strip>>;
227
+ declare const PipelineArchived: CrmEventDefinition<z.ZodObject<{
228
+ pipelineId: z.ZodString;
229
+ }, z.core.$strip>>;
230
+ declare const ActivityPlanned: CrmEventDefinition<z.ZodObject<{
231
+ activityId: z.ZodString;
232
+ type: z.ZodString;
233
+ subjectKind: z.ZodString;
234
+ subjectId: z.ZodString;
235
+ }, z.core.$strip>>;
236
+ declare const ActivityCompleted: CrmEventDefinition<z.ZodObject<{
237
+ activityId: z.ZodString;
238
+ completedAt: z.ZodISODateTime;
239
+ }, z.core.$strip>>;
240
+ declare const ActivityCancelled: CrmEventDefinition<z.ZodObject<{
241
+ activityId: z.ZodString;
242
+ reason: z.ZodOptional<z.ZodString>;
243
+ }, z.core.$strip>>;
244
+ declare const ActivityRescheduled: CrmEventDefinition<z.ZodObject<{
245
+ activityId: z.ZodString;
246
+ dueDate: z.ZodISODateTime;
247
+ previousDueDate: z.ZodOptional<z.ZodISODateTime>;
248
+ reason: z.ZodOptional<z.ZodString>;
249
+ by: z.ZodOptional<z.ZodString>;
250
+ }, z.core.$strip>>;
251
+ declare const ActivityOutcomeRecorded: CrmEventDefinition<z.ZodObject<{
252
+ activityId: z.ZodString;
253
+ outcome: z.ZodRecord<z.ZodString, z.ZodUnknown>;
254
+ }, z.core.$strip>>;
255
+ declare const NoteAdded: CrmEventDefinition<z.ZodObject<{
256
+ noteId: z.ZodString;
257
+ subjectKind: z.ZodString;
258
+ subjectId: z.ZodString;
259
+ }, z.core.$strip>>;
260
+ declare const CampaignCreated: CrmEventDefinition<z.ZodObject<{
261
+ campaignId: z.ZodString;
262
+ name: z.ZodString;
263
+ }, z.core.$strip>>;
264
+ declare const CampaignActivated: CrmEventDefinition<z.ZodObject<{
265
+ campaignId: z.ZodString;
266
+ }, z.core.$strip>>;
267
+ declare const CampaignPaused: CrmEventDefinition<z.ZodObject<{
268
+ campaignId: z.ZodString;
269
+ reason: z.ZodOptional<z.ZodString>;
270
+ }, z.core.$strip>>;
271
+ declare const CampaignResumed: CrmEventDefinition<z.ZodObject<{
272
+ campaignId: z.ZodString;
273
+ }, z.core.$strip>>;
274
+ declare const CampaignCompleted: CrmEventDefinition<z.ZodObject<{
275
+ campaignId: z.ZodString;
276
+ actualRevenueAmount: z.ZodOptional<z.ZodNumber>;
277
+ }, z.core.$strip>>;
278
+ declare const CampaignArchived: CrmEventDefinition<z.ZodObject<{
279
+ campaignId: z.ZodString;
280
+ }, z.core.$strip>>;
281
+ declare const SubscriptionCreated: CrmEventDefinition<z.ZodObject<{
282
+ subscriptionId: z.ZodString;
283
+ contactId: z.ZodString;
284
+ campaignId: z.ZodString;
285
+ requiresConfirmation: z.ZodBoolean;
286
+ }, z.core.$strip>>;
287
+ declare const SubscriptionConfirmed: CrmEventDefinition<z.ZodObject<{
288
+ subscriptionId: z.ZodString;
289
+ contactId: z.ZodString;
290
+ campaignId: z.ZodString;
291
+ confirmedAt: z.ZodISODateTime;
292
+ }, z.core.$strip>>;
293
+ declare const SubscriptionUnsubscribed: CrmEventDefinition<z.ZodObject<{
294
+ subscriptionId: z.ZodString;
295
+ contactId: z.ZodString;
296
+ campaignId: z.ZodString;
297
+ reason: z.ZodOptional<z.ZodString>;
298
+ }, z.core.$strip>>;
299
+ declare const SubscriptionBounced: CrmEventDefinition<z.ZodObject<{
300
+ subscriptionId: z.ZodString;
301
+ contactId: z.ZodString;
302
+ campaignId: z.ZodString;
303
+ reason: z.ZodOptional<z.ZodString>;
304
+ }, z.core.$strip>>;
305
+ declare const SubscriptionComplained: CrmEventDefinition<z.ZodObject<{
306
+ subscriptionId: z.ZodString;
307
+ contactId: z.ZodString;
308
+ campaignId: z.ZodString;
309
+ }, z.core.$strip>>;
310
+ declare const EmailEventOpened: CrmEventDefinition<z.ZodObject<{
311
+ eventId: z.ZodString;
312
+ kind: z.ZodString;
313
+ subjectKind: z.ZodString;
314
+ subjectId: z.ZodString;
315
+ campaignId: z.ZodOptional<z.ZodString>;
316
+ }, z.core.$strip>>;
317
+ declare const EmailEventClicked: CrmEventDefinition<z.ZodObject<{
318
+ eventId: z.ZodString;
319
+ kind: z.ZodString;
320
+ subjectKind: z.ZodString;
321
+ subjectId: z.ZodString;
322
+ campaignId: z.ZodOptional<z.ZodString>;
323
+ }, z.core.$strip>>;
324
+ declare const EmailEventBounced: CrmEventDefinition<z.ZodObject<{
325
+ eventId: z.ZodString;
326
+ kind: z.ZodString;
327
+ subjectKind: z.ZodString;
328
+ subjectId: z.ZodString;
329
+ campaignId: z.ZodOptional<z.ZodString>;
330
+ }, z.core.$strip>>;
331
+ declare const EmailEventComplained: CrmEventDefinition<z.ZodObject<{
332
+ eventId: z.ZodString;
333
+ kind: z.ZodString;
334
+ subjectKind: z.ZodString;
335
+ subjectId: z.ZodString;
336
+ campaignId: z.ZodOptional<z.ZodString>;
337
+ }, z.core.$strip>>;
338
+ declare const EmailEventUnsubscribed: CrmEventDefinition<z.ZodObject<{
339
+ eventId: z.ZodString;
340
+ kind: z.ZodString;
341
+ subjectKind: z.ZodString;
342
+ subjectId: z.ZodString;
343
+ campaignId: z.ZodOptional<z.ZodString>;
344
+ }, z.core.$strip>>;
345
+ declare const ConversationCreated: CrmEventDefinition<z.ZodObject<{
346
+ conversationId: z.ZodString;
347
+ channel: z.ZodString;
348
+ participantKind: z.ZodString;
349
+ participantId: z.ZodString;
350
+ }, z.core.$strip>>;
351
+ declare const ConversationAssigned: CrmEventDefinition<z.ZodObject<{
352
+ conversationId: z.ZodString;
353
+ assigneeId: z.ZodString;
354
+ previousAssigneeId: z.ZodOptional<z.ZodString>;
355
+ }, z.core.$strip>>;
356
+ declare const ConversationResolved: CrmEventDefinition<z.ZodObject<{
357
+ conversationId: z.ZodString;
358
+ status: z.ZodString;
359
+ previousStatus: z.ZodString;
360
+ }, z.core.$strip>>;
361
+ declare const ConversationReopened: CrmEventDefinition<z.ZodObject<{
362
+ conversationId: z.ZodString;
363
+ status: z.ZodString;
364
+ previousStatus: z.ZodString;
365
+ }, z.core.$strip>>;
366
+ declare const MessageReceived: CrmEventDefinition<z.ZodObject<{
367
+ messageId: z.ZodString;
368
+ conversationId: z.ZodString;
369
+ direction: z.ZodString;
370
+ externalMessageId: z.ZodOptional<z.ZodString>;
371
+ }, z.core.$strip>>;
372
+ declare const MessageSent: CrmEventDefinition<z.ZodObject<{
373
+ messageId: z.ZodString;
374
+ conversationId: z.ZodString;
375
+ direction: z.ZodString;
376
+ externalMessageId: z.ZodOptional<z.ZodString>;
377
+ }, z.core.$strip>>;
378
+ declare const crmEventDefinitions: ReadonlyArray<CrmEventDefinition>;
52
379
  //#endregion
53
- //#region src/domain/ports/id-generator.port.d.ts
54
- interface IdGenerator {
55
- next(): string;
380
+ //#region src/models/tenant-field.d.ts
381
+ type TenantFieldType = 'objectId' | 'string';
382
+ interface SchemaBuildConfig {
383
+ /** Schema type for the tenant column. Engine plumbs this everywhere. */
384
+ tenantFieldType: TenantFieldType;
56
385
  }
57
- //#endregion
58
- //#region src/services/mutation-options.d.ts
59
386
  /**
60
- * Options passed to every service mutation. All fields optional — hosts
61
- * opt into the behaviours they need.
387
+ * Tenant-column schema definition. Use this anywhere a model has
388
+ * `organizationId` — never write the raw type in a schema file.
62
389
  */
63
- interface MutationOptions {
64
- /**
65
- * When `false`, skip event emission for this call. Useful for bulk seeds
66
- * and when a higher-level orchestration verb composes several services
67
- * and only wants the outer event to fire (see `LeadService.convert`).
68
- */
69
- emitEvents?: boolean;
70
- /**
71
- * Host-provided DB transaction handle (mongoose `ClientSession`,
72
- * Prisma tx, Postgres client with `BEGIN`, etc.). Typed as `unknown`
73
- * to keep crm framework-agnostic.
74
- *
75
- * Forwarded to:
76
- * - The host's repository adapter (if it reads `options.session`) so
77
- * the business write joins the transaction.
78
- * - `outbox.save(event, { session })` so the outbox row commits
79
- * atomically with the business write — PACKAGE_RULES §5.5 + P8's
80
- * session-bound dispatch pattern.
81
- *
82
- * Wrap the CRM service call in your own transaction:
83
- *
84
- * ```ts
85
- * const session = await mongoose.connection.startSession();
86
- * try {
87
- * await session.withTransaction(async () => {
88
- * await accountService.create({ name: 'Acme' }, { session });
89
- * });
90
- * } finally {
91
- * await session.endSession();
92
- * }
93
- * ```
94
- */
95
- session?: unknown;
96
- }
390
+ declare function tenantFieldDef(config: SchemaBuildConfig): SchemaDefinitionProperty;
97
391
  //#endregion
98
- //#region src/services/account.service.d.ts
99
- interface AccountServiceConfig {
100
- repo: AccountRepository;
101
- events?: EventTransport | undefined;
102
- /**
103
- * Optional host-owned outbox. See `emitCrmEvent` for the dispatch contract:
104
- * outbox save (session-bound when `options.session` is passed) is issued
105
- * before the transport publish so durable at-least-once delivery works
106
- * even when the transport is down.
107
- */
108
- outbox?: OutboxStore | undefined;
109
- clock?: Clock;
110
- ids?: IdGenerator;
111
- }
112
- interface CreateAccountInput {
392
+ //#region src/models/pipeline.schema.d.ts
393
+ /** Mongoose document shape for a single embedded stage. */
394
+ interface IStageDocument {
395
+ _id: mongoose1.Types.ObjectId;
113
396
  name: string;
114
- domain?: string;
115
- industry?: string;
116
- sizeBucket?: string;
117
- annualRevenue?: {
118
- amount: number;
119
- currency: string;
120
- };
121
- ownerId?: string;
122
- tags?: readonly string[];
123
- metadata?: Record<string, unknown>;
124
- }
125
- declare class AccountService {
126
- private readonly repo;
127
- private readonly events;
128
- private readonly outbox;
129
- private readonly clock;
130
- private readonly ids;
131
- constructor(config: AccountServiceConfig);
132
- create(input: CreateAccountInput, options?: MutationOptions): Promise<Account>;
133
- update(id: string, patch: Partial<Account>, options?: MutationOptions): Promise<Account>;
134
- delete(id: string): Promise<void>;
135
- findById(id: string): Promise<Account | null>;
136
- findByDomain(domain: string): Promise<Account | null>;
137
- list(filter?: Parameters<AccountRepository['list']>[0]): Promise<Account[]>;
138
- private _require;
139
- private _emit;
140
- }
141
- //#endregion
142
- //#region src/domain/enums/activity.d.ts
143
- /**
144
- * Activity type vocabulary. The package stores `type` as free-form
145
- * string so hosts can extend; these constants cover the common
146
- * channels that automation tooling typically keys off.
147
- */
148
- declare const ACTIVITY_TYPES: readonly ["call", "email", "meeting", "task", "note", "sms", "social", "demo", "other"];
149
- type ActivityType = (typeof ACTIVITY_TYPES)[number];
150
- declare const ACTIVITY_STATUSES: readonly ["planned", "in_progress", "completed", "cancelled"];
151
- type ActivityStatus = (typeof ACTIVITY_STATUSES)[number];
152
- declare const ACTIVITY_TRANSITIONS: Record<ActivityStatus, readonly ActivityStatus[]>;
153
- declare function canTransitionActivity(from: ActivityStatus, to: ActivityStatus): boolean;
154
- /**
155
- * Kinds of entity an Activity can reference. Polymorphic linking lets
156
- * hosts log a call against a Lead, an Opportunity, a Contact, or a
157
- * stand-alone Account.
158
- */
159
- declare const SUBJECT_KINDS: readonly ["lead", "opportunity", "contact", "account"];
160
- type SubjectKind = (typeof SUBJECT_KINDS)[number];
161
- //#endregion
162
- //#region src/domain/entities/activity.d.ts
163
- /**
164
- * An activity is a polymorphic record of outreach / task / event
165
- * attached to any CRM entity. `subjectKind` + `subjectId` define the
166
- * link — this is how hosts build automation atop the package (subscribe
167
- * to `crm:activity.*` events, filter by `subjectKind`, react).
168
- */
169
- interface Activity {
170
- id: string;
171
- type: ActivityType | string;
172
- status: ActivityStatus;
173
- subjectKind: SubjectKind;
174
- subjectId: string;
175
- subject?: string;
176
- body?: string;
177
- /** When the activity is / was scheduled to happen. */
178
- scheduledAt?: Date;
179
- completedAt?: Date;
180
- cancelledAt?: Date;
181
- /** User-level ownership (call made by / task assigned to). */
182
- ownerId?: string;
183
- participantIds?: readonly string[];
184
- /**
185
- * Optional external ref — e.g. Calendar event id, email thread id,
186
- * call-recording url. Host-owned semantics.
187
- */
188
- externalRef?: string;
189
- metadata?: Record<string, unknown>;
190
- createdAt: Date;
191
- updatedAt: Date;
192
- }
193
- //#endregion
194
- //#region src/domain/entities/note.d.ts
195
- /**
196
- * A free-text note on a CRM entity. Separate from Activity because
197
- * notes aren't status-tracked — they're typed once and stay; they're
198
- * also the cheapest "activity log" entry to create.
199
- */
200
- interface Note {
201
- id: string;
202
- subjectKind: SubjectKind;
203
- subjectId: string;
204
- body: string;
205
- authorId?: string;
206
- /** Supports @-mentions, Markdown, etc. — host-interpreted. */
207
- format?: 'plain' | 'markdown';
208
- metadata?: Record<string, unknown>;
209
- createdAt: Date;
210
- updatedAt: Date;
211
- }
212
- //#endregion
213
- //#region src/domain/ports/activity-repository.port.d.ts
214
- interface ActivityFilter {
215
- subjectKind?: SubjectKind;
216
- subjectId?: string;
217
- status?: ActivityStatus | readonly ActivityStatus[];
218
- ownerId?: string;
219
- scheduledBetween?: {
220
- from: Date;
221
- to: Date;
222
- };
223
- }
224
- interface ActivityRepository {
225
- findById(id: string): Promise<Activity | null>;
226
- list(filter?: ActivityFilter): Promise<Activity[]>;
227
- create(a: Omit<Activity, 'createdAt' | 'updatedAt'>): Promise<Activity>;
228
- update(id: string, patch: Partial<Activity>): Promise<Activity>;
229
- casStatus(id: string, expected: ActivityStatus, next: ActivityStatus, patch?: Partial<Activity>): Promise<Activity | null>;
230
- }
231
- //#endregion
232
- //#region src/domain/ports/note-repository.port.d.ts
233
- interface NoteRepository {
234
- findById(id: string): Promise<Note | null>;
235
- listBySubject(subjectKind: SubjectKind, subjectId: string): Promise<Note[]>;
236
- create(n: Omit<Note, 'createdAt' | 'updatedAt'>): Promise<Note>;
237
- update(id: string, patch: Partial<Note>): Promise<Note>;
238
- delete(id: string): Promise<void>;
239
- }
240
- //#endregion
241
- //#region src/services/activity.service.d.ts
242
- interface ActivityServiceConfig {
243
- repo: ActivityRepository;
244
- notes: NoteRepository;
245
- events?: EventTransport | undefined;
246
- /** Optional host-owned outbox. See AccountServiceConfig.outbox. */
247
- outbox?: OutboxStore | undefined;
248
- clock?: Clock;
249
- ids?: IdGenerator;
250
- }
251
- interface CreateActivityInput {
252
- type: ActivityType | string;
253
- subjectKind: SubjectKind;
254
- subjectId: string;
255
- subject?: string;
256
- body?: string;
257
- scheduledAt?: Date;
258
- ownerId?: string;
259
- participantIds?: readonly string[];
260
- externalRef?: string;
261
- metadata?: Record<string, unknown>;
262
- }
263
- interface CreateNoteInput {
264
- subjectKind: SubjectKind;
265
- subjectId: string;
266
- body: string;
267
- authorId?: string;
268
- format?: 'plain' | 'markdown';
269
- metadata?: Record<string, unknown>;
270
- }
271
- /**
272
- * Polymorphic activity / note service. Activities have a lifecycle
273
- * (`planned → in_progress → completed | cancelled`); notes are
274
- * immutable-ish (create + update body, no status).
275
- */
276
- declare class ActivityService {
277
- private readonly repo;
278
- private readonly noteRepo;
279
- private readonly events;
280
- private readonly outbox;
281
- private readonly clock;
282
- private readonly ids;
283
- constructor(config: ActivityServiceConfig);
284
- plan(input: CreateActivityInput, options?: MutationOptions): Promise<Activity>;
285
- start(id: string, options?: MutationOptions): Promise<Activity>;
286
- complete(id: string, completedAt?: Date, options?: MutationOptions): Promise<Activity>;
287
- cancel(id: string, reason?: string, options?: MutationOptions): Promise<Activity>;
288
- addNote(input: CreateNoteInput, options?: MutationOptions): Promise<Note>;
289
- listBySubject(subjectKind: SubjectKind, subjectId: string): Promise<Activity[]>;
290
- listNotesBySubject(subjectKind: SubjectKind, subjectId: string): Promise<Note[]>;
291
- findById(id: string): Promise<Activity | null>;
292
- private _transition;
293
- private _emit;
397
+ /** Ascending order in the pipeline. Treated as identifier on `moveToStage`. */
398
+ sequence: number;
399
+ /** Default win-probability assigned to opportunities entering this stage. */
400
+ defaultProbability: number;
401
+ color?: string;
402
+ description?: string;
294
403
  }
295
- //#endregion
296
- //#region src/domain/entities/contact.d.ts
297
- /**
298
- * A person — the atom of a CRM. Contacts may belong to an Account,
299
- * but can also exist stand-alone (independent consultants, consumers).
300
- *
301
- * Structured `name` + `contact` come from `@classytic/primitives/person`.
302
- * Hosts projecting over a commerce Customer record embed the same shapes —
303
- * no translation needed at the boundary.
304
- */
305
- interface Contact {
306
- id: string;
307
- name: PersonName;
308
- contact: ContactInfo;
309
- jobTitle?: string;
310
- accountId?: string;
311
- ownerId?: string;
312
- /** Free-form tags for segmentation. */
313
- tags?: readonly string[];
314
- /** Host-owned arbitrary payload (social handles, custom fields, etc.). */
404
+ /** Mongoose document shape for a Pipeline. */
405
+ interface IPipelineDocument {
406
+ _id: mongoose1.Types.ObjectId;
407
+ organizationId: mongoose1.Types.ObjectId | string;
408
+ name: string;
409
+ /** Deprecates the pipeline — existing opps still move, no new ones created. */
410
+ isArchived: boolean;
411
+ /** Embedded stages, kept ordered by `sequence`. */
412
+ stages: IStageDocument[];
413
+ /** Free-form label for the sales team / motion that owns this pipeline. */
414
+ teamRef?: string;
315
415
  metadata?: Record<string, unknown>;
316
416
  createdAt: Date;
317
417
  updatedAt: Date;
318
418
  }
419
+ declare function buildPipelineSchema(config: SchemaBuildConfig): Schema<IPipelineDocument>;
420
+ declare function createPipelineModel(connection: Connection, config: SchemaBuildConfig): Model<IPipelineDocument>;
319
421
  //#endregion
320
- //#region src/domain/ports/contact-repository.port.d.ts
321
- interface ContactFilter {
322
- accountId?: string;
323
- ownerId?: string;
324
- email?: string;
325
- tags?: readonly string[];
326
- }
327
- interface ContactRepository {
328
- findById(id: string): Promise<Contact | null>;
329
- findByEmail(email: string): Promise<Contact | null>;
330
- list(filter?: ContactFilter): Promise<Contact[]>;
331
- create(c: Omit<Contact, 'createdAt' | 'updatedAt'>): Promise<Contact>;
332
- update(id: string, patch: Partial<Contact>): Promise<Contact>;
333
- delete(id: string): Promise<void>;
334
- }
335
- //#endregion
336
- //#region src/services/contact.service.d.ts
337
- interface ContactServiceConfig {
338
- repo: ContactRepository;
339
- events?: EventTransport | undefined;
340
- /** Optional host-owned outbox. See AccountServiceConfig.outbox. */
341
- outbox?: OutboxStore | undefined;
342
- clock?: Clock;
343
- ids?: IdGenerator;
344
- }
345
- interface CreateContactInput {
346
- name: PersonName;
347
- contact?: ContactInfo;
348
- jobTitle?: string;
349
- accountId?: string;
350
- ownerId?: string;
351
- tags?: readonly string[];
352
- metadata?: Record<string, unknown>;
353
- }
354
- declare class ContactService {
355
- private readonly repo;
356
- private readonly events;
357
- private readonly outbox;
358
- private readonly clock;
359
- private readonly ids;
360
- constructor(config: ContactServiceConfig);
361
- create(input: CreateContactInput, options?: MutationOptions): Promise<Contact>;
362
- update(id: string, patch: Partial<Contact>, options?: MutationOptions): Promise<Contact>;
363
- delete(id: string): Promise<void>;
364
- findById(id: string): Promise<Contact | null>;
365
- findByEmail(email: string): Promise<Contact | null>;
366
- list(filter?: Parameters<ContactRepository['list']>[0]): Promise<Contact[]>;
367
- private _require;
368
- private _emit;
369
- }
370
- //#endregion
371
- //#region src/domain/enums/lead-status.d.ts
422
+ //#region src/enums/lead-status.d.ts
372
423
  /**
373
424
  * Lead lifecycle — from initial inquiry through conversion.
374
425
  *
@@ -384,56 +435,43 @@ type LeadStatus = (typeof LEAD_STATUSES)[number];
384
435
  declare const LEAD_TRANSITIONS: Record<LeadStatus, readonly LeadStatus[]>;
385
436
  declare function canTransitionLead(from: LeadStatus, to: LeadStatus): boolean;
386
437
  //#endregion
387
- //#region src/domain/entities/lead.d.ts
388
- /**
389
- * An unqualified inquiry. Leads are the top of the funnel — they
390
- * don't yet have a dedicated Contact + Account + Opportunity in the
391
- * system. `LeadService.convert(...)` creates all three and
392
- * transitions the lead to `converted`.
393
- */
394
- interface Lead {
395
- id: string;
396
- /** Name / email / phone / company captured at inquiry time. */
438
+ //#region src/models/lead.schema.d.ts
439
+ interface ILeadStatusEntry {
440
+ status: LeadStatus;
441
+ occurredAt: Date;
442
+ by?: string;
443
+ note?: string;
444
+ }
445
+ interface ILeadDocument {
446
+ _id: mongoose1.Types.ObjectId;
447
+ organizationId: mongoose1.Types.ObjectId | string;
448
+ fullName: string;
397
449
  firstName?: string;
398
450
  lastName?: string;
399
- fullName: string;
400
451
  email?: string;
401
452
  phone?: string;
402
453
  companyName?: string;
403
454
  jobTitle?: string;
404
- /** How the lead arrived (`web-form`, `referral`, `event`, `ads`, …). */
405
455
  source?: string;
406
- /** Free-form campaign identifier — the host's campaign system owns its semantics. */
407
456
  campaignRef?: string;
408
- /**
409
- * Lead score — updated by `LeadService.recomputeScore(...)` whenever
410
- * the score function decides. Stored on the record for indexing +
411
- * routing. Raw score units; range is host-defined.
412
- */
413
- score?: number;
457
+ score: number;
414
458
  status: LeadStatus;
415
- statusHistory: readonly LeadStatusEntry[];
459
+ statusHistory: ILeadStatusEntry[];
416
460
  ownerId?: string;
417
- tags?: readonly string[];
418
- /** On successful `convert`, links to the resulting records. */
419
- convertedContactId?: string;
420
- convertedAccountId?: string;
421
- convertedOpportunityId?: string;
461
+ tags: string[];
462
+ convertedContactId?: mongoose1.Types.ObjectId | string;
463
+ convertedAccountId?: mongoose1.Types.ObjectId | string;
464
+ convertedOpportunityId?: mongoose1.Types.ObjectId | string;
422
465
  convertedAt?: Date;
423
- /** Disqualification detail. */
424
466
  disqualifyReason?: string;
425
467
  metadata?: Record<string, unknown>;
426
468
  createdAt: Date;
427
469
  updatedAt: Date;
428
470
  }
429
- interface LeadStatusEntry {
430
- status: LeadStatus;
431
- occurredAt: Date;
432
- by?: string;
433
- note?: string;
434
- }
471
+ declare function buildLeadSchema(config: SchemaBuildConfig): Schema<ILeadDocument>;
472
+ declare function createLeadModel(connection: Connection, config: SchemaBuildConfig): Model<ILeadDocument>;
435
473
  //#endregion
436
- //#region src/domain/enums/opportunity-status.d.ts
474
+ //#region src/enums/opportunity-status.d.ts
437
475
  /**
438
476
  * Top-level opportunity status. Movement through pipeline STAGES
439
477
  * happens while status is `open`; `won` / `lost` are terminal
@@ -446,975 +484,1004 @@ type OpportunityStatus = (typeof OPPORTUNITY_STATUSES)[number];
446
484
  declare const OPPORTUNITY_TRANSITIONS: Record<OpportunityStatus, readonly OpportunityStatus[]>;
447
485
  declare function canTransitionOpportunity(from: OpportunityStatus, to: OpportunityStatus): boolean;
448
486
  //#endregion
449
- //#region src/domain/entities/opportunity.d.ts
487
+ //#region src/models/opportunity.schema.d.ts
488
+ interface IOpportunityStatusEntry {
489
+ status: OpportunityStatus;
490
+ stageId?: string;
491
+ occurredAt: Date;
492
+ by?: string;
493
+ note?: string;
494
+ }
495
+ interface IOpportunityDocument {
496
+ _id: mongoose1.Types.ObjectId;
497
+ organizationId: mongoose1.Types.ObjectId | string;
498
+ name: string;
499
+ accountId?: mongoose1.Types.ObjectId | string;
500
+ primaryContactId?: mongoose1.Types.ObjectId | string;
501
+ pipelineId: mongoose1.Types.ObjectId | string;
502
+ stageId: string;
503
+ status: OpportunityStatus;
504
+ statusHistory: IOpportunityStatusEntry[];
505
+ amount?: number;
506
+ currency: string;
507
+ probability: number;
508
+ expectedCloseAt?: Date;
509
+ closedAt?: Date;
510
+ lostReasonId?: mongoose1.Types.ObjectId | string;
511
+ ownerId?: string;
512
+ sourceLeadId?: mongoose1.Types.ObjectId | string;
513
+ tags: string[];
514
+ metadata?: Record<string, unknown>;
515
+ createdAt: Date;
516
+ updatedAt: Date;
517
+ }
518
+ declare function buildOpportunitySchema(config: SchemaBuildConfig): Schema<IOpportunityDocument>;
519
+ declare function createOpportunityModel(connection: Connection, config: SchemaBuildConfig): Model<IOpportunityDocument>;
520
+ //#endregion
521
+ //#region src/models/contact.schema.d.ts
522
+ interface IContactName {
523
+ given: string;
524
+ family: string;
525
+ middle?: string;
526
+ prefix?: string;
527
+ suffix?: string;
528
+ preferred?: string;
529
+ }
530
+ interface IContactInfoEmbedded {
531
+ email?: string;
532
+ personalEmail?: string;
533
+ phone?: string;
534
+ alternatePhone?: string;
535
+ whatsapp?: string;
536
+ telegram?: string;
537
+ linkedin?: string;
538
+ instagram?: string;
539
+ }
540
+ interface IContactDocument {
541
+ _id: mongoose1.Types.ObjectId;
542
+ organizationId: mongoose1.Types.ObjectId | string;
543
+ name: IContactName;
544
+ contact: IContactInfoEmbedded;
545
+ jobTitle?: string;
546
+ accountId?: mongoose1.Types.ObjectId | string;
547
+ ownerId?: string;
548
+ tags: string[];
549
+ metadata?: Record<string, unknown>;
550
+ createdAt: Date;
551
+ updatedAt: Date;
552
+ }
553
+ declare function buildContactSchema(config: SchemaBuildConfig): Schema<IContactDocument>;
554
+ declare function createContactModel(connection: Connection, config: SchemaBuildConfig): Model<IContactDocument>;
555
+ //#endregion
556
+ //#region src/models/account.schema.d.ts
557
+ interface IAccountDocument {
558
+ _id: mongoose1.Types.ObjectId;
559
+ organizationId: mongoose1.Types.ObjectId | string;
560
+ name: string;
561
+ domain?: string;
562
+ industry?: string;
563
+ sizeBucket?: string;
564
+ annualRevenueAmount?: number;
565
+ annualRevenueCurrency: string;
566
+ ownerId?: string;
567
+ tags: string[];
568
+ metadata?: Record<string, unknown>;
569
+ createdAt: Date;
570
+ updatedAt: Date;
571
+ }
572
+ declare function buildAccountSchema(config: SchemaBuildConfig): Schema<IAccountDocument>;
573
+ declare function createAccountModel(connection: Connection, config: SchemaBuildConfig): Model<IAccountDocument>;
574
+ //#endregion
575
+ //#region src/enums/campaign-status.d.ts
450
576
  /**
451
- * A qualified sales opportunity. Opportunities live inside a Pipeline
452
- * and progress through Stages. Status captures the outcome
453
- * (`open` / `won` / `lost` / `abandoned`); stage captures the current
454
- * step.
577
+ * Campaign lifecycle.
578
+ *
579
+ * draft — being built; not running, no sends, no UTM attribution counted
580
+ * active — running; participating in attribution + automation
581
+ * paused — temporarily halted; can be resumed; no sends fire
582
+ * completed — finished its run; preserved for historical attribution
583
+ * archived — soft-hidden from the default UI; still queryable
455
584
  */
456
- interface Opportunity {
457
- id: string;
585
+ declare const CAMPAIGN_STATUSES: readonly ["draft", "active", "paused", "completed", "archived"];
586
+ type CampaignStatus = (typeof CAMPAIGN_STATUSES)[number];
587
+ declare const CAMPAIGN_TRANSITIONS: Record<CampaignStatus, readonly CampaignStatus[]>;
588
+ declare function canTransitionCampaign(from: CampaignStatus, to: CampaignStatus): boolean;
589
+ //#endregion
590
+ //#region src/models/campaign.schema.d.ts
591
+ interface ICampaignStatusEntry {
592
+ status: CampaignStatus;
593
+ occurredAt: Date;
594
+ by?: string;
595
+ note?: string;
596
+ }
597
+ interface ICampaignDocument {
598
+ _id: mongoose1.Types.ObjectId;
599
+ organizationId: mongoose1.Types.ObjectId | string;
458
600
  name: string;
459
- accountId?: string;
460
- primaryContactId?: string;
461
- /** Which pipeline this opportunity belongs to (sales, partnerships, renewals). */
462
- pipelineId: string;
463
- /** Current stage — index INTO the pipeline's `stages` array. */
464
- stageId: string;
465
- status: OpportunityStatus;
466
- statusHistory: readonly OpportunityStatusEntry[];
467
- /** Host-expected deal value. */
468
- amount?: Money;
469
- /**
470
- * Win probability (0-1). Defaults to the stage's probability at
471
- * every stage move; host can override for hand-tuned forecasts.
472
- */
473
- probability: number;
474
- /** When the deal is expected to close. Drives forecast grouping. */
475
- expectedCloseAt?: Date;
476
- /** Populated on win/lose. */
477
- closedAt?: Date;
478
- lostReasonId?: string;
601
+ description?: string;
602
+ status: CampaignStatus;
603
+ statusHistory: ICampaignStatusEntry[];
604
+ utmSource?: string;
605
+ utmMedium?: string;
606
+ utmCampaign?: string;
607
+ startDate?: Date;
608
+ endDate?: Date;
609
+ budgetAmount?: number;
610
+ expectedRevenueAmount?: number;
611
+ actualRevenueAmount?: number;
612
+ currency: string;
479
613
  ownerId?: string;
480
- sourceLeadId?: string;
481
- tags?: readonly string[];
614
+ tags: string[];
482
615
  metadata?: Record<string, unknown>;
483
616
  createdAt: Date;
484
617
  updatedAt: Date;
485
618
  }
486
- interface OpportunityStatusEntry {
487
- status: OpportunityStatus;
488
- stageId?: string;
619
+ declare function buildCampaignSchema(config: SchemaBuildConfig): Schema<ICampaignDocument>;
620
+ declare function createCampaignModel(connection: Connection, config: SchemaBuildConfig): Model<ICampaignDocument>;
621
+ //#endregion
622
+ //#region src/enums/subscription-status.d.ts
623
+ /**
624
+ * Subscription lifecycle.
625
+ *
626
+ * pending — double-opt-in pending confirmation
627
+ * confirmed — actively subscribed; eligible to receive sends
628
+ * unsubscribed — user opted out via unsubscribe link (or operator action)
629
+ * bounced — provider reported hard bounce; permanent suppression
630
+ * complained — recipient hit "spam" / "report" in their email client;
631
+ * permanent suppression (CAN-SPAM + provider trust)
632
+ *
633
+ * All terminal states are non-reopenable. Re-subscribing creates a new
634
+ * Subscription row — the audit trail of unsubscribes/bounces is preserved.
635
+ */
636
+ declare const SUBSCRIPTION_STATUSES: readonly ["pending", "confirmed", "unsubscribed", "bounced", "complained"];
637
+ type SubscriptionStatus = (typeof SUBSCRIPTION_STATUSES)[number];
638
+ declare const SUBSCRIPTION_TRANSITIONS: Record<SubscriptionStatus, readonly SubscriptionStatus[]>;
639
+ declare function canTransitionSubscription(from: SubscriptionStatus, to: SubscriptionStatus): boolean;
640
+ //#endregion
641
+ //#region src/models/subscription.schema.d.ts
642
+ interface ISubscriptionStatusEntry {
643
+ status: SubscriptionStatus;
489
644
  occurredAt: Date;
490
645
  by?: string;
491
646
  note?: string;
492
647
  }
493
- //#endregion
494
- //#region src/domain/ports/lead-repository.port.d.ts
495
- interface LeadFilter {
496
- status?: LeadStatus | readonly LeadStatus[];
497
- ownerId?: string;
648
+ interface ISubscriptionDocument {
649
+ _id: mongoose1.Types.ObjectId;
650
+ organizationId: mongoose1.Types.ObjectId | string;
651
+ contactId: mongoose1.Types.ObjectId | string;
652
+ campaignId: mongoose1.Types.ObjectId | string;
653
+ status: SubscriptionStatus;
654
+ statusHistory: ISubscriptionStatusEntry[];
498
655
  source?: string;
499
- minScore?: number;
500
- }
501
- interface LeadRepository {
502
- findById(id: string): Promise<Lead | null>;
503
- findByEmail(email: string): Promise<Lead | null>;
504
- list(filter?: LeadFilter): Promise<Lead[]>;
505
- create(l: Omit<Lead, 'createdAt' | 'updatedAt'>): Promise<Lead>;
506
- update(id: string, patch: Partial<Lead>): Promise<Lead>;
507
- casStatus(id: string, expected: LeadStatus, next: LeadStatus, patch?: Partial<Lead>): Promise<Lead | null>;
656
+ consentTimestamp: Date;
657
+ consentIpAddress?: string;
658
+ consentUserAgent?: string;
659
+ confirmationToken?: string;
660
+ unsubscribeToken: string;
661
+ /** Audit timestamps for compliance — set on the matching transition. */
662
+ confirmedAt?: Date;
663
+ unsubscribedAt?: Date;
664
+ unsubscribeReason?: string;
665
+ bouncedAt?: Date;
666
+ bounceReason?: string;
667
+ complainedAt?: Date;
668
+ metadata?: Record<string, unknown>;
669
+ createdAt: Date;
670
+ updatedAt: Date;
508
671
  }
672
+ declare function buildSubscriptionSchema(config: SchemaBuildConfig): Schema<ISubscriptionDocument>;
673
+ declare function createSubscriptionModel(connection: Connection, config: SchemaBuildConfig): Model<ISubscriptionDocument>;
509
674
  //#endregion
510
- //#region src/domain/ports/opportunity-repository.port.d.ts
511
- interface OpportunityFilter {
512
- status?: OpportunityStatus | readonly OpportunityStatus[];
513
- pipelineId?: string;
514
- stageId?: string;
675
+ //#region src/enums/activity.d.ts
676
+ /**
677
+ * Activity type vocabulary. The package stores `type` as free-form
678
+ * string so hosts can extend; these constants cover the common
679
+ * channels that automation tooling typically keys off.
680
+ */
681
+ declare const ACTIVITY_TYPES: readonly ["call", "email", "meeting", "task", "note", "sms", "social", "demo", "other"];
682
+ type ActivityType = (typeof ACTIVITY_TYPES)[number];
683
+ declare const ACTIVITY_STATUSES: readonly ["planned", "in_progress", "completed", "cancelled"];
684
+ type ActivityStatus = (typeof ACTIVITY_STATUSES)[number];
685
+ declare const ACTIVITY_TRANSITIONS: Record<ActivityStatus, readonly ActivityStatus[]>;
686
+ declare function canTransitionActivity(from: ActivityStatus, to: ActivityStatus): boolean;
687
+ /**
688
+ * Kinds of entity an Activity can reference. Polymorphic linking lets
689
+ * hosts log a call against a Lead, an Opportunity, a Contact, or a
690
+ * stand-alone Account.
691
+ */
692
+ declare const SUBJECT_KINDS: readonly ["lead", "opportunity", "contact", "account"];
693
+ type SubjectKind = (typeof SUBJECT_KINDS)[number];
694
+ //#endregion
695
+ //#region src/models/activity.schema.d.ts
696
+ interface IActivityDocument {
697
+ _id: mongoose1.Types.ObjectId;
698
+ organizationId: mongoose1.Types.ObjectId | string;
699
+ type: ActivityType;
700
+ status: ActivityStatus;
701
+ subjectKind: SubjectKind;
702
+ subjectId: mongoose1.Types.ObjectId | string;
703
+ title: string;
704
+ description?: string;
515
705
  ownerId?: string;
516
- accountId?: string;
517
- closingBetween?: {
518
- from: Date;
519
- to: Date;
520
- };
521
- }
522
- interface OpportunityRepository {
523
- findById(id: string): Promise<Opportunity | null>;
524
- list(filter?: OpportunityFilter): Promise<Opportunity[]>;
525
- create(o: Omit<Opportunity, 'createdAt' | 'updatedAt'>): Promise<Opportunity>;
526
- update(id: string, patch: Partial<Opportunity>): Promise<Opportunity>;
527
- delete(id: string): Promise<void>;
528
- casStatus(id: string, expected: OpportunityStatus, next: OpportunityStatus, patch?: Partial<Opportunity>): Promise<Opportunity | null>;
529
- casStage(id: string, expectedStageId: string, nextStageId: string, patch?: Partial<Opportunity>): Promise<Opportunity | null>;
706
+ participantIds: string[];
707
+ dueDate?: Date;
708
+ completedAt?: Date;
709
+ externalRef?: string;
710
+ metadata?: Record<string, unknown>;
711
+ createdAt: Date;
712
+ updatedAt: Date;
530
713
  }
714
+ declare function buildActivitySchema(config: SchemaBuildConfig): Schema<IActivityDocument>;
715
+ declare function createActivityModel(connection: Connection, config: SchemaBuildConfig): Model<IActivityDocument>;
531
716
  //#endregion
532
- //#region src/services/opportunity.service.d.ts
533
- interface OpportunityPipelinePort {
534
- findById(id: string): Promise<{
535
- id: string;
536
- stages: readonly {
537
- id: string;
538
- }[];
539
- } | null>;
540
- getStage(pipelineId: string, stageId: string): Promise<{
541
- id: string;
542
- sequence: number;
543
- defaultProbability: number;
544
- }>;
545
- getFirstStage(pipelineId: string): Promise<{
546
- id: string;
547
- sequence: number;
548
- defaultProbability: number;
549
- }>;
550
- }
551
- interface OpportunityServiceConfig {
552
- repo: OpportunityRepository;
553
- pipelines: OpportunityPipelinePort;
554
- events?: EventTransport | undefined;
555
- /** Optional host-owned outbox. See AccountServiceConfig.outbox. */
556
- outbox?: OutboxStore | undefined;
557
- clock?: Clock;
558
- ids?: IdGenerator;
717
+ //#region src/models/note.schema.d.ts
718
+ interface INoteDocument {
719
+ _id: mongoose1.Types.ObjectId;
720
+ organizationId: mongoose1.Types.ObjectId | string;
721
+ subjectKind: SubjectKind;
722
+ subjectId: mongoose1.Types.ObjectId | string;
723
+ body: string;
724
+ authorId?: string;
725
+ pinned: boolean;
726
+ metadata?: Record<string, unknown>;
727
+ createdAt: Date;
728
+ updatedAt: Date;
559
729
  }
560
- interface CreateOpportunityInput {
730
+ declare function buildNoteSchema(config: SchemaBuildConfig): Schema<INoteDocument>;
731
+ declare function createNoteModel(connection: Connection, config: SchemaBuildConfig): Model<INoteDocument>;
732
+ //#endregion
733
+ //#region src/models/loss-reason.schema.d.ts
734
+ interface ILossReasonDocument {
735
+ _id: mongoose1.Types.ObjectId;
736
+ organizationId: mongoose1.Types.ObjectId | string;
561
737
  name: string;
562
- pipelineId: string;
563
- /** Starting stage — defaults to the pipeline's first stage. */
564
- stageId?: string;
565
- accountId?: string;
566
- primaryContactId?: string;
567
- sourceLeadId?: string;
568
- amount?: Money;
569
- /** Override the stage default. Defaults to the stage's `defaultProbability`. */
570
- probability?: number;
571
- expectedCloseAt?: Date;
572
- ownerId?: string;
573
- tags?: readonly string[];
738
+ description?: string;
739
+ isActive: boolean;
740
+ /** Display ordering in admin UIs. */
741
+ sequence: number;
574
742
  metadata?: Record<string, unknown>;
743
+ createdAt: Date;
744
+ updatedAt: Date;
575
745
  }
576
- interface MoveStageInput {
577
- stageId: string;
578
- /** Override probability. Defaults to the target stage's `defaultProbability`. */
579
- probability?: number;
580
- by?: string;
581
- note?: string;
582
- }
583
- interface CloseInput {
584
- amount?: Money;
585
- closedAt?: Date;
586
- by?: string;
587
- note?: string;
588
- }
589
- interface LoseInput extends CloseInput {
590
- lostReasonId?: string;
591
- }
746
+ declare function buildLossReasonSchema(config: SchemaBuildConfig): Schema<ILossReasonDocument>;
747
+ declare function createLossReasonModel(connection: Connection, config: SchemaBuildConfig): Model<ILossReasonDocument>;
748
+ //#endregion
749
+ //#region src/enums/email-event-kind.d.ts
592
750
  /**
593
- * Opportunity lifecycle — stage movement + outcome resolution. Stage
594
- * changes call the pipeline service to validate membership + default
595
- * probability; status transitions (`won` / `lost` / `abandoned`) are
596
- * terminal.
751
+ * Email engagement event kinds, recorded against a Contact / Campaign /
752
+ * Subscription tuple as the recipient interacts with a sent message.
753
+ *
754
+ * open — recipient's mail client loaded the tracking pixel.
755
+ * Imperfect signal: image-block clients suppress it, some
756
+ * clients pre-fetch and inflate counts. Useful in aggregate.
757
+ *
758
+ * click — recipient followed a tracked link. High-signal: actual
759
+ * user action, not background fetch.
760
+ *
761
+ * bounce — provider reported delivery failed. Pair with the
762
+ * `bounce` status on Subscription for permanent suppression.
763
+ *
764
+ * complaint — recipient hit "spam" / "report" in their inbox. Pair
765
+ * with `complained` Subscription status.
766
+ *
767
+ * unsubscribe — recipient hit the unsubscribe link in the email. May
768
+ * fire BEFORE the Subscription itself is marked unsubscribed
769
+ * (the host can choose to confirm before flipping status).
770
+ *
771
+ * Open and click are "engagement signal"; bounce/complaint/unsubscribe are
772
+ * "suppression signal". The service exposes aggregates over both
773
+ * categories so consumers can compute open rate, click rate, bounce rate
774
+ * without filtering kind themselves.
597
775
  */
598
- declare class OpportunityService {
599
- private readonly repo;
600
- private readonly pipelines;
601
- private readonly events;
602
- private readonly outbox;
603
- private readonly clock;
604
- private readonly ids;
605
- constructor(config: OpportunityServiceConfig);
606
- create(input: CreateOpportunityInput, options?: MutationOptions): Promise<Opportunity>;
607
- moveToStage(id: string, input: MoveStageInput, options?: MutationOptions): Promise<Opportunity>;
608
- win(id: string, input?: CloseInput, options?: MutationOptions): Promise<Opportunity>;
609
- lose(id: string, input?: LoseInput, options?: MutationOptions): Promise<Opportunity>;
610
- abandon(id: string, by?: string, note?: string, options?: MutationOptions): Promise<Opportunity>;
611
- delete(id: string): Promise<void>;
612
- findById(id: string): Promise<Opportunity | null>;
613
- list(filter?: Parameters<OpportunityRepository['list']>[0]): Promise<Opportunity[]>;
614
- private _require;
615
- private _transitionStatus;
616
- private _emit;
776
+ declare const EMAIL_EVENT_KINDS: readonly ["open", "click", "bounce", "complaint", "unsubscribe"];
777
+ type EmailEventKind = (typeof EMAIL_EVENT_KINDS)[number];
778
+ /** Engagement (positive signal). */
779
+ //#endregion
780
+ //#region src/models/email-event.schema.d.ts
781
+ declare const EMAIL_EVENT_SUBJECT_KINDS: readonly ["contact", "subscription"];
782
+ type EmailEventSubjectKind = (typeof EMAIL_EVENT_SUBJECT_KINDS)[number];
783
+ interface IEmailEventDocument {
784
+ _id: mongoose1.Types.ObjectId;
785
+ organizationId: mongoose1.Types.ObjectId | string;
786
+ kind: EmailEventKind;
787
+ subjectKind: EmailEventSubjectKind;
788
+ subjectId: mongoose1.Types.ObjectId | string;
789
+ campaignId?: mongoose1.Types.ObjectId | string;
790
+ messageId?: string;
791
+ url?: string;
792
+ userAgent?: string;
793
+ ip?: string;
794
+ metadata?: Record<string, unknown>;
795
+ createdAt: Date;
617
796
  }
797
+ declare function buildEmailEventSchema(config: SchemaBuildConfig): Schema<IEmailEventDocument>;
798
+ declare function createEmailEventModel(connection: Connection, config: SchemaBuildConfig): Model<IEmailEventDocument>;
618
799
  //#endregion
619
- //#region src/services/scoring.d.ts
800
+ //#region src/enums/conversation.d.ts
620
801
  /**
621
- * Pluggable lead scorer. Hosts inject whatever rubric they need —
622
- * firmographic model, in-app signals, ML prediction, external enrichment.
802
+ * Conversation / Message vocabulary.
623
803
  *
624
- * Stay pure: the scorer must not mutate anything, not hit I/O, and
625
- * be deterministic given its input. Side-effects belong in the
626
- * caller — `LeadService.recomputeScore` runs the scorer then persists.
804
+ * Conversations unify a contact's (or pre-conversion lead's) message
805
+ * thread across channels (WhatsApp / Telegram / Email / IG-DM / SMS).
806
+ * The host's webhook adapters write to this surface — the CRM does not
807
+ * dial outward.
627
808
  */
628
- type LeadScorer = (lead: Lead) => number;
809
+ declare const CONVERSATION_CHANNELS: readonly ["whatsapp", "telegram", "email", "instagram_dm", "sms", "web_chat"];
810
+ type ConversationChannel = (typeof CONVERSATION_CHANNELS)[number];
811
+ declare const CONVERSATION_STATUSES: readonly ["open", "snoozed", "resolved"];
812
+ type ConversationStatus = (typeof CONVERSATION_STATUSES)[number];
813
+ /** Conversations attach to either a Contact or a (yet-unconverted) Lead. */
814
+ declare const CONVERSATION_PARTICIPANT_KINDS: readonly ["contact", "lead"];
815
+ type ConversationParticipantKind = (typeof CONVERSATION_PARTICIPANT_KINDS)[number];
816
+ declare const MESSAGE_DIRECTIONS: readonly ["inbound", "outbound"];
817
+ type MessageDirection = (typeof MESSAGE_DIRECTIONS)[number];
818
+ declare const MESSAGE_DELIVERY_STATUSES: readonly ["queued", "sent", "delivered", "read", "failed"];
819
+ type MessageDeliveryStatus = (typeof MESSAGE_DELIVERY_STATUSES)[number];
820
+ //#endregion
821
+ //#region src/models/conversation.schema.d.ts
822
+ interface IConversationDocument {
823
+ _id: mongoose1.Types.ObjectId;
824
+ organizationId: mongoose1.Types.ObjectId | string;
825
+ channel: ConversationChannel;
826
+ participantKind: ConversationParticipantKind;
827
+ participantId: mongoose1.Types.ObjectId | string;
828
+ /** Channel-side identifier (e.g. WhatsApp wa_id, Telegram chat id). */
829
+ channelUserId?: string;
830
+ status: ConversationStatus;
831
+ /** Last message timestamp — surfaced for inbox sort + idle SLAs. */
832
+ lastMessageAt?: Date;
833
+ /** Unread inbound count. Reset to 0 on `markRead`. */
834
+ unreadCount: number;
835
+ /** Assignee. Stored as a string so hosts can use either userId or a queue id. */
836
+ assigneeId?: string;
837
+ /** Free-form bag — channel-specific settings (preview text, IMAP folder). */
838
+ metadata?: Record<string, unknown>;
839
+ createdAt: Date;
840
+ updatedAt: Date;
841
+ }
842
+ declare function buildConversationSchema(config: SchemaBuildConfig): Schema<IConversationDocument>;
843
+ declare function createConversationModel(connection: Connection, config: SchemaBuildConfig): Model<IConversationDocument>;
844
+ //#endregion
845
+ //#region src/models/message.schema.d.ts
846
+ interface IMessageAttachment {
847
+ type: string;
848
+ url: string;
849
+ name?: string;
850
+ size?: number;
851
+ mimeType?: string;
852
+ }
853
+ interface IMessageDocument {
854
+ _id: mongoose1.Types.ObjectId;
855
+ organizationId: mongoose1.Types.ObjectId | string;
856
+ conversationId: mongoose1.Types.ObjectId | string;
857
+ direction: MessageDirection;
858
+ body: string;
859
+ attachments: IMessageAttachment[];
860
+ /** Provider-side id — webhook dedup key. */
861
+ externalMessageId?: string;
862
+ status: MessageDeliveryStatus;
863
+ /** Channel id of the sender (phone number, email, telegram id). */
864
+ senderRef?: string;
865
+ /** Channel id of the recipient. */
866
+ recipientRef?: string;
867
+ /** When the wire said it left / arrived; falls back to createdAt. */
868
+ sentAt?: Date;
869
+ metadata?: Record<string, unknown>;
870
+ createdAt: Date;
871
+ updatedAt: Date;
872
+ }
873
+ declare function buildMessageSchema(config: SchemaBuildConfig): Schema<IMessageDocument>;
874
+ declare function createMessageModel(connection: Connection, config: SchemaBuildConfig): Model<IMessageDocument>;
875
+ //#endregion
876
+ //#region src/models/create-models.d.ts
877
+ interface CrmModels {
878
+ Pipeline: Model<IPipelineDocument>;
879
+ Lead: Model<ILeadDocument>;
880
+ Opportunity: Model<IOpportunityDocument>;
881
+ Contact: Model<IContactDocument>;
882
+ Account: Model<IAccountDocument>;
883
+ Campaign: Model<ICampaignDocument>;
884
+ Subscription: Model<ISubscriptionDocument>;
885
+ Activity: Model<IActivityDocument>;
886
+ Note: Model<INoteDocument>;
887
+ LossReason: Model<ILossReasonDocument>;
888
+ EmailEvent: Model<IEmailEventDocument>;
889
+ Conversation: Model<IConversationDocument>;
890
+ Message: Model<IMessageDocument>;
891
+ }
892
+ /** Mongoose model names this package registers — drives the §21 collision check. */
893
+ declare const CRM_MODEL_NAMES: readonly ["Pipeline", "Lead", "Opportunity", "Contact", "Account", "Campaign", "EmailSubscription", "Activity", "Note", "LossReason", "EmailEvent", "Conversation", "Message"];
894
+ interface CreateModelsOptions extends SchemaBuildConfig {
895
+ /**
896
+ * On a model-name collision: `false` (default) throws
897
+ * `CrmModelCollisionError`; `true` deletes + re-registers (test
898
+ * fixtures / hot reload only).
899
+ */
900
+ forceRecreate?: boolean;
901
+ /** Mongoose auto-index builds. Default `true` (dev); `false` in prod. */
902
+ autoIndex?: boolean;
903
+ }
904
+ declare function createCrmModels(connection: Connection, config: CreateModelsOptions): CrmModels;
905
+ //#endregion
906
+ //#region src/repositories/pipeline.repository.d.ts
907
+ interface PipelineRepositoryDeps {
908
+ events: EventTransport;
909
+ }
910
+ declare class PipelineRepository extends Repository<IPipelineDocument> {
911
+ private readonly events;
912
+ constructor(model: Model<IPipelineDocument>, plugins: PluginType[], deps: PipelineRepositoryDeps, pagination?: PaginationConfig);
913
+ /**
914
+ * Read a single embedded stage. Used by Opportunity create / move to
915
+ * validate stage membership + pick up `defaultProbability`.
916
+ */
917
+ getStage(pipelineId: string, stageId: string, ctx?: CrmContext): Promise<IStageDocument>;
918
+ /** First stage by `sequence` ascending. */
919
+ getFirstStage(pipelineId: string, ctx?: CrmContext): Promise<IStageDocument>;
920
+ /** Stage immediately after the given one. `null` at terminal stage. */
921
+ getNextStage(pipelineId: string, stageId: string, ctx?: CrmContext): Promise<IStageDocument | null>;
922
+ /** Soft-deprecate — existing opps still move; no new ones can be created. */
923
+ archive(id: string, ctx?: CrmContext): Promise<IPipelineDocument>;
924
+ }
925
+ //#endregion
926
+ //#region src/shared/calculators/scoring.d.ts
927
+ /** Minimal Lead shape the scorer reads. */
928
+ type LeadInput = Pick<ILeadDocument, 'email' | 'phone' | 'companyName' | 'tags' | 'jobTitle'>;
929
+ type LeadScorer = (lead: LeadInput) => number;
629
930
  /**
630
- * Reference implementation. Intentionally simple so hosts read it and
631
- * understand the shape, then replace it.
931
+ * Reference implementation. Intentionally simple — hosts read it and
932
+ * replace it.
632
933
  *
633
934
  * +20 if email is present
634
935
  * +20 if phone is present
635
936
  * +20 if companyName is present
636
937
  * +10 per populated tag up to 40
637
- * +10 if jobTitle looks director/manager/head (case-insensitive)
638
- */
639
- declare function defaultLeadScorer(lead: Lead): number;
640
- /**
641
- * Compose a scoring pipeline — each rule returns an increment
642
- * (positive or negative). Useful when host logic is a bunch of
643
- * small, testable rules.
644
- *
645
- * @example
646
- * ```ts
647
- * const scorer = composeScorers(
648
- * (l) => (l.email ? 20 : 0),
649
- * (l) => (l.source === 'referral' ? 30 : 0),
650
- * (l) => (l.tags?.includes('enterprise') ? 50 : 0),
651
- * );
652
- * ```
938
+ * +10 if jobTitle hints senior (director / manager / head / chief / vp / founder)
653
939
  */
940
+ declare function defaultLeadScorer(lead: LeadInput): number;
941
+ /** Compose multiple small rules into one scorer. */
654
942
  declare function composeScorers(...rules: readonly LeadScorer[]): LeadScorer;
655
943
  //#endregion
656
- //#region src/services/lead.service.d.ts
657
- interface LeadAccountWriter {
658
- create(input: CreateAccountInput, options?: MutationOptions): Promise<Account>;
659
- delete?(id: string): Promise<void>;
660
- }
661
- interface LeadContactWriter {
662
- create(input: CreateContactInput, options?: MutationOptions): Promise<Contact>;
663
- delete?(id: string): Promise<void>;
664
- }
665
- interface LeadOpportunityWriter {
666
- create(input: CreateOpportunityInput, options?: MutationOptions): Promise<Opportunity>;
667
- delete?(id: string): Promise<void>;
668
- }
669
- interface LeadServiceConfig {
670
- repo: LeadRepository;
671
- contacts: LeadContactWriter;
672
- accounts: LeadAccountWriter;
673
- opportunities: LeadOpportunityWriter;
674
- events?: EventTransport | undefined;
675
- /** Optional host-owned outbox. See AccountServiceConfig.outbox. */
676
- outbox?: OutboxStore | undefined;
677
- clock?: Clock;
678
- ids?: IdGenerator;
679
- /** Override the default scorer (`defaultLeadScorer`). */
944
+ //#region src/repositories/lead.repository.d.ts
945
+ interface LeadRepositoryDeps {
946
+ events: EventTransport;
947
+ /** Override the default lead scorer. */
680
948
  scorer?: LeadScorer;
681
- /** When true (default), `create()` rejects dup emails. */
949
+ /** Reject `create()` when an email already exists in-tenant (default true). */
682
950
  rejectDuplicates?: boolean;
683
951
  }
684
- interface CreateLeadInput {
685
- firstName?: string;
686
- lastName?: string;
687
- fullName?: string;
688
- email?: string;
689
- phone?: string;
690
- companyName?: string;
691
- jobTitle?: string;
692
- source?: string;
693
- campaignRef?: string;
694
- ownerId?: string;
695
- tags?: readonly string[];
696
- metadata?: Record<string, unknown>;
697
- }
698
- interface ConvertLeadInput {
699
- /** Pipeline the resulting opportunity enters. */
700
- pipelineId: string;
701
- /** Opportunity name. Defaults to `<lead.companyName> — <lead.fullName>`. */
702
- opportunityName?: string;
703
- /** Optional deal amount. */
704
- amount?: Money;
705
- expectedCloseAt?: Date;
952
+ type TransitionExtras = {
706
953
  by?: string;
707
- }
708
- interface ConvertResult {
709
- lead: Lead;
710
- contactId: string;
711
- accountId?: string;
712
- opportunityId: string;
713
- }
714
- /**
715
- * Lead lifecycle + the crown-jewel `convert()` verb that atomically
716
- * spawns a Contact + Account + Opportunity and transitions the lead
717
- * to `converted`.
718
- */
719
- declare class LeadService {
720
- private readonly repo;
721
- private readonly contacts;
722
- private readonly accounts;
723
- private readonly opportunities;
954
+ note?: string;
955
+ patch?: Partial<ILeadDocument>;
956
+ };
957
+ declare class LeadRepository extends Repository<ILeadDocument> {
724
958
  private readonly events;
725
- private readonly outbox;
726
- private readonly clock;
727
- private readonly ids;
728
959
  private readonly scorer;
729
960
  private readonly rejectDuplicates;
730
- constructor(config: LeadServiceConfig);
731
- create(input: CreateLeadInput, options?: MutationOptions): Promise<Lead>;
732
- markContacted(id: string, by?: string, note?: string, options?: MutationOptions): Promise<Lead>;
733
- qualify(id: string, by?: string, note?: string, options?: MutationOptions): Promise<Lead>;
734
- disqualify(id: string, reason: string, by?: string, options?: MutationOptions): Promise<Lead>;
735
- nurture(id: string, by?: string, options?: MutationOptions): Promise<Lead>;
736
- convert(id: string, input: ConvertLeadInput, options?: MutationOptions): Promise<ConvertResult>;
737
- /** Recompute score and persist. Hosts can call on update or on demand. */
738
- recomputeScore(id: string, options?: MutationOptions): Promise<Lead>;
739
- findById(id: string): Promise<Lead | null>;
740
- list(filter?: Parameters<LeadRepository['list']>[0]): Promise<Lead[]>;
741
- private _updateScore;
742
- private _require;
961
+ constructor(model: Model<ILeadDocument>, plugins: PluginType[], deps: LeadRepositoryDeps, pagination?: PaginationConfig);
962
+ /**
963
+ * Create a Lead. Auto-scores on insert so hosts can route immediately.
964
+ * Rejects duplicate emails inside the same tenant unless
965
+ * `rejectDuplicates: false` was set at engine boot.
966
+ */
967
+ createLead(input: Partial<ILeadDocument>, ctx?: CrmContext): Promise<ILeadDocument>;
968
+ markContacted(id: string, extras?: TransitionExtras, ctx?: CrmContext): Promise<ILeadDocument>;
969
+ qualify(id: string, extras?: TransitionExtras, ctx?: CrmContext): Promise<ILeadDocument>;
970
+ disqualify(id: string, reason: string, extras?: Omit<TransitionExtras, 'patch'>, ctx?: CrmContext): Promise<ILeadDocument>;
971
+ nurture(id: string, extras?: TransitionExtras, ctx?: CrmContext): Promise<ILeadDocument>;
972
+ /**
973
+ * Merge enrichment payload (3rd-party provider response or manual
974
+ * patch) into the lead doc, then re-score. Emits `crm:lead.enriched`
975
+ * with the field set written so subscribers can fan out (e.g. push a
976
+ * `recompute_segment` job) without re-reading the doc.
977
+ *
978
+ * The `provider` tag flows into the event for attribution; the package
979
+ * is provider-agnostic so the actual enrichment call lives at the host.
980
+ */
981
+ enrich(id: string, patch: Partial<ILeadDocument>, extras?: {
982
+ provider?: string;
983
+ }, ctx?: CrmContext): Promise<ILeadDocument>;
984
+ /**
985
+ * Assign / re-assign the lead's owner. Emits `crm:lead.assigned` with
986
+ * the previous owner so audit consumers can compute hand-off chains.
987
+ */
988
+ assign(id: string, ownerId: string, ctx?: CrmContext): Promise<ILeadDocument>;
989
+ /**
990
+ * Recompute + persist the lead's score. Called from `createLead`
991
+ * automatically; hosts also call this after enrichment writes.
992
+ */
993
+ recomputeScore(id: string, ctx?: CrmContext): Promise<ILeadDocument>;
743
994
  private _transition;
744
- private _emit;
995
+ /**
996
+ * Count leads created on or after `since`. Used by dashboard KPI tiles.
997
+ * Goes through the inherited tenant-scoped `count()` — the plugin
998
+ * injects `organizationId` (never a manual `if (ctx)` filter, which
999
+ * silently reads ALL tenants when ctx is absent).
1000
+ */
1001
+ countCreatedSince(since: Date, ctx?: CrmContext): Promise<number>;
1002
+ /** Latest `limit` leads — lightweight projection for the activity feed. */
1003
+ findRecentForFeed(limit: number, ctx?: CrmContext): Promise<ILeadDocument[]>;
745
1004
  }
746
1005
  //#endregion
747
- //#region src/domain/entities/pipeline.d.ts
748
- /**
749
- * A Stage in a Pipeline. Stages are ordered (`sequence` ascending).
750
- * Default win-probability applies at every opportunity-movement unless
751
- * the caller overrides.
752
- */
753
- interface Stage {
754
- id: string;
755
- name: string;
756
- sequence: number;
757
- /** Default win probability (0-1) assigned to opportunities entering this stage. */
758
- defaultProbability: number;
759
- /** Optional UX hint for color-coded kanbans. */
760
- color?: string;
761
- /** Describes what "reaching this stage" means. */
762
- description?: string;
1006
+ //#region src/repositories/opportunity.repository.d.ts
1007
+ interface OpportunityRepositoryDeps {
1008
+ events: EventTransport;
1009
+ /** Cross-repo reference: read embedded stages from Pipeline. */
1010
+ pipelines: PipelineRepository;
763
1011
  }
764
- /**
765
- * A Pipeline groups ordered Stages for a specific sales motion. Hosts
766
- * typically have 1-N pipelines (e.g. `new-business`, `expansion`,
767
- * `partnerships`, `renewals`). Opportunities pin to exactly one
768
- * pipeline at create time.
769
- */
770
- interface Pipeline {
771
- id: string;
1012
+ interface OpenOpportunityInput {
772
1013
  name: string;
773
- /** Deprecates this pipeline — opportunities can still move within it but no new ones are created. */
774
- isArchived: boolean;
775
- stages: readonly Stage[];
776
- /** Free-form label for the sales team / motion that owns this pipeline. */
777
- teamRef?: string;
1014
+ pipelineId: string;
1015
+ /** When omitted, opp enters the pipeline's first stage. */
1016
+ stageId?: string;
1017
+ accountId?: string;
1018
+ primaryContactId?: string;
1019
+ sourceLeadId?: string;
1020
+ amount?: number;
1021
+ currency?: string;
1022
+ /** Override the stage's `defaultProbability`. */
1023
+ probability?: number;
1024
+ expectedCloseAt?: Date;
1025
+ ownerId?: string;
1026
+ tags?: readonly string[];
778
1027
  metadata?: Record<string, unknown>;
779
- createdAt: Date;
780
- updatedAt: Date;
781
1028
  }
782
- //#endregion
783
- //#region src/domain/ports/pipeline-repository.port.d.ts
784
- interface PipelineRepository {
785
- findById(id: string): Promise<Pipeline | null>;
786
- list(includeArchived?: boolean): Promise<Pipeline[]>;
787
- create(p: Omit<Pipeline, 'createdAt' | 'updatedAt'>): Promise<Pipeline>;
788
- update(id: string, patch: Partial<Pipeline>): Promise<Pipeline>;
1029
+ interface MoveStageInput {
1030
+ stageId: string;
1031
+ by?: string;
1032
+ note?: string;
1033
+ /** Override the target stage's `defaultProbability`. */
1034
+ probability?: number;
789
1035
  }
790
- //#endregion
791
- //#region src/services/pipeline.service.d.ts
792
- interface PipelineServiceConfig {
793
- repo: PipelineRepository;
794
- events?: EventTransport | undefined;
795
- /** Optional host-owned outbox. See AccountServiceConfig.outbox. */
796
- outbox?: OutboxStore | undefined;
797
- clock?: Clock;
798
- ids?: IdGenerator;
799
- }
800
- interface CreatePipelineInput {
801
- name: string;
802
- stages: readonly Omit<Stage, 'id'>[];
803
- teamRef?: string;
804
- metadata?: Record<string, unknown>;
1036
+ interface CloseInput {
1037
+ by?: string;
1038
+ note?: string;
1039
+ closedAt?: Date;
1040
+ amount?: number;
1041
+ lostReasonId?: string;
805
1042
  }
806
- /**
807
- * Pipeline lifecycle + stage helpers. The service owns stage id
808
- * generation so callers don't have to — create a pipeline with an
809
- * array of stage seeds and the service hands back the persisted
810
- * pipeline with ids assigned.
811
- */
812
- declare class PipelineService {
813
- private readonly repo;
1043
+ declare class OpportunityRepository extends Repository<IOpportunityDocument> {
814
1044
  private readonly events;
815
- private readonly outbox;
816
- private readonly clock;
817
- private readonly ids;
818
- constructor(config: PipelineServiceConfig);
819
- create(input: CreatePipelineInput, options?: MutationOptions): Promise<Pipeline>;
820
- archive(id: string, options?: MutationOptions): Promise<Pipeline>;
821
- getStage(pipelineId: string, stageId: string): Promise<Stage>;
822
- /** First stage in the ordered pipeline (minimum `sequence`). */
823
- getFirstStage(pipelineId: string): Promise<Stage>;
824
- /** Next stage after `stageId` in the ordered pipeline, or `null` at the end. */
825
- getNextStage(pipelineId: string, stageId: string): Promise<Stage | null>;
826
- findById(id: string): Promise<Pipeline | null>;
827
- list(includeArchived?: boolean): Promise<Pipeline[]>;
828
- private _require;
829
- private _emit;
830
- }
831
- //#endregion
832
- //#region src/services/forecast.d.ts
833
- interface ForecastRow {
834
- /** Bucket key — typically ISO month `YYYY-MM` derived from `expectedCloseAt`. */
835
- bucket: string;
836
- /** Sum of unweighted `amount`. */
837
- committed: number;
838
- /** Sum of `amount × probability`. */
839
- weighted: number;
840
- /** Count of opportunities contributing. */
841
- count: number;
842
- currency: string;
843
- }
844
- interface ForecastOptions {
1045
+ private readonly pipelines;
1046
+ constructor(model: Model<IOpportunityDocument>, plugins: PluginType[], deps: OpportunityRepositoryDeps, pagination?: PaginationConfig);
1047
+ openOpportunity(input: OpenOpportunityInput, ctx?: CrmContext): Promise<IOpportunityDocument>;
845
1048
  /**
846
- * Bucket function — given an opportunity, return a string bucket
847
- * key. Default is ISO-month `YYYY-MM`. Custom buckets (quarter,
848
- * fiscal-year, custom periods) plug in here.
1049
+ * Atomic stage move. CAS on `(status='open', stageId=current)` so two
1050
+ * concurrent moves can't both win. Emits `stage_advanced` or
1051
+ * `stage_regressed` based on `sequence` ordering of the embedded stages.
849
1052
  */
850
- bucket?: (o: Opportunity) => string;
1053
+ moveToStage(id: string, input: MoveStageInput, ctx?: CrmContext): Promise<IOpportunityDocument>;
1054
+ win(id: string, input?: CloseInput, ctx?: CrmContext): Promise<IOpportunityDocument>;
1055
+ lose(id: string, input?: CloseInput, ctx?: CrmContext): Promise<IOpportunityDocument>;
851
1056
  /**
852
- * Filter opportunities before bucketing. Default includes only
853
- * `open` opportunities with `amount` and `expectedCloseAt`.
1057
+ * Re-assign opportunity owner. Mutates `ownerId` only; no status
1058
+ * transition. Emits `crm:opportunity.assigned` with the previous owner.
854
1059
  */
855
- include?: (o: Opportunity) => boolean;
856
- }
857
- /**
858
- * Aggregate opportunities into a forecast table — one row per
859
- * bucket (month by default). Committed is `Σ amount`; weighted is
860
- * `Σ amount × probability`.
861
- *
862
- * Integer-safe: sums are computed on minor-unit amounts (consistent
863
- * with `@classytic/primitives/money`'s integer convention). Currency
864
- * mismatch across a bucket throws — hosts dealing in multi-currency
865
- * run this once per currency after partitioning.
866
- */
867
- declare function forecast(opportunities: readonly Opportunity[], options?: ForecastOptions): ForecastRow[];
868
- /**
869
- * Pipeline conversion summary — how opportunities partitioned across
870
- * stages and final outcome. Useful for "where do deals die?" analysis.
871
- */
872
- interface ConversionRow {
873
- stageId: string;
874
- currentCount: number;
875
- wonFromHere: number;
876
- lostFromHere: number;
877
- conversionRate: number;
1060
+ assign(id: string, ownerId: string, ctx?: CrmContext): Promise<IOpportunityDocument>;
1061
+ /**
1062
+ * Adjust the deal amount on an open opportunity. Rejected after
1063
+ * win/lose/abandon — closed-deal amounts are immutable history.
1064
+ * Emits `crm:opportunity.amount_changed` with the delta so forecast
1065
+ * subscribers can re-bucket.
1066
+ */
1067
+ updateAmount(id: string, amount: number, extras?: {
1068
+ reason?: string;
1069
+ by?: string;
1070
+ }, ctx?: CrmContext): Promise<IOpportunityDocument>;
1071
+ /**
1072
+ * Push `expectedCloseAt` further out. Only valid on open opps —
1073
+ * extending a closed deal is meaningless. The new date must be later
1074
+ * than the current one; pulling the date in is silently accepted
1075
+ * (no event is meaningful there).
1076
+ */
1077
+ extendCloseDate(id: string, newDate: Date, extras?: {
1078
+ reason?: string;
1079
+ by?: string;
1080
+ }, ctx?: CrmContext): Promise<IOpportunityDocument>;
1081
+ abandon(id: string, by?: string, note?: string, ctx?: CrmContext): Promise<IOpportunityDocument>;
1082
+ private _closeStatus;
1083
+ /** Count opportunities with status `open`. Used by dashboard KPI tiles. */
1084
+ countOpen(ctx?: CrmContext): Promise<number>;
1085
+ /**
1086
+ * Sum `amount` on won opportunities updated within `since`.
1087
+ * Goes through the tenant-scoped `findAll()` — the multiTenant plugin
1088
+ * injects `organizationId`, so a missing ctx can't leak other tenants'
1089
+ * revenue (a manual `if (ctx)` filter would).
1090
+ */
1091
+ sumWonSince(since: Date, ctx?: CrmContext): Promise<{
1092
+ total: number;
1093
+ currency?: string;
1094
+ } | null>;
1095
+ /** Latest `limit` opportunities — lightweight projection for the activity feed. */
1096
+ findRecentForFeed(limit: number, ctx?: CrmContext): Promise<IOpportunityDocument[]>;
878
1097
  }
879
- declare function pipelineConversion(opportunities: readonly Opportunity[], stageIds: readonly string[]): ConversionRow[];
880
1098
  //#endregion
881
- //#region src/domain/entities/loss-reason.d.ts
882
- /**
883
- * Standardized reason for closing an opportunity as `lost`. Seeded
884
- * once by the host; referenced by id on the opportunity. Keeping
885
- * these as records (vs free-form strings) is what makes lost-reason
886
- * analytics possible.
887
- */
888
- interface LossReason {
889
- id: string;
890
- name: string;
891
- /** Broad category — `price`, `feature-gap`, `timing`, `competitor`, `no-decision`. */
892
- category?: string;
893
- description?: string;
894
- active: boolean;
895
- metadata?: Record<string, unknown>;
896
- createdAt: Date;
897
- updatedAt: Date;
1099
+ //#region src/repositories/contact.repository.d.ts
1100
+ interface ContactRepositoryDeps {
1101
+ events: EventTransport;
1102
+ }
1103
+ interface MergeContactInput {
1104
+ /** Surviving contact id. Receives merged fields. */
1105
+ primaryId: string;
1106
+ /** Secondary contact id. Deleted after merge. */
1107
+ secondaryId: string;
1108
+ /**
1109
+ * Per-field strategy. Defaults to `'preferPrimary'` — primary's
1110
+ * non-empty fields win, secondary fills the gaps. Use `'preferSecondary'`
1111
+ * for the reverse. `tags` are always unioned.
1112
+ */
1113
+ strategy?: 'preferPrimary' | 'preferSecondary';
898
1114
  }
899
- //#endregion
900
- //#region src/domain/ports/loss-reason-repository.port.d.ts
901
- interface LossReasonRepository {
902
- findById(id: string): Promise<LossReason | null>;
903
- list(activeOnly?: boolean): Promise<LossReason[]>;
904
- create(r: Omit<LossReason, 'createdAt' | 'updatedAt'>): Promise<LossReason>;
905
- update(id: string, patch: Partial<LossReason>): Promise<LossReason>;
1115
+ declare class ContactRepository extends Repository<IContactDocument> {
1116
+ private readonly events;
1117
+ constructor(model: Model<IContactDocument>, plugins: PluginType[], deps: ContactRepositoryDeps, pagination?: PaginationConfig);
1118
+ /**
1119
+ * Create + emit `crm:contact.created`. Hosts call this instead of
1120
+ * `create()` when they want the event side-effect (e.g. webhook fan-out).
1121
+ */
1122
+ createContact(input: Partial<IContactDocument>, ctx?: CrmContext): Promise<IContactDocument>;
1123
+ /**
1124
+ * Merge enrichment payload into the contact. The `patch` is applied
1125
+ * verbatim; deep-merge of `contact.*` channels is the caller's job
1126
+ * (they own the provider response shape).
1127
+ */
1128
+ enrich(id: string, patch: Partial<IContactDocument>, extras?: {
1129
+ provider?: string;
1130
+ }, ctx?: CrmContext): Promise<IContactDocument>;
1131
+ /** Set / re-assign the contact's owner. Atomic; no-op when unchanged. */
1132
+ assign(id: string, ownerId: string, ctx?: CrmContext): Promise<IContactDocument>;
1133
+ /**
1134
+ * Merge two contacts. Secondary's non-conflicting fields are folded
1135
+ * into primary, tags are unioned, then secondary is deleted.
1136
+ *
1137
+ * Hosts wanting transactional safety can pass `ctx.session` —
1138
+ * mongokit threads it through `update` + `delete`.
1139
+ *
1140
+ * Re-pointing related Activities / Notes / Opportunities is the host's
1141
+ * job (they own those relationships) — subscribe to
1142
+ * `crm:contact.merged` and rewrite `subjectId` / `primaryContactId`
1143
+ * from there.
1144
+ */
1145
+ merge(input: MergeContactInput, ctx?: CrmContext): Promise<IContactDocument>;
906
1146
  }
907
1147
  //#endregion
908
- //#region src/domain/ports/outreach.port.d.ts
909
- /**
910
- * Optional outreach bridge. When wired, activity transitions can send
911
- * real emails / SMS / etc. via the host's pipeline. This package does
912
- * NOT call it automatically — hosts subscribe to `crm:activity.*`
913
- * events and call `outreach.send()` from their own handler so pacing,
914
- * retry, and bounce handling live where they belong.
915
- *
916
- * The port intentionally stays minimal; implementations forward to
917
- * SendGrid, Postmark, Twilio, Mailgun, or an in-house SMTP stack.
918
- */
919
- interface OutreachSendRequest {
920
- channel: 'email' | 'sms' | 'social' | string;
921
- toAddress: string;
922
- subject?: string;
923
- body: string;
924
- activityId?: string;
925
- /** Host metadata — template id, campaign ref, personalization vars. */
926
- metadata?: Record<string, unknown>;
1148
+ //#region src/repositories/account.repository.d.ts
1149
+ interface AccountRepositoryDeps {
1150
+ events: EventTransport;
927
1151
  }
928
- interface OutreachSendResult {
929
- /** Opaque ref — typically the provider's message id. */
930
- providerMessageId?: string;
931
- acceptedAt: Date;
1152
+ interface MergeAccountInput {
1153
+ primaryId: string;
1154
+ secondaryId: string;
1155
+ strategy?: 'preferPrimary' | 'preferSecondary';
932
1156
  }
933
- interface OutreachPort {
934
- send(request: OutreachSendRequest): Promise<OutreachSendResult>;
1157
+ declare class AccountRepository extends Repository<IAccountDocument> {
1158
+ private readonly events;
1159
+ constructor(model: Model<IAccountDocument>, plugins: PluginType[], deps: AccountRepositoryDeps, pagination?: PaginationConfig);
1160
+ createAccount(input: Partial<IAccountDocument>, ctx?: CrmContext): Promise<IAccountDocument>;
1161
+ enrich(id: string, patch: Partial<IAccountDocument>, extras?: {
1162
+ provider?: string;
1163
+ }, ctx?: CrmContext): Promise<IAccountDocument>;
1164
+ assign(id: string, ownerId: string, ctx?: CrmContext): Promise<IAccountDocument>;
1165
+ merge(input: MergeAccountInput, ctx?: CrmContext): Promise<IAccountDocument>;
935
1166
  }
936
1167
  //#endregion
937
- //#region src/adapters/in-memory/account-repository.d.ts
938
- declare class InMemoryAccountRepository implements AccountRepository {
939
- private readonly clock;
940
- private readonly items;
941
- constructor(clock?: Clock);
942
- findById(id: string): Promise<Account | null>;
943
- findByDomain(domain: string): Promise<Account | null>;
944
- list(filter?: AccountFilter): Promise<Account[]>;
945
- create(input: Omit<Account, 'createdAt' | 'updatedAt'>): Promise<Account>;
946
- update(id: string, patch: Partial<Account>): Promise<Account>;
947
- delete(id: string): Promise<void>;
1168
+ //#region src/repositories/campaign.repository.d.ts
1169
+ interface CampaignRepositoryDeps {
1170
+ events: EventTransport;
1171
+ /** Reject `createCampaign` on duplicate names per-tenant (default true). */
1172
+ rejectDuplicates?: boolean;
948
1173
  }
949
- //#endregion
950
- //#region src/adapters/in-memory/activity-repository.d.ts
951
- declare class InMemoryActivityRepository implements ActivityRepository {
952
- private readonly clock;
953
- private readonly items;
954
- constructor(clock?: Clock);
955
- findById(id: string): Promise<Activity | null>;
956
- list(filter?: ActivityFilter): Promise<Activity[]>;
957
- create(input: Omit<Activity, 'createdAt' | 'updatedAt'>): Promise<Activity>;
958
- update(id: string, patch: Partial<Activity>): Promise<Activity>;
959
- casStatus(id: string, expected: ActivityStatus, next: ActivityStatus, patch?: Partial<Activity>): Promise<Activity | null>;
1174
+ type ExtrasWithRevenue = {
1175
+ by?: string;
1176
+ note?: string;
1177
+ actualRevenueAmount?: number;
1178
+ };
1179
+ declare class CampaignRepository extends Repository<ICampaignDocument> {
1180
+ private readonly events;
1181
+ private readonly rejectDuplicates;
1182
+ constructor(model: Model<ICampaignDocument>, plugins: PluginType[], deps: CampaignRepositoryDeps, pagination?: PaginationConfig);
1183
+ createCampaign(input: Partial<ICampaignDocument>, ctx?: CrmContext): Promise<ICampaignDocument>;
1184
+ activate(id: string, by?: string, ctx?: CrmContext): Promise<ICampaignDocument>;
1185
+ pause(id: string, reason?: string, by?: string, ctx?: CrmContext): Promise<ICampaignDocument>;
1186
+ resume(id: string, by?: string, ctx?: CrmContext): Promise<ICampaignDocument>;
1187
+ complete(id: string, extras?: ExtrasWithRevenue, ctx?: CrmContext): Promise<ICampaignDocument>;
1188
+ archive(id: string, by?: string, ctx?: CrmContext): Promise<ICampaignDocument>;
1189
+ private _transition;
960
1190
  }
961
1191
  //#endregion
962
- //#region src/adapters/in-memory/contact-repository.d.ts
963
- declare class InMemoryContactRepository implements ContactRepository {
964
- private readonly clock;
965
- private readonly items;
966
- constructor(clock?: Clock);
967
- findById(id: string): Promise<Contact | null>;
968
- findByEmail(email: string): Promise<Contact | null>;
969
- list(filter?: ContactFilter): Promise<Contact[]>;
970
- create(input: Omit<Contact, 'createdAt' | 'updatedAt'>): Promise<Contact>;
971
- update(id: string, patch: Partial<Contact>): Promise<Contact>;
972
- delete(id: string): Promise<void>;
1192
+ //#region src/repositories/subscription.repository.d.ts
1193
+ interface SubscriptionRepositoryDeps {
1194
+ events: EventTransport;
1195
+ /** Default DOI policy when `subscribe` doesn't pass `requireDoubleOptIn`. */
1196
+ defaultRequireDoubleOptIn?: boolean;
973
1197
  }
974
- //#endregion
975
- //#region src/adapters/in-memory/event-transport.d.ts
976
- type RecordedEvent = DomainEvent;
977
- declare class InMemoryEventTransport implements EventTransport {
978
- readonly name = "crm-in-memory";
979
- readonly events: RecordedEvent[];
980
- private readonly subscribers;
981
- publish(event: DomainEvent): Promise<void>;
982
- subscribe(pattern: string, handler: EventHandler): Promise<() => void>;
1198
+ interface SubscribeInput {
1199
+ contactId: string;
1200
+ campaignId: string;
1201
+ source?: string;
1202
+ consentIpAddress?: string;
1203
+ consentUserAgent?: string;
1204
+ metadata?: Record<string, unknown>;
1205
+ /** Override the engine-level default. */
1206
+ requireDoubleOptIn?: boolean;
983
1207
  }
984
- //#endregion
985
- //#region src/adapters/in-memory/lead-repository.d.ts
986
- declare class InMemoryLeadRepository implements LeadRepository {
987
- private readonly clock;
988
- private readonly items;
989
- constructor(clock?: Clock);
990
- findById(id: string): Promise<Lead | null>;
991
- findByEmail(email: string): Promise<Lead | null>;
992
- list(filter?: LeadFilter): Promise<Lead[]>;
993
- create(input: Omit<Lead, 'createdAt' | 'updatedAt'>): Promise<Lead>;
994
- update(id: string, patch: Partial<Lead>): Promise<Lead>;
995
- casStatus(id: string, expected: LeadStatus, next: LeadStatus, patch?: Partial<Lead>): Promise<Lead | null>;
1208
+ declare class SubscriptionRepository extends Repository<ISubscriptionDocument> {
1209
+ private readonly events;
1210
+ private readonly defaultRequireDoubleOptIn;
1211
+ constructor(model: Model<ISubscriptionDocument>, plugins: PluginType[], deps: SubscriptionRepositoryDeps, pagination?: PaginationConfig);
1212
+ subscribe(input: SubscribeInput, ctx?: CrmContext): Promise<ISubscriptionDocument>;
1213
+ confirm(token: string, ctx?: CrmContext): Promise<ISubscriptionDocument>;
1214
+ unsubscribe(token: string, reason?: string, ctx?: CrmContext): Promise<ISubscriptionDocument>;
1215
+ markBounced(id: string, reason?: string, ctx?: CrmContext): Promise<ISubscriptionDocument>;
1216
+ markComplained(id: string, ctx?: CrmContext): Promise<ISubscriptionDocument>;
1217
+ private _transition;
996
1218
  }
997
1219
  //#endregion
998
- //#region src/adapters/in-memory/loss-reason-repository.d.ts
999
- declare class InMemoryLossReasonRepository implements LossReasonRepository {
1000
- private readonly clock;
1001
- private readonly items;
1002
- constructor(clock?: Clock);
1003
- findById(id: string): Promise<LossReason | null>;
1004
- list(activeOnly?: boolean): Promise<LossReason[]>;
1005
- create(input: Omit<LossReason, 'createdAt' | 'updatedAt'>): Promise<LossReason>;
1006
- update(id: string, patch: Partial<LossReason>): Promise<LossReason>;
1220
+ //#region src/repositories/activity.repository.d.ts
1221
+ interface ActivityRepositoryDeps {
1222
+ events: EventTransport;
1007
1223
  }
1008
- //#endregion
1009
- //#region src/adapters/in-memory/note-repository.d.ts
1010
- declare class InMemoryNoteRepository implements NoteRepository {
1011
- private readonly clock;
1012
- private readonly items;
1013
- constructor(clock?: Clock);
1014
- findById(id: string): Promise<Note | null>;
1015
- listBySubject(subjectKind: SubjectKind, subjectId: string): Promise<Note[]>;
1016
- create(input: Omit<Note, 'createdAt' | 'updatedAt'>): Promise<Note>;
1017
- update(id: string, patch: Partial<Note>): Promise<Note>;
1018
- delete(id: string): Promise<void>;
1224
+ declare class ActivityRepository extends Repository<IActivityDocument> {
1225
+ private readonly events;
1226
+ constructor(model: Model<IActivityDocument>, plugins: PluginType[], deps: ActivityRepositoryDeps, pagination?: PaginationConfig);
1227
+ plan(input: Partial<IActivityDocument>, ctx?: CrmContext): Promise<IActivityDocument>;
1228
+ start(id: string, ctx?: CrmContext): Promise<IActivityDocument>;
1229
+ complete(id: string, completedAt?: Date, ctx?: CrmContext): Promise<IActivityDocument>;
1230
+ cancel(id: string, reason?: string, ctx?: CrmContext): Promise<IActivityDocument>;
1231
+ /**
1232
+ * Push an activity's `dueDate`. Allowed on planned / in_progress only;
1233
+ * rescheduling a completed or cancelled activity makes no sense.
1234
+ * Emits `crm:activity.rescheduled` with the previous date for audit.
1235
+ */
1236
+ reschedule(id: string, newDueDate: Date, extras?: {
1237
+ reason?: string;
1238
+ by?: string;
1239
+ }, ctx?: CrmContext): Promise<IActivityDocument>;
1240
+ /**
1241
+ * Record the outcome of an activity (call result, email engagement,
1242
+ * meeting confirmation). Stored under `metadata.outcome` so it stays
1243
+ * polymorphic — the shape is the channel's concern, not this package's.
1244
+ *
1245
+ * Does NOT transition status — call `complete()` separately when the
1246
+ * outcome implies completion. This split lets agents log partial
1247
+ * outcomes (e.g. "left voicemail") without forcing terminal state.
1248
+ */
1249
+ recordOutcome(id: string, outcome: Record<string, unknown>, ctx?: CrmContext): Promise<IActivityDocument>;
1250
+ private _transition;
1251
+ /** Count `planned` activities with `dueDate` before `asOf`. Dashboard KPI. */
1252
+ countOverduePlanned(asOf: Date, ctx?: CrmContext): Promise<number>;
1253
+ /** Latest `limit` activities — lightweight projection for the activity feed. */
1254
+ findRecentForFeed(limit: number, ctx?: CrmContext): Promise<IActivityDocument[]>;
1019
1255
  }
1020
1256
  //#endregion
1021
- //#region src/adapters/in-memory/opportunity-repository.d.ts
1022
- declare class InMemoryOpportunityRepository implements OpportunityRepository {
1023
- private readonly clock;
1024
- private readonly items;
1025
- constructor(clock?: Clock);
1026
- findById(id: string): Promise<Opportunity | null>;
1027
- list(filter?: OpportunityFilter): Promise<Opportunity[]>;
1028
- create(input: Omit<Opportunity, 'createdAt' | 'updatedAt'>): Promise<Opportunity>;
1029
- update(id: string, patch: Partial<Opportunity>): Promise<Opportunity>;
1030
- delete(id: string): Promise<void>;
1031
- casStatus(id: string, expected: OpportunityStatus, next: OpportunityStatus, patch?: Partial<Opportunity>): Promise<Opportunity | null>;
1032
- casStage(id: string, expectedStageId: string, nextStageId: string, patch?: Partial<Opportunity>): Promise<Opportunity | null>;
1257
+ //#region src/repositories/note.repository.d.ts
1258
+ interface NoteRepositoryDeps {
1259
+ events: EventTransport;
1033
1260
  }
1034
- //#endregion
1035
- //#region src/adapters/in-memory/pipeline-repository.d.ts
1036
- declare class InMemoryPipelineRepository implements PipelineRepository {
1037
- private readonly clock;
1038
- private readonly items;
1039
- constructor(clock?: Clock);
1040
- findById(id: string): Promise<Pipeline | null>;
1041
- list(includeArchived?: boolean): Promise<Pipeline[]>;
1042
- create(input: Omit<Pipeline, 'createdAt' | 'updatedAt'>): Promise<Pipeline>;
1043
- update(id: string, patch: Partial<Pipeline>): Promise<Pipeline>;
1261
+ declare class NoteRepository extends Repository<INoteDocument> {
1262
+ private readonly events;
1263
+ constructor(model: Model<INoteDocument>, plugins: PluginType[], deps: NoteRepositoryDeps, pagination?: PaginationConfig);
1264
+ addNote(input: Partial<INoteDocument>, ctx?: CrmContext): Promise<INoteDocument>;
1044
1265
  }
1045
1266
  //#endregion
1046
- //#region src/adapters/in-memory/outreach-port.d.ts
1047
- declare class NullOutreachPort implements OutreachPort {
1048
- private counter;
1049
- send(_request: OutreachSendRequest): Promise<OutreachSendResult>;
1050
- }
1051
- interface RecordedOutreach extends OutreachSendRequest, OutreachSendResult {}
1052
- declare class InMemoryOutreachPort implements OutreachPort {
1053
- private readonly nowMs;
1054
- readonly sent: RecordedOutreach[];
1055
- private counter;
1056
- constructor(nowMs?: () => number);
1057
- send(request: OutreachSendRequest): Promise<OutreachSendResult>;
1267
+ //#region src/repositories/loss-reason.repository.d.ts
1268
+ interface LossReasonRepositoryDeps {
1269
+ events: EventTransport;
1270
+ }
1271
+ declare class LossReasonRepository extends Repository<ILossReasonDocument> {
1272
+ private readonly events;
1273
+ constructor(model: Model<ILossReasonDocument>, plugins: PluginType[], deps: LossReasonRepositoryDeps, pagination?: PaginationConfig);
1274
+ /** Hide from pickers without breaking historical FKs. */
1275
+ archive(id: string, ctx?: CrmContext): Promise<ILossReasonDocument>;
1058
1276
  }
1059
1277
  //#endregion
1060
- //#region src/adapters/in-memory/id-generator.d.ts
1061
- declare const cryptoIdGenerator: IdGenerator;
1062
- declare function createSequentialIdGenerator(prefix?: string): IdGenerator;
1063
- //#endregion
1064
- //#region src/domain/errors.d.ts
1065
- /**
1066
- * CRM error vocabulary. Hosts catch `CrmError` at API edges.
1067
- */
1068
- declare class CrmError extends Error {
1069
- readonly code: string;
1070
- readonly details?: Record<string, unknown>;
1071
- constructor(code: string, message: string, details?: Record<string, unknown>);
1072
- }
1073
- declare class NotFoundError extends CrmError {
1074
- constructor(kind: string, id: string);
1075
- }
1076
- declare class InvalidLeadTransitionError extends CrmError {
1077
- constructor(details: {
1078
- from: string;
1079
- to: string;
1080
- leadId: string;
1081
- });
1278
+ //#region src/repositories/email-event.repository.d.ts
1279
+ interface EmailEventRepositoryDeps {
1280
+ events: EventTransport;
1082
1281
  }
1083
- declare class InvalidOpportunityTransitionError extends CrmError {
1084
- constructor(details: {
1085
- from: string;
1086
- to: string;
1087
- opportunityId: string;
1088
- });
1282
+ interface RecordEmailEventInput {
1283
+ subjectKind: EmailEventSubjectKind;
1284
+ subjectId: string;
1285
+ campaignId?: string;
1286
+ messageId?: string;
1287
+ url?: string;
1288
+ userAgent?: string;
1289
+ ip?: string;
1290
+ metadata?: Record<string, unknown>;
1089
1291
  }
1090
- declare class InvalidActivityTransitionError extends CrmError {
1091
- constructor(details: {
1092
- from: string;
1093
- to: string;
1094
- activityId: string;
1095
- });
1292
+ declare class EmailEventRepository extends Repository<IEmailEventDocument> {
1293
+ private readonly events;
1294
+ constructor(model: Model<IEmailEventDocument>, plugins: PluginType[], deps: EmailEventRepositoryDeps, pagination?: PaginationConfig);
1295
+ recordOpen(input: RecordEmailEventInput, ctx?: CrmContext): Promise<IEmailEventDocument>;
1296
+ recordClick(input: RecordEmailEventInput, ctx?: CrmContext): Promise<IEmailEventDocument>;
1297
+ recordBounce(input: RecordEmailEventInput, ctx?: CrmContext): Promise<IEmailEventDocument>;
1298
+ recordComplaint(input: RecordEmailEventInput, ctx?: CrmContext): Promise<IEmailEventDocument>;
1299
+ recordUnsubscribe(input: RecordEmailEventInput, ctx?: CrmContext): Promise<IEmailEventDocument>;
1300
+ private _record;
1096
1301
  }
1097
- declare class StageNotInPipelineError extends CrmError {
1098
- constructor(details: {
1099
- pipelineId: string;
1100
- stageId: string;
1101
- });
1302
+ //#endregion
1303
+ //#region src/repositories/conversation.repository.d.ts
1304
+ interface ConversationRepositoryDeps {
1305
+ events: EventTransport;
1306
+ }
1307
+ interface OpenConversationInput {
1308
+ channel: ConversationChannel;
1309
+ participantKind: ConversationParticipantKind;
1310
+ participantId: string;
1311
+ channelUserId?: string;
1312
+ assigneeId?: string;
1313
+ metadata?: Record<string, unknown>;
1102
1314
  }
1103
- declare class LeadAlreadyConvertedError extends CrmError {
1104
- constructor(leadId: string);
1315
+ declare class ConversationRepository extends Repository<IConversationDocument> {
1316
+ private readonly events;
1317
+ constructor(model: Model<IConversationDocument>, plugins: PluginType[], deps: ConversationRepositoryDeps, pagination?: PaginationConfig);
1318
+ openConversation(input: OpenConversationInput, ctx?: CrmContext): Promise<IConversationDocument>;
1319
+ assign(id: string, assigneeId: string, ctx?: CrmContext): Promise<IConversationDocument>;
1320
+ resolve(id: string, ctx?: CrmContext): Promise<IConversationDocument>;
1321
+ reopen(id: string, ctx?: CrmContext): Promise<IConversationDocument>;
1322
+ /** Reset unread badge. Pure UI affordance, no event. */
1323
+ markRead(id: string, ctx?: CrmContext): Promise<IConversationDocument>;
1324
+ /**
1325
+ * Bump `lastMessageAt` and (for inbound) increment `unreadCount`.
1326
+ * Called by `MessageRepository` when a message is recorded — not part
1327
+ * of the public surface. Returns the conversation for the caller's
1328
+ * follow-up writes.
1329
+ */
1330
+ touch(id: string, args: {
1331
+ direction: 'inbound' | 'outbound';
1332
+ sentAt: Date;
1333
+ }, ctx?: CrmContext): Promise<IConversationDocument | null>;
1334
+ private _setStatus;
1105
1335
  }
1106
- declare class DuplicateLeadError extends CrmError {
1107
- constructor(details: {
1108
- email?: string;
1109
- phone?: string;
1110
- });
1336
+ //#endregion
1337
+ //#region src/repositories/message.repository.d.ts
1338
+ interface MessageRepositoryDeps {
1339
+ events: EventTransport;
1340
+ conversations: ConversationRepository;
1111
1341
  }
1112
- declare class EmptyPipelineError extends CrmError {
1113
- constructor(pipelineId: string);
1342
+ interface RecordMessageInput {
1343
+ conversationId: string;
1344
+ body: string;
1345
+ attachments?: IMessageAttachment[];
1346
+ externalMessageId?: string;
1347
+ senderRef?: string;
1348
+ recipientRef?: string;
1349
+ sentAt?: Date;
1350
+ metadata?: Record<string, unknown>;
1351
+ }
1352
+ declare class MessageRepository extends Repository<IMessageDocument> {
1353
+ private readonly events;
1354
+ private readonly conversations;
1355
+ constructor(model: Model<IMessageDocument>, plugins: PluginType[], deps: MessageRepositoryDeps, pagination?: PaginationConfig);
1356
+ recordInbound(input: RecordMessageInput, ctx?: CrmContext): Promise<IMessageDocument>;
1357
+ recordOutbound(input: RecordMessageInput, ctx?: CrmContext): Promise<IMessageDocument>;
1358
+ /** Wire delivery callback (e.g. WhatsApp `status` webhook). */
1359
+ updateStatus(id: string, status: MessageDeliveryStatus, ctx?: CrmContext): Promise<IMessageDocument>;
1360
+ private _record;
1361
+ /** Latest `limit` messages — lightweight projection for the activity feed. */
1362
+ findRecentForFeed(limit: number, ctx?: CrmContext): Promise<IMessageDocument[]>;
1114
1363
  }
1115
1364
  //#endregion
1116
- //#region src/events/event-constants.d.ts
1117
- /** PACKAGE_RULES rule 12: `package:resource.verb`. */
1118
- declare const CRM_EVENTS: {
1119
- readonly LEAD_CREATED: "crm:lead.created";
1120
- readonly LEAD_STATUS_CHANGED: "crm:lead.status_changed";
1121
- readonly LEAD_SCORED: "crm:lead.scored";
1122
- readonly LEAD_CONVERTED: "crm:lead.converted";
1123
- readonly LEAD_DISQUALIFIED: "crm:lead.disqualified";
1124
- readonly ACCOUNT_CREATED: "crm:account.created";
1125
- readonly ACCOUNT_UPDATED: "crm:account.updated";
1126
- readonly CONTACT_CREATED: "crm:contact.created";
1127
- readonly CONTACT_UPDATED: "crm:contact.updated";
1128
- readonly OPPORTUNITY_CREATED: "crm:opportunity.created";
1129
- readonly OPPORTUNITY_STAGE_ADVANCED: "crm:opportunity.stage_advanced";
1130
- readonly OPPORTUNITY_STAGE_REGRESSED: "crm:opportunity.stage_regressed";
1131
- readonly OPPORTUNITY_WON: "crm:opportunity.won";
1132
- readonly OPPORTUNITY_LOST: "crm:opportunity.lost";
1133
- readonly OPPORTUNITY_ABANDONED: "crm:opportunity.abandoned";
1134
- readonly PIPELINE_CREATED: "crm:pipeline.created";
1135
- readonly PIPELINE_ARCHIVED: "crm:pipeline.archived";
1136
- readonly ACTIVITY_PLANNED: "crm:activity.planned";
1137
- readonly ACTIVITY_COMPLETED: "crm:activity.completed";
1138
- readonly ACTIVITY_CANCELLED: "crm:activity.cancelled";
1139
- readonly NOTE_ADDED: "crm:note.added";
1140
- };
1141
- type CrmEventName = (typeof CRM_EVENTS)[keyof typeof CRM_EVENTS];
1142
- //#endregion
1143
- //#region src/events/event-catalog.d.ts
1144
- interface CrmEventSchema {
1145
- type: 'object';
1146
- properties?: Record<string, {
1147
- type?: string;
1148
- format?: string;
1149
- [key: string]: unknown;
1150
- }>;
1151
- required?: string[];
1152
- [key: string]: unknown;
1365
+ //#region src/engine/engine-types.d.ts
1366
+ interface CrmLogger {
1367
+ info?(msg: string, meta?: Record<string, unknown>): void;
1368
+ warn?(msg: string, meta?: Record<string, unknown>): void;
1369
+ error?(msg: string, meta?: Record<string, unknown>): void;
1370
+ debug?(msg: string, meta?: Record<string, unknown>): void;
1371
+ }
1372
+ interface CrmConfig {
1373
+ /** Mongoose connection — package creates models on this connection. */
1374
+ connection: Connection;
1375
+ /**
1376
+ * Tenant column shape. `'objectId'` (default) matches Better Auth orgs
1377
+ * and unlocks `$lookup` / `.populate()`; `'string'` matches UUID hosts.
1378
+ * Threaded into both schemas and `multiTenantPlugin` so they stay in
1379
+ * sync (PACKAGE_RULES §9.2).
1380
+ */
1381
+ tenantFieldType?: TenantFieldType;
1382
+ /**
1383
+ * When `true` (default), wire `multiTenantPlugin` on every repository
1384
+ * with `required: true`. Set `false` only for fixtures / smoke tests
1385
+ * that intentionally drop the scope.
1386
+ */
1387
+ multiTenant?: boolean;
1388
+ /** Arc-compatible event transport. Default: in-process bus. */
1389
+ eventTransport?: EventTransport;
1390
+ /** Logger surface used by the in-process bus + repositories. */
1391
+ logger?: CrmLogger;
1392
+ /** Pagination defaults for every repository (mongokit). */
1393
+ pagination?: PaginationConfig;
1394
+ /** Extra mongokit plugins applied to every repository (after defaults). */
1395
+ plugins?: PluginType[];
1396
+ /**
1397
+ * On a Mongoose model-name collision (another engine / hot reload owns
1398
+ * the name): `false` (default) throws `CrmModelCollisionError`; `true`
1399
+ * deletes + re-registers. Test fixtures / hot reload only (§21).
1400
+ */
1401
+ forceRecreate?: boolean;
1402
+ /**
1403
+ * Mongoose auto-index builds. Default `true` (dev). Set `false` in
1404
+ * production and run `engine.syncIndexes()` from a deploy script so
1405
+ * cold-boot index builds don't blow Fastify's plugin timeout (§35).
1406
+ */
1407
+ autoIndex?: boolean;
1408
+ /**
1409
+ * Skip the boot-time `transactions` capability requirement, downgrading
1410
+ * it to a warning. Set only when the host never calls
1411
+ * `LeadService.convert()` inside a transaction (standalone Mongo).
1412
+ */
1413
+ allowNonTransactional?: boolean;
1414
+ }
1415
+ interface ResolvedCrmConfig extends SchemaBuildConfig {
1416
+ connection: Connection;
1417
+ tenantFieldType: TenantFieldType;
1418
+ multiTenant: boolean;
1419
+ forceRecreate: boolean;
1420
+ autoIndex: boolean;
1421
+ logger?: CrmLogger;
1422
+ pagination?: PaginationConfig;
1423
+ plugins: PluginType[];
1424
+ }
1425
+ interface CrmRepositories {
1426
+ pipeline: PipelineRepository;
1427
+ lead: LeadRepository;
1428
+ opportunity: OpportunityRepository;
1429
+ contact: ContactRepository;
1430
+ account: AccountRepository;
1431
+ campaign: CampaignRepository;
1432
+ subscription: SubscriptionRepository;
1433
+ activity: ActivityRepository;
1434
+ note: NoteRepository;
1435
+ lossReason: LossReasonRepository;
1436
+ emailEvent: EmailEventRepository;
1437
+ conversation: ConversationRepository;
1438
+ message: MessageRepository;
1439
+ }
1440
+ interface CrmEngine {
1441
+ /** Repositories ARE the API surface (PACKAGE_RULES §1). */
1442
+ readonly repositories: Readonly<CrmRepositories>;
1443
+ /** Arc-compatible event transport. */
1444
+ readonly events: EventTransport;
1445
+ /** Mongoose models — for arc adapter wiring. */
1446
+ readonly models: Readonly<CrmModels>;
1447
+ /** Resolved configuration (frozen). */
1448
+ readonly config: Readonly<ResolvedCrmConfig>;
1449
+ /**
1450
+ * Build/align every model's indexes against its schema. Call once per
1451
+ * deploy when booted with `autoIndex: false` (§35). Idempotent.
1452
+ */
1453
+ syncIndexes(): Promise<void>;
1454
+ /** Release resources. Safe to call multiple times. */
1455
+ dispose(): Promise<void>;
1153
1456
  }
1154
- interface CrmEventDefinition<TSchema extends z.ZodType = z.ZodType> {
1155
- readonly name: string;
1156
- readonly version: number;
1157
- readonly description?: string;
1158
- readonly schema: CrmEventSchema;
1159
- readonly zodSchema: TSchema;
1160
- create(payload: z.infer<TSchema>, meta?: Partial<DomainEvent['meta']>): DomainEvent<z.infer<TSchema>>;
1161
- readonly __payload?: z.infer<TSchema>;
1162
- }
1163
- type CrmEventPayloadOf<D> = D extends CrmEventDefinition<infer S> ? z.infer<S> : never;
1164
- declare const leadCreatedSchema: z.ZodObject<{
1165
- leadId: z.ZodString;
1166
- email: z.ZodOptional<z.ZodString>;
1167
- score: z.ZodOptional<z.ZodNumber>;
1168
- }, z.core.$strip>;
1169
- declare const leadStatusChangedSchema: z.ZodObject<{
1170
- leadId: z.ZodString;
1171
- from: z.ZodString;
1172
- to: z.ZodString;
1173
- }, z.core.$strip>;
1174
- declare const leadScoredSchema: z.ZodObject<{
1175
- leadId: z.ZodString;
1176
- score: z.ZodNumber;
1177
- }, z.core.$strip>;
1178
- declare const leadConvertedSchema: z.ZodObject<{
1179
- leadId: z.ZodString;
1180
- contactId: z.ZodString;
1181
- accountId: z.ZodOptional<z.ZodString>;
1182
- opportunityId: z.ZodString;
1183
- }, z.core.$strip>;
1184
- declare const leadDisqualifiedSchema: z.ZodObject<{
1185
- leadId: z.ZodString;
1186
- reason: z.ZodString;
1187
- }, z.core.$strip>;
1188
- declare const accountCreatedSchema: z.ZodObject<{
1189
- accountId: z.ZodString;
1190
- }, z.core.$strip>;
1191
- declare const accountUpdatedSchema: z.ZodObject<{
1192
- accountId: z.ZodString;
1193
- }, z.core.$strip>;
1194
- declare const contactCreatedSchema: z.ZodObject<{
1195
- contactId: z.ZodString;
1196
- email: z.ZodOptional<z.ZodString>;
1197
- }, z.core.$strip>;
1198
- declare const contactUpdatedSchema: z.ZodObject<{
1199
- contactId: z.ZodString;
1200
- }, z.core.$strip>;
1201
- declare const opportunityCreatedSchema: z.ZodObject<{
1202
- opportunityId: z.ZodString;
1203
- pipelineId: z.ZodString;
1204
- stageId: z.ZodString;
1205
- }, z.core.$strip>;
1206
- declare const opportunityStageAdvancedSchema: z.ZodObject<{
1207
- opportunityId: z.ZodString;
1208
- from: z.ZodObject<{
1209
- stageId: z.ZodString;
1210
- sequence: z.ZodNumber;
1211
- }, z.core.$strip>;
1212
- to: z.ZodObject<{
1213
- stageId: z.ZodString;
1214
- sequence: z.ZodNumber;
1215
- }, z.core.$strip>;
1216
- }, z.core.$strip>;
1217
- declare const opportunityStageRegressedSchema: z.ZodObject<{
1218
- opportunityId: z.ZodString;
1219
- from: z.ZodObject<{
1220
- stageId: z.ZodString;
1221
- sequence: z.ZodNumber;
1222
- }, z.core.$strip>;
1223
- to: z.ZodObject<{
1224
- stageId: z.ZodString;
1225
- sequence: z.ZodNumber;
1226
- }, z.core.$strip>;
1227
- }, z.core.$strip>;
1228
- declare const opportunityWonSchema: z.ZodObject<{
1229
- opportunityId: z.ZodString;
1230
- amount: z.ZodOptional<z.ZodObject<{
1231
- amount: z.ZodNumber;
1232
- currency: z.ZodString;
1233
- }, z.core.$strip>>;
1234
- }, z.core.$strip>;
1235
- declare const opportunityLostSchema: z.ZodObject<{
1236
- opportunityId: z.ZodString;
1237
- lostReasonId: z.ZodOptional<z.ZodString>;
1238
- }, z.core.$strip>;
1239
- declare const opportunityAbandonedSchema: z.ZodObject<{
1240
- opportunityId: z.ZodString;
1241
- }, z.core.$strip>;
1242
- declare const pipelineCreatedSchema: z.ZodObject<{
1243
- pipelineId: z.ZodString;
1244
- }, z.core.$strip>;
1245
- declare const pipelineArchivedSchema: z.ZodObject<{
1246
- pipelineId: z.ZodString;
1247
- }, z.core.$strip>;
1248
- declare const activityPlannedSchema: z.ZodObject<{
1249
- activityId: z.ZodString;
1250
- type: z.ZodString;
1251
- subjectKind: z.ZodEnum<{
1252
- lead: "lead";
1253
- opportunity: "opportunity";
1254
- contact: "contact";
1255
- account: "account";
1256
- }>;
1257
- subjectId: z.ZodString;
1258
- }, z.core.$strip>;
1259
- declare const activityCompletedSchema: z.ZodObject<{
1260
- activityId: z.ZodString;
1261
- completedAt: z.ZodISODateTime;
1262
- }, z.core.$strip>;
1263
- declare const activityCancelledSchema: z.ZodObject<{
1264
- activityId: z.ZodString;
1265
- reason: z.ZodOptional<z.ZodString>;
1266
- }, z.core.$strip>;
1267
- declare const noteAddedSchema: z.ZodObject<{
1268
- noteId: z.ZodString;
1269
- subjectKind: z.ZodEnum<{
1270
- lead: "lead";
1271
- opportunity: "opportunity";
1272
- contact: "contact";
1273
- account: "account";
1274
- }>;
1275
- subjectId: z.ZodString;
1276
- }, z.core.$strip>;
1277
- type LeadCreatedPayload = z.infer<typeof leadCreatedSchema>;
1278
- type LeadStatusChangedPayload = z.infer<typeof leadStatusChangedSchema>;
1279
- type LeadScoredPayload = z.infer<typeof leadScoredSchema>;
1280
- type LeadConvertedPayload = z.infer<typeof leadConvertedSchema>;
1281
- type LeadDisqualifiedPayload = z.infer<typeof leadDisqualifiedSchema>;
1282
- type AccountCreatedPayload = z.infer<typeof accountCreatedSchema>;
1283
- type AccountUpdatedPayload = z.infer<typeof accountUpdatedSchema>;
1284
- type ContactCreatedPayload = z.infer<typeof contactCreatedSchema>;
1285
- type ContactUpdatedPayload = z.infer<typeof contactUpdatedSchema>;
1286
- type OpportunityCreatedPayload = z.infer<typeof opportunityCreatedSchema>;
1287
- type OpportunityStageAdvancedPayload = z.infer<typeof opportunityStageAdvancedSchema>;
1288
- type OpportunityStageRegressedPayload = z.infer<typeof opportunityStageRegressedSchema>;
1289
- type OpportunityWonPayload = z.infer<typeof opportunityWonSchema>;
1290
- type OpportunityLostPayload = z.infer<typeof opportunityLostSchema>;
1291
- type OpportunityAbandonedPayload = z.infer<typeof opportunityAbandonedSchema>;
1292
- type PipelineCreatedPayload = z.infer<typeof pipelineCreatedSchema>;
1293
- type PipelineArchivedPayload = z.infer<typeof pipelineArchivedSchema>;
1294
- type ActivityPlannedPayload = z.infer<typeof activityPlannedSchema>;
1295
- type ActivityCompletedPayload = z.infer<typeof activityCompletedSchema>;
1296
- type ActivityCancelledPayload = z.infer<typeof activityCancelledSchema>;
1297
- type NoteAddedPayload = z.infer<typeof noteAddedSchema>;
1298
- declare const LeadCreated: CrmEventDefinition<z.ZodObject<{
1299
- leadId: z.ZodString;
1300
- email: z.ZodOptional<z.ZodString>;
1301
- score: z.ZodOptional<z.ZodNumber>;
1302
- }, z.core.$strip>>;
1303
- declare const LeadStatusChanged: CrmEventDefinition<z.ZodObject<{
1304
- leadId: z.ZodString;
1305
- from: z.ZodString;
1306
- to: z.ZodString;
1307
- }, z.core.$strip>>;
1308
- declare const LeadScored: CrmEventDefinition<z.ZodObject<{
1309
- leadId: z.ZodString;
1310
- score: z.ZodNumber;
1311
- }, z.core.$strip>>;
1312
- declare const LeadConverted: CrmEventDefinition<z.ZodObject<{
1313
- leadId: z.ZodString;
1314
- contactId: z.ZodString;
1315
- accountId: z.ZodOptional<z.ZodString>;
1316
- opportunityId: z.ZodString;
1317
- }, z.core.$strip>>;
1318
- declare const LeadDisqualified: CrmEventDefinition<z.ZodObject<{
1319
- leadId: z.ZodString;
1320
- reason: z.ZodString;
1321
- }, z.core.$strip>>;
1322
- declare const AccountCreated: CrmEventDefinition<z.ZodObject<{
1323
- accountId: z.ZodString;
1324
- }, z.core.$strip>>;
1325
- declare const AccountUpdated: CrmEventDefinition<z.ZodObject<{
1326
- accountId: z.ZodString;
1327
- }, z.core.$strip>>;
1328
- declare const ContactCreated: CrmEventDefinition<z.ZodObject<{
1329
- contactId: z.ZodString;
1330
- email: z.ZodOptional<z.ZodString>;
1331
- }, z.core.$strip>>;
1332
- declare const ContactUpdated: CrmEventDefinition<z.ZodObject<{
1333
- contactId: z.ZodString;
1334
- }, z.core.$strip>>;
1335
- declare const OpportunityCreated: CrmEventDefinition<z.ZodObject<{
1336
- opportunityId: z.ZodString;
1337
- pipelineId: z.ZodString;
1338
- stageId: z.ZodString;
1339
- }, z.core.$strip>>;
1340
- declare const OpportunityStageAdvanced: CrmEventDefinition<z.ZodObject<{
1341
- opportunityId: z.ZodString;
1342
- from: z.ZodObject<{
1343
- stageId: z.ZodString;
1344
- sequence: z.ZodNumber;
1345
- }, z.core.$strip>;
1346
- to: z.ZodObject<{
1347
- stageId: z.ZodString;
1348
- sequence: z.ZodNumber;
1349
- }, z.core.$strip>;
1350
- }, z.core.$strip>>;
1351
- declare const OpportunityStageRegressed: CrmEventDefinition<z.ZodObject<{
1352
- opportunityId: z.ZodString;
1353
- from: z.ZodObject<{
1354
- stageId: z.ZodString;
1355
- sequence: z.ZodNumber;
1356
- }, z.core.$strip>;
1357
- to: z.ZodObject<{
1358
- stageId: z.ZodString;
1359
- sequence: z.ZodNumber;
1360
- }, z.core.$strip>;
1361
- }, z.core.$strip>>;
1362
- declare const OpportunityWon: CrmEventDefinition<z.ZodObject<{
1363
- opportunityId: z.ZodString;
1364
- amount: z.ZodOptional<z.ZodObject<{
1365
- amount: z.ZodNumber;
1366
- currency: z.ZodString;
1367
- }, z.core.$strip>>;
1368
- }, z.core.$strip>>;
1369
- declare const OpportunityLost: CrmEventDefinition<z.ZodObject<{
1370
- opportunityId: z.ZodString;
1371
- lostReasonId: z.ZodOptional<z.ZodString>;
1372
- }, z.core.$strip>>;
1373
- declare const OpportunityAbandoned: CrmEventDefinition<z.ZodObject<{
1374
- opportunityId: z.ZodString;
1375
- }, z.core.$strip>>;
1376
- declare const PipelineCreated: CrmEventDefinition<z.ZodObject<{
1377
- pipelineId: z.ZodString;
1378
- }, z.core.$strip>>;
1379
- declare const PipelineArchived: CrmEventDefinition<z.ZodObject<{
1380
- pipelineId: z.ZodString;
1381
- }, z.core.$strip>>;
1382
- declare const ActivityPlanned: CrmEventDefinition<z.ZodObject<{
1383
- activityId: z.ZodString;
1384
- type: z.ZodString;
1385
- subjectKind: z.ZodEnum<{
1386
- lead: "lead";
1387
- opportunity: "opportunity";
1388
- contact: "contact";
1389
- account: "account";
1390
- }>;
1391
- subjectId: z.ZodString;
1392
- }, z.core.$strip>>;
1393
- declare const ActivityCompleted: CrmEventDefinition<z.ZodObject<{
1394
- activityId: z.ZodString;
1395
- completedAt: z.ZodISODateTime;
1396
- }, z.core.$strip>>;
1397
- declare const ActivityCancelled: CrmEventDefinition<z.ZodObject<{
1398
- activityId: z.ZodString;
1399
- reason: z.ZodOptional<z.ZodString>;
1400
- }, z.core.$strip>>;
1401
- declare const NoteAdded: CrmEventDefinition<z.ZodObject<{
1402
- noteId: z.ZodString;
1403
- subjectKind: z.ZodEnum<{
1404
- lead: "lead";
1405
- opportunity: "opportunity";
1406
- contact: "contact";
1407
- account: "account";
1408
- }>;
1409
- subjectId: z.ZodString;
1410
- }, z.core.$strip>>;
1411
1457
  /**
1412
- * Every CRM event defined in the package — pass to Arc's `EventRegistry`.
1413
- * Hosts wire ONE array; the whole `crm:*` namespace becomes introspectable
1414
- * via OpenAPI and auto-validated at publish time when
1415
- * `eventPlugin({ validateMode: 'reject' })` is set.
1458
+ * Operation context threaded through every domain verb. Extends
1459
+ * primitives' `OperationContext` and narrows `IdLike` to types Mongoose
1460
+ * stores cleanly. Host-specific extras go through the inherited
1461
+ * `metadata?` field — keep the typed surface tight.
1416
1462
  */
1417
- declare const crmEventDefinitions: ReadonlyArray<CrmEventDefinition>;
1463
+ interface CrmContext extends OperationContext {
1464
+ organizationId?: string | mongoose1.Types.ObjectId;
1465
+ actorId?: string | mongoose1.Types.ObjectId;
1466
+ /** Mongo transaction session (passed through to mongokit). */
1467
+ session?: unknown;
1468
+ }
1469
+ type _ModelTypes = Model<unknown>;
1470
+ //#endregion
1471
+ //#region src/events/helpers.d.ts
1472
+ declare function createEvent$1<T>(type: string, payload: T, ctx?: CrmContext, meta?: Partial<EventMeta>): DomainEvent<T>;
1473
+ //#endregion
1474
+ //#region src/events/in-process-bus.d.ts
1475
+ declare class InProcessCrmBus implements EventTransport {
1476
+ readonly name = "in-process-crm";
1477
+ private subs;
1478
+ private logger?;
1479
+ constructor(options?: {
1480
+ logger?: CrmLogger;
1481
+ });
1482
+ publish(event: DomainEvent): Promise<void>;
1483
+ subscribe(pattern: string, handler: EventHandler): Promise<() => void>;
1484
+ close(): Promise<void>;
1485
+ }
1418
1486
  //#endregion
1419
- export { ACTIVITY_STATUSES, ACTIVITY_TRANSITIONS, ACTIVITY_TYPES, type Account, AccountCreated, type AccountCreatedPayload, type AccountFilter, type AccountRepository, AccountService, type AccountServiceConfig, AccountUpdated, type AccountUpdatedPayload, type Activity, ActivityCancelled, type ActivityCancelledPayload, ActivityCompleted, type ActivityCompletedPayload, type ActivityFilter, ActivityPlanned, type ActivityPlannedPayload, type ActivityRepository, ActivityService, type ActivityServiceConfig, type ActivityStatus, type ActivityType, CRM_EVENTS, type Clock, type CloseInput, type Contact, ContactCreated, type ContactCreatedPayload, type ContactFilter, type ContactRepository, ContactService, type ContactServiceConfig, ContactUpdated, type ContactUpdatedPayload, type ConversionRow, type ConvertLeadInput, type ConvertResult, type CreateAccountInput, type CreateActivityInput, type CreateContactInput, type CreateLeadInput, type CreateNoteInput, type CreateOpportunityInput, type CreatePipelineInput, CrmError, type CrmEventDefinition, type CrmEventName, type CrmEventPayloadOf, type CrmEventSchema, DuplicateLeadError, EmptyPipelineError, type ForecastOptions, type ForecastRow, type IdGenerator, InMemoryAccountRepository, InMemoryActivityRepository, InMemoryContactRepository, InMemoryEventTransport, InMemoryLeadRepository, InMemoryLossReasonRepository, InMemoryNoteRepository, InMemoryOpportunityRepository, InMemoryOutreachPort, InMemoryPipelineRepository, InvalidActivityTransitionError, InvalidLeadTransitionError, InvalidOpportunityTransitionError, LEAD_STATUSES, LEAD_TRANSITIONS, type Lead, LeadAlreadyConvertedError, LeadConverted, type LeadConvertedPayload, LeadCreated, type LeadCreatedPayload, LeadDisqualified, type LeadDisqualifiedPayload, type LeadFilter, type LeadRepository, LeadScored, type LeadScoredPayload, type LeadScorer, LeadService, type LeadServiceConfig, type LeadStatus, LeadStatusChanged, type LeadStatusChangedPayload, type LeadStatusEntry, type LoseInput, type LossReason, type LossReasonRepository, type MoveStageInput, NotFoundError, type Note, NoteAdded, type NoteAddedPayload, type NoteRepository, NullOutreachPort, OPPORTUNITY_STATUSES, OPPORTUNITY_TRANSITIONS, type Opportunity, OpportunityAbandoned, type OpportunityAbandonedPayload, OpportunityCreated, type OpportunityCreatedPayload, type OpportunityFilter, OpportunityLost, type OpportunityLostPayload, type OpportunityRepository, OpportunityService, type OpportunityServiceConfig, OpportunityStageAdvanced, type OpportunityStageAdvancedPayload, OpportunityStageRegressed, type OpportunityStageRegressedPayload, type OpportunityStatus, type OpportunityStatusEntry, OpportunityWon, type OpportunityWonPayload, type OutreachPort, type OutreachSendRequest, type OutreachSendResult, type Pipeline, PipelineArchived, type PipelineArchivedPayload, PipelineCreated, type PipelineCreatedPayload, type PipelineRepository, PipelineService, type PipelineServiceConfig, type RecordedEvent, type RecordedOutreach, SUBJECT_KINDS, type Stage, StageNotInPipelineError, type SubjectKind, canTransitionActivity, canTransitionLead, canTransitionOpportunity, composeScorers, createSequentialIdGenerator, crmEventDefinitions, cryptoIdGenerator, defaultLeadScorer, forecast, pipelineConversion, systemClock };
1420
- //# sourceMappingURL=index.d.ts.map
1487
+ export { IMessageAttachment as $, ContactUpdated as $n, CAMPAIGN_TRANSITIONS as $t, AccountRepository as A, SchemaBuildConfig as An, PipelineArchived as Ar, buildActivitySchema as At, OpportunityRepositoryDeps as B, ActivityOutcomeRecorded as Bn, ISubscriptionDocument as Bt, ActivityRepository as C, LEAD_TRANSITIONS as Cn, OpportunityAssigned as Cr, ILossReasonDocument as Ct, SubscriptionRepositoryDeps as D, IStageDocument as Dn, OpportunityStageAdvanced as Dr, buildNoteSchema as Dt, SubscriptionRepository as E, IPipelineDocument as En, OpportunityLost as Er, INoteDocument as Et, MergeContactInput as F, AccountEnriched as Fn, SubscriptionCreated as Fr, ActivityStatus as Ft, composeScorers as G, CampaignCompleted as Gn, SUBSCRIPTION_TRANSITIONS as Gt, LeadRepositoryDeps as H, ActivityRescheduled as Hn, buildSubscriptionSchema as Ht, CloseInput as I, AccountMerged as In, SubscriptionUnsubscribed as Ir, ActivityType as It, PipelineRepositoryDeps as J, CampaignResumed as Jn, ICampaignDocument as Jt, defaultLeadScorer as K, CampaignCreated as Kn, SubscriptionStatus as Kt, MoveStageInput as L, AccountUpdated as Ln, crmEventDefinitions as Lr, SUBJECT_KINDS as Lt, MergeAccountInput as M, tenantFieldDef as Mn, SubscriptionBounced as Mr, ACTIVITY_STATUSES as Mt, ContactRepository as N, AccountAssigned as Nn, SubscriptionComplained as Nr, ACTIVITY_TRANSITIONS as Nt, CampaignRepository as O, buildPipelineSchema as On, OpportunityStageRegressed as Or, createNoteModel as Ot, ContactRepositoryDeps as P, AccountCreated as Pn, SubscriptionConfirmed as Pr, ACTIVITY_TYPES as Pt, createCrmModels as Q, ContactMerged as Qn, CAMPAIGN_STATUSES as Qt, OpenOpportunityInput as R, ActivityCancelled as Rn, CRM_EVENTS as Rr, SubjectKind as Rt, NoteRepositoryDeps as S, LEAD_STATUSES as Sn, OpportunityAmountChanged as Sr, EmailEventKind as St, SubscribeInput as T, canTransitionLead as Tn, OpportunityExtended as Tr, createLossReasonModel as Tt, LeadInput as U, CampaignActivated as Un, createSubscriptionModel as Ut, LeadRepository as V, ActivityPlanned as Vn, ISubscriptionStatusEntry as Vt, LeadScorer as W, CampaignArchived as Wn, SUBSCRIPTION_STATUSES as Wt, CreateModelsOptions as X, ContactCreated as Xn, buildCampaignSchema as Xt, CRM_MODEL_NAMES as Y, ContactAssigned as Yn, ICampaignStatusEntry as Yt, CrmModels as Z, ContactEnriched as Zn, createCampaignModel as Zt, EmailEventRepositoryDeps as _, canTransitionOpportunity as _n, LeadStatusChanged as _r, EmailEventSubjectKind as _t, CrmEngine as a, IContactDocument as an, CrmEventSchema as ar, createConversationModel as at, LossReasonRepositoryDeps as b, buildLeadSchema as bn, NoteAdded as br, createEmailEventModel as bt, ResolvedCrmConfig as c, buildContactSchema as cn, EmailEventComplained as cr, CONVERSATION_STATUSES as ct, MessageRepositoryDeps as d, IOpportunityStatusEntry as dn, LeadAssigned as dr, ConversationStatus as dt, CampaignStatus as en, ConversationAssigned as er, IMessageDocument as et, RecordMessageInput as f, buildOpportunitySchema as fn, LeadConverted as fr, MESSAGE_DELIVERY_STATUSES as ft, EmailEventRepository as g, OpportunityStatus as gn, LeadScored as gr, EMAIL_EVENT_SUBJECT_KINDS as gt, OpenConversationInput as h, OPPORTUNITY_TRANSITIONS as hn, LeadEnriched as hr, MessageDirection as ht, CrmContext as i, createAccountModel as in, CrmEventDefinition as ir, buildConversationSchema as it, AccountRepositoryDeps as j, TenantFieldType as jn, PipelineCreated as jr, createActivityModel as jt, CampaignRepositoryDeps as k, createPipelineModel as kn, OpportunityWon as kr, IActivityDocument as kt, _ModelTypes as l, createContactModel as ln, EmailEventOpened as lr, ConversationChannel as lt, ConversationRepositoryDeps as m, OPPORTUNITY_STATUSES as mn, LeadDisqualified as mr, MessageDeliveryStatus as mt, createEvent$1 as n, IAccountDocument as nn, ConversationReopened as nr, createMessageModel as nt, CrmLogger as o, IContactInfoEmbedded as on, EmailEventBounced as or, CONVERSATION_CHANNELS as ot, ConversationRepository as p, createOpportunityModel as pn, LeadCreated as pr, MESSAGE_DIRECTIONS as pt, PipelineRepository as q, CampaignPaused as qn, canTransitionSubscription as qt, CrmConfig as r, buildAccountSchema as rn, ConversationResolved as rr, IConversationDocument as rt, CrmRepositories as s, IContactName as sn, EmailEventClicked as sr, CONVERSATION_PARTICIPANT_KINDS as st, InProcessCrmBus as t, canTransitionCampaign as tn, ConversationCreated as tr, buildMessageSchema as tt, MessageRepository as u, IOpportunityDocument as un, EmailEventUnsubscribed as ur, ConversationParticipantKind as ut, RecordEmailEventInput as v, ILeadDocument as vn, MessageReceived as vr, IEmailEventDocument as vt, ActivityRepositoryDeps as w, LeadStatus as wn, OpportunityCreated as wr, buildLossReasonSchema as wt, NoteRepository as x, createLeadModel as xn, OpportunityAbandoned as xr, EMAIL_EVENT_KINDS as xt, LossReasonRepository as y, ILeadStatusEntry as yn, MessageSent as yr, buildEmailEventSchema as yt, OpportunityRepository as z, ActivityCompleted as zn, CrmEventName as zr, canTransitionActivity as zt };