@htlkg/data 0.0.24 → 0.0.25

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.
@@ -703,9 +703,6 @@ async function updateReservationStatus(client, id, newStatus) {
703
703
  }
704
704
  }
705
705
 
706
- // src/mutations/contacts.ts
707
- import { z } from "zod";
708
-
709
706
  // src/queries/contacts.ts
710
707
  async function getContact(client, id) {
711
708
  try {
@@ -721,56 +718,178 @@ async function getContact(client, id) {
721
718
  }
722
719
  }
723
720
 
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(),
721
+ // src/validation/contact.schemas.ts
722
+ import { z } from "zod";
723
+ var emailSchema = z.string().trim().toLowerCase().email("Invalid email address").max(254, "Email must not exceed 254 characters");
724
+ var optionalEmailSchema = z.string().trim().toLowerCase().email("Invalid email address").max(254, "Email must not exceed 254 characters").optional();
725
+ var PHONE_REGEX = /^[\+]?[(]?[0-9]{1,4}[)]?[-\s\.]?[(]?[0-9]{1,3}[)]?[-\s\.]?[0-9]{1,4}[-\s\.]?[0-9]{1,4}[-\s\.]?[0-9]{0,9}$/;
726
+ var phoneSchema = z.string().trim().min(7, "Phone number must be at least 7 characters").max(20, "Phone number must not exceed 20 characters").regex(PHONE_REGEX, "Invalid phone number format");
727
+ var optionalPhoneSchema = z.string().trim().optional().refine(
728
+ (val) => {
729
+ if (!val || val === "") return true;
730
+ return PHONE_REGEX.test(val) && val.length >= 7 && val.length <= 20;
731
+ },
732
+ { message: "Invalid phone number format" }
733
+ );
734
+ var NAME_REGEX = /^[\p{L}\s\-'\.]+$/u;
735
+ var firstNameSchema = z.string().trim().min(1, "First name is required").max(100, "First name must not exceed 100 characters").regex(NAME_REGEX, "First name contains invalid characters");
736
+ var lastNameSchema = z.string().trim().min(1, "Last name is required").max(100, "Last name must not exceed 100 characters").regex(NAME_REGEX, "Last name contains invalid characters");
737
+ var optionalFirstNameSchema = z.string().trim().min(1, "First name cannot be empty if provided").max(100, "First name must not exceed 100 characters").regex(NAME_REGEX, "First name contains invalid characters").optional();
738
+ var optionalLastNameSchema = z.string().trim().min(1, "Last name cannot be empty if provided").max(100, "Last name must not exceed 100 characters").regex(NAME_REGEX, "Last name contains invalid characters").optional();
739
+ var LOCALE_REGEX = /^[a-z]{2,3}(-[A-Z][a-z]{3})?(-([A-Z]{2}|[0-9]{3}))?$/;
740
+ var localeSchema = z.string().regex(LOCALE_REGEX, "Invalid locale format. Use BCP 47 format (e.g., 'en', 'en-US', 'pt-BR')");
741
+ var optionalLocaleSchema = z.string().regex(LOCALE_REGEX, "Invalid locale format. Use BCP 47 format (e.g., 'en', 'en-US', 'pt-BR')").optional();
742
+ var ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{1,3})?(Z|[+-]\d{2}:\d{2})?)?$/;
743
+ var isoDateStringSchema = z.string().refine(
744
+ (val) => {
745
+ if (!ISO_DATE_REGEX.test(val)) return false;
746
+ const date = new Date(val);
747
+ return !isNaN(date.getTime());
748
+ },
749
+ { message: "Invalid date format. Use ISO 8601 format (e.g., '2024-01-15T10:30:00Z')" }
750
+ );
751
+ var optionalIsoDateStringSchema = z.string().refine(
752
+ (val) => {
753
+ if (!ISO_DATE_REGEX.test(val)) return false;
754
+ const date = new Date(val);
755
+ return !isNaN(date.getTime());
756
+ },
757
+ { message: "Invalid date format. Use ISO 8601 format (e.g., '2024-01-15T10:30:00Z')" }
758
+ ).optional();
759
+ var uuidSchema = z.string().uuid("Invalid UUID format").or(z.string().min(1, "ID is required"));
760
+ var brandIdSchema = z.string().min(1, "Brand ID is required");
761
+ var tagSchema = z.string().trim().toLowerCase().min(1, "Tag cannot be empty").max(50, "Tag must not exceed 50 characters");
762
+ var tagsArraySchema = z.array(tagSchema).max(100, "Cannot have more than 100 tags").optional();
763
+ var customFieldDefinitionSchema = z.object({
764
+ type: z.enum(["string", "number", "boolean", "date", "array", "object"]),
765
+ required: z.boolean().optional().default(false),
766
+ maxLength: z.number().positive().optional(),
767
+ minLength: z.number().nonnegative().optional(),
768
+ min: z.number().optional(),
769
+ max: z.number().optional(),
770
+ pattern: z.string().optional(),
771
+ enum: z.array(z.union([z.string(), z.number()])).optional()
772
+ });
773
+ var contactPreferencesSchema = z.record(
774
+ z.string(),
775
+ z.union([
776
+ z.string(),
777
+ z.number(),
778
+ z.boolean(),
779
+ z.array(z.any()),
780
+ z.record(z.any()),
781
+ z.null()
782
+ ])
783
+ ).optional().refine(
784
+ (preferences) => {
785
+ if (!preferences) return true;
786
+ if (preferences.theme && !["light", "dark", "system"].includes(preferences.theme)) {
787
+ return false;
788
+ }
789
+ if (preferences.language && typeof preferences.language === "string") {
790
+ const localeResult = localeSchema.safeParse(preferences.language);
791
+ if (!localeResult.success) return false;
792
+ }
793
+ return true;
794
+ },
795
+ {
796
+ message: "Invalid preferences structure. Check theme (light/dark/system) and language (BCP 47) values."
797
+ }
798
+ );
799
+ var notificationPreferencesSchema = z.object({
800
+ email: z.boolean().optional().default(true),
801
+ sms: z.boolean().optional().default(false),
802
+ push: z.boolean().optional().default(false)
803
+ });
804
+ var communicationPreferencesSchema = z.object({
805
+ preferredChannel: z.enum(["email", "phone", "sms"]).optional().default("email")
806
+ });
807
+ var baseContactFieldsSchema = {
808
+ brandId: brandIdSchema,
809
+ email: emailSchema,
810
+ phone: optionalPhoneSchema,
811
+ firstName: firstNameSchema,
812
+ lastName: lastNameSchema,
813
+ locale: optionalLocaleSchema,
814
+ gdprConsent: z.boolean({
815
+ required_error: "GDPR consent is required",
816
+ invalid_type_error: "GDPR consent must be a boolean"
817
+ }),
818
+ gdprConsentDate: optionalIsoDateStringSchema,
734
819
  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(),
820
+ preferences: contactPreferencesSchema,
821
+ tags: tagsArraySchema,
822
+ totalVisits: z.number().int().min(0, "Total visits cannot be negative").optional(),
823
+ lastVisitDate: optionalIsoDateStringSchema,
824
+ firstVisitDate: optionalIsoDateStringSchema,
825
+ legacyId: z.string().max(255, "Legacy ID must not exceed 255 characters").optional()
826
+ };
827
+ var createAuditFieldsSchema = {
828
+ createdAt: optionalIsoDateStringSchema,
743
829
  createdBy: z.string().optional(),
744
- updatedAt: z.string().optional(),
830
+ updatedAt: optionalIsoDateStringSchema,
745
831
  updatedBy: z.string().optional()
832
+ };
833
+ var updateAuditFieldsSchema = {
834
+ updatedAt: optionalIsoDateStringSchema,
835
+ updatedBy: z.string().optional(),
836
+ deletedAt: z.string().nullable().optional(),
837
+ deletedBy: z.string().nullable().optional()
838
+ };
839
+ var createContactSchema = z.object({
840
+ ...baseContactFieldsSchema,
841
+ ...createAuditFieldsSchema
746
842
  });
