@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,172 @@
1
+ import { applyBashEventLocationRedaction, computeLocationPublicRevealDueAt, defaultLocationRevealAudience, geocodeQueryTextForPublicBashMap, isLocationGloballyPublic, redactBashEventLocation, shouldAutoRevealLocationToPublicNow, viewerCanSeeFullBashLocation, viewerOwnsValidTicketForBash, } from "../locationVisibility.js";
2
+ const baseBash = {
3
+ creatorId: "host-1",
4
+ hideLocation: true,
5
+ locationRevealedAt: null,
6
+ locationRevealAudience: "TicketHolders",
7
+ locationRevealMode: "AtStartTime",
8
+ locationRevealOffsetMins: null,
9
+ startDateTime: new Date("2026-06-15T20:00:00.000Z"),
10
+ };
11
+ describe("viewerOwnsValidTicketForBash", () => {
12
+ it("returns false when no tickets", () => {
13
+ expect(viewerOwnsValidTicketForBash(undefined, "u1")).toBe(false);
14
+ expect(viewerOwnsValidTicketForBash([], "u1")).toBe(false);
15
+ });
16
+ it("returns true for non-cancelled ticket", () => {
17
+ expect(viewerOwnsValidTicketForBash([{ status: "CheckedIn", ownerId: "u1" }], "u1")).toBe(true);
18
+ });
19
+ it("excludes Cancelled and WaitlistPending", () => {
20
+ expect(viewerOwnsValidTicketForBash([{ status: "Cancelled", ownerId: "u1" }], "u1")).toBe(false);
21
+ expect(viewerOwnsValidTicketForBash([{ status: "WaitlistPending", ownerId: "u1" }], "u1")).toBe(false);
22
+ });
23
+ });
24
+ describe("viewerCanSeeFullBashLocation", () => {
25
+ it("allows full location when hideLocation is false", () => {
26
+ expect(viewerCanSeeFullBashLocation({ ...baseBash, hideLocation: false }, { viewerUserId: null, isHostOrOrganizer: false, viewerOwnsValidTicket: false, viewerPublicRsvpGoing: false, viewerHasAcceptedInvitation: false })).toBe(true);
27
+ });
28
+ it("allows when globally revealed", () => {
29
+ expect(viewerCanSeeFullBashLocation({ ...baseBash, locationRevealedAt: new Date() }, { viewerUserId: null, isHostOrOrganizer: false, viewerOwnsValidTicket: false, viewerPublicRsvpGoing: false, viewerHasAcceptedInvitation: false })).toBe(true);
30
+ });
31
+ it("allows host or organizer", () => {
32
+ expect(viewerCanSeeFullBashLocation(baseBash, {
33
+ viewerUserId: "host-1",
34
+ isHostOrOrganizer: true,
35
+ viewerOwnsValidTicket: false,
36
+ viewerPublicRsvpGoing: false,
37
+ viewerHasAcceptedInvitation: false,
38
+ })).toBe(true);
39
+ });
40
+ it("TicketHolders: only ticket", () => {
41
+ const ctxNo = {
42
+ viewerUserId: "u2",
43
+ isHostOrOrganizer: false,
44
+ viewerOwnsValidTicket: false,
45
+ viewerPublicRsvpGoing: true,
46
+ viewerHasAcceptedInvitation: true,
47
+ };
48
+ expect(viewerCanSeeFullBashLocation(baseBash, ctxNo)).toBe(false);
49
+ expect(viewerCanSeeFullBashLocation(baseBash, { ...ctxNo, viewerOwnsValidTicket: true })).toBe(true);
50
+ });
51
+ it("TicketHoldersAndRsvps: Going without ticket", () => {
52
+ const bash = { ...baseBash, locationRevealAudience: "TicketHoldersAndRsvps" };
53
+ expect(viewerCanSeeFullBashLocation(bash, {
54
+ viewerUserId: "u2",
55
+ isHostOrOrganizer: false,
56
+ viewerOwnsValidTicket: false,
57
+ viewerPublicRsvpGoing: true,
58
+ viewerHasAcceptedInvitation: false,
59
+ })).toBe(true);
60
+ });
61
+ it("TicketHoldersAndInvited: accepted invite without ticket or RSVP", () => {
62
+ const bash = { ...baseBash, locationRevealAudience: "TicketHoldersAndInvited" };
63
+ expect(viewerCanSeeFullBashLocation(bash, {
64
+ viewerUserId: "u2",
65
+ isHostOrOrganizer: false,
66
+ viewerOwnsValidTicket: false,
67
+ viewerPublicRsvpGoing: false,
68
+ viewerHasAcceptedInvitation: true,
69
+ })).toBe(true);
70
+ });
71
+ });
72
+ describe("isLocationGloballyPublic", () => {
73
+ it("true when not hidden or revealed at set", () => {
74
+ expect(isLocationGloballyPublic({ ...baseBash, hideLocation: false })).toBe(true);
75
+ expect(isLocationGloballyPublic({ ...baseBash, locationRevealedAt: new Date() })).toBe(true);
76
+ expect(isLocationGloballyPublic(baseBash)).toBe(false);
77
+ });
78
+ });
79
+ describe("redactBashEventLocation / applyBashEventLocationRedaction", () => {
80
+ it("clears sensitive fields", () => {
81
+ const e = {
82
+ id: "e1",
83
+ hideLocation: true,
84
+ location: "a|b",
85
+ street: "1 Main",
86
+ zipCode: "10001",
87
+ venueId: "v1",
88
+ city: "NYC",
89
+ state: "NY",
90
+ country: "US",
91
+ coordinates: [{ id: "c1", latitude: 1, longitude: 2 }],
92
+ };
93
+ const r = redactBashEventLocation(e);
94
+ expect(r.location).toBeNull();
95
+ expect(r.street).toBeNull();
96
+ expect(r.zipCode).toBeNull();
97
+ expect(r.venueId).toBeNull();
98
+ expect(r.coordinates).toEqual([]);
99
+ expect(r.city).toBe("NYC");
100
+ });
101
+ it("apply skips when canSeeFull", () => {
102
+ const e = { hideLocation: true, location: "x", street: "y", zipCode: "z", venueId: null, coordinates: [] };
103
+ const r = applyBashEventLocationRedaction(e, true);
104
+ expect(r.location).toBe("x");
105
+ });
106
+ });
107
+ describe("computeLocationPublicRevealDueAt / shouldAutoRevealLocationToPublicNow", () => {
108
+ it("AtStartTime returns start", () => {
109
+ const start = new Date("2026-01-10T18:00:00.000Z");
110
+ expect(computeLocationPublicRevealDueAt({
111
+ ...baseBash,
112
+ startDateTime: start,
113
+ locationRevealMode: "AtStartTime",
114
+ })).toEqual(start);
115
+ });
116
+ it("CustomOffset subtracts minutes", () => {
117
+ const start = new Date("2026-01-10T18:00:00.000Z");
118
+ const due = computeLocationPublicRevealDueAt({
119
+ ...baseBash,
120
+ startDateTime: start,
121
+ locationRevealMode: "CustomOffset",
122
+ locationRevealOffsetMins: 120,
123
+ });
124
+ expect(due?.getTime()).toBe(start.getTime() - 120 * 60_000);
125
+ });
126
+ it("ManualOnly returns null", () => {
127
+ expect(computeLocationPublicRevealDueAt({
128
+ ...baseBash,
129
+ locationRevealMode: "ManualOnly",
130
+ })).toBeNull();
131
+ });
132
+ it("shouldAutoRevealLocationToPublicNow respects time", () => {
133
+ const bash = {
134
+ ...baseBash,
135
+ locationRevealMode: "AtStartTime",
136
+ startDateTime: new Date("2026-01-10T18:00:00.000Z"),
137
+ };
138
+ expect(shouldAutoRevealLocationToPublicNow(bash, new Date("2026-01-10T17:59:00.000Z"))).toBe(false);
139
+ expect(shouldAutoRevealLocationToPublicNow(bash, new Date("2026-01-10T18:00:00.000Z"))).toBe(true);
140
+ });
141
+ it("ManualOnly never auto-reveals", () => {
142
+ expect(shouldAutoRevealLocationToPublicNow({ ...baseBash, locationRevealMode: "ManualOnly", startDateTime: new Date("2020-01-01") }, new Date("2030-01-01"))).toBe(false);
143
+ });
144
+ });
145
+ describe("defaultLocationRevealAudience", () => {
146
+ it("matches paid vs free default", () => {
147
+ expect(defaultLocationRevealAudience(true)).toBe("TicketHolders");
148
+ expect(defaultLocationRevealAudience(false)).toBe("TicketHoldersAndRsvps");
149
+ });
150
+ });
151
+ describe("geocodeQueryTextForPublicBashMap", () => {
152
+ it("uses city when location redacted", () => {
153
+ expect(geocodeQueryTextForPublicBashMap({
154
+ location: null,
155
+ hideLocation: true,
156
+ locationRevealedAt: null,
157
+ city: "Brooklyn",
158
+ state: "NY",
159
+ country: "US",
160
+ })).toBe("Brooklyn, NY, US");
161
+ });
162
+ it("prefers full location when present", () => {
163
+ expect(geocodeQueryTextForPublicBashMap({
164
+ location: "Venue|123 St",
165
+ hideLocation: true,
166
+ city: "Brooklyn",
167
+ state: "NY",
168
+ country: "US",
169
+ })).toBe("Venue|123 St");
170
+ });
171
+ });
172
+ //# sourceMappingURL=locationVisibility.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locationVisibility.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/locationVisibility.test.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,+BAA+B,EAC/B,gCAAgC,EAChC,6BAA6B,EAC7B,gCAAgC,EAChC,wBAAwB,EACxB,uBAAuB,EACvB,mCAAmC,EACnC,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAElC,MAAM,QAAQ,GAAG;IACf,SAAS,EAAE,QAAQ;IACnB,YAAY,EAAE,IAAI;IAClB,kBAAkB,EAAE,IAAI;IACxB,sBAAsB,EAAE,eAAwB;IAChD,kBAAkB,EAAE,aAAsB;IAC1C,wBAAwB,EAAE,IAAI;IAC9B,aAAa,EAAE,IAAI,IAAI,CAAC,0BAA0B,CAAC;CACpD,CAAC;AAEF,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,4BAA4B,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClE,MAAM,CAAC,4BAA4B,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CACJ,4BAA4B,CAC1B,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EACxC,IAAI,CACL,CACF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CACJ,4BAA4B,CAC1B,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EACxC,IAAI,CACL,CACF,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,MAAM,CACJ,4BAA4B,CAC1B,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAC9C,IAAI,CACL,CACF,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CACJ,4BAA4B,CAC1B,EAAE,GAAG,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,EACpC,EAAE,YAAY,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,qBAAqB,EAAE,KAAK,EAAE,qBAAqB,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CACjJ,CACF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CACJ,4BAA4B,CAC1B,EAAE,GAAG,QAAQ,EAAE,kBAAkB,EAAE,IAAI,IAAI,EAAE,EAAE,EAC/C,EAAE,YAAY,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,qBAAqB,EAAE,KAAK,EAAE,qBAAqB,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CACjJ,CACF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CACJ,4BAA4B,CAAC,QAAQ,EAAE;YACrC,YAAY,EAAE,QAAQ;YACtB,iBAAiB,EAAE,IAAI;YACvB,qBAAqB,EAAE,KAAK;YAC5B,qBAAqB,EAAE,KAAK;YAC5B,2BAA2B,EAAE,KAAK;SACnC,CAAC,CACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,KAAK,GAAG;YACZ,YAAY,EAAE,IAAI;YAClB,iBAAiB,EAAE,KAAK;YACxB,qBAAqB,EAAE,KAAK;YAC5B,qBAAqB,EAAE,IAAI;YAC3B,2BAA2B,EAAE,IAAI;SAClC,CAAC;QACF,MAAM,CAAC,4BAA4B,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClE,MAAM,CACJ,4BAA4B,CAAC,QAAQ,EAAE,EAAE,GAAG,KAAK,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC,CAClF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,IAAI,GAAG,EAAE,GAAG,QAAQ,EAAE,sBAAsB,EAAE,uBAAgC,EAAE,CAAC;QACvF,MAAM,CACJ,4BAA4B,CAAC,IAAI,EAAE;YACjC,YAAY,EAAE,IAAI;YAClB,iBAAiB,EAAE,KAAK;YACxB,qBAAqB,EAAE,KAAK;YAC5B,qBAAqB,EAAE,IAAI;YAC3B,2BAA2B,EAAE,KAAK;SACnC,CAAC,CACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,IAAI,GAAG,EAAE,GAAG,QAAQ,EAAE,sBAAsB,EAAE,yBAAkC,EAAE,CAAC;QACzF,MAAM,CACJ,4BAA4B,CAAC,IAAI,EAAE;YACjC,YAAY,EAAE,IAAI;YAClB,iBAAiB,EAAE,KAAK;YACxB,qBAAqB,EAAE,KAAK;YAC5B,qBAAqB,EAAE,KAAK;YAC5B,2BAA2B,EAAE,IAAI;SAClC,CAAC,CACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,wBAAwB,CAAC,EAAE,GAAG,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClF,MAAM,CAAC,wBAAwB,CAAC,EAAE,GAAG,QAAQ,EAAE,kBAAkB,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7F,MAAM,CAAC,wBAAwB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2DAA2D,EAAE,GAAG,EAAE;IACzE,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,GAAG;YACR,EAAE,EAAE,IAAI;YACR,YAAY,EAAE,IAAI;YAClB,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,OAAO;YAChB,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,KAAK;YACX,KAAK,EAAE,IAAI;YACX,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;SACvD,CAAC;QACF,MAAM,CAAC,GAAG,uBAAuB,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAClC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;QAC3G,MAAM,CAAC,GAAG,+BAA+B,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QACnD,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,wEAAwE,EAAE,GAAG,EAAE;IACtF,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACnD,MAAM,CACJ,gCAAgC,CAAC;YAC/B,GAAG,QAAQ;YACX,aAAa,EAAE,KAAK;YACpB,kBAAkB,EAAE,aAAa;SAClC,CAAC,CACH,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,gCAAgC,CAAC;YAC3C,GAAG,QAAQ;YACX,aAAa,EAAE,KAAK;YACpB,kBAAkB,EAAE,cAAc;YAClC,wBAAwB,EAAE,GAAG;SAC9B,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,GAAG,GAAG,MAAM,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CACJ,gCAAgC,CAAC;YAC/B,GAAG,QAAQ;YACX,kBAAkB,EAAE,YAAY;SACjC,CAAC,CACH,CAAC,QAAQ,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,IAAI,GAAG;YACX,GAAG,QAAQ;YACX,kBAAkB,EAAE,aAAsB;YAC1C,aAAa,EAAE,IAAI,IAAI,CAAC,0BAA0B,CAAC;SACpD,CAAC;QACF,MAAM,CAAC,mCAAmC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpG,MAAM,CAAC,mCAAmC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CACJ,mCAAmC,CACjC,EAAE,GAAG,QAAQ,EAAE,kBAAkB,EAAE,YAAY,EAAE,aAAa,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC,EAAE,EACxF,IAAI,IAAI,CAAC,YAAY,CAAC,CACvB,CACF,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC7C,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,6BAA6B,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAClE,MAAM,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CACJ,gCAAgC,CAAC;YAC/B,QAAQ,EAAE,IAAI;YACd,YAAY,EAAE,IAAI;YAClB,kBAAkB,EAAE,IAAI;YACxB,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,IAAI;YACX,OAAO,EAAE,IAAI;SACd,CAAC,CACH,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CACJ,gCAAgC,CAAC;YAC/B,QAAQ,EAAE,cAAc;YACxB,YAAY,EAAE,IAAI;YAClB,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,IAAI;YACX,OAAO,EAAE,IAAI;SACd,CAAC,CACH,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -25,6 +25,23 @@ export declare const FLYER_PRICING_TIERS: FlyerTierQuote[];
25
25
  /** Default sponsor slot list prices (Utah / intermountain — cents). */
