@fonderie/customers 6.2.2 → 6.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +10 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +11 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/dtos/customer.ts","../src/models/customer.model.ts","../src/models/customer-relationship.model.ts","../src/models/customer-address.model.ts","../src/models/customer-email.model.ts","../src/models/customer-label.model.ts","../src/models/customer-note.model.ts","../src/models/customer-phone.model.ts","../src/models/customer-tag.model.ts","../src/routes.ts","../src/schemas.ts","../src/controllers/customer.controller.ts","../src/utils.ts","../src/controllers/customer-address.controller.ts","../src/controllers/customer-email.controller.ts","../src/controllers/customer-note.controller.ts","../src/controllers/customer-phone.controller.ts","../src/controllers/customer-tag.controller.ts","../src/controllers/customer-label.controller.ts","../src/controllers/customer-relationship.controller.ts","../src/module.ts"],"sourcesContent":["export type { CustomersEventKey, ICustomersConfig } from './config';\nexport { EVENT_KEYS } from './config';\nexport type {\n\tIAddressDTO,\n\tICustomerAddressDTO,\n\tICustomerDetailDTO,\n\tICustomerDTO,\n\tICustomerEmailDTO,\n\tICustomerNoteDTO,\n\tICustomerPhoneDTO,\n\tICustomerLabelDTO,\n} from './dtos/customer';\nexport {\n\ttoAddressDTO,\n\ttoCustomerAddressDTO,\n\ttoCustomerDetailDTO,\n\ttoCustomerDTO,\n\ttoCustomerEmailDTO,\n\ttoCustomerNoteDTO,\n\ttoCustomerPhoneDTO,\n\ttoCustomerLabelDTO,\n} from './dtos/customer';\nexport {\n\tCustomerAddressModel,\n\tCustomerEmailModel,\n\tCustomerModel,\n\tCustomerNoteModel,\n\tCustomerPhoneModel,\n\tCustomerTagModel,\n} from './models';\nexport { CustomersModule } from './module';\nexport type {\n\tCustomerType,\n\tIAddress,\n\tICustomer,\n\tICustomerAddress,\n\tICustomerDetail,\n\tICustomerEmail,\n\tICustomerNote,\n\tICustomerPhone,\n} from './types';\n\n// Request validation — enforced contract for body-taking routes; exported\n// for docs generation and typed clients.\nexport * as schemas from './schemas';\n","export const EVENT_KEYS = {\n\tcustomerCreated: 'fonderie.customer.created',\n\tcustomerUpdated: 'fonderie.customer.updated',\n\tcustomerDeleted: 'fonderie.customer.deleted',\n\tcustomerBlacklisted: 'fonderie.customer.blacklisted',\n\tcustomerUnblacklisted: 'fonderie.customer.unblacklisted',\n} as const;\n\nexport type CustomersEventKey = (typeof EVENT_KEYS)[keyof typeof EVENT_KEYS];\n\nexport const DEFAULT_REFERENCE_CODE_PREFIX = 'CLT';\n\n// Referral codes are RANDOM (not sequential like reference codes) so they are\n// safe to share publicly — a customer can't guess another's by incrementing.\n// Unambiguous alphabet: no 0/O, 1/I/L. 8 chars over 31 symbols ≈ 8.5e11 space,\n// so collisions within a workspace are astronomically rare (and the unique\n// index is the hard guard; generation retries on the vanishing chance).\nexport const REFERRAL_CODE_ALPHABET = '23456789ABCDEFGHJKMNPQRSTUVWXYZ';\nexport const REFERRAL_CODE_LENGTH = 8;\n\nexport type ICustomersConfig = {\n\t/** Prefix used when auto-generating customer reference codes. Defaults to DEFAULT_REFERENCE_CODE_PREFIX. */\n\treferenceCodePrefix?: string;\n};\n","import { arrayOrEmpty, booleanOrFalse, dateOrEmpty, stringOrEmpty } from '@fonderie/core';\n\nimport type { CustomerLabelType, CustomerSex } from '../types';\nimport type {\n\tIAddress,\n\tICustomer,\n\tICustomerAddress,\n\tICustomerDetail,\n\tICustomerDetailD2,\n\tICustomerEmail,\n\tICustomerNote,\n\tICustomerPhone,\n\tICustomerRelationship,\n\tICustomerRelationshipExpanded,\n\tICustomerRelationshipExpandedD2,\n\tICustomerShallow,\n\tICustomerLabel,\n} from '../types';\n\nexport interface ICustomerDTO {\n\tid: string;\n\ttype: string;\n\tsex: CustomerSex;\n\tfirstName: string;\n\tlastName: string;\n\tcompanyName: string;\n\tavatarUrl: string;\n\tlocale: string;\n\treferenceCode: string;\n\treferralCode: string;\n\treferredBy: string | null;\n\tblacklisted: { status: boolean; reason: string | null };\n\tcreatedBy: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface ICustomerRelationshipDTO {\n\tid: string;\n\trelatedId: string;\n\trelationship: string;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface ICustomerShallowDTO extends ICustomerDTO {\n\temails: ICustomerEmailDTO[];\n\tphones: ICustomerPhoneDTO[];\n\taddresses: ICustomerAddressDTO[];\n\tnotes: ICustomerNoteDTO[];\n\ttags: string[];\n}\n\n// Flat merge: relationship metadata + customer fields spread at the same level.\n// `id` is the relationship record id; `customerId` is the related customer's\n// id. NOTE: the spread customer fields include the related CUSTOMER's\n// createdAt/updatedAt — `relationshipCreatedAt` is when the relationship\n// itself was created (what ICustomerRelationshipDTO.createdAt means).\nexport type ICustomerRelationshipExpandedDTO = Omit<ICustomerShallowDTO, 'id'> & {\n\tid: string;\n\tcustomerId: string;\n\trelationship: string;\n\tisPrimary: boolean;\n\trelationshipCreatedAt: string;\n};\n\nexport interface ICustomerDetailDTO extends ICustomerDTO {\n\temails: ICustomerEmailDTO[];\n\tphones: ICustomerPhoneDTO[];\n\taddresses: ICustomerAddressDTO[];\n\tnotes: ICustomerNoteDTO[];\n\trelationships: ICustomerRelationshipExpandedDTO[];\n\ttags: string[];\n}\n\n// Depth-2 DTOs: each depth-1 relationship entry carries its own relationships array.\nexport type ICustomerRelationshipExpandedD2DTO = ICustomerRelationshipExpandedDTO & {\n\trelationships: ICustomerRelationshipExpandedDTO[];\n};\n\nexport interface ICustomerDetailD2DTO extends Omit<ICustomerDetailDTO, 'relationships'> {\n\trelationships: ICustomerRelationshipExpandedD2DTO[];\n}\n\nexport interface ICustomerEmailDTO {\n\tid: string;\n\temail: string;\n\tlabel: string;\n\t// Id of the shared label row (see /customers/labels) — null when unlabeled.\n\tlabelId: string | null;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface ICustomerPhoneDTO {\n\tid: string;\n\tphone: string;\n\tlabel: string;\n\t// Id of the shared label row (see /customers/labels) — null when unlabeled.\n\tlabelId: string | null;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface IAddressDTO {\n\tcountryIso: string;\n\tsubdivision1Iso: string;\n\tsubdivision2Iso: string;\n\tzipPostalCode: string;\n\tunit: string;\n\tline1: string;\n\tline2: string;\n}\n\nexport interface ICustomerAddressDTO {\n\tid: string;\n\tlabel: string;\n\t// Id of the shared label row (see /customers/labels) — null when unlabeled.\n\tlabelId: string | null;\n\tisPrimary: boolean;\n\taddress: IAddressDTO;\n}\n\nexport interface ICustomerNoteDTO {\n\tid: string;\n\tauthorId: string;\n\tbody: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\n// Serialized shared label (GET /customers/labels) — the one customers\n// response that previously bypassed DTO mapping and leaked raw Date rows.\nexport interface ICustomerLabelDTO {\n\tid: string;\n\ttype: CustomerLabelType;\n\tvalue: string;\n\tcreatedAt: string;\n}\n\nconst VALID_SEX: CustomerSex[] = ['UNKNOWN', 'MALE', 'FEMALE'];\n\nexport function toCustomerDTO(c: ICustomer): ICustomerDTO {\n\treturn {\n\t\tid: stringOrEmpty(c.id),\n\t\ttype: stringOrEmpty(c.type),\n\t\tsex: VALID_SEX.includes(c.sex as CustomerSex) ? (c.sex as CustomerSex) : 'UNKNOWN',\n\t\tfirstName: stringOrEmpty(c.firstName),\n\t\tlastName: stringOrEmpty(c.lastName),\n\t\tcompanyName: stringOrEmpty(c.companyName),\n\t\tavatarUrl: stringOrEmpty(c.avatarUrl),\n\t\tlocale: stringOrEmpty(c.locale),\n\t\treferenceCode: stringOrEmpty(c.referenceCode),\n\t\treferralCode: stringOrEmpty(c.referralCode),\n\t\treferredBy: c.referredBy ?? null,\n\t\tblacklisted: { status: booleanOrFalse(c.isBlacklisted), reason: c.blacklistReason ?? null },\n\t\tcreatedBy: stringOrEmpty(c.createdBy),\n\t\tcreatedAt: dateOrEmpty(c.createdAt),\n\t\tupdatedAt: dateOrEmpty(c.updatedAt),\n\t};\n}\n\nexport function toCustomerRelationshipDTO(r: ICustomerRelationship): ICustomerRelationshipDTO {\n\treturn {\n\t\tid: stringOrEmpty(r.id),\n\t\trelatedId: stringOrEmpty(r.relatedId),\n\t\trelationship: stringOrEmpty(r.relationship),\n\t\tisPrimary: booleanOrFalse(r.isPrimary),\n\t\tcreatedAt: dateOrEmpty(r.createdAt),\n\t};\n}\n\nexport function toCustomerShallowDTO(c: ICustomerShallow): ICustomerShallowDTO {\n\treturn {\n\t\t...toCustomerDTO(c),\n\t\temails: arrayOrEmpty<ICustomerEmail>(c.emails).map(toCustomerEmailDTO),\n\t\tphones: arrayOrEmpty<ICustomerPhone>(c.phones).map(toCustomerPhoneDTO),\n\t\taddresses: arrayOrEmpty<ICustomerAddress>(c.addresses).map(toCustomerAddressDTO),\n\t\tnotes: arrayOrEmpty<ICustomerNote>(c.notes).map(toCustomerNoteDTO),\n\t\ttags: arrayOrEmpty<string>(c.tags),\n\t};\n}\n\nexport function toCustomerRelationshipExpandedDTO(r: ICustomerRelationshipExpanded): ICustomerRelationshipExpandedDTO {\n\tconst { id: customerId, ...customerFields } = toCustomerShallowDTO(r.customer);\n\treturn {\n\t\tid: stringOrEmpty(r.id),\n\t\tcustomerId,\n\t\trelationship: stringOrEmpty(r.relationship),\n\t\tisPrimary: booleanOrFalse(r.isPrimary),\n\t\trelationshipCreatedAt: dateOrEmpty(r.createdAt),\n\t\t...customerFields,\n\t};\n}\n\nexport function toCustomerDetailDTO(c: ICustomerDetail): ICustomerDetailDTO {\n\treturn {\n\t\t...toCustomerDTO(c),\n\t\temails: arrayOrEmpty<ICustomerEmail>(c.emails).map(toCustomerEmailDTO),\n\t\tphones: arrayOrEmpty<ICustomerPhone>(c.phones).map(toCustomerPhoneDTO),\n\t\taddresses: arrayOrEmpty<ICustomerAddress>(c.addresses).map(toCustomerAddressDTO),\n\t\tnotes: arrayOrEmpty<ICustomerNote>(c.notes).map(toCustomerNoteDTO),\n\t\trelationships: arrayOrEmpty<ICustomerRelationshipExpanded>(c.relationships).map(toCustomerRelationshipExpandedDTO),\n\t\ttags: arrayOrEmpty<string>(c.tags),\n\t};\n}\n\nexport function toCustomerRelationshipExpandedD2DTO(r: ICustomerRelationshipExpandedD2): ICustomerRelationshipExpandedD2DTO {\n\tconst { id: customerId, ...customerFields } = toCustomerShallowDTO(r.customer);\n\treturn {\n\t\tid: stringOrEmpty(r.id),\n\t\tcustomerId,\n\t\trelationship: stringOrEmpty(r.relationship),\n\t\tisPrimary: booleanOrFalse(r.isPrimary),\n\t\trelationshipCreatedAt: dateOrEmpty(r.createdAt),\n\t\t...customerFields,\n\t\trelationships: arrayOrEmpty<ICustomerRelationshipExpanded>(r.customer.relationships).map(toCustomerRelationshipExpandedDTO),\n\t};\n}\n\nexport function toCustomerDetailD2DTO(c: ICustomerDetailD2): ICustomerDetailD2DTO {\n\treturn {\n\t\t...toCustomerDTO(c),\n\t\temails: arrayOrEmpty<ICustomerEmail>(c.emails).map(toCustomerEmailDTO),\n\t\tphones: arrayOrEmpty<ICustomerPhone>(c.phones).map(toCustomerPhoneDTO),\n\t\taddresses: arrayOrEmpty<ICustomerAddress>(c.addresses).map(toCustomerAddressDTO),\n\t\tnotes: arrayOrEmpty<ICustomerNote>(c.notes).map(toCustomerNoteDTO),\n\t\trelationships: arrayOrEmpty<ICustomerRelationshipExpandedD2>(c.relationships).map(toCustomerRelationshipExpandedD2DTO),\n\t\ttags: arrayOrEmpty<string>(c.tags),\n\t};\n}\n\nexport function toAddressDTO(a: IAddress): IAddressDTO {\n\treturn {\n\t\tcountryIso: stringOrEmpty(a.countryIso),\n\t\tsubdivision1Iso: stringOrEmpty(a.subdivision1Iso),\n\t\tsubdivision2Iso: stringOrEmpty(a.subdivision2Iso),\n\t\tzipPostalCode: stringOrEmpty(a.zipPostalCode),\n\t\tunit: stringOrEmpty(a.unit),\n\t\tline1: stringOrEmpty(a.line1),\n\t\tline2: stringOrEmpty(a.line2),\n\t};\n}\n\nexport function toCustomerEmailDTO(e: ICustomerEmail): ICustomerEmailDTO {\n\treturn {\n\t\tid: stringOrEmpty(e.id),\n\t\temail: stringOrEmpty(e.email),\n\t\tlabel: stringOrEmpty(e.label),\n\t\tlabelId: e.labelId ?? null,\n\t\tisPrimary: booleanOrFalse(e.isPrimary),\n\t\tcreatedAt: dateOrEmpty(e.createdAt),\n\t};\n}\n\nexport function toCustomerPhoneDTO(p: ICustomerPhone): ICustomerPhoneDTO {\n\treturn {\n\t\tid: stringOrEmpty(p.id),\n\t\tphone: stringOrEmpty(p.phone),\n\t\tlabel: stringOrEmpty(p.label),\n\t\tlabelId: p.labelId ?? null,\n\t\tisPrimary: booleanOrFalse(p.isPrimary),\n\t\tcreatedAt: dateOrEmpty(p.createdAt),\n\t};\n}\n\nexport function toCustomerAddressDTO(ca: ICustomerAddress): ICustomerAddressDTO {\n\treturn {\n\t\tid: stringOrEmpty(ca.addrId),\n\t\tlabel: stringOrEmpty(ca.label),\n\t\tlabelId: ca.labelId ?? null,\n\t\tisPrimary: booleanOrFalse(ca.isPrimary),\n\t\taddress: toAddressDTO(ca.address),\n\t};\n}\n\nexport function toCustomerNoteDTO(n: ICustomerNote): ICustomerNoteDTO {\n\treturn {\n\t\tid: stringOrEmpty(n.id),\n\t\tauthorId: stringOrEmpty(n.authorId),\n\t\tbody: stringOrEmpty(n.body),\n\t\tcreatedAt: dateOrEmpty(n.createdAt),\n\t\tupdatedAt: dateOrEmpty(n.updatedAt),\n\t};\n}\n\nexport function toCustomerLabelDTO(l: ICustomerLabel): ICustomerLabelDTO {\n\treturn {\n\t\tid: stringOrEmpty(l.id),\n\t\ttype: l.type,\n\t\tvalue: stringOrEmpty(l.value),\n\t\tcreatedAt: dateOrEmpty(l.createdAt),\n\t};\n}\n","import { randomInt } from 'node:crypto';\n\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { DEFAULT_REFERENCE_CODE_PREFIX, REFERRAL_CODE_ALPHABET, REFERRAL_CODE_LENGTH } from '../config';\nimport type {\n\tICustomer,\n\tICustomerAddress,\n\tICustomerDetail,\n\tICustomerDetailD2,\n\tICustomerEmail,\n\tICustomerPhone,\n\tICustomerRelationship,\n\tICustomerRelationshipExpanded,\n\tICustomerShallow,\n\tICustomerShallowD2,\n} from '../types';\n\nfunction groupByCustomer<T extends { customerId: string }>(rows: T[]): Map<string, T[]> {\n\treturn rows.reduce((m, r) => {\n\t\tif (!m.has(r.customerId)) m.set(r.customerId, []);\n\t\tm.get(r.customerId)!.push(r);\n\t\treturn m;\n\t}, new Map<string, T[]>());\n}\n\nconst SELECT_CUSTOMER = `\n\tid,\n\tworkspace_id AS \"workspaceId\",\n\ttype,\n\tsex,\n\tfirst_name AS \"firstName\",\n\tlast_name AS \"lastName\",\n\tcompany_name AS \"companyName\",\n\tavatar_url AS \"avatarUrl\",\n\tlocale,\n\treference_code AS \"referenceCode\",\n\treferral_code AS \"referralCode\",\n\treferred_by AS \"referredBy\",\n\tis_blacklisted AS \"isBlacklisted\",\n\tblacklist_reason AS \"blacklistReason\",\n\tcreated_by AS \"createdBy\",\n\tcreated_at AS \"createdAt\",\n\tupdated_at AS \"updatedAt\"\n`;\n\nexport interface ListCustomersOpts {\n\tworkspaceId: string;\n\tsearch?: string | undefined;\n\tblacklisted?: boolean | undefined;\n\tlimit?: number | undefined;\n\toffset?: number | undefined;\n}\n\nexport interface CreateCustomerOpts {\n\tworkspaceId: string;\n\ttype?: string;\n\tsex?: string;\n\tfirstName?: string | null;\n\tlastName?: string | null;\n\tcompanyName?: string | null;\n\tavatarUrl?: string | null;\n\tlocale?: string;\n\t/** Explicit code to assign. Omit to auto-generate ({prefix}-0001, …). */\n\treferenceCode?: string;\n\t/** Prefix used when auto-generating. Defaults to 'CLT'. */\n\treferenceCodePrefix?: string;\n\t/** Explicit referral code. Omit to auto-generate a random, workspace-unique one. */\n\treferralCode?: string;\n\t/** The referrer's referral code (from signup). Resolved to `referredBy` within the same workspace; ignored if it doesn't match a customer. */\n\treferredByCode?: string;\n\tcreatedBy?: string | null;\n}\n\nexport interface UpdateCustomerOpts {\n\ttype?: string;\n\tsex?: string;\n\tfirstName?: string | null;\n\tlastName?: string | null;\n\tcompanyName?: string | null;\n\tavatarUrl?: string | null;\n\tlocale?: string;\n\t/** Explicit code to assign. Omit to keep existing or auto-generate if none. */\n\treferenceCode?: string;\n}\n\nexport class CustomerModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tprivate async allocateCode(workspaceId: string, prefix: string): Promise<string> {\n\t\tconst [row] = await this.store.query<{ nextVal: number }>(\n\t\t\t`INSERT INTO fonderie_customer_sequences (workspace_id, prefix, next_val)\n\t\t\t VALUES ($1, $2, 1)\n\t\t\t ON CONFLICT (workspace_id, prefix) DO UPDATE\n\t\t\t SET next_val = fonderie_customer_sequences.next_val + 1\n\t\t\t RETURNING next_val AS \"nextVal\"`,\n\t\t\t[workspaceId, prefix],\n\t\t);\n\t\treturn `${prefix}-${String(row!.nextVal).padStart(4, '0')}`;\n\t}\n\n\t/** A random referral code (crypto-random over the unambiguous alphabet). */\n\tprivate randomReferralCode(): string {\n\t\tlet out = '';\n\t\tfor (let i = 0; i < REFERRAL_CODE_LENGTH; i++) {\n\t\t\tout += REFERRAL_CODE_ALPHABET[randomInt(REFERRAL_CODE_ALPHABET.length)];\n\t\t}\n\t\treturn out;\n\t}\n\n\t/**\n\t * A referral code unique within the workspace. Random codes collide only\n\t * astronomically rarely; we still pre-check and retry a few times, and the\n\t * unique index is the final guard. Throws only if the space is somehow\n\t * exhausted (not reachable in practice).\n\t */\n\tprivate async allocateReferralCode(workspaceId: string): Promise<string> {\n\t\tfor (let attempt = 0; attempt < 5; attempt++) {\n\t\t\tconst code = this.randomReferralCode();\n\t\t\tconst [hit] = await this.store.query<{ one: number }>(\n\t\t\t\t`SELECT 1 AS one FROM fonderie_customers WHERE workspace_id = $1 AND referral_code = $2 LIMIT 1`,\n\t\t\t\t[workspaceId, code],\n\t\t\t);\n\t\t\tif (!hit) return code;\n\t\t}\n\t\tthrow new Error('could not allocate a unique referral code');\n\t}\n\n\t/** Resolve a referral code to the referring customer's id, within a workspace. */\n\tasync resolveReferralCode(workspaceId: string, code: string): Promise<string | null> {\n\t\tconst [row] = await this.store.query<{ id: string }>(\n\t\t\t`SELECT id FROM fonderie_customers WHERE workspace_id = $1 AND referral_code = $2 LIMIT 1`,\n\t\t\t[workspaceId, code],\n\t\t);\n\t\treturn row?.id ?? null;\n\t}\n\n\tasync list(opts: ListCustomersOpts): Promise<ICustomer[]> {\n\t\tconst conditions: string[] = ['workspace_id = $1'];\n\t\tconst params: unknown[] = [opts.workspaceId];\n\n\t\tif (opts.blacklisted !== undefined) {\n\t\t\tparams.push(opts.blacklisted);\n\t\t\tconditions.push(`is_blacklisted = $${params.length}`);\n\t\t}\n\n\t\tif (opts.search) {\n\t\t\tparams.push(`%${opts.search}%`);\n\t\t\tconst idx = params.length;\n\t\t\tconditions.push(\n\t\t\t\t`(first_name ILIKE $${idx} OR last_name ILIKE $${idx} OR company_name ILIKE $${idx} OR reference_code ILIKE $${idx})`,\n\t\t\t);\n\t\t}\n\n\t\tconst limit = Math.min(opts.limit ?? 50, 200);\n\t\tconst offset = opts.offset ?? 0;\n\t\tparams.push(limit, offset);\n\t\tconst limitIdx = params.length - 1;\n\t\tconst offsetIdx = params.length;\n\n\t\treturn this.store.query<ICustomer>(\n\t\t\t`SELECT ${SELECT_CUSTOMER}\n\t\t\t FROM fonderie_customers\n\t\t\t WHERE ${conditions.join(' AND ')}\n\t\t\t ORDER BY created_at DESC\n\t\t\t LIMIT $${limitIdx} OFFSET $${offsetIdx}`,\n\t\t\tparams,\n\t\t);\n\t}\n\n\tasync count(opts: Omit<ListCustomersOpts, 'limit' | 'offset'>): Promise<number> {\n\t\tconst conditions: string[] = ['workspace_id = $1'];\n\t\tconst params: unknown[] = [opts.workspaceId];\n\n\t\tif (opts.blacklisted !== undefined) {\n\t\t\tparams.push(opts.blacklisted);\n\t\t\tconditions.push(`is_blacklisted = $${params.length}`);\n\t\t}\n\n\t\tif (opts.search) {\n\t\t\tparams.push(`%${opts.search}%`);\n\t\t\tconst idx = params.length;\n\t\t\tconditions.push(\n\t\t\t\t`(first_name ILIKE $${idx} OR last_name ILIKE $${idx} OR company_name ILIKE $${idx} OR reference_code ILIKE $${idx})`,\n\t\t\t);\n\t\t}\n\n\t\tconst [row] = await this.store.query<{ count: string }>(\n\t\t\t`SELECT COUNT(*) AS count\n\t\t\t FROM fonderie_customers\n\t\t\t WHERE ${conditions.join(' AND ')}`,\n\t\t\tparams,\n\t\t);\n\t\treturn Number(row?.count ?? 0);\n\t}\n\n\tasync findById(id: string, workspaceId: string): Promise<ICustomer | null> {\n\t\tconst [row] = await this.store.query<ICustomer>(\n\t\t\t`SELECT ${SELECT_CUSTOMER}\n\t\t\t FROM fonderie_customers\n\t\t\t WHERE id = $1 AND workspace_id = $2`,\n\t\t\t[id, workspaceId],\n\t\t);\n\t\treturn row ?? null;\n\t}\n\n\tasync findDetail(id: string, workspaceId: string, depth: 2): Promise<ICustomerDetailD2 | null>;\n\tasync findDetail(id: string, workspaceId: string, depth?: 1): Promise<ICustomerDetail | null>;\n\tasync findDetail(id: string, workspaceId: string, depth = 1): Promise<ICustomerDetail | ICustomerDetailD2 | null> {\n\t\tconst [row] = await this.store.query<ICustomer>(\n\t\t\t`SELECT ${SELECT_CUSTOMER}\n\t\t\t FROM fonderie_customers\n\t\t\t WHERE id = $1 AND workspace_id = $2`,\n\t\t\t[id, workspaceId],\n\t\t);\n\t\tif (!row) return null;\n\n\t\tconst [emailRows, phoneRows, addressRows, noteRows, relationshipRows, tagRows] = await Promise.all([\n\t\t\tthis.store.query<ICustomerEmail>(\n\t\t\t\t`SELECT id,\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t email,\n\t\t\t\t label_id AS \"labelId\",\n\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\t\t\t\t is_primary AS \"isPrimary\",\n\t\t\t\t created_at AS \"createdAt\"\n\t\t\t\t FROM fonderie_customer_emails\n\t\t\t\t WHERE customer_id = $1\n\t\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t\tthis.store.query<ICustomerPhone>(\n\t\t\t\t`SELECT id,\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t phone,\n\t\t\t\t label_id AS \"labelId\",\n\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\t\t\t\t is_primary AS \"isPrimary\",\n\t\t\t\t created_at AS \"createdAt\"\n\t\t\t\t FROM fonderie_customer_phones\n\t\t\t\t WHERE customer_id = $1\n\t\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t\tthis.store.query<ICustomerAddress>(\n\t\t\t\t`SELECT ca.addr_id AS \"addrId\",\n\t\t\t\t ca.customer_id AS \"customerId\",\n\t\t\t\t ca.label_id AS \"labelId\",\n\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = ca.label_id) AS label,\n\t\t\t\t ca.is_primary AS \"isPrimary\",\n\t\t\t\t jsonb_build_object(\n\t\t\t\t 'id', a.id,\n\t\t\t\t 'countryIso', a.country_iso,\n\t\t\t\t 'subdivision1Iso', a.subdivision1_iso,\n\t\t\t\t 'subdivision2Iso', a.subdivision2_iso,\n\t\t\t\t 'zipPostalCode', a.zip_postal_code,\n\t\t\t\t 'unit', a.unit,\n\t\t\t\t 'line1', a.line1,\n\t\t\t\t 'line2', a.line2\n\t\t\t\t ) AS address\n\t\t\t\t FROM fonderie_customer_addresses ca\n\t\t\t\t JOIN fonderie_addresses a ON a.id = ca.addr_id\n\t\t\t\t WHERE ca.customer_id = $1\n\t\t\t\t ORDER BY ca.is_primary DESC`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t\tthis.store.query<{\n\t\t\t\tid: string;\n\t\t\t\tcustomerId: string;\n\t\t\t\tauthorId: string | null;\n\t\t\t\tbody: string;\n\t\t\t\tcreatedAt: string;\n\t\t\t\tupdatedAt: string;\n\t\t\t}>(\n\t\t\t\t`SELECT id,\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t author_id AS \"authorId\",\n\t\t\t\t body,\n\t\t\t\t created_at AS \"createdAt\",\n\t\t\t\t updated_at AS \"updatedAt\"\n\t\t\t\t FROM fonderie_customer_notes\n\t\t\t\t WHERE customer_id = $1\n\t\t\t\t ORDER BY created_at DESC`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t\tthis.store.query<ICustomerRelationship>(\n\t\t\t\t`SELECT id,\n\t\t\t\t workspace_id AS \"workspaceId\",\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t related_id AS \"relatedId\",\n\t\t\t\t relationship,\n\t\t\t\t is_primary AS \"isPrimary\",\n\t\t\t\t created_at AS \"createdAt\"\n\t\t\t\t FROM fonderie_customer_relationships\n\t\t\t\t WHERE customer_id = $1\n\t\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t\tthis.store.query<{ tag: string }>(\n\t\t\t\t`SELECT tag FROM fonderie_customer_tags WHERE customer_id = $1 ORDER BY tag ASC`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t]);\n\n\t\t// Batch-resolve related customers so the caller gets full detail in one request.\n\t\tconst relatedIds = relationshipRows.map((r) => r.relatedId);\n\t\tlet expandedRelationships: ICustomerRelationshipExpanded[] = [];\n\n\t\tif (relatedIds.length > 0) {\n\t\t\tconst [relCustomers, relEmails, relPhones, relAddresses, relNotes, relTags] = await Promise.all([\n\t\t\t\tthis.store.query<ICustomer>(\n\t\t\t\t\t`SELECT ${SELECT_CUSTOMER} FROM fonderie_customers WHERE id = ANY($1::uuid[]) AND workspace_id = $2`,\n\t\t\t\t\t[relatedIds, workspaceId],\n\t\t\t\t),\n\t\t\t\tthis.store.query<ICustomerEmail>(\n\t\t\t\t\t`SELECT id, customer_id AS \"customerId\", email, label_id AS \"labelId\",\n\t\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\t\t\t\t\t is_primary AS \"isPrimary\", created_at AS \"createdAt\"\n\t\t\t\t\t FROM fonderie_customer_emails WHERE customer_id = ANY($1::uuid[]) ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t\t[relatedIds],\n\t\t\t\t),\n\t\t\t\tthis.store.query<ICustomerPhone>(\n\t\t\t\t\t`SELECT id, customer_id AS \"customerId\", phone, label_id AS \"labelId\",\n\t\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\t\t\t\t\t is_primary AS \"isPrimary\", created_at AS \"createdAt\"\n\t\t\t\t\t FROM fonderie_customer_phones WHERE customer_id = ANY($1::uuid[]) ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t\t[relatedIds],\n\t\t\t\t),\n\t\t\t\tthis.store.query<ICustomerAddress>(\n\t\t\t\t\t`SELECT ca.addr_id AS \"addrId\",\n\t\t\t\t\t ca.customer_id AS \"customerId\",\n\t\t\t\t\t ca.label_id AS \"labelId\",\n\t\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = ca.label_id) AS label,\n\t\t\t\t\t ca.is_primary AS \"isPrimary\",\n\t\t\t\t\t jsonb_build_object(\n\t\t\t\t\t 'id', a.id,\n\t\t\t\t\t 'countryIso', a.country_iso,\n\t\t\t\t\t 'subdivision1Iso', a.subdivision1_iso,\n\t\t\t\t\t 'subdivision2Iso', a.subdivision2_iso,\n\t\t\t\t\t 'zipPostalCode', a.zip_postal_code,\n\t\t\t\t\t 'unit', a.unit,\n\t\t\t\t\t 'line1', a.line1,\n\t\t\t\t\t 'line2', a.line2\n\t\t\t\t\t ) AS address\n\t\t\t\t\t FROM fonderie_customer_addresses ca\n\t\t\t\t\t JOIN fonderie_addresses a ON a.id = ca.addr_id\n\t\t\t\t\t WHERE ca.customer_id = ANY($1::uuid[])\n\t\t\t\t\t ORDER BY ca.is_primary DESC`,\n\t\t\t\t\t[relatedIds],\n\t\t\t\t),\n\t\t\t\tthis.store.query<{ id: string; customerId: string; authorId: string | null; body: string; createdAt: string; updatedAt: string }>(\n\t\t\t\t\t`SELECT id, customer_id AS \"customerId\", author_id AS \"authorId\", body, created_at AS \"createdAt\", updated_at AS \"updatedAt\"\n\t\t\t\t\t FROM fonderie_customer_notes WHERE customer_id = ANY($1::uuid[]) ORDER BY created_at DESC`,\n\t\t\t\t\t[relatedIds],\n\t\t\t\t),\n\t\t\t\tthis.store.query<{ customerId: string; tag: string }>(\n\t\t\t\t\t`SELECT customer_id AS \"customerId\", tag FROM fonderie_customer_tags WHERE customer_id = ANY($1::uuid[]) ORDER BY tag ASC`,\n\t\t\t\t\t[relatedIds],\n\t\t\t\t),\n\t\t\t]);\n\n\t\t\tconst customerMap = new Map(relCustomers.map((c) => [c.id, c]));\n\n\t\t\tconst emailMap = groupByCustomer(relEmails);\n\t\t\tconst phoneMap = groupByCustomer(relPhones);\n\t\t\tconst addrMap = groupByCustomer(relAddresses);\n\t\t\tconst noteMap = groupByCustomer(relNotes);\n\t\t\tconst tagMap = relTags.reduce((m, t) => {\n\t\t\t\tif (!m.has(t.customerId)) m.set(t.customerId, []);\n\t\t\t\tm.get(t.customerId)!.push(t.tag);\n\t\t\t\treturn m;\n\t\t\t}, new Map<string, string[]>());\n\n\t\t\texpandedRelationships = relationshipRows.map((rel) => ({\n\t\t\t\tid: rel.id,\n\t\t\t\tworkspaceId: rel.workspaceId,\n\t\t\t\tcustomerId: rel.customerId,\n\t\t\t\trelationship: rel.relationship,\n\t\t\t\tisPrimary: rel.isPrimary,\n\t\t\t\tcreatedAt: rel.createdAt,\n\t\t\t\tcustomer: {\n\t\t\t\t\t...(customerMap.get(rel.relatedId) ?? ({ id: rel.relatedId } as ICustomer)),\n\t\t\t\t\temails: emailMap.get(rel.relatedId) ?? [],\n\t\t\t\t\tphones: phoneMap.get(rel.relatedId) ?? [],\n\t\t\t\t\taddresses: addrMap.get(rel.relatedId) ?? [],\n\t\t\t\t\tnotes: noteMap.get(rel.relatedId) ?? [],\n\t\t\t\t\ttags: tagMap.get(rel.relatedId) ?? [],\n\t\t\t\t} as ICustomerShallow,\n\t\t\t}));\n\t\t}\n\n\t\t// ── Depth-2: resolve relationships of relationships ────────────────────\n\t\tif (depth >= 2 && expandedRelationships.length > 0) {\n\t\t\tconst visited = new Set([id]); // guard against cycles back to root\n\t\t\tconst d1Ids = expandedRelationships.map((r) => r.customer.id);\n\n\t\t\tconst d2RelRows = await this.store.query<ICustomerRelationship>(\n\t\t\t\t`SELECT id,\n\t\t\t\t workspace_id AS \"workspaceId\",\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t related_id AS \"relatedId\",\n\t\t\t\t relationship,\n\t\t\t\t is_primary AS \"isPrimary\",\n\t\t\t\t created_at AS \"createdAt\"\n\t\t\t\t FROM fonderie_customer_relationships\n\t\t\t\t WHERE customer_id = ANY($1::uuid[])\n\t\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t[d1Ids],\n\t\t\t);\n\n\t\t\tconst validD2Rows = d2RelRows.filter((r) => !visited.has(r.relatedId));\n\t\t\tconst d2Ids = [...new Set(validD2Rows.map((r) => r.relatedId))];\n\n\t\t\tlet d2CustomerMap = new Map<string, ICustomer>();\n\t\t\tlet d2EmailMap = new Map<string, ICustomerEmail[]>();\n\t\t\tlet d2PhoneMap = new Map<string, ICustomerPhone[]>();\n\t\t\tlet d2NoteMap = new Map<string, { id: string; customerId: string; authorId: string | null; body: string; createdAt: string; updatedAt: string }[]>();\n\t\t\tlet d2TagMap = new Map<string, string[]>();\n\n\t\t\tif (d2Ids.length > 0) {\n\t\t\t\t// Addresses are omitted at depth-2: family members share the parent tenant's\n\t\t\t\t// address and duplicating it inflates mobile payloads unnecessarily.\n\t\t\t\tconst [d2Customers, d2Emails, d2Phones, d2Notes, d2Tags] = await Promise.all([\n\t\t\t\t\tthis.store.query<ICustomer>(\n\t\t\t\t\t\t`SELECT ${SELECT_CUSTOMER} FROM fonderie_customers WHERE id = ANY($1::uuid[]) AND workspace_id = $2`,\n\t\t\t\t\t\t[d2Ids, workspaceId],\n\t\t\t\t\t),\n\t\t\t\t\tthis.store.query<ICustomerEmail>(\n\t\t\t\t\t\t`SELECT id, customer_id AS \"customerId\", email, label_id AS \"labelId\",\n\t\t\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\t\t\t\t\t\t is_primary AS \"isPrimary\", created_at AS \"createdAt\"\n\t\t\t\t\t\t FROM fonderie_customer_emails WHERE customer_id = ANY($1::uuid[]) ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t\t\t[d2Ids],\n\t\t\t\t\t),\n\t\t\t\t\tthis.store.query<ICustomerPhone>(\n\t\t\t\t\t\t`SELECT id, customer_id AS \"customerId\", phone, label_id AS \"labelId\",\n\t\t\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\t\t\t\t\t\t is_primary AS \"isPrimary\", created_at AS \"createdAt\"\n\t\t\t\t\t\t FROM fonderie_customer_phones WHERE customer_id = ANY($1::uuid[]) ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t\t\t[d2Ids],\n\t\t\t\t\t),\n\t\t\t\t\tthis.store.query<{ id: string; customerId: string; authorId: string | null; body: string; createdAt: string; updatedAt: string }>(\n\t\t\t\t\t\t`SELECT id, customer_id AS \"customerId\", author_id AS \"authorId\", body, created_at AS \"createdAt\", updated_at AS \"updatedAt\"\n\t\t\t\t\t\t FROM fonderie_customer_notes WHERE customer_id = ANY($1::uuid[]) ORDER BY created_at DESC`,\n\t\t\t\t\t\t[d2Ids],\n\t\t\t\t\t),\n\t\t\t\t\tthis.store.query<{ customerId: string; tag: string }>(\n\t\t\t\t\t\t`SELECT customer_id AS \"customerId\", tag FROM fonderie_customer_tags WHERE customer_id = ANY($1::uuid[]) ORDER BY tag ASC`,\n\t\t\t\t\t\t[d2Ids],\n\t\t\t\t\t),\n\t\t\t\t]);\n\n\t\t\t\td2CustomerMap = new Map(d2Customers.map((c) => [c.id, c]));\n\t\t\t\td2EmailMap = groupByCustomer(d2Emails);\n\t\t\t\td2PhoneMap = groupByCustomer(d2Phones);\n\t\t\t\td2NoteMap = groupByCustomer(d2Notes);\n\t\t\t\td2TagMap = d2Tags.reduce((m, t) => {\n\t\t\t\t\tif (!m.has(t.customerId)) m.set(t.customerId, []);\n\t\t\t\t\tm.get(t.customerId)!.push(t.tag);\n\t\t\t\t\treturn m;\n\t\t\t\t}, new Map<string, string[]>());\n\t\t\t}\n\n\t\t\tconst d2Relationships: ICustomerRelationshipExpanded[] = validD2Rows.map((d2rel) => ({\n\t\t\t\tid: d2rel.id,\n\t\t\t\tworkspaceId: d2rel.workspaceId,\n\t\t\t\tcustomerId: d2rel.customerId,\n\t\t\t\trelationship: d2rel.relationship,\n\t\t\t\tisPrimary: d2rel.isPrimary,\n\t\t\t\tcreatedAt: d2rel.createdAt,\n\t\t\t\tcustomer: {\n\t\t\t\t\t...(d2CustomerMap.get(d2rel.relatedId) ?? ({ id: d2rel.relatedId } as ICustomer)),\n\t\t\t\t\temails: d2EmailMap.get(d2rel.relatedId) ?? [],\n\t\t\t\t\tphones: d2PhoneMap.get(d2rel.relatedId) ?? [],\n\t\t\t\t\taddresses: [],\n\t\t\t\t\tnotes: d2NoteMap.get(d2rel.relatedId) ?? [],\n\t\t\t\t\ttags: d2TagMap.get(d2rel.relatedId) ?? [],\n\t\t\t\t} as ICustomerShallow,\n\t\t\t}));\n\n\t\t\tconst d2RelsByD1Id = groupByCustomer(d2Relationships);\n\n\t\t\tconst d2ExpandedRelationships = expandedRelationships.map((rel) => ({\n\t\t\t\t...rel,\n\t\t\t\tcustomer: {\n\t\t\t\t\t...rel.customer,\n\t\t\t\t\trelationships: d2RelsByD1Id.get(rel.customer.id) ?? [],\n\t\t\t\t} as ICustomerShallowD2,\n\t\t\t}));\n\n\t\t\treturn {\n\t\t\t\t...row,\n\t\t\t\temails: emailRows as unknown as ICustomerDetail['emails'],\n\t\t\t\tphones: phoneRows as unknown as ICustomerDetail['phones'],\n\t\t\t\taddresses: addressRows,\n\t\t\t\tnotes: noteRows as unknown as ICustomerDetail['notes'],\n\t\t\t\trelationships: d2ExpandedRelationships,\n\t\t\t\ttags: tagRows.map((t) => t.tag),\n\t\t\t} as ICustomerDetailD2;\n\t\t}\n\n\t\treturn {\n\t\t\t...row,\n\t\t\t// DB returns TEXT for label; cast to the narrow union that callers expect.\n\t\t\temails: emailRows as unknown as ICustomerDetail['emails'],\n\t\t\tphones: phoneRows as unknown as ICustomerDetail['phones'],\n\t\t\taddresses: addressRows,\n\t\t\tnotes: noteRows as unknown as ICustomerDetail['notes'],\n\t\t\trelationships: expandedRelationships,\n\t\t\ttags: tagRows.map((t) => t.tag),\n\t\t};\n\t}\n\n\tasync create(opts: CreateCustomerOpts): Promise<ICustomer> {\n\t\tconst referenceCode = opts.referenceCode\n\t\t\t?? await this.allocateCode(opts.workspaceId, opts.referenceCodePrefix ?? DEFAULT_REFERENCE_CODE_PREFIX);\n\n\t\t// Every customer gets a shareable referral code at creation.\n\t\tconst referralCode = opts.referralCode ?? await this.allocateReferralCode(opts.workspaceId);\n\n\t\t// If they signed up with someone's code, record who referred them (same\n\t\t// workspace). An unknown code is ignored, not an error — signup shouldn't\n\t\t// fail because a referral code was mistyped.\n\t\tconst referredBy = opts.referredByCode\n\t\t\t? await this.resolveReferralCode(opts.workspaceId, opts.referredByCode)\n\t\t\t: null;\n\n\t\tconst [row] = await this.store.query<ICustomer>(\n\t\t\t`INSERT INTO fonderie_customers\n\t\t\t (workspace_id, type, sex, first_name, last_name, company_name, avatar_url, locale, reference_code, referral_code, referred_by, created_by)\n\t\t\t VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)\n\t\t\t RETURNING ${SELECT_CUSTOMER}`,\n\t\t\t[\n\t\t\t\topts.workspaceId,\n\t\t\t\topts.type ?? 'individual',\n\t\t\t\topts.sex ?? 'UNKNOWN',\n\t\t\t\topts.firstName ?? null,\n\t\t\t\topts.lastName ?? null,\n\t\t\t\topts.companyName ?? null,\n\t\t\t\topts.avatarUrl ?? null,\n\t\t\t\topts.locale ?? 'en-US',\n\t\t\t\treferenceCode,\n\t\t\t\treferralCode,\n\t\t\t\treferredBy,\n\t\t\t\topts.createdBy ?? null,\n\t\t\t],\n\t\t);\n\t\tif (!row) throw new Error('Failed to create customer');\n\t\treturn row;\n\t}\n\n\tasync update(\n\t\tid: string,\n\t\tworkspaceId: string,\n\t\topts: UpdateCustomerOpts,\n\t\treferenceCodePrefix = DEFAULT_REFERENCE_CODE_PREFIX,\n\t): Promise<ICustomer | null> {\n\t\t// Auto-assign a reference code if the customer doesn't have one yet.\n\t\tlet autoCode: string | undefined;\n\t\tif (!opts.referenceCode) {\n\t\t\tconst current = await this.findById(id, workspaceId);\n\t\t\tif (!current) return null;\n\t\t\tif (!current.referenceCode) {\n\t\t\t\tautoCode = await this.allocateCode(workspaceId, referenceCodePrefix);\n\t\t\t}\n\t\t}\n\n\t\tconst sets: string[] = ['updated_at = now()'];\n\t\tconst params: unknown[] = [id, workspaceId];\n\n\t\tif (opts.type !== undefined) {\n\t\t\tparams.push(opts.type);\n\t\t\tsets.push(`type = $${params.length}`);\n\t\t}\n\t\tif (opts.sex !== undefined) {\n\t\t\tparams.push(opts.sex);\n\t\t\tsets.push(`sex = $${params.length}`);\n\t\t}\n\t\tif (opts.firstName !== undefined) {\n\t\t\tparams.push(opts.firstName);\n\t\t\tsets.push(`first_name = $${params.length}`);\n\t\t}\n\t\tif (opts.lastName !== undefined) {\n\t\t\tparams.push(opts.lastName);\n\t\t\tsets.push(`last_name = $${params.length}`);\n\t\t}\n\t\tif (opts.companyName !== undefined) {\n\t\t\tparams.push(opts.companyName);\n\t\t\tsets.push(`company_name = $${params.length}`);\n\t\t}\n\t\tif (opts.avatarUrl !== undefined) {\n\t\t\tparams.push(opts.avatarUrl);\n\t\t\tsets.push(`avatar_url = $${params.length}`);\n\t\t}\n\t\tif (opts.locale !== undefined) {\n\t\t\tparams.push(opts.locale);\n\t\t\tsets.push(`locale = $${params.length}`);\n\t\t}\n\t\tconst resolvedCode = opts.referenceCode ?? autoCode;\n\t\tif (resolvedCode !== undefined) {\n\t\t\tparams.push(resolvedCode);\n\t\t\tsets.push(`reference_code = $${params.length}`);\n\t\t}\n\n\t\tconst [row] = await this.store.query<ICustomer>(\n\t\t\t`UPDATE fonderie_customers\n\t\t\t SET ${sets.join(', ')}\n\t\t\t WHERE id = $1 AND workspace_id = $2\n\t\t\t RETURNING ${SELECT_CUSTOMER}`,\n\t\t\tparams,\n\t\t);\n\t\treturn row ?? null;\n\t}\n\n\tasync delete(id: string, workspaceId: string): Promise<void> {\n\t\t// Delete all sub-resources in parallel, then the customer.\n\t\t// Relationships: remove both sides (owner and target) — detach strategy,\n\t\t// related customers are left intact.\n\t\t// Addresses: delete the underlying fonderie_addresses rows directly; their\n\t\t// ON DELETE CASCADE removes the customer_addresses link automatically.\n\t\t// The remaining DB cascades on the customer row act as a safety net.\n\t\tawait Promise.all([\n\t\t\tthis.store.query(`DELETE FROM fonderie_customer_emails WHERE customer_id = $1`, [id]),\n\t\t\tthis.store.query(`DELETE FROM fonderie_customer_phones WHERE customer_id = $1`, [id]),\n\t\t\tthis.store.query(\n\t\t\t\t`DELETE FROM fonderie_addresses WHERE id IN (\n\t\t\t\t\tSELECT addr_id FROM fonderie_customer_addresses WHERE customer_id = $1\n\t\t\t\t)`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t\tthis.store.query(`DELETE FROM fonderie_customer_notes WHERE customer_id = $1`, [id]),\n\t\t\tthis.store.query(`DELETE FROM fonderie_customer_tags WHERE customer_id = $1`, [id]),\n\t\t\tthis.store.query(\n\t\t\t\t`DELETE FROM fonderie_customer_relationships WHERE customer_id = $1 OR related_id = $1`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t]);\n\t\tawait this.store.query(`DELETE FROM fonderie_customers WHERE id = $1 AND workspace_id = $2`, [\n\t\t\tid,\n\t\t\tworkspaceId,\n\t\t]);\n\t}\n\n\tasync blacklist(id: string, workspaceId: string, reason?: string | null): Promise<void> {\n\t\tawait this.store.query(\n\t\t\t`UPDATE fonderie_customers\n\t\t\t SET is_blacklisted = true, blacklist_reason = $3, updated_at = now()\n\t\t\t WHERE id = $1 AND workspace_id = $2`,\n\t\t\t[id, workspaceId, reason ?? null],\n\t\t);\n\t}\n\n\tasync unblacklist(id: string, workspaceId: string): Promise<void> {\n\t\tawait this.store.query(\n\t\t\t`UPDATE fonderie_customers\n\t\t\t SET is_blacklisted = false, blacklist_reason = null, updated_at = now()\n\t\t\t WHERE id = $1 AND workspace_id = $2`,\n\t\t\t[id, workspaceId],\n\t\t);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { ICustomerRelationship } from '../types';\n\nexport interface AddRelationshipOpts {\n\tworkspaceId: string;\n\tcustomerId: string;\n\trelatedId: string;\n\trelationship: string;\n\tisPrimary?: boolean;\n}\n\nexport class CustomerRelationshipModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tasync list(customerId: string): Promise<ICustomerRelationship[]> {\n\t\treturn this.store.query<ICustomerRelationship>(\n\t\t\t`SELECT id,\n\t\t\t workspace_id AS \"workspaceId\",\n\t\t\t customer_id AS \"customerId\",\n\t\t\t related_id AS \"relatedId\",\n\t\t\t relationship,\n\t\t\t is_primary AS \"isPrimary\",\n\t\t\t created_at AS \"createdAt\"\n\t\t\t FROM fonderie_customer_relationships\n\t\t\t WHERE customer_id = $1\n\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t[customerId],\n\t\t);\n\t}\n\n\tasync add(opts: AddRelationshipOpts): Promise<ICustomerRelationship> {\n\t\treturn this.store.transaction(async (tx) => {\n\t\t\tif (opts.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_relationships\n\t\t\t\t\t SET is_primary = false\n\t\t\t\t\t WHERE customer_id = $1`,\n\t\t\t\t\t[opts.customerId],\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst [row] = await tx.query<ICustomerRelationship>(\n\t\t\t\t`INSERT INTO fonderie_customer_relationships\n\t\t\t\t (workspace_id, customer_id, related_id, relationship, is_primary)\n\t\t\t\t VALUES ($1, $2, $3, $4, $5)\n\t\t\t\t ON CONFLICT (customer_id, related_id) DO UPDATE\n\t\t\t\t SET relationship = EXCLUDED.relationship,\n\t\t\t\t is_primary = EXCLUDED.is_primary\n\t\t\t\t RETURNING\n\t\t\t\t id,\n\t\t\t\t workspace_id AS \"workspaceId\",\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t related_id AS \"relatedId\",\n\t\t\t\t relationship,\n\t\t\t\t is_primary AS \"isPrimary\",\n\t\t\t\t created_at AS \"createdAt\"`,\n\t\t\t\t[opts.workspaceId, opts.customerId, opts.relatedId, opts.relationship, opts.isPrimary ?? false],\n\t\t\t);\n\t\t\tif (!row) throw new Error('Failed to create relationship');\n\t\t\treturn row;\n\t\t});\n\t}\n\n\tasync setPrimary(customerId: string, relatedId: string): Promise<void> {\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_relationships SET is_primary = false WHERE customer_id = $1`,\n\t\t\t\t[customerId],\n\t\t\t);\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_relationships\n\t\t\t\t SET is_primary = true\n\t\t\t\t WHERE customer_id = $1 AND related_id = $2`,\n\t\t\t\t[customerId, relatedId],\n\t\t\t);\n\t\t});\n\t}\n\n\tasync remove(customerId: string, relatedId: string): Promise<void> {\n\t\tawait this.store.query(\n\t\t\t`DELETE FROM fonderie_customer_relationships\n\t\t\t WHERE customer_id = $1 AND related_id = $2`,\n\t\t\t[customerId, relatedId],\n\t\t);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { ICustomerAddress } from '../types';\n\nconst SELECT_CUSTOMER_ADDRESS = `\n\tca.addr_id AS \"addrId\",\n\tca.customer_id AS \"customerId\",\n\tca.label_id AS \"labelId\",\n\t(SELECT value FROM fonderie_customer_labels WHERE id = ca.label_id) AS label,\n\tca.is_primary AS \"isPrimary\",\n\tjsonb_build_object(\n\t\t'id', a.id,\n\t\t'countryIso', a.country_iso,\n\t\t'subdivision1Iso', a.subdivision1_iso,\n\t\t'subdivision2Iso', a.subdivision2_iso,\n\t\t'zipPostalCode', a.zip_postal_code,\n\t\t'unit', a.unit,\n\t\t'line1', a.line1,\n\t\t'line2', a.line2\n\t) AS address\n`;\n\nexport class CustomerAddressModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tlist(customerId: string): Promise<ICustomerAddress[]> {\n\t\treturn this.store.query<ICustomerAddress>(\n\t\t\t`SELECT ${SELECT_CUSTOMER_ADDRESS}\n\t\t\t FROM fonderie_customer_addresses ca\n\t\t\t JOIN fonderie_addresses a ON a.id = ca.addr_id\n\t\t\t WHERE ca.customer_id = $1\n\t\t\t ORDER BY ca.is_primary DESC`,\n\t\t\t[customerId],\n\t\t);\n\t}\n\n\tasync add(opts: {\n\t\tcustomerId: string;\n\t\tcountryIso: string;\n\t\tsubdivision1Iso?: string | null;\n\t\tsubdivision2Iso?: string | null;\n\t\tzipPostalCode: string;\n\t\tunit?: string | null;\n\t\tline1?: string | null;\n\t\tline2?: string | null;\n\t\tlabelId: string;\n\t\tisPrimary?: boolean;\n\t}): Promise<ICustomerAddress> {\n\t\tconst [existing] = await this.store.query<{ addrId: string }>(\n\t\t\t`SELECT ca.addr_id AS \"addrId\"\n\t\t\t FROM fonderie_customer_addresses ca\n\t\t\t JOIN fonderie_addresses a ON a.id = ca.addr_id\n\t\t\t WHERE ca.customer_id = $1\n\t\t\t AND a.country_iso = $2\n\t\t\t AND a.zip_postal_code = $3\n\t\t\t AND a.subdivision1_iso IS NOT DISTINCT FROM $4\n\t\t\t AND a.subdivision2_iso IS NOT DISTINCT FROM $5\n\t\t\t AND a.unit IS NOT DISTINCT FROM $6\n\t\t\t AND a.line1 IS NOT DISTINCT FROM $7\n\t\t\t AND a.line2 IS NOT DISTINCT FROM $8\n\t\t\t LIMIT 1`,\n\t\t\t[\n\t\t\t\topts.customerId,\n\t\t\t\topts.countryIso,\n\t\t\t\topts.zipPostalCode,\n\t\t\t\topts.subdivision1Iso ?? null,\n\t\t\t\topts.subdivision2Iso ?? null,\n\t\t\t\topts.unit ?? null,\n\t\t\t\topts.line1 ?? null,\n\t\t\t\topts.line2 ?? null,\n\t\t\t],\n\t\t);\n\t\tif (existing) {\n\t\t\tthrow Object.assign(new Error('Duplicate address for this customer'), { code: 'DUPLICATE_ADDRESS' });\n\t\t}\n\n\t\treturn this.store.transaction(async (tx) => {\n\t\t\tif (opts.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_addresses\n\t\t\t\t\t SET is_primary = false\n\t\t\t\t\t WHERE customer_id = $1`,\n\t\t\t\t\t[opts.customerId],\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst [addr] = await tx.query<{ id: string }>(\n\t\t\t\t`INSERT INTO fonderie_addresses\n\t\t\t\t (id, country_iso, subdivision1_iso, subdivision2_iso, zip_postal_code, unit, line1, line2)\n\t\t\t\t VALUES (gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7)\n\t\t\t\t RETURNING id`,\n\t\t\t\t[\n\t\t\t\t\topts.countryIso,\n\t\t\t\t\topts.subdivision1Iso ?? null,\n\t\t\t\t\topts.subdivision2Iso ?? null,\n\t\t\t\t\topts.zipPostalCode,\n\t\t\t\t\topts.unit ?? null,\n\t\t\t\t\topts.line1 ?? null,\n\t\t\t\t\topts.line2 ?? null,\n\t\t\t\t],\n\t\t\t);\n\t\t\tif (!addr) throw new Error('Failed to create address');\n\n\t\t\tconst [row] = await tx.query<ICustomerAddress>(\n\t\t\t\t`INSERT INTO fonderie_customer_addresses (addr_id, customer_id, label_id, is_primary)\n\t\t\t\t VALUES ($1, $2, $3, $4)\n\t\t\t\t RETURNING\n\t\t\t\t addr_id AS \"addrId\",\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t label_id AS \"labelId\",\n\t\t\t\t NULL::text AS label,\n\t\t\t\t is_primary AS \"isPrimary\",\n\t\t\t\t NULL::jsonb AS address`,\n\t\t\t\t[addr.id, opts.customerId, opts.labelId, opts.isPrimary ?? false],\n\t\t\t);\n\t\t\tif (!row) throw new Error('Failed to link address');\n\n\t\t\tconst [full] = await tx.query<ICustomerAddress>(\n\t\t\t\t`SELECT ${SELECT_CUSTOMER_ADDRESS}\n\t\t\t\t FROM fonderie_customer_addresses ca\n\t\t\t\t JOIN fonderie_addresses a ON a.id = ca.addr_id\n\t\t\t\t WHERE ca.addr_id = $1 AND ca.customer_id = $2`,\n\t\t\t\t[addr.id, opts.customerId],\n\t\t\t);\n\t\t\tif (!full) throw new Error('Failed to fetch created address');\n\t\t\treturn full;\n\t\t});\n\t}\n\n\tasync updateLabel(addrId: string, customerId: string, labelId: string): Promise<ICustomerAddress> {\n\t\tconst [row] = await this.store.query<ICustomerAddress>(\n\t\t\t`UPDATE fonderie_customer_addresses\n\t\t\t SET label_id = $3\n\t\t\t WHERE addr_id = $1 AND customer_id = $2\n\t\t\t RETURNING addr_id AS \"addrId\", customer_id AS \"customerId\", label_id AS \"labelId\", NULL::text AS label, is_primary AS \"isPrimary\", NULL::jsonb AS address`,\n\t\t\t[addrId, customerId, labelId],\n\t\t);\n\t\tif (!row) throw Object.assign(new Error('Address not found'), { code: 'NOT_FOUND' });\n\n\t\tconst [full] = await this.store.query<ICustomerAddress>(\n\t\t\t`SELECT ${SELECT_CUSTOMER_ADDRESS}\n\t\t\t FROM fonderie_customer_addresses ca\n\t\t\t JOIN fonderie_addresses a ON a.id = ca.addr_id\n\t\t\t WHERE ca.addr_id = $1 AND ca.customer_id = $2`,\n\t\t\t[addrId, customerId],\n\t\t);\n\t\tif (!full) throw new Error('Failed to fetch updated address');\n\t\treturn full;\n\t}\n\n\tasync setPrimary(addrId: string, customerId: string): Promise<void> {\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_addresses SET is_primary = false WHERE customer_id = $1`,\n\t\t\t\t[customerId],\n\t\t\t);\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_addresses SET is_primary = true WHERE addr_id = $1 AND customer_id = $2`,\n\t\t\t\t[addrId, customerId],\n\t\t\t);\n\t\t});\n\t}\n\n\t/** Returns false when the address is not linked to THIS customer (no-op). */\n\tasync remove(addrId: string, customerId: string): Promise<boolean> {\n\t\treturn this.store.transaction(async (tx) => {\n\t\t\tconst [deleted] = await tx.query<{ isPrimary: boolean }>(\n\t\t\t\t`DELETE FROM fonderie_customer_addresses\n\t\t\t\t WHERE addr_id = $1 AND customer_id = $2\n\t\t\t\t RETURNING is_primary AS \"isPrimary\"`,\n\t\t\t\t[addrId, customerId],\n\t\t\t);\n\t\t\t// Only delete the underlying fonderie_addresses row when the scoped\n\t\t\t// link delete above actually matched. Deleting unconditionally by id\n\t\t\t// would let any caller destroy ANOTHER customer's/tenant's address\n\t\t\t// (the shared-table row + their link via cascade) with a guessed id.\n\t\t\tif (!deleted) return false;\n\t\t\tawait tx.query(`DELETE FROM fonderie_addresses WHERE id = $1`, [addrId]);\n\t\t\tif (deleted.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_addresses\n\t\t\t\t\t SET is_primary = true\n\t\t\t\t\t WHERE addr_id = (\n\t\t\t\t\t SELECT addr_id FROM fonderie_customer_addresses\n\t\t\t\t\t WHERE customer_id = $1\n\t\t\t\t\t ORDER BY addr_id ASC\n\t\t\t\t\t LIMIT 1\n\t\t\t\t\t )`,\n\t\t\t\t\t[customerId],\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { ICustomerEmail } from '../types';\n\nconst SELECT_EMAIL = `\n\tid,\n\tcustomer_id AS \"customerId\",\n\temail,\n\tlabel_id AS \"labelId\",\n\t(SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\tis_primary AS \"isPrimary\",\n\tcreated_at AS \"createdAt\"\n`;\n\nexport class CustomerEmailModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tlist(customerId: string): Promise<ICustomerEmail[]> {\n\t\treturn this.store.query<ICustomerEmail>(\n\t\t\t`SELECT ${SELECT_EMAIL}\n\t\t\t FROM fonderie_customer_emails\n\t\t\t WHERE customer_id = $1\n\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t[customerId],\n\t\t);\n\t}\n\n\tasync add(opts: {\n\t\tcustomerId: string;\n\t\temail: string;\n\t\tlabelId: string;\n\t\tisPrimary?: boolean;\n\t}): Promise<ICustomerEmail> {\n\t\treturn this.store.transaction(async (tx) => {\n\t\t\tif (opts.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_emails SET is_primary = false WHERE customer_id = $1`,\n\t\t\t\t\t[opts.customerId],\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst [row] = await tx.query<ICustomerEmail>(\n\t\t\t\t`INSERT INTO fonderie_customer_emails (id, customer_id, email, label_id, is_primary)\n\t\t\t\t VALUES (gen_random_uuid(), $1, $2, $3, $4)\n\t\t\t\t RETURNING ${SELECT_EMAIL}`,\n\t\t\t\t[opts.customerId, opts.email, opts.labelId, opts.isPrimary ?? false],\n\t\t\t);\n\t\t\tif (!row) throw new Error('Failed to add customer email');\n\t\t\treturn row;\n\t\t});\n\t}\n\n\tasync updateLabel(emailId: string, customerId: string, labelId: string): Promise<ICustomerEmail> {\n\t\tconst [row] = await this.store.query<ICustomerEmail>(\n\t\t\t`UPDATE fonderie_customer_emails\n\t\t\t SET label_id = $3\n\t\t\t WHERE id = $1 AND customer_id = $2\n\t\t\t RETURNING ${SELECT_EMAIL}`,\n\t\t\t[emailId, customerId, labelId],\n\t\t);\n\t\tif (!row) throw Object.assign(new Error('Email not found'), { code: 'NOT_FOUND' });\n\t\treturn row;\n\t}\n\n\tasync setPrimary(emailId: string, customerId: string): Promise<void> {\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_emails SET is_primary = false WHERE customer_id = $1`,\n\t\t\t\t[customerId],\n\t\t\t);\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_emails SET is_primary = true WHERE id = $1 AND customer_id = $2`,\n\t\t\t\t[emailId, customerId],\n\t\t\t);\n\t\t});\n\t}\n\n\tasync remove(emailId: string, customerId: string): Promise<void> {\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tconst [deleted] = await tx.query<{ isPrimary: boolean }>(\n\t\t\t\t`DELETE FROM fonderie_customer_emails\n\t\t\t\t WHERE id = $1 AND customer_id = $2\n\t\t\t\t RETURNING is_primary AS \"isPrimary\"`,\n\t\t\t\t[emailId, customerId],\n\t\t\t);\n\t\t\tif (deleted?.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_emails\n\t\t\t\t\t SET is_primary = true\n\t\t\t\t\t WHERE id = (\n\t\t\t\t\t SELECT id FROM fonderie_customer_emails\n\t\t\t\t\t WHERE customer_id = $1\n\t\t\t\t\t ORDER BY created_at ASC\n\t\t\t\t\t LIMIT 1\n\t\t\t\t\t )`,\n\t\t\t\t\t[customerId],\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { CustomerLabelType, ICustomerLabel } from '../types';\n\nconst SELECT = `id, type, value, created_at AS \"createdAt\"`;\n\nexport class CustomerLabelModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\t/**\n\t * List the labels a workspace may use: the shared/system defaults\n\t * (workspace_id IS NULL) plus its OWN private labels. Never another\n\t * workspace's — so listing can't enumerate other tenants' custom\n\t * vocabulary.\n\t */\n\tlist(type: CustomerLabelType, workspaceId: string): Promise<ICustomerLabel[]> {\n\t\treturn this.store.query<ICustomerLabel>(\n\t\t\t`SELECT ${SELECT}\n\t\t\t FROM fonderie_customer_labels\n\t\t\t WHERE type = $1 AND (workspace_id IS NULL OR workspace_id = $2)\n\t\t\t ORDER BY value ASC`,\n\t\t\t[type, workspaceId],\n\t\t);\n\t}\n\n\t/**\n\t * Delete a label. Only a label OWNED BY this workspace and UNREFERENCED can\n\t * go: shared defaults (workspace_id IS NULL) and other workspaces' labels\n\t * are untouchable, and an in-use label (still pointed at by an email/phone/\n\t * address) is refused so it can't be destroyed out from under a record.\n\t * Returns false when nothing was deleted.\n\t */\n\tasync remove(id: string, workspaceId: string): Promise<boolean> {\n\t\tconst rows = await this.store.query<{ id: string }>(\n\t\t\t`DELETE FROM fonderie_customer_labels l\n\t\t\t WHERE l.id = $1\n\t\t\t AND l.workspace_id = $2\n\t\t\t AND NOT EXISTS (SELECT 1 FROM fonderie_customer_emails WHERE label_id = l.id)\n\t\t\t AND NOT EXISTS (SELECT 1 FROM fonderie_customer_phones WHERE label_id = l.id)\n\t\t\t AND NOT EXISTS (SELECT 1 FROM fonderie_customer_addresses WHERE label_id = l.id)\n\t\t\t RETURNING l.id`,\n\t\t\t[id, workspaceId],\n\t\t);\n\t\treturn rows.length > 0;\n\t}\n\n\t/**\n\t * Resolve a (type, value) to a label id for this workspace: reuse a shared\n\t * default when one exists (so common labels like 'work' stay canonical),\n\t * otherwise create/reuse a label PRIVATE to this workspace.\n\t */\n\tasync findOrCreate(\n\t\ttype: CustomerLabelType,\n\t\traw: string,\n\t\tworkspaceId: string,\n\t): Promise<ICustomerLabel> {\n\t\tconst value = raw.trim().toLowerCase();\n\n\t\t// Prefer a shared default — keeps the seeded vocabulary canonical and\n\t\t// avoids minting a per-workspace duplicate of 'work'/'personal'/etc.\n\t\tconst [shared] = await this.store.query<ICustomerLabel>(\n\t\t\t`SELECT ${SELECT} FROM fonderie_customer_labels\n\t\t\t WHERE type = $1 AND value = $2 AND workspace_id IS NULL\n\t\t\t LIMIT 1`,\n\t\t\t[type, value],\n\t\t);\n\t\tif (shared) return shared;\n\n\t\t// Otherwise a workspace-private label (unique per type+value+workspace).\n\t\tconst [row] = await this.store.query<ICustomerLabel>(\n\t\t\t`INSERT INTO fonderie_customer_labels (type, value, workspace_id)\n\t\t\t VALUES ($1, $2, $3)\n\t\t\t ON CONFLICT (type, value, workspace_id) WHERE workspace_id IS NOT NULL\n\t\t\t DO UPDATE SET value = EXCLUDED.value\n\t\t\t RETURNING ${SELECT}`,\n\t\t\t[type, value, workspaceId],\n\t\t);\n\t\tif (!row) throw new Error('Failed to find or create label');\n\t\treturn row;\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { ICustomerNote } from '../types';\n\nconst SELECT_NOTE = `\n\tid,\n\tcustomer_id AS \"customerId\",\n\tauthor_id AS \"authorId\",\n\tbody,\n\tcreated_at AS \"createdAt\",\n\tupdated_at AS \"updatedAt\"\n`;\n\nexport class CustomerNoteModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tlist(customerId: string): Promise<ICustomerNote[]> {\n\t\treturn this.store.query<ICustomerNote>(\n\t\t\t`SELECT ${SELECT_NOTE}\n\t\t\t FROM fonderie_customer_notes\n\t\t\t WHERE customer_id = $1\n\t\t\t ORDER BY created_at DESC`,\n\t\t\t[customerId],\n\t\t);\n\t}\n\n\tasync create(opts: {\n\t\tcustomerId: string;\n\t\tauthorId?: string | null;\n\t\tbody: string;\n\t}): Promise<ICustomerNote> {\n\t\tconst [row] = await this.store.query<ICustomerNote>(\n\t\t\t`INSERT INTO fonderie_customer_notes (id, customer_id, author_id, body)\n\t\t\t VALUES (gen_random_uuid(), $1, $2, $3)\n\t\t\t RETURNING ${SELECT_NOTE}`,\n\t\t\t[opts.customerId, opts.authorId ?? null, opts.body],\n\t\t);\n\t\tif (!row) throw new Error('Failed to create note');\n\t\treturn row;\n\t}\n\n\tasync update(noteId: string, customerId: string, body: string): Promise<ICustomerNote | null> {\n\t\tconst [row] = await this.store.query<ICustomerNote>(\n\t\t\t`UPDATE fonderie_customer_notes\n\t\t\t SET body = $1, updated_at = now()\n\t\t\t WHERE id = $2 AND customer_id = $3\n\t\t\t RETURNING ${SELECT_NOTE}`,\n\t\t\t[body, noteId, customerId],\n\t\t);\n\t\treturn row ?? null;\n\t}\n\n\tasync delete(noteId: string, customerId: string): Promise<void> {\n\t\tawait this.store.query(\n\t\t\t`DELETE FROM fonderie_customer_notes\n\t\t\t WHERE id = $1 AND customer_id = $2`,\n\t\t\t[noteId, customerId],\n\t\t);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { ICustomerPhone } from '../types';\n\nconst SELECT_PHONE = `\n\tid,\n\tcustomer_id AS \"customerId\",\n\tphone,\n\tlabel_id AS \"labelId\",\n\t(SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\tis_primary AS \"isPrimary\",\n\tcreated_at AS \"createdAt\"\n`;\n\nexport class CustomerPhoneModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tlist(customerId: string): Promise<ICustomerPhone[]> {\n\t\treturn this.store.query<ICustomerPhone>(\n\t\t\t`SELECT ${SELECT_PHONE}\n\t\t\t FROM fonderie_customer_phones\n\t\t\t WHERE customer_id = $1\n\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t[customerId],\n\t\t);\n\t}\n\n\tasync add(opts: {\n\t\tcustomerId: string;\n\t\tphone: string;\n\t\tlabelId: string;\n\t\tisPrimary?: boolean;\n\t}): Promise<ICustomerPhone> {\n\t\treturn this.store.transaction(async (tx) => {\n\t\t\tif (opts.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_phones SET is_primary = false WHERE customer_id = $1`,\n\t\t\t\t\t[opts.customerId],\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst [row] = await tx.query<ICustomerPhone>(\n\t\t\t\t`INSERT INTO fonderie_customer_phones (id, customer_id, phone, label_id, is_primary)\n\t\t\t\t VALUES (gen_random_uuid(), $1, $2, $3, $4)\n\t\t\t\t RETURNING ${SELECT_PHONE}`,\n\t\t\t\t[opts.customerId, opts.phone, opts.labelId, opts.isPrimary ?? false],\n\t\t\t);\n\t\t\tif (!row) throw new Error('Failed to add customer phone');\n\t\t\treturn row;\n\t\t});\n\t}\n\n\tasync updateLabel(phoneId: string, customerId: string, labelId: string): Promise<ICustomerPhone> {\n\t\tconst [row] = await this.store.query<ICustomerPhone>(\n\t\t\t`UPDATE fonderie_customer_phones\n\t\t\t SET label_id = $3\n\t\t\t WHERE id = $1 AND customer_id = $2\n\t\t\t RETURNING ${SELECT_PHONE}`,\n\t\t\t[phoneId, customerId, labelId],\n\t\t);\n\t\tif (!row) throw Object.assign(new Error('Phone not found'), { code: 'NOT_FOUND' });\n\t\treturn row;\n\t}\n\n\tasync setPrimary(phoneId: string, customerId: string): Promise<void> {\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_phones SET is_primary = false WHERE customer_id = $1`,\n\t\t\t\t[customerId],\n\t\t\t);\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_phones SET is_primary = true WHERE id = $1 AND customer_id = $2`,\n\t\t\t\t[phoneId, customerId],\n\t\t\t);\n\t\t});\n\t}\n\n\tasync remove(phoneId: string, customerId: string): Promise<void> {\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tconst [deleted] = await tx.query<{ isPrimary: boolean }>(\n\t\t\t\t`DELETE FROM fonderie_customer_phones\n\t\t\t\t WHERE id = $1 AND customer_id = $2\n\t\t\t\t RETURNING is_primary AS \"isPrimary\"`,\n\t\t\t\t[phoneId, customerId],\n\t\t\t);\n\t\t\tif (deleted?.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_phones\n\t\t\t\t\t SET is_primary = true\n\t\t\t\t\t WHERE id = (\n\t\t\t\t\t SELECT id FROM fonderie_customer_phones\n\t\t\t\t\t WHERE customer_id = $1\n\t\t\t\t\t ORDER BY created_at ASC\n\t\t\t\t\t LIMIT 1\n\t\t\t\t\t )`,\n\t\t\t\t\t[customerId],\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nexport class CustomerTagModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tasync list(customerId: string): Promise<string[]> {\n\t\tconst rows = await this.store.query<{ tag: string }>(\n\t\t\t`SELECT tag FROM fonderie_customer_tags\n\t\t\t WHERE customer_id = $1\n\t\t\t ORDER BY tag ASC`,\n\t\t\t[customerId],\n\t\t);\n\t\treturn rows.map((r) => r.tag);\n\t}\n\n\tasync add(customerId: string, tag: string): Promise<void> {\n\t\tawait this.store.query(\n\t\t\t`INSERT INTO fonderie_customer_tags (customer_id, tag)\n\t\t\t VALUES ($1, $2)\n\t\t\t ON CONFLICT (customer_id, tag) DO NOTHING`,\n\t\t\t[customerId, tag],\n\t\t);\n\t}\n\n\tasync remove(customerId: string, tag: string): Promise<void> {\n\t\tawait this.store.query(\n\t\t\t`DELETE FROM fonderie_customer_tags\n\t\t\t WHERE customer_id = $1 AND tag = $2`,\n\t\t\t[customerId, tag],\n\t\t);\n\t}\n}\n","import type { Middleware } from '@fonderie/core';\nimport { requireAuth, validate } from '@fonderie/core/middlewares';\n\nimport {\n\tnoteSchema,\n\taddTagSchema,\n\taddEmailSchema,\n\taddPhoneSchema,\n\tblacklistSchema,\n\taddAddressSchema,\n\tupdateEmailSchema,\n\tupdatePhoneSchema,\n\tupdateAddressSchema,\n\tcreateCustomerSchema,\n\tupdateCustomerSchema,\n\taddRelationshipSchema,\n} from './schemas';\nimport type { EventBus } from '@fonderie/events';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport { withWorkspace } from '@fonderie/workspaces';\n\nimport type { ICustomersConfig } from './config';\nimport { customerController } from './controllers/customer.controller';\nimport { customerAddressController } from './controllers/customer-address.controller';\nimport { customerEmailController } from './controllers/customer-email.controller';\nimport { customerNoteController } from './controllers/customer-note.controller';\nimport { customerPhoneController } from './controllers/customer-phone.controller';\nimport { customerTagController } from './controllers/customer-tag.controller';\nimport { customerLabelController } from './controllers/customer-label.controller';\nimport { customerRelationshipController } from './controllers/customer-relationship.controller';\n\ntype RouteDefinition = [string, string, ...Middleware[]];\n\nexport function buildCustomerRoutes(\n\tstore: IStoreAdapter,\n\tconfig: ICustomersConfig,\n\tbus?: EventBus,\n): RouteDefinition[] {\n\tconst wsCtx = withWorkspace(store);\n\n\tconst customer = customerController(store, config, bus);\n\tconst email = customerEmailController(store);\n\tconst phone = customerPhoneController(store);\n\tconst address = customerAddressController(store);\n\tconst note = customerNoteController(store);\n\tconst tag = customerTagController(store);\n\tconst relationship = customerRelationshipController(store);\n\tconst label = customerLabelController(store);\n\n\treturn [\n\t\t// ── Labels ───────────────────────────────────────────────────────\n\t\t['GET', '/customers/labels', requireAuth, wsCtx, label.list],\n\t\t['DELETE', '/customers/labels/:labelId', requireAuth, wsCtx, label.remove],\n\n\t\t// ── Core customer CRUD ───────────────────────────────────────────\n\t\t['GET', '/customers', requireAuth, wsCtx, customer.list],\n\t\t['POST', '/customers', requireAuth, wsCtx, validate(createCustomerSchema), customer.create],\n\t\t['GET', '/customers/:customerId', requireAuth, wsCtx, customer.get],\n\t\t['PUT', '/customers/:customerId', requireAuth, wsCtx, validate(updateCustomerSchema), customer.update],\n\t\t['DELETE', '/customers/:customerId', requireAuth, wsCtx, customer.delete],\n\t\t['POST', '/customers/:customerId/blacklist', requireAuth, wsCtx, validate(blacklistSchema), customer.blacklist],\n\t\t['POST', '/customers/:customerId/unblacklist', requireAuth, wsCtx, customer.unblacklist],\n\n\t\t// ── Emails ───────────────────────────────────────────────────────\n\t\t['GET', '/customers/:customerId/emails', requireAuth, wsCtx, email.list],\n\t\t['POST', '/customers/:customerId/emails', requireAuth, wsCtx, validate(addEmailSchema), email.add],\n\t\t['PATCH', '/customers/:customerId/emails/:emailId', requireAuth, wsCtx, validate(updateEmailSchema), email.update],\n\t\t['PUT', '/customers/:customerId/emails/:emailId/primary', requireAuth, wsCtx, email.setPrimary],\n\t\t['DELETE', '/customers/:customerId/emails/:emailId', requireAuth, wsCtx, email.remove],\n\n\t\t// ── Phones ───────────────────────────────────────────────────────\n\t\t['GET', '/customers/:customerId/phones', requireAuth, wsCtx, phone.list],\n\t\t['POST', '/customers/:customerId/phones', requireAuth, wsCtx, validate(addPhoneSchema), phone.add],\n\t\t['PATCH', '/customers/:customerId/phones/:phoneId', requireAuth, wsCtx, validate(updatePhoneSchema), phone.update],\n\t\t['PUT', '/customers/:customerId/phones/:phoneId/primary', requireAuth, wsCtx, phone.setPrimary],\n\t\t['DELETE', '/customers/:customerId/phones/:phoneId', requireAuth, wsCtx, phone.remove],\n\n\t\t// ── Addresses ────────────────────────────────────────────────────\n\t\t['GET', '/customers/:customerId/addresses', requireAuth, wsCtx, address.list],\n\t\t['POST', '/customers/:customerId/addresses', requireAuth, wsCtx, validate(addAddressSchema), address.add],\n\t\t['PATCH', '/customers/:customerId/addresses/:addrId', requireAuth, wsCtx, validate(updateAddressSchema), address.update],\n\t\t['PUT', '/customers/:customerId/addresses/:addrId/primary', requireAuth, wsCtx, address.setPrimary],\n\t\t['DELETE', '/customers/:customerId/addresses/:addrId', requireAuth, wsCtx, address.remove],\n\n\t\t// ── Notes ────────────────────────────────────────────────────────\n\t\t['GET', '/customers/:customerId/notes', requireAuth, wsCtx, note.list],\n\t\t['POST', '/customers/:customerId/notes', requireAuth, wsCtx, validate(noteSchema), note.create],\n\t\t['PUT', '/customers/:customerId/notes/:noteId', requireAuth, wsCtx, validate(noteSchema), note.update],\n\t\t['DELETE', '/customers/:customerId/notes/:noteId', requireAuth, wsCtx, note.delete],\n\n\t\t// ── Tags ─────────────────────────────────────────────────────────\n\t\t['GET', '/customers/:customerId/tags', requireAuth, wsCtx, tag.list],\n\t\t['POST', '/customers/:customerId/tags', requireAuth, wsCtx, validate(addTagSchema), tag.add],\n\t\t['DELETE', '/customers/:customerId/tags/:tag', requireAuth, wsCtx, tag.remove],\n\n\t\t// ── Relationships ────────────────────────────────────────────────────\n\t\t['GET', '/customers/:customerId/relationships', requireAuth, wsCtx, relationship.list],\n\t\t['POST', '/customers/:customerId/relationships', requireAuth, wsCtx, validate(addRelationshipSchema), relationship.add],\n\t\t['PUT', '/customers/:customerId/relationships/:relatedId/primary', requireAuth, wsCtx, relationship.setPrimary],\n\t\t['DELETE', '/customers/:customerId/relationships/:relatedId', requireAuth, wsCtx, relationship.remove],\n\t];\n}\n","import { z } from 'zod';\n\n// Request schemas — the validation contract for every body-taking customers\n// route. Wired via @fonderie/core's validate(); same pattern as\n// @fonderie/auth. Exported for docs generation and typed clients.\n\nconst label = z.string().max(100).optional();\nconst isPrimary = z.boolean().optional();\nconst email = z.string().trim().pipe(z.email());\nconst phone = z\n\t.string()\n\t.refine((v) => /^\\+?[1-9]\\d{6,14}$/.test(v.replace(/[\\s\\-()]/g, '')), 'Invalid phone number');\n\nconst customerFields = {\n\ttype: z.enum(['individual', 'business']).optional(),\n\tsex: z.enum(['UNKNOWN', 'MALE', 'FEMALE']).optional(),\n\tfirstName: z.string().max(100).nullable().optional(),\n\tlastName: z.string().max(100).nullable().optional(),\n\tcompanyName: z.string().max(200).nullable().optional(),\n\tavatarUrl: z.string().trim().pipe(z.url()).nullable().optional(),\n\tlocale: z.string().max(35).nullable().optional(),\n\treferenceCode: z.string().max(100).nullable().optional(),\n};\n\n// Referral codes are create-time only: the update controller never writes\n// them, so accepting them on update produced 200-OK silent no-ops.\nconst referralFields = {\n\treferralCode: z.string().max(100).nullable().optional(),\n\treferredByCode: z.string().max(100).nullable().optional(),\n};\n\nexport const createCustomerSchema = z.object({ ...customerFields, ...referralFields });\n\nexport const updateCustomerSchema = z\n\t.object(customerFields)\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\n\nexport const blacklistSchema = z.object({ reason: z.string().max(1000).optional() });\n\nexport const addEmailSchema = z.object({ email, label, isPrimary });\n// Only the label is editable on an existing email/phone/address — the\n// controllers apply nothing else (value changes are remove-and-re-add, and\n// setPrimary has its own route). The old schemas accepted content fields\n// and isPrimary that were silently ignored.\nexport const updateEmailSchema = z.object({\n\tlabel: z.string().trim().min(1, 'label is required').max(100),\n});\n\nexport const addPhoneSchema = z.object({ phone, label, isPrimary });\nexport const updatePhoneSchema = z.object({\n\tlabel: z.string().trim().min(1, 'label is required').max(100),\n});\n\nconst addressFields = {\n\tlabel,\n\tisPrimary,\n\tline1: z.string().max(200).nullable().optional(),\n\tline2: z.string().max(200).nullable().optional(),\n\tunit: z.string().max(50).nullable().optional(),\n\tzipPostalCode: z.string().max(20).nullable().optional(),\n\tcountryIso: z.string().max(3).nullable().optional(),\n\tsubdivision1Iso: z.string().max(10).nullable().optional(),\n\tsubdivision2Iso: z.string().max(10).nullable().optional(),\n};\nexport const addAddressSchema = z.object(addressFields);\nexport const updateAddressSchema = z.object({\n\tlabel: z.string().trim().min(1, 'label is required').max(100),\n});\n\nexport const noteSchema = z.object({ body: z.string().trim().min(1, 'body is required').max(10000) });\n\nexport const addTagSchema = z.object({ tag: z.string().trim().min(1, 'tag is required').max(100) });\n\nexport const addRelationshipSchema = z.object({\n\trelatedId: z.string().min(1, 'relatedId is required'),\n\t// The controller has always required this — an optional schema let a\n\t// type-correct client walk straight into a guaranteed 422.\n\trelationship: z.string().trim().min(1, 'relationship is required').max(100),\n\tisPrimary,\n});\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { EventBus } from '@fonderie/events';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { DEFAULT_REFERENCE_CODE_PREFIX, EVENT_KEYS, type ICustomersConfig } from '../config';\nimport { toCustomerDetailD2DTO, toCustomerDetailDTO, toCustomerDTO } from '../dtos/customer';\nimport type { ICustomerDetailD2 } from '../types';\nimport { CustomerModel } from '../models/customer.model';\nimport { isUuid } from '../utils';\n\nexport function customerController(store: IStoreAdapter, config: ICustomersConfig = {}, bus?: EventBus) {\n\tconst customers = new CustomerModel(store);\n\tconst prefix = (config.referenceCodePrefix ?? DEFAULT_REFERENCE_CODE_PREFIX).toUpperCase();\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst query = ctx.request.url ? new URL(ctx.request.url).searchParams : null;\n\t\t\tconst search = query?.get('search') ?? undefined;\n\t\t\tconst archived = query?.get('blacklisted');\n\t\t\tconst limit = Number(query?.get('limit') ?? 50);\n\t\t\tconst offset = Number(query?.get('offset') ?? 0);\n\n\t\t\tconst listOpts: Parameters<typeof customers.list>[0] = {\n\t\t\t\tworkspaceId,\n\t\t\t\tlimit: Number.isFinite(limit) ? limit : 50,\n\t\t\t\toffset: Number.isFinite(offset) ? offset : 0,\n\t\t\t};\n\n\t\t\tif (search !== undefined) {\n\t\t\t\tlistOpts.search = search;\n\t\t\t}\n\n\t\t\tif (archived !== null && archived !== undefined) {\n\t\t\t\tlistOpts.blacklisted = archived === 'true' || archived === '1';\n\t\t\t}\n\n\t\t\tconst { limit: _limit, offset: _offset, ...countOpts } = listOpts;\n\t\t\tconst [list, total] = await Promise.all([\n\t\t\t\tcustomers.list(listOpts),\n\t\t\t\tcustomers.count(countOpts),\n\t\t\t]);\n\n\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMERS_FETCHED', 'Customers retrieved successfully.', {\n\t\t\t\tcustomers: list.map(toCustomerDTO),\n\t\t\t\ttotal,\n\t\t\t});\n\t\t},\n\n\t\tasync get(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst id = params?.['customerId'];\n\t\t\tif (!isUuid(id)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst query = ctx.request.url ? new URL(ctx.request.url).searchParams : null;\n\t\t\tconst depth = query?.get('depth') === '1' ? 1 : 2;\n\n\t\t\tif (depth === 2) {\n\t\t\t\tconst customer = await customers.findDetail(id, workspaceId, 2);\n\t\t\t\tif (!customer) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found');\n\t\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMER_FETCHED', 'Customer retrieved successfully.', toCustomerDetailD2DTO(customer));\n\t\t\t}\n\n\t\t\tconst customer = await customers.findDetail(id, workspaceId);\n\t\t\tif (!customer) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found');\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMER_FETCHED', 'Customer retrieved successfully.', toCustomerDetailDTO(customer));\n\t\t},\n\n\t\tasync create(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst type = body?.['type'];\n\t\t\tconst sex = body?.['sex'];\n\t\t\tconst firstName = body?.['firstName'];\n\t\t\tconst lastName = body?.['lastName'];\n\t\t\tconst companyName = body?.['companyName'];\n\n\t\t\tconst avatarUrl = body?.['avatarUrl'];\n\t\t\tconst locale = body?.['locale'];\n\t\t\tconst referenceCode = body?.['referenceCode'];\n\t\t\tconst referralCode = body?.['referralCode'];\n\t\t\tconst referredByCode = body?.['referredByCode'];\n\n\t\t\tif (type !== undefined && type !== 'individual' && type !== 'business') {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t'type must be individual or business',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (sex !== undefined && sex !== 'UNKNOWN' && sex !== 'MALE' && sex !== 'FEMALE') {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t'sex must be UNKNOWN, MALE, or FEMALE',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet customer: Awaited<ReturnType<typeof customers.create>>;\n\t\t\ttry {\n\t\t\t\tcustomer = await customers.create({\n\t\t\t\t\tworkspaceId,\n\t\t\t\t\ttype: typeof type === 'string' ? type : 'individual',\n\t\t\t\t\tsex: typeof sex === 'string' ? sex : 'UNKNOWN',\n\t\t\t\t\tfirstName: typeof firstName === 'string' ? firstName : null,\n\t\t\t\t\tlastName: typeof lastName === 'string' ? lastName : null,\n\t\t\t\t\tcompanyName: typeof companyName === 'string' ? companyName : null,\n\n\t\t\t\t\tavatarUrl: typeof avatarUrl === 'string' ? avatarUrl : null,\n\t\t\t\t\tlocale: typeof locale === 'string' ? locale : 'en-US',\n\t\t\t\t\t// exactOptionalPropertyTypes: omit the key entirely when absent\n\t\t\t\t\t...(typeof referenceCode === 'string' ? { referenceCode: referenceCode.toUpperCase() } : {}),\n\t\t\t\t\treferenceCodePrefix: prefix,\n\t\t\t\t\t// referral: explicit code override is rare; referredByCode is the signup input\n\t\t\t\t\t...(typeof referralCode === 'string' ? { referralCode: referralCode.toUpperCase() } : {}),\n\t\t\t\t\t...(typeof referredByCode === 'string' ? { referredByCode: referredByCode.toUpperCase() } : {}),\n\t\t\t\t\tcreatedBy: ctx.user?.id ?? null,\n\t\t\t\t});\n\t\t\t} catch (err: unknown) {\n\t\t\t\tif (err instanceof Error && err.message.includes('idx_fc_reference_code')) {\n\t\t\t\t\treturn setApiResponse(HTTP.CONFLICT, 'DUPLICATE_REFERENCE_CODE', 'A customer with this reference code already exists');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tbus\n\t\t\t\t?.emit(EVENT_KEYS.customerCreated, {\n\t\t\t\t\tcustomerId: customer.id,\n\t\t\t\t\tworkspaceId: customer.workspaceId,\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'CUSTOMER_CREATED', 'Customer created successfully.', {\n\t\t\t\tcustomer: toCustomerDTO(customer),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst id = params?.['customerId'];\n\t\t\tif (!isUuid(id)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst opts: Parameters<typeof customers.update>[2] = {};\n\n\t\t\tif (body?.['type'] !== undefined) {\n\t\t\t\tconst t = body['type'];\n\t\t\t\tif (t !== 'individual' && t !== 'business') {\n\t\t\t\t\treturn setApiResponse(\n\t\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t\t'type must be individual or business',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\topts.type = t;\n\t\t\t}\n\n\t\t\tif (body?.['sex'] !== undefined) {\n\t\t\t\tconst s = body['sex'];\n\t\t\t\tif (s !== 'UNKNOWN' && s !== 'MALE' && s !== 'FEMALE') {\n\t\t\t\t\treturn setApiResponse(\n\t\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t\t'sex must be UNKNOWN, MALE, or FEMALE',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\topts.sex = s;\n\t\t\t}\n\n\t\t\tif (body?.['firstName'] !== undefined) {\n\t\t\t\topts.firstName = typeof body['firstName'] === 'string' ? body['firstName'] : null;\n\t\t\t}\n\n\t\t\tif (body?.['lastName'] !== undefined) {\n\t\t\t\topts.lastName = typeof body['lastName'] === 'string' ? body['lastName'] : null;\n\t\t\t}\n\n\t\t\tif (body?.['companyName'] !== undefined) {\n\t\t\t\topts.companyName = typeof body['companyName'] === 'string' ? body['companyName'] : null;\n\t\t\t}\n\n\t\t\tif (body?.['avatarUrl'] !== undefined) {\n\t\t\t\topts.avatarUrl = typeof body['avatarUrl'] === 'string' ? body['avatarUrl'] : null;\n\t\t\t}\n\n\t\t\tif (body?.['locale'] !== undefined && typeof body['locale'] === 'string') {\n\t\t\t\topts.locale = body['locale'];\n\t\t\t}\n\n\t\t\tif (body?.['referenceCode'] !== undefined && typeof body['referenceCode'] === 'string') {\n\t\t\t\topts.referenceCode = body['referenceCode'].toUpperCase();\n\t\t\t}\n\n\t\t\tconst customer = await customers.update(id, workspaceId, opts, prefix);\n\t\t\tif (!customer) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found');\n\t\t\t}\n\n\t\t\tbus\n\t\t\t\t?.emit(EVENT_KEYS.customerUpdated, {\n\t\t\t\t\tcustomerId: customer.id,\n\t\t\t\t\tworkspaceId: customer.workspaceId,\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\n\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMER_UPDATED', 'Customer updated successfully.', {\n\t\t\t\tcustomer: toCustomerDTO(customer),\n\t\t\t});\n\t\t},\n\n\t\tasync delete(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst id = params?.['customerId'];\n\t\t\tif (!isUuid(id)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst existing = await customers.findById(id, workspaceId);\n\t\t\tif (!existing) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found');\n\t\t\t}\n\n\t\t\tawait customers.delete(id, workspaceId);\n\n\t\t\tbus\n\t\t\t\t?.emit(EVENT_KEYS.customerDeleted, {\n\t\t\t\t\tcustomerId: id,\n\t\t\t\t\tworkspaceId,\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\n\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMER_DELETED', 'Customer deleted successfully.');\n\t\t},\n\n\t\tasync blacklist(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst id = params?.['customerId'];\n\t\t\tif (!isUuid(id)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst existing = await customers.findById(id, workspaceId);\n\t\t\tif (!existing) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst reason = typeof body?.['reason'] === 'string' ? body['reason'].trim() || null : null;\n\n\t\t\tawait customers.blacklist(id, workspaceId, reason);\n\n\t\t\tbus\n\t\t\t\t?.emit(EVENT_KEYS.customerBlacklisted, {\n\t\t\t\t\tcustomerId: id,\n\t\t\t\t\tworkspaceId,\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\n\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMER_BLACKLISTED', 'Customer blacklisted successfully.');\n\t\t},\n\n\t\tasync unblacklist(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst id = params?.['customerId'];\n\t\t\tif (!isUuid(id)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst existing = await customers.findById(id, workspaceId);\n\t\t\tif (!existing) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found');\n\t\t\t}\n\n\t\t\tawait customers.unblacklist(id, workspaceId);\n\n\t\t\tbus\n\t\t\t\t?.emit(EVENT_KEYS.customerUnblacklisted, {\n\t\t\t\t\tcustomerId: id,\n\t\t\t\t\tworkspaceId,\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\n\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMER_UNBLACKLISTED', 'Customer removed from blacklist successfully.');\n\t\t},\n\t};\n}\n","const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport function isUuid(value: unknown): value is string {\n\treturn typeof value === 'string' && UUID_RE.test(value);\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport { toCustomerAddressDTO } from '../dtos/customer';\nimport { CustomerModel } from '../models/customer.model';\nimport { CustomerAddressModel } from '../models/customer-address.model';\nimport { CustomerLabelModel } from '../models/customer-label.model';\nimport { isUuid } from '../utils';\n\nexport function customerAddressController(store: IStoreAdapter) {\n\tconst customers = new CustomerModel(store);\n\tconst addresses = new CustomerAddressModel(store);\n\tconst labels = new CustomerLabelModel(store);\n\n\tasync function resolveCustomer(ctx: IFonderieContext) {\n\t\tconst workspaceId = ctx.workspace?.id;\n\t\tif (!workspaceId) {\n\t\t\treturn {\n\t\t\t\terror: setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t),\n\t\t\t};\n\t\t}\n\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst id = params?.['customerId'];\n\t\tif (!isUuid(id)) {\n\t\t\treturn {error: setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID')};\n\t\t}\n\n\t\tconst customer = await customers.findById(id, workspaceId);\n\t\tif (!customer) {\n\t\t\treturn {error: setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found')};\n\t\t}\n\n\t\treturn { customer, workspaceId };\n\t}\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst list = await addresses.list(r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'ADDRESSES_FETCHED', 'Addresses retrieved successfully.', {\n\t\t\t\taddresses: list.map(toCustomerAddressDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync add(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst countryIso = body?.['countryIso'];\n\t\t\tconst zipPostalCode = body?.['zipPostalCode'];\n\n\t\t\tif (typeof countryIso !== 'string' || countryIso.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'countryIso is required');\n\t\t\t}\n\n\t\t\tif (typeof zipPostalCode !== 'string' || zipPostalCode.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'zipPostalCode is required');\n\t\t\t}\n\n\t\t\tconst rawLabel = typeof body?.['label'] === 'string' ? body['label'] : 'service';\n\t\t\tconst { id: labelId } = await labels.findOrCreate('address', rawLabel, r.workspaceId);\n\n\t\t\tlet created: Awaited<ReturnType<typeof addresses.add>>;\n\t\t\ttry {\n\t\t\t\tcreated = await addresses.add({\n\t\t\t\t\tcustomerId: r.customer.id,\n\t\t\t\t\tcountryIso: countryIso.trim(),\n\t\t\t\t\tzipPostalCode: zipPostalCode.trim(),\n\t\t\t\t\tsubdivision1Iso: typeof body?.['subdivision1Iso'] === 'string' ? body['subdivision1Iso'] : null,\n\t\t\t\t\tsubdivision2Iso: typeof body?.['subdivision2Iso'] === 'string' ? body['subdivision2Iso'] : null,\n\t\t\t\t\tunit: typeof body?.['unit'] === 'string' ? body['unit'] : null,\n\t\t\t\t\tline1: typeof body?.['line1'] === 'string' ? body['line1'] : null,\n\t\t\t\t\tline2: typeof body?.['line2'] === 'string' ? body['line2'] : null,\n\t\t\t\t\tlabelId,\n\t\t\t\t\tisPrimary: body?.['isPrimary'] === true,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && (err as any).code === 'DUPLICATE_ADDRESS') {\n\t\t\t\t\treturn setApiResponse(HTTP.CONFLICT, 'DUPLICATE_ADDRESS', 'This address is already linked to this customer');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'ADDRESS_ADDED', 'Address added successfully.', {\n\t\t\t\taddress: toCustomerAddressDTO(created),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst addrId = params?.['addrId'];\n\t\t\tif (!isUuid(addrId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'addrId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tif (typeof body?.['label'] !== 'string' || body['label'].trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'label is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { id: labelId } = await labels.findOrCreate('address', body['label'].trim(), r.workspaceId);\n\t\t\t\tconst updated = await addresses.updateLabel(addrId, r.customer.id, labelId);\n\t\t\t\treturn setApiResponse(HTTP.OK, 'ADDRESS_UPDATED', 'Address updated successfully.', {\n\t\t\t\t\taddress: toCustomerAddressDTO(updated),\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && (err as any).code === 'NOT_FOUND') {\n\t\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Address not found');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t},\n\n\t\tasync setPrimary(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst addrId = params?.['addrId'];\n\t\t\tif (!isUuid(addrId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'addrId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait addresses.setPrimary(addrId, r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'ADDRESS_PRIMARY_SET', 'Primary address updated successfully.');\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst addrId = params?.['addrId'];\n\t\t\tif (!isUuid(addrId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'addrId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst removed = await addresses.remove(addrId, r.customer.id);\n\t\t\tif (!removed) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Address not found');\n\t\t\t}\n\t\t\treturn setApiResponse(HTTP.OK, 'ADDRESS_REMOVED', 'Address removed successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport { toCustomerEmailDTO } from '../dtos/customer';\nimport { CustomerModel } from '../models/customer.model';\nimport { CustomerEmailModel } from '../models/customer-email.model';\nimport { CustomerLabelModel } from '../models/customer-label.model';\nimport { isUuid } from '../utils';\n\nexport function customerEmailController(store: IStoreAdapter) {\n\tconst customers = new CustomerModel(store);\n\tconst emails = new CustomerEmailModel(store);\n\tconst labels = new CustomerLabelModel(store);\n\n\tasync function resolveCustomer(ctx: IFonderieContext) {\n\t\tconst workspaceId = ctx.workspace?.id;\n\t\tif (!workspaceId) {\n\t\t\treturn {\n\t\t\t\terror: setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t),\n\t\t\t};\n\t\t}\n\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst id = params?.['customerId'];\n\t\tif (!isUuid(id)) {\n\t\t\treturn {error: setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID')};\n\t\t}\n\n\t\tconst customer = await customers.findById(id, workspaceId);\n\t\tif (!customer) {\n\t\t\treturn {error: setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found')};\n\t\t}\n\n\t\treturn { customer, workspaceId };\n\t}\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst list = await emails.list(r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'EMAILS_FETCHED', 'Emails retrieved successfully.', {\n\t\t\t\temails: list.map(toCustomerEmailDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync add(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst email = body?.['email'];\n\t\t\tif (typeof email !== 'string' || email.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'email is required');\n\t\t\t}\n\n\t\t\tconst rawLabel = typeof body?.['label'] === 'string' ? body['label'] : 'work';\n\t\t\tconst isPrimary = body?.['isPrimary'] === true;\n\t\t\tconst { id: labelId } = await labels.findOrCreate('email', rawLabel, r.workspaceId);\n\n\t\t\tlet created: Awaited<ReturnType<typeof emails.add>>;\n\t\t\ttry {\n\t\t\t\tcreated = await emails.add({\n\t\t\t\t\tcustomerId: r.customer.id,\n\t\t\t\t\temail: email.trim(),\n\t\t\t\t\tlabelId,\n\t\t\t\t\tisPrimary,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && err.message.includes('idx_fce_unique_email')) {\n\t\t\t\t\treturn setApiResponse(HTTP.CONFLICT, 'DUPLICATE_EMAIL', 'This email is already linked to this customer');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'EMAIL_ADDED', 'Email added successfully.', {\n\t\t\t\temail: toCustomerEmailDTO(created),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst emailId = params?.['emailId'];\n\t\t\tif (!isUuid(emailId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'emailId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tif (typeof body?.['label'] !== 'string' || body['label'].trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'label is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { id: labelId } = await labels.findOrCreate('email', body['label'].trim(), r.workspaceId);\n\t\t\t\tconst updated = await emails.updateLabel(emailId, r.customer.id, labelId);\n\t\t\t\treturn setApiResponse(HTTP.OK, 'EMAIL_UPDATED', 'Email updated successfully.', {\n\t\t\t\t\temail: toCustomerEmailDTO(updated),\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && (err as any).code === 'NOT_FOUND') {\n\t\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Email not found');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t},\n\n\t\tasync setPrimary(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst emailId = params?.['emailId'];\n\t\t\tif (!isUuid(emailId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'emailId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait emails.setPrimary(emailId, r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'EMAIL_PRIMARY_SET', 'Primary email updated successfully.');\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst emailId = params?.['emailId'];\n\t\t\tif (!isUuid(emailId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'emailId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait emails.remove(emailId, r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'EMAIL_REMOVED', 'Email removed successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport { toCustomerNoteDTO } from '../dtos/customer';\nimport { CustomerModel } from '../models/customer.model';\nimport { CustomerNoteModel } from '../models/customer-note.model';\nimport { isUuid } from '../utils';\n\nexport function customerNoteController(store: IStoreAdapter) {\n\tconst customers = new CustomerModel(store);\n\tconst notes = new CustomerNoteModel(store);\n\n\tasync function resolveCustomer(ctx: IFonderieContext) {\n\t\tconst workspaceId = ctx.workspace?.id;\n\t\tif (!workspaceId)\n\t\t\treturn {\n\t\t\t\terror: setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t),\n\t\t\t};\n\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst id = params?.['customerId'];\n\t\tif (!isUuid(id))\n\t\t\treturn { error: setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID') };\n\n\t\tconst customer = await customers.findById(id, workspaceId);\n\t\tif (!customer)\n\t\t\treturn { error: setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found') };\n\n\t\treturn { customer, workspaceId };\n\t}\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst list = await notes.list(r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'NOTES_FETCHED', 'Notes retrieved successfully.', {\n\t\t\t\tnotes: list.map(toCustomerNoteDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync create(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst bodyText = body?.['body'];\n\t\t\tif (typeof bodyText !== 'string' || bodyText.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'body is required');\n\t\t\t}\n\n\t\t\tconst note = await notes.create({\n\t\t\t\tcustomerId: r.customer.id,\n\t\t\t\tauthorId: ctx.user?.id ?? null,\n\t\t\t\tbody: bodyText.trim(),\n\t\t\t});\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'NOTE_CREATED', 'Note created successfully.', {\n\t\t\t\tnote: toCustomerNoteDTO(note),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst noteId = params?.['noteId'];\n\t\t\tif (!isUuid(noteId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'noteId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst bodyText = body?.['body'];\n\t\t\tif (typeof bodyText !== 'string' || bodyText.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'body is required');\n\t\t\t}\n\n\t\t\tconst note = await notes.update(noteId, r.customer.id, bodyText.trim());\n\t\t\tif (!note) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Note not found');\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.OK, 'NOTE_UPDATED', 'Note updated successfully.', {\n\t\t\t\tnote: toCustomerNoteDTO(note),\n\t\t\t});\n\t\t},\n\n\t\tasync delete(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst noteId = params?.['noteId'];\n\t\t\tif (!isUuid(noteId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'noteId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait notes.delete(noteId, r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'NOTE_DELETED', 'Note deleted successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport { toCustomerPhoneDTO } from '../dtos/customer';\nimport { CustomerModel } from '../models/customer.model';\nimport { CustomerPhoneModel } from '../models/customer-phone.model';\nimport { CustomerLabelModel } from '../models/customer-label.model';\nimport { isUuid } from '../utils';\n\nexport function customerPhoneController(store: IStoreAdapter) {\n\tconst customers = new CustomerModel(store);\n\tconst phones = new CustomerPhoneModel(store);\n\tconst labels = new CustomerLabelModel(store);\n\n\tasync function resolveCustomer(ctx: IFonderieContext) {\n\t\tconst workspaceId = ctx.workspace?.id;\n\t\tif (!workspaceId) {\n\t\t\treturn {\n\t\t\t\terror: setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t),\n\t\t\t};\n\t\t}\n\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst id = params?.['customerId'];\n\t\tif (!isUuid(id)) {\n\t\t\treturn {error: setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID')};\n\t\t}\n\n\t\tconst customer = await customers.findById(id, workspaceId);\n\t\tif (!customer) {\n\t\t\treturn {error: setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found')};\n\t\t}\n\n\t\treturn { customer, workspaceId };\n\t}\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst list = await phones.list(r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'PHONES_FETCHED', 'Phones retrieved successfully.', {\n\t\t\t\tphones: list.map(toCustomerPhoneDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync add(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst phone = body?.['phone'];\n\t\t\tif (typeof phone !== 'string' || phone.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'phone is required');\n\t\t\t}\n\n\t\t\tconst rawLabel = typeof body?.['label'] === 'string' ? body['label'] : 'mobile';\n\t\t\tconst isPrimary = body?.['isPrimary'] === true;\n\t\t\tconst { id: labelId } = await labels.findOrCreate('phone', rawLabel, r.workspaceId);\n\n\t\t\tlet created: Awaited<ReturnType<typeof phones.add>>;\n\t\t\ttry {\n\t\t\t\tcreated = await phones.add({\n\t\t\t\t\tcustomerId: r.customer.id,\n\t\t\t\t\tphone: phone.trim(),\n\t\t\t\t\tlabelId,\n\t\t\t\t\tisPrimary,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && err.message.includes('idx_fcp_unique_phone')) {\n\t\t\t\t\treturn setApiResponse(HTTP.CONFLICT, 'DUPLICATE_PHONE', 'This phone is already linked to this customer');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'PHONE_ADDED', 'Phone added successfully.', {\n\t\t\t\tphone: toCustomerPhoneDTO(created),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst phoneId = params?.['phoneId'];\n\t\t\tif (!isUuid(phoneId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'phoneId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tif (typeof body?.['label'] !== 'string' || body['label'].trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'label is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { id: labelId } = await labels.findOrCreate('phone', body['label'].trim(), r.workspaceId);\n\t\t\t\tconst updated = await phones.updateLabel(phoneId, r.customer.id, labelId);\n\t\t\t\treturn setApiResponse(HTTP.OK, 'PHONE_UPDATED', 'Phone updated successfully.', {\n\t\t\t\t\tphone: toCustomerPhoneDTO(updated),\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && (err as any).code === 'NOT_FOUND') {\n\t\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Phone not found');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t},\n\n\t\tasync setPrimary(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst phoneId = params?.['phoneId'];\n\t\t\tif (!isUuid(phoneId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'phoneId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait phones.setPrimary(phoneId, r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'PHONE_PRIMARY_SET', 'Primary phone updated successfully.');\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst phoneId = params?.['phoneId'];\n\t\t\tif (!isUuid(phoneId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'phoneId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait phones.remove(phoneId, r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'PHONE_REMOVED', 'Phone removed successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { CustomerModel } from '../models/customer.model';\nimport { CustomerTagModel } from '../models/customer-tag.model';\nimport { isUuid } from '../utils';\n\nexport function customerTagController(store: IStoreAdapter) {\n\tconst customers = new CustomerModel(store);\n\tconst tags = new CustomerTagModel(store);\n\n\tasync function resolveCustomer(ctx: IFonderieContext) {\n\t\tconst workspaceId = ctx.workspace?.id;\n\t\tif (!workspaceId)\n\t\t\treturn {\n\t\t\t\terror: setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t),\n\t\t\t};\n\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst id = params?.['customerId'];\n\t\tif (!isUuid(id))\n\t\t\treturn { error: setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID') };\n\n\t\tconst customer = await customers.findById(id, workspaceId);\n\t\tif (!customer)\n\t\t\treturn { error: setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found') };\n\n\t\treturn { customer, workspaceId };\n\t}\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst list = await tags.list(r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'TAGS_FETCHED', 'Tags retrieved successfully.', {\n\t\t\t\ttags: list,\n\t\t\t});\n\t\t},\n\n\t\tasync add(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst tag = body?.['tag'];\n\t\t\tif (typeof tag !== 'string' || tag.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'tag is required');\n\t\t\t}\n\n\t\t\tawait tags.add(r.customer.id, tag.trim());\n\t\t\treturn setApiResponse(HTTP.CREATED, 'TAG_ADDED', 'Tag added successfully.');\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst tag = params?.['tag'];\n\t\t\tif (!tag) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'tag is required');\n\t\t\t}\n\n\t\t\tawait tags.remove(r.customer.id, tag);\n\t\t\treturn setApiResponse(HTTP.OK, 'TAG_REMOVED', 'Tag removed successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport { CustomerLabelModel } from '../models/customer-label.model';\nimport { toCustomerLabelDTO } from '../dtos/customer';\nimport type { CustomerLabelType } from '../types';\nimport { isUuid } from '../utils';\n\nconst VALID_TYPES: CustomerLabelType[] = ['phone', 'email', 'address'];\n\nexport function customerLabelController(store: IStoreAdapter) {\n\tconst labels = new CustomerLabelModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst query = ctx.request.url ? new URL(ctx.request.url).searchParams : null;\n\t\t\tconst type = query?.get('type') as CustomerLabelType | null;\n\n\t\t\tif (!type || !VALID_TYPES.includes(type)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'type must be phone, email, or address');\n\t\t\t}\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst rows = await labels.list(type, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'LABELS_FETCHED', 'Labels retrieved successfully.', {\n\t\t\t\tlabels: rows.map(toCustomerLabelDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst labelId = params?.['labelId'];\n\n\t\t\tif (!isUuid(labelId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'labelId must be a valid UUID');\n\t\t\t}\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst removed = await labels.remove(labelId, ctx.workspace.id);\n\t\t\tif (!removed) {\n\t\t\t\t// Not this workspace's label, a shared default, or still in use.\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.CONFLICT,\n\t\t\t\t\t'LABEL_IN_USE',\n\t\t\t\t\t'Label cannot be deleted — it is a shared default, not owned by this workspace, or still referenced.',\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn setApiResponse(HTTP.OK, 'LABEL_DELETED', 'Label deleted successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { toCustomerRelationshipDTO } from '../dtos/customer';\nimport { CustomerModel } from '../models/customer.model';\nimport { CustomerRelationshipModel } from '../models/customer-relationship.model';\nimport { isUuid } from '../utils';\n\nexport function customerRelationshipController(store: IStoreAdapter) {\n\tconst customers = new CustomerModel(store);\n\tconst relationships = new CustomerRelationshipModel(store);\n\n\tasync function resolveCustomer(ctx: IFonderieContext) {\n\t\tconst workspaceId = ctx.workspace?.id;\n\t\tif (!workspaceId) {\n\t\t\treturn {\n\t\t\t\terror: setApiResponse(HTTP.BAD_REQUEST, 'MISSING_WORKSPACE', 'Workspace context is required'),\n\t\t\t};\n\t\t}\n\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst id = params?.['customerId'];\n\t\tif (!isUuid(id)) {\n\t\t\treturn { error: setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID') };\n\t\t}\n\n\t\tconst customer = await customers.findById(id, workspaceId);\n\t\tif (!customer) {\n\t\t\treturn { error: setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found') };\n\t\t}\n\n\t\treturn { customer, workspaceId };\n\t}\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst list = await relationships.list(r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'RELATIONSHIPS_FETCHED', 'Relationships retrieved successfully.', {\n\t\t\t\trelationships: list.map(toCustomerRelationshipDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync add(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst relatedId = body?.['relatedId'];\n\t\t\tconst relationship = body?.['relationship'];\n\n\t\t\tif (!isUuid(relatedId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'relatedId must be a valid UUID');\n\t\t\t}\n\t\t\tif (typeof relationship !== 'string' || relationship.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'relationship is required');\n\t\t\t}\n\t\t\tif (relatedId === r.customer.id) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'A customer cannot be related to itself');\n\t\t\t}\n\n\t\t\tconst related = await customers.findById(relatedId, r.workspaceId);\n\t\t\tif (!related) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Related customer not found');\n\t\t\t}\n\n\t\t\tconst created = await relationships.add({\n\t\t\t\tworkspaceId: r.workspaceId,\n\t\t\t\tcustomerId: r.customer.id,\n\t\t\t\trelatedId,\n\t\t\t\trelationship: relationship.trim(),\n\t\t\t\tisPrimary: body?.['isPrimary'] === true,\n\t\t\t});\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'RELATIONSHIP_ADDED', 'Relationship added successfully.', {\n\t\t\t\trelationship: toCustomerRelationshipDTO(created),\n\t\t\t});\n\t\t},\n\n\t\tasync setPrimary(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst relatedId = params?.['relatedId'];\n\t\t\tif (!isUuid(relatedId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'relatedId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait relationships.setPrimary(r.customer.id, relatedId);\n\t\t\treturn setApiResponse(HTTP.OK, 'RELATIONSHIP_PRIMARY_SET', 'Primary relationship updated successfully.');\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst relatedId = params?.['relatedId'];\n\t\t\tif (!isUuid(relatedId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'relatedId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait relationships.remove(r.customer.id, relatedId);\n\t\t\treturn setApiResponse(HTTP.OK, 'RELATIONSHIP_REMOVED', 'Relationship removed successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieApp, IFonderieModule } from '@fonderie/core';\nimport type { EventBus } from '@fonderie/events';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { ICustomersConfig } from './config';\nimport { buildCustomerRoutes } from './routes';\n\nexport class CustomersModule implements IFonderieModule {\n\treadonly name = '@fonderie/customers';\n\treadonly deps = ['@fonderie/workspaces'];\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\tprivate config: ICustomersConfig = {},\n\t\tprivate bus?: EventBus,\n\t) {}\n\n\tinstall(app: IFonderieApp): void {\n\t\tconst routes = buildCustomerRoutes(this.store, this.config, this.bus);\n\t\tfor (const [method, path, ...handlers] of routes) {\n\t\t\tapp.addRoute(method, path, ...handlers);\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["customerFields","import_core","customer","import_core","import_core","email","isPrimary","import_core","import_core","phone","isPrimary","import_core","import_core","import_core","email","phone","label"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/dtos/customer.ts","../src/models/customer.model.ts","../src/models/customer-relationship.model.ts","../src/models/customer-address.model.ts","../src/models/customer-email.model.ts","../src/models/customer-label.model.ts","../src/models/customer-note.model.ts","../src/models/customer-phone.model.ts","../src/models/customer-tag.model.ts","../src/routes.ts","../src/schemas.ts","../src/controllers/customer.controller.ts","../src/utils.ts","../src/controllers/customer-address.controller.ts","../src/controllers/customer-email.controller.ts","../src/controllers/customer-note.controller.ts","../src/controllers/customer-phone.controller.ts","../src/controllers/customer-tag.controller.ts","../src/controllers/customer-label.controller.ts","../src/controllers/customer-relationship.controller.ts","../src/module.ts"],"sourcesContent":["export type { CustomersEventKey, ICustomersConfig } from './config';\nexport { EVENT_KEYS } from './config';\nexport type {\n\tIAddressDTO,\n\tICustomerAddressDTO,\n\tICustomerDetailDTO,\n\tICustomerDTO,\n\tICustomerEmailDTO,\n\tICustomerNoteDTO,\n\tICustomerPhoneDTO,\n\tICustomerLabelDTO,\n} from './dtos/customer';\nexport {\n\ttoAddressDTO,\n\ttoCustomerAddressDTO,\n\ttoCustomerDetailDTO,\n\ttoCustomerDTO,\n\ttoCustomerEmailDTO,\n\ttoCustomerNoteDTO,\n\ttoCustomerPhoneDTO,\n\ttoCustomerLabelDTO,\n} from './dtos/customer';\nexport {\n\tCustomerAddressModel,\n\tCustomerEmailModel,\n\tCustomerModel,\n\tCustomerNoteModel,\n\tCustomerPhoneModel,\n\tCustomerTagModel,\n} from './models';\nexport { CustomersModule } from './module';\nexport type {\n\tCustomerType,\n\tIAddress,\n\tICustomer,\n\tICustomerAddress,\n\tICustomerDetail,\n\tICustomerEmail,\n\tICustomerNote,\n\tICustomerPhone,\n} from './types';\n\n// Request validation — enforced contract for body-taking routes; exported\n// for docs generation and typed clients.\nexport * as schemas from './schemas';\n","export const EVENT_KEYS = {\n\tcustomerCreated: 'fonderie.customer.created',\n\tcustomerUpdated: 'fonderie.customer.updated',\n\tcustomerDeleted: 'fonderie.customer.deleted',\n\tcustomerBlacklisted: 'fonderie.customer.blacklisted',\n\tcustomerUnblacklisted: 'fonderie.customer.unblacklisted',\n} as const;\n\nexport type CustomersEventKey = (typeof EVENT_KEYS)[keyof typeof EVENT_KEYS];\n\nexport const DEFAULT_REFERENCE_CODE_PREFIX = 'CLT';\n\n// Referral codes are RANDOM (not sequential like reference codes) so they are\n// safe to share publicly — a customer can't guess another's by incrementing.\n// Unambiguous alphabet: no 0/O, 1/I/L. 8 chars over 31 symbols ≈ 8.5e11 space,\n// so collisions within a workspace are astronomically rare (and the unique\n// index is the hard guard; generation retries on the vanishing chance).\nexport const REFERRAL_CODE_ALPHABET = '23456789ABCDEFGHJKMNPQRSTUVWXYZ';\nexport const REFERRAL_CODE_LENGTH = 8;\n\nexport type ICustomersConfig = {\n\t/** Prefix used when auto-generating customer reference codes. Defaults to DEFAULT_REFERENCE_CODE_PREFIX. */\n\treferenceCodePrefix?: string;\n};\n","import { arrayOrEmpty, booleanOrFalse, dateOrEmpty, stringOrEmpty } from '@fonderie/core';\n\nimport type { CustomerLabelType, CustomerSex } from '../types';\nimport type {\n\tIAddress,\n\tICustomer,\n\tICustomerAddress,\n\tICustomerDetail,\n\tICustomerDetailD2,\n\tICustomerEmail,\n\tICustomerNote,\n\tICustomerPhone,\n\tICustomerRelationship,\n\tICustomerRelationshipExpanded,\n\tICustomerRelationshipExpandedD2,\n\tICustomerShallow,\n\tICustomerLabel,\n} from '../types';\n\nexport interface ICustomerDTO {\n\tid: string;\n\ttype: string;\n\tsex: CustomerSex;\n\tfirstName: string;\n\tlastName: string;\n\tcompanyName: string;\n\tavatarUrl: string;\n\tlocale: string;\n\treferenceCode: string;\n\treferralCode: string;\n\treferredBy: string | null;\n\tblacklisted: { status: boolean; reason: string | null };\n\tcreatedBy: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface ICustomerRelationshipDTO {\n\tid: string;\n\trelatedId: string;\n\trelationship: string;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface ICustomerShallowDTO extends ICustomerDTO {\n\temails: ICustomerEmailDTO[];\n\tphones: ICustomerPhoneDTO[];\n\taddresses: ICustomerAddressDTO[];\n\tnotes: ICustomerNoteDTO[];\n\ttags: string[];\n}\n\n// Flat merge: relationship metadata + customer fields spread at the same level.\n// `id` is the relationship record id; `customerId` is the related customer's\n// id. NOTE: the spread customer fields include the related CUSTOMER's\n// createdAt/updatedAt — `relationshipCreatedAt` is when the relationship\n// itself was created (what ICustomerRelationshipDTO.createdAt means).\nexport type ICustomerRelationshipExpandedDTO = Omit<ICustomerShallowDTO, 'id'> & {\n\tid: string;\n\tcustomerId: string;\n\trelationship: string;\n\tisPrimary: boolean;\n\trelationshipCreatedAt: string;\n};\n\nexport interface ICustomerDetailDTO extends ICustomerDTO {\n\temails: ICustomerEmailDTO[];\n\tphones: ICustomerPhoneDTO[];\n\taddresses: ICustomerAddressDTO[];\n\tnotes: ICustomerNoteDTO[];\n\trelationships: ICustomerRelationshipExpandedDTO[];\n\ttags: string[];\n}\n\n// Depth-2 DTOs: each depth-1 relationship entry carries its own relationships array.\nexport type ICustomerRelationshipExpandedD2DTO = ICustomerRelationshipExpandedDTO & {\n\trelationships: ICustomerRelationshipExpandedDTO[];\n};\n\nexport interface ICustomerDetailD2DTO extends Omit<ICustomerDetailDTO, 'relationships'> {\n\trelationships: ICustomerRelationshipExpandedD2DTO[];\n}\n\nexport interface ICustomerEmailDTO {\n\tid: string;\n\temail: string;\n\tlabel: string;\n\t// Id of the shared label row (see /customers/labels) — null when unlabeled.\n\tlabelId: string | null;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface ICustomerPhoneDTO {\n\tid: string;\n\tphone: string;\n\tlabel: string;\n\t// Id of the shared label row (see /customers/labels) — null when unlabeled.\n\tlabelId: string | null;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface IAddressDTO {\n\tcountryIso: string;\n\tsubdivision1Iso: string;\n\tsubdivision2Iso: string;\n\tzipPostalCode: string;\n\tunit: string;\n\tline1: string;\n\tline2: string;\n}\n\nexport interface ICustomerAddressDTO {\n\tid: string;\n\tlabel: string;\n\t// Id of the shared label row (see /customers/labels) — null when unlabeled.\n\tlabelId: string | null;\n\tisPrimary: boolean;\n\taddress: IAddressDTO;\n}\n\nexport interface ICustomerNoteDTO {\n\tid: string;\n\tauthorId: string;\n\tbody: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\n// Serialized shared label (GET /customers/labels) — the one customers\n// response that previously bypassed DTO mapping and leaked raw Date rows.\nexport interface ICustomerLabelDTO {\n\tid: string;\n\ttype: CustomerLabelType;\n\tvalue: string;\n\tcreatedAt: string;\n}\n\nconst VALID_SEX: CustomerSex[] = ['UNKNOWN', 'MALE', 'FEMALE'];\n\nexport function toCustomerDTO(c: ICustomer): ICustomerDTO {\n\treturn {\n\t\tid: stringOrEmpty(c.id),\n\t\ttype: stringOrEmpty(c.type),\n\t\tsex: VALID_SEX.includes(c.sex as CustomerSex) ? (c.sex as CustomerSex) : 'UNKNOWN',\n\t\tfirstName: stringOrEmpty(c.firstName),\n\t\tlastName: stringOrEmpty(c.lastName),\n\t\tcompanyName: stringOrEmpty(c.companyName),\n\t\tavatarUrl: stringOrEmpty(c.avatarUrl),\n\t\tlocale: stringOrEmpty(c.locale),\n\t\treferenceCode: stringOrEmpty(c.referenceCode),\n\t\treferralCode: stringOrEmpty(c.referralCode),\n\t\treferredBy: c.referredBy ?? null,\n\t\tblacklisted: { status: booleanOrFalse(c.isBlacklisted), reason: c.blacklistReason ?? null },\n\t\tcreatedBy: stringOrEmpty(c.createdBy),\n\t\tcreatedAt: dateOrEmpty(c.createdAt),\n\t\tupdatedAt: dateOrEmpty(c.updatedAt),\n\t};\n}\n\nexport function toCustomerRelationshipDTO(r: ICustomerRelationship): ICustomerRelationshipDTO {\n\treturn {\n\t\tid: stringOrEmpty(r.id),\n\t\trelatedId: stringOrEmpty(r.relatedId),\n\t\trelationship: stringOrEmpty(r.relationship),\n\t\tisPrimary: booleanOrFalse(r.isPrimary),\n\t\tcreatedAt: dateOrEmpty(r.createdAt),\n\t};\n}\n\nexport function toCustomerShallowDTO(c: ICustomerShallow): ICustomerShallowDTO {\n\treturn {\n\t\t...toCustomerDTO(c),\n\t\temails: arrayOrEmpty<ICustomerEmail>(c.emails).map(toCustomerEmailDTO),\n\t\tphones: arrayOrEmpty<ICustomerPhone>(c.phones).map(toCustomerPhoneDTO),\n\t\taddresses: arrayOrEmpty<ICustomerAddress>(c.addresses).map(toCustomerAddressDTO),\n\t\tnotes: arrayOrEmpty<ICustomerNote>(c.notes).map(toCustomerNoteDTO),\n\t\ttags: arrayOrEmpty<string>(c.tags),\n\t};\n}\n\nexport function toCustomerRelationshipExpandedDTO(r: ICustomerRelationshipExpanded): ICustomerRelationshipExpandedDTO {\n\tconst { id: customerId, ...customerFields } = toCustomerShallowDTO(r.customer);\n\treturn {\n\t\tid: stringOrEmpty(r.id),\n\t\tcustomerId,\n\t\trelationship: stringOrEmpty(r.relationship),\n\t\tisPrimary: booleanOrFalse(r.isPrimary),\n\t\trelationshipCreatedAt: dateOrEmpty(r.createdAt),\n\t\t...customerFields,\n\t};\n}\n\nexport function toCustomerDetailDTO(c: ICustomerDetail): ICustomerDetailDTO {\n\treturn {\n\t\t...toCustomerDTO(c),\n\t\temails: arrayOrEmpty<ICustomerEmail>(c.emails).map(toCustomerEmailDTO),\n\t\tphones: arrayOrEmpty<ICustomerPhone>(c.phones).map(toCustomerPhoneDTO),\n\t\taddresses: arrayOrEmpty<ICustomerAddress>(c.addresses).map(toCustomerAddressDTO),\n\t\tnotes: arrayOrEmpty<ICustomerNote>(c.notes).map(toCustomerNoteDTO),\n\t\trelationships: arrayOrEmpty<ICustomerRelationshipExpanded>(c.relationships).map(toCustomerRelationshipExpandedDTO),\n\t\ttags: arrayOrEmpty<string>(c.tags),\n\t};\n}\n\nexport function toCustomerRelationshipExpandedD2DTO(r: ICustomerRelationshipExpandedD2): ICustomerRelationshipExpandedD2DTO {\n\tconst { id: customerId, ...customerFields } = toCustomerShallowDTO(r.customer);\n\treturn {\n\t\tid: stringOrEmpty(r.id),\n\t\tcustomerId,\n\t\trelationship: stringOrEmpty(r.relationship),\n\t\tisPrimary: booleanOrFalse(r.isPrimary),\n\t\trelationshipCreatedAt: dateOrEmpty(r.createdAt),\n\t\t...customerFields,\n\t\trelationships: arrayOrEmpty<ICustomerRelationshipExpanded>(r.customer.relationships).map(toCustomerRelationshipExpandedDTO),\n\t};\n}\n\nexport function toCustomerDetailD2DTO(c: ICustomerDetailD2): ICustomerDetailD2DTO {\n\treturn {\n\t\t...toCustomerDTO(c),\n\t\temails: arrayOrEmpty<ICustomerEmail>(c.emails).map(toCustomerEmailDTO),\n\t\tphones: arrayOrEmpty<ICustomerPhone>(c.phones).map(toCustomerPhoneDTO),\n\t\taddresses: arrayOrEmpty<ICustomerAddress>(c.addresses).map(toCustomerAddressDTO),\n\t\tnotes: arrayOrEmpty<ICustomerNote>(c.notes).map(toCustomerNoteDTO),\n\t\trelationships: arrayOrEmpty<ICustomerRelationshipExpandedD2>(c.relationships).map(toCustomerRelationshipExpandedD2DTO),\n\t\ttags: arrayOrEmpty<string>(c.tags),\n\t};\n}\n\nexport function toAddressDTO(a: IAddress): IAddressDTO {\n\treturn {\n\t\tcountryIso: stringOrEmpty(a.countryIso),\n\t\tsubdivision1Iso: stringOrEmpty(a.subdivision1Iso),\n\t\tsubdivision2Iso: stringOrEmpty(a.subdivision2Iso),\n\t\tzipPostalCode: stringOrEmpty(a.zipPostalCode),\n\t\tunit: stringOrEmpty(a.unit),\n\t\tline1: stringOrEmpty(a.line1),\n\t\tline2: stringOrEmpty(a.line2),\n\t};\n}\n\nexport function toCustomerEmailDTO(e: ICustomerEmail): ICustomerEmailDTO {\n\treturn {\n\t\tid: stringOrEmpty(e.id),\n\t\temail: stringOrEmpty(e.email),\n\t\tlabel: stringOrEmpty(e.label),\n\t\tlabelId: e.labelId ?? null,\n\t\tisPrimary: booleanOrFalse(e.isPrimary),\n\t\tcreatedAt: dateOrEmpty(e.createdAt),\n\t};\n}\n\nexport function toCustomerPhoneDTO(p: ICustomerPhone): ICustomerPhoneDTO {\n\treturn {\n\t\tid: stringOrEmpty(p.id),\n\t\tphone: stringOrEmpty(p.phone),\n\t\tlabel: stringOrEmpty(p.label),\n\t\tlabelId: p.labelId ?? null,\n\t\tisPrimary: booleanOrFalse(p.isPrimary),\n\t\tcreatedAt: dateOrEmpty(p.createdAt),\n\t};\n}\n\nexport function toCustomerAddressDTO(ca: ICustomerAddress): ICustomerAddressDTO {\n\treturn {\n\t\tid: stringOrEmpty(ca.addrId),\n\t\tlabel: stringOrEmpty(ca.label),\n\t\tlabelId: ca.labelId ?? null,\n\t\tisPrimary: booleanOrFalse(ca.isPrimary),\n\t\taddress: toAddressDTO(ca.address),\n\t};\n}\n\nexport function toCustomerNoteDTO(n: ICustomerNote): ICustomerNoteDTO {\n\treturn {\n\t\tid: stringOrEmpty(n.id),\n\t\tauthorId: stringOrEmpty(n.authorId),\n\t\tbody: stringOrEmpty(n.body),\n\t\tcreatedAt: dateOrEmpty(n.createdAt),\n\t\tupdatedAt: dateOrEmpty(n.updatedAt),\n\t};\n}\n\nexport function toCustomerLabelDTO(l: ICustomerLabel): ICustomerLabelDTO {\n\treturn {\n\t\tid: stringOrEmpty(l.id),\n\t\ttype: l.type,\n\t\tvalue: stringOrEmpty(l.value),\n\t\tcreatedAt: dateOrEmpty(l.createdAt),\n\t};\n}\n","import { randomInt } from 'node:crypto';\n\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { DEFAULT_REFERENCE_CODE_PREFIX, REFERRAL_CODE_ALPHABET, REFERRAL_CODE_LENGTH } from '../config';\nimport type {\n\tICustomer,\n\tICustomerAddress,\n\tICustomerDetail,\n\tICustomerDetailD2,\n\tICustomerEmail,\n\tICustomerPhone,\n\tICustomerRelationship,\n\tICustomerRelationshipExpanded,\n\tICustomerShallow,\n\tICustomerShallowD2,\n} from '../types';\n\nfunction groupByCustomer<T extends { customerId: string }>(rows: T[]): Map<string, T[]> {\n\treturn rows.reduce((m, r) => {\n\t\tif (!m.has(r.customerId)) m.set(r.customerId, []);\n\t\tm.get(r.customerId)!.push(r);\n\t\treturn m;\n\t}, new Map<string, T[]>());\n}\n\nconst SELECT_CUSTOMER = `\n\tid,\n\tworkspace_id AS \"workspaceId\",\n\ttype,\n\tsex,\n\tfirst_name AS \"firstName\",\n\tlast_name AS \"lastName\",\n\tcompany_name AS \"companyName\",\n\tavatar_url AS \"avatarUrl\",\n\tlocale,\n\treference_code AS \"referenceCode\",\n\treferral_code AS \"referralCode\",\n\treferred_by AS \"referredBy\",\n\tis_blacklisted AS \"isBlacklisted\",\n\tblacklist_reason AS \"blacklistReason\",\n\tcreated_by AS \"createdBy\",\n\tcreated_at AS \"createdAt\",\n\tupdated_at AS \"updatedAt\"\n`;\n\nexport interface ListCustomersOpts {\n\tworkspaceId: string;\n\tsearch?: string | undefined;\n\tblacklisted?: boolean | undefined;\n\tlimit?: number | undefined;\n\toffset?: number | undefined;\n}\n\nexport interface CreateCustomerOpts {\n\tworkspaceId: string;\n\ttype?: string;\n\tsex?: string;\n\tfirstName?: string | null;\n\tlastName?: string | null;\n\tcompanyName?: string | null;\n\tavatarUrl?: string | null;\n\tlocale?: string;\n\t/** Explicit code to assign. Omit to auto-generate ({prefix}-0001, …). */\n\treferenceCode?: string;\n\t/** Prefix used when auto-generating. Defaults to 'CLT'. */\n\treferenceCodePrefix?: string;\n\t/** Explicit referral code. Omit to auto-generate a random, workspace-unique one. */\n\treferralCode?: string;\n\t/** The referrer's referral code (from signup). Resolved to `referredBy` within the same workspace; ignored if it doesn't match a customer. */\n\treferredByCode?: string;\n\tcreatedBy?: string | null;\n}\n\nexport interface UpdateCustomerOpts {\n\ttype?: string;\n\tsex?: string;\n\tfirstName?: string | null;\n\tlastName?: string | null;\n\tcompanyName?: string | null;\n\tavatarUrl?: string | null;\n\tlocale?: string;\n\t/** Explicit code to assign. Omit to keep existing or auto-generate if none. */\n\treferenceCode?: string;\n}\n\nexport class CustomerModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tprivate async allocateCode(workspaceId: string, prefix: string): Promise<string> {\n\t\tconst [row] = await this.store.query<{ nextVal: number }>(\n\t\t\t`INSERT INTO fonderie_customer_sequences (workspace_id, prefix, next_val)\n\t\t\t VALUES ($1, $2, 1)\n\t\t\t ON CONFLICT (workspace_id, prefix) DO UPDATE\n\t\t\t SET next_val = fonderie_customer_sequences.next_val + 1\n\t\t\t RETURNING next_val AS \"nextVal\"`,\n\t\t\t[workspaceId, prefix],\n\t\t);\n\t\treturn `${prefix}-${String(row!.nextVal).padStart(4, '0')}`;\n\t}\n\n\t/** A random referral code (crypto-random over the unambiguous alphabet). */\n\tprivate randomReferralCode(): string {\n\t\tlet out = '';\n\t\tfor (let i = 0; i < REFERRAL_CODE_LENGTH; i++) {\n\t\t\tout += REFERRAL_CODE_ALPHABET[randomInt(REFERRAL_CODE_ALPHABET.length)];\n\t\t}\n\t\treturn out;\n\t}\n\n\t/**\n\t * A referral code unique within the workspace. Random codes collide only\n\t * astronomically rarely; we still pre-check and retry a few times, and the\n\t * unique index is the final guard. Throws only if the space is somehow\n\t * exhausted (not reachable in practice).\n\t */\n\tprivate async allocateReferralCode(workspaceId: string): Promise<string> {\n\t\tfor (let attempt = 0; attempt < 5; attempt++) {\n\t\t\tconst code = this.randomReferralCode();\n\t\t\tconst [hit] = await this.store.query<{ one: number }>(\n\t\t\t\t`SELECT 1 AS one FROM fonderie_customers WHERE workspace_id = $1 AND referral_code = $2 LIMIT 1`,\n\t\t\t\t[workspaceId, code],\n\t\t\t);\n\t\t\tif (!hit) return code;\n\t\t}\n\t\tthrow new Error('could not allocate a unique referral code');\n\t}\n\n\t/** Resolve a referral code to the referring customer's id, within a workspace. */\n\tasync resolveReferralCode(workspaceId: string, code: string): Promise<string | null> {\n\t\tconst [row] = await this.store.query<{ id: string }>(\n\t\t\t`SELECT id FROM fonderie_customers WHERE workspace_id = $1 AND referral_code = $2 LIMIT 1`,\n\t\t\t[workspaceId, code],\n\t\t);\n\t\treturn row?.id ?? null;\n\t}\n\n\tasync list(opts: ListCustomersOpts): Promise<ICustomer[]> {\n\t\tconst conditions: string[] = ['workspace_id = $1'];\n\t\tconst params: unknown[] = [opts.workspaceId];\n\n\t\tif (opts.blacklisted !== undefined) {\n\t\t\tparams.push(opts.blacklisted);\n\t\t\tconditions.push(`is_blacklisted = $${params.length}`);\n\t\t}\n\n\t\tif (opts.search) {\n\t\t\tparams.push(`%${opts.search}%`);\n\t\t\tconst idx = params.length;\n\t\t\tconditions.push(\n\t\t\t\t`(first_name ILIKE $${idx} OR last_name ILIKE $${idx} OR company_name ILIKE $${idx} OR reference_code ILIKE $${idx})`,\n\t\t\t);\n\t\t}\n\n\t\tconst limit = Math.min(opts.limit ?? 50, 200);\n\t\tconst offset = opts.offset ?? 0;\n\t\tparams.push(limit, offset);\n\t\tconst limitIdx = params.length - 1;\n\t\tconst offsetIdx = params.length;\n\n\t\treturn this.store.query<ICustomer>(\n\t\t\t`SELECT ${SELECT_CUSTOMER}\n\t\t\t FROM fonderie_customers\n\t\t\t WHERE ${conditions.join(' AND ')}\n\t\t\t ORDER BY created_at DESC\n\t\t\t LIMIT $${limitIdx} OFFSET $${offsetIdx}`,\n\t\t\tparams,\n\t\t);\n\t}\n\n\tasync count(opts: Omit<ListCustomersOpts, 'limit' | 'offset'>): Promise<number> {\n\t\tconst conditions: string[] = ['workspace_id = $1'];\n\t\tconst params: unknown[] = [opts.workspaceId];\n\n\t\tif (opts.blacklisted !== undefined) {\n\t\t\tparams.push(opts.blacklisted);\n\t\t\tconditions.push(`is_blacklisted = $${params.length}`);\n\t\t}\n\n\t\tif (opts.search) {\n\t\t\tparams.push(`%${opts.search}%`);\n\t\t\tconst idx = params.length;\n\t\t\tconditions.push(\n\t\t\t\t`(first_name ILIKE $${idx} OR last_name ILIKE $${idx} OR company_name ILIKE $${idx} OR reference_code ILIKE $${idx})`,\n\t\t\t);\n\t\t}\n\n\t\tconst [row] = await this.store.query<{ count: string }>(\n\t\t\t`SELECT COUNT(*) AS count\n\t\t\t FROM fonderie_customers\n\t\t\t WHERE ${conditions.join(' AND ')}`,\n\t\t\tparams,\n\t\t);\n\t\treturn Number(row?.count ?? 0);\n\t}\n\n\tasync findById(id: string, workspaceId: string): Promise<ICustomer | null> {\n\t\tconst [row] = await this.store.query<ICustomer>(\n\t\t\t`SELECT ${SELECT_CUSTOMER}\n\t\t\t FROM fonderie_customers\n\t\t\t WHERE id = $1 AND workspace_id = $2`,\n\t\t\t[id, workspaceId],\n\t\t);\n\t\treturn row ?? null;\n\t}\n\n\tasync findDetail(id: string, workspaceId: string, depth: 2): Promise<ICustomerDetailD2 | null>;\n\tasync findDetail(id: string, workspaceId: string, depth?: 1): Promise<ICustomerDetail | null>;\n\tasync findDetail(id: string, workspaceId: string, depth = 1): Promise<ICustomerDetail | ICustomerDetailD2 | null> {\n\t\tconst [row] = await this.store.query<ICustomer>(\n\t\t\t`SELECT ${SELECT_CUSTOMER}\n\t\t\t FROM fonderie_customers\n\t\t\t WHERE id = $1 AND workspace_id = $2`,\n\t\t\t[id, workspaceId],\n\t\t);\n\t\tif (!row) return null;\n\n\t\tconst [emailRows, phoneRows, addressRows, noteRows, relationshipRows, tagRows] = await Promise.all([\n\t\t\tthis.store.query<ICustomerEmail>(\n\t\t\t\t`SELECT id,\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t email,\n\t\t\t\t label_id AS \"labelId\",\n\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\t\t\t\t is_primary AS \"isPrimary\",\n\t\t\t\t created_at AS \"createdAt\"\n\t\t\t\t FROM fonderie_customer_emails\n\t\t\t\t WHERE customer_id = $1\n\t\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t\tthis.store.query<ICustomerPhone>(\n\t\t\t\t`SELECT id,\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t phone,\n\t\t\t\t label_id AS \"labelId\",\n\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\t\t\t\t is_primary AS \"isPrimary\",\n\t\t\t\t created_at AS \"createdAt\"\n\t\t\t\t FROM fonderie_customer_phones\n\t\t\t\t WHERE customer_id = $1\n\t\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t\tthis.store.query<ICustomerAddress>(\n\t\t\t\t`SELECT ca.addr_id AS \"addrId\",\n\t\t\t\t ca.customer_id AS \"customerId\",\n\t\t\t\t ca.label_id AS \"labelId\",\n\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = ca.label_id) AS label,\n\t\t\t\t ca.is_primary AS \"isPrimary\",\n\t\t\t\t jsonb_build_object(\n\t\t\t\t 'id', a.id,\n\t\t\t\t 'countryIso', a.country_iso,\n\t\t\t\t 'subdivision1Iso', a.subdivision1_iso,\n\t\t\t\t 'subdivision2Iso', a.subdivision2_iso,\n\t\t\t\t 'zipPostalCode', a.zip_postal_code,\n\t\t\t\t 'unit', a.unit,\n\t\t\t\t 'line1', a.line1,\n\t\t\t\t 'line2', a.line2\n\t\t\t\t ) AS address\n\t\t\t\t FROM fonderie_customer_addresses ca\n\t\t\t\t JOIN fonderie_addresses a ON a.id = ca.addr_id\n\t\t\t\t WHERE ca.customer_id = $1\n\t\t\t\t ORDER BY ca.is_primary DESC`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t\tthis.store.query<{\n\t\t\t\tid: string;\n\t\t\t\tcustomerId: string;\n\t\t\t\tauthorId: string | null;\n\t\t\t\tbody: string;\n\t\t\t\tcreatedAt: string;\n\t\t\t\tupdatedAt: string;\n\t\t\t}>(\n\t\t\t\t`SELECT id,\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t author_id AS \"authorId\",\n\t\t\t\t body,\n\t\t\t\t created_at AS \"createdAt\",\n\t\t\t\t updated_at AS \"updatedAt\"\n\t\t\t\t FROM fonderie_customer_notes\n\t\t\t\t WHERE customer_id = $1\n\t\t\t\t ORDER BY created_at DESC`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t\tthis.store.query<ICustomerRelationship>(\n\t\t\t\t`SELECT id,\n\t\t\t\t workspace_id AS \"workspaceId\",\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t related_id AS \"relatedId\",\n\t\t\t\t relationship,\n\t\t\t\t is_primary AS \"isPrimary\",\n\t\t\t\t created_at AS \"createdAt\"\n\t\t\t\t FROM fonderie_customer_relationships\n\t\t\t\t WHERE customer_id = $1\n\t\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t\tthis.store.query<{ tag: string }>(\n\t\t\t\t`SELECT tag FROM fonderie_customer_tags WHERE customer_id = $1 ORDER BY tag ASC`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t]);\n\n\t\t// Batch-resolve related customers so the caller gets full detail in one request.\n\t\tconst relatedIds = relationshipRows.map((r) => r.relatedId);\n\t\tlet expandedRelationships: ICustomerRelationshipExpanded[] = [];\n\n\t\tif (relatedIds.length > 0) {\n\t\t\tconst [relCustomers, relEmails, relPhones, relAddresses, relNotes, relTags] = await Promise.all([\n\t\t\t\tthis.store.query<ICustomer>(\n\t\t\t\t\t`SELECT ${SELECT_CUSTOMER} FROM fonderie_customers WHERE id = ANY($1::uuid[]) AND workspace_id = $2`,\n\t\t\t\t\t[relatedIds, workspaceId],\n\t\t\t\t),\n\t\t\t\tthis.store.query<ICustomerEmail>(\n\t\t\t\t\t`SELECT id, customer_id AS \"customerId\", email, label_id AS \"labelId\",\n\t\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\t\t\t\t\t is_primary AS \"isPrimary\", created_at AS \"createdAt\"\n\t\t\t\t\t FROM fonderie_customer_emails WHERE customer_id = ANY($1::uuid[]) ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t\t[relatedIds],\n\t\t\t\t),\n\t\t\t\tthis.store.query<ICustomerPhone>(\n\t\t\t\t\t`SELECT id, customer_id AS \"customerId\", phone, label_id AS \"labelId\",\n\t\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\t\t\t\t\t is_primary AS \"isPrimary\", created_at AS \"createdAt\"\n\t\t\t\t\t FROM fonderie_customer_phones WHERE customer_id = ANY($1::uuid[]) ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t\t[relatedIds],\n\t\t\t\t),\n\t\t\t\tthis.store.query<ICustomerAddress>(\n\t\t\t\t\t`SELECT ca.addr_id AS \"addrId\",\n\t\t\t\t\t ca.customer_id AS \"customerId\",\n\t\t\t\t\t ca.label_id AS \"labelId\",\n\t\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = ca.label_id) AS label,\n\t\t\t\t\t ca.is_primary AS \"isPrimary\",\n\t\t\t\t\t jsonb_build_object(\n\t\t\t\t\t 'id', a.id,\n\t\t\t\t\t 'countryIso', a.country_iso,\n\t\t\t\t\t 'subdivision1Iso', a.subdivision1_iso,\n\t\t\t\t\t 'subdivision2Iso', a.subdivision2_iso,\n\t\t\t\t\t 'zipPostalCode', a.zip_postal_code,\n\t\t\t\t\t 'unit', a.unit,\n\t\t\t\t\t 'line1', a.line1,\n\t\t\t\t\t 'line2', a.line2\n\t\t\t\t\t ) AS address\n\t\t\t\t\t FROM fonderie_customer_addresses ca\n\t\t\t\t\t JOIN fonderie_addresses a ON a.id = ca.addr_id\n\t\t\t\t\t WHERE ca.customer_id = ANY($1::uuid[])\n\t\t\t\t\t ORDER BY ca.is_primary DESC`,\n\t\t\t\t\t[relatedIds],\n\t\t\t\t),\n\t\t\t\tthis.store.query<{ id: string; customerId: string; authorId: string | null; body: string; createdAt: string; updatedAt: string }>(\n\t\t\t\t\t`SELECT id, customer_id AS \"customerId\", author_id AS \"authorId\", body, created_at AS \"createdAt\", updated_at AS \"updatedAt\"\n\t\t\t\t\t FROM fonderie_customer_notes WHERE customer_id = ANY($1::uuid[]) ORDER BY created_at DESC`,\n\t\t\t\t\t[relatedIds],\n\t\t\t\t),\n\t\t\t\tthis.store.query<{ customerId: string; tag: string }>(\n\t\t\t\t\t`SELECT customer_id AS \"customerId\", tag FROM fonderie_customer_tags WHERE customer_id = ANY($1::uuid[]) ORDER BY tag ASC`,\n\t\t\t\t\t[relatedIds],\n\t\t\t\t),\n\t\t\t]);\n\n\t\t\tconst customerMap = new Map(relCustomers.map((c) => [c.id, c]));\n\n\t\t\tconst emailMap = groupByCustomer(relEmails);\n\t\t\tconst phoneMap = groupByCustomer(relPhones);\n\t\t\tconst addrMap = groupByCustomer(relAddresses);\n\t\t\tconst noteMap = groupByCustomer(relNotes);\n\t\t\tconst tagMap = relTags.reduce((m, t) => {\n\t\t\t\tif (!m.has(t.customerId)) m.set(t.customerId, []);\n\t\t\t\tm.get(t.customerId)!.push(t.tag);\n\t\t\t\treturn m;\n\t\t\t}, new Map<string, string[]>());\n\n\t\t\texpandedRelationships = relationshipRows.map((rel) => ({\n\t\t\t\tid: rel.id,\n\t\t\t\tworkspaceId: rel.workspaceId,\n\t\t\t\tcustomerId: rel.customerId,\n\t\t\t\trelationship: rel.relationship,\n\t\t\t\tisPrimary: rel.isPrimary,\n\t\t\t\tcreatedAt: rel.createdAt,\n\t\t\t\tcustomer: {\n\t\t\t\t\t...(customerMap.get(rel.relatedId) ?? ({ id: rel.relatedId } as ICustomer)),\n\t\t\t\t\temails: emailMap.get(rel.relatedId) ?? [],\n\t\t\t\t\tphones: phoneMap.get(rel.relatedId) ?? [],\n\t\t\t\t\taddresses: addrMap.get(rel.relatedId) ?? [],\n\t\t\t\t\tnotes: noteMap.get(rel.relatedId) ?? [],\n\t\t\t\t\ttags: tagMap.get(rel.relatedId) ?? [],\n\t\t\t\t} as ICustomerShallow,\n\t\t\t}));\n\t\t}\n\n\t\t// ── Depth-2: resolve relationships of relationships ────────────────────\n\t\tif (depth >= 2 && expandedRelationships.length > 0) {\n\t\t\tconst visited = new Set([id]); // guard against cycles back to root\n\t\t\tconst d1Ids = expandedRelationships.map((r) => r.customer.id);\n\n\t\t\tconst d2RelRows = await this.store.query<ICustomerRelationship>(\n\t\t\t\t`SELECT id,\n\t\t\t\t workspace_id AS \"workspaceId\",\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t related_id AS \"relatedId\",\n\t\t\t\t relationship,\n\t\t\t\t is_primary AS \"isPrimary\",\n\t\t\t\t created_at AS \"createdAt\"\n\t\t\t\t FROM fonderie_customer_relationships\n\t\t\t\t WHERE customer_id = ANY($1::uuid[])\n\t\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t[d1Ids],\n\t\t\t);\n\n\t\t\tconst validD2Rows = d2RelRows.filter((r) => !visited.has(r.relatedId));\n\t\t\tconst d2Ids = [...new Set(validD2Rows.map((r) => r.relatedId))];\n\n\t\t\tlet d2CustomerMap = new Map<string, ICustomer>();\n\t\t\tlet d2EmailMap = new Map<string, ICustomerEmail[]>();\n\t\t\tlet d2PhoneMap = new Map<string, ICustomerPhone[]>();\n\t\t\tlet d2NoteMap = new Map<string, { id: string; customerId: string; authorId: string | null; body: string; createdAt: string; updatedAt: string }[]>();\n\t\t\tlet d2TagMap = new Map<string, string[]>();\n\n\t\t\tif (d2Ids.length > 0) {\n\t\t\t\t// Addresses are omitted at depth-2: family members share the parent tenant's\n\t\t\t\t// address and duplicating it inflates mobile payloads unnecessarily.\n\t\t\t\tconst [d2Customers, d2Emails, d2Phones, d2Notes, d2Tags] = await Promise.all([\n\t\t\t\t\tthis.store.query<ICustomer>(\n\t\t\t\t\t\t`SELECT ${SELECT_CUSTOMER} FROM fonderie_customers WHERE id = ANY($1::uuid[]) AND workspace_id = $2`,\n\t\t\t\t\t\t[d2Ids, workspaceId],\n\t\t\t\t\t),\n\t\t\t\t\tthis.store.query<ICustomerEmail>(\n\t\t\t\t\t\t`SELECT id, customer_id AS \"customerId\", email, label_id AS \"labelId\",\n\t\t\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\t\t\t\t\t\t is_primary AS \"isPrimary\", created_at AS \"createdAt\"\n\t\t\t\t\t\t FROM fonderie_customer_emails WHERE customer_id = ANY($1::uuid[]) ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t\t\t[d2Ids],\n\t\t\t\t\t),\n\t\t\t\t\tthis.store.query<ICustomerPhone>(\n\t\t\t\t\t\t`SELECT id, customer_id AS \"customerId\", phone, label_id AS \"labelId\",\n\t\t\t\t\t\t (SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\t\t\t\t\t\t is_primary AS \"isPrimary\", created_at AS \"createdAt\"\n\t\t\t\t\t\t FROM fonderie_customer_phones WHERE customer_id = ANY($1::uuid[]) ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t\t\t\t[d2Ids],\n\t\t\t\t\t),\n\t\t\t\t\tthis.store.query<{ id: string; customerId: string; authorId: string | null; body: string; createdAt: string; updatedAt: string }>(\n\t\t\t\t\t\t`SELECT id, customer_id AS \"customerId\", author_id AS \"authorId\", body, created_at AS \"createdAt\", updated_at AS \"updatedAt\"\n\t\t\t\t\t\t FROM fonderie_customer_notes WHERE customer_id = ANY($1::uuid[]) ORDER BY created_at DESC`,\n\t\t\t\t\t\t[d2Ids],\n\t\t\t\t\t),\n\t\t\t\t\tthis.store.query<{ customerId: string; tag: string }>(\n\t\t\t\t\t\t`SELECT customer_id AS \"customerId\", tag FROM fonderie_customer_tags WHERE customer_id = ANY($1::uuid[]) ORDER BY tag ASC`,\n\t\t\t\t\t\t[d2Ids],\n\t\t\t\t\t),\n\t\t\t\t]);\n\n\t\t\t\td2CustomerMap = new Map(d2Customers.map((c) => [c.id, c]));\n\t\t\t\td2EmailMap = groupByCustomer(d2Emails);\n\t\t\t\td2PhoneMap = groupByCustomer(d2Phones);\n\t\t\t\td2NoteMap = groupByCustomer(d2Notes);\n\t\t\t\td2TagMap = d2Tags.reduce((m, t) => {\n\t\t\t\t\tif (!m.has(t.customerId)) m.set(t.customerId, []);\n\t\t\t\t\tm.get(t.customerId)!.push(t.tag);\n\t\t\t\t\treturn m;\n\t\t\t\t}, new Map<string, string[]>());\n\t\t\t}\n\n\t\t\tconst d2Relationships: ICustomerRelationshipExpanded[] = validD2Rows.map((d2rel) => ({\n\t\t\t\tid: d2rel.id,\n\t\t\t\tworkspaceId: d2rel.workspaceId,\n\t\t\t\tcustomerId: d2rel.customerId,\n\t\t\t\trelationship: d2rel.relationship,\n\t\t\t\tisPrimary: d2rel.isPrimary,\n\t\t\t\tcreatedAt: d2rel.createdAt,\n\t\t\t\tcustomer: {\n\t\t\t\t\t...(d2CustomerMap.get(d2rel.relatedId) ?? ({ id: d2rel.relatedId } as ICustomer)),\n\t\t\t\t\temails: d2EmailMap.get(d2rel.relatedId) ?? [],\n\t\t\t\t\tphones: d2PhoneMap.get(d2rel.relatedId) ?? [],\n\t\t\t\t\taddresses: [],\n\t\t\t\t\tnotes: d2NoteMap.get(d2rel.relatedId) ?? [],\n\t\t\t\t\ttags: d2TagMap.get(d2rel.relatedId) ?? [],\n\t\t\t\t} as ICustomerShallow,\n\t\t\t}));\n\n\t\t\tconst d2RelsByD1Id = groupByCustomer(d2Relationships);\n\n\t\t\tconst d2ExpandedRelationships = expandedRelationships.map((rel) => ({\n\t\t\t\t...rel,\n\t\t\t\tcustomer: {\n\t\t\t\t\t...rel.customer,\n\t\t\t\t\trelationships: d2RelsByD1Id.get(rel.customer.id) ?? [],\n\t\t\t\t} as ICustomerShallowD2,\n\t\t\t}));\n\n\t\t\treturn {\n\t\t\t\t...row,\n\t\t\t\temails: emailRows as unknown as ICustomerDetail['emails'],\n\t\t\t\tphones: phoneRows as unknown as ICustomerDetail['phones'],\n\t\t\t\taddresses: addressRows,\n\t\t\t\tnotes: noteRows as unknown as ICustomerDetail['notes'],\n\t\t\t\trelationships: d2ExpandedRelationships,\n\t\t\t\ttags: tagRows.map((t) => t.tag),\n\t\t\t} as ICustomerDetailD2;\n\t\t}\n\n\t\treturn {\n\t\t\t...row,\n\t\t\t// DB returns TEXT for label; cast to the narrow union that callers expect.\n\t\t\temails: emailRows as unknown as ICustomerDetail['emails'],\n\t\t\tphones: phoneRows as unknown as ICustomerDetail['phones'],\n\t\t\taddresses: addressRows,\n\t\t\tnotes: noteRows as unknown as ICustomerDetail['notes'],\n\t\t\trelationships: expandedRelationships,\n\t\t\ttags: tagRows.map((t) => t.tag),\n\t\t};\n\t}\n\n\tasync create(opts: CreateCustomerOpts): Promise<ICustomer> {\n\t\tconst referenceCode = opts.referenceCode\n\t\t\t?? await this.allocateCode(opts.workspaceId, opts.referenceCodePrefix ?? DEFAULT_REFERENCE_CODE_PREFIX);\n\n\t\t// Every customer gets a shareable referral code at creation.\n\t\tconst referralCode = opts.referralCode ?? await this.allocateReferralCode(opts.workspaceId);\n\n\t\t// If they signed up with someone's code, record who referred them (same\n\t\t// workspace). An unknown code is ignored, not an error — signup shouldn't\n\t\t// fail because a referral code was mistyped.\n\t\tconst referredBy = opts.referredByCode\n\t\t\t? await this.resolveReferralCode(opts.workspaceId, opts.referredByCode)\n\t\t\t: null;\n\n\t\tconst [row] = await this.store.query<ICustomer>(\n\t\t\t`INSERT INTO fonderie_customers\n\t\t\t (workspace_id, type, sex, first_name, last_name, company_name, avatar_url, locale, reference_code, referral_code, referred_by, created_by)\n\t\t\t VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)\n\t\t\t RETURNING ${SELECT_CUSTOMER}`,\n\t\t\t[\n\t\t\t\topts.workspaceId,\n\t\t\t\topts.type ?? 'individual',\n\t\t\t\topts.sex ?? 'UNKNOWN',\n\t\t\t\topts.firstName ?? null,\n\t\t\t\topts.lastName ?? null,\n\t\t\t\topts.companyName ?? null,\n\t\t\t\topts.avatarUrl ?? null,\n\t\t\t\topts.locale ?? 'en-US',\n\t\t\t\treferenceCode,\n\t\t\t\treferralCode,\n\t\t\t\treferredBy,\n\t\t\t\topts.createdBy ?? null,\n\t\t\t],\n\t\t);\n\t\tif (!row) throw new Error('Failed to create customer');\n\t\treturn row;\n\t}\n\n\tasync update(\n\t\tid: string,\n\t\tworkspaceId: string,\n\t\topts: UpdateCustomerOpts,\n\t\treferenceCodePrefix = DEFAULT_REFERENCE_CODE_PREFIX,\n\t): Promise<ICustomer | null> {\n\t\t// Auto-assign a reference code if the customer doesn't have one yet.\n\t\tlet autoCode: string | undefined;\n\t\tif (!opts.referenceCode) {\n\t\t\tconst current = await this.findById(id, workspaceId);\n\t\t\tif (!current) return null;\n\t\t\tif (!current.referenceCode) {\n\t\t\t\tautoCode = await this.allocateCode(workspaceId, referenceCodePrefix);\n\t\t\t}\n\t\t}\n\n\t\tconst sets: string[] = ['updated_at = now()'];\n\t\tconst params: unknown[] = [id, workspaceId];\n\n\t\tif (opts.type !== undefined) {\n\t\t\tparams.push(opts.type);\n\t\t\tsets.push(`type = $${params.length}`);\n\t\t}\n\t\tif (opts.sex !== undefined) {\n\t\t\tparams.push(opts.sex);\n\t\t\tsets.push(`sex = $${params.length}`);\n\t\t}\n\t\tif (opts.firstName !== undefined) {\n\t\t\tparams.push(opts.firstName);\n\t\t\tsets.push(`first_name = $${params.length}`);\n\t\t}\n\t\tif (opts.lastName !== undefined) {\n\t\t\tparams.push(opts.lastName);\n\t\t\tsets.push(`last_name = $${params.length}`);\n\t\t}\n\t\tif (opts.companyName !== undefined) {\n\t\t\tparams.push(opts.companyName);\n\t\t\tsets.push(`company_name = $${params.length}`);\n\t\t}\n\t\tif (opts.avatarUrl !== undefined) {\n\t\t\tparams.push(opts.avatarUrl);\n\t\t\tsets.push(`avatar_url = $${params.length}`);\n\t\t}\n\t\tif (opts.locale !== undefined) {\n\t\t\tparams.push(opts.locale);\n\t\t\tsets.push(`locale = $${params.length}`);\n\t\t}\n\t\tconst resolvedCode = opts.referenceCode ?? autoCode;\n\t\tif (resolvedCode !== undefined) {\n\t\t\tparams.push(resolvedCode);\n\t\t\tsets.push(`reference_code = $${params.length}`);\n\t\t}\n\n\t\tconst [row] = await this.store.query<ICustomer>(\n\t\t\t`UPDATE fonderie_customers\n\t\t\t SET ${sets.join(', ')}\n\t\t\t WHERE id = $1 AND workspace_id = $2\n\t\t\t RETURNING ${SELECT_CUSTOMER}`,\n\t\t\tparams,\n\t\t);\n\t\treturn row ?? null;\n\t}\n\n\tasync delete(id: string, workspaceId: string): Promise<void> {\n\t\t// Delete all sub-resources in parallel, then the customer.\n\t\t// Relationships: remove both sides (owner and target) — detach strategy,\n\t\t// related customers are left intact.\n\t\t// Addresses: delete the underlying fonderie_addresses rows directly; their\n\t\t// ON DELETE CASCADE removes the customer_addresses link automatically.\n\t\t// The remaining DB cascades on the customer row act as a safety net.\n\t\tawait Promise.all([\n\t\t\tthis.store.query(`DELETE FROM fonderie_customer_emails WHERE customer_id = $1`, [id]),\n\t\t\tthis.store.query(`DELETE FROM fonderie_customer_phones WHERE customer_id = $1`, [id]),\n\t\t\tthis.store.query(\n\t\t\t\t`DELETE FROM fonderie_addresses WHERE id IN (\n\t\t\t\t\tSELECT addr_id FROM fonderie_customer_addresses WHERE customer_id = $1\n\t\t\t\t)`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t\tthis.store.query(`DELETE FROM fonderie_customer_notes WHERE customer_id = $1`, [id]),\n\t\t\tthis.store.query(`DELETE FROM fonderie_customer_tags WHERE customer_id = $1`, [id]),\n\t\t\tthis.store.query(\n\t\t\t\t`DELETE FROM fonderie_customer_relationships WHERE customer_id = $1 OR related_id = $1`,\n\t\t\t\t[id],\n\t\t\t),\n\t\t]);\n\t\tawait this.store.query(`DELETE FROM fonderie_customers WHERE id = $1 AND workspace_id = $2`, [\n\t\t\tid,\n\t\t\tworkspaceId,\n\t\t]);\n\t}\n\n\tasync blacklist(id: string, workspaceId: string, reason?: string | null): Promise<void> {\n\t\tawait this.store.query(\n\t\t\t`UPDATE fonderie_customers\n\t\t\t SET is_blacklisted = true, blacklist_reason = $3, updated_at = now()\n\t\t\t WHERE id = $1 AND workspace_id = $2`,\n\t\t\t[id, workspaceId, reason ?? null],\n\t\t);\n\t}\n\n\tasync unblacklist(id: string, workspaceId: string): Promise<void> {\n\t\tawait this.store.query(\n\t\t\t`UPDATE fonderie_customers\n\t\t\t SET is_blacklisted = false, blacklist_reason = null, updated_at = now()\n\t\t\t WHERE id = $1 AND workspace_id = $2`,\n\t\t\t[id, workspaceId],\n\t\t);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { ICustomerRelationship } from '../types';\n\nexport interface AddRelationshipOpts {\n\tworkspaceId: string;\n\tcustomerId: string;\n\trelatedId: string;\n\trelationship: string;\n\tisPrimary?: boolean;\n}\n\nexport class CustomerRelationshipModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tasync list(customerId: string): Promise<ICustomerRelationship[]> {\n\t\treturn this.store.query<ICustomerRelationship>(\n\t\t\t`SELECT id,\n\t\t\t workspace_id AS \"workspaceId\",\n\t\t\t customer_id AS \"customerId\",\n\t\t\t related_id AS \"relatedId\",\n\t\t\t relationship,\n\t\t\t is_primary AS \"isPrimary\",\n\t\t\t created_at AS \"createdAt\"\n\t\t\t FROM fonderie_customer_relationships\n\t\t\t WHERE customer_id = $1\n\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t[customerId],\n\t\t);\n\t}\n\n\tasync add(opts: AddRelationshipOpts): Promise<ICustomerRelationship> {\n\t\treturn this.store.transaction(async (tx) => {\n\t\t\tif (opts.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_relationships\n\t\t\t\t\t SET is_primary = false\n\t\t\t\t\t WHERE customer_id = $1`,\n\t\t\t\t\t[opts.customerId],\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst [row] = await tx.query<ICustomerRelationship>(\n\t\t\t\t`INSERT INTO fonderie_customer_relationships\n\t\t\t\t (workspace_id, customer_id, related_id, relationship, is_primary)\n\t\t\t\t VALUES ($1, $2, $3, $4, $5)\n\t\t\t\t ON CONFLICT (customer_id, related_id) DO UPDATE\n\t\t\t\t SET relationship = EXCLUDED.relationship,\n\t\t\t\t is_primary = EXCLUDED.is_primary\n\t\t\t\t RETURNING\n\t\t\t\t id,\n\t\t\t\t workspace_id AS \"workspaceId\",\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t related_id AS \"relatedId\",\n\t\t\t\t relationship,\n\t\t\t\t is_primary AS \"isPrimary\",\n\t\t\t\t created_at AS \"createdAt\"`,\n\t\t\t\t[opts.workspaceId, opts.customerId, opts.relatedId, opts.relationship, opts.isPrimary ?? false],\n\t\t\t);\n\t\t\tif (!row) throw new Error('Failed to create relationship');\n\t\t\treturn row;\n\t\t});\n\t}\n\n\tasync setPrimary(customerId: string, relatedId: string): Promise<void> {\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_relationships SET is_primary = false WHERE customer_id = $1`,\n\t\t\t\t[customerId],\n\t\t\t);\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_relationships\n\t\t\t\t SET is_primary = true\n\t\t\t\t WHERE customer_id = $1 AND related_id = $2`,\n\t\t\t\t[customerId, relatedId],\n\t\t\t);\n\t\t});\n\t}\n\n\tasync remove(customerId: string, relatedId: string): Promise<void> {\n\t\tawait this.store.query(\n\t\t\t`DELETE FROM fonderie_customer_relationships\n\t\t\t WHERE customer_id = $1 AND related_id = $2`,\n\t\t\t[customerId, relatedId],\n\t\t);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { ICustomerAddress } from '../types';\n\nconst SELECT_CUSTOMER_ADDRESS = `\n\tca.addr_id AS \"addrId\",\n\tca.customer_id AS \"customerId\",\n\tca.label_id AS \"labelId\",\n\t(SELECT value FROM fonderie_customer_labels WHERE id = ca.label_id) AS label,\n\tca.is_primary AS \"isPrimary\",\n\tjsonb_build_object(\n\t\t'id', a.id,\n\t\t'countryIso', a.country_iso,\n\t\t'subdivision1Iso', a.subdivision1_iso,\n\t\t'subdivision2Iso', a.subdivision2_iso,\n\t\t'zipPostalCode', a.zip_postal_code,\n\t\t'unit', a.unit,\n\t\t'line1', a.line1,\n\t\t'line2', a.line2\n\t) AS address\n`;\n\nexport class CustomerAddressModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tlist(customerId: string): Promise<ICustomerAddress[]> {\n\t\treturn this.store.query<ICustomerAddress>(\n\t\t\t`SELECT ${SELECT_CUSTOMER_ADDRESS}\n\t\t\t FROM fonderie_customer_addresses ca\n\t\t\t JOIN fonderie_addresses a ON a.id = ca.addr_id\n\t\t\t WHERE ca.customer_id = $1\n\t\t\t ORDER BY ca.is_primary DESC`,\n\t\t\t[customerId],\n\t\t);\n\t}\n\n\tasync add(opts: {\n\t\tcustomerId: string;\n\t\tcountryIso: string;\n\t\tsubdivision1Iso?: string | null;\n\t\tsubdivision2Iso?: string | null;\n\t\tzipPostalCode: string;\n\t\tunit?: string | null;\n\t\tline1?: string | null;\n\t\tline2?: string | null;\n\t\tlabelId: string;\n\t\tisPrimary?: boolean;\n\t}): Promise<ICustomerAddress> {\n\t\tconst [existing] = await this.store.query<{ addrId: string }>(\n\t\t\t`SELECT ca.addr_id AS \"addrId\"\n\t\t\t FROM fonderie_customer_addresses ca\n\t\t\t JOIN fonderie_addresses a ON a.id = ca.addr_id\n\t\t\t WHERE ca.customer_id = $1\n\t\t\t AND a.country_iso = $2\n\t\t\t AND a.zip_postal_code = $3\n\t\t\t AND a.subdivision1_iso IS NOT DISTINCT FROM $4\n\t\t\t AND a.subdivision2_iso IS NOT DISTINCT FROM $5\n\t\t\t AND a.unit IS NOT DISTINCT FROM $6\n\t\t\t AND a.line1 IS NOT DISTINCT FROM $7\n\t\t\t AND a.line2 IS NOT DISTINCT FROM $8\n\t\t\t LIMIT 1`,\n\t\t\t[\n\t\t\t\topts.customerId,\n\t\t\t\topts.countryIso,\n\t\t\t\topts.zipPostalCode,\n\t\t\t\topts.subdivision1Iso ?? null,\n\t\t\t\topts.subdivision2Iso ?? null,\n\t\t\t\topts.unit ?? null,\n\t\t\t\topts.line1 ?? null,\n\t\t\t\topts.line2 ?? null,\n\t\t\t],\n\t\t);\n\t\tif (existing) {\n\t\t\tthrow Object.assign(new Error('Duplicate address for this customer'), { code: 'DUPLICATE_ADDRESS' });\n\t\t}\n\n\t\treturn this.store.transaction(async (tx) => {\n\t\t\tif (opts.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_addresses\n\t\t\t\t\t SET is_primary = false\n\t\t\t\t\t WHERE customer_id = $1`,\n\t\t\t\t\t[opts.customerId],\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst [addr] = await tx.query<{ id: string }>(\n\t\t\t\t`INSERT INTO fonderie_addresses\n\t\t\t\t (id, country_iso, subdivision1_iso, subdivision2_iso, zip_postal_code, unit, line1, line2)\n\t\t\t\t VALUES (gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7)\n\t\t\t\t RETURNING id`,\n\t\t\t\t[\n\t\t\t\t\topts.countryIso,\n\t\t\t\t\topts.subdivision1Iso ?? null,\n\t\t\t\t\topts.subdivision2Iso ?? null,\n\t\t\t\t\topts.zipPostalCode,\n\t\t\t\t\topts.unit ?? null,\n\t\t\t\t\topts.line1 ?? null,\n\t\t\t\t\topts.line2 ?? null,\n\t\t\t\t],\n\t\t\t);\n\t\t\tif (!addr) throw new Error('Failed to create address');\n\n\t\t\tconst [row] = await tx.query<ICustomerAddress>(\n\t\t\t\t`INSERT INTO fonderie_customer_addresses (addr_id, customer_id, label_id, is_primary)\n\t\t\t\t VALUES ($1, $2, $3, $4)\n\t\t\t\t RETURNING\n\t\t\t\t addr_id AS \"addrId\",\n\t\t\t\t customer_id AS \"customerId\",\n\t\t\t\t label_id AS \"labelId\",\n\t\t\t\t NULL::text AS label,\n\t\t\t\t is_primary AS \"isPrimary\",\n\t\t\t\t NULL::jsonb AS address`,\n\t\t\t\t[addr.id, opts.customerId, opts.labelId, opts.isPrimary ?? false],\n\t\t\t);\n\t\t\tif (!row) throw new Error('Failed to link address');\n\n\t\t\tconst [full] = await tx.query<ICustomerAddress>(\n\t\t\t\t`SELECT ${SELECT_CUSTOMER_ADDRESS}\n\t\t\t\t FROM fonderie_customer_addresses ca\n\t\t\t\t JOIN fonderie_addresses a ON a.id = ca.addr_id\n\t\t\t\t WHERE ca.addr_id = $1 AND ca.customer_id = $2`,\n\t\t\t\t[addr.id, opts.customerId],\n\t\t\t);\n\t\t\tif (!full) throw new Error('Failed to fetch created address');\n\t\t\treturn full;\n\t\t});\n\t}\n\n\tasync updateLabel(addrId: string, customerId: string, labelId: string): Promise<ICustomerAddress> {\n\t\tconst [row] = await this.store.query<ICustomerAddress>(\n\t\t\t`UPDATE fonderie_customer_addresses\n\t\t\t SET label_id = $3\n\t\t\t WHERE addr_id = $1 AND customer_id = $2\n\t\t\t RETURNING addr_id AS \"addrId\", customer_id AS \"customerId\", label_id AS \"labelId\", NULL::text AS label, is_primary AS \"isPrimary\", NULL::jsonb AS address`,\n\t\t\t[addrId, customerId, labelId],\n\t\t);\n\t\tif (!row) throw Object.assign(new Error('Address not found'), { code: 'NOT_FOUND' });\n\n\t\tconst [full] = await this.store.query<ICustomerAddress>(\n\t\t\t`SELECT ${SELECT_CUSTOMER_ADDRESS}\n\t\t\t FROM fonderie_customer_addresses ca\n\t\t\t JOIN fonderie_addresses a ON a.id = ca.addr_id\n\t\t\t WHERE ca.addr_id = $1 AND ca.customer_id = $2`,\n\t\t\t[addrId, customerId],\n\t\t);\n\t\tif (!full) throw new Error('Failed to fetch updated address');\n\t\treturn full;\n\t}\n\n\tasync setPrimary(addrId: string, customerId: string): Promise<void> {\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_addresses SET is_primary = false WHERE customer_id = $1`,\n\t\t\t\t[customerId],\n\t\t\t);\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_addresses SET is_primary = true WHERE addr_id = $1 AND customer_id = $2`,\n\t\t\t\t[addrId, customerId],\n\t\t\t);\n\t\t});\n\t}\n\n\t/** Returns false when the address is not linked to THIS customer (no-op). */\n\tasync remove(addrId: string, customerId: string): Promise<boolean> {\n\t\treturn this.store.transaction(async (tx) => {\n\t\t\tconst [deleted] = await tx.query<{ isPrimary: boolean }>(\n\t\t\t\t`DELETE FROM fonderie_customer_addresses\n\t\t\t\t WHERE addr_id = $1 AND customer_id = $2\n\t\t\t\t RETURNING is_primary AS \"isPrimary\"`,\n\t\t\t\t[addrId, customerId],\n\t\t\t);\n\t\t\t// Only delete the underlying fonderie_addresses row when the scoped\n\t\t\t// link delete above actually matched. Deleting unconditionally by id\n\t\t\t// would let any caller destroy ANOTHER customer's/tenant's address\n\t\t\t// (the shared-table row + their link via cascade) with a guessed id.\n\t\t\tif (!deleted) return false;\n\t\t\tawait tx.query(`DELETE FROM fonderie_addresses WHERE id = $1`, [addrId]);\n\t\t\tif (deleted.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_addresses\n\t\t\t\t\t SET is_primary = true\n\t\t\t\t\t WHERE addr_id = (\n\t\t\t\t\t SELECT addr_id FROM fonderie_customer_addresses\n\t\t\t\t\t WHERE customer_id = $1\n\t\t\t\t\t ORDER BY addr_id ASC\n\t\t\t\t\t LIMIT 1\n\t\t\t\t\t )`,\n\t\t\t\t\t[customerId],\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { ICustomerEmail } from '../types';\n\nconst SELECT_EMAIL = `\n\tid,\n\tcustomer_id AS \"customerId\",\n\temail,\n\tlabel_id AS \"labelId\",\n\t(SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\tis_primary AS \"isPrimary\",\n\tcreated_at AS \"createdAt\"\n`;\n\nexport class CustomerEmailModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tlist(customerId: string): Promise<ICustomerEmail[]> {\n\t\treturn this.store.query<ICustomerEmail>(\n\t\t\t`SELECT ${SELECT_EMAIL}\n\t\t\t FROM fonderie_customer_emails\n\t\t\t WHERE customer_id = $1\n\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t[customerId],\n\t\t);\n\t}\n\n\tasync add(opts: {\n\t\tcustomerId: string;\n\t\temail: string;\n\t\tlabelId: string;\n\t\tisPrimary?: boolean;\n\t}): Promise<ICustomerEmail> {\n\t\treturn this.store.transaction(async (tx) => {\n\t\t\tif (opts.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_emails SET is_primary = false WHERE customer_id = $1`,\n\t\t\t\t\t[opts.customerId],\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst [row] = await tx.query<ICustomerEmail>(\n\t\t\t\t`INSERT INTO fonderie_customer_emails (id, customer_id, email, label_id, is_primary)\n\t\t\t\t VALUES (gen_random_uuid(), $1, $2, $3, $4)\n\t\t\t\t RETURNING ${SELECT_EMAIL}`,\n\t\t\t\t[opts.customerId, opts.email, opts.labelId, opts.isPrimary ?? false],\n\t\t\t);\n\t\t\tif (!row) throw new Error('Failed to add customer email');\n\t\t\treturn row;\n\t\t});\n\t}\n\n\tasync updateLabel(emailId: string, customerId: string, labelId: string): Promise<ICustomerEmail> {\n\t\tconst [row] = await this.store.query<ICustomerEmail>(\n\t\t\t`UPDATE fonderie_customer_emails\n\t\t\t SET label_id = $3\n\t\t\t WHERE id = $1 AND customer_id = $2\n\t\t\t RETURNING ${SELECT_EMAIL}`,\n\t\t\t[emailId, customerId, labelId],\n\t\t);\n\t\tif (!row) throw Object.assign(new Error('Email not found'), { code: 'NOT_FOUND' });\n\t\treturn row;\n\t}\n\n\tasync setPrimary(emailId: string, customerId: string): Promise<void> {\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_emails SET is_primary = false WHERE customer_id = $1`,\n\t\t\t\t[customerId],\n\t\t\t);\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_emails SET is_primary = true WHERE id = $1 AND customer_id = $2`,\n\t\t\t\t[emailId, customerId],\n\t\t\t);\n\t\t});\n\t}\n\n\tasync remove(emailId: string, customerId: string): Promise<void> {\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tconst [deleted] = await tx.query<{ isPrimary: boolean }>(\n\t\t\t\t`DELETE FROM fonderie_customer_emails\n\t\t\t\t WHERE id = $1 AND customer_id = $2\n\t\t\t\t RETURNING is_primary AS \"isPrimary\"`,\n\t\t\t\t[emailId, customerId],\n\t\t\t);\n\t\t\tif (deleted?.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_emails\n\t\t\t\t\t SET is_primary = true\n\t\t\t\t\t WHERE id = (\n\t\t\t\t\t SELECT id FROM fonderie_customer_emails\n\t\t\t\t\t WHERE customer_id = $1\n\t\t\t\t\t ORDER BY created_at ASC\n\t\t\t\t\t LIMIT 1\n\t\t\t\t\t )`,\n\t\t\t\t\t[customerId],\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { CustomerLabelType, ICustomerLabel } from '../types';\n\nconst SELECT = `id, type, value, created_at AS \"createdAt\"`;\n\nexport class CustomerLabelModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\t/**\n\t * List the labels a workspace may use: the shared/system defaults\n\t * (workspace_id IS NULL) plus its OWN private labels. Never another\n\t * workspace's — so listing can't enumerate other tenants' custom\n\t * vocabulary.\n\t */\n\tlist(type: CustomerLabelType, workspaceId: string): Promise<ICustomerLabel[]> {\n\t\treturn this.store.query<ICustomerLabel>(\n\t\t\t`SELECT ${SELECT}\n\t\t\t FROM fonderie_customer_labels\n\t\t\t WHERE type = $1 AND (workspace_id IS NULL OR workspace_id = $2)\n\t\t\t ORDER BY value ASC`,\n\t\t\t[type, workspaceId],\n\t\t);\n\t}\n\n\t/**\n\t * Delete a label. Only a label OWNED BY this workspace and UNREFERENCED can\n\t * go: shared defaults (workspace_id IS NULL) and other workspaces' labels\n\t * are untouchable, and an in-use label (still pointed at by an email/phone/\n\t * address) is refused so it can't be destroyed out from under a record.\n\t * Returns false when nothing was deleted.\n\t */\n\tasync remove(id: string, workspaceId: string): Promise<boolean> {\n\t\tconst rows = await this.store.query<{ id: string }>(\n\t\t\t`DELETE FROM fonderie_customer_labels l\n\t\t\t WHERE l.id = $1\n\t\t\t AND l.workspace_id = $2\n\t\t\t AND NOT EXISTS (SELECT 1 FROM fonderie_customer_emails WHERE label_id = l.id)\n\t\t\t AND NOT EXISTS (SELECT 1 FROM fonderie_customer_phones WHERE label_id = l.id)\n\t\t\t AND NOT EXISTS (SELECT 1 FROM fonderie_customer_addresses WHERE label_id = l.id)\n\t\t\t RETURNING l.id`,\n\t\t\t[id, workspaceId],\n\t\t);\n\t\treturn rows.length > 0;\n\t}\n\n\t/**\n\t * Resolve a (type, value) to a label id for this workspace: reuse a shared\n\t * default when one exists (so common labels like 'work' stay canonical),\n\t * otherwise create/reuse a label PRIVATE to this workspace.\n\t */\n\tasync findOrCreate(\n\t\ttype: CustomerLabelType,\n\t\traw: string,\n\t\tworkspaceId: string,\n\t): Promise<ICustomerLabel> {\n\t\tconst value = raw.trim().toLowerCase();\n\n\t\t// Prefer a shared default — keeps the seeded vocabulary canonical and\n\t\t// avoids minting a per-workspace duplicate of 'work'/'personal'/etc.\n\t\tconst [shared] = await this.store.query<ICustomerLabel>(\n\t\t\t`SELECT ${SELECT} FROM fonderie_customer_labels\n\t\t\t WHERE type = $1 AND value = $2 AND workspace_id IS NULL\n\t\t\t LIMIT 1`,\n\t\t\t[type, value],\n\t\t);\n\t\tif (shared) return shared;\n\n\t\t// Otherwise a workspace-private label (unique per type+value+workspace).\n\t\tconst [row] = await this.store.query<ICustomerLabel>(\n\t\t\t`INSERT INTO fonderie_customer_labels (type, value, workspace_id)\n\t\t\t VALUES ($1, $2, $3)\n\t\t\t ON CONFLICT (type, value, workspace_id) WHERE workspace_id IS NOT NULL\n\t\t\t DO UPDATE SET value = EXCLUDED.value\n\t\t\t RETURNING ${SELECT}`,\n\t\t\t[type, value, workspaceId],\n\t\t);\n\t\tif (!row) throw new Error('Failed to find or create label');\n\t\treturn row;\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { ICustomerNote } from '../types';\n\nconst SELECT_NOTE = `\n\tid,\n\tcustomer_id AS \"customerId\",\n\tauthor_id AS \"authorId\",\n\tbody,\n\tcreated_at AS \"createdAt\",\n\tupdated_at AS \"updatedAt\"\n`;\n\nexport class CustomerNoteModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tlist(customerId: string): Promise<ICustomerNote[]> {\n\t\treturn this.store.query<ICustomerNote>(\n\t\t\t`SELECT ${SELECT_NOTE}\n\t\t\t FROM fonderie_customer_notes\n\t\t\t WHERE customer_id = $1\n\t\t\t ORDER BY created_at DESC`,\n\t\t\t[customerId],\n\t\t);\n\t}\n\n\tasync create(opts: {\n\t\tcustomerId: string;\n\t\tauthorId?: string | null;\n\t\tbody: string;\n\t}): Promise<ICustomerNote> {\n\t\tconst [row] = await this.store.query<ICustomerNote>(\n\t\t\t`INSERT INTO fonderie_customer_notes (id, customer_id, author_id, body)\n\t\t\t VALUES (gen_random_uuid(), $1, $2, $3)\n\t\t\t RETURNING ${SELECT_NOTE}`,\n\t\t\t[opts.customerId, opts.authorId ?? null, opts.body],\n\t\t);\n\t\tif (!row) throw new Error('Failed to create note');\n\t\treturn row;\n\t}\n\n\tasync update(noteId: string, customerId: string, body: string): Promise<ICustomerNote | null> {\n\t\tconst [row] = await this.store.query<ICustomerNote>(\n\t\t\t`UPDATE fonderie_customer_notes\n\t\t\t SET body = $1, updated_at = now()\n\t\t\t WHERE id = $2 AND customer_id = $3\n\t\t\t RETURNING ${SELECT_NOTE}`,\n\t\t\t[body, noteId, customerId],\n\t\t);\n\t\treturn row ?? null;\n\t}\n\n\tasync delete(noteId: string, customerId: string): Promise<void> {\n\t\tawait this.store.query(\n\t\t\t`DELETE FROM fonderie_customer_notes\n\t\t\t WHERE id = $1 AND customer_id = $2`,\n\t\t\t[noteId, customerId],\n\t\t);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { ICustomerPhone } from '../types';\n\nconst SELECT_PHONE = `\n\tid,\n\tcustomer_id AS \"customerId\",\n\tphone,\n\tlabel_id AS \"labelId\",\n\t(SELECT value FROM fonderie_customer_labels WHERE id = label_id) AS label,\n\tis_primary AS \"isPrimary\",\n\tcreated_at AS \"createdAt\"\n`;\n\nexport class CustomerPhoneModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tlist(customerId: string): Promise<ICustomerPhone[]> {\n\t\treturn this.store.query<ICustomerPhone>(\n\t\t\t`SELECT ${SELECT_PHONE}\n\t\t\t FROM fonderie_customer_phones\n\t\t\t WHERE customer_id = $1\n\t\t\t ORDER BY is_primary DESC, created_at ASC`,\n\t\t\t[customerId],\n\t\t);\n\t}\n\n\tasync add(opts: {\n\t\tcustomerId: string;\n\t\tphone: string;\n\t\tlabelId: string;\n\t\tisPrimary?: boolean;\n\t}): Promise<ICustomerPhone> {\n\t\treturn this.store.transaction(async (tx) => {\n\t\t\tif (opts.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_phones SET is_primary = false WHERE customer_id = $1`,\n\t\t\t\t\t[opts.customerId],\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst [row] = await tx.query<ICustomerPhone>(\n\t\t\t\t`INSERT INTO fonderie_customer_phones (id, customer_id, phone, label_id, is_primary)\n\t\t\t\t VALUES (gen_random_uuid(), $1, $2, $3, $4)\n\t\t\t\t RETURNING ${SELECT_PHONE}`,\n\t\t\t\t[opts.customerId, opts.phone, opts.labelId, opts.isPrimary ?? false],\n\t\t\t);\n\t\t\tif (!row) throw new Error('Failed to add customer phone');\n\t\t\treturn row;\n\t\t});\n\t}\n\n\tasync updateLabel(phoneId: string, customerId: string, labelId: string): Promise<ICustomerPhone> {\n\t\tconst [row] = await this.store.query<ICustomerPhone>(\n\t\t\t`UPDATE fonderie_customer_phones\n\t\t\t SET label_id = $3\n\t\t\t WHERE id = $1 AND customer_id = $2\n\t\t\t RETURNING ${SELECT_PHONE}`,\n\t\t\t[phoneId, customerId, labelId],\n\t\t);\n\t\tif (!row) throw Object.assign(new Error('Phone not found'), { code: 'NOT_FOUND' });\n\t\treturn row;\n\t}\n\n\tasync setPrimary(phoneId: string, customerId: string): Promise<void> {\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_phones SET is_primary = false WHERE customer_id = $1`,\n\t\t\t\t[customerId],\n\t\t\t);\n\t\t\tawait tx.query(\n\t\t\t\t`UPDATE fonderie_customer_phones SET is_primary = true WHERE id = $1 AND customer_id = $2`,\n\t\t\t\t[phoneId, customerId],\n\t\t\t);\n\t\t});\n\t}\n\n\tasync remove(phoneId: string, customerId: string): Promise<void> {\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tconst [deleted] = await tx.query<{ isPrimary: boolean }>(\n\t\t\t\t`DELETE FROM fonderie_customer_phones\n\t\t\t\t WHERE id = $1 AND customer_id = $2\n\t\t\t\t RETURNING is_primary AS \"isPrimary\"`,\n\t\t\t\t[phoneId, customerId],\n\t\t\t);\n\t\t\tif (deleted?.isPrimary) {\n\t\t\t\tawait tx.query(\n\t\t\t\t\t`UPDATE fonderie_customer_phones\n\t\t\t\t\t SET is_primary = true\n\t\t\t\t\t WHERE id = (\n\t\t\t\t\t SELECT id FROM fonderie_customer_phones\n\t\t\t\t\t WHERE customer_id = $1\n\t\t\t\t\t ORDER BY created_at ASC\n\t\t\t\t\t LIMIT 1\n\t\t\t\t\t )`,\n\t\t\t\t\t[customerId],\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nexport class CustomerTagModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tasync list(customerId: string): Promise<string[]> {\n\t\tconst rows = await this.store.query<{ tag: string }>(\n\t\t\t`SELECT tag FROM fonderie_customer_tags\n\t\t\t WHERE customer_id = $1\n\t\t\t ORDER BY tag ASC`,\n\t\t\t[customerId],\n\t\t);\n\t\treturn rows.map((r) => r.tag);\n\t}\n\n\tasync add(customerId: string, tag: string): Promise<void> {\n\t\tawait this.store.query(\n\t\t\t`INSERT INTO fonderie_customer_tags (customer_id, tag)\n\t\t\t VALUES ($1, $2)\n\t\t\t ON CONFLICT (customer_id, tag) DO NOTHING`,\n\t\t\t[customerId, tag],\n\t\t);\n\t}\n\n\tasync remove(customerId: string, tag: string): Promise<void> {\n\t\tawait this.store.query(\n\t\t\t`DELETE FROM fonderie_customer_tags\n\t\t\t WHERE customer_id = $1 AND tag = $2`,\n\t\t\t[customerId, tag],\n\t\t);\n\t}\n}\n","import type { Middleware } from '@fonderie/core';\nimport { requireAuth, validate } from '@fonderie/core/middlewares';\n\nimport {\n\tnoteSchema,\n\taddTagSchema,\n\taddEmailSchema,\n\taddPhoneSchema,\n\tblacklistSchema,\n\taddAddressSchema,\n\tupdateEmailSchema,\n\tupdatePhoneSchema,\n\tupdateAddressSchema,\n\tcreateCustomerSchema,\n\tupdateCustomerSchema,\n\taddRelationshipSchema,\n} from './schemas';\nimport type { EventBus } from '@fonderie/events';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport { withWorkspace } from '@fonderie/workspaces';\n\nimport type { ICustomersConfig } from './config';\nimport { customerController } from './controllers/customer.controller';\nimport { customerAddressController } from './controllers/customer-address.controller';\nimport { customerEmailController } from './controllers/customer-email.controller';\nimport { customerNoteController } from './controllers/customer-note.controller';\nimport { customerPhoneController } from './controllers/customer-phone.controller';\nimport { customerTagController } from './controllers/customer-tag.controller';\nimport { customerLabelController } from './controllers/customer-label.controller';\nimport { customerRelationshipController } from './controllers/customer-relationship.controller';\n\ntype RouteDefinition = [string, string, ...Middleware[]];\n\nexport function buildCustomerRoutes(\n\tstore: IStoreAdapter,\n\tconfig: ICustomersConfig,\n\tbus?: EventBus,\n): RouteDefinition[] {\n\tconst wsCtx = withWorkspace(store);\n\n\tconst customer = customerController(store, config, bus);\n\tconst email = customerEmailController(store);\n\tconst phone = customerPhoneController(store);\n\tconst address = customerAddressController(store);\n\tconst note = customerNoteController(store);\n\tconst tag = customerTagController(store);\n\tconst relationship = customerRelationshipController(store);\n\tconst label = customerLabelController(store);\n\n\treturn [\n\t\t// ── Labels ───────────────────────────────────────────────────────\n\t\t['GET', '/customers/labels', requireAuth, wsCtx, label.list],\n\t\t['DELETE', '/customers/labels/:labelId', requireAuth, wsCtx, label.remove],\n\n\t\t// ── Core customer CRUD ───────────────────────────────────────────\n\t\t['GET', '/customers', requireAuth, wsCtx, customer.list],\n\t\t['POST', '/customers', requireAuth, wsCtx, validate(createCustomerSchema), customer.create],\n\t\t['GET', '/customers/:customerId', requireAuth, wsCtx, customer.get],\n\t\t['PUT', '/customers/:customerId', requireAuth, wsCtx, validate(updateCustomerSchema), customer.update],\n\t\t['DELETE', '/customers/:customerId', requireAuth, wsCtx, customer.delete],\n\t\t['POST', '/customers/:customerId/blacklist', requireAuth, wsCtx, validate(blacklistSchema), customer.blacklist],\n\t\t['POST', '/customers/:customerId/unblacklist', requireAuth, wsCtx, customer.unblacklist],\n\n\t\t// ── Emails ───────────────────────────────────────────────────────\n\t\t['GET', '/customers/:customerId/emails', requireAuth, wsCtx, email.list],\n\t\t['POST', '/customers/:customerId/emails', requireAuth, wsCtx, validate(addEmailSchema), email.add],\n\t\t['PATCH', '/customers/:customerId/emails/:emailId', requireAuth, wsCtx, validate(updateEmailSchema), email.update],\n\t\t['PUT', '/customers/:customerId/emails/:emailId/primary', requireAuth, wsCtx, email.setPrimary],\n\t\t['DELETE', '/customers/:customerId/emails/:emailId', requireAuth, wsCtx, email.remove],\n\n\t\t// ── Phones ───────────────────────────────────────────────────────\n\t\t['GET', '/customers/:customerId/phones', requireAuth, wsCtx, phone.list],\n\t\t['POST', '/customers/:customerId/phones', requireAuth, wsCtx, validate(addPhoneSchema), phone.add],\n\t\t['PATCH', '/customers/:customerId/phones/:phoneId', requireAuth, wsCtx, validate(updatePhoneSchema), phone.update],\n\t\t['PUT', '/customers/:customerId/phones/:phoneId/primary', requireAuth, wsCtx, phone.setPrimary],\n\t\t['DELETE', '/customers/:customerId/phones/:phoneId', requireAuth, wsCtx, phone.remove],\n\n\t\t// ── Addresses ────────────────────────────────────────────────────\n\t\t['GET', '/customers/:customerId/addresses', requireAuth, wsCtx, address.list],\n\t\t['POST', '/customers/:customerId/addresses', requireAuth, wsCtx, validate(addAddressSchema), address.add],\n\t\t['PATCH', '/customers/:customerId/addresses/:addrId', requireAuth, wsCtx, validate(updateAddressSchema), address.update],\n\t\t['PUT', '/customers/:customerId/addresses/:addrId/primary', requireAuth, wsCtx, address.setPrimary],\n\t\t['DELETE', '/customers/:customerId/addresses/:addrId', requireAuth, wsCtx, address.remove],\n\n\t\t// ── Notes ────────────────────────────────────────────────────────\n\t\t['GET', '/customers/:customerId/notes', requireAuth, wsCtx, note.list],\n\t\t['POST', '/customers/:customerId/notes', requireAuth, wsCtx, validate(noteSchema), note.create],\n\t\t['PUT', '/customers/:customerId/notes/:noteId', requireAuth, wsCtx, validate(noteSchema), note.update],\n\t\t['DELETE', '/customers/:customerId/notes/:noteId', requireAuth, wsCtx, note.delete],\n\n\t\t// ── Tags ─────────────────────────────────────────────────────────\n\t\t['GET', '/customers/:customerId/tags', requireAuth, wsCtx, tag.list],\n\t\t['POST', '/customers/:customerId/tags', requireAuth, wsCtx, validate(addTagSchema), tag.add],\n\t\t['DELETE', '/customers/:customerId/tags/:tag', requireAuth, wsCtx, tag.remove],\n\n\t\t// ── Relationships ────────────────────────────────────────────────────\n\t\t['GET', '/customers/:customerId/relationships', requireAuth, wsCtx, relationship.list],\n\t\t['POST', '/customers/:customerId/relationships', requireAuth, wsCtx, validate(addRelationshipSchema), relationship.add],\n\t\t['PUT', '/customers/:customerId/relationships/:relatedId/primary', requireAuth, wsCtx, relationship.setPrimary],\n\t\t['DELETE', '/customers/:customerId/relationships/:relatedId', requireAuth, wsCtx, relationship.remove],\n\t];\n}\n","import { z } from 'zod';\n\n// Request schemas — the validation contract for every body-taking customers\n// route. Wired via @fonderie/core's validate(); same pattern as\n// @fonderie/auth. Exported for docs generation and typed clients.\n\nconst label = z.string().max(100).optional();\nconst isPrimary = z.boolean().optional();\nconst email = z.string().trim().pipe(z.email());\nconst phone = z\n\t.string()\n\t.refine((v) => /^\\+?[1-9]\\d{6,14}$/.test(v.replace(/[\\s\\-()]/g, '')), 'Invalid phone number');\n\nconst customerFields = {\n\ttype: z.enum(['individual', 'business']).optional(),\n\tsex: z.enum(['UNKNOWN', 'MALE', 'FEMALE']).optional(),\n\tfirstName: z.string().max(100).nullable().optional(),\n\tlastName: z.string().max(100).nullable().optional(),\n\tcompanyName: z.string().max(200).nullable().optional(),\n\tavatarUrl: z.string().trim().pipe(z.url()).nullable().optional(),\n\tlocale: z.string().max(35).nullable().optional(),\n\treferenceCode: z.string().max(100).nullable().optional(),\n};\n\n// Referral codes are create-time only: the update controller never writes\n// them, so accepting them on update produced 200-OK silent no-ops.\nconst referralFields = {\n\treferralCode: z.string().max(100).nullable().optional(),\n\treferredByCode: z.string().max(100).nullable().optional(),\n};\n\nexport const createCustomerSchema = z.object({ ...customerFields, ...referralFields });\n\nexport const updateCustomerSchema = z\n\t.object(customerFields)\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\n\nexport const blacklistSchema = z.object({ reason: z.string().max(1000).optional() });\n\nexport const addEmailSchema = z.object({ email, label, isPrimary });\n// Only the label is editable on an existing email/phone/address — the\n// controllers apply nothing else (value changes are remove-and-re-add, and\n// setPrimary has its own route). The old schemas accepted content fields\n// and isPrimary that were silently ignored.\nexport const updateEmailSchema = z.object({\n\tlabel: z.string().trim().min(1, 'label is required').max(100),\n});\n\nexport const addPhoneSchema = z.object({ phone, label, isPrimary });\nexport const updatePhoneSchema = z.object({\n\tlabel: z.string().trim().min(1, 'label is required').max(100),\n});\n\nconst addressFields = {\n\tlabel,\n\tisPrimary,\n\tline1: z.string().max(200).nullable().optional(),\n\tline2: z.string().max(200).nullable().optional(),\n\tunit: z.string().max(50).nullable().optional(),\n\tzipPostalCode: z.string().max(20).nullable().optional(),\n\tcountryIso: z.string().max(3).nullable().optional(),\n\tsubdivision1Iso: z.string().max(10).nullable().optional(),\n\tsubdivision2Iso: z.string().max(10).nullable().optional(),\n};\nexport const addAddressSchema = z.object(addressFields);\nexport const updateAddressSchema = z.object({\n\tlabel: z.string().trim().min(1, 'label is required').max(100),\n});\n\nexport const noteSchema = z.object({ body: z.string().trim().min(1, 'body is required').max(10000) });\n\nexport const addTagSchema = z.object({ tag: z.string().trim().min(1, 'tag is required').max(100) });\n\nexport const addRelationshipSchema = z.object({\n\trelatedId: z.string().min(1, 'relatedId is required'),\n\t// The controller has always required this — an optional schema let a\n\t// type-correct client walk straight into a guaranteed 422.\n\trelationship: z.string().trim().min(1, 'relationship is required').max(100),\n\tisPrimary,\n});\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse, background } from '@fonderie/core';\nimport type { EventBus } from '@fonderie/events';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { DEFAULT_REFERENCE_CODE_PREFIX, EVENT_KEYS, type ICustomersConfig } from '../config';\nimport { toCustomerDetailD2DTO, toCustomerDetailDTO, toCustomerDTO } from '../dtos/customer';\nimport type { ICustomerDetailD2 } from '../types';\nimport { CustomerModel } from '../models/customer.model';\nimport { isUuid } from '../utils';\n\nexport function customerController(store: IStoreAdapter, config: ICustomersConfig = {}, bus?: EventBus) {\n\tconst customers = new CustomerModel(store);\n\tconst prefix = (config.referenceCodePrefix ?? DEFAULT_REFERENCE_CODE_PREFIX).toUpperCase();\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst query = ctx.request.url ? new URL(ctx.request.url).searchParams : null;\n\t\t\tconst search = query?.get('search') ?? undefined;\n\t\t\tconst archived = query?.get('blacklisted');\n\t\t\tconst limit = Number(query?.get('limit') ?? 50);\n\t\t\tconst offset = Number(query?.get('offset') ?? 0);\n\n\t\t\tconst listOpts: Parameters<typeof customers.list>[0] = {\n\t\t\t\tworkspaceId,\n\t\t\t\tlimit: Number.isFinite(limit) ? limit : 50,\n\t\t\t\toffset: Number.isFinite(offset) ? offset : 0,\n\t\t\t};\n\n\t\t\tif (search !== undefined) {\n\t\t\t\tlistOpts.search = search;\n\t\t\t}\n\n\t\t\tif (archived !== null && archived !== undefined) {\n\t\t\t\tlistOpts.blacklisted = archived === 'true' || archived === '1';\n\t\t\t}\n\n\t\t\tconst { limit: _limit, offset: _offset, ...countOpts } = listOpts;\n\t\t\tconst [list, total] = await Promise.all([\n\t\t\t\tcustomers.list(listOpts),\n\t\t\t\tcustomers.count(countOpts),\n\t\t\t]);\n\n\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMERS_FETCHED', 'Customers retrieved successfully.', {\n\t\t\t\tcustomers: list.map(toCustomerDTO),\n\t\t\t\ttotal,\n\t\t\t});\n\t\t},\n\n\t\tasync get(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst id = params?.['customerId'];\n\t\t\tif (!isUuid(id)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst query = ctx.request.url ? new URL(ctx.request.url).searchParams : null;\n\t\t\tconst depth = query?.get('depth') === '1' ? 1 : 2;\n\n\t\t\tif (depth === 2) {\n\t\t\t\tconst customer = await customers.findDetail(id, workspaceId, 2);\n\t\t\t\tif (!customer) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found');\n\t\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMER_FETCHED', 'Customer retrieved successfully.', toCustomerDetailD2DTO(customer));\n\t\t\t}\n\n\t\t\tconst customer = await customers.findDetail(id, workspaceId);\n\t\t\tif (!customer) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found');\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMER_FETCHED', 'Customer retrieved successfully.', toCustomerDetailDTO(customer));\n\t\t},\n\n\t\tasync create(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst type = body?.['type'];\n\t\t\tconst sex = body?.['sex'];\n\t\t\tconst firstName = body?.['firstName'];\n\t\t\tconst lastName = body?.['lastName'];\n\t\t\tconst companyName = body?.['companyName'];\n\n\t\t\tconst avatarUrl = body?.['avatarUrl'];\n\t\t\tconst locale = body?.['locale'];\n\t\t\tconst referenceCode = body?.['referenceCode'];\n\t\t\tconst referralCode = body?.['referralCode'];\n\t\t\tconst referredByCode = body?.['referredByCode'];\n\n\t\t\tif (type !== undefined && type !== 'individual' && type !== 'business') {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t'type must be individual or business',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (sex !== undefined && sex !== 'UNKNOWN' && sex !== 'MALE' && sex !== 'FEMALE') {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t'sex must be UNKNOWN, MALE, or FEMALE',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet customer: Awaited<ReturnType<typeof customers.create>>;\n\t\t\ttry {\n\t\t\t\tcustomer = await customers.create({\n\t\t\t\t\tworkspaceId,\n\t\t\t\t\ttype: typeof type === 'string' ? type : 'individual',\n\t\t\t\t\tsex: typeof sex === 'string' ? sex : 'UNKNOWN',\n\t\t\t\t\tfirstName: typeof firstName === 'string' ? firstName : null,\n\t\t\t\t\tlastName: typeof lastName === 'string' ? lastName : null,\n\t\t\t\t\tcompanyName: typeof companyName === 'string' ? companyName : null,\n\n\t\t\t\t\tavatarUrl: typeof avatarUrl === 'string' ? avatarUrl : null,\n\t\t\t\t\tlocale: typeof locale === 'string' ? locale : 'en-US',\n\t\t\t\t\t// exactOptionalPropertyTypes: omit the key entirely when absent\n\t\t\t\t\t...(typeof referenceCode === 'string' ? { referenceCode: referenceCode.toUpperCase() } : {}),\n\t\t\t\t\treferenceCodePrefix: prefix,\n\t\t\t\t\t// referral: explicit code override is rare; referredByCode is the signup input\n\t\t\t\t\t...(typeof referralCode === 'string' ? { referralCode: referralCode.toUpperCase() } : {}),\n\t\t\t\t\t...(typeof referredByCode === 'string' ? { referredByCode: referredByCode.toUpperCase() } : {}),\n\t\t\t\t\tcreatedBy: ctx.user?.id ?? null,\n\t\t\t\t});\n\t\t\t} catch (err: unknown) {\n\t\t\t\tif (err instanceof Error && err.message.includes('idx_fc_reference_code')) {\n\t\t\t\t\treturn setApiResponse(HTTP.CONFLICT, 'DUPLICATE_REFERENCE_CODE', 'A customer with this reference code already exists');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tawait background(bus\n\t\t\t\t?.emit(EVENT_KEYS.customerCreated, {\n\t\t\t\t\tcustomerId: customer.id,\n\t\t\t\t\tworkspaceId: customer.workspaceId,\n\t\t\t\t}));\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'CUSTOMER_CREATED', 'Customer created successfully.', {\n\t\t\t\tcustomer: toCustomerDTO(customer),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst id = params?.['customerId'];\n\t\t\tif (!isUuid(id)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst opts: Parameters<typeof customers.update>[2] = {};\n\n\t\t\tif (body?.['type'] !== undefined) {\n\t\t\t\tconst t = body['type'];\n\t\t\t\tif (t !== 'individual' && t !== 'business') {\n\t\t\t\t\treturn setApiResponse(\n\t\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t\t'type must be individual or business',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\topts.type = t;\n\t\t\t}\n\n\t\t\tif (body?.['sex'] !== undefined) {\n\t\t\t\tconst s = body['sex'];\n\t\t\t\tif (s !== 'UNKNOWN' && s !== 'MALE' && s !== 'FEMALE') {\n\t\t\t\t\treturn setApiResponse(\n\t\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t\t'sex must be UNKNOWN, MALE, or FEMALE',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\topts.sex = s;\n\t\t\t}\n\n\t\t\tif (body?.['firstName'] !== undefined) {\n\t\t\t\topts.firstName = typeof body['firstName'] === 'string' ? body['firstName'] : null;\n\t\t\t}\n\n\t\t\tif (body?.['lastName'] !== undefined) {\n\t\t\t\topts.lastName = typeof body['lastName'] === 'string' ? body['lastName'] : null;\n\t\t\t}\n\n\t\t\tif (body?.['companyName'] !== undefined) {\n\t\t\t\topts.companyName = typeof body['companyName'] === 'string' ? body['companyName'] : null;\n\t\t\t}\n\n\t\t\tif (body?.['avatarUrl'] !== undefined) {\n\t\t\t\topts.avatarUrl = typeof body['avatarUrl'] === 'string' ? body['avatarUrl'] : null;\n\t\t\t}\n\n\t\t\tif (body?.['locale'] !== undefined && typeof body['locale'] === 'string') {\n\t\t\t\topts.locale = body['locale'];\n\t\t\t}\n\n\t\t\tif (body?.['referenceCode'] !== undefined && typeof body['referenceCode'] === 'string') {\n\t\t\t\topts.referenceCode = body['referenceCode'].toUpperCase();\n\t\t\t}\n\n\t\t\tconst customer = await customers.update(id, workspaceId, opts, prefix);\n\t\t\tif (!customer) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found');\n\t\t\t}\n\n\t\t\tawait background(bus\n\t\t\t\t?.emit(EVENT_KEYS.customerUpdated, {\n\t\t\t\t\tcustomerId: customer.id,\n\t\t\t\t\tworkspaceId: customer.workspaceId,\n\t\t\t\t}));\n\n\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMER_UPDATED', 'Customer updated successfully.', {\n\t\t\t\tcustomer: toCustomerDTO(customer),\n\t\t\t});\n\t\t},\n\n\t\tasync delete(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst id = params?.['customerId'];\n\t\t\tif (!isUuid(id)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst existing = await customers.findById(id, workspaceId);\n\t\t\tif (!existing) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found');\n\t\t\t}\n\n\t\t\tawait customers.delete(id, workspaceId);\n\n\t\t\tawait background(bus\n\t\t\t\t?.emit(EVENT_KEYS.customerDeleted, {\n\t\t\t\t\tcustomerId: id,\n\t\t\t\t\tworkspaceId,\n\t\t\t\t}));\n\n\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMER_DELETED', 'Customer deleted successfully.');\n\t\t},\n\n\t\tasync blacklist(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst id = params?.['customerId'];\n\t\t\tif (!isUuid(id)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst existing = await customers.findById(id, workspaceId);\n\t\t\tif (!existing) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst reason = typeof body?.['reason'] === 'string' ? body['reason'].trim() || null : null;\n\n\t\t\tawait customers.blacklist(id, workspaceId, reason);\n\n\t\t\tawait background(bus\n\t\t\t\t?.emit(EVENT_KEYS.customerBlacklisted, {\n\t\t\t\t\tcustomerId: id,\n\t\t\t\t\tworkspaceId,\n\t\t\t\t}));\n\n\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMER_BLACKLISTED', 'Customer blacklisted successfully.');\n\t\t},\n\n\t\tasync unblacklist(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst workspaceId = ctx.workspace?.id;\n\t\t\tif (!workspaceId) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst id = params?.['customerId'];\n\t\t\tif (!isUuid(id)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst existing = await customers.findById(id, workspaceId);\n\t\t\tif (!existing) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found');\n\t\t\t}\n\n\t\t\tawait customers.unblacklist(id, workspaceId);\n\n\t\t\tawait background(bus\n\t\t\t\t?.emit(EVENT_KEYS.customerUnblacklisted, {\n\t\t\t\t\tcustomerId: id,\n\t\t\t\t\tworkspaceId,\n\t\t\t\t}));\n\n\t\t\treturn setApiResponse(HTTP.OK, 'CUSTOMER_UNBLACKLISTED', 'Customer removed from blacklist successfully.');\n\t\t},\n\t};\n}\n","const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport function isUuid(value: unknown): value is string {\n\treturn typeof value === 'string' && UUID_RE.test(value);\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport { toCustomerAddressDTO } from '../dtos/customer';\nimport { CustomerModel } from '../models/customer.model';\nimport { CustomerAddressModel } from '../models/customer-address.model';\nimport { CustomerLabelModel } from '../models/customer-label.model';\nimport { isUuid } from '../utils';\n\nexport function customerAddressController(store: IStoreAdapter) {\n\tconst customers = new CustomerModel(store);\n\tconst addresses = new CustomerAddressModel(store);\n\tconst labels = new CustomerLabelModel(store);\n\n\tasync function resolveCustomer(ctx: IFonderieContext) {\n\t\tconst workspaceId = ctx.workspace?.id;\n\t\tif (!workspaceId) {\n\t\t\treturn {\n\t\t\t\terror: setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t),\n\t\t\t};\n\t\t}\n\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst id = params?.['customerId'];\n\t\tif (!isUuid(id)) {\n\t\t\treturn {error: setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID')};\n\t\t}\n\n\t\tconst customer = await customers.findById(id, workspaceId);\n\t\tif (!customer) {\n\t\t\treturn {error: setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found')};\n\t\t}\n\n\t\treturn { customer, workspaceId };\n\t}\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst list = await addresses.list(r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'ADDRESSES_FETCHED', 'Addresses retrieved successfully.', {\n\t\t\t\taddresses: list.map(toCustomerAddressDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync add(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst countryIso = body?.['countryIso'];\n\t\t\tconst zipPostalCode = body?.['zipPostalCode'];\n\n\t\t\tif (typeof countryIso !== 'string' || countryIso.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'countryIso is required');\n\t\t\t}\n\n\t\t\tif (typeof zipPostalCode !== 'string' || zipPostalCode.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'zipPostalCode is required');\n\t\t\t}\n\n\t\t\tconst rawLabel = typeof body?.['label'] === 'string' ? body['label'] : 'service';\n\t\t\tconst { id: labelId } = await labels.findOrCreate('address', rawLabel, r.workspaceId);\n\n\t\t\tlet created: Awaited<ReturnType<typeof addresses.add>>;\n\t\t\ttry {\n\t\t\t\tcreated = await addresses.add({\n\t\t\t\t\tcustomerId: r.customer.id,\n\t\t\t\t\tcountryIso: countryIso.trim(),\n\t\t\t\t\tzipPostalCode: zipPostalCode.trim(),\n\t\t\t\t\tsubdivision1Iso: typeof body?.['subdivision1Iso'] === 'string' ? body['subdivision1Iso'] : null,\n\t\t\t\t\tsubdivision2Iso: typeof body?.['subdivision2Iso'] === 'string' ? body['subdivision2Iso'] : null,\n\t\t\t\t\tunit: typeof body?.['unit'] === 'string' ? body['unit'] : null,\n\t\t\t\t\tline1: typeof body?.['line1'] === 'string' ? body['line1'] : null,\n\t\t\t\t\tline2: typeof body?.['line2'] === 'string' ? body['line2'] : null,\n\t\t\t\t\tlabelId,\n\t\t\t\t\tisPrimary: body?.['isPrimary'] === true,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && (err as any).code === 'DUPLICATE_ADDRESS') {\n\t\t\t\t\treturn setApiResponse(HTTP.CONFLICT, 'DUPLICATE_ADDRESS', 'This address is already linked to this customer');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'ADDRESS_ADDED', 'Address added successfully.', {\n\t\t\t\taddress: toCustomerAddressDTO(created),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst addrId = params?.['addrId'];\n\t\t\tif (!isUuid(addrId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'addrId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tif (typeof body?.['label'] !== 'string' || body['label'].trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'label is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { id: labelId } = await labels.findOrCreate('address', body['label'].trim(), r.workspaceId);\n\t\t\t\tconst updated = await addresses.updateLabel(addrId, r.customer.id, labelId);\n\t\t\t\treturn setApiResponse(HTTP.OK, 'ADDRESS_UPDATED', 'Address updated successfully.', {\n\t\t\t\t\taddress: toCustomerAddressDTO(updated),\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && (err as any).code === 'NOT_FOUND') {\n\t\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Address not found');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t},\n\n\t\tasync setPrimary(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst addrId = params?.['addrId'];\n\t\t\tif (!isUuid(addrId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'addrId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait addresses.setPrimary(addrId, r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'ADDRESS_PRIMARY_SET', 'Primary address updated successfully.');\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst addrId = params?.['addrId'];\n\t\t\tif (!isUuid(addrId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'addrId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst removed = await addresses.remove(addrId, r.customer.id);\n\t\t\tif (!removed) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Address not found');\n\t\t\t}\n\t\t\treturn setApiResponse(HTTP.OK, 'ADDRESS_REMOVED', 'Address removed successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport { toCustomerEmailDTO } from '../dtos/customer';\nimport { CustomerModel } from '../models/customer.model';\nimport { CustomerEmailModel } from '../models/customer-email.model';\nimport { CustomerLabelModel } from '../models/customer-label.model';\nimport { isUuid } from '../utils';\n\nexport function customerEmailController(store: IStoreAdapter) {\n\tconst customers = new CustomerModel(store);\n\tconst emails = new CustomerEmailModel(store);\n\tconst labels = new CustomerLabelModel(store);\n\n\tasync function resolveCustomer(ctx: IFonderieContext) {\n\t\tconst workspaceId = ctx.workspace?.id;\n\t\tif (!workspaceId) {\n\t\t\treturn {\n\t\t\t\terror: setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t),\n\t\t\t};\n\t\t}\n\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst id = params?.['customerId'];\n\t\tif (!isUuid(id)) {\n\t\t\treturn {error: setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID')};\n\t\t}\n\n\t\tconst customer = await customers.findById(id, workspaceId);\n\t\tif (!customer) {\n\t\t\treturn {error: setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found')};\n\t\t}\n\n\t\treturn { customer, workspaceId };\n\t}\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst list = await emails.list(r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'EMAILS_FETCHED', 'Emails retrieved successfully.', {\n\t\t\t\temails: list.map(toCustomerEmailDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync add(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst email = body?.['email'];\n\t\t\tif (typeof email !== 'string' || email.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'email is required');\n\t\t\t}\n\n\t\t\tconst rawLabel = typeof body?.['label'] === 'string' ? body['label'] : 'work';\n\t\t\tconst isPrimary = body?.['isPrimary'] === true;\n\t\t\tconst { id: labelId } = await labels.findOrCreate('email', rawLabel, r.workspaceId);\n\n\t\t\tlet created: Awaited<ReturnType<typeof emails.add>>;\n\t\t\ttry {\n\t\t\t\tcreated = await emails.add({\n\t\t\t\t\tcustomerId: r.customer.id,\n\t\t\t\t\temail: email.trim(),\n\t\t\t\t\tlabelId,\n\t\t\t\t\tisPrimary,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && err.message.includes('idx_fce_unique_email')) {\n\t\t\t\t\treturn setApiResponse(HTTP.CONFLICT, 'DUPLICATE_EMAIL', 'This email is already linked to this customer');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'EMAIL_ADDED', 'Email added successfully.', {\n\t\t\t\temail: toCustomerEmailDTO(created),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst emailId = params?.['emailId'];\n\t\t\tif (!isUuid(emailId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'emailId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tif (typeof body?.['label'] !== 'string' || body['label'].trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'label is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { id: labelId } = await labels.findOrCreate('email', body['label'].trim(), r.workspaceId);\n\t\t\t\tconst updated = await emails.updateLabel(emailId, r.customer.id, labelId);\n\t\t\t\treturn setApiResponse(HTTP.OK, 'EMAIL_UPDATED', 'Email updated successfully.', {\n\t\t\t\t\temail: toCustomerEmailDTO(updated),\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && (err as any).code === 'NOT_FOUND') {\n\t\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Email not found');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t},\n\n\t\tasync setPrimary(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst emailId = params?.['emailId'];\n\t\t\tif (!isUuid(emailId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'emailId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait emails.setPrimary(emailId, r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'EMAIL_PRIMARY_SET', 'Primary email updated successfully.');\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst emailId = params?.['emailId'];\n\t\t\tif (!isUuid(emailId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'emailId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait emails.remove(emailId, r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'EMAIL_REMOVED', 'Email removed successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport { toCustomerNoteDTO } from '../dtos/customer';\nimport { CustomerModel } from '../models/customer.model';\nimport { CustomerNoteModel } from '../models/customer-note.model';\nimport { isUuid } from '../utils';\n\nexport function customerNoteController(store: IStoreAdapter) {\n\tconst customers = new CustomerModel(store);\n\tconst notes = new CustomerNoteModel(store);\n\n\tasync function resolveCustomer(ctx: IFonderieContext) {\n\t\tconst workspaceId = ctx.workspace?.id;\n\t\tif (!workspaceId)\n\t\t\treturn {\n\t\t\t\terror: setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t),\n\t\t\t};\n\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst id = params?.['customerId'];\n\t\tif (!isUuid(id))\n\t\t\treturn { error: setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID') };\n\n\t\tconst customer = await customers.findById(id, workspaceId);\n\t\tif (!customer)\n\t\t\treturn { error: setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found') };\n\n\t\treturn { customer, workspaceId };\n\t}\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst list = await notes.list(r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'NOTES_FETCHED', 'Notes retrieved successfully.', {\n\t\t\t\tnotes: list.map(toCustomerNoteDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync create(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst bodyText = body?.['body'];\n\t\t\tif (typeof bodyText !== 'string' || bodyText.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'body is required');\n\t\t\t}\n\n\t\t\tconst note = await notes.create({\n\t\t\t\tcustomerId: r.customer.id,\n\t\t\t\tauthorId: ctx.user?.id ?? null,\n\t\t\t\tbody: bodyText.trim(),\n\t\t\t});\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'NOTE_CREATED', 'Note created successfully.', {\n\t\t\t\tnote: toCustomerNoteDTO(note),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst noteId = params?.['noteId'];\n\t\t\tif (!isUuid(noteId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'noteId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst bodyText = body?.['body'];\n\t\t\tif (typeof bodyText !== 'string' || bodyText.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'body is required');\n\t\t\t}\n\n\t\t\tconst note = await notes.update(noteId, r.customer.id, bodyText.trim());\n\t\t\tif (!note) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Note not found');\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.OK, 'NOTE_UPDATED', 'Note updated successfully.', {\n\t\t\t\tnote: toCustomerNoteDTO(note),\n\t\t\t});\n\t\t},\n\n\t\tasync delete(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst noteId = params?.['noteId'];\n\t\t\tif (!isUuid(noteId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'noteId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait notes.delete(noteId, r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'NOTE_DELETED', 'Note deleted successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport { toCustomerPhoneDTO } from '../dtos/customer';\nimport { CustomerModel } from '../models/customer.model';\nimport { CustomerPhoneModel } from '../models/customer-phone.model';\nimport { CustomerLabelModel } from '../models/customer-label.model';\nimport { isUuid } from '../utils';\n\nexport function customerPhoneController(store: IStoreAdapter) {\n\tconst customers = new CustomerModel(store);\n\tconst phones = new CustomerPhoneModel(store);\n\tconst labels = new CustomerLabelModel(store);\n\n\tasync function resolveCustomer(ctx: IFonderieContext) {\n\t\tconst workspaceId = ctx.workspace?.id;\n\t\tif (!workspaceId) {\n\t\t\treturn {\n\t\t\t\terror: setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t),\n\t\t\t};\n\t\t}\n\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst id = params?.['customerId'];\n\t\tif (!isUuid(id)) {\n\t\t\treturn {error: setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID')};\n\t\t}\n\n\t\tconst customer = await customers.findById(id, workspaceId);\n\t\tif (!customer) {\n\t\t\treturn {error: setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found')};\n\t\t}\n\n\t\treturn { customer, workspaceId };\n\t}\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst list = await phones.list(r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'PHONES_FETCHED', 'Phones retrieved successfully.', {\n\t\t\t\tphones: list.map(toCustomerPhoneDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync add(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst phone = body?.['phone'];\n\t\t\tif (typeof phone !== 'string' || phone.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'phone is required');\n\t\t\t}\n\n\t\t\tconst rawLabel = typeof body?.['label'] === 'string' ? body['label'] : 'mobile';\n\t\t\tconst isPrimary = body?.['isPrimary'] === true;\n\t\t\tconst { id: labelId } = await labels.findOrCreate('phone', rawLabel, r.workspaceId);\n\n\t\t\tlet created: Awaited<ReturnType<typeof phones.add>>;\n\t\t\ttry {\n\t\t\t\tcreated = await phones.add({\n\t\t\t\t\tcustomerId: r.customer.id,\n\t\t\t\t\tphone: phone.trim(),\n\t\t\t\t\tlabelId,\n\t\t\t\t\tisPrimary,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && err.message.includes('idx_fcp_unique_phone')) {\n\t\t\t\t\treturn setApiResponse(HTTP.CONFLICT, 'DUPLICATE_PHONE', 'This phone is already linked to this customer');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'PHONE_ADDED', 'Phone added successfully.', {\n\t\t\t\tphone: toCustomerPhoneDTO(created),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst phoneId = params?.['phoneId'];\n\t\t\tif (!isUuid(phoneId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'phoneId must be a valid UUID');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tif (typeof body?.['label'] !== 'string' || body['label'].trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'label is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { id: labelId } = await labels.findOrCreate('phone', body['label'].trim(), r.workspaceId);\n\t\t\t\tconst updated = await phones.updateLabel(phoneId, r.customer.id, labelId);\n\t\t\t\treturn setApiResponse(HTTP.OK, 'PHONE_UPDATED', 'Phone updated successfully.', {\n\t\t\t\t\tphone: toCustomerPhoneDTO(updated),\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && (err as any).code === 'NOT_FOUND') {\n\t\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Phone not found');\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t},\n\n\t\tasync setPrimary(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst phoneId = params?.['phoneId'];\n\t\t\tif (!isUuid(phoneId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'phoneId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait phones.setPrimary(phoneId, r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'PHONE_PRIMARY_SET', 'Primary phone updated successfully.');\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) {\n\t\t\t\treturn r.error;\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst phoneId = params?.['phoneId'];\n\t\t\tif (!isUuid(phoneId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'phoneId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait phones.remove(phoneId, r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'PHONE_REMOVED', 'Phone removed successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { CustomerModel } from '../models/customer.model';\nimport { CustomerTagModel } from '../models/customer-tag.model';\nimport { isUuid } from '../utils';\n\nexport function customerTagController(store: IStoreAdapter) {\n\tconst customers = new CustomerModel(store);\n\tconst tags = new CustomerTagModel(store);\n\n\tasync function resolveCustomer(ctx: IFonderieContext) {\n\t\tconst workspaceId = ctx.workspace?.id;\n\t\tif (!workspaceId)\n\t\t\treturn {\n\t\t\t\terror: setApiResponse(\n\t\t\t\t\tHTTP.BAD_REQUEST,\n\t\t\t\t\t'MISSING_WORKSPACE',\n\t\t\t\t\t'Workspace context is required',\n\t\t\t\t),\n\t\t\t};\n\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst id = params?.['customerId'];\n\t\tif (!isUuid(id))\n\t\t\treturn { error: setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID') };\n\n\t\tconst customer = await customers.findById(id, workspaceId);\n\t\tif (!customer)\n\t\t\treturn { error: setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found') };\n\n\t\treturn { customer, workspaceId };\n\t}\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst list = await tags.list(r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'TAGS_FETCHED', 'Tags retrieved successfully.', {\n\t\t\t\ttags: list,\n\t\t\t});\n\t\t},\n\n\t\tasync add(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst tag = body?.['tag'];\n\t\t\tif (typeof tag !== 'string' || tag.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'tag is required');\n\t\t\t}\n\n\t\t\tawait tags.add(r.customer.id, tag.trim());\n\t\t\treturn setApiResponse(HTTP.CREATED, 'TAG_ADDED', 'Tag added successfully.');\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst tag = params?.['tag'];\n\t\t\tif (!tag) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'tag is required');\n\t\t\t}\n\n\t\t\tawait tags.remove(r.customer.id, tag);\n\t\t\treturn setApiResponse(HTTP.OK, 'TAG_REMOVED', 'Tag removed successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport { CustomerLabelModel } from '../models/customer-label.model';\nimport { toCustomerLabelDTO } from '../dtos/customer';\nimport type { CustomerLabelType } from '../types';\nimport { isUuid } from '../utils';\n\nconst VALID_TYPES: CustomerLabelType[] = ['phone', 'email', 'address'];\n\nexport function customerLabelController(store: IStoreAdapter) {\n\tconst labels = new CustomerLabelModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst query = ctx.request.url ? new URL(ctx.request.url).searchParams : null;\n\t\t\tconst type = query?.get('type') as CustomerLabelType | null;\n\n\t\t\tif (!type || !VALID_TYPES.includes(type)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'type must be phone, email, or address');\n\t\t\t}\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst rows = await labels.list(type, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'LABELS_FETCHED', 'Labels retrieved successfully.', {\n\t\t\t\tlabels: rows.map(toCustomerLabelDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst labelId = params?.['labelId'];\n\n\t\t\tif (!isUuid(labelId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'labelId must be a valid UUID');\n\t\t\t}\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst removed = await labels.remove(labelId, ctx.workspace.id);\n\t\t\tif (!removed) {\n\t\t\t\t// Not this workspace's label, a shared default, or still in use.\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.CONFLICT,\n\t\t\t\t\t'LABEL_IN_USE',\n\t\t\t\t\t'Label cannot be deleted — it is a shared default, not owned by this workspace, or still referenced.',\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn setApiResponse(HTTP.OK, 'LABEL_DELETED', 'Label deleted successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieContext } from '@fonderie/core';\nimport { HTTP, setApiResponse } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { toCustomerRelationshipDTO } from '../dtos/customer';\nimport { CustomerModel } from '../models/customer.model';\nimport { CustomerRelationshipModel } from '../models/customer-relationship.model';\nimport { isUuid } from '../utils';\n\nexport function customerRelationshipController(store: IStoreAdapter) {\n\tconst customers = new CustomerModel(store);\n\tconst relationships = new CustomerRelationshipModel(store);\n\n\tasync function resolveCustomer(ctx: IFonderieContext) {\n\t\tconst workspaceId = ctx.workspace?.id;\n\t\tif (!workspaceId) {\n\t\t\treturn {\n\t\t\t\terror: setApiResponse(HTTP.BAD_REQUEST, 'MISSING_WORKSPACE', 'Workspace context is required'),\n\t\t\t};\n\t\t}\n\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst id = params?.['customerId'];\n\t\tif (!isUuid(id)) {\n\t\t\treturn { error: setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'customerId must be a valid UUID') };\n\t\t}\n\n\t\tconst customer = await customers.findById(id, workspaceId);\n\t\tif (!customer) {\n\t\t\treturn { error: setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Customer not found') };\n\t\t}\n\n\t\treturn { customer, workspaceId };\n\t}\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst list = await relationships.list(r.customer.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'RELATIONSHIPS_FETCHED', 'Relationships retrieved successfully.', {\n\t\t\t\trelationships: list.map(toCustomerRelationshipDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync add(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst relatedId = body?.['relatedId'];\n\t\t\tconst relationship = body?.['relationship'];\n\n\t\t\tif (!isUuid(relatedId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'relatedId must be a valid UUID');\n\t\t\t}\n\t\t\tif (typeof relationship !== 'string' || relationship.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'relationship is required');\n\t\t\t}\n\t\t\tif (relatedId === r.customer.id) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'A customer cannot be related to itself');\n\t\t\t}\n\n\t\t\tconst related = await customers.findById(relatedId, r.workspaceId);\n\t\t\tif (!related) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Related customer not found');\n\t\t\t}\n\n\t\t\tconst created = await relationships.add({\n\t\t\t\tworkspaceId: r.workspaceId,\n\t\t\t\tcustomerId: r.customer.id,\n\t\t\t\trelatedId,\n\t\t\t\trelationship: relationship.trim(),\n\t\t\t\tisPrimary: body?.['isPrimary'] === true,\n\t\t\t});\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'RELATIONSHIP_ADDED', 'Relationship added successfully.', {\n\t\t\t\trelationship: toCustomerRelationshipDTO(created),\n\t\t\t});\n\t\t},\n\n\t\tasync setPrimary(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst relatedId = params?.['relatedId'];\n\t\t\tif (!isUuid(relatedId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'relatedId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait relationships.setPrimary(r.customer.id, relatedId);\n\t\t\treturn setApiResponse(HTTP.OK, 'RELATIONSHIP_PRIMARY_SET', 'Primary relationship updated successfully.');\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst r = await resolveCustomer(ctx);\n\t\t\tif ('error' in r) return r.error;\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst relatedId = params?.['relatedId'];\n\t\t\tif (!isUuid(relatedId)) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'relatedId must be a valid UUID');\n\t\t\t}\n\n\t\t\tawait relationships.remove(r.customer.id, relatedId);\n\t\t\treturn setApiResponse(HTTP.OK, 'RELATIONSHIP_REMOVED', 'Relationship removed successfully.');\n\t\t},\n\t};\n}\n","import type { IFonderieApp, IFonderieModule } from '@fonderie/core';\nimport type { EventBus } from '@fonderie/events';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { ICustomersConfig } from './config';\nimport { buildCustomerRoutes } from './routes';\n\nexport class CustomersModule implements IFonderieModule {\n\treadonly name = '@fonderie/customers';\n\treadonly deps = ['@fonderie/workspaces'];\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\tprivate config: ICustomersConfig = {},\n\t\tprivate bus?: EventBus,\n\t) {}\n\n\tinstall(app: IFonderieApp): void {\n\t\tconst routes = buildCustomerRoutes(this.store, this.config, this.bus);\n\t\tfor (const [method, path, ...handlers] of routes) {\n\t\t\tapp.addRoute(method, path, ...handlers);\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["customerFields","import_core","customer","import_core","import_core","email","isPrimary","import_core","import_core","phone","isPrimary","import_core","import_core","import_core","email","phone","label"]}
|