@htlkg/data 0.0.20 → 0.0.21

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.
@@ -1,8 +1,9 @@
1
1
  import { C as CreateAuditFields, a as UpdateWithSoftDeleteFields } from '../common-DSxswsZ3.js';
2
2
  export { S as SoftDeleteFields, U as UpdateAuditFields } from '../common-DSxswsZ3.js';
3
- import { Brand, Account, User, SystemSettings } from '@htlkg/core/types';
3
+ import { Brand, Account, User, SystemSettings, Contact } from '@htlkg/core/types';
4
4
  export { C as CreateProductInstanceInput, U as UpdateProductInstanceInput, c as createProductInstance, d as deleteProductInstance, t as toggleProductInstanceEnabled, u as updateProductInstance } from '../productInstances-BpQv1oLS.js';
5
5
  export { C as CreateReservationInput, b as ReservationStatus, R as ReservationValidationError, U as UpdateReservationInput, c as createReservation, d as deleteReservation, r as restoreReservation, s as softDeleteReservation, u as updateReservation, a as updateReservationStatus } from '../reservations-CdDfkcZ_.js';
6
+ import { z } from 'zod';
6
7
 
7
8
  /**
8
9
  * Brand Mutation Functions
@@ -377,4 +378,339 @@ declare function updateSystemSettings<TClient = any>(client: TClient, input: Upd
377
378
  */
378
379
  declare function initializeSystemSettings<TClient = any>(client: TClient, initializedBy: string): Promise<SystemSettings | null>;
379
380
 