747
843
  var updateContactSchema = z.object({
748
844
  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(),
845
+ brandId: brandIdSchema.optional(),
846
+ email: optionalEmailSchema,
847
+ phone: optionalPhoneSchema,
848
+ firstName: optionalFirstNameSchema,
849
+ lastName: optionalLastNameSchema,
850
+ locale: optionalLocaleSchema,
755
851
  gdprConsent: z.boolean().optional(),
756
- gdprConsentDate: z.string().optional(),
852
+ gdprConsentDate: optionalIsoDateStringSchema,
757
853
  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()
854
+ preferences: contactPreferencesSchema,
855
+ tags: tagsArraySchema,
856
+ totalVisits: z.number().int().min(0, "Total visits cannot be negative").optional(),
857
+ lastVisitDate: optionalIsoDateStringSchema,
858
+ firstVisitDate: optionalIsoDateStringSchema,
859
+ legacyId: z.string().max(255, "Legacy ID must not exceed 255 characters").optional(),
860
+ ...updateAuditFieldsSchema
769
861
  });
770
862
  var mergeContactsSchema = z.object({
771
863
  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")
864
+ duplicateIds: z.array(z.string().min(1, "Duplicate ID cannot be empty")).min(1, "At least one duplicate ID is required").max(50, "Cannot merge more than 50 contacts at once")
773
865
  });
