@bash-app/bash-common 30.432.0 → 30.434.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.
Files changed (164) hide show
  1. package/dist/__tests__/appleIapProducts.test.js +1 -0
  2. package/dist/__tests__/appleIapProducts.test.js.map +1 -1
  3. package/dist/__tests__/bashSectionTitleForBashType.test.js +2 -0
  4. package/dist/__tests__/bashSectionTitleForBashType.test.js.map +1 -1
  5. package/dist/__tests__/discoveryScope.test.js +9 -1
  6. package/dist/__tests__/discoveryScope.test.js.map +1 -1
  7. package/dist/__tests__/promoCartEngine.test.d.ts +2 -0
  8. package/dist/__tests__/promoCartEngine.test.d.ts.map +1 -0
  9. package/dist/__tests__/promoCartEngine.test.js +174 -0
  10. package/dist/__tests__/promoCartEngine.test.js.map +1 -0
  11. package/dist/__tests__/promoCodeRedemption.test.d.ts +2 -0
  12. package/dist/__tests__/promoCodeRedemption.test.d.ts.map +1 -0
  13. package/dist/__tests__/promoCodeRedemption.test.js +103 -0
  14. package/dist/__tests__/promoCodeRedemption.test.js.map +1 -0
  15. package/dist/__tests__/promoCodeScope.test.d.ts +2 -0
  16. package/dist/__tests__/promoCodeScope.test.d.ts.map +1 -0
  17. package/dist/__tests__/promoCodeScope.test.js +84 -0
  18. package/dist/__tests__/promoCodeScope.test.js.map +1 -0
  19. package/dist/__tests__/statusEnums.test.js +1 -0
  20. package/dist/__tests__/statusEnums.test.js.map +1 -1
  21. package/dist/appleIapProducts.d.ts +12 -2
  22. package/dist/appleIapProducts.d.ts.map +1 -1
  23. package/dist/appleIapProducts.js +13 -0
  24. package/dist/appleIapProducts.js.map +1 -1
  25. package/dist/definitions.d.ts +3 -1
  26. package/dist/definitions.d.ts.map +1 -1
  27. package/dist/definitions.js +4 -2
  28. package/dist/definitions.js.map +1 -1
  29. package/dist/discoveryScope.d.ts +6 -6
  30. package/dist/discoveryScope.d.ts.map +1 -1
  31. package/dist/discoveryScope.js +25 -16
  32. package/dist/discoveryScope.js.map +1 -1
  33. package/dist/extendedSchemas.d.ts +1 -0
  34. package/dist/extendedSchemas.d.ts.map +1 -1
  35. package/dist/extendedSchemas.js.map +1 -1
  36. package/dist/index.d.ts +4 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +4 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/intros/__tests__/introsReleaseGates.test.d.ts +2 -0
  41. package/dist/intros/__tests__/introsReleaseGates.test.d.ts.map +1 -0
  42. package/dist/intros/__tests__/introsReleaseGates.test.js +140 -0
  43. package/dist/intros/__tests__/introsReleaseGates.test.js.map +1 -0
  44. package/dist/intros/__tests__/psychExperiment.test.d.ts +2 -0
  45. package/dist/intros/__tests__/psychExperiment.test.d.ts.map +1 -0
  46. package/dist/intros/__tests__/psychExperiment.test.js +63 -0
  47. package/dist/intros/__tests__/psychExperiment.test.js.map +1 -0
  48. package/dist/intros/__tests__/reasons.test.d.ts +2 -0
  49. package/dist/intros/__tests__/reasons.test.d.ts.map +1 -0
  50. package/dist/intros/__tests__/reasons.test.js +72 -0
  51. package/dist/intros/__tests__/reasons.test.js.map +1 -0
  52. package/dist/intros/__tests__/sharedNotes.test.d.ts +2 -0
  53. package/dist/intros/__tests__/sharedNotes.test.d.ts.map +1 -0
  54. package/dist/intros/__tests__/sharedNotes.test.js +19 -0
  55. package/dist/intros/__tests__/sharedNotes.test.js.map +1 -0
  56. package/dist/intros/__tests__/transitions.test.d.ts +2 -0
  57. package/dist/intros/__tests__/transitions.test.d.ts.map +1 -0
  58. package/dist/intros/__tests__/transitions.test.js +43 -0
  59. package/dist/intros/__tests__/transitions.test.js.map +1 -0
  60. package/dist/intros/__tests__/warmDisclosure.test.d.ts +2 -0
  61. package/dist/intros/__tests__/warmDisclosure.test.d.ts.map +1 -0
  62. package/dist/intros/__tests__/warmDisclosure.test.js +10 -0
  63. package/dist/intros/__tests__/warmDisclosure.test.js.map +1 -0
  64. package/dist/intros/age.d.ts +4 -0
  65. package/dist/intros/age.d.ts.map +1 -0
  66. package/dist/intros/age.js +14 -0
  67. package/dist/intros/age.js.map +1 -0
  68. package/dist/intros/attendance.d.ts +11 -0
  69. package/dist/intros/attendance.d.ts.map +1 -0
  70. package/dist/intros/attendance.js +27 -0
  71. package/dist/intros/attendance.js.map +1 -0
  72. package/dist/intros/attributeCatalog.d.ts +17 -0
  73. package/dist/intros/attributeCatalog.d.ts.map +1 -0
  74. package/dist/intros/attributeCatalog.js +96 -0
  75. package/dist/intros/attributeCatalog.js.map +1 -0
  76. package/dist/intros/index.d.ts +10 -0
  77. package/dist/intros/index.d.ts.map +1 -0
  78. package/dist/intros/index.js +10 -0
  79. package/dist/intros/index.js.map +1 -0
  80. package/dist/intros/intents.d.ts +8 -0
  81. package/dist/intros/intents.d.ts.map +1 -0
  82. package/dist/intros/intents.js +44 -0
  83. package/dist/intros/intents.js.map +1 -0
  84. package/dist/intros/psychExperiment.d.ts +29 -0
  85. package/dist/intros/psychExperiment.d.ts.map +1 -0
  86. package/dist/intros/psychExperiment.js +82 -0
  87. package/dist/intros/psychExperiment.js.map +1 -0
  88. package/dist/intros/reasons.d.ts +38 -0
  89. package/dist/intros/reasons.d.ts.map +1 -0
  90. package/dist/intros/reasons.js +162 -0
  91. package/dist/intros/reasons.js.map +1 -0
  92. package/dist/intros/sharedNotes.d.ts +10 -0
  93. package/dist/intros/sharedNotes.d.ts.map +1 -0
  94. package/dist/intros/sharedNotes.js +56 -0
  95. package/dist/intros/sharedNotes.js.map +1 -0
  96. package/dist/intros/transitions.d.ts +29 -0
  97. package/dist/intros/transitions.d.ts.map +1 -0
  98. package/dist/intros/transitions.js +128 -0
  99. package/dist/intros/transitions.js.map +1 -0
  100. package/dist/intros/validation.d.ts +50 -0
  101. package/dist/intros/validation.d.ts.map +1 -0
  102. package/dist/intros/validation.js +212 -0
  103. package/dist/intros/validation.js.map +1 -0
  104. package/dist/mirroredPrismaEnums.d.ts +1 -0
  105. package/dist/mirroredPrismaEnums.d.ts.map +1 -1
  106. package/dist/mirroredPrismaEnums.js +1 -0
  107. package/dist/mirroredPrismaEnums.js.map +1 -1
  108. package/dist/promoCartEngine.d.ts +55 -0
  109. package/dist/promoCartEngine.d.ts.map +1 -0
  110. package/dist/promoCartEngine.js +135 -0
  111. package/dist/promoCartEngine.js.map +1 -0
  112. package/dist/promoCodeRedemption.d.ts +44 -0
  113. package/dist/promoCodeRedemption.d.ts.map +1 -0
  114. package/dist/promoCodeRedemption.js +81 -0
  115. package/dist/promoCodeRedemption.js.map +1 -0
  116. package/dist/promoCodeScope.d.ts +91 -0
  117. package/dist/promoCodeScope.d.ts.map +1 -0
  118. package/dist/promoCodeScope.js +102 -0
  119. package/dist/promoCodeScope.js.map +1 -0
  120. package/dist/utils/__tests__/discountEngine/discountCalculator.test.js +8 -1
  121. package/dist/utils/__tests__/discountEngine/discountCalculator.test.js.map +1 -1
  122. package/dist/utils/__tests__/paymentUtils.test.js +87 -4
  123. package/dist/utils/__tests__/paymentUtils.test.js.map +1 -1
  124. package/dist/utils/paymentUtils.d.ts +30 -5
  125. package/dist/utils/paymentUtils.d.ts.map +1 -1
  126. package/dist/utils/paymentUtils.js +43 -11
  127. package/dist/utils/paymentUtils.js.map +1 -1
  128. package/package.json +1 -1
  129. package/prisma/schema.prisma +427 -6
  130. package/src/__tests__/appleIapProducts.test.ts +3 -0
  131. package/src/__tests__/bashSectionTitleForBashType.test.ts +4 -0
  132. package/src/__tests__/discoveryScope.test.ts +22 -1
  133. package/src/__tests__/promoCartEngine.test.ts +201 -0
  134. package/src/__tests__/promoCodeRedemption.test.ts +134 -0
  135. package/src/__tests__/promoCodeScope.test.ts +119 -0
  136. package/src/__tests__/statusEnums.test.ts +1 -0
  137. package/src/appleIapProducts.ts +29 -2
  138. package/src/definitions.ts +4 -2
  139. package/src/discoveryScope.ts +30 -20
  140. package/src/extendedSchemas.ts +1 -0
  141. package/src/index.ts +4 -0
  142. package/src/intros/__tests__/introsReleaseGates.test.ts +181 -0
  143. package/src/intros/__tests__/psychExperiment.test.ts +99 -0
  144. package/src/intros/__tests__/reasons.test.ts +122 -0
  145. package/src/intros/__tests__/sharedNotes.test.ts +20 -0
  146. package/src/intros/__tests__/transitions.test.ts +61 -0
  147. package/src/intros/__tests__/warmDisclosure.test.ts +10 -0
  148. package/src/intros/age.ts +13 -0
  149. package/src/intros/attendance.ts +33 -0
  150. package/src/intros/attributeCatalog.ts +113 -0
  151. package/src/intros/index.ts +9 -0
  152. package/src/intros/intents.ts +49 -0
  153. package/src/intros/psychExperiment.ts +130 -0
  154. package/src/intros/reasons.ts +187 -0
  155. package/src/intros/sharedNotes.ts +59 -0
  156. package/src/intros/transitions.ts +172 -0
  157. package/src/intros/validation.ts +248 -0
  158. package/src/mirroredPrismaEnums.ts +1 -0
  159. package/src/promoCartEngine.ts +193 -0
  160. package/src/promoCodeRedemption.ts +123 -0
  161. package/src/promoCodeScope.ts +180 -0
  162. package/src/utils/__tests__/discountEngine/discountCalculator.test.ts +8 -0
  163. package/src/utils/__tests__/paymentUtils.test.ts +100 -6
  164. package/src/utils/paymentUtils.ts +56 -13
