@bash-app/bash-common 30.329.0 → 30.331.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/__tests__/locationShare.test.d.ts +2 -0
- package/dist/__tests__/locationShare.test.d.ts.map +1 -0
- package/dist/__tests__/locationShare.test.js +74 -0
- package/dist/__tests__/locationShare.test.js.map +1 -0
- package/dist/hostPromoter.d.ts +200 -0
- package/dist/hostPromoter.d.ts.map +1 -0
- package/dist/hostPromoter.js +123 -0
- package/dist/hostPromoter.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/locationShare.d.ts +98 -0
- package/dist/locationShare.d.ts.map +1 -0
- package/dist/locationShare.js +95 -0
- package/dist/locationShare.js.map +1 -0
- package/package.json +1 -1
- package/prisma/schema.prisma +125 -0
- package/src/__tests__/locationShare.test.ts +122 -0
- package/src/hostPromoter.ts +321 -0
- package/src/index.ts +2 -0
- package/src/locationShare.ts +173 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event location sharing — consented near-live pins for bash participants
|
|
3
|
+
* (host / organizers / co-hosts, live task assignees, hired providers).
|
|
4
|
+
*/
|
|
5
|
+
import { PUBLIC_TEASER_USER_DATA_TO_SELECT } from "./extendedSchemas.js";
|
|
6
|
+
/** Grace after event end before an Active session auto-expires. */
|
|
7
|
+
export const LOCATION_SHARE_GRACE_MS = 4 * 60 * 60 * 1000;
|
|
8
|
+
/** Fallback TTL when the bash has no start/end times. */
|
|
9
|
+
export const LOCATION_SHARE_DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
10
|
+
/** Fallback window after start when end is missing. */
|
|
11
|
+
export const LOCATION_SHARE_START_FALLBACK_MS = 12 * 60 * 60 * 1000;
|
|
12
|
+
export const LocationShareStatus = {
|
|
13
|
+
Active: "Active",
|
|
14
|
+
Stopped: "Stopped",
|
|
15
|
+
Expired: "Expired",
|
|
16
|
+
};
|
|
17
|
+
/** EventTask statuses that allow location share (assignee). */
|
|
18
|
+
export const LOCATION_SHARE_TASK_STATUSES = [
|
|
19
|
+
"Accepted",
|
|
20
|
+
"Assigned",
|
|
21
|
+
"InProgress",
|
|
22
|
+
];
|
|
23
|
+
/** ServiceBooking statuses that allow location share (provider). */
|
|
24
|
+
export const LOCATION_SHARE_BOOKING_STATUSES = [
|
|
25
|
+
"Approved",
|
|
26
|
+
"Confirmed",
|
|
27
|
+
];
|
|
28
|
+
export function isLocationShareLiveTaskStatus(status) {
|
|
29
|
+
return (status != null &&
|
|
30
|
+
LOCATION_SHARE_TASK_STATUSES.includes(status));
|
|
31
|
+
}
|
|
32
|
+
export function isLocationShareLiveBookingStatus(status) {
|
|
33
|
+
return (status != null &&
|
|
34
|
+
LOCATION_SHARE_BOOKING_STATUSES.includes(status));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* True when the user is the event creator or an AssociatedBash organizer/co-host.
|
|
38
|
+
* Super-users are intentionally not included.
|
|
39
|
+
*/
|
|
40
|
+
export function isLocationShareCrewMember(userId, bash, association) {
|
|
41
|
+
if (!userId)
|
|
42
|
+
return false;
|
|
43
|
+
if (userId === bash.creatorId)
|
|
44
|
+
return true;
|
|
45
|
+
if (association?.isOrganizer === true || association?.isCoHost === true) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Sync eligibility when the caller already knows task/booking flags
|
|
52
|
+
* (e.g. tests or UI that prefetched live assignment). API resolves flags via Prisma.
|
|
53
|
+
*/
|
|
54
|
+
export function isLocationShareEventParticipant(userId, bash, options) {
|
|
55
|
+
if (isLocationShareCrewMember(userId, bash, options?.association ?? null)) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
if (!userId)
|
|
59
|
+
return false;
|
|
60
|
+
return Boolean(options?.hasLiveTaskAssignment ||
|
|
61
|
+
options?.hasLiveHiredBooking ||
|
|
62
|
+
options?.hasActivePromoterRole);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Compute session expiry from bash times (end + grace, else start + 12h, else now + 24h).
|
|
66
|
+
*/
|
|
67
|
+
export function computeLocationShareExpiresAt(bash, now = new Date()) {
|
|
68
|
+
const end = bash.endDateTime ? new Date(bash.endDateTime) : null;
|
|
69
|
+
if (end && !Number.isNaN(end.getTime())) {
|
|
70
|
+
return new Date(end.getTime() + LOCATION_SHARE_GRACE_MS);
|
|
71
|
+
}
|
|
72
|
+
const start = bash.startDateTime ? new Date(bash.startDateTime) : null;
|
|
73
|
+
if (start && !Number.isNaN(start.getTime())) {
|
|
74
|
+
return new Date(start.getTime() + LOCATION_SHARE_START_FALLBACK_MS);
|
|
75
|
+
}
|
|
76
|
+
return new Date(now.getTime() + LOCATION_SHARE_DEFAULT_TTL_MS);
|
|
77
|
+
}
|
|
78
|
+
export const LOCATION_SHARE_SESSION_USER_SELECT = {
|
|
79
|
+
...PUBLIC_TEASER_USER_DATA_TO_SELECT,
|
|
80
|
+
};
|
|
81
|
+
export const LOCATION_SHARE_SESSION_LIST_SELECT = {
|
|
82
|
+
id: true,
|
|
83
|
+
bashEventId: true,
|
|
84
|
+
userId: true,
|
|
85
|
+
status: true,
|
|
86
|
+
startedAt: true,
|
|
87
|
+
expiresAt: true,
|
|
88
|
+
stoppedAt: true,
|
|
89
|
+
lastLat: true,
|
|
90
|
+
lastLng: true,
|
|
91
|
+
lastAccuracyM: true,
|
|
92
|
+
lastUpdatedAt: true,
|
|
93
|
+
user: { select: LOCATION_SHARE_SESSION_USER_SELECT },
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=locationShare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locationShare.js","sourceRoot":"","sources":["../src/locationShare.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/prisma/schema.prisma
CHANGED
|
@@ -1028,6 +1028,7 @@ model BashEvent {
|
|
|
1028
1028
|
sponsorships SponsoredEvent[]
|
|
1029
1029
|
tickets Ticket[]
|
|
1030
1030
|
hostCrmCommunityPromptDismisses HostCrmCommunityPromptDismiss[]
|
|
1031
|
+
hostPromoterPayouts HostPromoterPayout[]
|
|
1031
1032
|
ticketGiftGrants TicketGiftGrant[]
|
|
1032
1033
|
ticketTiers TicketTier[]
|
|
1033
1034
|
tierReleaseReminders TierReleaseReminder[]
|
|
@@ -1135,6 +1136,9 @@ model BashEvent {
|
|
|
1135
1136
|
promotionBlasts PromotionBlast[]
|
|
1136
1137
|
orgPromotionalPackages OrgPromotionalPackage[]
|
|
1137
1138
|
|
|
1139
|
+
/// Crew location sharing (host + organizers/co-hosts opt-in near-live pins)
|
|
1140
|
+
locationShareSessions LocationShareSession[]
|
|
1141
|
+
|
|
1138
1142
|
@@index([templateId])
|
|
1139
1143
|
@@index([parentEventId])
|
|
1140
1144
|
@@index([organizationId])
|
|
@@ -2504,6 +2508,36 @@ model EventNetworkingOptIn {
|
|
|
2504
2508
|
@@index([bashEventId])
|
|
2505
2509
|
}
|
|
2506
2510
|
|
|
2511
|
+
/// Consented near-live location share for bash crew (host / organizers / co-hosts).
|
|
2512
|
+
/// One row per (bashEventId, userId); restart reactivates the same row.
|
|
2513
|
+
/// Follow-ons (tasks, door staff, service bookings) can extend audience rules without a second GPS stack.
|
|
2514
|
+
model LocationShareSession {
|
|
2515
|
+
id String @id @default(cuid())
|
|
2516
|
+
bashEventId String
|
|
2517
|
+
userId String
|
|
2518
|
+
status LocationShareStatus @default(Active)
|
|
2519
|
+
startedAt DateTime @default(now())
|
|
2520
|
+
expiresAt DateTime
|
|
2521
|
+
stoppedAt DateTime?
|
|
2522
|
+
lastLat Float?
|
|
2523
|
+
lastLng Float?
|
|
2524
|
+
lastAccuracyM Float?
|
|
2525
|
+
lastUpdatedAt DateTime?
|
|
2526
|
+
|
|
2527
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
2528
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
2529
|
+
|
|
2530
|
+
@@unique([bashEventId, userId])
|
|
2531
|
+
@@index([bashEventId, status, lastUpdatedAt])
|
|
2532
|
+
@@index([userId, status])
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
enum LocationShareStatus {
|
|
2536
|
+
Active
|
|
2537
|
+
Stopped
|
|
2538
|
+
Expired
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2507
2541
|
/// Host-scoped CRM row for a past guest (VIP tags, notes)
|
|
2508
2542
|
model HostAttendeeProfile {
|
|
2509
2543
|
id String @id @default(cuid())
|
|
@@ -2568,6 +2602,91 @@ model HostCrmCommunityPromptDismiss {
|
|
|
2568
2602
|
@@index([hostUserId])
|
|
2569
2603
|
}
|
|
2570
2604
|
|
|
2605
|
+
/// Host-owned promoter roster (Community Builder → Promoters).
|
|
2606
|
+
/// Distinct from per-bash `Promoter` (promo-code assignment) and hireable Event Promoter bookings.
|
|
2607
|
+
model HostPromoter {
|
|
2608
|
+
id String @id @default(cuid())
|
|
2609
|
+
hostUserId String
|
|
2610
|
+
/// Linked Bash user when known; null for email-only ad-hoc / pending invites.
|
|
2611
|
+
promoterUserId String?
|
|
2612
|
+
/// Normalized lowercase email (required for ad-hoc add and invite).
|
|
2613
|
+
email String
|
|
2614
|
+
displayName String?
|
|
2615
|
+
/// "PromoCode" | "EventPromoter" | "Influencer" | "ContentCreator" | "Model" | "BrandAmbassador"
|
|
2616
|
+
kinds String[] @default([])
|
|
2617
|
+
status HostPromoterStatus @default(Active)
|
|
2618
|
+
notes String? @db.Text
|
|
2619
|
+
defaultBashPointsRewardPerTicket Int?
|
|
2620
|
+
/// Nano / Micro / Macro / Mega when kinds includes Influencer (optional).
|
|
2621
|
+
influencerAudienceTier InfluencerAudienceTier?
|
|
2622
|
+
/// "Manual" | "PastPromoCode" | "PastEventBooking"
|
|
2623
|
+
sources String[] @default([])
|
|
2624
|
+
lastPromotedAt DateTime?
|
|
2625
|
+
createdAt DateTime @default(now())
|
|
2626
|
+
updatedAt DateTime @updatedAt
|
|
2627
|
+
|
|
2628
|
+
host User @relation("HostPromotersOwned", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
2629
|
+
promoter User? @relation("HostPromotersAsPromoter", fields: [promoterUserId], references: [id], onDelete: SetNull)
|
|
2630
|
+
payouts HostPromoterPayout[]
|
|
2631
|
+
|
|
2632
|
+
@@unique([hostUserId, email])
|
|
2633
|
+
@@index([hostUserId, status])
|
|
2634
|
+
@@index([hostUserId, promoterUserId])
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
enum HostPromoterStatus {
|
|
2638
|
+
Active
|
|
2639
|
+
Archived
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
/// Off-platform cash ledger OR platform Stripe Connect payout for host bookkeeping.
|
|
2643
|
+
/// OffPlatform does not move money. Platform uses Checkout + Connect destination transfer.
|
|
2644
|
+
model HostPromoterPayout {
|
|
2645
|
+
id String @id @default(cuid())
|
|
2646
|
+
hostPromoterId String
|
|
2647
|
+
bashEventId String?
|
|
2648
|
+
serviceBookingId String?
|
|
2649
|
+
amountCents Int
|
|
2650
|
+
currency String @default("usd")
|
|
2651
|
+
status HostPromoterPayoutStatus @default(Unpaid)
|
|
2652
|
+
method HostPromoterPayoutMethod @default(Other)
|
|
2653
|
+
channel HostPromoterPayoutChannel @default(OffPlatform)
|
|
2654
|
+
stripeCheckoutSessionId String?
|
|
2655
|
+
stripePaymentIntentId String?
|
|
2656
|
+
platformFeeCents Int @default(0)
|
|
2657
|
+
paidAt DateTime?
|
|
2658
|
+
note String? @db.Text
|
|
2659
|
+
createdAt DateTime @default(now())
|
|
2660
|
+
updatedAt DateTime @updatedAt
|
|
2661
|
+
|
|
2662
|
+
hostPromoter HostPromoter @relation(fields: [hostPromoterId], references: [id], onDelete: Cascade)
|
|
2663
|
+
bashEvent BashEvent? @relation(fields: [bashEventId], references: [id], onDelete: SetNull)
|
|
2664
|
+
serviceBooking ServiceBooking? @relation(fields: [serviceBookingId], references: [id], onDelete: SetNull)
|
|
2665
|
+
|
|
2666
|
+
@@index([hostPromoterId, status])
|
|
2667
|
+
@@index([bashEventId])
|
|
2668
|
+
@@index([serviceBookingId])
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2671
|
+
enum HostPromoterPayoutStatus {
|
|
2672
|
+
Unpaid
|
|
2673
|
+
Paid
|
|
2674
|
+
Cancelled
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
enum HostPromoterPayoutMethod {
|
|
2678
|
+
Venmo
|
|
2679
|
+
Zelle
|
|
2680
|
+
CashApp
|
|
2681
|
+
Cash
|
|
2682
|
+
Other
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2685
|
+
enum HostPromoterPayoutChannel {
|
|
2686
|
+
OffPlatform
|
|
2687
|
+
Platform
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2571
2690
|
/// Host-defined reusable CRM audience filters (tag, cadence, lapsed, cohorts).
|
|
2572
2691
|
model HostCrmSavedSegment {
|
|
2573
2692
|
id String @id @default(cuid())
|
|
@@ -3036,6 +3155,8 @@ model Ticket {
|
|
|
3036
3155
|
invitationId String?
|
|
3037
3156
|
checkoutId String?
|
|
3038
3157
|
checkedInMethod String? @default("manual")
|
|
3158
|
+
/// Door staff user who confirmed cash collected (door_cash path).
|
|
3159
|
+
cashMarkedById String?
|
|
3039
3160
|
waitlistPosition Int?
|
|
3040
3161
|
waitlistJoinedAt DateTime?
|
|
3041
3162
|
waitlistNotifiedAt DateTime?
|
|
@@ -3878,6 +3999,7 @@ model User {
|
|
|
3878
3999
|
|
|
3879
4000
|
associatedBashes AssociatedBash[]
|
|
3880
4001
|
associatedServices AssociatedService[]
|
|
4002
|
+
locationShareSessions LocationShareSession[]
|
|
3881
4003
|
comment BashComment[]
|
|
3882
4004
|
referredTransactions BashCreditTransaction[] @relation("ReferredUser")
|
|
3883
4005
|
creditTransactions BashCreditTransaction[]
|
|
@@ -4045,6 +4167,8 @@ model User {
|
|
|
4045
4167
|
hostAttendeeActivityAsHost HostAttendeeActivity[] @relation("HostAttendeeActivityHost")
|
|
4046
4168
|
hostAttendeeActivityAsTarget HostAttendeeActivity[] @relation("HostAttendeeActivityAttendee")
|
|
4047
4169
|
hostCrmCommunityPromptDismisses HostCrmCommunityPromptDismiss[] @relation("HostCrmCommunityPromptDismissHost")
|
|
4170
|
+
hostPromotersOwned HostPromoter[] @relation("HostPromotersOwned")
|
|
4171
|
+
hostPromotersAsPromoter HostPromoter[] @relation("HostPromotersAsPromoter")
|
|
4048
4172
|
bashListsCreated BashList[]
|
|
4049
4173
|
userItineraries UserItinerary[]
|
|
4050
4174
|
aiRecommendationLogs AIRecommendationLog[]
|
|
@@ -6468,6 +6592,7 @@ model ServiceBooking {
|
|
|
6468
6592
|
sentReminders SentReminder[]
|
|
6469
6593
|
offers ServiceBookingOffer[]
|
|
6470
6594
|
promotionBlastEntitlement PromotionBlastEntitlement?
|
|
6595
|
+
hostPromoterPayouts HostPromoterPayout[]
|
|
6471
6596
|
|
|
6472
6597
|
// Agency routing — all nullable; solo bookings unchanged.
|
|
6473
6598
|
representedByOrganizationId String?
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computeLocationShareExpiresAt,
|
|
3
|
+
isLocationShareCrewMember,
|
|
4
|
+
isLocationShareEventParticipant,
|
|
5
|
+
isLocationShareLiveBookingStatus,
|
|
6
|
+
isLocationShareLiveTaskStatus,
|
|
7
|
+
LOCATION_SHARE_BOOKING_STATUSES,
|
|
8
|
+
LOCATION_SHARE_DEFAULT_TTL_MS,
|
|
9
|
+
LOCATION_SHARE_GRACE_MS,
|
|
10
|
+
LOCATION_SHARE_START_FALLBACK_MS,
|
|
11
|
+
LOCATION_SHARE_TASK_STATUSES,
|
|
12
|
+
} from "../locationShare.js";
|
|
13
|
+
|
|
14
|
+
describe("locationShare helpers", () => {
|
|
15
|
+
describe("isLocationShareCrewMember", () => {
|
|
16
|
+
it("allows the creator", () => {
|
|
17
|
+
expect(
|
|
18
|
+
isLocationShareCrewMember("u1", { creatorId: "u1" }, null)
|
|
19
|
+
).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("allows organizers and co-hosts", () => {
|
|
23
|
+
expect(
|
|
24
|
+
isLocationShareCrewMember("u2", { creatorId: "u1" }, {
|
|
25
|
+
isOrganizer: true,
|
|
26
|
+
})
|
|
27
|
+
).toBe(true);
|
|
28
|
+
expect(
|
|
29
|
+
isLocationShareCrewMember("u3", { creatorId: "u1" }, {
|
|
30
|
+
isCoHost: true,
|
|
31
|
+
})
|
|
32
|
+
).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("rejects attendees", () => {
|
|
36
|
+
expect(
|
|
37
|
+
isLocationShareCrewMember("u4", { creatorId: "u1" }, {
|
|
38
|
+
isOrganizer: false,
|
|
39
|
+
isCoHost: false,
|
|
40
|
+
})
|
|
41
|
+
).toBe(false);
|
|
42
|
+
expect(isLocationShareCrewMember(undefined, { creatorId: "u1" })).toBe(
|
|
43
|
+
false
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("isLocationShareEventParticipant", () => {
|
|
49
|
+
it("allows live task assignees, hired providers, and promoters", () => {
|
|
50
|
+
expect(
|
|
51
|
+
isLocationShareEventParticipant("u2", { creatorId: "u1" }, {
|
|
52
|
+
hasLiveTaskAssignment: true,
|
|
53
|
+
})
|
|
54
|
+
).toBe(true);
|
|
55
|
+
expect(
|
|
56
|
+
isLocationShareEventParticipant("u3", { creatorId: "u1" }, {
|
|
57
|
+
hasLiveHiredBooking: true,
|
|
58
|
+
})
|
|
59
|
+
).toBe(true);
|
|
60
|
+
expect(
|
|
61
|
+
isLocationShareEventParticipant("u5", { creatorId: "u1" }, {
|
|
62
|
+
hasActivePromoterRole: true,
|
|
63
|
+
})
|
|
64
|
+
).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("rejects when no crew and no live role", () => {
|
|
68
|
+
expect(
|
|
69
|
+
isLocationShareEventParticipant("u4", { creatorId: "u1" }, {})
|
|
70
|
+
).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("status allow-lists", () => {
|
|
75
|
+
it("includes live task and booking statuses", () => {
|
|
76
|
+
expect(LOCATION_SHARE_TASK_STATUSES).toEqual([
|
|
77
|
+
"Accepted",
|
|
78
|
+
"Assigned",
|
|
79
|
+
"InProgress",
|
|
80
|
+
]);
|
|
81
|
+
expect(LOCATION_SHARE_BOOKING_STATUSES).toEqual([
|
|
82
|
+
"Approved",
|
|
83
|
+
"Confirmed",
|
|
84
|
+
]);
|
|
85
|
+
expect(isLocationShareLiveTaskStatus("InProgress")).toBe(true);
|
|
86
|
+
expect(isLocationShareLiveTaskStatus("Completed")).toBe(false);
|
|
87
|
+
expect(isLocationShareLiveBookingStatus("Approved")).toBe(true);
|
|
88
|
+
expect(isLocationShareLiveBookingStatus("Pending")).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("computeLocationShareExpiresAt", () => {
|
|
93
|
+
const now = new Date("2026-07-23T12:00:00.000Z");
|
|
94
|
+
|
|
95
|
+
it("uses end + grace when end exists", () => {
|
|
96
|
+
const end = new Date("2026-07-23T18:00:00.000Z");
|
|
97
|
+
const expires = computeLocationShareExpiresAt(
|
|
98
|
+
{ endDateTime: end, startDateTime: null },
|
|
99
|
+
now
|
|
100
|
+
);
|
|
101
|
+
expect(expires.getTime()).toBe(end.getTime() + LOCATION_SHARE_GRACE_MS);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("falls back to start + 12h", () => {
|
|
105
|
+
const start = new Date("2026-07-23T14:00:00.000Z");
|
|
106
|
+
const expires = computeLocationShareExpiresAt(
|
|
107
|
+
{ startDateTime: start, endDateTime: null },
|
|
108
|
+
now
|
|
109
|
+
);
|
|
110
|
+
expect(expires.getTime()).toBe(
|
|
111
|
+
start.getTime() + LOCATION_SHARE_START_FALLBACK_MS
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("falls back to now + 24h", () => {
|
|
116
|
+
const expires = computeLocationShareExpiresAt({}, now);
|
|
117
|
+
expect(expires.getTime()).toBe(
|
|
118
|
+
now.getTime() + LOCATION_SHARE_DEFAULT_TTL_MS
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|