@bash-app/bash-common 30.220.0 → 30.222.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 (40) hide show
  1. package/dist/definitions.d.ts +7 -1
  2. package/dist/definitions.d.ts.map +1 -1
  3. package/dist/definitions.js +19 -1
  4. package/dist/definitions.js.map +1 -1
  5. package/dist/extendedSchemas.d.ts +147 -0
  6. package/dist/extendedSchemas.d.ts.map +1 -1
  7. package/dist/extendedSchemas.js +20 -0
  8. package/dist/extendedSchemas.js.map +1 -1
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/membershipDefinitions.d.ts +4 -2
  14. package/dist/membershipDefinitions.d.ts.map +1 -1
  15. package/dist/membershipDefinitions.js +9 -2
  16. package/dist/membershipDefinitions.js.map +1 -1
  17. package/dist/utils/__tests__/paymentUtils.test.js +126 -1
  18. package/dist/utils/__tests__/paymentUtils.test.js.map +1 -1
  19. package/dist/utils/__tests__/urlUtils.externalLink.test.d.ts +2 -0
  20. package/dist/utils/__tests__/urlUtils.externalLink.test.d.ts.map +1 -0
  21. package/dist/utils/__tests__/urlUtils.externalLink.test.js +29 -0
  22. package/dist/utils/__tests__/urlUtils.externalLink.test.js.map +1 -0
  23. package/dist/utils/paymentUtils.d.ts +29 -1
  24. package/dist/utils/paymentUtils.d.ts.map +1 -1
  25. package/dist/utils/paymentUtils.js +43 -3
  26. package/dist/utils/paymentUtils.js.map +1 -1
  27. package/dist/utils/urlUtils.d.ts +11 -0
  28. package/dist/utils/urlUtils.d.ts.map +1 -1
  29. package/dist/utils/urlUtils.js +40 -0
  30. package/dist/utils/urlUtils.js.map +1 -1
  31. package/package.json +1 -1
  32. package/prisma/schema.prisma +96 -1
  33. package/src/definitions.ts +22 -0
  34. package/src/extendedSchemas.ts +25 -0
  35. package/src/index.ts +1 -0
  36. package/src/membershipDefinitions.ts +10 -2
  37. package/src/utils/__tests__/paymentUtils.test.ts +174 -0
  38. package/src/utils/__tests__/urlUtils.externalLink.test.ts +36 -0
  39. package/src/utils/paymentUtils.ts +68 -1
  40. package/src/utils/urlUtils.ts +45 -0
@@ -1,11 +1,20 @@
1
- import { BashEventPromoCode, MembershipTier, TicketTier, VenuePricingPlan as VenuePricingPlanOption } from "@prisma/client";
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
  // ============================================
@@ -206,3 +206,48 @@ export function redirectToStripeWithTimeout(
206
206
  }
207
207
  }
208
208
  }
209
+
210
+ /**
211
+ * Builds a safe absolute URL for `<a href>` from user-edited service/profile links.
212
+ *
213
+ * Browsers resolve `href="example.com"` as a **relative** URL (under the current path),
214
+ * not `https://example.com`, so bare domains must be prefixed with `https://`.
215
+ * Stores sometimes save Instagram as `@handle` instead of a full URL — those are mapped
216
+ * to `https://www.instagram.com/{handle}/`.
217
+ *
218
+ * @returns `"#"` when the value is empty or unsafe (`javascript:`, `data:`).
219
+ */
220
+ export function toExternalLinkHref(raw: string | undefined | null): string {
221
+ if (raw == null) {
222
+ return "#";
223
+ }
224
+ const trimmed = raw.trim();
225
+ if (trimmed === "") {
226
+ return "#";
227
+ }
228
+
229
+ const lower = trimmed.toLowerCase();
230
+ if (lower.startsWith("javascript:") || lower.startsWith("data:")) {
231
+ return "#";
232
+ }
233
+
234
+ if (/^https?:\/\//i.test(trimmed)) {
235
+ return trimmed;
236
+ }
237
+ if (/^mailto:/i.test(trimmed) || /^tel:/i.test(trimmed)) {
238
+ return trimmed;
239
+ }
240
+
241
+ // Instagram @handle (often stored as a handle instead of a URL)
242
+ if (trimmed.startsWith("@")) {
243
+ const handle = trimmed
244
+ .slice(1)
245
+ .trim()
246
+ .replace(/^@+/, "");
247
+ if (/^[a-zA-Z0-9._]{1,30}$/.test(handle)) {
248
+ return `https://www.instagram.com/${handle}/`;
249
+ }
250
+ }
251
+
252
+ return `https://${trimmed}`;
253
+ }