380
- export { type CreateAccountInput, CreateAuditFields, type CreateBrandInput, type CreateUserInput, type UpdateAccountInput, type UpdateBrandInput, type UpdateSystemSettingsInput, type UpdateUserInput, UpdateWithSoftDeleteFields, createAccount, createBrand, createUser, deleteAccount, deleteBrand, deleteUser, initializeSystemSettings, restoreAccount, restoreBrand, restoreUser, softDeleteAccount, softDeleteBrand, softDeleteUser, updateAccount, updateBrand, updateSystemSettings, updateUser };
381
+ /**
382
+ * Contact Mutation Functions
383
+ *
384
+ * Provides type-safe mutation functions for creating, updating, and deleting contacts.
385
+ * Includes Zod validation for input data before mutations.
386
+ */
387
+
388
+ /**
389
+ * Zod schema for creating a contact
390
+ */
391
+ declare const createContactSchema: z.ZodObject<{
392
+ brandId: z.ZodString;
393
+ email: z.ZodString;
394
+ phone: z.ZodOptional<z.ZodString>;
395
+ firstName: z.ZodString;
396
+ lastName: z.ZodString;
397
+ locale: z.ZodOptional<z.ZodString>;
398
+ gdprConsent: z.ZodBoolean;
399
+ gdprConsentDate: z.ZodOptional<z.ZodString>;
400
+ marketingOptIn: z.ZodOptional<z.ZodBoolean>;
401
+ preferences: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
402
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
403
+ totalVisits: z.ZodOptional<z.ZodNumber>;
404
+ lastVisitDate: z.ZodOptional<z.ZodString>;
405
+ firstVisitDate: z.ZodOptional<z.ZodString>;
406
+ legacyId: z.ZodOptional<z.ZodString>;
407
+ createdAt: z.ZodOptional<z.ZodString>;
408
+ createdBy: z.ZodOptional<z.ZodString>;
409
+ updatedAt: z.ZodOptional<z.ZodString>;
410
+ updatedBy: z.ZodOptional<z.ZodString>;
411
+ }, "strip", z.ZodTypeAny, {
412
+ brandId: string;
413
+ email: string;
414
+ lastName: string;
415
+ firstName: string;
416
+ gdprConsent: boolean;
417
+ updatedAt?: string | undefined;
418
+ updatedBy?: string | undefined;
419
+ phone?: string | undefined;
420
+ locale?: string | undefined;
421
+ gdprConsentDate?: string | undefined;
422
+ marketingOptIn?: boolean | undefined;
423
+ preferences?: Record<string, any> | undefined;
424
+ tags?: string[] | undefined;
425
+ totalVisits?: number | undefined;
426
+ lastVisitDate?: string | undefined;
427
+ firstVisitDate?: string | undefined;
428
+ legacyId?: string | undefined;
429
+ createdAt?: string | undefined;
430
+ createdBy?: string | undefined;
431
+ }, {
432
+ brandId: string;
433
+ email: string;
434
+ lastName: string;
435
+ firstName: string;
436
+ gdprConsent: boolean;
437
+ updatedAt?: string | undefined;
438
+ updatedBy?: string | undefined;
439
+ phone?: string | undefined;
440
+ locale?: string | undefined;
441
+ gdprConsentDate?: string | undefined;
442
+ marketingOptIn?: boolean | undefined;
443
+ preferences?: Record<string, any> | undefined;
444
+ tags?: string[] | undefined;
445
+ totalVisits?: number | undefined;
446
+ lastVisitDate?: string | undefined;
447
+ firstVisitDate?: string | undefined;
448
+ legacyId?: string | undefined;
449
+ createdAt?: string | undefined;
450
+ createdBy?: string | undefined;
451
+ }>;
452
+ /**
453
+ * Zod schema for updating a contact
454
+ */
455
+ declare const updateContactSchema: z.ZodObject<{
456
+ id: z.ZodString;
457
+ brandId: z.ZodOptional<z.ZodString>;
458
+ email: z.ZodOptional<z.ZodString>;
459
+ phone: z.ZodOptional<z.ZodString>;
460
+ firstName: z.ZodOptional<z.ZodString>;
461
+ lastName: z.ZodOptional<z.ZodString>;
462
+ locale: z.ZodOptional<z.ZodString>;
463
+ gdprConsent: z.ZodOptional<z.ZodBoolean>;
464
+ gdprConsentDate: z.ZodOptional<z.ZodString>;
465
+ marketingOptIn: z.ZodOptional<z.ZodBoolean>;
466
+ preferences: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
467
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
468
+ totalVisits: z.ZodOptional<z.ZodNumber>;
469
+ lastVisitDate: z.ZodOptional<z.ZodString>;
470
+ firstVisitDate: z.ZodOptional<z.ZodString>;
471
+ legacyId: z.ZodOptional<z.ZodString>;
472
+ updatedAt: z.ZodOptional<z.ZodString>;
473
+ updatedBy: z.ZodOptional<z.ZodString>;
474
+ deletedAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
475
+ deletedBy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
476
+ }, "strip", z.ZodTypeAny, {
477
+ id: string;
478
+ updatedAt?: string | undefined;
479
+ updatedBy?: string | undefined;
480
+ brandId?: string | undefined;
481
+ deletedAt?: string | null | undefined;
482
+ deletedBy?: string | null | undefined;
483
+ email?: string | undefined;
484
+ phone?: string | undefined;
485
+ lastName?: string | undefined;
486
+ firstName?: string | undefined;
487
+ locale?: string | undefined;
488
+ gdprConsent?: boolean | undefined;
489
+ gdprConsentDate?: string | undefined;
490
+ marketingOptIn?: boolean | undefined;
491
+ preferences?: Record<string, any> | undefined;
492
+ tags?: string[] | undefined;
493
+ totalVisits?: number | undefined;
494
+ lastVisitDate?: string | undefined;
495
+ firstVisitDate?: string | undefined;
496
+ legacyId?: string | undefined;
497
+ }, {
498
+ id: string;
499
+ updatedAt?: string | undefined;
500
+ updatedBy?: string | undefined;
501
+ brandId?: string | undefined;
502
+ deletedAt?: string | null | undefined;
503
+ deletedBy?: string | null | undefined;
504
+ email?: string | undefined;
505
+ phone?: string | undefined;
506
+ lastName?: string | undefined;
507
+ firstName?: string | undefined;
508
+ locale?: string | undefined;
509
+ gdprConsent?: boolean | undefined;
510
+ gdprConsentDate?: string | undefined;
511
+ marketingOptIn?: boolean | undefined;
512
+ preferences?: Record<string, any> | undefined;
513
+ tags?: string[] | undefined;
514
+ totalVisits?: number | undefined;
515
+ lastVisitDate?: string | undefined;
516
+ firstVisitDate?: string | undefined;
517
+ legacyId?: string | undefined;
518
+ }>;
519
+ /**
520
+ * Zod schema for merging contacts
521
+ */
522
+ declare const mergeContactsSchema: z.ZodObject<{
523
+ primaryId: z.ZodString;
524
+ duplicateIds: z.ZodArray<z.ZodString, "many">;
525
+ }, "strip", z.ZodTypeAny, {
526
+ primaryId: string;
527
+ duplicateIds: string[];
528
+ }, {
529
+ primaryId: string;
530
+ duplicateIds: string[];
531
+ }>;
532
+ /**
533
+ * Validation error class for contact operations
534
+ */
535
+ declare class ContactValidationError extends Error {
536
+ readonly issues: z.ZodIssue[];
537
+ constructor(message: string, issues?: z.ZodIssue[]);
538
+ }
539
+ /**
540
+ * Input type for creating a contact
541
+ */
542
+ interface CreateContactInput extends CreateAuditFields {
543
+ brandId: string;
544
+ email: string;
545
+ phone?: string;
546
+ firstName: string;
547
+ lastName: string;
548
+ locale?: string;
549
+ gdprConsent: boolean;
550
+ gdprConsentDate?: string;
551
+ marketingOptIn?: boolean;
552
+ preferences?: Record<string, any>;
553
+ tags?: string[];
554
+ totalVisits?: number;
555
+ lastVisitDate?: string;
556
+ firstVisitDate?: string;
557
+ legacyId?: string;
558
+ }
559
+ /**
560
+ * Input type for updating a contact
561
+ */
562
+ interface UpdateContactInput extends UpdateWithSoftDeleteFields {
563
+ id: string;
564
+ brandId?: string;
565
+ email?: string;
566
+ phone?: string;
567
+ firstName?: string;
568
+ lastName?: string;
569
+ locale?: string;
570
+ gdprConsent?: boolean;
571
+ gdprConsentDate?: string;
572
+ marketingOptIn?: boolean;
573
+ preferences?: Record<string, any>;
574
+ tags?: string[];
575
+ totalVisits?: number;
576
+ lastVisitDate?: string;
577
+ firstVisitDate?: string;
578
+ legacyId?: string;
579
+ }
580
+ /**
581
+ * Input type for merging contacts
582
+ */
583
+ interface MergeContactsInput {
584
+ primaryId: string;
585
+ duplicateIds: string[];
586
+ }
587
+ /**
588
+ * Result type for merge operation
589
+ */
590
+ interface MergeContactsResult {
591
+ success: boolean;
592
+ mergedContact?: Contact;
593
+ deletedIds?: string[];
594
+ error?: string;
595
+ }
596
+ /**
597
+ * Create a new contact with Zod validation
598
+ *
599
+ * @throws {ContactValidationError} if input validation fails
600
+ *
601
+ * @example
602
+ * ```typescript
603
+ * import { createContact } from '@htlkg/data/mutations';
604
+ * import { generateClient } from '@htlkg/data/client';
605
+ *
606
+ * const client = generateClient<Schema>();
607
+ * const contact = await createContact(client, {
608
+ * brandId: 'brand-123',
609
+ * email: 'guest@example.com',
610
+ * firstName: 'John',
611
+ * lastName: 'Doe',
612
+ * gdprConsent: true,
613
+ * gdprConsentDate: new Date().toISOString()
614
+ * });
615
+ * ```
616
+ */
617
+ declare function createContact<TClient = any>(client: TClient, input: CreateContactInput): Promise<Contact | null>;
618
+ /**
619
+ * Update an existing contact with Zod validation
620
+ *
621
+ * @throws {ContactValidationError} if input validation fails
622
+ *
623
+ * @example
624
+ * ```typescript
625
+ * import { updateContact } from '@htlkg/data/mutations';
626
+ * import { generateClient } from '@htlkg/data/client';
627
+ *
628
+ * const client = generateClient<Schema>();
629
+ * const contact = await updateContact(client, {
630
+ * id: 'contact-123',
631
+ * firstName: 'Jane',
632
+ * marketingOptIn: true
633
+ * });
634
+ * ```
635
+ */
636
+ declare function updateContact<TClient = any>(client: TClient, input: UpdateContactInput): Promise<Contact | null>;
637
+ /**
638
+ * Soft delete a contact (sets deletedAt/deletedBy instead of removing)
639
+ *
640
+ * @example
641
+ * ```typescript
642
+ * import { softDeleteContact } from '@htlkg/data/mutations';
643
+ * import { generateClient } from '@htlkg/data/client';
644
+ *
645
+ * const client = generateClient<Schema>();
646
+ * await softDeleteContact(client, 'contact-123', 'admin@example.com');
647
+ * ```
648
+ */
649
+ declare function softDeleteContact<TClient = any>(client: TClient, id: string, deletedBy: string): Promise<boolean>;
650
+ /**
651
+ * Restore a soft-deleted contact
652
+ * Checks retention period before allowing restoration
653
+ *
654
+ * @example
655
+ * ```typescript
656
+ * import { restoreContact } from '@htlkg/data/mutations';
657
+ * import { generateClient } from '@htlkg/data/client';
658
+ *
659
+ * const client = generateClient<Schema>();
660
+ * await restoreContact(client, 'contact-123');
661
+ * // Or with custom retention days:
662
+ * await restoreContact(client, 'contact-123', 60);
663
+ * ```
664
+ */
665
+ declare function restoreContact<TClient = any>(client: TClient, id: string, retentionDays?: number): Promise<{
666
+ success: boolean;
667
+ error?: string;
668
+ }>;
669
+ /**
670
+ * Hard delete a contact (permanently removes from database)
671
+ * Use with caution - prefer softDeleteContact for recoverable deletion
672
+ *
673
+ * @example
674
+ * ```typescript
675
+ * import { deleteContact } from '@htlkg/data/mutations';
676
+ * import { generateClient } from '@htlkg/data/client';
677
+ *
678
+ * const client = generateClient<Schema>();
679
+ * await deleteContact(client, 'contact-123');
680
+ * ```
681
+ */
682
+ declare function deleteContact<TClient = any>(client: TClient, id: string): Promise<boolean>;
683
+ /**
684
+ * Merge duplicate contacts into a primary contact
685
+ *
686
+ * This function:
687
+ * 1. Validates all contact IDs exist
688
+ * 2. Aggregates data from duplicates into the primary contact
689
+ * 3. Soft deletes the duplicate contacts
690
+ * 4. Returns the updated primary contact
691
+ *
692
+ * Merge strategy:
693
+ * - totalVisits: Sum of all contacts
694
+ * - firstVisitDate: Earliest date across all contacts
695
+ * - lastVisitDate: Latest date across all contacts
696
+ * - tags: Merged unique tags from all contacts
697
+ * - preferences: Primary contact preferences take precedence
698
+ * - Other fields: Primary contact values are preserved
699
+ *
700
+ * @throws {ContactValidationError} if input validation fails
701
+ *
702
+ * @example
703
+ * ```typescript
704
+ * import { mergeContacts } from '@htlkg/data/mutations';
705
+ * import { generateClient } from '@htlkg/data/client';
706
+ *
707
+ * const client = generateClient<Schema>();
708
+ * const result = await mergeContacts(client, {
709
+ * primaryId: 'contact-primary',
710
+ * duplicateIds: ['contact-dup-1', 'contact-dup-2']
711
+ * }, 'admin@example.com');
712
+ * ```
713
+ */
714
+ declare function mergeContacts<TClient = any>(client: TClient, input: MergeContactsInput, mergedBy: string): Promise<MergeContactsResult>;
715
+
716
+ export { ContactValidationError, type CreateAccountInput, CreateAuditFields, type CreateBrandInput, type CreateContactInput, type CreateUserInput, type MergeContactsInput, type MergeContactsResult, type UpdateAccountInput, type UpdateBrandInput, type UpdateContactInput, type UpdateSystemSettingsInput, type UpdateUserInput, UpdateWithSoftDeleteFields, createAccount, createBrand, createContact, createContactSchema, createUser, deleteAccount, deleteBrand, deleteContact, deleteUser, initializeSystemSettings, mergeContacts, mergeContactsSchema, restoreAccount, restoreBrand, restoreContact, restoreUser, softDeleteAccount, softDeleteBrand, softDeleteContact, softDeleteUser, updateAccount, updateBrand, updateContact, updateContactSchema, updateSystemSettings, updateUser };
@@ -702,30 +702,316 @@ async function updateReservationStatus(client, id, newStatus) {
702
702
  throw error;
703
703
  }
