@bash-app/bash-common 30.228.0 → 30.229.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 (43) hide show
  1. package/dist/extendedSchemas.d.ts +16 -1
  2. package/dist/extendedSchemas.d.ts.map +1 -1
  3. package/dist/extendedSchemas.js +1 -0
  4. package/dist/extendedSchemas.js.map +1 -1
  5. package/dist/index.d.ts +3 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +3 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/utils/__tests__/flyerUtils.test.js +55 -1
  10. package/dist/utils/__tests__/flyerUtils.test.js.map +1 -1
  11. package/dist/utils/__tests__/lobFlyerUtils.test.d.ts +2 -0
  12. package/dist/utils/__tests__/lobFlyerUtils.test.d.ts.map +1 -0
  13. package/dist/utils/__tests__/lobFlyerUtils.test.js +39 -0
  14. package/dist/utils/__tests__/lobFlyerUtils.test.js.map +1 -0
  15. package/dist/utils/__tests__/locationVisibility.test.d.ts +2 -0
  16. package/dist/utils/__tests__/locationVisibility.test.d.ts.map +1 -0
  17. package/dist/utils/__tests__/locationVisibility.test.js +172 -0
  18. package/dist/utils/__tests__/locationVisibility.test.js.map +1 -0
  19. package/dist/utils/flyerUtils.d.ts +19 -0
  20. package/dist/utils/flyerUtils.d.ts.map +1 -1
  21. package/dist/utils/flyerUtils.js +38 -0
  22. package/dist/utils/flyerUtils.js.map +1 -1
  23. package/dist/utils/lobFlyerUtils.d.ts +41 -0
  24. package/dist/utils/lobFlyerUtils.d.ts.map +1 -0
  25. package/dist/utils/lobFlyerUtils.js +68 -0
  26. package/dist/utils/lobFlyerUtils.js.map +1 -0
  27. package/dist/utils/locationVisibility.d.ts +66 -0
  28. package/dist/utils/locationVisibility.d.ts.map +1 -0
  29. package/dist/utils/locationVisibility.js +114 -0
  30. package/dist/utils/locationVisibility.js.map +1 -0
  31. package/dist/utils/service/serviceUtils.js +2 -2
  32. package/dist/utils/service/serviceUtils.js.map +1 -1
  33. package/package.json +1 -1
  34. package/prisma/schema.prisma +237 -61
  35. package/src/extendedSchemas.ts +16 -0
  36. package/src/index.ts +6 -0
  37. package/src/utils/__tests__/flyerUtils.test.ts +77 -0
  38. package/src/utils/__tests__/lobFlyerUtils.test.ts +58 -0
  39. package/src/utils/__tests__/locationVisibility.test.ts +248 -0
  40. package/src/utils/flyerUtils.ts +69 -0
  41. package/src/utils/lobFlyerUtils.ts +83 -0
  42. package/src/utils/locationVisibility.ts +165 -0
  43. package/src/utils/service/serviceUtils.ts +2 -2
