@classytic/crm 0.1.1 → 0.3.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,145 +1,305 @@
1
- import { DomainEvent, EventHandler, EventTransport } from "@classytic/primitives/events";
1
+ import { t as __exportAll } from "./chunk.js";
2
+ import * as mongoose0 from "mongoose";
3
+ import { Connection, Model, Schema, SchemaDefinitionProperty } from "mongoose";
4
+ import { PaginationConfig, PluginType, Repository } from "@classytic/mongokit";
5
+ import { DomainEvent, EventHandler, EventMeta, EventTransport } from "@classytic/primitives/events";
6
+ import * as _classytic_primitives_state_machine0 from "@classytic/primitives/state-machine";
2
7
  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";
8
+ import { StatusChangeEntry, StatusHistory, appendStatus, lastTransitionTo, latestEntry, timeInStatus } from "@classytic/primitives/status-history";
9
+ import { OperationContext } from "@classytic/primitives/context";
6
10
 
7
- //#region src/domain/entities/account.d.ts
11
+ //#region src/models/tenant-field.d.ts
12
+ type TenantFieldType = 'objectId' | 'string';
13
+ interface SchemaBuildConfig {
14
+ /** Schema type for the tenant column. Engine plumbs this everywhere. */
15
+ tenantFieldType: TenantFieldType;
16
+ }
8
17
  /**
9
- * An account (company / organization). Contacts, Opportunities, and
10
- * Activities can reference it.
18
+ * Tenant-column schema definition. Use this anywhere a model has
19
+ * `organizationId` — never write the raw type in a schema file.
11
20
  */
12
- interface Account {
13
- id: string;
21
+ declare function tenantFieldDef(config: SchemaBuildConfig): SchemaDefinitionProperty;
22
+ //#endregion
23
+ //#region src/models/pipeline.schema.d.ts
24
+ /** Mongoose document shape for a single embedded stage. */
25
+ interface IStageDocument {
26
+ _id: mongoose0.Types.ObjectId;
14
27
  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[];
28
+ /** Ascending order in the pipeline. Treated as identifier on `moveToStage`. */
29
+ sequence: number;
30
+ /** Default win-probability assigned to opportunities entering this stage. */
31
+ defaultProbability: number;
32
+ color?: string;
33
+ description?: string;
34
+ }
35
+ /** Mongoose document shape for a Pipeline. */
36
+ interface IPipelineDocument {
37
+ _id: mongoose0.Types.ObjectId;
38
+ organizationId: mongoose0.Types.ObjectId | string;
39
+ name: string;
40
+ /** Deprecates the pipeline — existing opps still move, no new ones created. */
41
+ isArchived: boolean;
42
+ /** Embedded stages, kept ordered by `sequence`. */
43
+ stages: IStageDocument[];
44
+ /** Free-form label for the sales team / motion that owns this pipeline. */
45
+ teamRef?: string;
26
46
  metadata?: Record<string, unknown>;
27
47
  createdAt: Date;
28
48
  updatedAt: Date;
29
49
  }
50
+ declare function buildPipelineSchema(config: SchemaBuildConfig): Schema<IPipelineDocument>;
51
+ declare function createPipelineModel(connection: Connection, config: SchemaBuildConfig): Model<IPipelineDocument>;
30
52
  //#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
- }
53
+ //#region src/enums/lead-status.d.ts
54
+ /**
55
+ * Lead lifecycle — from initial inquiry through conversion.
56
+ *
57
+ * new — just captured (web form, import, referral)
58
+ * contacted — SDR has reached out
59
+ * qualified — meets ICP / BANT
60
+ * converted — turned into Contact + Account + Opportunity
61
+ * disqualified — rejected (bad fit, spam, duplicate)
62
+ * nurturing — not ready now; parked for later outreach
63
+ */
64
+ declare const LEAD_STATUSES: readonly ["new", "contacted", "qualified", "converted", "disqualified", "nurturing"];
65
+ type LeadStatus = (typeof LEAD_STATUSES)[number];
66
+ declare const LEAD_TRANSITIONS: Record<LeadStatus, readonly LeadStatus[]>;
67
+ declare function canTransitionLead(from: LeadStatus, to: LeadStatus): boolean;
46
68
  //#endregion