704
704
  }
705
+
706
+ // src/mutations/contacts.ts
707
+ import { z } from "zod";
708
+
709
+ // src/queries/contacts.ts
710
+ async function getContact(client, id) {
711
+ try {
712
+ const { data, errors } = await client.models.Contact.get({ id });
713
+ if (errors) {
714
+ console.error("[getContact] GraphQL errors:", errors);
715
+ return null;
716
+ }
717
+ return data;
718
+ } catch (error) {
719
+ console.error("[getContact] Error fetching contact:", error);
720
+ throw error;
721
+ }
722
+ }
723
+
724
+ // src/mutations/contacts.ts
725
+ var createContactSchema = z.object({
726
+ brandId: z.string().min(1, "Brand ID is required"),
727
+ email: z.string().email("Invalid email address"),
728
+ phone: z.string().optional(),
729
+ firstName: z.string().min(1, "First name is required"),
730
+ lastName: z.string().min(1, "Last name is required"),
731
+ locale: z.string().optional(),
732
+ gdprConsent: z.boolean(),
733
+ gdprConsentDate: z.string().optional(),
734
+ marketingOptIn: z.boolean().optional(),
735
+ preferences: z.record(z.any()).optional(),
736
+ tags: z.array(z.string()).optional(),
737
+ totalVisits: z.number().int().min(0).optional(),
738
+ lastVisitDate: z.string().optional(),
739
+ firstVisitDate: z.string().optional(),
740
+ legacyId: z.string().optional(),
741
+ // Audit fields
742
+ createdAt: z.string().optional(),
743
+ createdBy: z.string().optional(),
744
+ updatedAt: z.string().optional(),
745
+ updatedBy: z.string().optional()
746
+ });
747
+ var updateContactSchema = z.object({
748
+ id: z.string().min(1, "Contact ID is required"),
749
+ brandId: z.string().min(1).optional(),
750
+ email: z.string().email("Invalid email address").optional(),
751
+ phone: z.string().optional(),
752
+ firstName: z.string().min(1).optional(),
753
+ lastName: z.string().min(1).optional(),
754
+ locale: z.string().optional(),
755
+ gdprConsent: z.boolean().optional(),
756
+ gdprConsentDate: z.string().optional(),
757
+ marketingOptIn: z.boolean().optional(),
758
+ preferences: z.record(z.any()).optional(),
759
+ tags: z.array(z.string()).optional(),
760
+ totalVisits: z.number().int().min(0).optional(),
761
+ lastVisitDate: z.string().optional(),
762
+ firstVisitDate: z.string().optional(),
763
+ legacyId: z.string().optional(),
764
+ // Audit fields
765
+ updatedAt: z.string().optional(),
766
+ updatedBy: z.string().optional(),
767
+ deletedAt: z.string().nullable().optional(),
768
+ deletedBy: z.string().nullable().optional()
769
+ });
770
+ var mergeContactsSchema = z.object({
771
+ primaryId: z.string().min(1, "Primary contact ID is required"),
772
+ duplicateIds: z.array(z.string().min(1)).min(1, "At least one duplicate ID is required")
773
+ });
774
+ var ContactValidationError = class extends Error {
775
+ issues;
776
+ constructor(message, issues = []) {
777
+ super(message);
778
+ this.name = "ContactValidationError";
779
+ this.issues = issues;
780
+ }
781
+ };
782
+ async function createContact(client, input) {
783
+ try {
784
+ const validationResult = createContactSchema.safeParse(input);
785
+ if (!validationResult.success) {
786
+ const errorMessage = validationResult.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
787
+ throw new ContactValidationError(
788
+ `Validation failed: ${errorMessage}`,
789
+ validationResult.error.issues
790
+ );
791
+ }
792
+ const { data, errors } = await client.models.Contact.create(input);
793
+ if (errors) {
794
+ console.error("[createContact] GraphQL errors:", errors);
795
+ return null;
796
+ }
797
+ return data;
798
+ } catch (error) {
799
+ if (error instanceof ContactValidationError) {
800
+ console.error("[createContact] Validation error:", error.message);
801
+ throw error;
802
+ }
803
+ console.error("[createContact] Error creating contact:", error);
804
+ throw error;
805
+ }
806
+ }
807
+ async function updateContact(client, input) {
808
+ try {
809
+ const validationResult = updateContactSchema.safeParse(input);
810
+ if (!validationResult.success) {
811
+ const errorMessage = validationResult.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
812
+ throw new ContactValidationError(
813
+ `Validation failed: ${errorMessage}`,
814
+ validationResult.error.issues
815
+ );
816
+ }
817
+ const { data, errors } = await client.models.Contact.update(input);
818
+ if (errors) {
819
+ console.error("[updateContact] GraphQL errors:", errors);
820
+ return null;
821
+ }
822
+ return data;
823
+ } catch (error) {
824
+ if (error instanceof ContactValidationError) {
825
+ console.error("[updateContact] Validation error:", error.message);
826
+ throw error;
827
+ }
828
+ console.error("[updateContact] Error updating contact:", error);
829
+ throw error;
830
+ }
831
+ }
832
+ async function softDeleteContact(client, id, deletedBy) {
833
+ try {
834
+ const { errors } = await client.models.Contact.update({
835
+ id,
836
+ deletedAt: (/* @__PURE__ */ new Date()).toISOString(),
837
+ deletedBy
838
+ });
839
+ if (errors) {
840
+ console.error("[softDeleteContact] GraphQL errors:", errors);
841
+ return false;
842
+ }
843
+ return true;
844
+ } catch (error) {
845
+ console.error("[softDeleteContact] Error soft-deleting contact:", error);
846
+ throw error;
847
+ }
848
+ }
849
+ async function restoreContact(client, id, retentionDays = DEFAULT_SOFT_DELETE_RETENTION_DAYS) {
850
+ try {
851
+ const contact = await getContact(client, id);
852
+ if (!contact) {
853
+ console.error("[restoreContact] Contact not found");
854
+ return { success: false, error: "Contact not found" };
855
+ }
856
+ const eligibility = checkRestoreEligibility(
857
+ contact.deletedAt,
858
+ retentionDays
859
+ );
860
+ if (!eligibility.canRestore) {
861
+ const errorMsg = `Cannot restore contact. Retention period of ${retentionDays} days has expired. Item was deleted ${eligibility.daysExpired} days ago.`;
862
+ console.error("[restoreContact]", errorMsg);
863
+ return { success: false, error: errorMsg };
864
+ }
865
+ const { errors } = await client.models.Contact.update({
866
+ id,
867
+ deletedAt: null,
868
+ deletedBy: null
869
+ });
870
+ if (errors) {
871
+ console.error("[restoreContact] GraphQL errors:", errors);
872
+ return { success: false, error: "Failed to restore contact" };
873
+ }
874
+ return { success: true };
875
+ } catch (error) {
876
+ console.error("[restoreContact] Error restoring contact:", error);
877
+ throw error;
878
+ }
879
+ }
880
+ async function deleteContact(client, id) {
881
+ try {
882
+ const { errors } = await client.models.Contact.delete({ id });
883
+ if (errors) {
884
+ console.error("[deleteContact] GraphQL errors:", errors);
885
+ return false;
886
+ }
887
+ return true;
888
+ } catch (error) {
889
+ console.error("[deleteContact] Error deleting contact:", error);
890
+ throw error;
891
+ }
892
+ }
893
+ async function mergeContacts(client, input, mergedBy) {
894
+ try {
895
+ const validationResult = mergeContactsSchema.safeParse(input);
896
+ if (!validationResult.success) {
897
+ const errorMessage = validationResult.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
898
+ throw new ContactValidationError(
899
+ `Validation failed: ${errorMessage}`,
900
+ validationResult.error.issues
901
+ );
902
+ }
903
+ const { primaryId, duplicateIds } = input;
904
+ const primaryContact = await getContact(client, primaryId);
905
+ if (!primaryContact) {
906
+ return {
907
+ success: false,
908
+ error: `Primary contact not found: ${primaryId}`
909
+ };
910
+ }
911
+ const duplicateContacts = [];
912
+ for (const duplicateId of duplicateIds) {
913
+ const duplicate = await getContact(client, duplicateId);
914
+ if (!duplicate) {
915
+ return {
916
+ success: false,
917
+ error: `Duplicate contact not found: ${duplicateId}`
918
+ };
919
+ }
920
+ duplicateContacts.push(duplicate);
921
+ }
922
+ let totalVisits = primaryContact.totalVisits || 0;
923
+ let firstVisitDate = primaryContact.firstVisitDate;
924
+ let lastVisitDate = primaryContact.lastVisitDate;
925
+ const allTags = new Set(primaryContact.tags || []);
926
+ for (const duplicate of duplicateContacts) {
927
+ totalVisits += duplicate.totalVisits || 0;
928
+ if (duplicate.firstVisitDate) {
929
+ if (!firstVisitDate || duplicate.firstVisitDate < firstVisitDate) {
930
+ firstVisitDate = duplicate.firstVisitDate;
931
+ }
932
+ }
933
+ if (duplicate.lastVisitDate) {
934
+ if (!lastVisitDate || duplicate.lastVisitDate > lastVisitDate) {
935
+ lastVisitDate = duplicate.lastVisitDate;
936
+ }
937
+ }
938
+ if (duplicate.tags) {
939
+ duplicate.tags.forEach((tag) => allTags.add(tag));
940
+ }
941
+ }
942
+ const updateInput = {
943
+ id: primaryId,
944
+ totalVisits,
945
+ firstVisitDate,
946
+ lastVisitDate,
947
+ tags: Array.from(allTags),
948
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
949
+ updatedBy: mergedBy
950
+ };
951
+ const updatedPrimary = await updateContact(client, updateInput);
952
+ if (!updatedPrimary) {
953
+ return {
954
+ success: false,
955
+ error: "Failed to update primary contact with merged data"
956
+ };
957
+ }
958
+ const deletedIds = [];
959
+ for (const duplicateId of duplicateIds) {
960
+ const deleted = await softDeleteContact(client, duplicateId, mergedBy);
961
+ if (deleted) {
962
+ deletedIds.push(duplicateId);
963
+ } else {
964
+ console.error(`[mergeContacts] Failed to soft-delete duplicate: ${duplicateId}`);
965
+ }
966
+ }
967
+ return {
968
+ success: true,
969
+ mergedContact: updatedPrimary,
970
+ deletedIds
971
+ };
972
+ } catch (error) {
973
+ if (error instanceof ContactValidationError) {
974
+ console.error("[mergeContacts] Validation error:", error.message);
975
+ throw error;
976
+ }
977
+ console.error("[mergeContacts] Error merging contacts:", error);
978
+ throw error;
979
+ }
980
+ }
705
981
  export {
982
+ ContactValidationError,
706
983
  ReservationValidationError,
707
984
  createAccount,
708
985
  createBrand,
986
+ createContact,
987
+ createContactSchema,
709
988
  createProductInstance,
710
989
  createReservation,
711
990
  createUser,
712
991
  deleteAccount,
713
992
  deleteBrand,
993
+ deleteContact,
714
994
  deleteProductInstance,
715
995
  deleteReservation,
716
996
  deleteUser,
717
997
  initializeSystemSettings,
998
+ mergeContacts,
999
+ mergeContactsSchema,
718
1000
  restoreAccount,
719
1001
  restoreBrand,
1002
+ restoreContact,
720
1003
  restoreReservation,
721
1004
  restoreUser,
722
1005
  softDeleteAccount,
723
1006
  softDeleteBrand,
1007
+ softDeleteContact,
724
1008
  softDeleteReservation,
725
1009
  softDeleteUser,
726
1010
  toggleProductInstanceEnabled,
727
1011
  updateAccount,
728
1012
  updateBrand,
1013
+ updateContact,
1014
+ updateContactSchema,
729
1015
  updateProductInstance,
730
1016
  updateReservation,
731
1017
  updateReservationStatus,