26
26
  export declare const FLYER_SPONSOR_DEFAULT_FEATURED_CENTS = 10000;
27
27
  export declare const FLYER_SPONSOR_DEFAULT_SUPPORTING_CENTS = 5000;
28
+ export declare const FLYER_SPONSOR_BUSINESS_NAME_MAX_LENGTH = 48;
29
+ export declare const FLYER_SPONSOR_CTA_MAX_LENGTH = 72;
30
+ export declare const FLYER_SPONSOR_CATEGORY_MAX_LENGTH = 40;
31
+ export declare const FLYER_SPONSOR_LOGO_ALT_MAX_LENGTH = 90;
32
+ export declare const FLYER_SPONSOR_LOGO_MAX_BYTES: number;
33
+ export declare const FLYER_SPONSOR_ALLOWED_LOGO_MIME_TYPES: readonly ["image/jpeg", "image/png", "image/webp", "image/svg+xml"];
34
+ export interface FlyerSponsorCreativeInput {
35
+ businessName?: string | null;
36
+ ctaText?: string | null;
37
+ category?: string | null;
38
+ logoAltText?: string | null;
39
+ }
40
+ export interface FlyerSponsorCreativeValidationResult {
41
+ ok: boolean;
42
+ errors: string[];
43
+ }
44
+ export declare function validateFlyerSponsorCreative(input: FlyerSponsorCreativeInput): FlyerSponsorCreativeValidationResult;
28
45
  export declare function isEddmFlatSizeCompliant(widthIn: number, heightIn: number): boolean;