47
- //#region src/domain/ports/clock.port.d.ts
48
- interface Clock {
49
- now(): Date;
69
+ //#region src/models/lead.schema.d.ts
70
+ interface ILeadStatusEntry {
71
+ status: LeadStatus;
72
+ occurredAt: Date;
73
+ by?: string;
74
+ note?: string;
50
75
  }
51
- declare const systemClock: Clock;
52
- //#endregion
53
- //#region src/domain/ports/id-generator.port.d.ts
54
- interface IdGenerator {
55
- next(): string;
76
+ interface ILeadDocument {
77
+ _id: mongoose0.Types.ObjectId;
78
+ organizationId: mongoose0.Types.ObjectId | string;
79
+ fullName: string;
80
+ firstName?: string;
81
+ lastName?: string;
82
+ email?: string;
83
+ phone?: string;
84
+ companyName?: string;
85
+ jobTitle?: string;
86
+ source?: string;
87
+ campaignRef?: string;
88
+ score: number;
89
+ status: LeadStatus;
90
+ statusHistory: ILeadStatusEntry[];
91
+ ownerId?: string;
92
+ tags: string[];
93
+ convertedContactId?: mongoose0.Types.ObjectId | string;
94
+ convertedAccountId?: mongoose0.Types.ObjectId | string;
95
+ convertedOpportunityId?: mongoose0.Types.ObjectId | string;
96
+ convertedAt?: Date;
97
+ disqualifyReason?: string;
98
+ metadata?: Record<string, unknown>;
99
+ createdAt: Date;
100
+ updatedAt: Date;
56
101
  }
102
+ declare function buildLeadSchema(config: SchemaBuildConfig): Schema<ILeadDocument>;
103
+ declare function createLeadModel(connection: Connection, config: SchemaBuildConfig): Model<ILeadDocument>;
57
104
  //#endregion
58
- //#region src/services/mutation-options.d.ts
105
+ //#region src/enums/opportunity-status.d.ts
59
106
  /**
60
- * Options passed to every service mutation. All fields optional — hosts
61
- * opt into the behaviours they need.
107
+ * Top-level opportunity status. Movement through pipeline STAGES
108
+ * happens while status is `open`; `won` / `lost` are terminal
109
+ * outcomes. Re-opening a lost deal is a host-level decision —
110
+ * convention is to clone the opportunity rather than revive the
111
+ * terminal record.
62
112
  */
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;
113
+ declare const OPPORTUNITY_STATUSES: readonly ["open", "won", "lost", "abandoned"];
114
+ type OpportunityStatus = (typeof OPPORTUNITY_STATUSES)[number];
115
+ declare const OPPORTUNITY_TRANSITIONS: Record<OpportunityStatus, readonly OpportunityStatus[]>;
116
+ declare function canTransitionOpportunity(from: OpportunityStatus, to: OpportunityStatus): boolean;
117
+ //#endregion
118
+ //#region src/models/opportunity.schema.d.ts
119
+ interface IOpportunityStatusEntry {
120
+ status: OpportunityStatus;
121
+ stageId?: string;
122
+ occurredAt: Date;
123
+ by?: string;
124
+ note?: string;
96
125
  }
126
+ interface IOpportunityDocument {
127
+ _id: mongoose0.Types.ObjectId;
128
+ organizationId: mongoose0.Types.ObjectId | string;
129
+ name: string;
130
+ accountId?: mongoose0.Types.ObjectId | string;
131
+ primaryContactId?: mongoose0.Types.ObjectId | string;
132
+ pipelineId: mongoose0.Types.ObjectId | string;
133
+ stageId: string;
134
+ status: OpportunityStatus;
135
+ statusHistory: IOpportunityStatusEntry[];
136
+ amount?: number;
137
+ currency: string;
138
+ probability: number;
139
+ expectedCloseAt?: Date;
140
+ closedAt?: Date;
141
+ lostReasonId?: mongoose0.Types.ObjectId | string;
142
+ ownerId?: string;
143
+ sourceLeadId?: mongoose0.Types.ObjectId | string;
144
+ tags: string[];
145
+ metadata?: Record<string, unknown>;
146
+ createdAt: Date;
147
+ updatedAt: Date;
148
+ }
149
+ declare function buildOpportunitySchema(config: SchemaBuildConfig): Schema<IOpportunityDocument>;
150
+ declare function createOpportunityModel(connection: Connection, config: SchemaBuildConfig): Model<IOpportunityDocument>;
97
151
  //#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;
152
+ //#region src/models/contact.schema.d.ts
153
+ interface IContactName {
154
+ given: string;
155
+ family: string;
156
+ middle?: string;
157
+ prefix?: string;
158
+ suffix?: string;
159
+ preferred?: string;
160
+ }
161
+ interface IContactInfoEmbedded {
162
+ email?: string;
163
+ personalEmail?: string;
164
+ phone?: string;
165
+ alternatePhone?: string;
166
+ }
167
+ interface IContactDocument {
168
+ _id: mongoose0.Types.ObjectId;
169
+ organizationId: mongoose0.Types.ObjectId | string;
170
+ name: IContactName;
171
+ contact: IContactInfoEmbedded;
172
+ jobTitle?: string;
173
+ accountId?: mongoose0.Types.ObjectId | string;
174
+ ownerId?: string;
175
+ tags: string[];
176
+ metadata?: Record<string, unknown>;
177
+ createdAt: Date;
178
+ updatedAt: Date;
111
179
  }
112
- interface CreateAccountInput {
180
+ declare function buildContactSchema(config: SchemaBuildConfig): Schema<IContactDocument>;
181
+ declare function createContactModel(connection: Connection, config: SchemaBuildConfig): Model<IContactDocument>;
182
+ //#endregion
183
+ //#region src/models/account.schema.d.ts
184
+ interface IAccountDocument {
185
+ _id: mongoose0.Types.ObjectId;
186
+ organizationId: mongoose0.Types.ObjectId | string;
113
187
  name: string;
114
188
  domain?: string;
115
189
  industry?: string;
116
190
  sizeBucket?: string;
117
- annualRevenue?: {
118
- amount: number;
119
- currency: string;
120
- };
191
+ annualRevenueAmount?: number;
192
+ annualRevenueCurrency: string;
121
193
  ownerId?: string;
122
- tags?: readonly string[];
194
+ tags: string[];
123
195
  metadata?: Record<string, unknown>;
196
+ createdAt: Date;
197
+ updatedAt: Date;
124
198
  }
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;
199
+ declare function buildAccountSchema(config: SchemaBuildConfig): Schema<IAccountDocument>;
200
+ declare function createAccountModel(connection: Connection, config: SchemaBuildConfig): Model<IAccountDocument>;
201
+ //#endregion
202
+ //#region src/enums/campaign-status.d.ts
203
+ /**
204
+ * Campaign lifecycle.
205
+ *
206
+ * draft — being built; not running, no sends, no UTM attribution counted
207
+ * active — running; participating in attribution + automation
208
+ * paused — temporarily halted; can be resumed; no sends fire
209
+ * completed — finished its run; preserved for historical attribution
210
+ * archived — soft-hidden from the default UI; still queryable
211
+ */
212
+ declare const CAMPAIGN_STATUSES: readonly ["draft", "active", "paused", "completed", "archived"];
213
+ type CampaignStatus = (typeof CAMPAIGN_STATUSES)[number];
214
+ declare const CAMPAIGN_TRANSITIONS: Record<CampaignStatus, readonly CampaignStatus[]>;
215
+ declare function canTransitionCampaign(from: CampaignStatus, to: CampaignStatus): boolean;
216
+ //#endregion
217
+ //#region src/models/campaign.schema.d.ts
218
+ interface ICampaignStatusEntry {
219
+ status: CampaignStatus;
220
+ occurredAt: Date;
221
+ by?: string;
222
+ note?: string;
140
223
  }
224
+ interface ICampaignDocument {
225
+ _id: mongoose0.Types.ObjectId;
226
+ organizationId: mongoose0.Types.ObjectId | string;
227
+ name: string;
228
+ description?: string;
229
+ status: CampaignStatus;
230
+ statusHistory: ICampaignStatusEntry[];
231
+ utmSource?: string;
232
+ utmMedium?: string;
233
+ utmCampaign?: string;
234
+ startDate?: Date;
235
+ endDate?: Date;
236
+ budgetAmount?: number;
237
+ expectedRevenueAmount?: number;
238
+ actualRevenueAmount?: number;
239
+ currency: string;
240
+ ownerId?: string;
241
+ tags: string[];
242
+ metadata?: Record<string, unknown>;
243
+ createdAt: Date;
244
+ updatedAt: Date;
245
+ }
246
+ declare function buildCampaignSchema(config: SchemaBuildConfig): Schema<ICampaignDocument>;
247
+ declare function createCampaignModel(connection: Connection, config: SchemaBuildConfig): Model<ICampaignDocument>;
141
248
  //#endregion
142
- //#region src/domain/enums/activity.d.ts
249
+ //#region src/enums/subscription-status.d.ts
250
+ /**
251
+ * Subscription lifecycle.
252
+ *
253
+ * pending — double-opt-in pending confirmation
254
+ * confirmed — actively subscribed; eligible to receive sends
255
+ * unsubscribed — user opted out via unsubscribe link (or operator action)
256
+ * bounced — provider reported hard bounce; permanent suppression
257
+ * complained — recipient hit "spam" / "report" in their email client;
258
+ * permanent suppression (CAN-SPAM + provider trust)
259
+ *
260
+ * All terminal states are non-reopenable. Re-subscribing creates a new
261
+ * Subscription row — the audit trail of unsubscribes/bounces is preserved.
262
+ */
263
+ declare const SUBSCRIPTION_STATUSES: readonly ["pending", "confirmed", "unsubscribed", "bounced", "complained"];
264
+ type SubscriptionStatus = (typeof SUBSCRIPTION_STATUSES)[number];
265
+ declare const SUBSCRIPTION_TRANSITIONS: Record<SubscriptionStatus, readonly SubscriptionStatus[]>;
266
+ declare function canTransitionSubscription(from: SubscriptionStatus, to: SubscriptionStatus): boolean;
267
+ //#endregion
268
+ //#region src/models/subscription.schema.d.ts
269
+ interface ISubscriptionStatusEntry {
270
+ status: SubscriptionStatus;
271
+ occurredAt: Date;
272
+ by?: string;
273
+ note?: string;
274
+ }
275
+ interface ISubscriptionDocument {
276
+ _id: mongoose0.Types.ObjectId;
277
+ organizationId: mongoose0.Types.ObjectId | string;
278
+ contactId: mongoose0.Types.ObjectId | string;
279
+ campaignId: mongoose0.Types.ObjectId | string;
280
+ status: SubscriptionStatus;
281
+ statusHistory: ISubscriptionStatusEntry[];
282
+ source?: string;
283
+ consentTimestamp: Date;
284
+ consentIpAddress?: string;
285
+ consentUserAgent?: string;
286
+ confirmationToken?: string;
287
+ unsubscribeToken: string;
288
+ /** Audit timestamps for compliance — set on the matching transition. */
289
+ confirmedAt?: Date;
290
+ unsubscribedAt?: Date;
291
+ unsubscribeReason?: string;
292
+ bouncedAt?: Date;
293
+ bounceReason?: string;
294
+ complainedAt?: Date;
295
+ metadata?: Record<string, unknown>;
296
+ createdAt: Date;
297
+ updatedAt: Date;
298
+ }
299
+ declare function buildSubscriptionSchema(config: SchemaBuildConfig): Schema<ISubscriptionDocument>;
300
+ declare function createSubscriptionModel(connection: Connection, config: SchemaBuildConfig): Model<ISubscriptionDocument>;
301
+ //#endregion
302
+ //#region src/enums/activity.d.ts
143
303
  /**
144
304
  * Activity type vocabulary. The package stores `type` as free-form
145
305
  * string so hosts can extend; these constants cover the common
@@ -159,414 +319,218 @@ declare function canTransitionActivity(from: ActivityStatus, to: ActivityStatus)
159
319
  declare const SUBJECT_KINDS: readonly ["lead", "opportunity", "contact", "account"];
160
320
  type SubjectKind = (typeof SUBJECT_KINDS)[number];
161
321
  //#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;
322
+ //#region src/models/activity.schema.d.ts
323
+ interface IActivityDocument {
324
+ _id: mongoose0.Types.ObjectId;
325
+ organizationId: mongoose0.Types.ObjectId | string;
326
+ type: ActivityType;
172
327
  status: ActivityStatus;
173
328
  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). */
329
+ subjectId: mongoose0.Types.ObjectId | string;
330
+ title: string;
331
+ description?: string;
182
332
  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
- */
333
+ participantIds: string[];
334
+ dueDate?: Date;
335
+ completedAt?: Date;
188
336
  externalRef?: string;
189
337
  metadata?: Record<string, unknown>;
190
338
  createdAt: Date;
191
339
  updatedAt: Date;
192
340
  }
341
+ declare function buildActivitySchema(config: SchemaBuildConfig): Schema<IActivityDocument>;
342
+ declare function createActivityModel(connection: Connection, config: SchemaBuildConfig): Model<IActivityDocument>;
193
343
  //#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;
344
+ //#region src/models/note.schema.d.ts
345
+ interface INoteDocument {
346
+ _id: mongoose0.Types.ObjectId;
347
+ organizationId: mongoose0.Types.ObjectId | string;
202
348
  subjectKind: SubjectKind;
203
- subjectId: string;
349
+ subjectId: mongoose0.Types.ObjectId | string;
204
350
  body: string;
205
351
  authorId?: string;
206
- /** Supports @-mentions, Markdown, etc. — host-interpreted. */
207
- format?: 'plain' | 'markdown';
352
+ pinned: boolean;
208
353
  metadata?: Record<string, unknown>;
209
354
  createdAt: Date;
210
355
  updatedAt: Date;
211
356
  }
357
+ declare function buildNoteSchema(config: SchemaBuildConfig): Schema<INoteDocument>;
358
+ declare function createNoteModel(connection: Connection, config: SchemaBuildConfig): Model<INoteDocument>;
212
359
  //#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';
360
+ //#region src/models/loss-reason.schema.d.ts
361
+ interface ILossReasonDocument {
362
+ _id: mongoose0.Types.ObjectId;
363
+ organizationId: mongoose0.Types.ObjectId | string;
364
+ name: string;
365
+ description?: string;
366
+ isActive: boolean;
367
+ /** Display ordering in admin UIs. */
368
+ sequence: number;
269
369
  metadata?: Record<string, unknown>;
370
+ createdAt: Date;
371
+ updatedAt: Date;
270
372
  }
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;
294
- }
373
+ declare function buildLossReasonSchema(config: SchemaBuildConfig): Schema<ILossReasonDocument>;
374
+ declare function createLossReasonModel(connection: Connection, config: SchemaBuildConfig): Model<ILossReasonDocument>;
295
375
  //#endregion
296
- //#region src/domain/entities/contact.d.ts
376
+ //#region src/enums/email-event-kind.d.ts
297
377
  /**
298
- * A person — the atom of a CRM. Contacts may belong to an Account,
299
- * but can also exist stand-alone (independent consultants, consumers).
378
+ * Email engagement event kinds, recorded against a Contact / Campaign /
379
+ * Subscription tuple as the recipient interacts with a sent message.
380
+ *
381
+ * open — recipient's mail client loaded the tracking pixel.
382
+ * Imperfect signal: image-block clients suppress it, some
383
+ * clients pre-fetch and inflate counts. Useful in aggregate.
384
+ *
385
+ * click — recipient followed a tracked link. High-signal: actual
386
+ * user action, not background fetch.
300
387
  *
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.
388
+ * bounce — provider reported delivery failed. Pair with the
389
+ * `bounce` status on Subscription for permanent suppression.
390
+ *
391
+ * complaint — recipient hit "spam" / "report" in their inbox. Pair
392
+ * with `complained` Subscription status.
393
+ *
394
+ * unsubscribe — recipient hit the unsubscribe link in the email. May
395
+ * fire BEFORE the Subscription itself is marked unsubscribed
396
+ * (the host can choose to confirm before flipping status).
397
+ *
398
+ * Open and click are "engagement signal"; bounce/complaint/unsubscribe are
399
+ * "suppression signal". The service exposes aggregates over both
400
+ * categories so consumers can compute open rate, click rate, bounce rate
401
+ * without filtering kind themselves.
304
402
  */
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.). */
403
+ declare const EMAIL_EVENT_KINDS: readonly ["open", "click", "bounce", "complaint", "unsubscribe"];
404
+ type EmailEventKind = (typeof EMAIL_EVENT_KINDS)[number];
405
+ /** Engagement (positive signal). */
406
+ //#endregion
407
+ //#region src/models/email-event.schema.d.ts
408
+ declare const EMAIL_EVENT_SUBJECT_KINDS: readonly ["contact", "subscription"];
409
+ type EmailEventSubjectKind = (typeof EMAIL_EVENT_SUBJECT_KINDS)[number];
410
+ interface IEmailEventDocument {
411
+ _id: mongoose0.Types.ObjectId;
412
+ organizationId: mongoose0.Types.ObjectId | string;
413
+ kind: EmailEventKind;
414
+ subjectKind: EmailEventSubjectKind;
415
+ subjectId: mongoose0.Types.ObjectId | string;
416
+ campaignId?: mongoose0.Types.ObjectId | string;
417
+ messageId?: string;
418
+ url?: string;
419
+ userAgent?: string;
420
+ ip?: string;
315
421
  metadata?: Record<string, unknown>;
316
422
  createdAt: Date;
317
- updatedAt: Date;
318
423
  }
424
+ declare function buildEmailEventSchema(config: SchemaBuildConfig): Schema<IEmailEventDocument>;
425
+ declare function createEmailEventModel(connection: Connection, config: SchemaBuildConfig): Model<IEmailEventDocument>;
319
426
  //#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
- }
427
+ //#region src/models/create-models.d.ts
428
+ interface CrmModels {
429
+ Pipeline: Model<IPipelineDocument>;
430
+ Lead: Model<ILeadDocument>;
431
+ Opportunity: Model<IOpportunityDocument>;
432
+ Contact: Model<IContactDocument>;
433
+ Account: Model<IAccountDocument>;
434
+ Campaign: Model<ICampaignDocument>;
435
+ Subscription: Model<ISubscriptionDocument>;
436
+ Activity: Model<IActivityDocument>;
437
+ Note: Model<INoteDocument>;
438
+ LossReason: Model<ILossReasonDocument>;
439
+ EmailEvent: Model<IEmailEventDocument>;
440
+ }
441
+ declare function createCrmModels(connection: Connection, config: SchemaBuildConfig): CrmModels;
335
442
  //#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>;
443
+ //#region src/repositories/pipeline.repository.d.ts
444
+ interface PipelineRepositoryDeps {
445
+ events: EventTransport;
353
446
  }
