// The port the crm module's use cases are written against: the platform's CRM service, in this // module's own words. adapters/service.ts is the one place that knows `@conusai-sdk/crm` exists, and // it turns every refusal into the kit's app errors (`businessAppError` of // `@conusai-kit/business`) before core sees it. import { token } from "@conusai-kit/core" import type { ContactView, PartyView } from "../contract/index.js" export interface Page { readonly rows: T[] readonly after?: string } /** What a `read` procedure may ask the service: nothing that changes a row. */ export interface CrmReads { /** The immutable app/environment binding UUID for attributed member references. */ readonly bindingId: string listParties(query: { q?: string; after?: string; limit: number }): Promise> getParty(partyId: string): Promise listContacts(partyId: string, query: { after?: string; limit: number }): Promise> } export interface CrmService extends CrmReads { createParty(input: { kind: "person" | "organization" displayName: string legalName?: string country: string identifiers: { scheme: string; value: string }[] key: string }): Promise addContact(input: { partyId: string kind: "email" | "phone" | "web" value: string purposes: ContactView["purposes"] isPrimary: boolean /** `YYYY-MM-DD`: the day the contact point starts to hold. */ effectiveFrom: string key: string }): Promise } /** The read view a `read` or `write` handler receives: the kind is a capability, not a label. */ export const crmReadsOf = (service: CrmReads): CrmReads => ({ bindingId: service.bindingId, listParties: (query) => service.listParties(query), getParty: (partyId) => service.getParty(partyId), listContacts: (partyId, query) => service.listContacts(partyId, query), }) /** * Bound by a composition root or a test that brings its own client (another organization, the * business-services fake). With nothing bound, the module calls this deployment's service. */ export const CRM_SERVICE = token("crm.service", { views: { read: crmReadsOf, write: crmReadsOf } })