@@ -0,0 +1,165 @@
1
+ import type { LocationRevealAudience, LocationRevealMode } from "@prisma/client";
2
+
3
+ /** Minimal bash fields used for location visibility (API + app). */
4
+ export type BashLocationVisibilityEvent = {
5
+ hideLocation?: boolean | null;
6
+ locationRevealedAt?: Date | string | null;
7
+ creatorId: string;
8
+ locationRevealAudience?: LocationRevealAudience | null;
9
+ locationRevealMode?: LocationRevealMode | null;
10
+ locationRevealOffsetMins?: number | null;
11
+ startDateTime?: Date | string | null;
12
+ };
13
+
14
+ export type ViewerLocationContext = {
15
+ viewerUserId: string | null | undefined;
16
+ isHostOrOrganizer: boolean;
17
+ viewerOwnsValidTicket: boolean;
18
+ viewerPublicRsvpGoing: boolean;
19
+ viewerHasAcceptedInvitation: boolean;
20
+ };
21
+
22
+ const VALID_TICKET_STATUSES_EXCLUDED = new Set(["Cancelled", "WaitlistPending"]);
23
+
24
+ /**
25
+ * True if the viewer holds at least one non-cancelled, non-waitlist-pending ticket for this bash.
26
+ * Aligns with bash-app `calculateTicketsBought` filtering.
27
+ */
28
+ export function viewerOwnsValidTicketForBash(
29
+ tickets: ReadonlyArray<{ status: string; ownerId: string | null }> | null | undefined,
30
+ viewerUserId: string | null | undefined
31
+ ): boolean {
32
+ if (!viewerUserId || !tickets?.length) return false;
33
+ return tickets.some(
34
+ (t) =>
35
+ t.ownerId === viewerUserId && !VALID_TICKET_STATUSES_EXCLUDED.has(t.status)
36
+ );
37
+ }
38
+
39
+ /**
40
+ * Whether the exact address / pin should be shown to this viewer.
41
+ * Public to everyone once `locationRevealedAt` is set. Otherwise gated by `hideLocation` and audience.
42
+ */
43
+ export function viewerCanSeeFullBashLocation(
44
+ bash: BashLocationVisibilityEvent,
45
+ ctx: ViewerLocationContext
46
+ ): boolean {
47
+ if (!bash.hideLocation) return true;
48
+ if (bash.locationRevealedAt) return true;
49
+ if (ctx.isHostOrOrganizer) return true;
50
+ if (!ctx.viewerUserId) return false;
51
+
52
+ const audience = bash.locationRevealAudience ?? "TicketHolders";
53
+
54
+ if (ctx.viewerOwnsValidTicket) return true;
55
+
56
+ if (audience === "TicketHolders") return false;
57
+
58
+ if (audience === "TicketHoldersAndRsvps" || audience === "TicketHoldersAndInvited") {
59
+ if (ctx.viewerPublicRsvpGoing) return true;
60
+ }
61
+
62
+ if (audience === "TicketHoldersAndInvited" && ctx.viewerHasAcceptedInvitation) {
63
+ return true;
64
+ }
65
+
66
+ return false;
67
+ }
68
+
69
+ /** Exact address is visible to everyone (not just gated audience). */
70
+ export function isLocationGloballyPublic(bash: BashLocationVisibilityEvent): boolean {
71
+ return !bash.hideLocation || bash.locationRevealedAt != null;
72
+ }
73
+
74
+ export type BashEventLocationRedactFields = {
75
+ hideLocation?: boolean | null;
76
+ location?: string | null;
77
+ street?: string | null;
78
+ zipCode?: string | null;
79
+ venueId?: string | null;
80
+ coordinates?: ReadonlyArray<unknown> | null;
81
+ };
82
+
83
+ /**
84
+ * Returns a shallow copy with exact-address fields stripped. Caller should only invoke when
85
+ * `viewerCanSeeFullBashLocation` is false and `hideLocation` is true.
86
+ */
87
+ export function redactBashEventLocation<T extends BashEventLocationRedactFields>(event: T): T {
88
+ const out = { ...event };
89
+ out.location = null;
90
+ out.street = null;
91
+ out.zipCode = null;
92
+ out.venueId = null;
93
+ const coords = (out as { coordinates?: unknown[] | null }).coordinates;
94
+ if (Array.isArray(coords) && coords.length > 0) {
95
+ (out as { coordinates?: unknown[] }).coordinates = [];
96
+ }
97
+ return out;
98
+ }
99
+
100
+ /**
101
+ * Applies redaction when hide-location is on and the viewer cannot see the full address.
102
+ */
103
+ export function applyBashEventLocationRedaction<T extends BashEventLocationRedactFields>(
104
+ event: T,
105
+ canSeeFull: boolean
106
+ ): T {
107
+ if (!event.hideLocation || canSeeFull) {
108
+ return event;
109
+ }
110
+ return redactBashEventLocation(event);
111
+ }
112
+
113
+ /** When the address becomes public to everyone (null = manual-only or unknown). */
114
+ export function computeLocationPublicRevealDueAt(bash: BashLocationVisibilityEvent): Date | null {
115
+ if (!bash.hideLocation || bash.locationRevealedAt) return null;
116
+ const start = bash.startDateTime ? new Date(bash.startDateTime) : null;
117
+ if (!start || Number.isNaN(start.getTime())) return null;
118
+ const mode = bash.locationRevealMode ?? "AtStartTime";
119
+ if (mode === "ManualOnly") return null;
120
+ if (mode === "AtStartTime") return start;
121
+ if (mode === "CustomOffset") {
122
+ const mins = bash.locationRevealOffsetMins;
123
+ if (mins == null || mins < 0) return null;
124
+ return new Date(start.getTime() - mins * 60_000);
125
+ }
126
+ return null;
127
+ }
128
+
129
+ /** Whether cron should set `locationRevealedAt` now (public reveal to all). */
130
+ export function shouldAutoRevealLocationToPublicNow(
131
+ bash: BashLocationVisibilityEvent,
132
+ now: Date
133
+ ): boolean {
134
+ if (!bash.hideLocation || bash.locationRevealedAt) return false;
135
+ const mode = bash.locationRevealMode ?? "AtStartTime";
136
+ if (mode === "ManualOnly") return false;
137
+ const due = computeLocationPublicRevealDueAt(bash);
138
+ if (!due) return false;
139
+ return now.getTime() >= due.getTime();
140
+ }
141
+
142
+ /** Default audience for the Where step: paid tiers → ticket holders only; else RSVPs+invited path default. */
143
+ export function defaultLocationRevealAudience(hasPaidTicketTiers: boolean): LocationRevealAudience {
144
+ return hasPaidTicketTiers ? "TicketHolders" : "TicketHoldersAndRsvps";
145
+ }
146
+
147
+ /** Address string for Google geocoding on public maps when full `location` is redacted. */
148
+ export function geocodeQueryTextForPublicBashMap(event: {
149
+ location?: string | null;
150
+ hideLocation?: boolean | null;
151
+ locationRevealedAt?: Date | string | null;
152
+ city?: string | null;
153
+ state?: string | null;
154
+ country?: string | null;
155
+ }): string | null {
156
+ const loc = event.location?.trim();
157
+ if (loc) return loc;
158
+ if (event.hideLocation && !event.locationRevealedAt) {
159
+ const parts = [event.city, event.state, event.country].filter(
160
+ (p): p is string => Boolean(p && String(p).trim())
161
+ );
162
+ return parts.length ? parts.join(", ") : null;
163
+ }
164
+ return null;
165
+ }
@@ -646,13 +646,13 @@ export const serviceSpecificInfoAI = {
646
646
  EventServices: {},
647
647
  EntertainmentServices: {
648
648
  yearsOfExperience: {
649
- description: `yearsOfExperience (use "LessThanOneYear", "OneToTwoYears", "ThreeToFiveYears", or "FivePlusYears" - for "10+ years" or similar use "FivePlusYears")`,
649
+ description: `yearsOfExperience (use "LessThanOneYear", "OneToThreeYears", "ThreeToFiveYears", or "FivePlusYears" - for "10+ years" or similar use "FivePlusYears")`,
650
650
  example: `"FivePlusYears"`,
651
651
  type: "string",
652
652
  isArray: false,
653
653
  },
654
654
  entertainmentServiceTypes: {
655
- description: `entertainmentServiceTypes (array of EntertainmentServiceType enum values: "Emcees", "DJs", "Bands", "SoloMusicians", "Comedy", "VarietyActs", "PerformanceArtists", "Magic", "Dancers", "Aerialists", "Impersonators", "Speakers", "Auctioneers", "Other")`,
655
+ description: `entertainmentServiceTypes (array of EntertainmentServiceType enum values: "Emcees", "DJs", "Bands", "SoloMusicians", "Comedy", "VarietyActs", "PerformanceArtists", "Magic", "Dancers", "Aerialists", "Impersonators", "Speakers", "Auctioneers", "Musicians", "Producers", "Singers", "Instrumentalists", "Other")`,
656
656
  example: `["DJs", "SoloMusicians"]`,
657
657
  type: "string",
658
658
  isArray: true,