354
- declare class ContactService {
355
- private readonly repo;
447
+ declare class PipelineRepository extends Repository<IPipelineDocument> {
356
448
  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;
449
+ constructor(model: Model<IPipelineDocument>, plugins: PluginType[], deps: PipelineRepositoryDeps, pagination?: PaginationConfig);
450
+ /**
451
+ * Read a single embedded stage. Used by Opportunity create / move to
452
+ * validate stage membership + pick up `defaultProbability`.
453
+ */
454
+ getStage(pipelineId: string, stageId: string, ctx?: CrmContext): Promise<IStageDocument>;
455
+ /** First stage by `sequence` ascending. */
456
+ getFirstStage(pipelineId: string, ctx?: CrmContext): Promise<IStageDocument>;
457
+ /** Stage immediately after the given one. `null` at terminal stage. */
458
+ getNextStage(pipelineId: string, stageId: string, ctx?: CrmContext): Promise<IStageDocument | null>;
459
+ /** Soft-deprecate — existing opps still move; no new ones can be created. */
460
+ archive(id: string, ctx?: CrmContext): Promise<IPipelineDocument>;
369
461
  }
370
462
  //#endregion
371
- //#region src/domain/enums/lead-status.d.ts
463
+ //#region src/shared/calculators/scoring.d.ts
464
+ /** Minimal Lead shape the scorer reads. */
465
+ type LeadInput = Pick<ILeadDocument, 'email' | 'phone' | 'companyName' | 'tags' | 'jobTitle'>;
466
+ type LeadScorer = (lead: LeadInput) => number;
372
467
  /**
373
- * Lead lifecycle — from initial inquiry through conversion.
468
+ * Reference implementation. Intentionally simple — hosts read it and
469
+ * replace it.
374
470
  *
375
- * new — just captured (web form, import, referral)
376
- * contacted — SDR has reached out
377
- * qualified — meets ICP / BANT
378
- * converted — turned into Contact + Account + Opportunity
379
- * disqualified — rejected (bad fit, spam, duplicate)
380
- * nurturing — not ready now; parked for later outreach
471
+ * +20 if email is present
472
+ * +20 if phone is present
473
+ * +20 if companyName is present
474
+ * +10 per populated tag up to 40
475
+ * +10 if jobTitle hints senior (director / manager / head / chief / vp / founder)
381
476
  */
382
- declare const LEAD_STATUSES: readonly ["new", "contacted", "qualified", "converted", "disqualified", "nurturing"];
383
- type LeadStatus = (typeof LEAD_STATUSES)[number];
384
- declare const LEAD_TRANSITIONS: Record<LeadStatus, readonly LeadStatus[]>;
385
- declare function canTransitionLead(from: LeadStatus, to: LeadStatus): boolean;
477
+ declare function defaultLeadScorer(lead: LeadInput): number;
478
+ /** Compose multiple small rules into one scorer. */
479
+ declare function composeScorers(...rules: readonly LeadScorer[]): LeadScorer;
386
480
  //#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. */
397
- firstName?: string;
398
- lastName?: string;
399
- fullName: string;
400
- email?: string;
401
- phone?: string;
402
- companyName?: string;
403
- jobTitle?: string;
404
- /** How the lead arrived (`web-form`, `referral`, `event`, `ads`, …). */
405
- source?: string;
406
- /** Free-form campaign identifier — the host's campaign system owns its semantics. */
407
- 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;
414
- status: LeadStatus;
415
- statusHistory: readonly LeadStatusEntry[];
416
- ownerId?: string;
417
- tags?: readonly string[];
418
- /** On successful `convert`, links to the resulting records. */
419
- convertedContactId?: string;
420
- convertedAccountId?: string;
421
- convertedOpportunityId?: string;
422
- convertedAt?: Date;
423
- /** Disqualification detail. */
424
- disqualifyReason?: string;
425
- metadata?: Record<string, unknown>;
426
- createdAt: Date;
427
- updatedAt: Date;
481
+ //#region src/repositories/lead.repository.d.ts
482
+ interface LeadRepositoryDeps {
483
+ events: EventTransport;
484
+ /** Override the default lead scorer. */
485
+ scorer?: LeadScorer;
486
+ /** Reject `create()` when an email already exists in-tenant (default true). */
487
+ rejectDuplicates?: boolean;
428
488
  }
429
- interface LeadStatusEntry {
430
- status: LeadStatus;
431
- occurredAt: Date;
489
+ type TransitionExtras = {
432
490
  by?: string;
433
491
  note?: string;
434
- }
435
- //#endregion
436
- //#region src/domain/enums/opportunity-status.d.ts
437
- /**
438
- * Top-level opportunity status. Movement through pipeline STAGES
439
- * happens while status is `open`; `won` / `lost` are terminal
440
- * outcomes. Re-opening a lost deal is a host-level decision —
441
- * convention is to clone the opportunity rather than revive the
442
- * terminal record.
443
- */
444
- declare const OPPORTUNITY_STATUSES: readonly ["open", "won", "lost", "abandoned"];
445
- type OpportunityStatus = (typeof OPPORTUNITY_STATUSES)[number];
446
- declare const OPPORTUNITY_TRANSITIONS: Record<OpportunityStatus, readonly OpportunityStatus[]>;
447
- declare function canTransitionOpportunity(from: OpportunityStatus, to: OpportunityStatus): boolean;
448
- //#endregion
449
- //#region src/domain/entities/opportunity.d.ts
450
- /**
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.
455
- */
456
- interface Opportunity {
457
- id: string;
458
- 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;
492
+ patch?: Partial<ILeadDocument>;
493
+ };
494
+ declare class LeadRepository extends Repository<ILeadDocument> {
495
+ private readonly events;
496
+ private readonly scorer;
497
+ private readonly rejectDuplicates;
498
+ constructor(model: Model<ILeadDocument>, plugins: PluginType[], deps: LeadRepositoryDeps, pagination?: PaginationConfig);
469
499
  /**
470
- * Win probability (0-1). Defaults to the stage's probability at
471
- * every stage move; host can override for hand-tuned forecasts.
500
+ * Create a Lead. Auto-scores on insert so hosts can route immediately.
501
+ * Rejects duplicate emails inside the same tenant unless
502
+ * `rejectDuplicates: false` was set at engine boot.
472
503
  */
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;
479
- ownerId?: string;
480
- sourceLeadId?: string;
481
- tags?: readonly string[];
482
- metadata?: Record<string, unknown>;
483
- createdAt: Date;
484
- updatedAt: Date;
485
- }
486
- interface OpportunityStatusEntry {
487
- status: OpportunityStatus;
488
- stageId?: string;
489
- occurredAt: Date;
490
- by?: string;
491
- note?: string;
492
- }
493
- //#endregion
494
- //#region src/domain/ports/lead-repository.port.d.ts
495
- interface LeadFilter {
496
- status?: LeadStatus | readonly LeadStatus[];
497
- ownerId?: string;
498
- 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>;
508
- }
509
- //#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;
515
- 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>;
504
+ createLead(input: Partial<ILeadDocument>, ctx?: CrmContext): Promise<ILeadDocument>;
505
+ markContacted(id: string, extras?: TransitionExtras, ctx?: CrmContext): Promise<ILeadDocument>;
506
+ qualify(id: string, extras?: TransitionExtras, ctx?: CrmContext): Promise<ILeadDocument>;
507
+ disqualify(id: string, reason: string, extras?: Omit<TransitionExtras, 'patch'>, ctx?: CrmContext): Promise<ILeadDocument>;
508
+ nurture(id: string, extras?: TransitionExtras, ctx?: CrmContext): Promise<ILeadDocument>;
509
+ /**
510
+ * Recompute + persist the lead's score. Called from `createLead`
511
+ * automatically; hosts also call this after enrichment writes.
512
+ */
513
+ recomputeScore(id: string, ctx?: CrmContext): Promise<ILeadDocument>;
514
+ private _transition;
530
515
  }
531
516
  //#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;
517
+ //#region src/repositories/opportunity.repository.d.ts
518
+ interface OpportunityRepositoryDeps {
519
+ events: EventTransport;
520
+ /** Cross-repo reference: read embedded stages from Pipeline. */
521
+ pipelines: PipelineRepository;
559
522
  }
560
- interface CreateOpportunityInput {
523
+ interface OpenOpportunityInput {
561
524
  name: string;
562
525
  pipelineId: string;
563
- /** Starting stage — defaults to the pipeline's first stage. */
526
+ /** When omitted, opp enters the pipeline's first stage. */
564
527
  stageId?: string;
565
528
  accountId?: string;
566
529
  primaryContactId?: string;
567
530
  sourceLeadId?: string;
568
- amount?: Money;
569
- /** Override the stage default. Defaults to the stage's `defaultProbability`. */
531
+ amount?: number;
532
+ currency?: string;
533
+ /** Override the stage's `defaultProbability`. */
570
534
  probability?: number;
571
535
  expectedCloseAt?: Date;
572
536
  ownerId?: string;
@@ -575,493 +539,408 @@ interface CreateOpportunityInput {
575
539
  }
576
540
  interface MoveStageInput {
577
541
  stageId: string;
578
- /** Override probability. Defaults to the target stage's `defaultProbability`. */
579
- probability?: number;
580
542
  by?: string;
581
543
  note?: string;
544
+ /** Override the target stage's `defaultProbability`. */
545
+ probability?: number;
582
546
  }
583
547
  interface CloseInput {
584
- amount?: Money;
585
- closedAt?: Date;
586
548
  by?: string;
587
549
  note?: string;
588
- }
589
- interface LoseInput extends CloseInput {
550
+ closedAt?: Date;
551
+ amount?: number;
590
552
  lostReasonId?: string;
591
553
  }
592
- /**
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.
597
- */
598
- declare class OpportunityService {
599
- private readonly repo;
554
+ declare class OpportunityRepository extends Repository<IOpportunityDocument> {
555
+ private readonly events;
600
556
  private readonly pipelines;
557
+ constructor(model: Model<IOpportunityDocument>, plugins: PluginType[], deps: OpportunityRepositoryDeps, pagination?: PaginationConfig);
558
+ openOpportunity(input: OpenOpportunityInput, ctx?: CrmContext): Promise<IOpportunityDocument>;
559
+ /**
560
+ * Atomic stage move. CAS on `(status='open', stageId=current)` so two
561
+ * concurrent moves can't both win. Emits `stage_advanced` or
562
+ * `stage_regressed` based on `sequence` ordering of the embedded stages.
563
+ */
564
+ moveToStage(id: string, input: MoveStageInput, ctx?: CrmContext): Promise<IOpportunityDocument>;
565
+ win(id: string, input?: CloseInput, ctx?: CrmContext): Promise<IOpportunityDocument>;
566
+ lose(id: string, input?: CloseInput, ctx?: CrmContext): Promise<IOpportunityDocument>;
567
+ abandon(id: string, by?: string, note?: string, ctx?: CrmContext): Promise<IOpportunityDocument>;
568
+ private _closeStatus;
569
+ }
570
+ //#endregion
571
+ //#region src/repositories/contact.repository.d.ts
572
+ interface ContactRepositoryDeps {
573
+ events: EventTransport;
574
+ }
575
+ declare class ContactRepository extends Repository<IContactDocument> {
601
576
  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;
577
+ constructor(model: Model<IContactDocument>, plugins: PluginType[], deps: ContactRepositoryDeps, pagination?: PaginationConfig);
578
+ /**
579
+ * Create + emit `crm:contact.created`. Hosts call this instead of
580
+ * `create()` when they want the event side-effect (e.g. webhook fan-out).
581
+ */
582
+ createContact(input: Partial<IContactDocument>, ctx?: CrmContext): Promise<IContactDocument>;
617
583
  }
618
584
  //#endregion
619
- //#region src/services/scoring.d.ts
620
- /**
621
- * Pluggable lead scorer. Hosts inject whatever rubric they need —
622
- * firmographic model, in-app signals, ML prediction, external enrichment.
623
- *
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.
627
- */
628
- type LeadScorer = (lead: Lead) => number;
629
- /**
630
- * Reference implementation. Intentionally simple so hosts read it and
631
- * understand the shape, then replace it.
632
- *
633
- * +20 if email is present
634
- * +20 if phone is present
635
- * +20 if companyName is present
636
- * +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
- * ```
653
- */
654
- declare function composeScorers(...rules: readonly LeadScorer[]): LeadScorer;
585
+ //#region src/repositories/account.repository.d.ts
586
+ interface AccountRepositoryDeps {
587
+ events: EventTransport;
588
+ }
589
+ declare class AccountRepository extends Repository<IAccountDocument> {
590
+ private readonly events;
591
+ constructor(model: Model<IAccountDocument>, plugins: PluginType[], deps: AccountRepositoryDeps, pagination?: PaginationConfig);
592
+ createAccount(input: Partial<IAccountDocument>, ctx?: CrmContext): Promise<IAccountDocument>;
593
+ }
655
594
  //#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`). */
680
- scorer?: LeadScorer;
681
- /** When true (default), `create()` rejects dup emails. */
595
+ //#region src/repositories/campaign.repository.d.ts
596
+ interface CampaignRepositoryDeps {
597
+ events: EventTransport;
598
+ /** Reject `createCampaign` on duplicate names per-tenant (default true). */
682
599
  rejectDuplicates?: boolean;
683
600
  }
684
- interface CreateLeadInput {
685
- firstName?: string;
686
- lastName?: string;
687
- fullName?: string;
688
- email?: string;
689
- phone?: string;
690
- companyName?: string;
691
- jobTitle?: string;
601
+ type ExtrasWithRevenue = {
602
+ by?: string;
603
+ note?: string;
604
+ actualRevenueAmount?: number;
605
+ };
606
+ declare class CampaignRepository extends Repository<ICampaignDocument> {
607
+ private readonly events;
608
+ private readonly rejectDuplicates;
609
+ constructor(model: Model<ICampaignDocument>, plugins: PluginType[], deps: CampaignRepositoryDeps, pagination?: PaginationConfig);
610
+ createCampaign(input: Partial<ICampaignDocument>, ctx?: CrmContext): Promise<ICampaignDocument>;
611
+ activate(id: string, by?: string, ctx?: CrmContext): Promise<ICampaignDocument>;
612
+ pause(id: string, reason?: string, by?: string, ctx?: CrmContext): Promise<ICampaignDocument>;
613
+ resume(id: string, by?: string, ctx?: CrmContext): Promise<ICampaignDocument>;
614
+ complete(id: string, extras?: ExtrasWithRevenue, ctx?: CrmContext): Promise<ICampaignDocument>;
615
+ archive(id: string, by?: string, ctx?: CrmContext): Promise<ICampaignDocument>;
616
+ private _transition;
617
+ }
618
+ //#endregion
619
+ //#region src/repositories/subscription.repository.d.ts
620
+ interface SubscriptionRepositoryDeps {
621
+ events: EventTransport;
622
+ /** Default DOI policy when `subscribe` doesn't pass `requireDoubleOptIn`. */
623
+ defaultRequireDoubleOptIn?: boolean;
624
+ }
625
+ interface SubscribeInput {
626
+ contactId: string;
627
+ campaignId: string;
692
628
  source?: string;
693
- campaignRef?: string;
694
- ownerId?: string;
695
- tags?: readonly string[];
629
+ consentIpAddress?: string;
630
+ consentUserAgent?: string;
696
631
  metadata?: Record<string, unknown>;
632
+ /** Override the engine-level default. */
633
+ requireDoubleOptIn?: boolean;
697
634
  }
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;
706
- by?: string;
635
+ declare class SubscriptionRepository extends Repository<ISubscriptionDocument> {
636
+ private readonly events;
637
+ private readonly defaultRequireDoubleOptIn;
638
+ constructor(model: Model<ISubscriptionDocument>, plugins: PluginType[], deps: SubscriptionRepositoryDeps, pagination?: PaginationConfig);
639
+ subscribe(input: SubscribeInput, ctx?: CrmContext): Promise<ISubscriptionDocument>;
640
+ confirm(token: string, ctx?: CrmContext): Promise<ISubscriptionDocument>;
641
+ unsubscribe(token: string, reason?: string, ctx?: CrmContext): Promise<ISubscriptionDocument>;
642
+ markBounced(id: string, reason?: string, ctx?: CrmContext): Promise<ISubscriptionDocument>;
643
+ markComplained(id: string, ctx?: CrmContext): Promise<ISubscriptionDocument>;
644
+ private _transition;
707
645
  }
708
- interface ConvertResult {
709
- lead: Lead;
710
- contactId: string;
711
- accountId?: string;
712
- opportunityId: string;
646
+ //#endregion
647
+ //#region src/repositories/activity.repository.d.ts
648
+ interface ActivityRepositoryDeps {
649
+ events: EventTransport;
713
650
  }
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;
651
+ declare class ActivityRepository extends Repository<IActivityDocument> {
724
652
  private readonly events;
725
- private readonly outbox;
726
- private readonly clock;
727
- private readonly ids;
728
- private readonly scorer;
729
- 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;
653
+ constructor(model: Model<IActivityDocument>, plugins: PluginType[], deps: ActivityRepositoryDeps, pagination?: PaginationConfig);
654
+ plan(input: Partial<IActivityDocument>, ctx?: CrmContext): Promise<IActivityDocument>;
655
+ start(id: string, ctx?: CrmContext): Promise<IActivityDocument>;
656
+ complete(id: string, completedAt?: Date, ctx?: CrmContext): Promise<IActivityDocument>;
657
+ cancel(id: string, reason?: string, ctx?: CrmContext): Promise<IActivityDocument>;
743
658
  private _transition;
744
- private _emit;
745
659
  }
746
660
  //#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;
661
+ //#region src/repositories/note.repository.d.ts
662
+ interface NoteRepositoryDeps {
663
+ events: EventTransport;
664
+ }
665
+ declare class NoteRepository extends Repository<INoteDocument> {
666
+ private readonly events;
667
+ constructor(model: Model<INoteDocument>, plugins: PluginType[], deps: NoteRepositoryDeps, pagination?: PaginationConfig);
668
+ addNote(input: Partial<INoteDocument>, ctx?: CrmContext): Promise<INoteDocument>;
669
+ }
670
+ //#endregion
671
+ //#region src/repositories/loss-reason.repository.d.ts
672
+ interface LossReasonRepositoryDeps {
673
+ events: EventTransport;
763
674
  }
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;
772
- 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;
778
- metadata?: Record<string, unknown>;
779
- createdAt: Date;
780
- updatedAt: Date;
675
+ declare class LossReasonRepository extends Repository<ILossReasonDocument> {
676
+ private readonly events;
677
+ constructor(model: Model<ILossReasonDocument>, plugins: PluginType[], deps: LossReasonRepositoryDeps, pagination?: PaginationConfig);
678
+ /** Hide from pickers without breaking historical FKs. */
679
+ archive(id: string, ctx?: CrmContext): Promise<ILossReasonDocument>;
781
680
  }
782
681
  //#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>;
682
+ //#region src/repositories/email-event.repository.d.ts
683
+ interface EmailEventRepositoryDeps {
684
+ events: EventTransport;
789
685
  }
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;
686
+ interface RecordEmailEventInput {
687
+ subjectKind: EmailEventSubjectKind;
688
+ subjectId: string;
689
+ campaignId?: string;
690
+ messageId?: string;
691
+ url?: string;
692
+ userAgent?: string;
693
+ ip?: string;
804
694
  metadata?: Record<string, unknown>;
805
695
  }
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;
696
+ declare class EmailEventRepository extends Repository<IEmailEventDocument> {
814
697
  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;
698
+ constructor(model: Model<IEmailEventDocument>, plugins: PluginType[], deps: EmailEventRepositoryDeps, pagination?: PaginationConfig);
699
+ recordOpen(input: RecordEmailEventInput, ctx?: CrmContext): Promise<IEmailEventDocument>;
700
+ recordClick(input: RecordEmailEventInput, ctx?: CrmContext): Promise<IEmailEventDocument>;
701
+ recordBounce(input: RecordEmailEventInput, ctx?: CrmContext): Promise<IEmailEventDocument>;
702
+ recordComplaint(input: RecordEmailEventInput, ctx?: CrmContext): Promise<IEmailEventDocument>;
703
+ recordUnsubscribe(input: RecordEmailEventInput, ctx?: CrmContext): Promise<IEmailEventDocument>;
704
+ private _record;
830
705
  }
831
706
  //#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 {
707
+ //#region src/engine/engine-types.d.ts
708
+ interface CrmLogger {
709
+ info?(msg: string, meta?: Record<string, unknown>): void;
710
+ warn?(msg: string, meta?: Record<string, unknown>): void;
711
+ error?(msg: string, meta?: Record<string, unknown>): void;
712
+ debug?(msg: string, meta?: Record<string, unknown>): void;
713
+ }
714
+ interface CrmConfig {
715
+ /** Mongoose connection — package creates models on this connection. */
716
+ connection: Connection;
845
717
  /**
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.
718
+ * Tenant column shape. `'objectId'` (default) matches Better Auth orgs
719
+ * and unlocks `$lookup` / `.populate()`; `'string'` matches UUID hosts.
720
+ * Threaded into both schemas and `multiTenantPlugin` so they stay in
721
+ * sync (PACKAGE_RULES §9.2).
849
722
  */
850
- bucket?: (o: Opportunity) => string;
723
+ tenantFieldType?: TenantFieldType;
851
724
  /**
852
- * Filter opportunities before bucketing. Default includes only
853
- * `open` opportunities with `amount` and `expectedCloseAt`.
725
+ * When `true` (default), wire `multiTenantPlugin` on every repository
726
+ * with `required: true`. Set `false` only for fixtures / smoke tests
727
+ * that intentionally drop the scope.
854
728
  */
855
- include?: (o: Opportunity) => boolean;
729
+ multiTenant?: boolean;
730
+ /** Arc-compatible event transport. Default: in-process bus. */
731
+ eventTransport?: EventTransport;
732
+ /** Logger surface used by the in-process bus + repositories. */
733
+ logger?: CrmLogger;
734
+ /** Pagination defaults for every repository (mongokit). */
735
+ pagination?: PaginationConfig;
736
+ /** Extra mongokit plugins applied to every repository (after defaults). */
737
+ plugins?: PluginType[];
738
+ }
739
+ interface ResolvedCrmConfig extends SchemaBuildConfig {
740
+ connection: Connection;
741
+ tenantFieldType: TenantFieldType;
742
+ multiTenant: boolean;
743
+ logger?: CrmLogger;
744
+ pagination?: PaginationConfig;
745
+ plugins: PluginType[];
746
+ }
747
+ interface CrmRepositories {
748
+ pipeline: PipelineRepository;
749
+ lead: LeadRepository;
750
+ opportunity: OpportunityRepository;
751
+ contact: ContactRepository;
752
+ account: AccountRepository;
753
+ campaign: CampaignRepository;
754
+ subscription: SubscriptionRepository;
755
+ activity: ActivityRepository;
756
+ note: NoteRepository;
757
+ lossReason: LossReasonRepository;
758
+ emailEvent: EmailEventRepository;
759
+ }
760
+ interface CrmEngine {
761
+ /** Repositories ARE the API surface (PACKAGE_RULES §1). */
762
+ readonly repositories: Readonly<CrmRepositories>;
763
+ /** Arc-compatible event transport. */
764
+ readonly events: EventTransport;
765
+ /** Mongoose models — for arc adapter wiring. */
766
+ readonly models: Readonly<CrmModels>;
767
+ /** Resolved configuration (frozen). */
768
+ readonly config: Readonly<ResolvedCrmConfig>;
769
+ /** Release resources. Safe to call multiple times. */
770
+ dispose(): Promise<void>;
856
771
  }
857
772
  /**
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.
773
+ * Operation context threaded through every domain verb. Extends
774
+ * primitives' `OperationContext` and narrows `IdLike` to types Mongoose
775
+ * stores cleanly. Host-specific extras go through the inherited
776
+ * `metadata?` field — keep the typed surface tight.
871
777
  */
872
- interface ConversionRow {
873
- stageId: string;
874
- currentCount: number;
875
- wonFromHere: number;
876
- lostFromHere: number;
877
- conversionRate: number;
778
+ interface CrmContext extends OperationContext {
779
+ organizationId?: string | mongoose0.Types.ObjectId;
780
+ actorId?: string | mongoose0.Types.ObjectId;
781
+ /** Mongo transaction session (passed through to mongokit). */
782
+ session?: unknown;
878
783
  }
879
- declare function pipelineConversion(opportunities: readonly Opportunity[], stageIds: readonly string[]): ConversionRow[];
784
+ type _ModelTypes = Model<unknown>;
880
785
  //#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;
898
- }
786
+ //#region src/engine/create-crm.d.ts
787
+ declare function createCrm(config: CrmConfig): Promise<CrmEngine>;
899
788
  //#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>;
789
+ //#region src/repositories/create-repositories.d.ts
790
+ interface CreateCrmRepositoriesDeps {
791
+ events: EventTransport;
792
+ config: ResolvedCrmConfig;
906
793
  }
794
+ declare function createCrmRepositories(models: CrmModels, deps: CreateCrmRepositoriesDeps): CrmRepositories;
907
795
  //#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>;
796
+ //#region src/services/lead.service.d.ts
797
+ interface ConvertLeadInput {
798
+ pipelineId: string;
799
+ /** Defaults to `<companyName> — <source>` / `<fullName> — New`. */
800
+ opportunityName?: string;
801
+ /** Stored flat on the opportunity (`amount` + `currency`). */
802
+ amount?: number;
803
+ currency?: string;
804
+ expectedCloseAt?: Date;
805
+ by?: string;
927
806
  }
928
- interface OutreachSendResult {
929
- /** Opaque ref — typically the provider's message id. */
930
- providerMessageId?: string;
931
- acceptedAt: Date;
807
+ interface ConvertResult {
808
+ lead: ILeadDocument;
809
+ contact: IContactDocument;
810
+ account?: IAccountDocument;
811
+ opportunity: IOpportunityDocument;
812
+ /** @deprecated use contact._id */
813
+ contactId: string;
814
+ /** @deprecated use account._id */
815
+ accountId?: string;
816
+ /** @deprecated use opportunity._id */
817
+ opportunityId: string;
932
818
  }
933
- interface OutreachPort {
934
- send(request: OutreachSendRequest): Promise<OutreachSendResult>;
819
+ interface LeadServiceDeps {
820
+ lead: LeadRepository;
821
+ contact: ContactRepository;
822
+ account: AccountRepository;
823
+ opportunity: OpportunityRepository;
824
+ events: EventTransport;
935
825
  }
936
- //#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>;
826
+ declare class LeadService {
827
+ private readonly leads;
828
+ private readonly contacts;
829
+ private readonly accounts;
830
+ private readonly opportunities;
831
+ private readonly events;
832
+ constructor(deps: LeadServiceDeps);
833
+ /**
834
+ * Construct from an already-built CrmEngine — the host's preferred path.
835
+ * Avoids manually wiring four repositories.
836
+ */
837
+ static fromEngine(engine: CrmEngine): LeadService;
838
+ /**
839
+ * Convert a Lead into Account + Contact + Opportunity, then transition
840
+ * the lead. Wrap the call in `connection.transaction()` and pass the
841
+ * session via `ctx.session` for atomicity — all four writes commit or
842
+ * roll back together.
843
+ *
844
+ * Best-effort compensation: if a downstream write fails, already-
845
+ * created docs are deleted in reverse order. The reverse delete may
846
+ * itself fail (e.g. transient DB issues) — when transactions are
847
+ * active, prefer that path; compensation is the no-transaction
848
+ * fallback.
849
+ */
850
+ convert(leadId: string, input: ConvertLeadInput, ctx?: CrmContext): Promise<ConvertResult>;
851
+ /**
852
+ * The state-machine hop from whichever status the lead is in to
853
+ * `converted`. We do this directly (instead of calling
854
+ * `LeadRepository.qualify` first then a hypothetical `complete`)
855
+ * because the conversion is "this lead is done" regardless of where
856
+ * it started — bypassing the `qualified` waypoint is intentional when
857
+ * a Lead is converted directly from `new` (importers, webhooks).
858
+ */
859
+ private _markConverted;
948
860
  }
949
861
  //#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>;
960
- }
862
+ //#region src/events/event-constants.d.ts
863
+ /** PACKAGE_RULES rule 12: `package:resource.verb`. */
864
+ declare const CRM_EVENTS: {
865
+ readonly LEAD_CREATED: "crm:lead.created";
866
+ readonly LEAD_STATUS_CHANGED: "crm:lead.status_changed";
867
+ readonly LEAD_SCORED: "crm:lead.scored";
868
+ readonly LEAD_CONVERTED: "crm:lead.converted";
869
+ readonly LEAD_DISQUALIFIED: "crm:lead.disqualified";
870
+ readonly ACCOUNT_CREATED: "crm:account.created";
871
+ readonly ACCOUNT_UPDATED: "crm:account.updated";
872
+ readonly CONTACT_CREATED: "crm:contact.created";
873
+ readonly CONTACT_UPDATED: "crm:contact.updated";
874
+ readonly OPPORTUNITY_CREATED: "crm:opportunity.created";
875
+ readonly OPPORTUNITY_STAGE_ADVANCED: "crm:opportunity.stage_advanced";
876
+ readonly OPPORTUNITY_STAGE_REGRESSED: "crm:opportunity.stage_regressed";
877
+ readonly OPPORTUNITY_WON: "crm:opportunity.won";
878
+ readonly OPPORTUNITY_LOST: "crm:opportunity.lost";
879
+ readonly OPPORTUNITY_ABANDONED: "crm:opportunity.abandoned";
880
+ readonly PIPELINE_CREATED: "crm:pipeline.created";
881
+ readonly PIPELINE_ARCHIVED: "crm:pipeline.archived";
882
+ readonly ACTIVITY_PLANNED: "crm:activity.planned";
883
+ readonly ACTIVITY_COMPLETED: "crm:activity.completed";
884
+ readonly ACTIVITY_CANCELLED: "crm:activity.cancelled";
885
+ readonly NOTE_ADDED: "crm:note.added";
886
+ readonly CAMPAIGN_CREATED: "crm:campaign.created";
887
+ readonly CAMPAIGN_ACTIVATED: "crm:campaign.activated";
888
+ readonly CAMPAIGN_PAUSED: "crm:campaign.paused";
889
+ readonly CAMPAIGN_RESUMED: "crm:campaign.resumed";
890
+ readonly CAMPAIGN_COMPLETED: "crm:campaign.completed";
891
+ readonly CAMPAIGN_ARCHIVED: "crm:campaign.archived";
892
+ readonly SUBSCRIPTION_CREATED: "crm:subscription.created";
893
+ readonly SUBSCRIPTION_CONFIRMED: "crm:subscription.confirmed";
894
+ readonly SUBSCRIPTION_UNSUBSCRIBED: "crm:subscription.unsubscribed";
895
+ readonly SUBSCRIPTION_BOUNCED: "crm:subscription.bounced";
896
+ readonly SUBSCRIPTION_COMPLAINED: "crm:subscription.complained";
897
+ readonly EMAIL_EVENT_OPENED: "crm:email_event.opened";
898
+ readonly EMAIL_EVENT_CLICKED: "crm:email_event.clicked";
899
+ readonly EMAIL_EVENT_BOUNCED: "crm:email_event.bounced";
900
+ readonly EMAIL_EVENT_COMPLAINED: "crm:email_event.complained";
901
+ readonly EMAIL_EVENT_UNSUBSCRIBED: "crm:email_event.unsubscribed";
902
+ };
903
+ type CrmEventName = (typeof CRM_EVENTS)[keyof typeof CRM_EVENTS];
961
904
  //#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>;
973
- }
905
+ //#region src/events/helpers.d.ts
906
+ declare function createEvent<T>(type: string, payload: T, ctx?: CrmContext, meta?: Partial<EventMeta>): DomainEvent<T>;
974
907
  //#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;
908
+ //#region src/events/in-process-bus.d.ts
909
+ declare class InProcessCrmBus implements EventTransport {
910
+ readonly name = "in-process-crm";
911
+ private subs;
912
+ private logger?;
913
+ constructor(options?: {
914
+ logger?: CrmLogger;
915
+ });
981
916
  publish(event: DomainEvent): Promise<void>;
982
917
  subscribe(pattern: string, handler: EventHandler): Promise<() => void>;
918
+ close(): Promise<void>;
983
919
  }
984
920
  //#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>;
996
- }
997
- //#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>;
1007
- }
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>;
1019
- }
1020
- //#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>;
1033
- }
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>;
1044
- }
1045
- //#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>;
1058
- }
1059
- //#endregion
1060
- //#region src/adapters/in-memory/id-generator.d.ts
1061
- declare const cryptoIdGenerator: IdGenerator;
1062
- declare function createSequentialIdGenerator(prefix?: string): IdGenerator;
921
+ //#region src/core/state-machines.d.ts
922
+ /**
923
+ * Declarative state machines for every CRM aggregate that has lifecycle
924
+ * transitions. Single source of truth for what `from -> to` moves are
925
+ * legal — the repositories' domain verbs (`qualify`, `convert`, `win`,
926
+ * `lose`, `confirm`, `unsubscribe`, ...) call `assertTransition` before
927
+ * they touch Mongo.
928
+ *
929
+ * Per PACKAGE_RULES §30: "State machines map to Arc Actions". The host
930
+ * exposes each domain verb as an arc Action (`POST /:id/action` with
931
+ * `{ action: 'qualify' }`); the verb's implementation lives on the
932
+ * Repository class and uses these machines internally.
933
+ *
934
+ * Built on `@classytic/primitives/state-machine` so the same shape lights
935
+ * up across kits (mongokit's `claim()` pairing, sqlitekit follow-ups).
936
+ */
937
+ declare const LEAD_MACHINE: _classytic_primitives_state_machine0.StateMachine<"new" | "contacted" | "qualified" | "converted" | "disqualified" | "nurturing">;
938
+ declare const OPPORTUNITY_MACHINE: _classytic_primitives_state_machine0.StateMachine<"open" | "won" | "lost" | "abandoned">;
939
+ declare const CAMPAIGN_MACHINE: _classytic_primitives_state_machine0.StateMachine<"draft" | "active" | "paused" | "completed" | "archived">;
940
+ declare const SUBSCRIPTION_MACHINE: _classytic_primitives_state_machine0.StateMachine<"pending" | "confirmed" | "unsubscribed" | "bounced" | "complained">;
941
+ declare const ACTIVITY_MACHINE: _classytic_primitives_state_machine0.StateMachine<"completed" | "planned" | "in_progress" | "cancelled">;
1063
942
  //#endregion