866
+ var searchContactSchema = z.object({
867
+ brandId: brandIdSchema.optional(),
868
+ email: z.string().optional(),
869
+ phone: z.string().optional(),
870
+ firstName: z.string().optional(),
871
+ lastName: z.string().optional(),
872
+ search: z.string().max(255, "Search query must not exceed 255 characters").optional(),
873
+ tags: z.array(z.string()).optional(),
874
+ gdprConsent: z.boolean().optional(),
875
+ marketingOptIn: z.boolean().optional(),
876
+ includeDeleted: z.boolean().optional().default(false),
877
+ limit: z.number().int().min(1).max(250).optional().default(25),
878
+ nextToken: z.string().optional()
879
+ });
880
+ var bulkImportContactSchema = z.object({
881
+ contacts: z.array(createContactSchema.omit({ createdAt: true, createdBy: true, updatedAt: true, updatedBy: true })).min(1, "At least one contact is required").max(1e3, "Cannot import more than 1000 contacts at once"),
882
+ skipDuplicates: z.boolean().optional().default(true),
883
+ updateExisting: z.boolean().optional().default(false)
884
+ });
885
+ function formatValidationErrors(error) {
886
+ return error.issues.map((issue) => {
887
+ const path = issue.path.join(".");
888
+ return path ? `${path}: ${issue.message}` : issue.message;
889
+ }).join("; ");
890
+ }
891
+
892
+ // src/mutations/contacts.ts
774
893
  var ContactValidationError = class extends Error {
775
894
  issues;
776
895
  constructor(message, issues = []) {
@@ -783,9 +902,8 @@ async function createContact(client, input) {
783
902
  try {
784
903
  const validationResult = createContactSchema.safeParse(input);
785
904
  if (!validationResult.success) {
786
- const errorMessage = validationResult.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
787
905
  throw new ContactValidationError(
788
- `Validation failed: ${errorMessage}`,
906
+ `Validation failed: ${formatValidationErrors(validationResult.error)}`,
789
907
  validationResult.error.issues
790
908
  );
791
909
  }
@@ -808,9 +926,8 @@ async function updateContact(client, input) {
808
926
  try {
809
927
  const validationResult = updateContactSchema.safeParse(input);
810
928
  if (!validationResult.success) {
811
- const errorMessage = validationResult.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
812
929
  throw new ContactValidationError(
813
- `Validation failed: ${errorMessage}`,
930
+ `Validation failed: ${formatValidationErrors(validationResult.error)}`,
814
931
  validationResult.error.issues
815
932
  );
816
933
  }
@@ -894,9 +1011,8 @@ async function mergeContacts(client, input, mergedBy) {
894
1011
  try {
895
1012
  const validationResult = mergeContactsSchema.safeParse(input);
896
1013
  if (!validationResult.success) {
897
- const errorMessage = validationResult.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
898
1014
  throw new ContactValidationError(
899
- `Validation failed: ${errorMessage}`,
1015
+ `Validation failed: ${formatValidationErrors(validationResult.error)}`,
900
1016
  validationResult.error.issues
901
1017
  );
902
1018
  }