@@ -633,7 +633,19 @@ model SentReminder {
633
633
  model BashEventPromoCode {
634
634
  id String @id @default(cuid())
635
635
  code String
636
- ticketTierId String
636
+ /// Legacy single tier. Null for bash-level codes; eligibility lives in eligibilityMode + eligibleTiers.
637
+ ticketTierId String?
638
+ /// Owning bash. Backfilled from the tier for legacy rows.
639
+ bashEventId String?
640
+ eligibilityMode PromoCodeEligibilityMode @default(SELECTED_TIERS)
641
+ /// Null on legacy rows: derived from the discount fields (see promoCodeScope.ts).
642
+ discountType PromoCodeDiscountType?
643
+ /// Percentage codes reduce BashPoints-priced tickets only when this is true.
644
+ discountAppliesToBashPoints Boolean @default(false)
645
+ /// Archived codes stay for history and stop working.
646
+ disabledAt DateTime?
647
+ /// Set when a legacy duplicate or shared pending promoter needs host review.
648
+ legacyReviewFlag String?
637
649
  stripeCouponId String?
638
650
  discountAmountInCents Int?
639
651
  discountAmountPercentage Int?
@@ -642,16 +654,81 @@ model BashEventPromoCode {
642
654
  promoterId String?
643
655
  meta String?
644
656
  bashPointsRewardPerTicket Int? // Host-defined BashPoints per ticket for promoter rewards
657
+ /// Successful orders allowed per account (Bash user id, else normalized email). Null means unlimited.
658
+ maxUsesPerAccount Int?
645
659
  promoter Promoter? @relation(fields: [promoterId], references: [id])
646
- ticketTier TicketTier @relation(fields: [ticketTierId], references: [id])
660
+ ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
661
+ bashEvent BashEvent? @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
647
662
  usedBy User[] @relation("BashEventPromoCodeToUser")
663
+ redemptions PromoCodeRedemption[]
664
+ eligibleTiers BashEventPromoCodeEligibleTier[]
648
665
 
649
666
  @@unique([ticketTierId, code])
667
+ @@index([bashEventId])
668
+ }
669
+
670
+ enum PromoCodeEligibilityMode {
671
+ ALL_TIERS
672
+ SELECTED_TIERS
673
+ }
674
+
675
+ enum PromoCodeDiscountType {
676
+ NONE
677
+ PERCENT
678
+ FIXED_CASH
679
+ }
680
+
681
+ /// Tiers a SELECTED_TIERS code applies to. ALL_TIERS codes store none.
682
+ model BashEventPromoCodeEligibleTier {
683
+ promoCodeId String
684
+ ticketTierId String
685
+ promoCode BashEventPromoCode @relation(fields: [promoCodeId], references: [id], onDelete: Cascade)
686
+ ticketTier TicketTier @relation(fields: [ticketTierId], references: [id], onDelete: Cascade)
687
+
688
+ @@id([promoCodeId, ticketTierId])
689
+ @@index([ticketTierId])
690
+ }
691
+
692
+ enum PromoCodeRedemptionStatus {
693
+ Reserved
694
+ Consumed
695
+ Released
696
+ Refunded
697
+ }
698
+
699
+ /// One row per order that used a promo code. `maxRedemptions` counts Consumed plus unexpired Reserved rows.
700
+ model PromoCodeRedemption {
701
+ id String @id @default(cuid())
702
+ promoCodeId String
703
+ userId String?
704
+ normalizedEmail String?
705
+ checkoutId String?
706
+ stripeCheckoutSessionId String? @unique
707
+ stripePaymentIntentId String? @unique
708
+ bashPointsPurchaseRef String? @unique
709
+ /// First ticket of a pay-at-door reservation that stored this code.
710
+ payAtDoorTicketId String? @unique
711
+ status PromoCodeRedemptionStatus @default(Reserved)
712
+ expiresAt DateTime?
713
+ consumedAt DateTime?
714
+ releasedAt DateTime?
715
+ refundedAt DateTime?
716
+ createdAt DateTime @default(now())
717
+ updatedAt DateTime @updatedAt
718
+
719
+ promoCode BashEventPromoCode @relation(fields: [promoCodeId], references: [id], onDelete: Restrict)
720
+
721
+ @@index([promoCodeId, status])
722
+ @@index([promoCodeId, userId])
723
+ @@index([promoCodeId, normalizedEmail])
724
+ @@index([status, expiresAt])
650
725
  }
651
726
 
652
727
  model Promoter {
653
728
  id String @id @default(cuid())
654
- promoterId String
729
+ /// The promoter's user id once known. Null for a pending invite (see invitedEmail).
730
+ /// Partial unique index (promotedForBash, invitedEmail) WHERE userId IS NULL lives in SQL.
731
+ promoterId String?
655
732
  promotedForBash String
656
733
  level Int @default(1)
657
734
  isActive Boolean @default(true)
@@ -1016,7 +1093,9 @@ model BashEvent {
1016
1093
  /// When true, the "Group up" section is hidden on the bash detail page.
1017
1094
  hideGroupUp Boolean @default(false)
1018
1095
  /// When true, the "Services open to" box is hidden on the bash detail page.
1019
- hideServicesOpenTo Boolean @default(false)
1096
+ hideServicesOpenTo Boolean @default(true)
1097
+ /// When true, the live location box is hidden on the bash detail page.
1098
+ hideLiveLocation Boolean @default(true)
1020
1099
  /// When true (default), cancelling the bash auto-refunds paid ticket holders. Hosts may set false only before any ticket/RSVP exists.
1021
1100
  refundsGuaranteedOnCancellation Boolean @default(true)
1022
1101
  /// When true, host may postpone after tickets exist. May only flip false→true before any ticket/RSVP exists.
@@ -1072,6 +1151,9 @@ model BashEvent {
1072
1151
  artistPayoutRate Float?
1073
1152
  topPromoterPrize String? // Optional prize label: "Free table for your crew"
1074
1153
  promoterLeaderboardIsPublic Boolean @default(false) // Host opts in to public leaderboard
1154
+ /// Host opt-in for promoter requests, the public leaderboard, and the prize.
1155
+ /// Off hides those settings and public surfaces. Codes, attribution, and commission agreements stay.
1156
+ promoterToolsEnabled Boolean @default(false)
1075
1157
  /// When true, the bash detail promo-code request CTA is hidden from guests (in-app notifications still work for hosts).
1076
1158
  hideGuestPromoCodeRequest Boolean @default(true)
1077
1159
  showAttendees Boolean @default(true)
@@ -1216,6 +1298,10 @@ model BashEvent {
1216
1298
  albumModerationEnabled Boolean @default(false)
1217
1299
  /// Opt-in post-event attendee networking
1218
1300
  postEventNetworkingEnabled Boolean @default(false)
1301
+ /// Intros at this bash. Off until the host turns it on.
1302
+ introsMode IntrosEventMode @default(Off)
1303
+ introsParticipations IntrosEventParticipation[]
1304
+ introductions Introduction[]
1219
1305
 
1220
1306
  // Host broadcast fields
1221
1307
  postEventMessageSent Boolean @default(false)
@@ -1313,6 +1399,7 @@ model BashEvent {
1313
1399
  hostPromoterPayouts HostPromoterPayout[]
1314
1400
  ticketGiftGrants TicketGiftGrant[]
1315
1401
  ticketTiers TicketTier[]
1402
+ promoCodes BashEventPromoCode[]
1316
1403
  tierReleaseReminders TierReleaseReminder[]
1317
1404
  favoritedBy UserFavorite[]
1318
1405
  userPromoCodeRedemption UserPromoCodeRedemption[]
@@ -3626,6 +3713,7 @@ model TicketTier {
3626
3713
  isDonationTier Boolean?
3627
3714
  hidden Boolean?
3628
3715
  promoCodes BashEventPromoCode[]
3716
+ promoCodeEligibility BashEventPromoCodeEligibleTier[]
3629
3717
  tickets Ticket[]
3630
3718
  bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
3631
3719
  waitList User[] @relation("TicketTierToUser")
@@ -4640,11 +4728,15 @@ model User {
4640
4728
  /// App Store originalTransactionId when membership is billed via StoreKit IAP.
4641
4729
  membershipAppleOriginalTransactionId String? @unique
4642
4730
  /// Which provider owns the active membership billing relationship.
4643
- membershipBillingProvider String? // "stripe" | "apple" | null
4731
+ membershipBillingProvider String? // "stripe" | "apple" | "google" | null
4732
+ /// Current Google Play purchase token when membership is billed via Play Billing.
4733
+ membershipGooglePurchaseToken String? @unique
4644
4734
  /// Optional consumer BashPass subscription (waives guest platform fee on tickets; monthly BashPoints).
4645
4735
  bashPassStripeSubscriptionId String? @unique
4646
4736
  /// App Store originalTransactionId when BashPass is billed via StoreKit IAP.
4647
4737
  bashPassAppleOriginalTransactionId String? @unique
4738
+ /// Current Google Play purchase token when BashPass is billed via Play Billing.
4739
+ bashPassGooglePurchaseToken String? @unique
4648
4740
  bashPassCurrentPeriodEnd DateTime?
4649
4741
  /// Stripe price tier for standalone BashPass; effective tier may also come from membership bundle.
4650
4742
  bashPassTier BashPassTier?
@@ -4704,6 +4796,7 @@ model User {
4704
4796
  birthdayOfferOptIns UserBirthdayOfferOptIn[]
4705
4797
  birthdayOfferDeliveries BirthdayOfferDelivery[]
4706
4798
  birthdayBoostGrants BirthdayBoostGrant[]
4799
+ playPurchases PlayPurchase[]
4707
4800
  birthdayRewardRedemptions BirthdayRewardRedemption[]
4708
4801
  birthdayRewardFavorites BirthdayRewardFavorite[]
4709
4802
  birthdayDealSuggestions BirthdayDealSuggestion[]
@@ -4849,6 +4942,11 @@ model User {
4849
4942
  connectionRequestsReceived UserConnection[] @relation("ConnectionRequestsReceived")
4850
4943
  interestExpressedByMe InterestExpression[] @relation("InterestExpressedByMe")
4851
4944
  interestExpressedToMe InterestExpression[] @relation("InterestExpressedToMe")
4945
+ introsProfile IntrosProfile?
4946
+ introsAttributes IntrosProfileAttribute[]
4947
+ introsEventParticipations IntrosEventParticipation[]
4948
+ warmSuggestionsMade WarmIntroSuggestion[] @relation("WarmSuggestionFacilitator")
4949
+ facilitatedIntroductions Introduction[] @relation("IntroFacilitator")
4852
4950
  links UserLink[]
4853
4951
  preferences UserPreferences?
4854
4952
  rankings UserRankings?
@@ -5093,6 +5191,8 @@ model UserPreferences {
5093
5191
  personalityType String?
5094
5192
  /// Self-reported locus of control: internal | external | mixed
5095
5193
  locusOfControl String?
5194
+ /// Warm Intro invitations from connections. Null means the person has not chosen.
5195
+ warmIntroInvites WarmIntroInvitePref?
5096
5196
  blockSearchEngineIndex Boolean @default(false)
5097
5197
  autoAddEventAttendees Boolean @default(true)
5098
5198
  trackFirstEventAttended Boolean @default(true)
@@ -5429,6 +5529,262 @@ model InterestExpression {
5429
5529
  @@index([targetId])
5430
5530
  }
5431
5531
 
5532
+ enum IntrosStatus {
5533
+ Off
5534
+ Active
5535
+ Paused
5536
+ }
5537
+
5538
+ enum IntrosIntent {
5539
+ Friends
5540
+ Professional
5541
+ MentorSeek
5542
+ MentorOffer
5543
+ Collaborator
5544
+ Romance
5545
+ }
5546
+
5547
+ enum IntrosWarmAudience {
5548
+ None
5549
+ Connections
5550
+ ConnectionsAndHosts
5551
+ }
5552
+
5553
+ enum WarmIntroInvitePref {
5554
+ Off
5555
+ Anonymous
5556
+ Named
5557
+ }
5558
+
5559
+ enum IntrosEventMode {
5560
+ Off
5561
+ Platonic
5562
+ All
5563
+ }
5564
+
5565
+ enum IntrosAttributeKind {
5566
+ BigFive
5567
+ MyersBriggs
5568
+ LocusOfControl
5569
+ CliftonStrengths
5570
+ EmotionalIntelligence
5571
+ HumanDesign
5572
+ }
5573
+
5574
+ enum IntrosVisibility {
5575
+ OnlyMe
5576
+ RevealedIntros
5577
+ }
5578
+
5579
+ enum IntroductionSource {
5580
+ BashSuggested
5581
+ Warm
5582
+ }
5583
+
5584
+ enum IntroductionMode {
5585
+ Mystery
5586
+ Known
5587
+ }
5588
+
5589
+ enum IntroductionState {
5590
+ Open
5591
+ Mutual
5592
+ Revealed
5593
+ Closed
5594
+ }
5595
+
5596
+ enum IntroductionClosedReason {
5597
+ Declined
5598
+ Withdrawn
5599
+ Expired
5600
+ NotRegistered
5601
+ Blocked
5602
+ EventCancelled
5603
+ IntrosDisabled
5604
+ ConsentRevoked
5605
+ }
5606
+
5607
+ enum IntroInterest {
5608
+ Pending
5609
+ Interested
5610
+ Declined
5611
+ Postponed
5612
+ Withdrawn
5613
+ }
5614
+
5615
+ enum RevealSource {
5616
+ EventStart
5617
+ EarlyReveal
5618
+ Warm
5619
+ }
5620
+
5621
+ enum EarlyRevealStatus {
5622
+ Requested
5623
+ Approved
5624
+ Completed
5625
+ Declined
5626
+ Expired
5627
+ Cancelled
5628
+ Refunded
5629
+ }
5630
+
5631
+ enum EarlyRevealPaymentMethod {
5632
+ Stripe
5633
+ AppleIap
5634
+ BashPoints
5635
+ }
5636
+
5637
+ model IntrosProfile {
5638
+ id String @id @default(cuid())
5639
+ userId String @unique
5640
+ status IntrosStatus @default(Off)
5641
+ pausedUntil DateTime?
5642
+ consentVersion Int @default(1)
5643
+ activatedAt DateTime?
5644
+ globalMatchingAt DateTime?
5645
+ romanceAt DateTime?
5646
+ psychologyExperimentAt DateTime?
5647
+ warmSuggestionAudience IntrosWarmAudience @default(None)
5648
+ intents IntrosIntent[]
5649
+ shortIntro String? @db.VarChar(140)
5650
+ conversationTopics String[] @default([])
5651
+ professionalInterests String[] @default([])
5652
+ travelRadiusMiles Int?
5653
+ romanceSeekingGenders String[] @default([])
5654
+ romanceAgeMin Int?
5655
+ romanceAgeMax Int?
5656
+ shareRegistrationStatus Boolean @default(false)
5657
+ revealOnlyAfterCheckIn Boolean @default(false)
5658
+ notifyIntros Boolean @default(true)
5659
+ legacyImportedAt DateTime?
5660
+ createdAt DateTime @default(now())
5661
+ updatedAt DateTime @updatedAt
5662
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
5663
+ }
5664
+
5665
+ model IntrosProfileAttribute {
5666
+ id String @id @default(cuid())
5667
+ userId String
5668
+ kind IntrosAttributeKind
5669
+ value Json
5670
+ source String
5671
+ visibility IntrosVisibility @default(OnlyMe)
5672
+ allowExperimentalMatching Boolean @default(false)
5673
+ updatedAt DateTime @updatedAt
5674
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
5675
+
5676
+ @@unique([userId, kind])
5677
+ }
5678
+
5679
+ model IntrosEventParticipation {
5680
+ id String @id @default(cuid())
5681
+ userId String
5682
+ bashEventId String
5683
+ optedInAt DateTime @default(now())
5684
+ optedOutAt DateTime?
5685
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
5686
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
5687
+
5688
+ @@unique([userId, bashEventId])
5689
+ @@index([bashEventId])
5690
+ }
5691
+
5692
+ model IntrosPair {
5693
+ id String @id @default(cuid())
5694
+ userLowId String
5695
+ userHighId String
5696
+ lastSuggestedAt DateTime?
5697
+ revealCount Int @default(0)
5698
+ lastRevealedAt DateTime?
5699
+ cooldownUntil DateTime?
5700
+ hiddenByLowAt DateTime?
5701
+ hiddenByHighAt DateTime?
5702
+ introductions Introduction[]
5703
+
5704
+ @@unique([userLowId, userHighId])
5705
+ }
5706
+
5707
+ model Introduction {
5708
+ id String @id @default(cuid())
5709
+ pairId String
5710
+ bashEventId String?
5711
+ source IntroductionSource
5712
+ mode IntroductionMode
5713
+ matchedIntent IntrosIntent?
5714
+ state IntroductionState @default(Open)
5715
+ closedReason IntroductionClosedReason?
5716
+ interestLow IntroInterest @default(Pending)
5717
+ interestHigh IntroInterest @default(Pending)
5718
+ interestLowAt DateTime?
5719
+ interestHighAt DateTime?
5720
+ mutedLow Boolean @default(false)
5721
+ mutedHigh Boolean @default(false)
5722
+ saidHiLowAt DateTime?
5723
+ saidHiHighAt DateTime?
5724
+ facilitationOkLowAt DateTime?
5725
+ facilitationOkHighAt DateTime?
5726
+ facilitatorId String?
5727
+ facilitatorNote String? @db.VarChar(280)
5728
+ facilitatorMadeAt DateTime?
5729
+ revealAt DateTime?
5730
+ revealedAt DateTime?
5731
+ revealSource RevealSource?
5732
+ nudgedAt DateTime?
5733
+ explanationCodes String[] @default([])
5734
+ scoreComponents Json?
5735
+ ruleVersion Int @default(1)
5736
+ expiresAt DateTime?
5737
+ version Int @default(0)
5738
+ createdAt DateTime @default(now())
5739
+ pair IntrosPair @relation(fields: [pairId], references: [id], onDelete: Cascade)
5740
+ bashEvent BashEvent? @relation(fields: [bashEventId], references: [id], onDelete: SetNull)
5741
+ facilitator User? @relation("IntroFacilitator", fields: [facilitatorId], references: [id], onDelete: SetNull)
5742
+ earlyReveals EarlyRevealRequest[]
5743
+
5744
+ @@index([bashEventId, state])
5745
+ @@index([facilitatorId])
5746
+ @@index([state, revealAt])
5747
+ }
5748
+
5749
+ model WarmIntroSuggestion {
5750
+ id String @id @default(cuid())
5751
+ facilitatorId String
5752
+ pairId String
5753
+ bashEventId String?
5754
+ linkedIntroductionId String?
5755
+ reasonCodes String[] @default([])
5756
+ madeAt DateTime?
5757
+ dismissedAt DateTime?
5758
+ offeredIntroductionId String?
5759
+ createdAt DateTime @default(now())
5760
+ facilitator User @relation("WarmSuggestionFacilitator", fields: [facilitatorId], references: [id], onDelete: Cascade)
5761
+
5762
+ @@unique([facilitatorId, pairId, bashEventId])
5763
+ @@index([facilitatorId, createdAt])
5764
+ }
5765
+
5766
+ model EarlyRevealRequest {
5767
+ id String @id @default(cuid())
5768
+ introductionId String
5769
+ requesterId String
5770
+ status EarlyRevealStatus @default(Requested)
5771
+ priceCents Int
5772
+ currency String @default("usd")
5773
+ paymentMethod EarlyRevealPaymentMethod?
5774
+ stripePaymentIntentId String? @unique
5775
+ appleTransactionId String? @unique
5776
+ bashCreditTransactionId String?
5777
+ idempotencyKey String @unique
5778
+ approvedAt DateTime?
5779
+ expiresAt DateTime
5780
+ createdAt DateTime @default(now())
5781
+ updatedAt DateTime @updatedAt
5782
+ introduction Introduction @relation(fields: [introductionId], references: [id], onDelete: Cascade)
5783
+
5784
+ @@index([introductionId])
5785
+ @@index([status, updatedAt])
5786
+ }
5787
+
5432
5788
  model BiometricCredential {
5433
5789
  id String @id @default(cuid())
5434
5790
  userId String
@@ -5593,6 +5949,33 @@ model ServiceSubscriptionCounts {
5593
5949
  subscriptionCount UserSubscription? @relation("subscriptionCount")
5594
5950
  }
5595
5951
 
5952
+ /// One row per Google Play purchase token. Renewals update the same row.
5953
+ /// A subscription replacement inserts a new row and keeps the previous token.
5954
+ model PlayPurchase {
5955
+ id String @id @default(cuid())
5956
+ purchaseToken String @unique
5957
+ linkedPurchaseToken String?
5958
+ productId String
5959
+ userId String
5960
+ family String
5961
+ purchaseState String
5962
+ expiryTime DateTime?
5963
+ orderId String?
5964
+ grantedAt DateTime?
5965
+ acknowledgedAt DateTime?
5966
+ consumedAt DateTime?
5967
+ finishError String?
5968
+ boostTargetType String?
5969
+ boostTargetId String?
5970
+ featuredUntil DateTime?
5971
+ createdAt DateTime @default(now())
5972
+ updatedAt DateTime @updatedAt
5973
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
5974
+
5975
+ @@index([userId])
5976
+ @@index([linkedPurchaseToken])
5977
+ }
5978
+
5596
5979
  model UserSubscription {
5597
5980
  id String @id @default(cuid())
5598
5981
  ownerId String @unique
@@ -5601,6 +5984,8 @@ model UserSubscription {
5601
5984
  stripeCheckoutSessionId String? @unique
5602
5985
  /// App Store originalTransactionId when listing seats are billed via StoreKit IAP.
5603
5986
  appleOriginalTransactionId String? @unique
5987
+ /// Current Google Play purchase token when listing seats are billed via Play Billing.
5988
+ googlePurchaseToken String? @unique
5604
5989
  type MembershipTier?
5605
5990
  status UserSubscriptionStatus?
5606
5991
  subscriptionCountId String @unique
@@ -5614,6 +5999,7 @@ model UserSubscription {
5614
5999
  @@index([stripeCheckoutSessionId])
5615
6000
  @@index([stripeSubscriptionId])
5616
6001
  @@index([appleOriginalTransactionId])
6002
+ @@index([googlePurchaseToken])
5617
6003
  }
5618
6004
 
5619
6005
  model Service {
@@ -6084,7 +6470,7 @@ model VendorSpaceRequirements {
6084
6470
  vendorId String @unique
6085
6471
  boothSize String @default("10x10")
6086
6472
  customSize String?
6087
- hasTent Boolean @default(false)
6473
+ hasTent Boolean @default(true)
6088
6474
  tableCount String @default("1")
6089
6475
  hasChairs Boolean @default(false)
6090
6476
  needsElectricity Boolean @default(false)
@@ -8513,6 +8899,8 @@ model UserReport {
8513
8899
  reporterId String
8514
8900
  reportedId String
8515
8901
  bashEventId String?
8902
+ /// Optional Intros introduction this report is about. The reporter may not know the other person yet.
8903
+ introductionId String?
8516
8904
  reason String
8517
8905
  details String
8518
8906
  createdAt DateTime @default(now())
@@ -8965,6 +9353,12 @@ enum NotificationType {
8965
9353
  InterestExpressedAnonymous
8966
9354
  /// Intros: both people sent a signal — unlocks chat
8967
9355
  IntroMutual
9356
+ /// Intros V2: never identifies anyone before a reveal
9357
+ IntrosUpdate
9358
+ /// Intros V2: identities, sent only to the two people
9359
+ IntrosRevealed
9360
+ /// Intros V2: warm invitations and facilitator suggestions
9361
+ IntrosWarm
8968
9362
  }
8969
9363
 
8970
9364
  /// Opt-in reminder for a scheduled tier release (v1.5).
@@ -11614,6 +12008,7 @@ enum CreditSourceType {
11614
12008
  CompetitionFinalist // Adventures reward for reaching a finalist placement
11615
12009
  CompetitionWin // Adventures reward for winning a competition prize
11616
12010
  HornClear // Stuff the horn: 5 points per clear, 100 per UTC day
12011
+ IntrosEarlyReveal // BashPoints spent on a paid early identity reveal
11617
12012
  }
11618
12013
 
11619
12014
  enum ReferralTier {
@@ -12472,6 +12867,13 @@ enum AuditAction {
12472
12867
  ORG_TAX_DOCUMENT_REVIEWED
12473
12868
  ORG_INSURANCE_CERTIFICATE_SUBMITTED
12474
12869
  ORG_INSURANCE_CERTIFICATE_REVIEWED
12870
+ INTROS_REVEALED
12871
+ INTROS_REVEAL_BLOCKED
12872
+ INTROS_WARM_ACCEPTED
12873
+ INTROS_EARLY_REVEAL_APPROVED
12874
+ INTROS_EARLY_REVEAL_PAID
12875
+ INTROS_REFUND_ISSUED
12876
+ INTROS_PAYMENT_RECONCILED
12475
12877
  }
12476
12878
 
12477
12879
  enum AuditCategory {
@@ -12487,6 +12889,7 @@ enum AuditCategory {
12487
12889
  Compliance
12488
12890
  Security
12489
12891
  Organization
12892
+ Intros
12490
12893
  }
12491
12894
 
12492
12895
  enum AuditSeverity {
@@ -13070,18 +13473,36 @@ model AppliedDiscount {
13070
13473
  displayReason String? // "Early Bird Discount" for receipt
13071
13474
  internalNote String? // For support/debugging
13072
13475
 
13476
+ /// BashPoints taken off by a percentage code that applies to BashPoints tiers.
13477
+ pointsDiscounted Int?
13478
+ /// Tickets this row attributes to the promoter (free and BashPoints included).
13479
+ attributedTicketCount Int?
13480
+
13481
+ /// Provisional until payment succeeds; readers count Final rows with no refundedAt.
13482
+ status AppliedDiscountStatus @default(Final)
13483
+ finalizedAt DateTime?
13484
+ refundedAt DateTime?
13485
+
13073
13486
  // Relations
13074
13487
  checkout Checkout? @relation(fields: [checkoutId], references: [id])
13075
13488
  ticket Ticket? @relation(fields: [ticketId], references: [id])
13076
13489
  ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
13077
13490
  attributionUser User? @relation("DiscountAttribution", fields: [attributionUserId], references: [id])
13078
13491
 
13492
+ @@unique([checkoutId, sourceId, ticketId])
13079
13493
  @@index([checkoutId])
13494
+ @@index([checkoutId, status])
13080
13495
  @@index([sourceType, sourceId])
13081
13496
  @@index([attributionUserId])
13082
13497
  @@index([createdAt])
13083
13498
  }
13084
13499
 
13500
+ enum AppliedDiscountStatus {
13501
+ Provisional
13502
+ Final
13503
+ Voided
13504
+ }
13505
+
13085
13506
  // Special offers - automatic, visible, conversion-oriented deals
13086
13507
  model SpecialOffer {
13087
13508
  id String @id @default(cuid())
@@ -41,6 +41,9 @@ describe("appleIapProducts", () => {
41
41
  expect(
42
42
  getAppleIapProduct("community.bash.us.boost.7d")?.family
43
43
  ).toBe("feature_boost");
44
+ expect(getAppleIapProduct("community.bash.us.intros.earlyreveal")?.family).toBe(
45
+ "early_reveal"
46
+ );
44
47
  });
45
48
 
46
49
  it("listAppleIapProductIds filters by family", () => {
@@ -54,6 +54,10 @@ describe("bashSectionTitleForBashType", () => {
54
54
  });
55
55
 
56
56
  it("scope rows use destination copy", () => {
57
+ expect(bashSectionTitleForBashType("Local")).toBe(
58
+ "City & Community Events"
59
+ );
60
+ expect(bashSectionTitleForBashType("State")).toBe("State Events");
57
61
  expect(bashSectionTitleForBashType("National")).toBe(
58
62
  "Across the country"
59
63
  );
@@ -15,10 +15,31 @@ describe("discoveryScope", () => {
15
15
  );
16
16
  });
17
17
 
18
- it("infers State from state fair copy", () => {
18
+ it("infers State from a state-run event, not from travel or sponsorship", () => {
19
19
  expect(inferDiscoveryScopeFromText("Utah State Fair")).toBe(
20
20
  DiscoveryScope.State
21
21
  );
22
+ expect(inferDiscoveryScopeFromText("People are coming from statewide")).toBe(
23
+ DiscoveryScope.Unspecified
24
+ );
25
+ expect(
26
+ inferDiscoveryScopeFromText("Sponsored by the Utah state agency office")
27
+ ).toBe(DiscoveryScope.Unspecified);
28
+ expect(
29
+ inferDiscoveryScopeFromText("Organized by Utah State Parks")
30
+ ).toBe(DiscoveryScope.State);
31
+ expect(
32
+ inferDiscoveryScopeFromText("Neighborhood party sponsored by the state")
33
+ ).toBe(DiscoveryScope.Unspecified);
34
+ });
35
+
36
+ it("infers Local from the organizer, not from how close the party is", () => {
37
+ expect(
38
+ inferDiscoveryScopeFromText("City of Provo parks and recreation concert")
39
+ ).toBe(DiscoveryScope.Local);
40
+ expect(inferDiscoveryScopeFromText("Sugar House block party")).toBe(
41
+ DiscoveryScope.Unspecified
42
+ );
22
43
  });
23
44
 
24
45
  it("infers National from destination brands", () => {