1064
- //#region src/domain/errors.d.ts
943
+ //#region src/core/errors.d.ts
1065
944
  /**
1066
945
  * CRM error vocabulary. Hosts catch `CrmError` at API edges.
1067
946
  */
@@ -1112,309 +991,585 @@ declare class DuplicateLeadError extends CrmError {
1112
991
  declare class EmptyPipelineError extends CrmError {
1113
992
  constructor(pipelineId: string);
1114
993
  }
994
+ declare class InvalidCampaignTransitionError extends CrmError {
995
+ constructor(details: {
996
+ from: string;
997
+ to: string;
998
+ campaignId: string;
999
+ });
1000
+ }
1001
+ declare class DuplicateCampaignError extends CrmError {
1002
+ constructor(name: string);
1003
+ }
1004
+ declare class InvalidSubscriptionTransitionError extends CrmError {
1005
+ constructor(details: {
1006
+ from: string;
1007
+ to: string;
1008
+ subscriptionId: string;
1009
+ });
1010
+ }
1011
+ declare class DuplicateSubscriptionError extends CrmError {
1012
+ constructor(details: {
1013
+ contactId: string;
1014
+ campaignId: string;
1015
+ });
1016
+ }
1017
+ declare class InvalidConfirmationTokenError extends CrmError {
1018
+ constructor();
1019
+ }
1020
+ declare class InvalidUnsubscribeTokenError extends CrmError {
1021
+ constructor();
1022
+ }
1115
1023
  //#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];
1024
+ //#region src/shared/calculators/forecast.d.ts
1025
+ type OpportunityForecast = Pick<IOpportunityDocument, 'amount' | 'currency' | 'probability' | 'status' | 'expectedCloseAt' | 'stageId' | 'statusHistory'>;
1026
+ interface ForecastRow {
1027
+ /** Bucket key — ISO month `YYYY-MM` by default. */
1028
+ bucket: string;
1029
+ /** Σ amount. */
1030
+ committed: number;
1031
+ /** Σ amount × probability. */
1032
+ weighted: number;
1033
+ /** Number of opportunities in the bucket. */
1034
+ count: number;
1035
+ currency: string;
1036
+ }
1037
+ interface ForecastOptions {
1038
+ /** Bucket function. Default: ISO month from `expectedCloseAt`. */
1039
+ bucket?: (o: OpportunityForecast) => string;
1040
+ /** Filter before bucketing. Default: open + has amount + has expectedCloseAt. */
1041
+ include?: (o: OpportunityForecast) => boolean;
1042
+ }
1043
+ /**
1044
+ * Aggregate opportunities into a forecast table — one row per bucket.
1045
+ * Currency mismatch within a bucket throws — partition by currency
1046
+ * upstream when multi-currency.
1047
+ */
1048
+ declare function forecast(opportunities: readonly OpportunityForecast[], options?: ForecastOptions): ForecastRow[];
1049
+ interface ConversionRow {
1050
+ stageId: string;
1051
+ currentCount: number;
1052
+ wonFromHere: number;
1053
+ lostFromHere: number;
1054
+ conversionRate: number;
1055
+ }
1056
+ /**
1057
+ * "Where do deals die?" summary — opportunities partitioned across
1058
+ * stages with current count + won/lost-from-here.
1059
+ */
1060
+ declare function pipelineConversion(opportunities: readonly OpportunityForecast[], stageIds: readonly string[]): ConversionRow[];
1142
1061
  //#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;
1153
- }
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>;
1062
+ //#region src/validators/common.d.ts
1063
+ declare const objectIdLike: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
1064
+ declare const metadataSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1065
+ declare const tagsSchema: z.ZodOptional<z.ZodArray<z.ZodString>>;
1066
+ declare const moneyAmountSchema: z.ZodNumber;
1067
+ declare const currencyCodeSchema: z.ZodString;
1068
+ //#endregion
1069
+ //#region src/validators/pipeline.schema.d.ts
1070
+ declare const stageInputSchema: z.ZodObject<{
1071
+ name: z.ZodString;
1072
+ sequence: z.ZodNumber;
1073
+ defaultProbability: z.ZodNumber;
1074
+ color: z.ZodOptional<z.ZodString>;
1075
+ description: z.ZodOptional<z.ZodString>;
1168
1076
  }, z.core.$strip>;
