@fonderie/customers 5.1.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/brain/signatures.md +0 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +1 -7
- package/dist/types.d.ts +1 -7
- package/package.json +1 -1
package/brain/signatures.md
CHANGED
|
@@ -155,12 +155,8 @@ new CustomersModule(store: IStoreAdapter, config?: ICustomersConfig, bus?: Event
|
|
|
155
155
|
.deps: string[]
|
|
156
156
|
.install(app: IFonderieApp): void
|
|
157
157
|
|
|
158
|
-
type AddressLabel = 'service' | 'billing' | 'other';
|
|
159
|
-
|
|
160
158
|
type CustomerType = 'individual' | 'business';
|
|
161
159
|
|
|
162
|
-
type EmailLabel = 'work' | 'personal' | 'billing';
|
|
163
|
-
|
|
164
160
|
interface IAddress {
|
|
165
161
|
id: string;
|
|
166
162
|
countryIso: string;
|
|
@@ -244,7 +240,5 @@ interface ICustomerTag {
|
|
|
244
240
|
tag: string;
|
|
245
241
|
}
|
|
246
242
|
|
|
247
|
-
type PhoneLabel = 'mobile' | 'office' | 'home' | 'fax';
|
|
248
|
-
|
|
249
243
|
namespace schemas — exports: addAddressSchema, addEmailSchema, addPhoneSchema, addRelationshipSchema, addTagSchema, blacklistSchema, createCustomerSchema, noteSchema, updateAddressSchema, updateCustomerSchema, updateEmailSchema, updatePhoneSchema
|
|
250
244
|
```
|
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\tICustomerTagDTO,\n} from './dtos/customer';\nexport {\n\ttoAddressDTO,\n\ttoCustomerAddressDTO,\n\ttoCustomerDetailDTO,\n\ttoCustomerDTO,\n\ttoCustomerEmailDTO,\n\ttoCustomerNoteDTO,\n\ttoCustomerPhoneDTO,\n\ttoCustomerTagDTO,\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\tAddressLabel,\n\tCustomerType,\n\tEmailLabel,\n\tIAddress,\n\tICustomer,\n\tICustomerAddress,\n\tICustomerDetail,\n\tICustomerEmail,\n\tICustomerNote,\n\tICustomerPhone,\n\tICustomerTag,\n\tPhoneLabel,\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 { 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\tICustomerTag,\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 id.\nexport type ICustomerRelationshipExpandedDTO = Omit<ICustomerShallowDTO, 'id'> & {\n\tid: string;\n\tcustomerId: string;\n\trelationship: string;\n\tisPrimary: boolean;\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\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface ICustomerPhoneDTO {\n\tid: string;\n\tphone: string;\n\tlabel: string;\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\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\nexport interface ICustomerTagDTO {\n\ttag: 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\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\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\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\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\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 toCustomerTagDTO(t: ICustomerTag): ICustomerTagDTO {\n\treturn {\n\t\ttag: stringOrEmpty(t.tag),\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\tasync remove(addrId: 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_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\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});\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\nexport class CustomerLabelModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tlist(type: CustomerLabelType): Promise<ICustomerLabel[]> {\n\t\treturn this.store.query<ICustomerLabel>(\n\t\t\t`SELECT id, type, value, created_at AS \"createdAt\"\n\t\t\t FROM fonderie_customer_labels\n\t\t\t WHERE type = $1\n\t\t\t ORDER BY value ASC`,\n\t\t\t[type],\n\t\t);\n\t}\n\n\tremove(id: string): Promise<void> {\n\t\treturn this.store.query(\n\t\t\t`DELETE FROM fonderie_customer_labels WHERE id = $1`,\n\t\t\t[id],\n\t\t).then(() => undefined);\n\t}\n\n\tasync findOrCreate(type: CustomerLabelType, raw: string): Promise<ICustomerLabel> {\n\t\tconst value = raw.trim().toLowerCase();\n\t\tconst [row] = await this.store.query<ICustomerLabel>(\n\t\t\t`INSERT INTO fonderie_customer_labels (type, value)\n\t\t\t VALUES ($1, $2)\n\t\t\t ON CONFLICT (type, value) DO UPDATE SET value = EXCLUDED.value\n\t\t\t RETURNING id, type, value, created_at AS \"createdAt\"`,\n\t\t\t[type, value],\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\treferralCode: z.string().max(100).nullable().optional(),\n\treferredByCode: z.string().max(100).nullable().optional(),\n};\n\nexport const createCustomerSchema = z.object(customerFields);\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 });\nexport const updateEmailSchema = z\n\t.object({ email: email.optional(), label, isPrimary })\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\n\nexport const addPhoneSchema = z.object({ phone, label, isPrimary });\nexport const updatePhoneSchema = z\n\t.object({ phone: phone.optional(), label, isPrimary })\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\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\n\t.object(addressFields)\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\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\trelationship: z.string().max(100).optional(),\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);\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());\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\tawait addresses.remove(addrId, r.customer.id);\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);\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());\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);\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());\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 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\n\t\t\tconst rows = await labels.list(type);\n\t\t\treturn setApiResponse(HTTP.OK, 'LABELS_FETCHED', 'Labels retrieved successfully.', { labels: rows });\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\n\t\t\tawait labels.remove(labelId);\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\tICustomerTagDTO,\n} from './dtos/customer';\nexport {\n\ttoAddressDTO,\n\ttoCustomerAddressDTO,\n\ttoCustomerDetailDTO,\n\ttoCustomerDTO,\n\ttoCustomerEmailDTO,\n\ttoCustomerNoteDTO,\n\ttoCustomerPhoneDTO,\n\ttoCustomerTagDTO,\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\tICustomerTag,\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 { 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\tICustomerTag,\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 id.\nexport type ICustomerRelationshipExpandedDTO = Omit<ICustomerShallowDTO, 'id'> & {\n\tid: string;\n\tcustomerId: string;\n\trelationship: string;\n\tisPrimary: boolean;\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\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface ICustomerPhoneDTO {\n\tid: string;\n\tphone: string;\n\tlabel: string;\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\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\nexport interface ICustomerTagDTO {\n\ttag: 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\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\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\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\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\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 toCustomerTagDTO(t: ICustomerTag): ICustomerTagDTO {\n\treturn {\n\t\ttag: stringOrEmpty(t.tag),\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\tasync remove(addrId: 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_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\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});\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\nexport class CustomerLabelModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tlist(type: CustomerLabelType): Promise<ICustomerLabel[]> {\n\t\treturn this.store.query<ICustomerLabel>(\n\t\t\t`SELECT id, type, value, created_at AS \"createdAt\"\n\t\t\t FROM fonderie_customer_labels\n\t\t\t WHERE type = $1\n\t\t\t ORDER BY value ASC`,\n\t\t\t[type],\n\t\t);\n\t}\n\n\tremove(id: string): Promise<void> {\n\t\treturn this.store.query(\n\t\t\t`DELETE FROM fonderie_customer_labels WHERE id = $1`,\n\t\t\t[id],\n\t\t).then(() => undefined);\n\t}\n\n\tasync findOrCreate(type: CustomerLabelType, raw: string): Promise<ICustomerLabel> {\n\t\tconst value = raw.trim().toLowerCase();\n\t\tconst [row] = await this.store.query<ICustomerLabel>(\n\t\t\t`INSERT INTO fonderie_customer_labels (type, value)\n\t\t\t VALUES ($1, $2)\n\t\t\t ON CONFLICT (type, value) DO UPDATE SET value = EXCLUDED.value\n\t\t\t RETURNING id, type, value, created_at AS \"createdAt\"`,\n\t\t\t[type, value],\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\treferralCode: z.string().max(100).nullable().optional(),\n\treferredByCode: z.string().max(100).nullable().optional(),\n};\n\nexport const createCustomerSchema = z.object(customerFields);\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 });\nexport const updateEmailSchema = z\n\t.object({ email: email.optional(), label, isPrimary })\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\n\nexport const addPhoneSchema = z.object({ phone, label, isPrimary });\nexport const updatePhoneSchema = z\n\t.object({ phone: phone.optional(), label, isPrimary })\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\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\n\t.object(addressFields)\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\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\trelationship: z.string().max(100).optional(),\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);\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());\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\tawait addresses.remove(addrId, r.customer.id);\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);\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());\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);\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());\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 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\n\t\t\tconst rows = await labels.list(type);\n\t\t\treturn setApiResponse(HTTP.OK, 'LABELS_FETCHED', 'Labels retrieved successfully.', { labels: rows });\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\n\t\t\tawait labels.remove(labelId);\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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CustomerSex, IAddress, ICustomerAddress, ICustomer, ICustomerDetail, ICustomerEmail, ICustomerNote, ICustomerPhone, ICustomerTag, ICustomerDetailD2 } from './types.cjs';
|
|
2
|
-
export {
|
|
2
|
+
export { CustomerType } from './types.cjs';
|
|
3
3
|
import { IStoreAdapter } from '@fonderie/store';
|
|
4
4
|
import { IFonderieModule, IFonderieApp } from '@fonderie/core';
|
|
5
5
|
import { EventBus } from '@fonderie/events';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CustomerSex, IAddress, ICustomerAddress, ICustomer, ICustomerDetail, ICustomerEmail, ICustomerNote, ICustomerPhone, ICustomerTag, ICustomerDetailD2 } from './types.js';
|
|
2
|
-
export {
|
|
2
|
+
export { CustomerType } from './types.js';
|
|
3
3
|
import { IStoreAdapter } from '@fonderie/store';
|
|
4
4
|
import { IFonderieModule, IFonderieApp } from '@fonderie/core';
|
|
5
5
|
import { EventBus } from '@fonderie/events';
|
package/dist/types.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["export type CustomerType = 'individual' | 'business';\nexport type CustomerSex = 'UNKNOWN' | 'MALE' | 'FEMALE';\nexport type CustomerLabelType = 'email' | 'phone' | 'address';\n\
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["export type CustomerType = 'individual' | 'business';\nexport type CustomerSex = 'UNKNOWN' | 'MALE' | 'FEMALE';\nexport type CustomerLabelType = 'email' | 'phone' | 'address';\n\nexport interface ICustomerLabel {\n\tid: string;\n\ttype: CustomerLabelType;\n\tvalue: string;\n\tcreatedAt: string;\n}\n\nexport interface ICustomer {\n\tid: string;\n\tworkspaceId: string;\n\ttype: CustomerType;\n\tsex: CustomerSex;\n\tfirstName: string | null;\n\tlastName: string | null;\n\tcompanyName: string | null;\n\tavatarUrl: string | null;\n\tlocale: string;\n\treferenceCode: string | null;\n\treferralCode: string | null;\n\treferredBy: string | null;\n\tisBlacklisted: boolean;\n\tblacklistReason: string | null;\n\tcreatedBy: string | null;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface ICustomerEmail {\n\tid: string;\n\tcustomerId: string;\n\temail: string;\n\tlabelId: string;\n\tlabel: string;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface ICustomerPhone {\n\tid: string;\n\tcustomerId: string;\n\tphone: string;\n\tlabelId: string;\n\tlabel: string;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface IAddress {\n\tid: string;\n\tcountryIso: string;\n\tsubdivision1Iso: string | null;\n\tsubdivision2Iso: string | null;\n\tzipPostalCode: string;\n\tunit: string | null;\n\tline1: string | null;\n\tline2: string | null;\n}\n\nexport interface ICustomerAddress {\n\taddrId: string;\n\tcustomerId: string;\n\tlabelId: string;\n\tlabel: string;\n\tisPrimary: boolean;\n\taddress: IAddress;\n}\n\nexport interface ICustomerNote {\n\tid: string;\n\tcustomerId: string;\n\tauthorId: string | null;\n\tbody: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface ICustomerTag {\n\tcustomerId: string;\n\ttag: string;\n}\n\nexport interface ICustomerRelationship {\n\tid: string;\n\tworkspaceId: string;\n\tcustomerId: string;\n\trelatedId: string;\n\trelationship: string;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface ICustomerShallow extends ICustomer {\n\temails: ICustomerEmail[];\n\tphones: ICustomerPhone[];\n\taddresses: ICustomerAddress[];\n\tnotes: ICustomerNote[];\n\ttags: string[];\n}\n\nexport interface ICustomerRelationshipExpanded {\n\tid: string;\n\tworkspaceId: string;\n\tcustomerId: string;\n\trelationship: string;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n\tcustomer: ICustomerShallow;\n}\n\nexport interface ICustomerDetail extends ICustomer {\n\temails: ICustomerEmail[];\n\tphones: ICustomerPhone[];\n\taddresses: ICustomerAddress[];\n\tnotes: ICustomerNote[];\n\trelationships: ICustomerRelationshipExpanded[];\n\ttags: string[];\n}\n\n// Depth-2 variants: depth-1 customers have their own relationships resolved.\nexport interface ICustomerShallowD2 extends ICustomerShallow {\n\trelationships: ICustomerRelationshipExpanded[];\n}\n\nexport interface ICustomerRelationshipExpandedD2 extends Omit<ICustomerRelationshipExpanded, 'customer'> {\n\tcustomer: ICustomerShallowD2;\n}\n\nexport interface ICustomerDetailD2 extends Omit<ICustomerDetail, 'relationships'> {\n\trelationships: ICustomerRelationshipExpandedD2[];\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
package/dist/types.d.cts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
type CustomerType = 'individual' | 'business';
|
|
2
2
|
type CustomerSex = 'UNKNOWN' | 'MALE' | 'FEMALE';
|
|
3
3
|
type CustomerLabelType = 'email' | 'phone' | 'address';
|
|
4
|
-
/** @deprecated resolved dynamically via fonderie_customer_labels */
|
|
5
|
-
type EmailLabel = 'work' | 'personal' | 'billing';
|
|
6
|
-
/** @deprecated resolved dynamically via fonderie_customer_labels */
|
|
7
|
-
type PhoneLabel = 'mobile' | 'office' | 'home' | 'fax';
|
|
8
|
-
/** @deprecated resolved dynamically via fonderie_customer_labels */
|
|
9
|
-
type AddressLabel = 'service' | 'billing' | 'other';
|
|
10
4
|
interface ICustomerLabel {
|
|
11
5
|
id: string;
|
|
12
6
|
type: CustomerLabelType;
|
|
@@ -123,4 +117,4 @@ interface ICustomerDetailD2 extends Omit<ICustomerDetail, 'relationships'> {
|
|
|
123
117
|
relationships: ICustomerRelationshipExpandedD2[];
|
|
124
118
|
}
|
|
125
119
|
|
|
126
|
-
export type {
|
|
120
|
+
export type { CustomerLabelType, CustomerSex, CustomerType, IAddress, ICustomer, ICustomerAddress, ICustomerDetail, ICustomerDetailD2, ICustomerEmail, ICustomerLabel, ICustomerNote, ICustomerPhone, ICustomerRelationship, ICustomerRelationshipExpanded, ICustomerRelationshipExpandedD2, ICustomerShallow, ICustomerShallowD2, ICustomerTag };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
type CustomerType = 'individual' | 'business';
|
|
2
2
|
type CustomerSex = 'UNKNOWN' | 'MALE' | 'FEMALE';
|
|
3
3
|
type CustomerLabelType = 'email' | 'phone' | 'address';
|
|
4
|
-
/** @deprecated resolved dynamically via fonderie_customer_labels */
|
|
5
|
-
type EmailLabel = 'work' | 'personal' | 'billing';
|
|
6
|
-
/** @deprecated resolved dynamically via fonderie_customer_labels */
|
|
7
|
-
type PhoneLabel = 'mobile' | 'office' | 'home' | 'fax';
|
|
8
|
-
/** @deprecated resolved dynamically via fonderie_customer_labels */
|
|
9
|
-
type AddressLabel = 'service' | 'billing' | 'other';
|
|
10
4
|
interface ICustomerLabel {
|
|
11
5
|
id: string;
|
|
12
6
|
type: CustomerLabelType;
|
|
@@ -123,4 +117,4 @@ interface ICustomerDetailD2 extends Omit<ICustomerDetail, 'relationships'> {
|
|
|
123
117
|
relationships: ICustomerRelationshipExpandedD2[];
|
|
124
118
|
}
|
|
125
119
|
|
|
126
|
-
export type {
|
|
120
|
+
export type { CustomerLabelType, CustomerSex, CustomerType, IAddress, ICustomer, ICustomerAddress, ICustomerDetail, ICustomerDetailD2, ICustomerEmail, ICustomerLabel, ICustomerNote, ICustomerPhone, ICustomerRelationship, ICustomerRelationshipExpanded, ICustomerRelationshipExpandedD2, ICustomerShallow, ICustomerShallowD2, ICustomerTag };
|
package/package.json
CHANGED