@classytic/crm 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["createPrimitiveEvent"],"sources":["../src/domain/errors.ts","../src/domain/ports/clock.port.ts","../src/adapters/in-memory/id-generator.ts","../src/events/event-constants.ts","../src/events/publish.ts","../src/services/account.service.ts","../src/domain/enums/activity.ts","../src/services/activity.service.ts","../src/services/contact.service.ts","../src/domain/enums/lead-status.ts","../src/services/scoring.ts","../src/services/lead.service.ts","../src/domain/enums/opportunity-status.ts","../src/services/opportunity.service.ts","../src/services/pipeline.service.ts","../src/services/forecast.ts","../src/adapters/in-memory/account-repository.ts","../src/adapters/in-memory/activity-repository.ts","../src/adapters/in-memory/contact-repository.ts","../src/adapters/in-memory/event-transport.ts","../src/adapters/in-memory/lead-repository.ts","../src/adapters/in-memory/loss-reason-repository.ts","../src/adapters/in-memory/note-repository.ts","../src/adapters/in-memory/opportunity-repository.ts","../src/adapters/in-memory/pipeline-repository.ts","../src/adapters/in-memory/outreach-port.ts","../src/events/event-catalog.ts"],"sourcesContent":["/**\n * CRM error vocabulary. Hosts catch `CrmError` at API edges.\n */\nexport class CrmError extends Error {\n readonly code: string;\n readonly details?: Record<string, unknown>;\n constructor(code: string, message: string, details?: Record<string, unknown>) {\n super(message);\n this.name = 'CrmError';\n this.code = code;\n if (details !== undefined) this.details = details;\n }\n}\n\nexport class NotFoundError extends CrmError {\n constructor(kind: string, id: string) {\n super(`${kind.toUpperCase()}_NOT_FOUND`, `${kind} '${id}' not found`, { kind, id });\n }\n}\n\nexport class InvalidLeadTransitionError extends CrmError {\n constructor(details: { from: string; to: string; leadId: string }) {\n super(\n 'INVALID_LEAD_TRANSITION',\n `Lead ${details.leadId} cannot move from '${details.from}' to '${details.to}'`,\n details,\n );\n }\n}\n\nexport class InvalidOpportunityTransitionError extends CrmError {\n constructor(details: { from: string; to: string; opportunityId: string }) {\n super(\n 'INVALID_OPPORTUNITY_TRANSITION',\n `Opportunity ${details.opportunityId} cannot move from '${details.from}' to '${details.to}'`,\n details,\n );\n }\n}\n\nexport class InvalidActivityTransitionError extends CrmError {\n constructor(details: { from: string; to: string; activityId: string }) {\n super(\n 'INVALID_ACTIVITY_TRANSITION',\n `Activity ${details.activityId} cannot move from '${details.from}' to '${details.to}'`,\n details,\n );\n }\n}\n\nexport class StageNotInPipelineError extends CrmError {\n constructor(details: { pipelineId: string; stageId: string }) {\n super(\n 'STAGE_NOT_IN_PIPELINE',\n `Stage '${details.stageId}' is not defined on pipeline '${details.pipelineId}'`,\n details,\n );\n }\n}\n\nexport class LeadAlreadyConvertedError extends CrmError {\n constructor(leadId: string) {\n super('LEAD_ALREADY_CONVERTED', `Lead '${leadId}' is already converted`, { leadId });\n }\n}\n\nexport class DuplicateLeadError extends CrmError {\n constructor(details: { email?: string; phone?: string }) {\n super('DUPLICATE_LEAD', `A lead with this email / phone already exists`, details);\n }\n}\n\nexport class EmptyPipelineError extends CrmError {\n constructor(pipelineId: string) {\n super('EMPTY_PIPELINE', `Pipeline '${pipelineId}' has no stages`, { pipelineId });\n }\n}\n","export interface Clock {\n now(): Date;\n}\n\nexport const systemClock: Clock = { now: () => new Date() };\n","import { randomUUID } from 'node:crypto';\nimport type { IdGenerator } from '../../domain/ports/id-generator.port.js';\n\nexport const cryptoIdGenerator: IdGenerator = { next: () => randomUUID() };\n\nexport function createSequentialIdGenerator(prefix = 'id_'): IdGenerator {\n let n = 0;\n return { next: () => `${prefix}${++n}` };\n}\n","/** PACKAGE_RULES rule 12: `package:resource.verb`. */\nexport const CRM_EVENTS = {\n LEAD_CREATED: 'crm:lead.created',\n LEAD_STATUS_CHANGED: 'crm:lead.status_changed',\n LEAD_SCORED: 'crm:lead.scored',\n LEAD_CONVERTED: 'crm:lead.converted',\n LEAD_DISQUALIFIED: 'crm:lead.disqualified',\n\n ACCOUNT_CREATED: 'crm:account.created',\n ACCOUNT_UPDATED: 'crm:account.updated',\n\n CONTACT_CREATED: 'crm:contact.created',\n CONTACT_UPDATED: 'crm:contact.updated',\n\n OPPORTUNITY_CREATED: 'crm:opportunity.created',\n OPPORTUNITY_STAGE_ADVANCED: 'crm:opportunity.stage_advanced',\n OPPORTUNITY_STAGE_REGRESSED: 'crm:opportunity.stage_regressed',\n OPPORTUNITY_WON: 'crm:opportunity.won',\n OPPORTUNITY_LOST: 'crm:opportunity.lost',\n OPPORTUNITY_ABANDONED: 'crm:opportunity.abandoned',\n\n PIPELINE_CREATED: 'crm:pipeline.created',\n PIPELINE_ARCHIVED: 'crm:pipeline.archived',\n\n ACTIVITY_PLANNED: 'crm:activity.planned',\n ACTIVITY_COMPLETED: 'crm:activity.completed',\n ACTIVITY_CANCELLED: 'crm:activity.cancelled',\n\n NOTE_ADDED: 'crm:note.added',\n} as const;\n\nexport type CrmEventName = (typeof CRM_EVENTS)[keyof typeof CRM_EVENTS];\n","import {\n createEvent,\n type EventMeta,\n type EventTransport,\n} from '@classytic/primitives/events';\nimport type { OutboxStore } from '@classytic/primitives/outbox';\n\n/**\n * Emit a CRM domain event through the host's infrastructure.\n *\n * Three independent seams, all optional:\n *\n * - `transport` — `EventTransport` (Kafka / SQS / Redis / in-process).\n * When present, the event is delivered via `transport.publish(event)`.\n * - `outbox` — `OutboxStore` (any backend implementing the primitives\n * contract — a host-owned mongo `event_outbox` collection, an arc\n * `repositoryAsOutboxStore(repo)` adapter, or a custom adapter). When\n * present, the event is persisted via `outbox.save(event, { session })`\n * **before** the transport publish so durable at-least-once delivery\n * is guaranteed even when the transport is down.\n * - `session` — any DB transaction handle the host is running. Threaded\n * into `outbox.save(event, { session })` so the outbox row commits\n * atomically with the host's business write. Typed `unknown` to keep\n * crm framework-agnostic.\n *\n * Error semantics:\n *\n * - Outbox errors **propagate**. When the host wraps the whole call in\n * `session.withTransaction(...)`, an outbox-save failure will abort\n * the transaction (business write rolls back with the missing outbox\n * row) — this is the desired atomicity. For the no-session case, the\n * caller handles the error.\n * - Transport errors are **logged and swallowed**. When an outbox is\n * wired the host's relay guarantees redelivery, so a flaky transport\n * must not block the request.\n */\nexport async function emitCrmEvent<T>(\n transport: EventTransport | undefined,\n outbox: OutboxStore | undefined,\n type: string,\n payload: T,\n meta: Partial<EventMeta> = {},\n session?: unknown,\n): Promise<void> {\n if (!transport && !outbox) return;\n const event = createEvent(type, payload, {\n source: 'crm',\n ...meta,\n });\n if (outbox) {\n await outbox.save(event, session !== undefined ? { session } : {});\n }\n if (transport) {\n try {\n await transport.publish(event);\n } catch (err) {\n console.error(`[crm] events.publish failed for ${event.type}:`, err);\n }\n }\n}\n","import type { EventTransport } from '@classytic/primitives/events';\nimport type { OutboxStore } from '@classytic/primitives/outbox';\nimport type { Account } from '../domain/entities/account.js';\nimport { NotFoundError } from '../domain/errors.js';\nimport type { AccountRepository } from '../domain/ports/account-repository.port.js';\nimport type { Clock } from '../domain/ports/clock.port.js';\nimport { systemClock } from '../domain/ports/clock.port.js';\nimport type { IdGenerator } from '../domain/ports/id-generator.port.js';\nimport { cryptoIdGenerator } from '../adapters/in-memory/id-generator.js';\nimport { CRM_EVENTS } from '../events/event-constants.js';\nimport { emitCrmEvent } from '../events/publish.js';\nimport type { MutationOptions } from './mutation-options.js';\n\nexport interface AccountServiceConfig {\n repo: AccountRepository;\n events?: EventTransport | undefined;\n /**\n * Optional host-owned outbox. See `emitCrmEvent` for the dispatch contract:\n * outbox save (session-bound when `options.session` is passed) is issued\n * before the transport publish so durable at-least-once delivery works\n * even when the transport is down.\n */\n outbox?: OutboxStore | undefined;\n clock?: Clock;\n ids?: IdGenerator;\n}\n\nexport interface CreateAccountInput {\n name: string;\n domain?: string;\n industry?: string;\n sizeBucket?: string;\n annualRevenue?: { amount: number; currency: string };\n ownerId?: string;\n tags?: readonly string[];\n metadata?: Record<string, unknown>;\n}\n\nexport class AccountService {\n private readonly repo: AccountRepository;\n private readonly events: EventTransport | undefined;\n private readonly outbox: OutboxStore | undefined;\n private readonly clock: Clock;\n private readonly ids: IdGenerator;\n\n constructor(config: AccountServiceConfig) {\n this.repo = config.repo;\n this.events = config.events;\n this.outbox = config.outbox;\n this.clock = config.clock ?? systemClock;\n this.ids = config.ids ?? cryptoIdGenerator;\n }\n\n async create(input: CreateAccountInput, options: MutationOptions = {}): Promise<Account> {\n const account = await this.repo.create({\n id: this.ids.next(),\n name: input.name,\n ...(input.domain !== undefined ? { domain: input.domain } : {}),\n ...(input.industry !== undefined ? { industry: input.industry } : {}),\n ...(input.sizeBucket !== undefined ? { sizeBucket: input.sizeBucket } : {}),\n ...(input.annualRevenue !== undefined ? { annualRevenue: input.annualRevenue } : {}),\n ...(input.ownerId !== undefined ? { ownerId: input.ownerId } : {}),\n ...(input.tags !== undefined ? { tags: input.tags } : {}),\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n });\n if (options.emitEvents !== false) {\n await this._emit(CRM_EVENTS.ACCOUNT_CREATED, { accountId: account.id }, { resource: 'account', resourceId: account.id }, options);\n }\n return account;\n }\n\n async update(id: string, patch: Partial<Account>, options: MutationOptions = {}): Promise<Account> {\n await this._require(id);\n const updated = await this.repo.update(id, patch);\n if (options.emitEvents !== false) {\n await this._emit(CRM_EVENTS.ACCOUNT_UPDATED, { accountId: id }, { resource: 'account', resourceId: id }, options);\n }\n return updated;\n }\n\n async delete(id: string): Promise<void> {\n await this._require(id);\n await this.repo.delete(id);\n }\n\n findById(id: string): Promise<Account | null> {\n return this.repo.findById(id);\n }\n\n findByDomain(domain: string): Promise<Account | null> {\n return this.repo.findByDomain(domain);\n }\n\n list(filter?: Parameters<AccountRepository['list']>[0]): Promise<Account[]> {\n return this.repo.list(filter);\n }\n\n private async _require(id: string): Promise<Account> {\n const a = await this.repo.findById(id);\n if (!a) throw new NotFoundError('account', id);\n return a;\n }\n\n private async _emit(\n event: string,\n payload: unknown,\n meta: { resource?: string; resourceId?: string } | undefined,\n options: MutationOptions = {},\n ): Promise<void> {\n await emitCrmEvent(this.events, this.outbox, event, payload, meta, options.session);\n }\n}\n","/**\n * Activity type vocabulary. The package stores `type` as free-form\n * string so hosts can extend; these constants cover the common\n * channels that automation tooling typically keys off.\n */\nexport const ACTIVITY_TYPES = [\n 'call',\n 'email',\n 'meeting',\n 'task',\n 'note',\n 'sms',\n 'social',\n 'demo',\n 'other',\n] as const;\nexport type ActivityType = (typeof ACTIVITY_TYPES)[number];\n\nexport const ACTIVITY_STATUSES = ['planned', 'in_progress', 'completed', 'cancelled'] as const;\nexport type ActivityStatus = (typeof ACTIVITY_STATUSES)[number];\n\nexport const ACTIVITY_TRANSITIONS: Record<ActivityStatus, readonly ActivityStatus[]> = {\n planned: ['in_progress', 'completed', 'cancelled'],\n in_progress: ['completed', 'cancelled'],\n completed: [],\n cancelled: [],\n};\n\nexport function canTransitionActivity(from: ActivityStatus, to: ActivityStatus): boolean {\n return ACTIVITY_TRANSITIONS[from].includes(to);\n}\n\n/**\n * Kinds of entity an Activity can reference. Polymorphic linking lets\n * hosts log a call against a Lead, an Opportunity, a Contact, or a\n * stand-alone Account.\n */\nexport const SUBJECT_KINDS = ['lead', 'opportunity', 'contact', 'account'] as const;\nexport type SubjectKind = (typeof SUBJECT_KINDS)[number];\n","import type { EventTransport } from '@classytic/primitives/events';\nimport type { OutboxStore } from '@classytic/primitives/outbox';\nimport type { Activity } from '../domain/entities/activity.js';\nimport type { Note } from '../domain/entities/note.js';\nimport {\n canTransitionActivity,\n type ActivityStatus,\n type ActivityType,\n type SubjectKind,\n} from '../domain/enums/activity.js';\nimport {\n InvalidActivityTransitionError,\n NotFoundError,\n} from '../domain/errors.js';\nimport type { ActivityRepository } from '../domain/ports/activity-repository.port.js';\nimport type { Clock } from '../domain/ports/clock.port.js';\nimport { systemClock } from '../domain/ports/clock.port.js';\nimport type { IdGenerator } from '../domain/ports/id-generator.port.js';\nimport type { NoteRepository } from '../domain/ports/note-repository.port.js';\nimport { cryptoIdGenerator } from '../adapters/in-memory/id-generator.js';\nimport { CRM_EVENTS } from '../events/event-constants.js';\nimport { emitCrmEvent } from '../events/publish.js';\nimport type { MutationOptions } from './mutation-options.js';\n\nexport interface ActivityServiceConfig {\n repo: ActivityRepository;\n notes: NoteRepository;\n events?: EventTransport | undefined;\n /** Optional host-owned outbox. See AccountServiceConfig.outbox. */\n outbox?: OutboxStore | undefined;\n clock?: Clock;\n ids?: IdGenerator;\n}\n\nexport interface CreateActivityInput {\n type: ActivityType | string;\n subjectKind: SubjectKind;\n subjectId: string;\n subject?: string;\n body?: string;\n scheduledAt?: Date;\n ownerId?: string;\n participantIds?: readonly string[];\n externalRef?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface CreateNoteInput {\n subjectKind: SubjectKind;\n subjectId: string;\n body: string;\n authorId?: string;\n format?: 'plain' | 'markdown';\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Polymorphic activity / note service. Activities have a lifecycle\n * (`planned → in_progress → completed | cancelled`); notes are\n * immutable-ish (create + update body, no status).\n */\nexport class ActivityService {\n private readonly repo: ActivityRepository;\n private readonly noteRepo: NoteRepository;\n private readonly events: EventTransport | undefined;\n private readonly outbox: OutboxStore | undefined;\n private readonly clock: Clock;\n private readonly ids: IdGenerator;\n\n constructor(config: ActivityServiceConfig) {\n this.repo = config.repo;\n this.noteRepo = config.notes;\n this.events = config.events;\n this.outbox = config.outbox;\n this.clock = config.clock ?? systemClock;\n this.ids = config.ids ?? cryptoIdGenerator;\n }\n\n async plan(input: CreateActivityInput, options: MutationOptions = {}): Promise<Activity> {\n const activity = await this.repo.create({\n id: this.ids.next(),\n type: input.type,\n status: 'planned',\n subjectKind: input.subjectKind,\n subjectId: input.subjectId,\n ...(input.subject !== undefined ? { subject: input.subject } : {}),\n ...(input.body !== undefined ? { body: input.body } : {}),\n ...(input.scheduledAt !== undefined ? { scheduledAt: input.scheduledAt } : {}),\n ...(input.ownerId !== undefined ? { ownerId: input.ownerId } : {}),\n ...(input.participantIds !== undefined\n ? { participantIds: input.participantIds }\n : {}),\n ...(input.externalRef !== undefined ? { externalRef: input.externalRef } : {}),\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n });\n await this._emit(CRM_EVENTS.ACTIVITY_PLANNED, {\n activityId: activity.id,\n type: activity.type,\n subjectKind: activity.subjectKind,\n subjectId: activity.subjectId,\n }, { resource: 'activity', resourceId: activity.id }, options);\n return activity;\n }\n\n async start(id: string, options: MutationOptions = {}): Promise<Activity> {\n return this._transition(id, 'in_progress', {}, options);\n }\n\n async complete(id: string, completedAt?: Date, options: MutationOptions = {}): Promise<Activity> {\n const now = completedAt ?? this.clock.now();\n const updated = await this._transition(id, 'completed', { completedAt: now }, options);\n await this._emit(CRM_EVENTS.ACTIVITY_COMPLETED, {\n activityId: id,\n completedAt: now,\n }, { resource: 'activity', resourceId: id }, options);\n return updated;\n }\n\n async cancel(id: string, reason?: string, options: MutationOptions = {}): Promise<Activity> {\n const now = this.clock.now();\n const patch: Partial<Activity> = { cancelledAt: now };\n if (reason !== undefined) {\n patch.metadata = { ...(patch.metadata ?? {}), cancelReason: reason };\n }\n const updated = await this._transition(id, 'cancelled', patch, options);\n await this._emit(CRM_EVENTS.ACTIVITY_CANCELLED, { activityId: id, reason }, { resource: 'activity', resourceId: id }, options);\n return updated;\n }\n\n async addNote(input: CreateNoteInput, options: MutationOptions = {}): Promise<Note> {\n const note = await this.noteRepo.create({\n id: this.ids.next(),\n subjectKind: input.subjectKind,\n subjectId: input.subjectId,\n body: input.body,\n format: input.format ?? 'plain',\n ...(input.authorId !== undefined ? { authorId: input.authorId } : {}),\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n });\n await this._emit(CRM_EVENTS.NOTE_ADDED, {\n noteId: note.id,\n subjectKind: note.subjectKind,\n subjectId: note.subjectId,\n }, { resource: 'note', resourceId: note.id }, options);\n return note;\n }\n\n listBySubject(subjectKind: SubjectKind, subjectId: string): Promise<Activity[]> {\n return this.repo.list({ subjectKind, subjectId });\n }\n\n listNotesBySubject(subjectKind: SubjectKind, subjectId: string): Promise<Note[]> {\n return this.noteRepo.listBySubject(subjectKind, subjectId);\n }\n\n findById(id: string): Promise<Activity | null> {\n return this.repo.findById(id);\n }\n\n // ── Internals ─────────────────────────────────────────────────────\n\n private async _transition(\n id: string,\n target: ActivityStatus,\n patch: Partial<Activity> = {},\n _options: MutationOptions = {},\n ): Promise<Activity> {\n const existing = await this.repo.findById(id);\n if (!existing) throw new NotFoundError('activity', id);\n if (!canTransitionActivity(existing.status, target)) {\n throw new InvalidActivityTransitionError({\n from: existing.status,\n to: target,\n activityId: id,\n });\n }\n const updated = await this.repo.casStatus(id, existing.status, target, patch);\n if (!updated) {\n throw new InvalidActivityTransitionError({\n from: existing.status,\n to: target,\n activityId: id,\n });\n }\n return updated;\n }\n\n private async _emit(\n event: string,\n payload: unknown,\n meta: { resource?: string; resourceId?: string } | undefined,\n options: MutationOptions = {},\n ): Promise<void> {\n await emitCrmEvent(this.events, this.outbox, event, payload, meta, options.session);\n }\n}\n","import type { EventTransport } from '@classytic/primitives/events';\nimport type { OutboxStore } from '@classytic/primitives/outbox';\nimport type { ContactInfo, PersonName } from '@classytic/primitives/person';\nimport type { Contact } from '../domain/entities/contact.js';\nimport { NotFoundError } from '../domain/errors.js';\nimport type { Clock } from '../domain/ports/clock.port.js';\nimport { systemClock } from '../domain/ports/clock.port.js';\nimport type { ContactRepository } from '../domain/ports/contact-repository.port.js';\nimport type { IdGenerator } from '../domain/ports/id-generator.port.js';\nimport { cryptoIdGenerator } from '../adapters/in-memory/id-generator.js';\nimport { CRM_EVENTS } from '../events/event-constants.js';\nimport { emitCrmEvent } from '../events/publish.js';\nimport type { MutationOptions } from './mutation-options.js';\n\nexport interface ContactServiceConfig {\n repo: ContactRepository;\n events?: EventTransport | undefined;\n /** Optional host-owned outbox. See AccountServiceConfig.outbox. */\n outbox?: OutboxStore | undefined;\n clock?: Clock;\n ids?: IdGenerator;\n}\n\nexport interface CreateContactInput {\n name: PersonName;\n contact?: ContactInfo;\n jobTitle?: string;\n accountId?: string;\n ownerId?: string;\n tags?: readonly string[];\n metadata?: Record<string, unknown>;\n}\n\nexport class ContactService {\n private readonly repo: ContactRepository;\n private readonly events: EventTransport | undefined;\n private readonly outbox: OutboxStore | undefined;\n private readonly clock: Clock;\n private readonly ids: IdGenerator;\n\n constructor(config: ContactServiceConfig) {\n this.repo = config.repo;\n this.events = config.events;\n this.outbox = config.outbox;\n this.clock = config.clock ?? systemClock;\n this.ids = config.ids ?? cryptoIdGenerator;\n }\n\n async create(input: CreateContactInput, options: MutationOptions = {}): Promise<Contact> {\n const contact = await this.repo.create({\n id: this.ids.next(),\n name: input.name,\n contact: input.contact ?? {},\n ...(input.jobTitle !== undefined ? { jobTitle: input.jobTitle } : {}),\n ...(input.accountId !== undefined ? { accountId: input.accountId } : {}),\n ...(input.ownerId !== undefined ? { ownerId: input.ownerId } : {}),\n ...(input.tags !== undefined ? { tags: input.tags } : {}),\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n });\n if (options.emitEvents !== false) {\n await this._emit(\n CRM_EVENTS.CONTACT_CREATED,\n {\n contactId: contact.id,\n email: contact.contact.email,\n },\n { resource: 'contact', resourceId: contact.id },\n options,\n );\n }\n return contact;\n }\n\n async update(id: string, patch: Partial<Contact>, options: MutationOptions = {}): Promise<Contact> {\n await this._require(id);\n const updated = await this.repo.update(id, patch);\n if (options.emitEvents !== false) {\n await this._emit(CRM_EVENTS.CONTACT_UPDATED, { contactId: id }, { resource: 'contact', resourceId: id }, options);\n }\n return updated;\n }\n\n async delete(id: string): Promise<void> {\n await this._require(id);\n await this.repo.delete(id);\n }\n\n findById(id: string): Promise<Contact | null> {\n return this.repo.findById(id);\n }\n\n findByEmail(email: string): Promise<Contact | null> {\n return this.repo.findByEmail(email);\n }\n\n list(filter?: Parameters<ContactRepository['list']>[0]): Promise<Contact[]> {\n return this.repo.list(filter);\n }\n\n private async _require(id: string): Promise<Contact> {\n const c = await this.repo.findById(id);\n if (!c) throw new NotFoundError('contact', id);\n return c;\n }\n\n private async _emit(\n event: string,\n payload: unknown,\n meta: { resource?: string; resourceId?: string } | undefined,\n options: MutationOptions = {},\n ): Promise<void> {\n await emitCrmEvent(this.events, this.outbox, event, payload, meta, options.session);\n }\n}\n","/**\n * Lead lifecycle — from initial inquiry through conversion.\n *\n * new — just captured (web form, import, referral)\n * contacted — SDR has reached out\n * qualified — meets ICP / BANT\n * converted — turned into Contact + Account + Opportunity\n * disqualified — rejected (bad fit, spam, duplicate)\n * nurturing — not ready now; parked for later outreach\n */\nexport const LEAD_STATUSES = [\n 'new',\n 'contacted',\n 'qualified',\n 'converted',\n 'disqualified',\n 'nurturing',\n] as const;\nexport type LeadStatus = (typeof LEAD_STATUSES)[number];\n\nexport const LEAD_TRANSITIONS: Record<LeadStatus, readonly LeadStatus[]> = {\n new: ['contacted', 'qualified', 'disqualified', 'nurturing'],\n contacted: ['qualified', 'disqualified', 'nurturing'],\n qualified: ['converted', 'disqualified', 'nurturing'],\n nurturing: ['contacted', 'qualified', 'disqualified'],\n converted: [],\n disqualified: [],\n};\n\nexport function canTransitionLead(from: LeadStatus, to: LeadStatus): boolean {\n return LEAD_TRANSITIONS[from].includes(to);\n}\n","import type { Lead } from '../domain/entities/lead.js';\n\n/**\n * Pluggable lead scorer. Hosts inject whatever rubric they need —\n * firmographic model, in-app signals, ML prediction, external enrichment.\n *\n * Stay pure: the scorer must not mutate anything, not hit I/O, and\n * be deterministic given its input. Side-effects belong in the\n * caller — `LeadService.recomputeScore` runs the scorer then persists.\n */\nexport type LeadScorer = (lead: Lead) => number;\n\n/**\n * Reference implementation. Intentionally simple so hosts read it and\n * understand the shape, then replace it.\n *\n * +20 if email is present\n * +20 if phone is present\n * +20 if companyName is present\n * +10 per populated tag up to 40\n * +10 if jobTitle looks director/manager/head (case-insensitive)\n */\nexport function defaultLeadScorer(lead: Lead): number {\n let score = 0;\n if (lead.email) score += 20;\n if (lead.phone) score += 20;\n if (lead.companyName) score += 20;\n score += Math.min(40, (lead.tags?.length ?? 0) * 10);\n if (lead.jobTitle && /(director|manager|head|chief|vp|founder)/i.test(lead.jobTitle)) {\n score += 10;\n }\n return score;\n}\n\n/**\n * Compose a scoring pipeline — each rule returns an increment\n * (positive or negative). Useful when host logic is a bunch of\n * small, testable rules.\n *\n * @example\n * ```ts\n * const scorer = composeScorers(\n * (l) => (l.email ? 20 : 0),\n * (l) => (l.source === 'referral' ? 30 : 0),\n * (l) => (l.tags?.includes('enterprise') ? 50 : 0),\n * );\n * ```\n */\nexport function composeScorers(...rules: readonly LeadScorer[]): LeadScorer {\n return (lead) => rules.reduce((acc, r) => acc + r(lead), 0);\n}\n","import type { EventTransport } from '@classytic/primitives/events';\nimport type { OutboxStore } from '@classytic/primitives/outbox';\nimport type { Money } from '@classytic/primitives/money';\nimport type { Account } from '../domain/entities/account.js';\nimport type { Contact } from '../domain/entities/contact.js';\nimport type { Lead, LeadStatusEntry } from '../domain/entities/lead.js';\nimport type { Opportunity } from '../domain/entities/opportunity.js';\nimport { canTransitionLead, type LeadStatus } from '../domain/enums/lead-status.js';\nimport {\n DuplicateLeadError,\n InvalidLeadTransitionError,\n LeadAlreadyConvertedError,\n NotFoundError,\n} from '../domain/errors.js';\nimport type { Clock } from '../domain/ports/clock.port.js';\nimport { systemClock } from '../domain/ports/clock.port.js';\nimport type { IdGenerator } from '../domain/ports/id-generator.port.js';\nimport type { LeadRepository } from '../domain/ports/lead-repository.port.js';\nimport { cryptoIdGenerator } from '../adapters/in-memory/id-generator.js';\nimport { CRM_EVENTS } from '../events/event-constants.js';\nimport { emitCrmEvent } from '../events/publish.js';\nimport type { CreateAccountInput } from './account.service.js';\nimport type { CreateContactInput } from './contact.service.js';\nimport type { MutationOptions } from './mutation-options.js';\nimport type { CreateOpportunityInput } from './opportunity.service.js';\nimport { defaultLeadScorer, type LeadScorer } from './scoring.js';\n\n/**\n * Derive a `PersonName` from a Lead's flat fields. Leads usually arrive from\n * web forms where the capture is either a split `firstName`/`lastName` or a\n * single `fullName` string — we support both without forcing callers to\n * normalise upstream.\n */\nfunction splitLeadName(lead: {\n firstName?: string;\n lastName?: string;\n fullName?: string;\n}): { given: string; family: string; preferred?: string } {\n if (lead.firstName || lead.lastName) {\n return {\n given: lead.firstName ?? '',\n family: lead.lastName ?? '',\n ...(lead.fullName && lead.fullName !== `${lead.firstName ?? ''} ${lead.lastName ?? ''}`.trim()\n ? { preferred: lead.fullName }\n : {}),\n };\n }\n const parts = (lead.fullName ?? '').trim().split(/\\s+/);\n if (parts.length <= 1) {\n return { given: parts[0] ?? 'Unknown', family: '' };\n }\n const family = parts.pop() ?? '';\n return { given: parts.join(' '), family };\n}\n\nexport interface LeadAccountWriter {\n create(input: CreateAccountInput, options?: MutationOptions): Promise<Account>;\n delete?(id: string): Promise<void>;\n}\n\nexport interface LeadContactWriter {\n create(input: CreateContactInput, options?: MutationOptions): Promise<Contact>;\n delete?(id: string): Promise<void>;\n}\n\nexport interface LeadOpportunityWriter {\n create(input: CreateOpportunityInput, options?: MutationOptions): Promise<Opportunity>;\n delete?(id: string): Promise<void>;\n}\n\nexport interface LeadServiceConfig {\n repo: LeadRepository;\n contacts: LeadContactWriter;\n accounts: LeadAccountWriter;\n opportunities: LeadOpportunityWriter;\n events?: EventTransport | undefined;\n /** Optional host-owned outbox. See AccountServiceConfig.outbox. */\n outbox?: OutboxStore | undefined;\n clock?: Clock;\n ids?: IdGenerator;\n /** Override the default scorer (`defaultLeadScorer`). */\n scorer?: LeadScorer;\n /** When true (default), `create()` rejects dup emails. */\n rejectDuplicates?: boolean;\n}\n\nexport interface CreateLeadInput {\n firstName?: string;\n lastName?: string;\n fullName?: string;\n email?: string;\n phone?: string;\n companyName?: string;\n jobTitle?: string;\n source?: string;\n campaignRef?: string;\n ownerId?: string;\n tags?: readonly string[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface ConvertLeadInput {\n /** Pipeline the resulting opportunity enters. */\n pipelineId: string;\n /** Opportunity name. Defaults to `<lead.companyName> — <lead.fullName>`. */\n opportunityName?: string;\n /** Optional deal amount. */\n amount?: Money;\n expectedCloseAt?: Date;\n by?: string;\n}\n\nexport interface ConvertResult {\n lead: Lead;\n contactId: string;\n accountId?: string;\n opportunityId: string;\n}\n\n/**\n * Lead lifecycle + the crown-jewel `convert()` verb that atomically\n * spawns a Contact + Account + Opportunity and transitions the lead\n * to `converted`.\n */\nexport class LeadService {\n private readonly repo: LeadRepository;\n private readonly contacts: LeadContactWriter;\n private readonly accounts: LeadAccountWriter;\n private readonly opportunities: LeadOpportunityWriter;\n private readonly events: EventTransport | undefined;\n private readonly outbox: OutboxStore | undefined;\n private readonly clock: Clock;\n private readonly ids: IdGenerator;\n private readonly scorer: LeadScorer;\n private readonly rejectDuplicates: boolean;\n\n constructor(config: LeadServiceConfig) {\n this.repo = config.repo;\n this.contacts = config.contacts;\n this.accounts = config.accounts;\n this.opportunities = config.opportunities;\n this.events = config.events;\n this.outbox = config.outbox;\n this.clock = config.clock ?? systemClock;\n this.ids = config.ids ?? cryptoIdGenerator;\n this.scorer = config.scorer ?? defaultLeadScorer;\n this.rejectDuplicates = config.rejectDuplicates ?? true;\n }\n\n async create(input: CreateLeadInput, options: MutationOptions = {}): Promise<Lead> {\n if (this.rejectDuplicates && input.email) {\n const existing = await this.repo.findByEmail(input.email);\n if (existing) throw new DuplicateLeadError({ email: input.email });\n }\n const now = this.clock.now();\n const fullName = input.fullName ?? `${input.firstName ?? ''} ${input.lastName ?? ''}`.trim();\n const draft: Omit<Lead, 'createdAt' | 'updatedAt'> = {\n id: this.ids.next(),\n fullName: fullName || input.email || input.phone || 'Unknown',\n status: 'new',\n statusHistory: [{ status: 'new', occurredAt: now }],\n ...(input.firstName !== undefined ? { firstName: input.firstName } : {}),\n ...(input.lastName !== undefined ? { lastName: input.lastName } : {}),\n ...(input.email !== undefined ? { email: input.email } : {}),\n ...(input.phone !== undefined ? { phone: input.phone } : {}),\n ...(input.companyName !== undefined ? { companyName: input.companyName } : {}),\n ...(input.jobTitle !== undefined ? { jobTitle: input.jobTitle } : {}),\n ...(input.source !== undefined ? { source: input.source } : {}),\n ...(input.campaignRef !== undefined ? { campaignRef: input.campaignRef } : {}),\n ...(input.ownerId !== undefined ? { ownerId: input.ownerId } : {}),\n ...(input.tags !== undefined ? { tags: input.tags } : {}),\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n };\n const lead = await this.repo.create(draft);\n // Auto-score on create so hosts can route immediately.\n const scored = await this._updateScore(lead, options);\n await this._emit(CRM_EVENTS.LEAD_CREATED, {\n leadId: scored.id,\n email: scored.email,\n score: scored.score,\n }, { resource: 'lead', resourceId: scored.id }, options);\n return scored;\n }\n\n async markContacted(id: string, by?: string, note?: string, options: MutationOptions = {}): Promise<Lead> {\n return this._transition(id, 'contacted', {\n ...(by !== undefined ? { by } : {}),\n ...(note !== undefined ? { note } : {}),\n }, options);\n }\n\n async qualify(id: string, by?: string, note?: string, options: MutationOptions = {}): Promise<Lead> {\n return this._transition(id, 'qualified', {\n ...(by !== undefined ? { by } : {}),\n ...(note !== undefined ? { note } : {}),\n }, options);\n }\n\n async disqualify(id: string, reason: string, by?: string, options: MutationOptions = {}): Promise<Lead> {\n const updated = await this._transition(id, 'disqualified', {\n note: reason,\n patch: { disqualifyReason: reason },\n ...(by !== undefined ? { by } : {}),\n }, options);\n await this._emit(CRM_EVENTS.LEAD_DISQUALIFIED, { leadId: id, reason }, undefined, options);\n return updated;\n }\n\n async nurture(id: string, by?: string, options: MutationOptions = {}): Promise<Lead> {\n return this._transition(id, 'nurturing', {\n ...(by !== undefined ? { by } : {}),\n }, options);\n }\n\n async convert(id: string, input: ConvertLeadInput, options: MutationOptions = {}): Promise<ConvertResult> {\n const existing = await this._require(id);\n if (existing.status === 'converted') {\n throw new LeadAlreadyConvertedError(id);\n }\n if (!canTransitionLead(existing.status, 'converted')) {\n throw new InvalidLeadTransitionError({\n from: existing.status,\n to: 'converted',\n leadId: id,\n });\n }\n\n let account: Account | undefined;\n let contact: Contact | undefined;\n let opportunity: Opportunity | undefined;\n\n try {\n // Inner mutations share the caller's session so the whole convert is\n // one atomic commit + outbox row set when wrapped in withTransaction.\n const innerOpts: MutationOptions = {\n emitEvents: false,\n ...(options.session !== undefined ? { session: options.session } : {}),\n };\n if (existing.companyName) {\n account = await this.accounts.create(\n {\n name: existing.companyName,\n ...(existing.ownerId !== undefined ? { ownerId: existing.ownerId } : {}),\n },\n innerOpts,\n );\n }\n\n const { given, family, preferred } = splitLeadName(existing);\n contact = await this.contacts.create(\n {\n name: {\n given,\n family,\n ...(preferred ? { preferred } : {}),\n },\n contact: {\n ...(existing.email !== undefined ? { email: existing.email } : {}),\n ...(existing.phone !== undefined ? { phone: existing.phone } : {}),\n },\n ...(existing.jobTitle !== undefined ? { jobTitle: existing.jobTitle } : {}),\n ...(account !== undefined ? { accountId: account.id } : {}),\n ...(existing.ownerId !== undefined ? { ownerId: existing.ownerId } : {}),\n },\n innerOpts,\n );\n\n // 3. Opportunity in the requested pipeline.\n const opportunityName =\n input.opportunityName ??\n `${existing.companyName ?? existing.fullName} — ${existing.source ?? 'New'}`;\n opportunity = await this.opportunities.create(\n {\n name: opportunityName,\n pipelineId: input.pipelineId,\n ...(account !== undefined ? { accountId: account.id } : {}),\n primaryContactId: contact.id,\n sourceLeadId: id,\n ...(input.amount !== undefined ? { amount: input.amount } : {}),\n ...(input.expectedCloseAt !== undefined ? { expectedCloseAt: input.expectedCloseAt } : {}),\n ...(existing.ownerId !== undefined ? { ownerId: existing.ownerId } : {}),\n },\n innerOpts,\n );\n\n // 4. Mark the lead converted with all cross-refs.\n const convertedAt = this.clock.now();\n const lead = await this._transition(id, 'converted', {\n patch: {\n convertedContactId: contact.id,\n ...(account !== undefined ? { convertedAccountId: account.id } : {}),\n convertedOpportunityId: opportunity.id,\n convertedAt,\n },\n ...(input.by !== undefined ? { by: input.by } : {}),\n }, options);\n\n if (account) {\n await this._emit(CRM_EVENTS.ACCOUNT_CREATED, { accountId: account.id }, { resource: 'account', resourceId: account.id }, options);\n }\n await this._emit(\n CRM_EVENTS.CONTACT_CREATED,\n { contactId: contact.id, email: contact.contact.email },\n { resource: 'contact', resourceId: contact.id },\n options,\n );\n await this._emit(\n CRM_EVENTS.OPPORTUNITY_CREATED,\n {\n opportunityId: opportunity.id,\n pipelineId: opportunity.pipelineId,\n stageId: opportunity.stageId,\n },\n { resource: 'opportunity', resourceId: opportunity.id },\n options,\n );\n\n await this._emit(CRM_EVENTS.LEAD_CONVERTED, {\n leadId: id,\n contactId: contact.id,\n accountId: account?.id,\n opportunityId: opportunity.id,\n }, { resource: 'lead', resourceId: id }, options);\n\n return {\n lead,\n contactId: contact.id,\n ...(account !== undefined ? { accountId: account.id } : {}),\n opportunityId: opportunity.id,\n };\n } catch (error) {\n if (opportunity?.id && this.opportunities.delete) {\n await this.opportunities.delete(opportunity.id);\n }\n if (contact?.id && this.contacts.delete) {\n await this.contacts.delete(contact.id);\n }\n if (account?.id && this.accounts.delete) {\n await this.accounts.delete(account.id);\n }\n throw error;\n }\n }\n\n /** Recompute score and persist. Hosts can call on update or on demand. */\n async recomputeScore(id: string, options: MutationOptions = {}): Promise<Lead> {\n const lead = await this._require(id);\n return this._updateScore(lead, options);\n }\n\n findById(id: string): Promise<Lead | null> {\n return this.repo.findById(id);\n }\n\n list(filter?: Parameters<LeadRepository['list']>[0]): Promise<Lead[]> {\n return this.repo.list(filter);\n }\n\n // ── Internals ─────────────────────────────────────────────────────\n\n private async _updateScore(lead: Lead, options: MutationOptions = {}): Promise<Lead> {\n const score = this.scorer(lead);\n if (score === lead.score) return lead;\n const updated = await this.repo.update(lead.id, { score });\n await this._emit(CRM_EVENTS.LEAD_SCORED, { leadId: lead.id, score }, { resource: 'lead', resourceId: lead.id }, options);\n return updated;\n }\n\n private async _require(id: string): Promise<Lead> {\n const l = await this.repo.findById(id);\n if (!l) throw new NotFoundError('lead', id);\n return l;\n }\n\n private async _transition(\n id: string,\n target: LeadStatus,\n opts: { patch?: Partial<Lead>; by?: string; note?: string } = {},\n options: MutationOptions = {},\n ): Promise<Lead> {\n const existing = await this._require(id);\n if (!canTransitionLead(existing.status, target)) {\n throw new InvalidLeadTransitionError({ from: existing.status, to: target, leadId: id });\n }\n const now = this.clock.now();\n const entry: LeadStatusEntry = {\n status: target,\n occurredAt: now,\n ...(opts.by !== undefined ? { by: opts.by } : {}),\n ...(opts.note !== undefined ? { note: opts.note } : {}),\n };\n const patch: Partial<Lead> = {\n ...(opts.patch ?? {}),\n statusHistory: [...existing.statusHistory, entry],\n };\n const updated = await this.repo.casStatus(id, existing.status, target, patch);\n if (!updated) {\n const latest = await this.repo.findById(id);\n throw new InvalidLeadTransitionError({\n from: latest?.status ?? existing.status,\n to: target,\n leadId: id,\n });\n }\n await this._emit(CRM_EVENTS.LEAD_STATUS_CHANGED, {\n leadId: id,\n from: existing.status,\n to: target,\n }, { resource: 'lead', resourceId: id }, options);\n return updated;\n }\n\n private async _emit(\n event: string,\n payload: unknown,\n meta: { resource?: string; resourceId?: string } | undefined,\n options: MutationOptions = {},\n ): Promise<void> {\n await emitCrmEvent(this.events, this.outbox, event, payload, meta, options.session);\n }\n}\n","/**\n * Top-level opportunity status. Movement through pipeline STAGES\n * happens while status is `open`; `won` / `lost` are terminal\n * outcomes. Re-opening a lost deal is a host-level decision —\n * convention is to clone the opportunity rather than revive the\n * terminal record.\n */\nexport const OPPORTUNITY_STATUSES = ['open', 'won', 'lost', 'abandoned'] as const;\nexport type OpportunityStatus = (typeof OPPORTUNITY_STATUSES)[number];\n\nexport const OPPORTUNITY_TRANSITIONS: Record<OpportunityStatus, readonly OpportunityStatus[]> = {\n open: ['won', 'lost', 'abandoned'],\n won: [],\n lost: [],\n abandoned: [],\n};\n\nexport function canTransitionOpportunity(\n from: OpportunityStatus,\n to: OpportunityStatus,\n): boolean {\n return OPPORTUNITY_TRANSITIONS[from].includes(to);\n}\n","import type { EventTransport } from '@classytic/primitives/events';\nimport type { OutboxStore } from '@classytic/primitives/outbox';\nimport type { Money } from '@classytic/primitives/money';\nimport type {\n Opportunity,\n OpportunityStatusEntry,\n} from '../domain/entities/opportunity.js';\nimport {\n canTransitionOpportunity,\n type OpportunityStatus,\n} from '../domain/enums/opportunity-status.js';\nimport {\n EmptyPipelineError,\n InvalidOpportunityTransitionError,\n NotFoundError,\n StageNotInPipelineError,\n} from '../domain/errors.js';\nimport type { Clock } from '../domain/ports/clock.port.js';\nimport { systemClock } from '../domain/ports/clock.port.js';\nimport type { IdGenerator } from '../domain/ports/id-generator.port.js';\nimport type { OpportunityRepository } from '../domain/ports/opportunity-repository.port.js';\nimport { cryptoIdGenerator } from '../adapters/in-memory/id-generator.js';\nimport { CRM_EVENTS } from '../events/event-constants.js';\nimport { emitCrmEvent } from '../events/publish.js';\nimport type { MutationOptions } from './mutation-options.js';\n\nexport interface OpportunityPipelinePort {\n findById(id: string): Promise<{ id: string; stages: readonly { id: string }[] } | null>;\n getStage(pipelineId: string, stageId: string): Promise<{ id: string; sequence: number; defaultProbability: number }>;\n getFirstStage(pipelineId: string): Promise<{ id: string; sequence: number; defaultProbability: number }>;\n}\n\nexport interface OpportunityServiceConfig {\n repo: OpportunityRepository;\n pipelines: OpportunityPipelinePort;\n events?: EventTransport | undefined;\n /** Optional host-owned outbox. See AccountServiceConfig.outbox. */\n outbox?: OutboxStore | undefined;\n clock?: Clock;\n ids?: IdGenerator;\n}\n\nexport interface CreateOpportunityInput {\n name: string;\n pipelineId: string;\n /** Starting stage — defaults to the pipeline's first stage. */\n stageId?: string;\n accountId?: string;\n primaryContactId?: string;\n sourceLeadId?: string;\n amount?: Money;\n /** Override the stage default. Defaults to the stage's `defaultProbability`. */\n probability?: number;\n expectedCloseAt?: Date;\n ownerId?: string;\n tags?: readonly string[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface MoveStageInput {\n stageId: string;\n /** Override probability. Defaults to the target stage's `defaultProbability`. */\n probability?: number;\n by?: string;\n note?: string;\n}\n\nexport interface CloseInput {\n amount?: Money;\n closedAt?: Date;\n by?: string;\n note?: string;\n}\n\nexport interface LoseInput extends CloseInput {\n lostReasonId?: string;\n}\n\n/**\n * Opportunity lifecycle — stage movement + outcome resolution. Stage\n * changes call the pipeline service to validate membership + default\n * probability; status transitions (`won` / `lost` / `abandoned`) are\n * terminal.\n */\nexport class OpportunityService {\n private readonly repo: OpportunityRepository;\n private readonly pipelines: OpportunityPipelinePort;\n private readonly events: EventTransport | undefined;\n private readonly outbox: OutboxStore | undefined;\n private readonly clock: Clock;\n private readonly ids: IdGenerator;\n\n constructor(config: OpportunityServiceConfig) {\n this.repo = config.repo;\n this.pipelines = config.pipelines;\n this.events = config.events;\n this.outbox = config.outbox;\n this.clock = config.clock ?? systemClock;\n this.ids = config.ids ?? cryptoIdGenerator;\n }\n\n async create(input: CreateOpportunityInput, options: MutationOptions = {}): Promise<Opportunity> {\n const pipeline = await this.pipelines.findById(input.pipelineId);\n if (!pipeline) throw new NotFoundError('pipeline', input.pipelineId);\n if (pipeline.stages.length === 0) throw new EmptyPipelineError(pipeline.id);\n\n const stage = input.stageId\n ? await this.pipelines.getStage(pipeline.id, input.stageId)\n : await this.pipelines.getFirstStage(pipeline.id);\n\n const probability = input.probability ?? stage.defaultProbability;\n const now = this.clock.now();\n const opportunity = await this.repo.create({\n id: this.ids.next(),\n name: input.name,\n pipelineId: pipeline.id,\n stageId: stage.id,\n status: 'open',\n statusHistory: [{ status: 'open', stageId: stage.id, occurredAt: now }],\n probability,\n ...(input.accountId !== undefined ? { accountId: input.accountId } : {}),\n ...(input.primaryContactId !== undefined\n ? { primaryContactId: input.primaryContactId }\n : {}),\n ...(input.sourceLeadId !== undefined ? { sourceLeadId: input.sourceLeadId } : {}),\n ...(input.amount !== undefined ? { amount: input.amount } : {}),\n ...(input.expectedCloseAt !== undefined\n ? { expectedCloseAt: input.expectedCloseAt }\n : {}),\n ...(input.ownerId !== undefined ? { ownerId: input.ownerId } : {}),\n ...(input.tags !== undefined ? { tags: input.tags } : {}),\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n });\n if (options.emitEvents !== false) {\n await this._emit(\n CRM_EVENTS.OPPORTUNITY_CREATED,\n {\n opportunityId: opportunity.id,\n pipelineId: pipeline.id,\n stageId: stage.id,\n },\n { resource: 'opportunity', resourceId: opportunity.id },\n options,\n );\n }\n return opportunity;\n }\n\n async moveToStage(id: string, input: MoveStageInput, options: MutationOptions = {}): Promise<Opportunity> {\n const existing = await this._require(id);\n if (existing.status !== 'open') {\n throw new InvalidOpportunityTransitionError({\n from: existing.status,\n to: existing.status,\n opportunityId: id,\n });\n }\n const target = await this.pipelines.getStage(existing.pipelineId, input.stageId);\n const currentStage = await this.pipelines.getStage(existing.pipelineId, existing.stageId);\n const probability = input.probability ?? target.defaultProbability;\n const now = this.clock.now();\n\n const entry: OpportunityStatusEntry = {\n status: 'open',\n stageId: target.id,\n occurredAt: now,\n ...(input.by !== undefined ? { by: input.by } : {}),\n ...(input.note !== undefined ? { note: input.note } : {}),\n };\n const patch: Partial<Opportunity> = {\n probability,\n statusHistory: [...existing.statusHistory, entry],\n };\n const updated = await this.repo.casStage(id, existing.stageId, target.id, patch);\n if (!updated) throw new StageNotInPipelineError({ pipelineId: existing.pipelineId, stageId: target.id });\n\n const eventName =\n target.sequence > currentStage.sequence\n ? CRM_EVENTS.OPPORTUNITY_STAGE_ADVANCED\n : CRM_EVENTS.OPPORTUNITY_STAGE_REGRESSED;\n await this._emit(eventName, {\n opportunityId: id,\n from: { stageId: currentStage.id, sequence: currentStage.sequence },\n to: { stageId: target.id, sequence: target.sequence },\n }, { resource: 'opportunity', resourceId: id }, options);\n return updated;\n }\n\n async win(id: string, input: CloseInput = {}, options: MutationOptions = {}): Promise<Opportunity> {\n const patch: Partial<Opportunity> = {\n closedAt: input.closedAt ?? this.clock.now(),\n probability: 1,\n ...(input.amount !== undefined ? { amount: input.amount } : {}),\n };\n const updated = await this._transitionStatus(id, 'won', patch, input.by, input.note);\n await this._emit(CRM_EVENTS.OPPORTUNITY_WON, {\n opportunityId: id,\n amount: updated.amount,\n }, { resource: 'opportunity', resourceId: id }, options);\n return updated;\n }\n\n async lose(id: string, input: LoseInput = {}, options: MutationOptions = {}): Promise<Opportunity> {\n const patch: Partial<Opportunity> = {\n closedAt: input.closedAt ?? this.clock.now(),\n probability: 0,\n ...(input.lostReasonId !== undefined ? { lostReasonId: input.lostReasonId } : {}),\n };\n const updated = await this._transitionStatus(id, 'lost', patch, input.by, input.note);\n await this._emit(CRM_EVENTS.OPPORTUNITY_LOST, {\n opportunityId: id,\n lostReasonId: input.lostReasonId,\n }, { resource: 'opportunity', resourceId: id }, options);\n return updated;\n }\n\n async abandon(id: string, by?: string, note?: string, options: MutationOptions = {}): Promise<Opportunity> {\n const patch: Partial<Opportunity> = {\n closedAt: this.clock.now(),\n probability: 0,\n };\n const updated = await this._transitionStatus(id, 'abandoned', patch, by, note);\n await this._emit(CRM_EVENTS.OPPORTUNITY_ABANDONED, { opportunityId: id }, { resource: 'opportunity', resourceId: id }, options);\n return updated;\n }\n\n async delete(id: string): Promise<void> {\n await this._require(id);\n await this.repo.delete(id);\n }\n\n findById(id: string): Promise<Opportunity | null> {\n return this.repo.findById(id);\n }\n\n list(filter?: Parameters<OpportunityRepository['list']>[0]): Promise<Opportunity[]> {\n return this.repo.list(filter);\n }\n\n // ── Internals ─────────────────────────────────────────────────────\n\n private async _require(id: string): Promise<Opportunity> {\n const o = await this.repo.findById(id);\n if (!o) throw new NotFoundError('opportunity', id);\n return o;\n }\n\n private async _transitionStatus(\n id: string,\n target: OpportunityStatus,\n patch: Partial<Opportunity>,\n by?: string,\n note?: string,\n ): Promise<Opportunity> {\n const existing = await this._require(id);\n if (!canTransitionOpportunity(existing.status, target)) {\n throw new InvalidOpportunityTransitionError({\n from: existing.status,\n to: target,\n opportunityId: id,\n });\n }\n const now = this.clock.now();\n const entry: OpportunityStatusEntry = {\n status: target,\n stageId: existing.stageId,\n occurredAt: now,\n ...(by !== undefined ? { by } : {}),\n ...(note !== undefined ? { note } : {}),\n };\n const fullPatch: Partial<Opportunity> = {\n ...patch,\n statusHistory: [...existing.statusHistory, entry],\n };\n const updated = await this.repo.casStatus(id, existing.status, target, fullPatch);\n if (!updated) {\n const latest = await this.repo.findById(id);\n throw new InvalidOpportunityTransitionError({\n from: latest?.status ?? existing.status,\n to: target,\n opportunityId: id,\n });\n }\n return updated;\n }\n\n private async _emit(\n event: string,\n payload: unknown,\n meta: { resource?: string; resourceId?: string } | undefined,\n options: MutationOptions = {},\n ): Promise<void> {\n await emitCrmEvent(this.events, this.outbox, event, payload, meta, options.session);\n }\n}\n","import type { EventTransport } from '@classytic/primitives/events';\nimport type { OutboxStore } from '@classytic/primitives/outbox';\nimport type { Pipeline, Stage } from '../domain/entities/pipeline.js';\nimport { EmptyPipelineError, NotFoundError, StageNotInPipelineError } from '../domain/errors.js';\nimport type { Clock } from '../domain/ports/clock.port.js';\nimport { systemClock } from '../domain/ports/clock.port.js';\nimport type { IdGenerator } from '../domain/ports/id-generator.port.js';\nimport type { PipelineRepository } from '../domain/ports/pipeline-repository.port.js';\nimport { cryptoIdGenerator } from '../adapters/in-memory/id-generator.js';\nimport { CRM_EVENTS } from '../events/event-constants.js';\nimport { emitCrmEvent } from '../events/publish.js';\nimport type { MutationOptions } from './mutation-options.js';\n\nexport interface PipelineServiceConfig {\n repo: PipelineRepository;\n events?: EventTransport | undefined;\n /** Optional host-owned outbox. See AccountServiceConfig.outbox. */\n outbox?: OutboxStore | undefined;\n clock?: Clock;\n ids?: IdGenerator;\n}\n\nexport interface CreatePipelineInput {\n name: string;\n stages: readonly Omit<Stage, 'id'>[];\n teamRef?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Pipeline lifecycle + stage helpers. The service owns stage id\n * generation so callers don't have to — create a pipeline with an\n * array of stage seeds and the service hands back the persisted\n * pipeline with ids assigned.\n */\nexport class PipelineService {\n private readonly repo: PipelineRepository;\n private readonly events: EventTransport | undefined;\n private readonly outbox: OutboxStore | undefined;\n private readonly clock: Clock;\n private readonly ids: IdGenerator;\n\n constructor(config: PipelineServiceConfig) {\n this.repo = config.repo;\n this.events = config.events;\n this.outbox = config.outbox;\n this.clock = config.clock ?? systemClock;\n this.ids = config.ids ?? cryptoIdGenerator;\n }\n\n async create(input: CreatePipelineInput, options: MutationOptions = {}): Promise<Pipeline> {\n if (input.stages.length === 0) {\n throw new EmptyPipelineError('<new>');\n }\n const id = this.ids.next();\n const stages: Stage[] = input.stages.map((s) => ({\n id: this.ids.next(),\n ...s,\n }));\n const pipeline = await this.repo.create({\n id,\n name: input.name,\n isArchived: false,\n stages,\n ...(input.teamRef !== undefined ? { teamRef: input.teamRef } : {}),\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n });\n await this._emit(CRM_EVENTS.PIPELINE_CREATED, { pipelineId: pipeline.id }, { resource: 'pipeline', resourceId: pipeline.id }, options);\n return pipeline;\n }\n\n async archive(id: string, options: MutationOptions = {}): Promise<Pipeline> {\n await this._require(id);\n const updated = await this.repo.update(id, { isArchived: true });\n await this._emit(CRM_EVENTS.PIPELINE_ARCHIVED, { pipelineId: id }, { resource: 'pipeline', resourceId: id }, options);\n return updated;\n }\n\n async getStage(pipelineId: string, stageId: string): Promise<Stage> {\n const pipeline = await this._require(pipelineId);\n const stage = pipeline.stages.find((s) => s.id === stageId);\n if (!stage) throw new StageNotInPipelineError({ pipelineId, stageId });\n return stage;\n }\n\n /** First stage in the ordered pipeline (minimum `sequence`). */\n async getFirstStage(pipelineId: string): Promise<Stage> {\n const pipeline = await this._require(pipelineId);\n const stages = [...pipeline.stages].sort((a, b) => a.sequence - b.sequence);\n if (stages.length === 0) throw new EmptyPipelineError(pipelineId);\n return stages[0]!;\n }\n\n /** Next stage after `stageId` in the ordered pipeline, or `null` at the end. */\n async getNextStage(pipelineId: string, stageId: string): Promise<Stage | null> {\n const pipeline = await this._require(pipelineId);\n const stages = [...pipeline.stages].sort((a, b) => a.sequence - b.sequence);\n const idx = stages.findIndex((s) => s.id === stageId);\n if (idx === -1) throw new StageNotInPipelineError({ pipelineId, stageId });\n return stages[idx + 1] ?? null;\n }\n\n findById(id: string): Promise<Pipeline | null> {\n return this.repo.findById(id);\n }\n\n list(includeArchived?: boolean): Promise<Pipeline[]> {\n return this.repo.list(includeArchived);\n }\n\n private async _require(id: string): Promise<Pipeline> {\n const p = await this.repo.findById(id);\n if (!p) throw new NotFoundError('pipeline', id);\n return p;\n }\n\n private async _emit(\n event: string,\n payload: unknown,\n meta: { resource?: string; resourceId?: string } | undefined,\n options: MutationOptions = {},\n ): Promise<void> {\n await emitCrmEvent(this.events, this.outbox, event, payload, meta, options.session);\n }\n}\n","import type { Money } from '@classytic/primitives/money';\nimport type { Opportunity } from '../domain/entities/opportunity.js';\n\nexport interface ForecastRow {\n /** Bucket key — typically ISO month `YYYY-MM` derived from `expectedCloseAt`. */\n bucket: string;\n /** Sum of unweighted `amount`. */\n committed: number;\n /** Sum of `amount × probability`. */\n weighted: number;\n /** Count of opportunities contributing. */\n count: number;\n currency: string;\n}\n\nexport interface ForecastOptions {\n /**\n * Bucket function — given an opportunity, return a string bucket\n * key. Default is ISO-month `YYYY-MM`. Custom buckets (quarter,\n * fiscal-year, custom periods) plug in here.\n */\n bucket?: (o: Opportunity) => string;\n /**\n * Filter opportunities before bucketing. Default includes only\n * `open` opportunities with `amount` and `expectedCloseAt`.\n */\n include?: (o: Opportunity) => boolean;\n}\n\n/**\n * Aggregate opportunities into a forecast table — one row per\n * bucket (month by default). Committed is `Σ amount`; weighted is\n * `Σ amount × probability`.\n *\n * Integer-safe: sums are computed on minor-unit amounts (consistent\n * with `@classytic/primitives/money`'s integer convention). Currency\n * mismatch across a bucket throws — hosts dealing in multi-currency\n * run this once per currency after partitioning.\n */\nexport function forecast(\n opportunities: readonly Opportunity[],\n options: ForecastOptions = {},\n): ForecastRow[] {\n const bucketFn = options.bucket ?? defaultBucket;\n const include = options.include ?? defaultInclude;\n\n const rows = new Map<string, ForecastRow>();\n for (const o of opportunities) {\n if (!include(o)) continue;\n const amt = o.amount!.amount;\n const bucket = bucketFn(o);\n const existing = rows.get(bucket);\n if (existing) {\n if (existing.currency !== o.amount!.currency) {\n throw new Error(\n `Forecast bucket '${bucket}' mixes currencies (${existing.currency} + ${o.amount!.currency}). Partition by currency before calling forecast().`,\n );\n }\n rows.set(bucket, {\n ...existing,\n committed: existing.committed + amt,\n weighted: existing.weighted + Math.round(amt * o.probability),\n count: existing.count + 1,\n });\n } else {\n rows.set(bucket, {\n bucket,\n committed: amt,\n weighted: Math.round(amt * o.probability),\n count: 1,\n currency: o.amount!.currency,\n });\n }\n }\n return [...rows.values()].sort((a, b) => a.bucket.localeCompare(b.bucket));\n}\n\nfunction defaultInclude(o: Opportunity): boolean {\n return o.status === 'open' && !!o.amount && !!o.expectedCloseAt;\n}\n\nfunction defaultBucket(o: Opportunity): string {\n const d = o.expectedCloseAt!;\n const yr = d.getUTCFullYear();\n const mo = String(d.getUTCMonth() + 1).padStart(2, '0');\n return `${yr}-${mo}`;\n}\n\n/**\n * Pipeline conversion summary — how opportunities partitioned across\n * stages and final outcome. Useful for \"where do deals die?\" analysis.\n */\nexport interface ConversionRow {\n stageId: string;\n currentCount: number;\n wonFromHere: number;\n lostFromHere: number;\n conversionRate: number;\n}\n\nexport function pipelineConversion(\n opportunities: readonly Opportunity[],\n stageIds: readonly string[],\n): ConversionRow[] {\n const rows = stageIds.map((stageId): ConversionRow => {\n let currentCount = 0;\n let wonFromHere = 0;\n let lostFromHere = 0;\n for (const o of opportunities) {\n if (o.stageId === stageId && o.status === 'open') currentCount++;\n if (o.status === 'won' || o.status === 'lost') {\n // Look back through history — did this opp pass through `stageId`?\n const passed = o.statusHistory.some((h) => h.stageId === stageId);\n if (passed || o.stageId === stageId) {\n if (o.status === 'won') wonFromHere++;\n else lostFromHere++;\n }\n }\n }\n const closed = wonFromHere + lostFromHere;\n return {\n stageId,\n currentCount,\n wonFromHere,\n lostFromHere,\n conversionRate: closed > 0 ? wonFromHere / closed : 0,\n };\n });\n return rows;\n}\n","import type { Account } from '../../domain/entities/account.js';\nimport type { Clock } from '../../domain/ports/clock.port.js';\nimport { systemClock } from '../../domain/ports/clock.port.js';\nimport type {\n AccountFilter,\n AccountRepository,\n} from '../../domain/ports/account-repository.port.js';\n\nexport class InMemoryAccountRepository implements AccountRepository {\n private readonly items = new Map<string, Account>();\n\n constructor(private readonly clock: Clock = systemClock) {}\n\n async findById(id: string): Promise<Account | null> {\n return this.items.get(id) ?? null;\n }\n\n async findByDomain(domain: string): Promise<Account | null> {\n for (const a of this.items.values()) {\n if (a.domain?.toLowerCase() === domain.toLowerCase()) return a;\n }\n return null;\n }\n\n async list(filter: AccountFilter = {}): Promise<Account[]> {\n return [...this.items.values()].filter((a) => {\n if (filter.ownerId !== undefined && a.ownerId !== filter.ownerId) return false;\n if (filter.industry !== undefined && a.industry !== filter.industry) return false;\n if (filter.domain !== undefined && a.domain?.toLowerCase() !== filter.domain.toLowerCase())\n return false;\n if (filter.tags && filter.tags.length > 0) {\n const at = new Set(a.tags ?? []);\n if (!filter.tags.every((t) => at.has(t))) return false;\n }\n return true;\n });\n }\n\n async create(input: Omit<Account, 'createdAt' | 'updatedAt'>): Promise<Account> {\n const now = this.clock.now();\n const a: Account = { ...input, createdAt: now, updatedAt: now };\n this.items.set(a.id, a);\n return a;\n }\n\n async update(id: string, patch: Partial<Account>): Promise<Account> {\n const e = this.items.get(id);\n if (!e) throw new Error(`Account '${id}' not found`);\n const a: Account = { ...e, ...patch, id, updatedAt: this.clock.now() };\n this.items.set(id, a);\n return a;\n }\n\n async delete(id: string): Promise<void> {\n this.items.delete(id);\n }\n}\n","import type { Activity } from '../../domain/entities/activity.js';\nimport type { ActivityStatus } from '../../domain/enums/activity.js';\nimport type { Clock } from '../../domain/ports/clock.port.js';\nimport { systemClock } from '../../domain/ports/clock.port.js';\nimport type {\n ActivityFilter,\n ActivityRepository,\n} from '../../domain/ports/activity-repository.port.js';\n\nexport class InMemoryActivityRepository implements ActivityRepository {\n private readonly items = new Map<string, Activity>();\n\n constructor(private readonly clock: Clock = systemClock) {}\n\n async findById(id: string): Promise<Activity | null> {\n return this.items.get(id) ?? null;\n }\n\n async list(filter: ActivityFilter = {}): Promise<Activity[]> {\n return [...this.items.values()].filter((a) => {\n if (filter.subjectKind !== undefined && a.subjectKind !== filter.subjectKind) return false;\n if (filter.subjectId !== undefined && a.subjectId !== filter.subjectId) return false;\n if (filter.status !== undefined) {\n const allowed = Array.isArray(filter.status) ? filter.status : [filter.status];\n if (!allowed.includes(a.status)) return false;\n }\n if (filter.ownerId !== undefined && a.ownerId !== filter.ownerId) return false;\n if (filter.scheduledBetween !== undefined) {\n if (!a.scheduledAt) return false;\n if (\n a.scheduledAt < filter.scheduledBetween.from ||\n a.scheduledAt > filter.scheduledBetween.to\n )\n return false;\n }\n return true;\n });\n }\n\n async create(input: Omit<Activity, 'createdAt' | 'updatedAt'>): Promise<Activity> {\n const now = this.clock.now();\n const a: Activity = { ...input, createdAt: now, updatedAt: now };\n this.items.set(a.id, a);\n return a;\n }\n\n async update(id: string, patch: Partial<Activity>): Promise<Activity> {\n const e = this.items.get(id);\n if (!e) throw new Error(`Activity '${id}' not found`);\n const a: Activity = { ...e, ...patch, id, updatedAt: this.clock.now() };\n this.items.set(id, a);\n return a;\n }\n\n async casStatus(\n id: string,\n expected: ActivityStatus,\n next: ActivityStatus,\n patch: Partial<Activity> = {},\n ): Promise<Activity | null> {\n const e = this.items.get(id);\n if (!e || e.status !== expected) return null;\n const updated: Activity = {\n ...e,\n ...patch,\n id,\n status: next,\n updatedAt: this.clock.now(),\n };\n this.items.set(id, updated);\n return updated;\n }\n}\n","import type { Contact } from '../../domain/entities/contact.js';\nimport type { Clock } from '../../domain/ports/clock.port.js';\nimport { systemClock } from '../../domain/ports/clock.port.js';\nimport type {\n ContactFilter,\n ContactRepository,\n} from '../../domain/ports/contact-repository.port.js';\n\nexport class InMemoryContactRepository implements ContactRepository {\n private readonly items = new Map<string, Contact>();\n\n constructor(private readonly clock: Clock = systemClock) {}\n\n async findById(id: string): Promise<Contact | null> {\n return this.items.get(id) ?? null;\n }\n\n async findByEmail(email: string): Promise<Contact | null> {\n const needle = email.toLowerCase();\n for (const c of this.items.values()) {\n if (c.contact.email?.toLowerCase() === needle) return c;\n }\n return null;\n }\n\n async list(filter: ContactFilter = {}): Promise<Contact[]> {\n const email = filter.email?.toLowerCase();\n return [...this.items.values()].filter((c) => {\n if (filter.accountId !== undefined && c.accountId !== filter.accountId) return false;\n if (filter.ownerId !== undefined && c.ownerId !== filter.ownerId) return false;\n if (email !== undefined && c.contact.email?.toLowerCase() !== email) return false;\n if (filter.tags && filter.tags.length > 0) {\n const ct = new Set(c.tags ?? []);\n if (!filter.tags.every((t) => ct.has(t))) return false;\n }\n return true;\n });\n }\n\n async create(input: Omit<Contact, 'createdAt' | 'updatedAt'>): Promise<Contact> {\n const now = this.clock.now();\n const c: Contact = { ...input, createdAt: now, updatedAt: now };\n this.items.set(c.id, c);\n return c;\n }\n\n async update(id: string, patch: Partial<Contact>): Promise<Contact> {\n const e = this.items.get(id);\n if (!e) throw new Error(`Contact '${id}' not found`);\n const c: Contact = { ...e, ...patch, id, updatedAt: this.clock.now() };\n this.items.set(id, c);\n return c;\n }\n\n async delete(id: string): Promise<void> {\n this.items.delete(id);\n }\n}\n","import type { DomainEvent, EventHandler, EventTransport } from '@classytic/primitives/events';\nimport { matchEventPattern } from '@classytic/primitives/events';\n\nexport type RecordedEvent = DomainEvent;\n\nexport class InMemoryEventTransport implements EventTransport {\n readonly name = 'crm-in-memory';\n readonly events: RecordedEvent[] = [];\n private readonly subscribers: Array<{ pattern: string; handler: EventHandler }> = [];\n\n async publish(event: DomainEvent): Promise<void> {\n this.events.push(event);\n for (const subscriber of this.subscribers) {\n if (!matchEventPattern(subscriber.pattern, event.type)) continue;\n await subscriber.handler(event);\n }\n }\n\n async subscribe(pattern: string, handler: EventHandler): Promise<() => void> {\n const entry = { pattern, handler };\n this.subscribers.push(entry);\n return async () => {\n const idx = this.subscribers.indexOf(entry);\n if (idx >= 0) this.subscribers.splice(idx, 1);\n };\n }\n}\n","import type { Lead } from '../../domain/entities/lead.js';\nimport type { LeadStatus } from '../../domain/enums/lead-status.js';\nimport type { Clock } from '../../domain/ports/clock.port.js';\nimport { systemClock } from '../../domain/ports/clock.port.js';\nimport type {\n LeadFilter,\n LeadRepository,\n} from '../../domain/ports/lead-repository.port.js';\n\nexport class InMemoryLeadRepository implements LeadRepository {\n private readonly items = new Map<string, Lead>();\n\n constructor(private readonly clock: Clock = systemClock) {}\n\n async findById(id: string): Promise<Lead | null> {\n return this.items.get(id) ?? null;\n }\n\n async findByEmail(email: string): Promise<Lead | null> {\n for (const l of this.items.values()) {\n if (l.email?.toLowerCase() === email.toLowerCase()) return l;\n }\n return null;\n }\n\n async list(filter: LeadFilter = {}): Promise<Lead[]> {\n return [...this.items.values()].filter((l) => {\n if (filter.status !== undefined) {\n const allowed = Array.isArray(filter.status) ? filter.status : [filter.status];\n if (!allowed.includes(l.status)) return false;\n }\n if (filter.ownerId !== undefined && l.ownerId !== filter.ownerId) return false;\n if (filter.source !== undefined && l.source !== filter.source) return false;\n if (filter.minScore !== undefined && (l.score ?? 0) < filter.minScore) return false;\n return true;\n });\n }\n\n async create(input: Omit<Lead, 'createdAt' | 'updatedAt'>): Promise<Lead> {\n const now = this.clock.now();\n const l: Lead = { ...input, createdAt: now, updatedAt: now };\n this.items.set(l.id, l);\n return l;\n }\n\n async update(id: string, patch: Partial<Lead>): Promise<Lead> {\n const e = this.items.get(id);\n if (!e) throw new Error(`Lead '${id}' not found`);\n const l: Lead = { ...e, ...patch, id, updatedAt: this.clock.now() };\n this.items.set(id, l);\n return l;\n }\n\n async casStatus(\n id: string,\n expected: LeadStatus,\n next: LeadStatus,\n patch: Partial<Lead> = {},\n ): Promise<Lead | null> {\n const e = this.items.get(id);\n if (!e || e.status !== expected) return null;\n const updated: Lead = {\n ...e,\n ...patch,\n id,\n status: next,\n updatedAt: this.clock.now(),\n };\n this.items.set(id, updated);\n return updated;\n }\n}\n","import type { LossReason } from '../../domain/entities/loss-reason.js';\nimport type { Clock } from '../../domain/ports/clock.port.js';\nimport { systemClock } from '../../domain/ports/clock.port.js';\nimport type { LossReasonRepository } from '../../domain/ports/loss-reason-repository.port.js';\n\nexport class InMemoryLossReasonRepository implements LossReasonRepository {\n private readonly items = new Map<string, LossReason>();\n\n constructor(private readonly clock: Clock = systemClock) {}\n\n async findById(id: string): Promise<LossReason | null> {\n return this.items.get(id) ?? null;\n }\n\n async list(activeOnly = true): Promise<LossReason[]> {\n return [...this.items.values()].filter((r) => (activeOnly ? r.active : true));\n }\n\n async create(input: Omit<LossReason, 'createdAt' | 'updatedAt'>): Promise<LossReason> {\n const now = this.clock.now();\n const r: LossReason = { ...input, createdAt: now, updatedAt: now };\n this.items.set(r.id, r);\n return r;\n }\n\n async update(id: string, patch: Partial<LossReason>): Promise<LossReason> {\n const e = this.items.get(id);\n if (!e) throw new Error(`LossReason '${id}' not found`);\n const r: LossReason = { ...e, ...patch, id, updatedAt: this.clock.now() };\n this.items.set(id, r);\n return r;\n }\n}\n","import type { Note } from '../../domain/entities/note.js';\nimport type { SubjectKind } from '../../domain/enums/activity.js';\nimport type { Clock } from '../../domain/ports/clock.port.js';\nimport { systemClock } from '../../domain/ports/clock.port.js';\nimport type { NoteRepository } from '../../domain/ports/note-repository.port.js';\n\nexport class InMemoryNoteRepository implements NoteRepository {\n private readonly items = new Map<string, Note>();\n\n constructor(private readonly clock: Clock = systemClock) {}\n\n async findById(id: string): Promise<Note | null> {\n return this.items.get(id) ?? null;\n }\n\n async listBySubject(subjectKind: SubjectKind, subjectId: string): Promise<Note[]> {\n return [...this.items.values()].filter(\n (n) => n.subjectKind === subjectKind && n.subjectId === subjectId,\n );\n }\n\n async create(input: Omit<Note, 'createdAt' | 'updatedAt'>): Promise<Note> {\n const now = this.clock.now();\n const n: Note = { ...input, createdAt: now, updatedAt: now };\n this.items.set(n.id, n);\n return n;\n }\n\n async update(id: string, patch: Partial<Note>): Promise<Note> {\n const e = this.items.get(id);\n if (!e) throw new Error(`Note '${id}' not found`);\n const n: Note = { ...e, ...patch, id, updatedAt: this.clock.now() };\n this.items.set(id, n);\n return n;\n }\n\n async delete(id: string): Promise<void> {\n this.items.delete(id);\n }\n}\n","import type { Opportunity } from '../../domain/entities/opportunity.js';\nimport type { OpportunityStatus } from '../../domain/enums/opportunity-status.js';\nimport type { Clock } from '../../domain/ports/clock.port.js';\nimport { systemClock } from '../../domain/ports/clock.port.js';\nimport type {\n OpportunityFilter,\n OpportunityRepository,\n} from '../../domain/ports/opportunity-repository.port.js';\n\nexport class InMemoryOpportunityRepository implements OpportunityRepository {\n private readonly items = new Map<string, Opportunity>();\n\n constructor(private readonly clock: Clock = systemClock) {}\n\n async findById(id: string): Promise<Opportunity | null> {\n return this.items.get(id) ?? null;\n }\n\n async list(filter: OpportunityFilter = {}): Promise<Opportunity[]> {\n return [...this.items.values()].filter((o) => {\n if (filter.status !== undefined) {\n const allowed = Array.isArray(filter.status) ? filter.status : [filter.status];\n if (!allowed.includes(o.status)) return false;\n }\n if (filter.pipelineId !== undefined && o.pipelineId !== filter.pipelineId) return false;\n if (filter.stageId !== undefined && o.stageId !== filter.stageId) return false;\n if (filter.ownerId !== undefined && o.ownerId !== filter.ownerId) return false;\n if (filter.accountId !== undefined && o.accountId !== filter.accountId) return false;\n if (filter.closingBetween !== undefined) {\n if (!o.expectedCloseAt) return false;\n if (\n o.expectedCloseAt < filter.closingBetween.from ||\n o.expectedCloseAt > filter.closingBetween.to\n )\n return false;\n }\n return true;\n });\n }\n\n async create(input: Omit<Opportunity, 'createdAt' | 'updatedAt'>): Promise<Opportunity> {\n const now = this.clock.now();\n const o: Opportunity = { ...input, createdAt: now, updatedAt: now };\n this.items.set(o.id, o);\n return o;\n }\n\n async update(id: string, patch: Partial<Opportunity>): Promise<Opportunity> {\n const e = this.items.get(id);\n if (!e) throw new Error(`Opportunity '${id}' not found`);\n const o: Opportunity = { ...e, ...patch, id, updatedAt: this.clock.now() };\n this.items.set(id, o);\n return o;\n }\n\n async delete(id: string): Promise<void> {\n this.items.delete(id);\n }\n\n async casStatus(\n id: string,\n expected: OpportunityStatus,\n next: OpportunityStatus,\n patch: Partial<Opportunity> = {},\n ): Promise<Opportunity | null> {\n const e = this.items.get(id);\n if (!e || e.status !== expected) return null;\n const updated: Opportunity = {\n ...e,\n ...patch,\n id,\n status: next,\n updatedAt: this.clock.now(),\n };\n this.items.set(id, updated);\n return updated;\n }\n\n async casStage(\n id: string,\n expectedStageId: string,\n nextStageId: string,\n patch: Partial<Opportunity> = {},\n ): Promise<Opportunity | null> {\n const e = this.items.get(id);\n if (!e || e.stageId !== expectedStageId) return null;\n const updated: Opportunity = {\n ...e,\n ...patch,\n id,\n stageId: nextStageId,\n updatedAt: this.clock.now(),\n };\n this.items.set(id, updated);\n return updated;\n }\n}\n","import type { Pipeline } from '../../domain/entities/pipeline.js';\nimport type { Clock } from '../../domain/ports/clock.port.js';\nimport { systemClock } from '../../domain/ports/clock.port.js';\nimport type { PipelineRepository } from '../../domain/ports/pipeline-repository.port.js';\n\nexport class InMemoryPipelineRepository implements PipelineRepository {\n private readonly items = new Map<string, Pipeline>();\n\n constructor(private readonly clock: Clock = systemClock) {}\n\n async findById(id: string): Promise<Pipeline | null> {\n return this.items.get(id) ?? null;\n }\n\n async list(includeArchived = false): Promise<Pipeline[]> {\n return [...this.items.values()].filter((p) => includeArchived || !p.isArchived);\n }\n\n async create(input: Omit<Pipeline, 'createdAt' | 'updatedAt'>): Promise<Pipeline> {\n const now = this.clock.now();\n const p: Pipeline = { ...input, createdAt: now, updatedAt: now };\n this.items.set(p.id, p);\n return p;\n }\n\n async update(id: string, patch: Partial<Pipeline>): Promise<Pipeline> {\n const e = this.items.get(id);\n if (!e) throw new Error(`Pipeline '${id}' not found`);\n const p: Pipeline = { ...e, ...patch, id, updatedAt: this.clock.now() };\n this.items.set(id, p);\n return p;\n }\n}\n","import type {\n OutreachPort,\n OutreachSendRequest,\n OutreachSendResult,\n} from '../../domain/ports/outreach.port.js';\n\nexport class NullOutreachPort implements OutreachPort {\n private counter = 0;\n async send(_request: OutreachSendRequest): Promise<OutreachSendResult> {\n return {\n providerMessageId: `null_msg_${++this.counter}`,\n acceptedAt: new Date(),\n };\n }\n}\n\nexport interface RecordedOutreach extends OutreachSendRequest, OutreachSendResult {}\n\nexport class InMemoryOutreachPort implements OutreachPort {\n readonly sent: RecordedOutreach[] = [];\n private counter = 0;\n constructor(private readonly nowMs: () => number = () => Date.now()) {}\n\n async send(request: OutreachSendRequest): Promise<OutreachSendResult> {\n const res: OutreachSendResult = {\n providerMessageId: `msg_${++this.counter}`,\n acceptedAt: new Date(this.nowMs()),\n };\n this.sent.push({ ...request, ...res });\n return res;\n }\n}\n","/**\n * CRM event catalog — Zod-source-of-truth definitions for every\n * `crm:*` event.\n *\n * Each definition exposes:\n * - `.zodSchema` — source of truth, used by host code's `.safeParse()`\n * - `.schema` — JSON Schema derived via `z.toJSONSchema()`, consumed\n * by Arc's EventRegistry + OpenAPI plugin\n * - `.create(...)` — DomainEvent envelope builder, structurally compatible\n * with `@classytic/arc`'s `EventDefinitionOutput`\n *\n * Structurally compatible with Arc 2.10's `EventRegistry` — hosts register\n * `crmEventDefinitions` directly, no adapter code. CRM does NOT import from\n * `@classytic/arc` (PACKAGE_RULES §11); compatibility is purely structural.\n *\n * Timestamps are modelled as `z.iso.datetime()` — events cross the wire and\n * JSON Schema cannot represent `Date` (PACKAGE_RULES §18.5 + §25). Subscribers\n * that need a `Date` instance call `new Date(payload.completedAt)`.\n *\n * @example Wiring into an Arc app\n * ```ts\n * import { createEventRegistry } from '@classytic/arc/events';\n * import { crmEventDefinitions } from '@classytic/crm';\n *\n * const registry = createEventRegistry();\n * for (const def of crmEventDefinitions) registry.register(def);\n *\n * const app = await createApp({\n * arcPlugins: { events: { registry, validateMode: 'reject' } },\n * });\n * ```\n */\nimport { z } from 'zod';\nimport type { DomainEvent } from '@classytic/primitives/events';\nimport { createEvent as createPrimitiveEvent } from '@classytic/primitives/events';\nimport { CRM_EVENTS } from './event-constants.js';\n\n// ─── Definition shape (structurally compatible with Arc EventRegistry) ────\n\nexport interface CrmEventSchema {\n type: 'object';\n properties?: Record<string, { type?: string; format?: string; [key: string]: unknown }>;\n required?: string[];\n [key: string]: unknown;\n}\n\nexport interface CrmEventDefinition<TSchema extends z.ZodType = z.ZodType> {\n readonly name: string;\n readonly version: number;\n readonly description?: string;\n readonly schema: CrmEventSchema;\n readonly zodSchema: TSchema;\n create(\n payload: z.infer<TSchema>,\n meta?: Partial<DomainEvent['meta']>,\n ): DomainEvent<z.infer<TSchema>>;\n readonly __payload?: z.infer<TSchema>;\n}\n\nexport type CrmEventPayloadOf<D> =\n D extends CrmEventDefinition<infer S> ? z.infer<S> : never;\n\nfunction defineCrmEvent<TSchema extends z.ZodType>(input: {\n name: string;\n version?: number;\n description?: string;\n zodSchema: TSchema;\n}): CrmEventDefinition<TSchema> {\n const { name, version = 1, description, zodSchema } = input;\n const def: CrmEventDefinition<TSchema> = {\n name,\n version,\n schema: z.toJSONSchema(zodSchema) as CrmEventSchema,\n zodSchema,\n create(payload, meta) {\n return createPrimitiveEvent(name, payload, { source: 'crm', ...meta });\n },\n };\n if (description !== undefined) {\n (def as { description: string }).description = description;\n }\n return def;\n}\n\n// ─── Reusable fragments ───────────────────────────────────────────────────\n\nconst money = z.object({\n amount: z.number().nonnegative(),\n currency: z.string().length(3),\n});\n\nconst subjectKind = z.enum(['lead', 'contact', 'account', 'opportunity']);\n\nconst stageRef = z.object({\n stageId: z.string(),\n sequence: z.number().int().nonnegative(),\n});\n\n// ─── Lead ─────────────────────────────────────────────────────────────────\n\nconst leadCreatedSchema = z.object({\n leadId: z.string(),\n email: z.string().optional(),\n score: z.number().optional(),\n});\n\nconst leadStatusChangedSchema = z.object({\n leadId: z.string(),\n from: z.string(),\n to: z.string(),\n});\n\nconst leadScoredSchema = z.object({\n leadId: z.string(),\n score: z.number(),\n});\n\nconst leadConvertedSchema = z.object({\n leadId: z.string(),\n contactId: z.string(),\n accountId: z.string().optional(),\n opportunityId: z.string(),\n});\n\nconst leadDisqualifiedSchema = z.object({\n leadId: z.string(),\n reason: z.string(),\n});\n\n// ─── Account ──────────────────────────────────────────────────────────────\n\nconst accountCreatedSchema = z.object({\n accountId: z.string(),\n});\n\nconst accountUpdatedSchema = z.object({\n accountId: z.string(),\n});\n\n// ─── Contact ──────────────────────────────────────────────────────────────\n\nconst contactCreatedSchema = z.object({\n contactId: z.string(),\n email: z.string().optional(),\n});\n\nconst contactUpdatedSchema = z.object({\n contactId: z.string(),\n});\n\n// ─── Opportunity ──────────────────────────────────────────────────────────\n\nconst opportunityCreatedSchema = z.object({\n opportunityId: z.string(),\n pipelineId: z.string(),\n stageId: z.string(),\n});\n\nconst opportunityStageAdvancedSchema = z.object({\n opportunityId: z.string(),\n from: stageRef,\n to: stageRef,\n});\n\nconst opportunityStageRegressedSchema = opportunityStageAdvancedSchema;\n\nconst opportunityWonSchema = z.object({\n opportunityId: z.string(),\n amount: money.optional(),\n});\n\nconst opportunityLostSchema = z.object({\n opportunityId: z.string(),\n lostReasonId: z.string().optional(),\n});\n\nconst opportunityAbandonedSchema = z.object({\n opportunityId: z.string(),\n});\n\n// ─── Pipeline ─────────────────────────────────────────────────────────────\n\nconst pipelineCreatedSchema = z.object({\n pipelineId: z.string(),\n});\n\nconst pipelineArchivedSchema = z.object({\n pipelineId: z.string(),\n});\n\n// ─── Activity / Note ──────────────────────────────────────────────────────\n\nconst activityPlannedSchema = z.object({\n activityId: z.string(),\n type: z.string(),\n subjectKind,\n subjectId: z.string(),\n});\n\nconst activityCompletedSchema = z.object({\n activityId: z.string(),\n completedAt: z.iso.datetime(),\n});\n\nconst activityCancelledSchema = z.object({\n activityId: z.string(),\n reason: z.string().optional(),\n});\n\nconst noteAddedSchema = z.object({\n noteId: z.string(),\n subjectKind,\n subjectId: z.string(),\n});\n\n// ─── Inferred payload types (exported for host subscribers) ──────────────\n\nexport type LeadCreatedPayload = z.infer<typeof leadCreatedSchema>;\nexport type LeadStatusChangedPayload = z.infer<typeof leadStatusChangedSchema>;\nexport type LeadScoredPayload = z.infer<typeof leadScoredSchema>;\nexport type LeadConvertedPayload = z.infer<typeof leadConvertedSchema>;\nexport type LeadDisqualifiedPayload = z.infer<typeof leadDisqualifiedSchema>;\nexport type AccountCreatedPayload = z.infer<typeof accountCreatedSchema>;\nexport type AccountUpdatedPayload = z.infer<typeof accountUpdatedSchema>;\nexport type ContactCreatedPayload = z.infer<typeof contactCreatedSchema>;\nexport type ContactUpdatedPayload = z.infer<typeof contactUpdatedSchema>;\nexport type OpportunityCreatedPayload = z.infer<typeof opportunityCreatedSchema>;\nexport type OpportunityStageAdvancedPayload = z.infer<typeof opportunityStageAdvancedSchema>;\nexport type OpportunityStageRegressedPayload = z.infer<typeof opportunityStageRegressedSchema>;\nexport type OpportunityWonPayload = z.infer<typeof opportunityWonSchema>;\nexport type OpportunityLostPayload = z.infer<typeof opportunityLostSchema>;\nexport type OpportunityAbandonedPayload = z.infer<typeof opportunityAbandonedSchema>;\nexport type PipelineCreatedPayload = z.infer<typeof pipelineCreatedSchema>;\nexport type PipelineArchivedPayload = z.infer<typeof pipelineArchivedSchema>;\nexport type ActivityPlannedPayload = z.infer<typeof activityPlannedSchema>;\nexport type ActivityCompletedPayload = z.infer<typeof activityCompletedSchema>;\nexport type ActivityCancelledPayload = z.infer<typeof activityCancelledSchema>;\nexport type NoteAddedPayload = z.infer<typeof noteAddedSchema>;\n\n// ─── Event definitions ────────────────────────────────────────────────────\n\nexport const LeadCreated = defineCrmEvent({\n name: CRM_EVENTS.LEAD_CREATED,\n description: 'A new lead was captured and initial score computed.',\n zodSchema: leadCreatedSchema,\n});\n\nexport const LeadStatusChanged = defineCrmEvent({\n name: CRM_EVENTS.LEAD_STATUS_CHANGED,\n description: 'A lead transitioned between lifecycle statuses.',\n zodSchema: leadStatusChangedSchema,\n});\n\nexport const LeadScored = defineCrmEvent({\n name: CRM_EVENTS.LEAD_SCORED,\n description: 'A lead score was recomputed and persisted.',\n zodSchema: leadScoredSchema,\n});\n\nexport const LeadConverted = defineCrmEvent({\n name: CRM_EVENTS.LEAD_CONVERTED,\n description: 'A lead was converted into a Contact + (optional) Account + Opportunity.',\n zodSchema: leadConvertedSchema,\n});\n\nexport const LeadDisqualified = defineCrmEvent({\n name: CRM_EVENTS.LEAD_DISQUALIFIED,\n description: 'A lead was disqualified with a reason.',\n zodSchema: leadDisqualifiedSchema,\n});\n\nexport const AccountCreated = defineCrmEvent({\n name: CRM_EVENTS.ACCOUNT_CREATED,\n description: 'A new account was created.',\n zodSchema: accountCreatedSchema,\n});\n\nexport const AccountUpdated = defineCrmEvent({\n name: CRM_EVENTS.ACCOUNT_UPDATED,\n description: 'An account record was updated.',\n zodSchema: accountUpdatedSchema,\n});\n\nexport const ContactCreated = defineCrmEvent({\n name: CRM_EVENTS.CONTACT_CREATED,\n description: 'A new contact was created.',\n zodSchema: contactCreatedSchema,\n});\n\nexport const ContactUpdated = defineCrmEvent({\n name: CRM_EVENTS.CONTACT_UPDATED,\n description: 'A contact record was updated.',\n zodSchema: contactUpdatedSchema,\n});\n\nexport const OpportunityCreated = defineCrmEvent({\n name: CRM_EVENTS.OPPORTUNITY_CREATED,\n description: 'A new opportunity was created in a pipeline at a starting stage.',\n zodSchema: opportunityCreatedSchema,\n});\n\nexport const OpportunityStageAdvanced = defineCrmEvent({\n name: CRM_EVENTS.OPPORTUNITY_STAGE_ADVANCED,\n description: 'An opportunity moved to a later stage in its pipeline.',\n zodSchema: opportunityStageAdvancedSchema,\n});\n\nexport const OpportunityStageRegressed = defineCrmEvent({\n name: CRM_EVENTS.OPPORTUNITY_STAGE_REGRESSED,\n description: 'An opportunity moved back to an earlier stage in its pipeline.',\n zodSchema: opportunityStageRegressedSchema,\n});\n\nexport const OpportunityWon = defineCrmEvent({\n name: CRM_EVENTS.OPPORTUNITY_WON,\n description: 'An opportunity closed as won.',\n zodSchema: opportunityWonSchema,\n});\n\nexport const OpportunityLost = defineCrmEvent({\n name: CRM_EVENTS.OPPORTUNITY_LOST,\n description: 'An opportunity closed as lost.',\n zodSchema: opportunityLostSchema,\n});\n\nexport const OpportunityAbandoned = defineCrmEvent({\n name: CRM_EVENTS.OPPORTUNITY_ABANDONED,\n description: 'An opportunity was abandoned without a close outcome.',\n zodSchema: opportunityAbandonedSchema,\n});\n\nexport const PipelineCreated = defineCrmEvent({\n name: CRM_EVENTS.PIPELINE_CREATED,\n description: 'A new pipeline was created.',\n zodSchema: pipelineCreatedSchema,\n});\n\nexport const PipelineArchived = defineCrmEvent({\n name: CRM_EVENTS.PIPELINE_ARCHIVED,\n description: 'A pipeline was archived.',\n zodSchema: pipelineArchivedSchema,\n});\n\nexport const ActivityPlanned = defineCrmEvent({\n name: CRM_EVENTS.ACTIVITY_PLANNED,\n description: 'An activity was planned against a polymorphic subject.',\n zodSchema: activityPlannedSchema,\n});\n\nexport const ActivityCompleted = defineCrmEvent({\n name: CRM_EVENTS.ACTIVITY_COMPLETED,\n description: 'An activity was marked completed.',\n zodSchema: activityCompletedSchema,\n});\n\nexport const ActivityCancelled = defineCrmEvent({\n name: CRM_EVENTS.ACTIVITY_CANCELLED,\n description: 'An activity was cancelled.',\n zodSchema: activityCancelledSchema,\n});\n\nexport const NoteAdded = defineCrmEvent({\n name: CRM_EVENTS.NOTE_ADDED,\n description: 'A note was attached to a polymorphic subject.',\n zodSchema: noteAddedSchema,\n});\n\n// ─── Aggregate catalog ────────────────────────────────────────────────────\n\n/**\n * Every CRM event defined in the package — pass to Arc's `EventRegistry`.\n * Hosts wire ONE array; the whole `crm:*` namespace becomes introspectable\n * via OpenAPI and auto-validated at publish time when\n * `eventPlugin({ validateMode: 'reject' })` is set.\n */\nexport const crmEventDefinitions: ReadonlyArray<CrmEventDefinition> = [\n LeadCreated,\n LeadStatusChanged,\n LeadScored,\n LeadConverted,\n LeadDisqualified,\n AccountCreated,\n AccountUpdated,\n ContactCreated,\n ContactUpdated,\n OpportunityCreated,\n OpportunityStageAdvanced,\n OpportunityStageRegressed,\n OpportunityWon,\n OpportunityLost,\n OpportunityAbandoned,\n PipelineCreated,\n PipelineArchived,\n ActivityPlanned,\n ActivityCompleted,\n ActivityCancelled,\n NoteAdded,\n];\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@classytic/crm",
3
+ "version": "0.1.0",
4
+ "description": "Framework-agnostic CRM primitive — leads, accounts, contacts, opportunities, pipelines, activities. Pure TypeScript, event-driven, ports-based. Build the next Buffer, HubSpot, or Pipedrive on top of it.",
5
+ "author": "Classytic",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "sideEffects": false,
9
+ "engines": {
10
+ "node": ">=22"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ },
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "CHANGELOG.md",
27
+ "LICENSE"
28
+ ],
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/classytic/crm.git"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/classytic/crm/issues"
35
+ },
36
+ "homepage": "https://github.com/classytic/crm#readme",
37
+ "scripts": {
38
+ "build": "tsdown",
39
+ "typecheck": "tsc --noEmit",
40
+ "test": "vitest run --project unit --project integration",
41
+ "test:unit": "vitest run --project unit",
42
+ "test:integration": "vitest run --project integration",
43
+ "test:watch": "vitest --project unit --project integration"
44
+ },
45
+ "peerDependencies": {
46
+ "@classytic/primitives": ">=0.1.0",
47
+ "zod": ">=4.0.0"
48
+ },
49
+ "devDependencies": {
50
+ "@classytic/primitives": "^0.1.0",
51
+ "@types/node": "^25.5.0",
52
+ "tsdown": "^0.14.2",
53
+ "typescript": "^5.8.2",
54
+ "vitest": "^3.2.4",
55
+ "zod": "^4.3.6"
56
+ }
57
+ }