1169
- declare const leadStatusChangedSchema: z.ZodObject<{
1170
- leadId: z.ZodString;
1171
- from: z.ZodString;
1172
- to: z.ZodString;
1077
+ declare const createPipelineSchema: z.ZodObject<{
1078
+ name: z.ZodString;
1079
+ stages: z.ZodDefault<z.ZodArray<z.ZodObject<{
1080
+ name: z.ZodString;
1081
+ sequence: z.ZodNumber;
1082
+ defaultProbability: z.ZodNumber;
1083
+ color: z.ZodOptional<z.ZodString>;
1084
+ description: z.ZodOptional<z.ZodString>;
1085
+ }, z.core.$strip>>>;
1086
+ teamRef: z.ZodOptional<z.ZodString>;
1087
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1173
1088
  }, z.core.$strip>;
1174
- declare const leadScoredSchema: z.ZodObject<{
1175
- leadId: z.ZodString;
1176
- score: z.ZodNumber;
1089
+ declare const updatePipelineSchema: z.ZodObject<{
1090
+ name: z.ZodOptional<z.ZodString>;
1091
+ stages: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodObject<{
1092
+ name: z.ZodString;
1093
+ sequence: z.ZodNumber;
1094
+ defaultProbability: z.ZodNumber;
1095
+ color: z.ZodOptional<z.ZodString>;
1096
+ description: z.ZodOptional<z.ZodString>;
1097
+ }, z.core.$strip>>>>;
1098
+ teamRef: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1099
+ metadata: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
1177
1100
  }, 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;
1101
+ declare const stageInputSchemaExport: z.ZodObject<{
1102
+ name: z.ZodString;
1103
+ sequence: z.ZodNumber;
1104
+ defaultProbability: z.ZodNumber;
1105
+ color: z.ZodOptional<z.ZodString>;
1106
+ description: z.ZodOptional<z.ZodString>;
1183
1107
  }, z.core.$strip>;
1184
- declare const leadDisqualifiedSchema: z.ZodObject<{
1185
- leadId: z.ZodString;
1186
- reason: z.ZodString;
1108
+ declare const pipelineFilterSchema: z.ZodObject<{
1109
+ isArchived: z.ZodOptional<z.ZodCoercedBoolean<unknown>>;
1110
+ teamRef: z.ZodOptional<z.ZodString>;
1187
1111
  }, z.core.$strip>;
1188
- declare const accountCreatedSchema: z.ZodObject<{
1189
- accountId: z.ZodString;
1112
+ type CreatePipelineDto = z.infer<typeof createPipelineSchema>;
1113
+ type UpdatePipelineDto = z.infer<typeof updatePipelineSchema>;
1114
+ type StageInputDto = z.infer<typeof stageInputSchema>;
1115
+ //#endregion
1116
+ //#region src/validators/lead.schema.d.ts
1117
+ declare const createLeadSchema: z.ZodObject<{
1118
+ fullName: z.ZodOptional<z.ZodString>;
1119
+ firstName: z.ZodOptional<z.ZodString>;
1120
+ lastName: z.ZodOptional<z.ZodString>;
1121
+ email: z.ZodOptional<z.ZodString>;
1122
+ phone: z.ZodOptional<z.ZodString>;
1123
+ companyName: z.ZodOptional<z.ZodString>;
1124
+ jobTitle: z.ZodOptional<z.ZodString>;
1125
+ source: z.ZodOptional<z.ZodString>;
1126
+ campaignRef: z.ZodOptional<z.ZodString>;
1127
+ ownerId: z.ZodOptional<z.ZodString>;
1128
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1129
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1190
1130
  }, z.core.$strip>;
1191
- declare const accountUpdatedSchema: z.ZodObject<{
1192
- accountId: z.ZodString;
1131
+ declare const updateLeadSchema: z.ZodObject<{
1132
+ fullName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1133
+ firstName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1134
+ lastName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1135
+ email: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1136
+ phone: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1137
+ companyName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1138
+ jobTitle: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1139
+ source: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1140
+ campaignRef: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1141
+ ownerId: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1142
+ tags: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
1143
+ metadata: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
1193
1144
  }, z.core.$strip>;
1194
- declare const contactCreatedSchema: z.ZodObject<{
1195
- contactId: z.ZodString;
1145
+ declare const leadFilterSchema: z.ZodObject<{
1146
+ status: z.ZodOptional<z.ZodEnum<{
1147
+ new: "new";
1148
+ contacted: "contacted";
1149
+ qualified: "qualified";
1150
+ converted: "converted";
1151
+ disqualified: "disqualified";
1152
+ nurturing: "nurturing";
1153
+ }>>;
1154
+ ownerId: z.ZodOptional<z.ZodString>;
1196
1155
  email: z.ZodOptional<z.ZodString>;
1156
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1157
+ source: z.ZodOptional<z.ZodString>;
1158
+ campaignRef: z.ZodOptional<z.ZodString>;
1159
+ scoreMin: z.ZodOptional<z.ZodNumber>;
1160
+ scoreMax: z.ZodOptional<z.ZodNumber>;
1197
1161
  }, z.core.$strip>;
1198
- declare const contactUpdatedSchema: z.ZodObject<{
1199
- contactId: z.ZodString;
1162
+ declare const convertLeadSchema: z.ZodObject<{
1163
+ pipelineId: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
1164
+ opportunityName: z.ZodOptional<z.ZodString>;
1165
+ amount: z.ZodOptional<z.ZodNumber>;
1166
+ currency: z.ZodOptional<z.ZodString>;
1167
+ expectedCloseAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1168
+ by: z.ZodOptional<z.ZodString>;
1200
1169
  }, z.core.$strip>;
1201
- declare const opportunityCreatedSchema: z.ZodObject<{
1202
- opportunityId: z.ZodString;
1203
- pipelineId: z.ZodString;
1170
+ type CreateLeadDto = z.infer<typeof createLeadSchema>;
1171
+ type UpdateLeadDto = z.infer<typeof updateLeadSchema>;
1172
+ type ConvertLeadDto = z.infer<typeof convertLeadSchema>;
1173
+ type LeadFilterDto = z.infer<typeof leadFilterSchema>;
1174
+ //#endregion
1175
+ //#region src/validators/opportunity.schema.d.ts
1176
+ declare const openOpportunitySchema: z.ZodObject<{
1177
+ name: z.ZodString;
1178
+ pipelineId: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
1179
+ stageId: z.ZodOptional<z.ZodString>;
1180
+ accountId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1181
+ primaryContactId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1182
+ sourceLeadId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1183
+ amount: z.ZodOptional<z.ZodNumber>;
1184
+ currency: z.ZodOptional<z.ZodString>;
1185
+ probability: z.ZodOptional<z.ZodNumber>;
1186
+ expectedCloseAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1187
+ ownerId: z.ZodOptional<z.ZodString>;
1188
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1189
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1190
+ }, z.core.$strip>;
1191
+ declare const moveStageSchema: z.ZodObject<{
1204
1192
  stageId: z.ZodString;
1193
+ by: z.ZodOptional<z.ZodString>;
1194
+ note: z.ZodOptional<z.ZodString>;
1195
+ probability: z.ZodOptional<z.ZodNumber>;
1205
1196
  }, 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>;
1197
+ declare const closeWinSchema: z.ZodObject<{
1198
+ by: z.ZodOptional<z.ZodString>;
1199
+ note: z.ZodOptional<z.ZodString>;
1200
+ closedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1201
+ amount: z.ZodOptional<z.ZodNumber>;
1216
1202
  }, 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;
1203
+ declare const closeLossSchema: z.ZodObject<{
1204
+ by: z.ZodOptional<z.ZodString>;
1205
+ note: z.ZodOptional<z.ZodString>;
1206
+ closedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1207
+ lostReasonId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1208
+ }, z.core.$strip>;
1209
+ declare const opportunityFilterSchema: z.ZodObject<{
1210
+ status: z.ZodOptional<z.ZodEnum<{
1211
+ open: "open";
1212
+ won: "won";
1213
+ lost: "lost";
1214
+ abandoned: "abandoned";
1215
+ }>>;
1216
+ pipelineId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1217
+ stageId: z.ZodOptional<z.ZodString>;
1218
+ ownerId: z.ZodOptional<z.ZodString>;
1219
+ accountId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1220
+ expectedCloseFrom: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1221
+ expectedCloseTo: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1222
+ }, z.core.$strip>;
1223
+ type OpenOpportunityDto = z.infer<typeof openOpportunitySchema>;
1224
+ type MoveStageDto = z.infer<typeof moveStageSchema>;
1225
+ type CloseWinDto = z.infer<typeof closeWinSchema>;
1226
+ type CloseLossDto = z.infer<typeof closeLossSchema>;
1227
+ type OpportunityFilterDto = z.infer<typeof opportunityFilterSchema>;
1228
+ //#endregion
1229
+ //#region src/validators/contact.schema.d.ts
1230
+ declare const createContactSchema: z.ZodObject<{
1231
+ name: z.ZodObject<{
1232
+ given: z.ZodString;
1233
+ family: z.ZodDefault<z.ZodString>;
1234
+ middle: z.ZodOptional<z.ZodString>;
1235
+ prefix: z.ZodOptional<z.ZodString>;
1236
+ suffix: z.ZodOptional<z.ZodString>;
1237
+ preferred: z.ZodOptional<z.ZodString>;
1226
1238
  }, z.core.$strip>;
1239
+ contact: z.ZodDefault<z.ZodObject<{
1240
+ email: z.ZodOptional<z.ZodString>;
1241
+ personalEmail: z.ZodOptional<z.ZodString>;
1242
+ phone: z.ZodOptional<z.ZodString>;
1243
+ alternatePhone: z.ZodOptional<z.ZodString>;
1244
+ }, z.core.$strip>>;
1245
+ jobTitle: z.ZodOptional<z.ZodString>;
1246
+ accountId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1247
+ ownerId: z.ZodOptional<z.ZodString>;
1248
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1249
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1227
1250
  }, 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;
1251
+ declare const updateContactSchema: z.ZodObject<{
1252
+ name: z.ZodOptional<z.ZodObject<{
1253
+ given: z.ZodString;
1254
+ family: z.ZodDefault<z.ZodString>;
1255
+ middle: z.ZodOptional<z.ZodString>;
1256
+ prefix: z.ZodOptional<z.ZodString>;
1257
+ suffix: z.ZodOptional<z.ZodString>;
1258
+ preferred: z.ZodOptional<z.ZodString>;
1233
1259
  }, z.core.$strip>>;
1260
+ contact: z.ZodOptional<z.ZodDefault<z.ZodObject<{
1261
+ email: z.ZodOptional<z.ZodString>;
1262
+ personalEmail: z.ZodOptional<z.ZodString>;
1263
+ phone: z.ZodOptional<z.ZodString>;
1264
+ alternatePhone: z.ZodOptional<z.ZodString>;
1265
+ }, z.core.$strip>>>;
1266
+ jobTitle: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1267
+ accountId: z.ZodOptional<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>>;
1268
+ ownerId: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1269
+ tags: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
1270
+ metadata: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
1271
+ }, z.core.$strip>;
1272
+ declare const contactFilterSchema: z.ZodObject<{
1273
+ accountId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1274
+ ownerId: z.ZodOptional<z.ZodString>;
1275
+ email: z.ZodOptional<z.ZodString>;
1276
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1277
+ }, z.core.$strip>;
1278
+ type CreateContactDto = z.infer<typeof createContactSchema>;
1279
+ type UpdateContactDto = z.infer<typeof updateContactSchema>;
1280
+ //#endregion
1281
+ //#region src/validators/account.schema.d.ts
1282
+ declare const createAccountSchema: z.ZodObject<{
1283
+ name: z.ZodString;
1284
+ domain: z.ZodOptional<z.ZodString>;
1285
+ industry: z.ZodOptional<z.ZodString>;
1286
+ sizeBucket: z.ZodOptional<z.ZodString>;
1287
+ annualRevenueAmount: z.ZodOptional<z.ZodNumber>;
1288
+ annualRevenueCurrency: z.ZodOptional<z.ZodString>;
1289
+ ownerId: z.ZodOptional<z.ZodString>;
1290
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1291
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1292
+ }, z.core.$strip>;
1293
+ declare const updateAccountSchema: z.ZodObject<{
1294
+ name: z.ZodOptional<z.ZodString>;
1295
+ domain: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1296
+ industry: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1297
+ sizeBucket: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1298
+ annualRevenueAmount: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
1299
+ annualRevenueCurrency: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1300
+ ownerId: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1301
+ tags: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
1302
+ metadata: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
1303
+ }, z.core.$strip>;
1304
+ declare const accountFilterSchema: z.ZodObject<{
1305
+ domain: z.ZodOptional<z.ZodString>;
1306
+ industry: z.ZodOptional<z.ZodString>;
1307
+ ownerId: z.ZodOptional<z.ZodString>;
1308
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1309
+ }, z.core.$strip>;
1310
+ type CreateAccountDto = z.infer<typeof createAccountSchema>;
1311
+ type UpdateAccountDto = z.infer<typeof updateAccountSchema>;
1312
+ //#endregion
1313
+ //#region src/validators/campaign.schema.d.ts
1314
+ declare const createCampaignSchema: z.ZodObject<{
1315
+ name: z.ZodString;
1316
+ description: z.ZodOptional<z.ZodString>;
1317
+ utmSource: z.ZodOptional<z.ZodString>;
1318
+ utmMedium: z.ZodOptional<z.ZodString>;
1319
+ utmCampaign: z.ZodOptional<z.ZodString>;
1320
+ startDate: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1321
+ endDate: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1322
+ budgetAmount: z.ZodOptional<z.ZodNumber>;
1323
+ expectedRevenueAmount: z.ZodOptional<z.ZodNumber>;
1324
+ currency: z.ZodOptional<z.ZodString>;
1325
+ ownerId: z.ZodOptional<z.ZodString>;
1326
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1327
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1328
+ }, z.core.$strip>;
1329
+ declare const updateCampaignSchema: z.ZodObject<{
1330
+ name: z.ZodOptional<z.ZodString>;
1331
+ description: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1332
+ utmSource: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1333
+ utmMedium: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1334
+ utmCampaign: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1335
+ startDate: z.ZodOptional<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
1336
+ endDate: z.ZodOptional<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
1337
+ budgetAmount: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
1338
+ expectedRevenueAmount: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
1339
+ currency: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1340
+ ownerId: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1341
+ tags: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
1342
+ metadata: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
1343
+ }, z.core.$strip>;
1344
+ declare const completeCampaignSchema: z.ZodObject<{
1345
+ by: z.ZodOptional<z.ZodString>;
1346
+ note: z.ZodOptional<z.ZodString>;
1347
+ actualRevenueAmount: z.ZodOptional<z.ZodNumber>;
1348
+ }, z.core.$strip>;
1349
+ declare const campaignFilterSchema: z.ZodObject<{
1350
+ status: z.ZodOptional<z.ZodEnum<{
1351
+ draft: "draft";
1352
+ active: "active";
1353
+ paused: "paused";
1354
+ completed: "completed";
1355
+ archived: "archived";
1356
+ }>>;
1357
+ ownerId: z.ZodOptional<z.ZodString>;
1358
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1359
+ startDateFrom: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1360
+ startDateTo: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1234
1361
  }, z.core.$strip>;
1235
- declare const opportunityLostSchema: z.ZodObject<{
1236
- opportunityId: z.ZodString;
1237
- lostReasonId: z.ZodOptional<z.ZodString>;
1362
+ type CreateCampaignDto = z.infer<typeof createCampaignSchema>;
1363
+ type UpdateCampaignDto = z.infer<typeof updateCampaignSchema>;
1364
+ type CompleteCampaignDto = z.infer<typeof completeCampaignSchema>;
1365
+ //#endregion
1366
+ //#region src/validators/subscription.schema.d.ts
1367
+ declare const subscribeSchema: z.ZodObject<{
1368
+ contactId: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
1369
+ campaignId: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
1370
+ source: z.ZodOptional<z.ZodString>;
1371
+ consentIpAddress: z.ZodOptional<z.ZodString>;
1372
+ consentUserAgent: z.ZodOptional<z.ZodString>;
1373
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1374
+ requireDoubleOptIn: z.ZodOptional<z.ZodBoolean>;
1238
1375
  }, z.core.$strip>;
1239
- declare const opportunityAbandonedSchema: z.ZodObject<{
1240
- opportunityId: z.ZodString;
1376
+ declare const confirmSubscriptionSchema: z.ZodObject<{
1377
+ token: z.ZodString;
1241
1378
  }, z.core.$strip>;
1242
- declare const pipelineCreatedSchema: z.ZodObject<{
1243
- pipelineId: z.ZodString;
1379
+ declare const unsubscribeSchema: z.ZodObject<{
1380
+ token: z.ZodString;
1381
+ reason: z.ZodOptional<z.ZodString>;
1244
1382
  }, z.core.$strip>;
1245
- declare const pipelineArchivedSchema: z.ZodObject<{
1246
- pipelineId: z.ZodString;
1383
+ declare const subscriptionFilterSchema: z.ZodObject<{
1384
+ status: z.ZodOptional<z.ZodEnum<{
1385
+ pending: "pending";
1386
+ confirmed: "confirmed";
1387
+ unsubscribed: "unsubscribed";
1388
+ bounced: "bounced";
1389
+ complained: "complained";
1390
+ }>>;
1391
+ contactId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1392
+ campaignId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1247
1393
  }, z.core.$strip>;
1248
- declare const activityPlannedSchema: z.ZodObject<{
1249
- activityId: z.ZodString;
1250
- type: z.ZodString;
1394
+ type SubscribeDto = z.infer<typeof subscribeSchema>;
1395
+ type ConfirmSubscriptionDto = z.infer<typeof confirmSubscriptionSchema>;
1396
+ type UnsubscribeDto = z.infer<typeof unsubscribeSchema>;
1397
+ //#endregion
1398
+ //#region src/validators/activity.schema.d.ts
1399
+ declare const planActivitySchema: z.ZodObject<{
1400
+ type: z.ZodEnum<{
1401
+ call: "call";
1402
+ email: "email";
1403
+ meeting: "meeting";
1404
+ task: "task";
1405
+ note: "note";
1406
+ sms: "sms";
1407
+ social: "social";
1408
+ demo: "demo";
1409
+ other: "other";
1410
+ }>;
1251
1411
  subjectKind: z.ZodEnum<{
1252
1412
  lead: "lead";
1253
1413
  opportunity: "opportunity";
1254
1414
  contact: "contact";
1255
1415
  account: "account";
1256
1416
  }>;
1257
- subjectId: z.ZodString;
1417
+ subjectId: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
1418
+ title: z.ZodString;
1419
+ description: z.ZodOptional<z.ZodString>;
1420
+ ownerId: z.ZodOptional<z.ZodString>;
1421
+ participantIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
1422
+ dueDate: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1423
+ externalRef: z.ZodOptional<z.ZodString>;
1424
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1258
1425
  }, z.core.$strip>;
1259
- declare const activityCompletedSchema: z.ZodObject<{
1260
- activityId: z.ZodString;
1261
- completedAt: z.ZodISODateTime;
1426
+ declare const updateActivitySchema: z.ZodObject<{
1427
+ type: z.ZodOptional<z.ZodEnum<{
1428
+ call: "call";
1429
+ email: "email";
1430
+ meeting: "meeting";
1431
+ task: "task";
1432
+ note: "note";
1433
+ sms: "sms";
1434
+ social: "social";
1435
+ demo: "demo";
1436
+ other: "other";
1437
+ }>>;
1438
+ subjectKind: z.ZodOptional<z.ZodEnum<{
1439
+ lead: "lead";
1440
+ opportunity: "opportunity";
1441
+ contact: "contact";
1442
+ account: "account";
1443
+ }>>;
1444
+ subjectId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1445
+ title: z.ZodOptional<z.ZodString>;
1446
+ description: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1447
+ ownerId: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1448
+ participantIds: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
1449
+ dueDate: z.ZodOptional<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
1450
+ externalRef: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1451
+ metadata: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
1262
1452
  }, z.core.$strip>;
1263
- declare const activityCancelledSchema: z.ZodObject<{
1264
- activityId: z.ZodString;
1453
+ declare const cancelActivitySchema: z.ZodObject<{
1265
1454
  reason: z.ZodOptional<z.ZodString>;
1266
1455
  }, z.core.$strip>;
1267
- declare const noteAddedSchema: z.ZodObject<{
1268
- noteId: z.ZodString;
1269
- subjectKind: z.ZodEnum<{
1456
+ declare const activityFilterSchema: z.ZodObject<{
1457
+ type: z.ZodOptional<z.ZodEnum<{
1458
+ call: "call";
1459
+ email: "email";
1460
+ meeting: "meeting";
1461
+ task: "task";
1462
+ note: "note";
1463
+ sms: "sms";
1464
+ social: "social";
1465
+ demo: "demo";
1466
+ other: "other";
1467
+ }>>;
1468
+ status: z.ZodOptional<z.ZodEnum<{
1469
+ completed: "completed";
1470
+ planned: "planned";
1471
+ in_progress: "in_progress";
1472
+ cancelled: "cancelled";
1473
+ }>>;
1474
+ subjectKind: z.ZodOptional<z.ZodEnum<{
1270
1475
  lead: "lead";
1271
1476
  opportunity: "opportunity";
1272
1477
  contact: "contact";
1273
1478
  account: "account";
1274
- }>;
1275
- subjectId: z.ZodString;
1479
+ }>>;
1480
+ subjectId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1481
+ ownerId: z.ZodOptional<z.ZodString>;
1482
+ dueDateFrom: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1483
+ dueDateTo: z.ZodOptional<z.ZodCoercedDate<unknown>>;
1276
1484
  }, 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;
1485
+ type PlanActivityDto = z.infer<typeof planActivitySchema>;
1486
+ type CancelActivityDto = z.infer<typeof cancelActivitySchema>;
1487
+ //#endregion
1488
+ //#region src/validators/note.schema.d.ts
1489
+ declare const addNoteSchema: z.ZodObject<{
1385
1490
  subjectKind: z.ZodEnum<{
1386
1491
  lead: "lead";
1387
1492
  opportunity: "opportunity";
1388
1493
  contact: "contact";
1389
1494
  account: "account";
1390
1495
  }>;
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<{
1496
+ subjectId: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
1497
+ body: z.ZodString;
1498
+ authorId: z.ZodOptional<z.ZodString>;
1499
+ pinned: z.ZodOptional<z.ZodBoolean>;
1500
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1501
+ }, z.core.$strip>;
1502
+ declare const updateNoteSchema: z.ZodObject<{
1503
+ body: z.ZodOptional<z.ZodString>;
1504
+ pinned: z.ZodOptional<z.ZodBoolean>;
1505
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1506
+ }, z.core.$strip>;
1507
+ declare const noteFilterSchema: z.ZodObject<{
1508
+ subjectKind: z.ZodOptional<z.ZodEnum<{
1404
1509
  lead: "lead";
1405
1510
  opportunity: "opportunity";
1406
1511
  contact: "contact";
1407
1512
  account: "account";
1513
+ }>>;
1514
+ subjectId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1515
+ authorId: z.ZodOptional<z.ZodString>;
1516
+ pinned: z.ZodOptional<z.ZodCoercedBoolean<unknown>>;
1517
+ }, z.core.$strip>;
1518
+ type AddNoteDto = z.infer<typeof addNoteSchema>;
1519
+ type UpdateNoteDto = z.infer<typeof updateNoteSchema>;
1520
+ //#endregion
1521
+ //#region src/validators/loss-reason.schema.d.ts
1522
+ declare const createLossReasonSchema: z.ZodObject<{
1523
+ name: z.ZodString;
1524
+ description: z.ZodOptional<z.ZodString>;
1525
+ isActive: z.ZodOptional<z.ZodBoolean>;
1526
+ sequence: z.ZodOptional<z.ZodNumber>;
1527
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1528
+ }, z.core.$strip>;
1529
+ declare const updateLossReasonSchema: z.ZodObject<{
1530
+ name: z.ZodOptional<z.ZodString>;
1531
+ description: z.ZodOptional<z.ZodOptional<z.ZodString>>;
1532
+ isActive: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
1533
+ sequence: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
1534
+ metadata: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
1535
+ }, z.core.$strip>;
1536
+ declare const lossReasonFilterSchema: z.ZodObject<{
1537
+ isActive: z.ZodOptional<z.ZodCoercedBoolean<unknown>>;
1538
+ }, z.core.$strip>;
1539
+ type CreateLossReasonDto = z.infer<typeof createLossReasonSchema>;
1540
+ type UpdateLossReasonDto = z.infer<typeof updateLossReasonSchema>;
1541
+ //#endregion
1542
+ //#region src/validators/email-event.schema.d.ts
1543
+ declare const recordEmailEventSchema: z.ZodObject<{
1544
+ subjectKind: z.ZodEnum<{
1545
+ contact: "contact";
1546
+ subscription: "subscription";
1408
1547
  }>;
1409
- subjectId: z.ZodString;
1410
- }, z.core.$strip>>;
1411
- /**
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.
1416
- */
1417
- declare const crmEventDefinitions: ReadonlyArray<CrmEventDefinition>;
1548
+ subjectId: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
1549
+ campaignId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1550
+ messageId: z.ZodOptional<z.ZodString>;
1551
+ url: z.ZodOptional<z.ZodString>;
1552
+ userAgent: z.ZodOptional<z.ZodString>;
1553
+ ip: z.ZodOptional<z.ZodString>;
1554
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1555
+ }, z.core.$strip>;
1556
+ declare const emailEventFilterSchema: z.ZodObject<{
1557
+ kind: z.ZodOptional<z.ZodEnum<{
1558
+ open: "open";
1559
+ click: "click";
1560
+ bounce: "bounce";
1561
+ complaint: "complaint";
1562
+ unsubscribe: "unsubscribe";
1563
+ }>>;
1564
+ subjectKind: z.ZodOptional<z.ZodEnum<{
1565
+ contact: "contact";
1566
+ subscription: "subscription";
1567
+ }>>;
1568
+ subjectId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1569
+ campaignId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>;
1570
+ messageId: z.ZodOptional<z.ZodString>;
1571
+ }, z.core.$strip>;
1572
+ type RecordEmailEventDto = z.infer<typeof recordEmailEventSchema>;
1418
1573
  //#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 };
1574
+ export { ACTIVITY_MACHINE, ACTIVITY_STATUSES, ACTIVITY_TRANSITIONS, ACTIVITY_TYPES, AccountRepository, AccountRepositoryDeps, ActivityRepository, ActivityRepositoryDeps, type ActivityStatus, type ActivityType, CAMPAIGN_MACHINE, CAMPAIGN_STATUSES, CAMPAIGN_TRANSITIONS, CRM_EVENTS, CampaignRepository, CampaignRepositoryDeps, type CampaignStatus, CloseInput, ContactRepository, ContactRepositoryDeps, ConversionRow, ConvertLeadInput, ConvertResult, CreateCrmRepositoriesDeps, CrmConfig, CrmContext, CrmEngine, CrmError, CrmEventName, CrmLogger, CrmModels, CrmRepositories, DuplicateCampaignError, DuplicateLeadError, DuplicateSubscriptionError, EMAIL_EVENT_KINDS, EMAIL_EVENT_SUBJECT_KINDS, type EmailEventKind, EmailEventRepository, EmailEventRepositoryDeps, EmailEventSubjectKind, EmptyPipelineError, ForecastOptions, ForecastRow, type IAccountDocument, type IActivityDocument, type ICampaignDocument, ICampaignStatusEntry, type IContactDocument, IContactInfoEmbedded, IContactName, type IEmailEventDocument, type ILeadDocument, ILeadStatusEntry, type ILossReasonDocument, type INoteDocument, type IOpportunityDocument, IOpportunityStatusEntry, type IPipelineDocument, IStageDocument, type ISubscriptionDocument, ISubscriptionStatusEntry, InProcessCrmBus, InvalidActivityTransitionError, InvalidCampaignTransitionError, InvalidConfirmationTokenError, InvalidLeadTransitionError, InvalidOpportunityTransitionError, InvalidSubscriptionTransitionError, InvalidUnsubscribeTokenError, LEAD_MACHINE, LEAD_STATUSES, LEAD_TRANSITIONS, LeadAlreadyConvertedError, LeadInput, LeadRepository, LeadRepositoryDeps, LeadScorer, LeadService, LeadServiceDeps, type LeadStatus, LossReasonRepository, LossReasonRepositoryDeps, MoveStageInput, NotFoundError, NoteRepository, NoteRepositoryDeps, OPPORTUNITY_MACHINE, OPPORTUNITY_STATUSES, OPPORTUNITY_TRANSITIONS, OpenOpportunityInput, OpportunityForecast, OpportunityRepository, OpportunityRepositoryDeps, type OpportunityStatus, PipelineRepository, PipelineRepositoryDeps, RecordEmailEventInput, ResolvedCrmConfig, SUBJECT_KINDS, SUBSCRIPTION_MACHINE, SUBSCRIPTION_STATUSES, SUBSCRIPTION_TRANSITIONS, SchemaBuildConfig, StageNotInPipelineError, type StatusChangeEntry, type StatusHistory, type SubjectKind, SubscribeInput, SubscriptionRepository, SubscriptionRepositoryDeps, type SubscriptionStatus, TenantFieldType, _ModelTypes, appendStatus, buildAccountSchema, buildActivitySchema, buildCampaignSchema, buildContactSchema, buildEmailEventSchema, buildLeadSchema, buildLossReasonSchema, buildNoteSchema, buildOpportunitySchema, buildPipelineSchema, buildSubscriptionSchema, canTransitionActivity, canTransitionCampaign, canTransitionLead, canTransitionOpportunity, canTransitionSubscription, composeScorers, createAccountModel, createActivityModel, createCampaignModel, createContactModel, createCrm, createCrmModels, createCrmRepositories, createEmailEventModel, createEvent, createLeadModel, createLossReasonModel, createNoteModel, createOpportunityModel, createPipelineModel, createSubscriptionModel, defaultLeadScorer, forecast, lastTransitionTo, latestEntry, pipelineConversion, tenantFieldDef, timeInStatus, index_d_exports as validators };
1420
1575
  //# sourceMappingURL=index.d.ts.map