@bash-app/bash-common 30.220.0 → 30.221.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.
- package/dist/definitions.d.ts +7 -1
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +19 -1
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +147 -0
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +20 -0
- package/dist/extendedSchemas.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/membershipDefinitions.d.ts +4 -2
- package/dist/membershipDefinitions.d.ts.map +1 -1
- package/dist/membershipDefinitions.js +9 -2
- package/dist/membershipDefinitions.js.map +1 -1
- package/dist/utils/__tests__/paymentUtils.test.js +126 -1
- package/dist/utils/__tests__/paymentUtils.test.js.map +1 -1
- package/dist/utils/__tests__/urlUtils.externalLink.test.d.ts +2 -0
- package/dist/utils/__tests__/urlUtils.externalLink.test.d.ts.map +1 -0
- package/dist/utils/__tests__/urlUtils.externalLink.test.js +29 -0
- package/dist/utils/__tests__/urlUtils.externalLink.test.js.map +1 -0
- package/dist/utils/paymentUtils.d.ts +29 -1
- package/dist/utils/paymentUtils.d.ts.map +1 -1
- package/dist/utils/paymentUtils.js +43 -3
- package/dist/utils/paymentUtils.js.map +1 -1
- package/dist/utils/urlUtils.d.ts +11 -0
- package/dist/utils/urlUtils.d.ts.map +1 -1
- package/dist/utils/urlUtils.js +40 -0
- package/dist/utils/urlUtils.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +58 -1
- package/src/definitions.ts +22 -0
- package/src/extendedSchemas.ts +25 -0
- package/src/index.ts +1 -0
- package/src/membershipDefinitions.ts +10 -2
- package/src/utils/__tests__/paymentUtils.test.ts +174 -0
- package/src/utils/__tests__/urlUtils.externalLink.test.ts +36 -0
- package/src/utils/paymentUtils.ts +68 -1
- package/src/utils/urlUtils.ts +45 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paymentUtils.js","sourceRoot":"","sources":["../../src/utils/paymentUtils.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"paymentUtils.js","sourceRoot":"","sources":["../../src/utils/paymentUtils.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/urlUtils.d.ts
CHANGED
|
@@ -41,4 +41,15 @@ export declare function executeWithTimeout<T>(operation: () => Promise<T>, timeo
|
|
|
41
41
|
* @param onTimeout - Callback when redirect times out
|
|
42
42
|
*/
|
|
43
43
|
export declare function redirectToStripeWithTimeout(redirectUrl: string, timeoutMs?: number, onTimeout?: () => void): void;
|
|
44
|
+
/**
|
|
45
|
+
* Builds a safe absolute URL for `<a href>` from user-edited service/profile links.
|
|
46
|
+
*
|
|
47
|
+
* Browsers resolve `href="example.com"` as a **relative** URL (under the current path),
|
|
48
|
+
* not `https://example.com`, so bare domains must be prefixed with `https://`.
|
|
49
|
+
* Stores sometimes save Instagram as `@handle` instead of a full URL — those are mapped
|
|
50
|
+
* to `https://www.instagram.com/{handle}/`.
|
|
51
|
+
*
|
|
52
|
+
* @returns `"#"` when the value is empty or unsafe (`javascript:`, `data:`).
|
|
53
|
+
*/
|
|
54
|
+
export declare function toExternalLinkHref(raw: string | undefined | null): string;
|
|
44
55
|
//# sourceMappingURL=urlUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"urlUtils.d.ts","sourceRoot":"","sources":["../../src/utils/urlUtils.ts"],"names":[],"mappings":"AAMA,wBAAgB,eAAe,IAAI,MAAM,CAKxC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAYvC;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAO3E;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CA0B7E;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CA0BvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd,MAAM,CAIR;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,SAAS,GAAE,MAAmC,EAC9C,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACxD,UAAU,GAAE,MAAU,GACrB,OAAO,CAAC,CAAC,CAAC,CA2BZ;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,EACnB,SAAS,GAAE,MAAmC,EAC9C,SAAS,CAAC,EAAE,MAAM,IAAI,GACrB,IAAI,CA8BN"}
|
|
1
|
+
{"version":3,"file":"urlUtils.d.ts","sourceRoot":"","sources":["../../src/utils/urlUtils.ts"],"names":[],"mappings":"AAMA,wBAAgB,eAAe,IAAI,MAAM,CAKxC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAYvC;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAO3E;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CA0B7E;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CA0BvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd,MAAM,CAIR;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,SAAS,GAAE,MAAmC,EAC9C,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACxD,UAAU,GAAE,MAAU,GACrB,OAAO,CAAC,CAAC,CAAC,CA2BZ;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,EACnB,SAAS,GAAE,MAAmC,EAC9C,SAAS,CAAC,EAAE,MAAM,IAAI,GACrB,IAAI,CA8BN;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAiCzE"}
|
package/dist/utils/urlUtils.js
CHANGED
|
@@ -161,4 +161,44 @@ export function redirectToStripeWithTimeout(redirectUrl, timeoutMs = STRIPE_REDI
|
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Builds a safe absolute URL for `<a href>` from user-edited service/profile links.
|
|
166
|
+
*
|
|
167
|
+
* Browsers resolve `href="example.com"` as a **relative** URL (under the current path),
|
|
168
|
+
* not `https://example.com`, so bare domains must be prefixed with `https://`.
|
|
169
|
+
* Stores sometimes save Instagram as `@handle` instead of a full URL — those are mapped
|
|
170
|
+
* to `https://www.instagram.com/{handle}/`.
|
|
171
|
+
*
|
|
172
|
+
* @returns `"#"` when the value is empty or unsafe (`javascript:`, `data:`).
|
|
173
|
+
*/
|
|
174
|
+
export function toExternalLinkHref(raw) {
|
|
175
|
+
if (raw == null) {
|
|
176
|
+
return "#";
|
|
177
|
+
}
|
|
178
|
+
const trimmed = raw.trim();
|
|
179
|
+
if (trimmed === "") {
|
|
180
|
+
return "#";
|
|
181
|
+
}
|
|
182
|
+
const lower = trimmed.toLowerCase();
|
|
183
|
+
if (lower.startsWith("javascript:") || lower.startsWith("data:")) {
|
|
184
|
+
return "#";
|
|
185
|
+
}
|
|
186
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
187
|
+
return trimmed;
|
|
188
|
+
}
|
|
189
|
+
if (/^mailto:/i.test(trimmed) || /^tel:/i.test(trimmed)) {
|
|
190
|
+
return trimmed;
|
|
191
|
+
}
|
|
192
|
+
// Instagram @handle (often stored as a handle instead of a URL)
|
|
193
|
+
if (trimmed.startsWith("@")) {
|
|
194
|
+
const handle = trimmed
|
|
195
|
+
.slice(1)
|
|
196
|
+
.trim()
|
|
197
|
+
.replace(/^@+/, "");
|
|
198
|
+
if (/^[a-zA-Z0-9._]{1,30}$/.test(handle)) {
|
|
199
|
+
return `https://www.instagram.com/${handle}/`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return `https://${trimmed}`;
|
|
203
|
+
}
|
|
164
204
|
//# sourceMappingURL=urlUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"urlUtils.js","sourceRoot":"","sources":["../../src/utils/urlUtils.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"urlUtils.js","sourceRoot":"","sources":["../../src/utils/urlUtils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/prisma/schema.prisma
CHANGED
|
@@ -767,6 +767,7 @@ model BashEvent {
|
|
|
767
767
|
sponsorBookingRequests SponsorBookingRequest[] @relation("SponsorBookingEvent")
|
|
768
768
|
sponsorships SponsoredEvent[]
|
|
769
769
|
tickets Ticket[]
|
|
770
|
+
ticketGiftGrants TicketGiftGrant[]
|
|
770
771
|
ticketTiers TicketTier[]
|
|
771
772
|
favoritedBy UserFavorite[]
|
|
772
773
|
userPromoCodeRedemption UserPromoCodeRedemption[]
|
|
@@ -1355,7 +1356,44 @@ model TicketTier {
|
|
|
1355
1356
|
// Checkout timer: host-configurable per-tier timeout (minutes). null = no timer.
|
|
1356
1357
|
checkoutTimeoutMinutes Int?
|
|
1357
1358
|
|
|
1359
|
+
/// When true, BashPass subscribers may redeem this tier (subject to caps).
|
|
1360
|
+
bashPassEligible Boolean @default(false)
|
|
1361
|
+
/// Max BashPass redemptions for this tier (null = unlimited within tier capacity).
|
|
1362
|
+
bashPassMaxRedemptions Int?
|
|
1363
|
+
|
|
1358
1364
|
@@unique([bashEventId, title])
|
|
1365
|
+
ticketGiftGrants TicketGiftGrant[]
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
enum TicketGiftGrantStatus {
|
|
1369
|
+
Active
|
|
1370
|
+
Revoked
|
|
1371
|
+
Exhausted
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
/// Host-initiated comp block: N tickets allocated to one person to distribute.
|
|
1375
|
+
model TicketGiftGrant {
|
|
1376
|
+
id String @id @default(cuid())
|
|
1377
|
+
bashEventId String
|
|
1378
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
1379
|
+
ticketTierId String
|
|
1380
|
+
ticketTier TicketTier @relation(fields: [ticketTierId], references: [id], onDelete: Restrict)
|
|
1381
|
+
grantedByUserId String
|
|
1382
|
+
grantedBy User @relation("GiftGrantsIssued", fields: [grantedByUserId], references: [id], onDelete: Cascade)
|
|
1383
|
+
grantedToUserId String
|
|
1384
|
+
grantedTo User @relation("GiftGrantsReceived", fields: [grantedToUserId], references: [id], onDelete: Cascade)
|
|
1385
|
+
quantity Int
|
|
1386
|
+
/// 0 = grantee may not re-gift; 1 = one level of re-gifting; null = unlimited
|
|
1387
|
+
maxGiftHops Int? @default(1)
|
|
1388
|
+
status TicketGiftGrantStatus @default(Active)
|
|
1389
|
+
note String?
|
|
1390
|
+
expiresAt DateTime?
|
|
1391
|
+
createdAt DateTime @default(now())
|
|
1392
|
+
tickets Ticket[]
|
|
1393
|
+
|
|
1394
|
+
@@index([bashEventId])
|
|
1395
|
+
@@index([grantedToUserId])
|
|
1396
|
+
@@index([grantedByUserId])
|
|
1359
1397
|
}
|
|
1360
1398
|
|
|
1361
1399
|
model Ticket {
|
|
@@ -1396,10 +1434,16 @@ model Ticket {
|
|
|
1396
1434
|
qrToken String?
|
|
1397
1435
|
|
|
1398
1436
|
// BashPoints Purchase Tracking
|
|
1399
|
-
purchaseType String? // "USD", "BashPoints", "Free", "Comp"
|
|
1437
|
+
purchaseType String? // "USD", "BashPoints", "Free", "Comp", "BashPass"
|
|
1400
1438
|
pointsPaid Int? // BashPoints amount paid (if purchaseType="BashPoints")
|
|
1401
1439
|
stripePurchaseId String? // Stripe payment ID (if purchaseType="USD")
|
|
1402
1440
|
|
|
1441
|
+
/// When set, this ticket was created from a host `TicketGiftGrant` (comp block for distribution).
|
|
1442
|
+
giftGrantId String?
|
|
1443
|
+
giftGrant TicketGiftGrant? @relation(fields: [giftGrantId], references: [id], onDelete: SetNull)
|
|
1444
|
+
/// 0 = held by original grantee; +1 per accepted gift transfer hop.
|
|
1445
|
+
giftHop Int @default(0)
|
|
1446
|
+
|
|
1403
1447
|
// Marketing attribution (from landing URL → sessionStorage → Stripe metadata → ticket row)
|
|
1404
1448
|
utmSource String?
|
|
1405
1449
|
utmMedium String?
|
|
@@ -1423,6 +1467,7 @@ model Ticket {
|
|
|
1423
1467
|
@@index([bashEventId])
|
|
1424
1468
|
@@index([waitlistUserId])
|
|
1425
1469
|
@@index([attributedArtistId])
|
|
1470
|
+
@@index([giftGrantId])
|
|
1426
1471
|
}
|
|
1427
1472
|
|
|
1428
1473
|
model TicketTransfer {
|
|
@@ -1917,6 +1962,10 @@ model User {
|
|
|
1917
1962
|
/// Optional consumer BashPass subscription (waives guest platform fee on tickets; monthly BashPoints).
|
|
1918
1963
|
bashPassStripeSubscriptionId String? @unique
|
|
1919
1964
|
bashPassCurrentPeriodEnd DateTime?
|
|
1965
|
+
/// Stripe price tier for standalone BashPass; effective tier may also come from membership bundle.
|
|
1966
|
+
bashPassTier BashPassTier?
|
|
1967
|
+
bashPassEventsUsedThisMonth Int @default(0)
|
|
1968
|
+
bashPassMonthlyCapResetAt DateTime?
|
|
1920
1969
|
isVerified Boolean @default(false)
|
|
1921
1970
|
verificationMethod String?
|
|
1922
1971
|
verifiedAt DateTime?
|
|
@@ -2053,6 +2102,8 @@ model User {
|
|
|
2053
2102
|
transfersTo TicketTransfer[] @relation("TransfersTo")
|
|
2054
2103
|
ticketTransferInvitesFrom TicketTransferInvite[] @relation("TicketTransferInviteFrom")
|
|
2055
2104
|
ticketTransferInvitesToUser TicketTransferInvite[] @relation("TicketTransferInviteToUser")
|
|
2105
|
+
giftGrantsIssued TicketGiftGrant[] @relation("GiftGrantsIssued")
|
|
2106
|
+
giftGrantsReceived TicketGiftGrant[] @relation("GiftGrantsReceived")
|
|
2056
2107
|
unblocksReceived UnblockedUserHistory[] @relation("UserUnblocksReceived")
|
|
2057
2108
|
unblocksCreated UnblockedUserHistory[] @relation("UserUnblocksMade")
|
|
2058
2109
|
suspendedBy User? @relation("SuspendedUsers", fields: [suspendedById], references: [id])
|
|
@@ -6196,6 +6247,12 @@ enum CommentStatus {
|
|
|
6196
6247
|
Trash
|
|
6197
6248
|
}
|
|
6198
6249
|
|
|
6250
|
+
enum BashPassTier {
|
|
6251
|
+
LITE
|
|
6252
|
+
STANDARD
|
|
6253
|
+
UNLIMITED
|
|
6254
|
+
}
|
|
6255
|
+
|
|
6199
6256
|
enum MembershipTier {
|
|
6200
6257
|
Basic
|
|
6201
6258
|
Premium
|
package/src/definitions.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BashEventType,
|
|
3
|
+
BashPassTier,
|
|
3
4
|
BracketType,
|
|
4
5
|
CompetitionType,
|
|
5
6
|
Contact,
|
|
@@ -45,6 +46,27 @@ export const STRIPE_API_TIMEOUT_MS = 15000 as const; // 15 seconds for API calls
|
|
|
45
46
|
*/
|
|
46
47
|
export const DEFAULT_IDEA_INTEREST_THRESHOLD = 35 as const;
|
|
47
48
|
|
|
49
|
+
/** Monthly BashPass event redemption caps by tier (`null` = unlimited). */
|
|
50
|
+
export const BASH_PASS_EVENT_CAPS: Record<BashPassTier, number | null> = {
|
|
51
|
+
[BashPassTier.LITE]: 4,
|
|
52
|
+
[BashPassTier.STANDARD]: 8,
|
|
53
|
+
[BashPassTier.UNLIMITED]: null,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** For comparing which BashPass tier is higher when user has membership bundle + standalone. */
|
|
57
|
+
export const BASH_PASS_TIER_RANK: Record<BashPassTier, number> = {
|
|
58
|
+
[BashPassTier.LITE]: 1,
|
|
59
|
+
[BashPassTier.STANDARD]: 2,
|
|
60
|
+
[BashPassTier.UNLIMITED]: 3,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Env var names for Stripe Price IDs (read at runtime in API). */
|
|
64
|
+
export const BASH_PASS_PRICE_ENV_KEYS: Record<BashPassTier, string> = {
|
|
65
|
+
[BashPassTier.LITE]: "STRIPE_BASH_PASS_LITE_PRICE_ID",
|
|
66
|
+
[BashPassTier.STANDARD]: "STRIPE_BASH_PASS_STANDARD_PRICE_ID",
|
|
67
|
+
[BashPassTier.UNLIMITED]: "STRIPE_BASH_PASS_UNLIMITED_PRICE_ID",
|
|
68
|
+
};
|
|
69
|
+
|
|
48
70
|
export const URL_PARAMS_BASH_EVENT_ID = "bashEventId" as const;
|
|
49
71
|
export const URL_PARAMS_BASH_EVENT_TITLE = "bashEventTitle" as const;
|
|
50
72
|
export const URL_PARAMS_BASH_EVENT_DESC = "bashEventDesc" as const;
|
package/src/extendedSchemas.ts
CHANGED
|
@@ -190,6 +190,12 @@ export const PRIVATE_USER_ACCOUNT_TO_SELECT = {
|
|
|
190
190
|
zipCode: true,
|
|
191
191
|
country: true,
|
|
192
192
|
phone: true,
|
|
193
|
+
membershipExpiresAt: true,
|
|
194
|
+
bashPassStripeSubscriptionId: true,
|
|
195
|
+
bashPassCurrentPeriodEnd: true,
|
|
196
|
+
bashPassTier: true,
|
|
197
|
+
bashPassEventsUsedThisMonth: true,
|
|
198
|
+
bashPassMonthlyCapResetAt: true,
|
|
193
199
|
userSubscription: {
|
|
194
200
|
select: PRIVATE_USER_SUBSCRIPTION_DATA_TO_SELECT,
|
|
195
201
|
},
|
|
@@ -1157,6 +1163,25 @@ export interface TicketTierExt extends TicketTier {
|
|
|
1157
1163
|
promoCodes: BashEventPromoCode[];
|
|
1158
1164
|
}
|
|
1159
1165
|
|
|
1166
|
+
/** Host / API include for `TicketGiftGrant` lists and create responses. */
|
|
1167
|
+
export const TICKET_GIFT_GRANT_DATA_TO_INCLUDE = {
|
|
1168
|
+
ticketTier: { select: { id: true, title: true, bashEventId: true } },
|
|
1169
|
+
grantedBy: { select: FRONT_END_USER_DATA_TO_SELECT },
|
|
1170
|
+
grantedTo: { select: FRONT_END_USER_DATA_TO_SELECT },
|
|
1171
|
+
tickets: {
|
|
1172
|
+
select: {
|
|
1173
|
+
id: true,
|
|
1174
|
+
status: true,
|
|
1175
|
+
giftHop: true,
|
|
1176
|
+
ownerId: true,
|
|
1177
|
+
},
|
|
1178
|
+
},
|
|
1179
|
+
} satisfies Prisma.TicketGiftGrantInclude;
|
|
1180
|
+
|
|
1181
|
+
export type TicketGiftGrantExt = Prisma.TicketGiftGrantGetPayload<{
|
|
1182
|
+
include: typeof TICKET_GIFT_GRANT_DATA_TO_INCLUDE;
|
|
1183
|
+
}>;
|
|
1184
|
+
|
|
1160
1185
|
export interface TicketExt extends Ticket {
|
|
1161
1186
|
owner: PublicUser;
|
|
1162
1187
|
forUser: PublicUser;
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
// Membership tier definitions and constants shared between frontend and backend
|
|
2
|
-
import { MembershipTier, ReferralTier, CreditTransactionType } from '@prisma/client';
|
|
2
|
+
import { BashPassTier, MembershipTier, ReferralTier, CreditTransactionType } from '@prisma/client';
|
|
3
3
|
|
|
4
|
-
export { MembershipTier, ReferralTier, CreditTransactionType };
|
|
4
|
+
export { BashPassTier, MembershipTier, ReferralTier, CreditTransactionType };
|
|
5
|
+
|
|
6
|
+
/** BashPass tier included with paid membership (standalone Stripe BashPass can stack; higher tier wins). */
|
|
7
|
+
export const MEMBERSHIP_BASH_PASS_TIER: Partial<Record<MembershipTier, BashPassTier>> = {
|
|
8
|
+
[MembershipTier.Premium]: BashPassTier.LITE,
|
|
9
|
+
[MembershipTier.Pro]: BashPassTier.STANDARD,
|
|
10
|
+
[MembershipTier.Elite]: BashPassTier.UNLIMITED,
|
|
11
|
+
[MembershipTier.Legend]: BashPassTier.UNLIMITED,
|
|
12
|
+
};
|
|
5
13
|
|
|
6
14
|
export type MembershipBillingInterval = 'Monthly' | 'Yearly';
|
|
7
15
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Testing business logic without database dependencies
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { BashPassTier, MembershipTier } from "@prisma/client";
|
|
6
7
|
import {
|
|
7
8
|
calculateDiscountFromPromoCode,
|
|
8
9
|
convertCentsToDollars,
|
|
@@ -19,6 +20,10 @@ import {
|
|
|
19
20
|
splitBashPlatformFeeForTicketCheckout,
|
|
20
21
|
computeTicketApplicationFeeAmountCents,
|
|
21
22
|
userHasActiveBashPass,
|
|
23
|
+
getEffectiveBashPassTier,
|
|
24
|
+
getBashPassMonthlyCapRemaining,
|
|
25
|
+
higherBashPassTier,
|
|
26
|
+
membershipGrantsBashPassTier,
|
|
22
27
|
} from "../paymentUtils.js";
|
|
23
28
|
|
|
24
29
|
describe("PaymentUtils - Core Functions", () => {
|
|
@@ -238,4 +243,173 @@ describe("PaymentUtils - Core Functions", () => {
|
|
|
238
243
|
expect(typeof fee).toBe("number");
|
|
239
244
|
});
|
|
240
245
|
});
|
|
246
|
+
|
|
247
|
+
describe("BashPass tier and monthly cap", () => {
|
|
248
|
+
const future = new Date(Date.now() + 86400000 * 30);
|
|
249
|
+
const past = new Date(Date.now() - 86400000);
|
|
250
|
+
|
|
251
|
+
test("higherBashPassTier picks the higher rank", () => {
|
|
252
|
+
expect(higherBashPassTier(BashPassTier.LITE, BashPassTier.STANDARD)).toBe(BashPassTier.STANDARD);
|
|
253
|
+
expect(higherBashPassTier(BashPassTier.STANDARD, BashPassTier.LITE)).toBe(BashPassTier.STANDARD);
|
|
254
|
+
expect(higherBashPassTier(BashPassTier.STANDARD, BashPassTier.UNLIMITED)).toBe(BashPassTier.UNLIMITED);
|
|
255
|
+
expect(higherBashPassTier(null, BashPassTier.LITE)).toBe(BashPassTier.LITE);
|
|
256
|
+
expect(higherBashPassTier(BashPassTier.LITE, null)).toBe(BashPassTier.LITE);
|
|
257
|
+
expect(higherBashPassTier(null, null)).toBeNull();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("membershipGrantsBashPassTier returns null for Basic or expired membership", () => {
|
|
261
|
+
expect(
|
|
262
|
+
membershipGrantsBashPassTier({
|
|
263
|
+
membershipTier: MembershipTier.Basic,
|
|
264
|
+
membershipExpiresAt: future,
|
|
265
|
+
})
|
|
266
|
+
).toBeNull();
|
|
267
|
+
expect(
|
|
268
|
+
membershipGrantsBashPassTier({
|
|
269
|
+
membershipTier: MembershipTier.Premium,
|
|
270
|
+
membershipExpiresAt: past,
|
|
271
|
+
})
|
|
272
|
+
).toBeNull();
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("membershipGrantsBashPassTier maps Premium/Pro/Elite/Legend", () => {
|
|
276
|
+
expect(
|
|
277
|
+
membershipGrantsBashPassTier({ membershipTier: MembershipTier.Premium, membershipExpiresAt: future })
|
|
278
|
+
).toBe(BashPassTier.LITE);
|
|
279
|
+
expect(
|
|
280
|
+
membershipGrantsBashPassTier({ membershipTier: MembershipTier.Pro, membershipExpiresAt: future })
|
|
281
|
+
).toBe(BashPassTier.STANDARD);
|
|
282
|
+
expect(
|
|
283
|
+
membershipGrantsBashPassTier({ membershipTier: MembershipTier.Elite, membershipExpiresAt: future })
|
|
284
|
+
).toBe(BashPassTier.UNLIMITED);
|
|
285
|
+
expect(
|
|
286
|
+
membershipGrantsBashPassTier({ membershipTier: MembershipTier.Legend, membershipExpiresAt: future })
|
|
287
|
+
).toBe(BashPassTier.UNLIMITED);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test("getEffectiveBashPassTier uses standalone subscription when active", () => {
|
|
291
|
+
expect(
|
|
292
|
+
getEffectiveBashPassTier({
|
|
293
|
+
bashPassTier: BashPassTier.STANDARD,
|
|
294
|
+
bashPassStripeSubscriptionId: "sub_1",
|
|
295
|
+
bashPassCurrentPeriodEnd: future,
|
|
296
|
+
membershipTier: MembershipTier.Basic,
|
|
297
|
+
membershipExpiresAt: null,
|
|
298
|
+
})
|
|
299
|
+
).toBe(BashPassTier.STANDARD);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("getEffectiveBashPassTier uses membership when no standalone", () => {
|
|
303
|
+
expect(
|
|
304
|
+
getEffectiveBashPassTier({
|
|
305
|
+
bashPassTier: null,
|
|
306
|
+
bashPassStripeSubscriptionId: null,
|
|
307
|
+
bashPassCurrentPeriodEnd: null,
|
|
308
|
+
membershipTier: MembershipTier.Pro,
|
|
309
|
+
membershipExpiresAt: future,
|
|
310
|
+
})
|
|
311
|
+
).toBe(BashPassTier.STANDARD);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test("getEffectiveBashPassTier takes higher of membership and standalone", () => {
|
|
315
|
+
expect(
|
|
316
|
+
getEffectiveBashPassTier({
|
|
317
|
+
bashPassTier: BashPassTier.LITE,
|
|
318
|
+
bashPassStripeSubscriptionId: "sub_1",
|
|
319
|
+
bashPassCurrentPeriodEnd: future,
|
|
320
|
+
membershipTier: MembershipTier.Pro,
|
|
321
|
+
membershipExpiresAt: future,
|
|
322
|
+
})
|
|
323
|
+
).toBe(BashPassTier.STANDARD);
|
|
324
|
+
expect(
|
|
325
|
+
getEffectiveBashPassTier({
|
|
326
|
+
bashPassTier: BashPassTier.UNLIMITED,
|
|
327
|
+
bashPassStripeSubscriptionId: "sub_1",
|
|
328
|
+
bashPassCurrentPeriodEnd: future,
|
|
329
|
+
membershipTier: MembershipTier.Premium,
|
|
330
|
+
membershipExpiresAt: future,
|
|
331
|
+
})
|
|
332
|
+
).toBe(BashPassTier.UNLIMITED);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
test("getEffectiveBashPassTier returns null when neither applies", () => {
|
|
336
|
+
expect(
|
|
337
|
+
getEffectiveBashPassTier({
|
|
338
|
+
bashPassTier: null,
|
|
339
|
+
bashPassStripeSubscriptionId: null,
|
|
340
|
+
bashPassCurrentPeriodEnd: null,
|
|
341
|
+
membershipTier: MembershipTier.Basic,
|
|
342
|
+
membershipExpiresAt: null,
|
|
343
|
+
})
|
|
344
|
+
).toBeNull();
|
|
345
|
+
expect(
|
|
346
|
+
getEffectiveBashPassTier({
|
|
347
|
+
bashPassTier: BashPassTier.LITE,
|
|
348
|
+
bashPassStripeSubscriptionId: null,
|
|
349
|
+
bashPassCurrentPeriodEnd: null,
|
|
350
|
+
membershipTier: MembershipTier.Basic,
|
|
351
|
+
membershipExpiresAt: null,
|
|
352
|
+
})
|
|
353
|
+
).toBeNull();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test("getBashPassMonthlyCapRemaining is 0 without effective tier", () => {
|
|
357
|
+
expect(
|
|
358
|
+
getBashPassMonthlyCapRemaining({
|
|
359
|
+
bashPassTier: null,
|
|
360
|
+
bashPassEventsUsedThisMonth: 0,
|
|
361
|
+
bashPassStripeSubscriptionId: null,
|
|
362
|
+
bashPassCurrentPeriodEnd: null,
|
|
363
|
+
membershipTier: MembershipTier.Basic,
|
|
364
|
+
membershipExpiresAt: null,
|
|
365
|
+
})
|
|
366
|
+
).toBe(0);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("getBashPassMonthlyCapRemaining is null for UNLIMITED effective tier", () => {
|
|
370
|
+
expect(
|
|
371
|
+
getBashPassMonthlyCapRemaining({
|
|
372
|
+
bashPassTier: null,
|
|
373
|
+
bashPassEventsUsedThisMonth: 100,
|
|
374
|
+
bashPassStripeSubscriptionId: null,
|
|
375
|
+
bashPassCurrentPeriodEnd: null,
|
|
376
|
+
membershipTier: MembershipTier.Elite,
|
|
377
|
+
membershipExpiresAt: future,
|
|
378
|
+
})
|
|
379
|
+
).toBeNull();
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
test("getBashPassMonthlyCapRemaining subtracts usage for capped tiers", () => {
|
|
383
|
+
expect(
|
|
384
|
+
getBashPassMonthlyCapRemaining({
|
|
385
|
+
bashPassTier: BashPassTier.LITE,
|
|
386
|
+
bashPassEventsUsedThisMonth: 2,
|
|
387
|
+
bashPassStripeSubscriptionId: "sub_1",
|
|
388
|
+
bashPassCurrentPeriodEnd: future,
|
|
389
|
+
membershipTier: MembershipTier.Basic,
|
|
390
|
+
membershipExpiresAt: null,
|
|
391
|
+
})
|
|
392
|
+
).toBe(2);
|
|
393
|
+
expect(
|
|
394
|
+
getBashPassMonthlyCapRemaining({
|
|
395
|
+
bashPassTier: BashPassTier.LITE,
|
|
396
|
+
bashPassEventsUsedThisMonth: 4,
|
|
397
|
+
bashPassStripeSubscriptionId: "sub_1",
|
|
398
|
+
bashPassCurrentPeriodEnd: future,
|
|
399
|
+
membershipTier: MembershipTier.Basic,
|
|
400
|
+
membershipExpiresAt: null,
|
|
401
|
+
})
|
|
402
|
+
).toBe(0);
|
|
403
|
+
expect(
|
|
404
|
+
getBashPassMonthlyCapRemaining({
|
|
405
|
+
bashPassTier: BashPassTier.STANDARD,
|
|
406
|
+
bashPassEventsUsedThisMonth: 8,
|
|
407
|
+
bashPassStripeSubscriptionId: "sub_1",
|
|
408
|
+
bashPassCurrentPeriodEnd: future,
|
|
409
|
+
membershipTier: MembershipTier.Basic,
|
|
410
|
+
membershipExpiresAt: null,
|
|
411
|
+
})
|
|
412
|
+
).toBe(0);
|
|
413
|
+
});
|
|
414
|
+
});
|
|
241
415
|
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { toExternalLinkHref } from "../urlUtils.js";
|
|
2
|
+
|
|
3
|
+
describe("toExternalLinkHref", () => {
|
|
4
|
+
it("returns # for empty or null", () => {
|
|
5
|
+
expect(toExternalLinkHref(null)).toBe("#");
|
|
6
|
+
expect(toExternalLinkHref(undefined)).toBe("#");
|
|
7
|
+
expect(toExternalLinkHref("")).toBe("#");
|
|
8
|
+
expect(toExternalLinkHref(" ")).toBe("#");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("leaves http(s) URLs unchanged", () => {
|
|
12
|
+
expect(toExternalLinkHref("https://example.com")).toBe("https://example.com");
|
|
13
|
+
expect(toExternalLinkHref("http://example.com/path")).toBe("http://example.com/path");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("prepends https to bare hostnames so browsers do not treat them as relative paths", () => {
|
|
17
|
+
expect(toExternalLinkHref("aavprod.com")).toBe("https://aavprod.com");
|
|
18
|
+
expect(toExternalLinkHref("www.foo.com/bar")).toBe("https://www.foo.com/bar");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("preserves mailto and tel", () => {
|
|
22
|
+
expect(toExternalLinkHref("mailto:a@b.com")).toBe("mailto:a@b.com");
|
|
23
|
+
expect(toExternalLinkHref("tel:+15555551212")).toBe("tel:+15555551212");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("maps Instagram @handle to instagram.com URL", () => {
|
|
27
|
+
expect(toExternalLinkHref("@a_prod_av")).toBe(
|
|
28
|
+
"https://www.instagram.com/a_prod_av/"
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("blocks obvious unsafe schemes", () => {
|
|
33
|
+
expect(toExternalLinkHref("javascript:alert(1)")).toBe("#");
|
|
34
|
+
expect(toExternalLinkHref("data:text/html,hi")).toBe("#");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BashEventPromoCode,
|
|
3
|
+
BashPassTier,
|
|
4
|
+
MembershipTier,
|
|
5
|
+
TicketTier,
|
|
6
|
+
VenuePricingPlan as VenuePricingPlanOption,
|
|
7
|
+
} from "@prisma/client";
|
|
2
8
|
import {
|
|
3
9
|
BASH_EVENT_FEES,
|
|
10
|
+
MEMBERSHIP_BASH_PASS_TIER,
|
|
4
11
|
PARTNER_PAYMENT_FEES,
|
|
5
12
|
SERVICE_BOOKING_FEES,
|
|
6
13
|
STRIPE_PROCESSING_FEE,
|
|
7
14
|
} from "../membershipDefinitions.js";
|
|
8
15
|
import {
|
|
16
|
+
BASH_PASS_EVENT_CAPS,
|
|
17
|
+
BASH_PASS_TIER_RANK,
|
|
9
18
|
NumberOfTicketsForDate,
|
|
10
19
|
PRICE_DOLLARS_AND_CENTS_RATIO,
|
|
11
20
|
} from "../definitions.js";
|
|
@@ -309,6 +318,64 @@ export function userHasActiveBashPass(user: {
|
|
|
309
318
|
return user.bashPassCurrentPeriodEnd > new Date();
|
|
310
319
|
}
|
|
311
320
|
|
|
321
|
+
/** When a user has both membership-included BashPass and a standalone subscription, keep the higher tier. */
|
|
322
|
+
export function higherBashPassTier(
|
|
323
|
+
a: BashPassTier | null,
|
|
324
|
+
b: BashPassTier | null
|
|
325
|
+
): BashPassTier | null {
|
|
326
|
+
if (!a && !b) return null;
|
|
327
|
+
if (!a) return b;
|
|
328
|
+
if (!b) return a;
|
|
329
|
+
return BASH_PASS_TIER_RANK[a] >= BASH_PASS_TIER_RANK[b] ? a : b;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function membershipGrantsBashPassTier(user: {
|
|
333
|
+
membershipTier: MembershipTier;
|
|
334
|
+
membershipExpiresAt: Date | null | undefined;
|
|
335
|
+
}): BashPassTier | null {
|
|
336
|
+
if (user.membershipTier === MembershipTier.Basic) return null;
|
|
337
|
+
if (user.membershipExpiresAt && user.membershipExpiresAt <= new Date()) {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
return MEMBERSHIP_BASH_PASS_TIER[user.membershipTier] ?? null;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** Effective BashPass tier from paid membership bundle and/or active standalone BashPass subscription. */
|
|
344
|
+
export function getEffectiveBashPassTier(user: {
|
|
345
|
+
bashPassTier: BashPassTier | null | undefined;
|
|
346
|
+
bashPassStripeSubscriptionId: string | null | undefined;
|
|
347
|
+
bashPassCurrentPeriodEnd: Date | null | undefined;
|
|
348
|
+
membershipTier: MembershipTier;
|
|
349
|
+
membershipExpiresAt: Date | null | undefined;
|
|
350
|
+
}): BashPassTier | null {
|
|
351
|
+
const fromMembership = membershipGrantsBashPassTier(user);
|
|
352
|
+
const fromStandalone =
|
|
353
|
+
userHasActiveBashPass(user) && user.bashPassTier ? user.bashPassTier : null;
|
|
354
|
+
return higherBashPassTier(fromMembership, fromStandalone);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Remaining BashPass redemptions this billing month.
|
|
359
|
+
* - `0` — no BashPass or cap exhausted
|
|
360
|
+
* - positive integer — spots left
|
|
361
|
+
* - `null` — unlimited tier (no monthly cap)
|
|
362
|
+
*/
|
|
363
|
+
export function getBashPassMonthlyCapRemaining(user: {
|
|
364
|
+
bashPassTier: BashPassTier | null | undefined;
|
|
365
|
+
bashPassEventsUsedThisMonth: number;
|
|
366
|
+
bashPassStripeSubscriptionId: string | null | undefined;
|
|
367
|
+
bashPassCurrentPeriodEnd: Date | null | undefined;
|
|
368
|
+
membershipTier: MembershipTier;
|
|
369
|
+
membershipExpiresAt: Date | null | undefined;
|
|
370
|
+
}): number | null {
|
|
371
|
+
const effective = getEffectiveBashPassTier(user);
|
|
372
|
+
if (!effective) return 0;
|
|
373
|
+
const cap = BASH_PASS_EVENT_CAPS[effective];
|
|
374
|
+
if (cap === null) return null;
|
|
375
|
+
const used = user.bashPassEventsUsedThisMonth ?? 0;
|
|
376
|
+
return Math.max(0, cap - used);
|
|
377
|
+
}
|
|
378
|
+
|
|
312
379
|
// ============================================
|
|
313
380
|
// VENUE PRICING PLANS
|
|
314
381
|
// ============================================
|