29
46
  /**
30
47
  * Map total selected households to a pricing tier. Returns null if below minimum (200 for EDDM Retail).
@@ -35,6 +52,8 @@ export declare function computeHostTotalAfterSponsors(printPostageCostCents: num
35
52
  export declare function distanceMiles(lat1: number, lng1: number, lat2: number, lng2: number): number;
36
53
  /** URL path for the public QR scan endpoint (prepend API origin in clients). */
37
54
  export declare function flyerQrScanPath(campaignQrSlug: string): string;
55
+ /** URL path for sponsor-specific scans on a campaign flyer. */
56
+ export declare function flyerSponsorQrScanPath(campaignQrSlug: string, sponsorQrSlug: string): string;
38
57
  /**
39
58
  * Short slug for campaign QR (avoid ambiguous chars); unique enforced in DB.
40
59
  */
@@ -1 +1 @@
1
- {"version":3,"file":"flyerUtils.d.ts","sourceRoot":"","sources":["../../src/utils/flyerUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,uBAAuB,QAAQ,CAAC;AAC7C,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,uEAAuE;AACvE,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD;;;GAGG;AACH,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,cAAc,GAAG,UAAU,GAAG,OAAO,CAAC;AAEnF,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,mBAAmB,EAAE,cAAc,EA6B/C,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,oCAAoC,QAAQ,CAAC;AAC1D,eAAO,MAAM,sCAAsC,OAAO,CAAC;AAE3D,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIlF;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,eAAe,EAAE,MAAM,GACtB,cAAc,GAAG,IAAI,CAkBvB;AAED,wBAAgB,6BAA6B,CAC3C,qBAAqB,EAAE,MAAM,EAC7B,mBAAmB,EAAE,MAAM,GAC1B,MAAM,CAER;AAID,4DAA4D;AAC5D,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,MAAM,CAUR;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAOlD"}
1
+ {"version":3,"file":"flyerUtils.d.ts","sourceRoot":"","sources":["../../src/utils/flyerUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,uBAAuB,QAAQ,CAAC;AAC7C,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,uEAAuE;AACvE,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD;;;GAGG;AACH,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,cAAc,GAAG,UAAU,GAAG,OAAO,CAAC;AAEnF,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,mBAAmB,EAAE,cAAc,EA6B/C,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,oCAAoC,QAAQ,CAAC;AAC1D,eAAO,MAAM,sCAAsC,OAAO,CAAC;AAC3D,eAAO,MAAM,sCAAsC,KAAK,CAAC;AACzD,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,eAAO,MAAM,iCAAiC,KAAK,CAAC;AACpD,eAAO,MAAM,iCAAiC,KAAK,CAAC;AACpD,eAAO,MAAM,4BAA4B,QAAkB,CAAC;AAC5D,eAAO,MAAM,qCAAqC,qEAKxC,CAAC;AAEX,MAAM,WAAW,yBAAyB;IACxC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,oCAAoC;IACnD,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,yBAAyB,GAC/B,oCAAoC,CAgCtC;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIlF;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,eAAe,EAAE,MAAM,GACtB,cAAc,GAAG,IAAI,CAkBvB;AAED,wBAAgB,6BAA6B,CAC3C,qBAAqB,EAAE,MAAM,EAC7B,mBAAmB,EAAE,MAAM,GAC1B,MAAM,CAER;AAID,4DAA4D;AAC5D,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,MAAM,CAUR;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,+DAA+D;AAC/D,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,GACpB,MAAM,CAIR;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAOlD"}
@@ -46,6 +46,40 @@ export const FLYER_PRICING_TIERS = [
46
46
  /** Default sponsor slot list prices (Utah / intermountain — cents). */
47
47
  export const FLYER_SPONSOR_DEFAULT_FEATURED_CENTS = 10000;
48
48
  export const FLYER_SPONSOR_DEFAULT_SUPPORTING_CENTS = 5000;
49
+ export const FLYER_SPONSOR_BUSINESS_NAME_MAX_LENGTH = 48;
50
+ export const FLYER_SPONSOR_CTA_MAX_LENGTH = 72;
51
+ export const FLYER_SPONSOR_CATEGORY_MAX_LENGTH = 40;
52
+ export const FLYER_SPONSOR_LOGO_ALT_MAX_LENGTH = 90;
53
+ export const FLYER_SPONSOR_LOGO_MAX_BYTES = 2 * 1024 * 1024;
54
+ export const FLYER_SPONSOR_ALLOWED_LOGO_MIME_TYPES = [
55
+ "image/jpeg",
56
+ "image/png",
57
+ "image/webp",
58
+ "image/svg+xml",
59
+ ];
60
+ export function validateFlyerSponsorCreative(input) {
61
+ const errors = [];
62
+ const businessName = input.businessName?.trim() ?? "";
63
+ const ctaText = input.ctaText?.trim() ?? "";
64
+ const category = input.category?.trim() ?? "";
65
+ const logoAltText = input.logoAltText?.trim() ?? "";
66
+ if (!businessName) {
67
+ errors.push("Business name is required.");
68
+ }
69
+ if (businessName.length > FLYER_SPONSOR_BUSINESS_NAME_MAX_LENGTH) {
70
+ errors.push(`Business name must be ${FLYER_SPONSOR_BUSINESS_NAME_MAX_LENGTH} characters or fewer.`);
71
+ }
72
+ if (ctaText.length > FLYER_SPONSOR_CTA_MAX_LENGTH) {
73
+ errors.push(`Sponsor offer must be ${FLYER_SPONSOR_CTA_MAX_LENGTH} characters or fewer.`);
74
+ }
75
+ if (category.length > FLYER_SPONSOR_CATEGORY_MAX_LENGTH) {
76
+ errors.push(`Sponsor category must be ${FLYER_SPONSOR_CATEGORY_MAX_LENGTH} characters or fewer.`);
77
+ }
78
+ if (logoAltText.length > FLYER_SPONSOR_LOGO_ALT_MAX_LENGTH) {
79
+ errors.push(`Logo description must be ${FLYER_SPONSOR_LOGO_ALT_MAX_LENGTH} characters or fewer.`);
80
+ }
81
+ return { ok: errors.length === 0, errors };
82
+ }
49
83
  export function isEddmFlatSizeCompliant(widthIn, heightIn) {
50
84
  const long = Math.max(widthIn, heightIn);
51
85
  const short = Math.min(widthIn, heightIn);
@@ -90,6 +124,10 @@ export function distanceMiles(lat1, lng1, lat2, lng2) {
90
124
  export function flyerQrScanPath(campaignQrSlug) {
91
125
  return `/api/flyer/qr-scan?c=${encodeURIComponent(campaignQrSlug)}`;
92
126
  }
127
+ /** URL path for sponsor-specific scans on a campaign flyer. */
128
+ export function flyerSponsorQrScanPath(campaignQrSlug, sponsorQrSlug) {
129
+ return `${flyerQrScanPath(campaignQrSlug)}&s=${encodeURIComponent(sponsorQrSlug)}`;
130
+ }
93
131
  /**
94
132
  * Short slug for campaign QR (avoid ambiguous chars); unique enforced in DB.
95
133
  */
@@ -1 +1 @@
1
- {"version":3,"file":"flyerUtils.js","sourceRoot":"","sources":["../../src/utils/flyerUtils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flyerUtils.js","sourceRoot":"","sources":["../../src/utils/flyerUtils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Lob addressed postcard economics and trim sizes.
3
+ *
4
+ * Do not use EDDM (`flyerUtils.ts`) per-piece rates for Lob — different product.
5
+ *
6
+ * **Pricing source:** Replace `LOB_ADDRESSED_UNIT_CENTS_BY_SIZE` after verifying
7
+ * print+postage with your Lob dashboard or account rep (note the date in comments
8
+ * when you update). Lob and USPS change rates periodically.
9
+ *
10
+ * **Trim:** Art for Lob must match these exact dimensions (not 6.25×11).
11
+ * See Lob artboard docs for bleed; these are trim sizes used for PDF page size.
12
+ */
13
+ export type LobPostcardSizeKey = "LOB_4X6" | "LOB_6X9" | "LOB_6X11";
14
+ /** Trim width × height in inches (portrait), matching Lob postcard products. */
15
+ export declare const LOB_POSTCARD_TRIM_IN: Record<LobPostcardSizeKey, {
16
+ widthIn: number;
17
+ heightIn: number;
18
+ }>;
19
+ /**
20
+ * Verified placeholder unit cost (print + postage to mailbox) in US cents per piece.
21
+ * TODO: replace with values from Lob dashboard for your mail class (e.g. first class).
22
+ * Last reviewed placeholder: 2026-04-26 — not for production pricing without verification.
23
+ */
24
+ export declare const LOB_ADDRESSED_UNIT_CENTS_BY_SIZE: Record<LobPostcardSizeKey, number>;
25
+ /** Bash platform margin on top of Lob unit cost (basis points, e.g. 1500 = 15%). */
26
+ export declare const LOB_BASH_MARGIN_BPS = 1500;
27
+ export declare function lobSizeToLobApiPostcardSize(size: LobPostcardSizeKey): "4x6" | "6x9" | "6x11";
28
+ /**
29
+ * Total print+postage charged to host before sponsor credits (integer cents).
30
+ */
31
+ export declare function quoteLobAddressedPostageCents(size: LobPostcardSizeKey, recipientCount: number): number;
32
+ /**
33
+ * Product rule: 4×6 does not support paid sponsor slots that require the full sponsor strip.
34
+ * Returns an error message if invalid, or undefined if OK.
35
+ */
36
+ export declare function validateLob4x6WithPaidSponsors(params: {
37
+ lobPostcardSize: LobPostcardSizeKey;
38
+ /** Count of sponsor rows with slotPriceCents > 0 or paidAt set */
39
+ blockingSponsorCount: number;
40
+ }): string | undefined;
41
+ //# sourceMappingURL=lobFlyerUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lobFlyerUtils.d.ts","sourceRoot":"","sources":["../../src/utils/lobFlyerUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAEpE,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,EAAE,MAAM,CACvC,kBAAkB,EAClB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAKtC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,EAAE,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAI/E,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAExC,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,kBAAkB,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAa5F;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,kBAAkB,EACxB,cAAc,EAAE,MAAM,GACrB,MAAM,CAMR;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,eAAe,EAAE,kBAAkB,CAAC;IACpC,kEAAkE;IAClE,oBAAoB,EAAE,MAAM,CAAC;CAC9B,GAAG,MAAM,GAAG,SAAS,CAMrB"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Lob addressed postcard economics and trim sizes.
3
+ *
4
+ * Do not use EDDM (`flyerUtils.ts`) per-piece rates for Lob — different product.
5
+ *
6
+ * **Pricing source:** Replace `LOB_ADDRESSED_UNIT_CENTS_BY_SIZE` after verifying
7
+ * print+postage with your Lob dashboard or account rep (note the date in comments
8
+ * when you update). Lob and USPS change rates periodically.
9
+ *
10
+ * **Trim:** Art for Lob must match these exact dimensions (not 6.25×11).
11
+ * See Lob artboard docs for bleed; these are trim sizes used for PDF page size.
12
+ */
13
+ /** Trim width × height in inches (portrait), matching Lob postcard products. */
14
+ export const LOB_POSTCARD_TRIM_IN = {
15
+ LOB_4X6: { widthIn: 4, heightIn: 6 },
16
+ LOB_6X9: { widthIn: 6, heightIn: 9 },
17
+ LOB_6X11: { widthIn: 6, heightIn: 11 },
18
+ };
19
+ /**
20
+ * Verified placeholder unit cost (print + postage to mailbox) in US cents per piece.
21
+ * TODO: replace with values from Lob dashboard for your mail class (e.g. first class).
22
+ * Last reviewed placeholder: 2026-04-26 — not for production pricing without verification.
23
+ */
24
+ export const LOB_ADDRESSED_UNIT_CENTS_BY_SIZE = {
25
+ LOB_4X6: 85,
26
+ LOB_6X9: 98,
27
+ LOB_6X11: 110,
28
+ };
29
+ /** Bash platform margin on top of Lob unit cost (basis points, e.g. 1500 = 15%). */
30
+ export const LOB_BASH_MARGIN_BPS = 1500;
31
+ export function lobSizeToLobApiPostcardSize(size) {
32
+ switch (size) {
33
+ case "LOB_4X6":
34
+ return "4x6";
35
+ case "LOB_6X9":
36
+ return "6x9";
37
+ case "LOB_6X11":
38
+ return "6x11";
39
+ default: {
40
+ const _x = size;
41
+ return _x;
42
+ }
43
+ }
44
+ }
45
+ /**
46
+ * Total print+postage charged to host before sponsor credits (integer cents).
47
+ */
48
+ export function quoteLobAddressedPostageCents(size, recipientCount) {
49
+ if (recipientCount < 1)
50
+ return 0;
51
+ const unit = LOB_ADDRESSED_UNIT_CENTS_BY_SIZE[size];
52
+ const subtotal = unit * recipientCount;
53
+ const margin = Math.round((subtotal * LOB_BASH_MARGIN_BPS) / 10000);
54
+ return subtotal + margin;
55
+ }
56
+ /**
57
+ * Product rule: 4×6 does not support paid sponsor slots that require the full sponsor strip.
58
+ * Returns an error message if invalid, or undefined if OK.
59
+ */
60
+ export function validateLob4x6WithPaidSponsors(params) {
61
+ if (params.lobPostcardSize !== "LOB_4X6")
62
+ return undefined;
63
+ if (params.blockingSponsorCount > 0) {
64
+ return "4×6 flyers cannot include paid sponsor slots. Choose 6×9 or 6×11, or remove sponsors.";
65
+ }
66
+ return undefined;
67
+ }
68
+ //# sourceMappingURL=lobFlyerUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lobFlyerUtils.js","sourceRoot":"","sources":["../../src/utils/lobFlyerUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,gFAAgF;AAChF,MAAM,CAAC,MAAM,oBAAoB,GAG7B;IACF,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;IACpC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;IACpC,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;CACvC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAuC;IAClF,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,EAAE;IACX,QAAQ,EAAE,GAAG;CACd,CAAC;AAEF,oFAAoF;AACpF,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAExC,MAAM,UAAU,2BAA2B,CAAC,IAAwB;IAClE,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC;QACf,KAAK,UAAU;YACb,OAAO,MAAM,CAAC;QAChB,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,EAAE,GAAU,IAAI,CAAC;YACvB,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAC3C,IAAwB,EACxB,cAAsB;IAEtB,IAAI,cAAc,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,gCAAgC,CAAC,IAAI,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,IAAI,GAAG,cAAc,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,GAAG,mBAAmB,CAAC,GAAG,KAAK,CAAC,CAAC;IACpE,OAAO,QAAQ,GAAG,MAAM,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,8BAA8B,CAAC,MAI9C;IACC,IAAI,MAAM,CAAC,eAAe,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3D,IAAI,MAAM,CAAC,oBAAoB,GAAG,CAAC,EAAE,CAAC;QACpC,OAAO,uFAAuF,CAAC;IACjG,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,66 @@
1
+ import type { LocationRevealAudience, LocationRevealMode } from "@prisma/client";
2
+ /** Minimal bash fields used for location visibility (API + app). */
3
+ export type BashLocationVisibilityEvent = {
4
+ hideLocation?: boolean | null;
5
+ locationRevealedAt?: Date | string | null;
6
+ creatorId: string;
7
+ locationRevealAudience?: LocationRevealAudience | null;
8
+ locationRevealMode?: LocationRevealMode | null;
9
+ locationRevealOffsetMins?: number | null;
10
+ startDateTime?: Date | string | null;
11
+ };
12
+ export type ViewerLocationContext = {
13
+ viewerUserId: string | null | undefined;
14
+ isHostOrOrganizer: boolean;
15
+ viewerOwnsValidTicket: boolean;
16
+ viewerPublicRsvpGoing: boolean;
17
+ viewerHasAcceptedInvitation: boolean;
18
+ };
19
+ /**
20
+ * True if the viewer holds at least one non-cancelled, non-waitlist-pending ticket for this bash.
21
+ * Aligns with bash-app `calculateTicketsBought` filtering.
22
+ */
23
+ export declare function viewerOwnsValidTicketForBash(tickets: ReadonlyArray<{
24
+ status: string;
25
+ ownerId: string | null;
26
+ }> | null | undefined, viewerUserId: string | null | undefined): boolean;
27
+ /**
28
+ * Whether the exact address / pin should be shown to this viewer.
29
+ * Public to everyone once `locationRevealedAt` is set. Otherwise gated by `hideLocation` and audience.
30
+ */
31
+ export declare function viewerCanSeeFullBashLocation(bash: BashLocationVisibilityEvent, ctx: ViewerLocationContext): boolean;
32
+ /** Exact address is visible to everyone (not just gated audience). */
33
+ export declare function isLocationGloballyPublic(bash: BashLocationVisibilityEvent): boolean;
34
+ export type BashEventLocationRedactFields = {
35
+ hideLocation?: boolean | null;
36
+ location?: string | null;
37
+ street?: string | null;
38
+ zipCode?: string | null;
39
+ venueId?: string | null;
40
+ coordinates?: ReadonlyArray<unknown> | null;
41
+ };
42
+ /**
43
+ * Returns a shallow copy with exact-address fields stripped. Caller should only invoke when
44
+ * `viewerCanSeeFullBashLocation` is false and `hideLocation` is true.
45
+ */
46
+ export declare function redactBashEventLocation<T extends BashEventLocationRedactFields>(event: T): T;
47
+ /**
48
+ * Applies redaction when hide-location is on and the viewer cannot see the full address.
49
+ */
50
+ export declare function applyBashEventLocationRedaction<T extends BashEventLocationRedactFields>(event: T, canSeeFull: boolean): T;
51
+ /** When the address becomes public to everyone (null = manual-only or unknown). */
52
+ export declare function computeLocationPublicRevealDueAt(bash: BashLocationVisibilityEvent): Date | null;
53
+ /** Whether cron should set `locationRevealedAt` now (public reveal to all). */
54
+ export declare function shouldAutoRevealLocationToPublicNow(bash: BashLocationVisibilityEvent, now: Date): boolean;
55
+ /** Default audience for the Where step: paid tiers → ticket holders only; else RSVPs+invited path default. */
56
+ export declare function defaultLocationRevealAudience(hasPaidTicketTiers: boolean): LocationRevealAudience;
57
+ /** Address string for Google geocoding on public maps when full `location` is redacted. */
58
+ export declare function geocodeQueryTextForPublicBashMap(event: {
59
+ location?: string | null;
60
+ hideLocation?: boolean | null;
61
+ locationRevealedAt?: Date | string | null;
62
+ city?: string | null;
63
+ state?: string | null;
64
+ country?: string | null;
65
+ }): string | null;
66
+ //# sourceMappingURL=locationVisibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locationVisibility.d.ts","sourceRoot":"","sources":["../../src/utils/locationVisibility.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ const VALID_TICKET_STATUSES_EXCLUDED = new Set(["Cancelled", "WaitlistPending"]);
2
+ /**
3
+ * True if the viewer holds at least one non-cancelled, non-waitlist-pending ticket for this bash.
4
+ * Aligns with bash-app `calculateTicketsBought` filtering.
5
+ */
6
+ export function viewerOwnsValidTicketForBash(tickets, viewerUserId) {
7
+ if (!viewerUserId || !tickets?.length)
8
+ return false;
9
+ return tickets.some((t) => t.ownerId === viewerUserId && !VALID_TICKET_STATUSES_EXCLUDED.has(t.status));
10
+ }
11
+ /**
12
+ * Whether the exact address / pin should be shown to this viewer.
13
+ * Public to everyone once `locationRevealedAt` is set. Otherwise gated by `hideLocation` and audience.
14
+ */
15
+ export function viewerCanSeeFullBashLocation(bash, ctx) {
16
+ if (!bash.hideLocation)
17
+ return true;
18
+ if (bash.locationRevealedAt)
19
+ return true;
20
+ if (ctx.isHostOrOrganizer)
21
+ return true;
22
+ if (!ctx.viewerUserId)
23
+ return false;
24
+ const audience = bash.locationRevealAudience ?? "TicketHolders";
25
+ if (ctx.viewerOwnsValidTicket)
26
+ return true;
27
+ if (audience === "TicketHolders")
28
+ return false;
29
+ if (audience === "TicketHoldersAndRsvps" || audience === "TicketHoldersAndInvited") {
30
+ if (ctx.viewerPublicRsvpGoing)
31
+ return true;
32
+ }
33
+ if (audience === "TicketHoldersAndInvited" && ctx.viewerHasAcceptedInvitation) {
34
+ return true;
35
+ }
36
+ return false;
37
+ }
38
+ /** Exact address is visible to everyone (not just gated audience). */
39
+ export function isLocationGloballyPublic(bash) {
40
+ return !bash.hideLocation || bash.locationRevealedAt != null;
41
+ }
42
+ /**
43
+ * Returns a shallow copy with exact-address fields stripped. Caller should only invoke when
44
+ * `viewerCanSeeFullBashLocation` is false and `hideLocation` is true.
45
+ */
46
+ export function redactBashEventLocation(event) {
47
+ const out = { ...event };
48
+ out.location = null;
49
+ out.street = null;
50
+ out.zipCode = null;
51
+ out.venueId = null;
52
+ const coords = out.coordinates;
53
+ if (Array.isArray(coords) && coords.length > 0) {
54
+ out.coordinates = [];
55
+ }
56
+ return out;
57
+ }
58
+ /**
59
+ * Applies redaction when hide-location is on and the viewer cannot see the full address.
60
+ */
61
+ export function applyBashEventLocationRedaction(event, canSeeFull) {
62
+ if (!event.hideLocation || canSeeFull) {
63
+ return event;
64
+ }
65
+ return redactBashEventLocation(event);
66
+ }
67
+ /** When the address becomes public to everyone (null = manual-only or unknown). */
68
+ export function computeLocationPublicRevealDueAt(bash) {
69
+ if (!bash.hideLocation || bash.locationRevealedAt)
70
+ return null;
71
+ const start = bash.startDateTime ? new Date(bash.startDateTime) : null;
72
+ if (!start || Number.isNaN(start.getTime()))
73
+ return null;
74
+ const mode = bash.locationRevealMode ?? "AtStartTime";
75
+ if (mode === "ManualOnly")
76
+ return null;
77
+ if (mode === "AtStartTime")
78
+ return start;
79
+ if (mode === "CustomOffset") {
80
+ const mins = bash.locationRevealOffsetMins;
81
+ if (mins == null || mins < 0)
82
+ return null;
83
+ return new Date(start.getTime() - mins * 60_000);
84
+ }
85
+ return null;
86
+ }
87
+ /** Whether cron should set `locationRevealedAt` now (public reveal to all). */
88
+ export function shouldAutoRevealLocationToPublicNow(bash, now) {
89
+ if (!bash.hideLocation || bash.locationRevealedAt)
90
+ return false;
91
+ const mode = bash.locationRevealMode ?? "AtStartTime";
92
+ if (mode === "ManualOnly")
93
+ return false;
94
+ const due = computeLocationPublicRevealDueAt(bash);
95
+ if (!due)
96
+ return false;
97
+ return now.getTime() >= due.getTime();
98
+ }
99
+ /** Default audience for the Where step: paid tiers → ticket holders only; else RSVPs+invited path default. */
100
+ export function defaultLocationRevealAudience(hasPaidTicketTiers) {
101
+ return hasPaidTicketTiers ? "TicketHolders" : "TicketHoldersAndRsvps";
102
+ }
103
+ /** Address string for Google geocoding on public maps when full `location` is redacted. */
104
+ export function geocodeQueryTextForPublicBashMap(event) {
105
+ const loc = event.location?.trim();
106
+ if (loc)
107
+ return loc;
108
+ if (event.hideLocation && !event.locationRevealedAt) {
109
+ const parts = [event.city, event.state, event.country].filter((p) => Boolean(p && String(p).trim()));
110
+ return parts.length ? parts.join(", ") : null;
111
+ }
112
+ return null;
113
+ }
114
+ //# sourceMappingURL=locationVisibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locationVisibility.js","sourceRoot":"","sources":["../../src/utils/locationVisibility.ts"],"names":[],"mappings":"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"}
@@ -434,13 +434,13 @@ export const serviceSpecificInfoAI = {
434
434
  EventServices: {},
435
435
  EntertainmentServices: {
436
436
  yearsOfExperience: {
437
- description: `yearsOfExperience (use "LessThanOneYear", "OneToTwoYears", "ThreeToFiveYears", or "FivePlusYears" - for "10+ years" or similar use "FivePlusYears")`,
437
+ description: `yearsOfExperience (use "LessThanOneYear", "OneToThreeYears", "ThreeToFiveYears", or "FivePlusYears" - for "10+ years" or similar use "FivePlusYears")`,
438
438
  example: `"FivePlusYears"`,
439
439
  type: "string",
440
440
  isArray: false,
441
441
  },
442
442
  entertainmentServiceTypes: {
443
- description: `entertainmentServiceTypes (array of EntertainmentServiceType enum values: "Emcees", "DJs", "Bands", "SoloMusicians", "Comedy", "VarietyActs", "PerformanceArtists", "Magic", "Dancers", "Aerialists", "Impersonators", "Speakers", "Auctioneers", "Other")`,
443
+ 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")`,
444
444
  example: `["DJs", "SoloMusicians"]`,
445
445
  type: "string",
446
446
  isArray: true,