@timardex/cluemart-shared 2.4.0 → 2.5.1

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 (59) hide show
  1. package/dist/{ad-DRI2CIzV.d.ts → ad-C7GbDyUi.d.ts} +1 -1
  2. package/dist/{ad-BSyVGv8q.d.mts → ad-DB9gvcJW.d.mts} +1 -1
  3. package/dist/auth/index.mjs +1 -1
  4. package/dist/{chunk-J72IMZ6E.mjs → chunk-4CQLKIQ6.mjs} +1 -1
  5. package/dist/chunk-4CQLKIQ6.mjs.map +1 -0
  6. package/dist/{chunk-XGGFB7HY.mjs → chunk-A52C6JDT.mjs} +2 -2
  7. package/dist/chunk-A52C6JDT.mjs.map +1 -0
  8. package/dist/{chunk-LHKJJ7SO.mjs → chunk-CQ26N3W6.mjs} +2 -2
  9. package/dist/{chunk-LEAXY4DI.mjs → chunk-H6HM2FOZ.mjs} +3 -3
  10. package/dist/{chunk-ZECVZFK3.mjs → chunk-NEOBS55J.mjs} +4 -3
  11. package/dist/{chunk-ZECVZFK3.mjs.map → chunk-NEOBS55J.mjs.map} +1 -1
  12. package/dist/{chunk-2RHUGVV3.mjs → chunk-WOBG6I4T.mjs} +175 -12
  13. package/dist/chunk-WOBG6I4T.mjs.map +1 -0
  14. package/dist/chunk-YCUKTVVA.mjs +69 -0
  15. package/dist/chunk-YCUKTVVA.mjs.map +1 -0
  16. package/dist/enums/index.cjs +1 -1
  17. package/dist/enums/index.cjs.map +1 -1
  18. package/dist/enums/index.d.mts +2 -2
  19. package/dist/enums/index.d.ts +2 -2
  20. package/dist/enums/index.mjs +1 -1
  21. package/dist/formFields/index.cjs +2 -1
  22. package/dist/formFields/index.cjs.map +1 -1
  23. package/dist/formFields/index.mjs +5 -5
  24. package/dist/graphql/index.cjs +193 -13
  25. package/dist/graphql/index.cjs.map +1 -1
  26. package/dist/graphql/index.d.mts +46 -3
  27. package/dist/graphql/index.d.ts +46 -3
  28. package/dist/graphql/index.mjs +9 -3
  29. package/dist/hooks/index.cjs +25 -14
  30. package/dist/hooks/index.cjs.map +1 -1
  31. package/dist/hooks/index.d.mts +1 -1
  32. package/dist/hooks/index.d.ts +1 -1
  33. package/dist/hooks/index.mjs +7 -7
  34. package/dist/index.cjs +211 -14
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.mts +69 -9
  37. package/dist/index.d.ts +69 -9
  38. package/dist/index.mjs +198 -10
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/region/index.cjs.map +1 -1
  41. package/dist/region/index.mjs +1 -1
  42. package/dist/sharing/index.cjs.map +1 -1
  43. package/dist/sharing/index.mjs +3 -3
  44. package/dist/types/index.d.mts +1 -1
  45. package/dist/types/index.d.ts +1 -1
  46. package/dist/types/index.mjs +1 -1
  47. package/dist/utils/index.cjs +36 -1
  48. package/dist/utils/index.cjs.map +1 -1
  49. package/dist/utils/index.d.mts +23 -6
  50. package/dist/utils/index.d.ts +23 -6
  51. package/dist/utils/index.mjs +17 -5
  52. package/package.json +1 -1
  53. package/dist/chunk-2RHUGVV3.mjs.map +0 -1
  54. package/dist/chunk-7DQBUZXY.mjs +0 -41
  55. package/dist/chunk-7DQBUZXY.mjs.map +0 -1
  56. package/dist/chunk-J72IMZ6E.mjs.map +0 -1
  57. package/dist/chunk-XGGFB7HY.mjs.map +0 -1
  58. /package/dist/{chunk-LHKJJ7SO.mjs.map → chunk-CQ26N3W6.mjs.map} +0 -0
  59. /package/dist/{chunk-LEAXY4DI.mjs.map → chunk-H6HM2FOZ.mjs.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/region/index.ts","../../src/enums/index.ts","../../src/region/constants.ts","../../src/region/useRegionSelectionState.ts","../../src/auth/UserProvider.tsx","../../src/utils/distance.ts"],"sourcesContent":["/**\n * Region selection — shared state hook and NZ region constants.\n *\n * The hook takes `location` as a prop (platform-specific: Expo geolocation on\n * mobile, `navigator.geolocation` on web) and reads `authUser` from the shared\n * auth context. Apps keep a thin local `RegionSelectionProvider` that feeds\n * the platform location and exposes the result via context.\n *\n * `\"use client\"`: required for Next.js; ignored by React Native.\n */\n\"use client\";\n\nexport { REGION_CENTERS, nzRegions, type RegionCenterType } from \"./constants\";\nexport {\n useRegionSelectionState,\n type IUseRegionSelectionReturn,\n type UseRegionSelectionStateArgs,\n} from \"./useRegionSelectionState\";\n","export enum EnumInviteStatus {\n ACCEPTED = \"Accepted\",\n COMPLETED = \"Completed\",\n EXPIRED = \"Expired\",\n NO_STATUS = \"No_Status\",\n PENDING = \"Pending\",\n REJECTED = \"Rejected\",\n UNAVAILABLE = \"Unavailable\",\n}\n\nexport enum EnumChatReportReason {\n INAPPROPRIATE_CONTENT = \"Inappropriate_Content\",\n HARASSMENT_OR_BULLYING = \"Harassment_or_Bullying\",\n HATE_SPEECH = \"Hate_Speech\",\n SPAM_OR_SCAM = \"Spam_or_Scam\",\n VIOLENCE_OR_DANGEROUS_BEHAVIOR = \"Violence_or_Dangerous_Behavior\",\n OTHER = \"Other\",\n}\n\nexport enum EnumChatType {\n GROUP = \"group\",\n PRIVATE = \"private\",\n}\n\nexport enum EnumPaymentMethod {\n CASH = \"cash\",\n EFTPOS = \"eftpos\",\n BANK_TRANSFER = \"bank_transfer\",\n PAYPAL = \"paypal\",\n STRIPE = \"stripe\",\n}\n\nexport enum EnumFoodFlavor {\n SALTY = \"Salty\",\n SAVOURY = \"Savoury\",\n SPICY = \"Spicy\",\n SWEET = \"Sweet\",\n OTHER = \"Not_Applicable\",\n}\n\nexport enum EnumFoodType {\n ADDITIVE_FREE = \"Additive_Free\",\n AIR_FRIED = \"Air_Fried\",\n ALLERGEN_FRIENDLY = \"Allergen_Friendly\",\n ATHLETE_FRIENDLY = \"Athlete_Friendly\",\n BAKED = \"Baked\",\n DAIRY_FREE = \"Dairy_Free\",\n DIABETIC_FRIENDLY = \"Diabetic_Friendly\",\n EGG_FREE = \"Egg_Free\",\n FRESH = \"Fresh\",\n GLUTEN_FREE = \"Gluten_Free\",\n GRILLED = \"Grilled\",\n HALAL = \"Halal\",\n HEART_HEALTHY = \"Heart_Healthy\",\n HIGH_FIBER = \"High_Fiber\",\n HIGH_PROTEIN = \"High_Protein\",\n KETO = \"Keto\",\n KOSHER = \"Kosher\",\n LACTOSE_FREE = \"Lactose_Free\",\n LOW_CALORIE = \"Low_Calorie\",\n LOW_CARB = \"Low_Carb\",\n LOW_FAT = \"Low_Fat\",\n LOW_SODIUM = \"Low_Sodium\",\n NO_ADDED_SUGAR = \"No_Added_Sugar\",\n NO_PRESERVATIVES = \"No_Preservatives\",\n NON_GMO = \"Non_GMO\",\n NUT_FREE = \"Nut_Free\",\n ORGANIC = \"Organic\",\n PALEO = \"Paleo\",\n PLANT_BASED = \"Plant_Based\",\n RAW = \"Raw\",\n SMOKED = \"Smoked\",\n SOY_FREE = \"Soy_Free\",\n SUGAR_FREE = \"Sugar_Free\",\n VEGAN = \"Vegan\",\n VEGETARIAN = \"Vegetarian\",\n}\n\nexport enum EnumResourceType {\n AFFILIATE = \"affiliate\",\n EVENT = \"event\",\n PARTNER = \"partner\",\n SCHOOL = \"school\",\n VENDOR = \"vendor\",\n VISITOR = \"visitor\",\n}\n\n/** `EnumResourceType` values that map to a stored resource — excludes the user-only `VISITOR` type. */\nexport type NonVisitorResourceType = Exclude<\n EnumResourceType,\n EnumResourceType.VISITOR\n>;\n\n/** Type guard that narrows an `EnumResourceType` to `NonVisitorResourceType`. */\nexport function isNonVisitorResourceType(\n value: EnumResourceType,\n): value is NonVisitorResourceType {\n return value !== EnumResourceType.VISITOR;\n}\n\nexport enum EnumEventType {\n MARKET = \"Market\",\n EXPO = \"Expo\",\n FAIR = \"Fair\",\n FESTIVAL = \"Festival\",\n}\n\nexport enum EnumVendorType {\n STALLHOLDER = \"Stallholder\",\n SHOP = \"Shop\",\n}\n\nexport enum EnumPartnerType {\n CHARITY_PARTNER = \"Charity_Partner\",\n MEDIA_PARTNER = \"Media_Partner\",\n SUPPORTING_PARTNER = \"Supporting_Partner\",\n}\n\nexport enum EnumOSPlatform {\n ANDROID = \"android\",\n IOS = \"ios\",\n WEB = \"web\",\n}\n\nexport enum EnumRelationResource {\n EVENT_INVITE_VENDOR = \"event_invite_vendor\",\n EVENT_UPDATE_RELATION_TO_VENDOR = \"event_update_relation_to_vendor\",\n VENDOR_UPDATE_RELATION_TO_EVENT = \"vendor_update_relation_to_event\",\n VENDOR_APPLICATION_TO_EVENT = \"vendor_application_to_event\",\n}\n\nexport enum EnumNotificationResourceType {\n AFFILIATE_REWARD_RECEIVED = \"affiliate_reward_received\",\n APPROVED_AFFILIATE = \"approved_affiliate\",\n APPROVED_EVENT = \"approved_event\",\n APPROVED_PARTNER = \"approved_partner\",\n APPROVED_SCHOOL = \"approved_school\",\n APPROVED_VENDOR = \"approved_vendor\",\n CREATED_EVENT = \"created_event\",\n CREATED_PARTNER = \"created_partner\",\n CREATED_VENDOR = \"created_vendor\",\n CREATED_COUPON = \"created_coupon\",\n DAILY_CLUE_GAME = \"daily_clue_game\",\n DEACTIVATED_EVENT = \"deactivated_event\",\n DEACTIVATED_PARTNER = \"deactivated_partner\",\n DEACTIVATED_VENDOR = \"deactivated_vendor\",\n DECLINED_EVENT = \"declined_event\",\n DECLINED_PARTNER = \"declined_partner\",\n DECLINED_VENDOR = \"declined_vendor\",\n DOWNGRADED_EVENT = \"downgraded_event\",\n DOWNGRADED_PARTNER = \"downgraded_partner\",\n DOWNGRADED_VENDOR = \"downgraded_vendor\",\n EVENT_DATE_CANCELED = \"event_date_canceled\",\n EVENT_INFO_CREATED = \"event_info_created\",\n EVENT_INVITE_VENDOR = EnumRelationResource.EVENT_INVITE_VENDOR,\n EVENT_STARTING_SOON = \"event_starting_soon\",\n EVENT_UPDATE_RELATION_TO_VENDOR = EnumRelationResource.EVENT_UPDATE_RELATION_TO_VENDOR,\n EXPIRATION_REMINDER_EVENT = \"expiration_reminder_event\",\n EXPIRATION_REMINDER_PARTNER = \"expiration_reminder_partner\",\n EXPIRATION_REMINDER_VENDOR = \"expiration_reminder_vendor\",\n NEW_CHAT_MESSAGE = \"new_chat_message\",\n NEW_POST_CREATED = \"new_post_created\",\n REGISTERED_USER_BY_SCHOOL_CODE = \"registered_user_by_school_code\",\n SYSTEM_ALERT = \"system_alert\",\n USER_COUPON_REDEEMED = \"user_coupon_redeemed\",\n VENDOR_APPLICATION_TO_EVENT = EnumRelationResource.VENDOR_APPLICATION_TO_EVENT,\n VENDOR_UPDATE_RELATION_TO_EVENT = EnumRelationResource.VENDOR_UPDATE_RELATION_TO_EVENT,\n}\n\nexport enum EnumNotificationType {\n CHAT = \"chat\",\n EVENT = EnumResourceType.EVENT,\n RELATION = \"relation\",\n SYSTEM = \"system\",\n VENDOR = EnumResourceType.VENDOR,\n}\n\nexport enum EnumRegions {\n All = \"All Regions\",\n Auckland = \"Auckland\",\n BayOfPlentyGisborne = \"Bay of Plenty & Gisborne\",\n CanterburyWestCoast = \"Canterbury & West Coast\",\n HawkesBay = \"Hawke's Bay\",\n ManawatuWanganui = \"Manawatu-Wanganui\",\n MarlboroughNelsonTasman = \"Marlborough & Nelson & Tasman\",\n Northland = \"Northland\",\n Otago = \"Otago\",\n Southland = \"Southland\",\n Taranaki = \"Taranaki\",\n Waikato = \"Waikato\",\n Wellington = \"Wellington\",\n}\n\nexport enum ImageTypeEnum {\n AVATAR = \"avatar\",\n COVER = \"cover\",\n IMAGE = \"image\",\n LOGO = \"logo\",\n}\n\nexport enum EnumUserLicence {\n PRO_EVENT = \"pro_event\",\n PRO_PLUS_EVENT = \"pro_plus_event\",\n PRO_PLUS_VENDOR = \"pro_plus_vendor\",\n PRO_VENDOR = \"pro_vendor\",\n STANDARD_EVENT = \"standard_event\",\n STANDARD_VENDOR = \"standard_vendor\",\n STANDARD_PARTNER = \"standard_partner\",\n STANDARD_AFFILIATE = \"standard_affiliate\",\n STANDARD_SCHOOL = \"standard_school\",\n}\n\nexport enum LicenceExpiryDate {\n ONE_YEAR = \"1 year\",\n ONE_MONTH = \"1 month\",\n TWO_MONTHS = \"2 months\",\n THREE_MONTHS = \"3 months\",\n SIX_MONTHS = \"6 months\",\n}\n\nexport enum EnumUserRole {\n ADMIN = \"admin\",\n AFFILIATE = \"affiliate\",\n CUSTOMER = \"customer\",\n EVENT = \"event\",\n PARTNER = \"partner\",\n SCHOOL = \"school\",\n SUPER_ADMIN = \"super_admin\",\n VENDOR = \"vendor\",\n}\n\nexport enum EnumSocialMedia {\n FACEBOOK = \"facebook\",\n INSTAGRAM = \"instagram\",\n TIKTOK = \"tiktok\",\n TWITTER = \"twitter\",\n WEBSITE = \"website\",\n YOUTUBE = \"youtube\",\n}\n\nexport enum EnumEventDateStatus {\n STARTING_SOON = \"Starting_Soon\",\n STARTED = \"Started\",\n TODAY = \"Today\",\n TOMORROW = \"Tomorrow\",\n THIS_WEEK = \"This_Week\",\n NEXT_WEEK = \"Next_Week\",\n UPCOMING = \"Upcoming\",\n ENDED = \"Ended\",\n RE_SCHEDULED = \"Rescheduled\",\n CANCELED = \"Canceled\",\n INVALID = \"Invalid\",\n}\n\nexport enum EnumSubscriptionStatus {\n ACTIVE = \"active\",\n INACTIVE = \"inactive\",\n CANCELLED = \"cancelled\",\n NO_SUBSCRIPTION = \"no_subscription\",\n PAST_DUE = \"past_due\",\n TRIALING = \"trialing\",\n}\n\nexport enum EnumBillingPeriod {\n MONTHLY_CANCEL_ANYTIME = \"monthly_cancel_anytime\",\n YEARLY_ANNUAL_BILLED = \"yearly_annual_billed\",\n}\n\nexport enum EnumReward {\n MYSTERY_JOY_BOX = \"mystery_joy_box\",\n MYSTERY_SQUEEZE_BOX = \"mystery_squeeze_box\",\n MYSTERY_DELUXE_BOX = \"mystery_deluxe_box\",\n}\n\nexport enum EnumPointType {\n DAILY_CLUE_GAME = \"daily_clue_game\",\n EVENT_CHECKIN = \"event_checkin\",\n INITIAL = \"initial\",\n MINI_QUIZ_GAME = \"mini_quiz_game\",\n ODD_ONE_OUT_GAME = \"odd_one_out_game\",\n}\n","import { EnumRegions } from \"../enums\";\nimport type { Region } from \"../types\";\n\nexport type RegionCenterType = Region & { region: EnumRegions };\n\n/**\n * New Zealand region centres used to derive the nearest region from a\n * location and to map a region selection to map coordinates.\n *\n * The final `EnumRegions.All` entry is the NZ-wide default.\n */\nexport const REGION_CENTERS: RegionCenterType[] = [\n {\n latitude: -36.8485,\n latitudeDelta: 2,\n longitude: 174.7633,\n longitudeDelta: 2,\n region: EnumRegions.Auckland,\n },\n {\n latitude: -38.1387,\n latitudeDelta: 2,\n longitude: 176.2452,\n longitudeDelta: 2,\n region: EnumRegions.BayOfPlentyGisborne,\n },\n {\n latitude: -43.5321,\n latitudeDelta: 2,\n longitude: 172.6362,\n longitudeDelta: 2,\n region: EnumRegions.CanterburyWestCoast,\n },\n {\n latitude: -39.4928,\n latitudeDelta: 2,\n longitude: 176.912,\n longitudeDelta: 2,\n region: EnumRegions.HawkesBay,\n },\n {\n latitude: -40.3523,\n latitudeDelta: 2,\n longitude: 175.6082,\n longitudeDelta: 2,\n region: EnumRegions.ManawatuWanganui,\n },\n {\n latitude: -41.2706,\n latitudeDelta: 2,\n longitude: 173.284,\n longitudeDelta: 2,\n region: EnumRegions.MarlboroughNelsonTasman,\n },\n {\n latitude: -35.7251,\n latitudeDelta: 2,\n longitude: 174.3237,\n longitudeDelta: 2,\n region: EnumRegions.Northland,\n },\n {\n latitude: -45.8788,\n latitudeDelta: 2,\n longitude: 170.5028,\n longitudeDelta: 2,\n region: EnumRegions.Otago,\n },\n {\n latitude: -46.4132,\n latitudeDelta: 2,\n longitude: 168.3538,\n longitudeDelta: 2,\n region: EnumRegions.Southland,\n },\n {\n latitude: -39.0556,\n latitudeDelta: 2,\n longitude: 174.0752,\n longitudeDelta: 2,\n region: EnumRegions.Taranaki,\n },\n {\n latitude: -37.787,\n latitudeDelta: 2,\n longitude: 175.2793,\n longitudeDelta: 2,\n region: EnumRegions.Waikato,\n },\n {\n latitude: -41.2866,\n latitudeDelta: 2,\n longitude: 174.7756,\n longitudeDelta: 2,\n region: EnumRegions.Wellington,\n },\n {\n latitude: -36.8624942, // Default: New Zealand\n latitudeDelta: 2,\n longitude: 174.7450494,\n longitudeDelta: 2,\n region: EnumRegions.All,\n },\n];\n\n/**\n * All selectable NZ regions, excluding the `All` pseudo-region.\n */\nexport const nzRegions = Object.values(EnumRegions).filter(\n (region) => region !== EnumRegions.All,\n);\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useAuthUser } from \"../auth/UserProvider\";\nimport { EnumRegions } from \"../enums\";\nimport { getDistanceInMeters } from \"../utils/distance\";\n\nimport { REGION_CENTERS, type RegionCenterType } from \"./constants\";\n\nexport interface IUseRegionSelectionReturn {\n derivedRegion: RegionCenterType;\n handleResetRegion: () => void;\n handleSelectRegion: (region: EnumRegions) => void;\n loading: boolean;\n selectedRegion: RegionCenterType;\n}\n\ntype DeriveRegionLocation = {\n latitude: number;\n longitude: number;\n};\n\nexport interface UseRegionSelectionStateArgs {\n location: DeriveRegionLocation | null | undefined;\n}\n\n// AUDIT-DECISION: Fail fast if REGION_CENTERS is empty. It is a compile-time\n// constant, so an empty array means the import is broken — an invariant beats\n// a silent undefined that surfaces as a crash three screens away.\nfunction getDefaultRegion(): RegionCenterType {\n const [first] = REGION_CENTERS;\n if (first === undefined) {\n throw new Error(\n \"[useRegionSelectionState] REGION_CENTERS is empty — check the import.\",\n );\n }\n return first;\n}\n\n// AUDIT-DECISION: Pure helper — no side effects, no hook calls.\n// Accepts nullable inputs so callers never need to guard before calling it.\nfunction deriveRegion(\n location: DeriveRegionLocation | null | undefined,\n preferredRegion: EnumRegions | null | undefined,\n): RegionCenterType {\n // 1. Location available — pick nearest valid region.\n if (location) {\n const { latitude, longitude } = location;\n const userCoords: [number, number] = [longitude, latitude];\n\n let nearestRegion: RegionCenterType = getDefaultRegion();\n let nearestDistance = Infinity;\n\n for (const region of REGION_CENTERS) {\n const regionCoords: [number, number] = [\n region.longitude,\n region.latitude,\n ];\n const distance = getDistanceInMeters(userCoords, regionCoords);\n\n // AUDIT-DECISION: Skip non-finite distances — getDistanceInMeters\n // returns Infinity for invalid coords. We do not want a bogus distance to\n // \"win\" the reduce by being the first candidate with the starting Infinity\n // baseline, which was the original bug.\n if (Number.isFinite(distance) && distance < nearestDistance) {\n nearestDistance = distance;\n nearestRegion = region;\n }\n }\n\n // AUDIT-DECISION: If every distance was non-finite (malformed location),\n // fall through to the preference path rather than returning REGION_CENTERS[0]\n // unconditionally. This keeps the fallback chain consistent.\n if (Number.isFinite(nearestDistance)) {\n return nearestRegion;\n }\n }\n\n // 2. No usable location — use the user's stored preference.\n if (preferredRegion) {\n const preferred = REGION_CENTERS.find((r) => r.region === preferredRegion);\n if (preferred) return preferred;\n }\n\n // 3. Last resort — first entry in the array.\n return getDefaultRegion();\n}\n\nexport function useRegionSelectionState({\n location,\n}: UseRegionSelectionStateArgs): IUseRegionSelectionReturn {\n const { authUser } = useAuthUser();\n\n const preferredRegion = authUser?.preferredRegion as\n EnumRegions | null | undefined;\n\n // AUDIT-DECISION: Derive the initial region synchronously so the very first\n // render never sees undefined. deriveRegion is cheap (O(n) array scan).\n const [selectedRegion, setSelectedRegion] = useState<RegionCenterType>(() =>\n deriveRegion(location, preferredRegion ?? null),\n );\n // AUDIT-DECISION: Track the auto-derived default separately from the user's\n // manual selection. derivedRegion always reflects deriveRegion(location,\n // preferredRegion) — nearest location → preferred region → first region — so\n // consumers can detect divergence from the default and reset back to it.\n const [derivedRegion, setDerivedRegion] = useState<RegionCenterType>(() =>\n deriveRegion(location, preferredRegion ?? null),\n );\n const [loading, setLoading] = useState<boolean>(false);\n const [hasManualSelection, setHasManualSelection] = useState<boolean>(false);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // AUDIT-DECISION: Reset manual selection when the authenticated user changes\n // so the auto-derive re-runs for the new identity. Keying on authUser?._id\n // (not the full object) avoids false resets on reference-stable re-renders.\n const prevUserIdRef = useRef<string | undefined>(authUser?._id);\n useEffect(() => {\n if (prevUserIdRef.current !== authUser?._id) {\n prevUserIdRef.current = authUser?._id;\n setHasManualSelection(false);\n }\n }, [authUser?._id]);\n\n // AUDIT-DECISION: Compute and apply the derived region in a single effect —\n // no intermediate memo. This removes the two-cycle race window where the\n // memoised value existed before the state write committed.\n useEffect(() => {\n const derived = deriveRegion(location, preferredRegion ?? null);\n setDerivedRegion(derived);\n\n if (hasManualSelection) return;\n setSelectedRegion(derived);\n }, [location, preferredRegion, hasManualSelection]);\n\n const handleSelectRegion = useCallback((region: EnumRegions) => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n }\n\n const regionCenter =\n REGION_CENTERS.find((r) => r.region === region) ?? getDefaultRegion();\n\n setLoading(true);\n setSelectedRegion(regionCenter);\n setHasManualSelection(true);\n\n timerRef.current = setTimeout(() => {\n setLoading(false);\n timerRef.current = null;\n }, 500);\n }, []);\n\n const handleResetRegion = useCallback(() => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n\n setLoading(false);\n const derived = deriveRegion(location, preferredRegion ?? null);\n setDerivedRegion(derived);\n setSelectedRegion(derived);\n setHasManualSelection(false);\n }, [location, preferredRegion]);\n\n // Cleanup on unmount.\n useEffect(() => {\n return () => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n }\n };\n }, []);\n\n return {\n derivedRegion,\n handleResetRegion,\n handleSelectRegion,\n loading,\n selectedRegion,\n };\n}\n\nexport default useRegionSelectionState;\n","/**\n * Shared React context for auth session state.\n *\n * Apps keep a thin local UserProvider that calls their platform `useAuth`\n * (adapters) and pass the result here as `value`. Import `useAuthUser` from\n * this module (or re-export it) so consumers share one context instance.\n *\n * `\"use client\"`: required for Next.js; ignored by React Native.\n */\n\"use client\";\n\nimport React, { createContext, useContext, type ReactNode } from \"react\";\n\nimport type { UseAuthReturnType } from \"./types\";\n\nconst UserContext = createContext<UseAuthReturnType | undefined>(undefined);\n\n/**\n * Provides auth session value from the app's thin useAuth wrapper.\n */\nexport const UserProvider: React.FC<{\n children: ReactNode;\n value: UseAuthReturnType;\n}> = ({ children, value }) => {\n return <UserContext.Provider value={value}>{children}</UserContext.Provider>;\n};\n\nexport const useAuthUser = (): UseAuthReturnType => {\n const context = useContext(UserContext);\n if (!context) {\n throw new Error(\"useAuthUser must be used within a UserProvider\");\n }\n return context;\n};\n","export function getDistanceInMeters(\n coordinates1: number[],\n coordinates2: number[],\n): number {\n const [lon1, lat1] = coordinates1;\n const [lon2, lat2] = coordinates2;\n\n const values = [lon1, lat1, lon2, lat2];\n if (values.some((v) => !Number.isFinite(v))) {\n return Infinity;\n }\n\n const toRadians = (deg: number) => (deg * Math.PI) / 180;\n const R = 6371000; // Earth radius in meters\n\n const dLat = toRadians(lat2 - lat1);\n const dLon = toRadians(lon2 - lon1);\n const radLat1 = toRadians(lat1);\n const radLat2 = toRadians(lat2);\n\n const a =\n Math.sin(dLat / 2) ** 2 +\n Math.sin(dLon / 2) ** 2 * Math.cos(radLat1) * Math.cos(radLat2);\n\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n return R * c;\n}\n\nexport function getDistanceInKilometers(\n coordinates1: number[],\n coordinates2: number[],\n): number {\n const distanceInMeters = getDistanceInMeters(coordinates1, coordinates2);\n\n if (!Number.isFinite(distanceInMeters)) {\n return Infinity;\n }\n\n return Math.round(distanceInMeters / 1000);\n}\n\nexport function formatDistance(\n coordinates1: number[],\n coordinates2: number[],\n): string {\n const distanceInMeters = getDistanceInMeters(coordinates1, coordinates2);\n\n if (!Number.isFinite(distanceInMeters)) {\n return \"—\";\n }\n\n const roundedMeters = Math.round(distanceInMeters);\n\n if (roundedMeters < 1000) {\n return `${roundedMeters} m`;\n }\n\n return `${Math.round(roundedMeters / 1000)} km`;\n}\n"],"mappings":"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","names":["EnumRegions","import_react"]}
1
+ {"version":3,"sources":["../../src/region/index.ts","../../src/enums/index.ts","../../src/region/constants.ts","../../src/region/useRegionSelectionState.ts","../../src/auth/UserProvider.tsx","../../src/utils/distance.ts"],"sourcesContent":["/**\n * Region selection — shared state hook and NZ region constants.\n *\n * The hook takes `location` as a prop (platform-specific: Expo geolocation on\n * mobile, `navigator.geolocation` on web) and reads `authUser` from the shared\n * auth context. Apps keep a thin local `RegionSelectionProvider` that feeds\n * the platform location and exposes the result via context.\n *\n * `\"use client\"`: required for Next.js; ignored by React Native.\n */\n\"use client\";\n\nexport { REGION_CENTERS, nzRegions, type RegionCenterType } from \"./constants\";\nexport {\n useRegionSelectionState,\n type IUseRegionSelectionReturn,\n type UseRegionSelectionStateArgs,\n} from \"./useRegionSelectionState\";\n","export enum EnumInviteStatus {\n ACCEPTED = \"Accepted\",\n CANCELED = \"Canceled\",\n COMPLETED = \"Completed\",\n EXPIRED = \"Expired\",\n NO_STATUS = \"No_Status\",\n PENDING = \"Pending\",\n REJECTED = \"Rejected\",\n}\n\nexport enum EnumChatReportReason {\n INAPPROPRIATE_CONTENT = \"Inappropriate_Content\",\n HARASSMENT_OR_BULLYING = \"Harassment_or_Bullying\",\n HATE_SPEECH = \"Hate_Speech\",\n SPAM_OR_SCAM = \"Spam_or_Scam\",\n VIOLENCE_OR_DANGEROUS_BEHAVIOR = \"Violence_or_Dangerous_Behavior\",\n OTHER = \"Other\",\n}\n\nexport enum EnumChatType {\n GROUP = \"group\",\n PRIVATE = \"private\",\n}\n\nexport enum EnumPaymentMethod {\n CASH = \"cash\",\n EFTPOS = \"eftpos\",\n BANK_TRANSFER = \"bank_transfer\",\n PAYPAL = \"paypal\",\n STRIPE = \"stripe\",\n}\n\nexport enum EnumFoodFlavor {\n SALTY = \"Salty\",\n SAVOURY = \"Savoury\",\n SPICY = \"Spicy\",\n SWEET = \"Sweet\",\n OTHER = \"Not_Applicable\",\n}\n\nexport enum EnumFoodType {\n ADDITIVE_FREE = \"Additive_Free\",\n AIR_FRIED = \"Air_Fried\",\n ALLERGEN_FRIENDLY = \"Allergen_Friendly\",\n ATHLETE_FRIENDLY = \"Athlete_Friendly\",\n BAKED = \"Baked\",\n DAIRY_FREE = \"Dairy_Free\",\n DIABETIC_FRIENDLY = \"Diabetic_Friendly\",\n EGG_FREE = \"Egg_Free\",\n FRESH = \"Fresh\",\n GLUTEN_FREE = \"Gluten_Free\",\n GRILLED = \"Grilled\",\n HALAL = \"Halal\",\n HEART_HEALTHY = \"Heart_Healthy\",\n HIGH_FIBER = \"High_Fiber\",\n HIGH_PROTEIN = \"High_Protein\",\n KETO = \"Keto\",\n KOSHER = \"Kosher\",\n LACTOSE_FREE = \"Lactose_Free\",\n LOW_CALORIE = \"Low_Calorie\",\n LOW_CARB = \"Low_Carb\",\n LOW_FAT = \"Low_Fat\",\n LOW_SODIUM = \"Low_Sodium\",\n NO_ADDED_SUGAR = \"No_Added_Sugar\",\n NO_PRESERVATIVES = \"No_Preservatives\",\n NON_GMO = \"Non_GMO\",\n NUT_FREE = \"Nut_Free\",\n ORGANIC = \"Organic\",\n PALEO = \"Paleo\",\n PLANT_BASED = \"Plant_Based\",\n RAW = \"Raw\",\n SMOKED = \"Smoked\",\n SOY_FREE = \"Soy_Free\",\n SUGAR_FREE = \"Sugar_Free\",\n VEGAN = \"Vegan\",\n VEGETARIAN = \"Vegetarian\",\n}\n\nexport enum EnumResourceType {\n AFFILIATE = \"affiliate\",\n EVENT = \"event\",\n PARTNER = \"partner\",\n SCHOOL = \"school\",\n VENDOR = \"vendor\",\n VISITOR = \"visitor\",\n}\n\n/** `EnumResourceType` values that map to a stored resource — excludes the user-only `VISITOR` type. */\nexport type NonVisitorResourceType = Exclude<\n EnumResourceType,\n EnumResourceType.VISITOR\n>;\n\n/** Type guard that narrows an `EnumResourceType` to `NonVisitorResourceType`. */\nexport function isNonVisitorResourceType(\n value: EnumResourceType,\n): value is NonVisitorResourceType {\n return value !== EnumResourceType.VISITOR;\n}\n\nexport enum EnumEventType {\n MARKET = \"Market\",\n EXPO = \"Expo\",\n FAIR = \"Fair\",\n FESTIVAL = \"Festival\",\n}\n\nexport enum EnumVendorType {\n STALLHOLDER = \"Stallholder\",\n SHOP = \"Shop\",\n}\n\nexport enum EnumPartnerType {\n CHARITY_PARTNER = \"Charity_Partner\",\n MEDIA_PARTNER = \"Media_Partner\",\n SUPPORTING_PARTNER = \"Supporting_Partner\",\n}\n\nexport enum EnumOSPlatform {\n ANDROID = \"android\",\n IOS = \"ios\",\n WEB = \"web\",\n}\n\nexport enum EnumRelationResource {\n EVENT_INVITE_VENDOR = \"event_invite_vendor\",\n EVENT_UPDATE_RELATION_TO_VENDOR = \"event_update_relation_to_vendor\",\n VENDOR_UPDATE_RELATION_TO_EVENT = \"vendor_update_relation_to_event\",\n VENDOR_APPLICATION_TO_EVENT = \"vendor_application_to_event\",\n}\n\nexport enum EnumNotificationResourceType {\n AFFILIATE_REWARD_RECEIVED = \"affiliate_reward_received\",\n APPROVED_AFFILIATE = \"approved_affiliate\",\n APPROVED_EVENT = \"approved_event\",\n APPROVED_PARTNER = \"approved_partner\",\n APPROVED_SCHOOL = \"approved_school\",\n APPROVED_VENDOR = \"approved_vendor\",\n CREATED_EVENT = \"created_event\",\n CREATED_PARTNER = \"created_partner\",\n CREATED_VENDOR = \"created_vendor\",\n CREATED_COUPON = \"created_coupon\",\n DAILY_CLUE_GAME = \"daily_clue_game\",\n DEACTIVATED_EVENT = \"deactivated_event\",\n DEACTIVATED_PARTNER = \"deactivated_partner\",\n DEACTIVATED_VENDOR = \"deactivated_vendor\",\n DECLINED_EVENT = \"declined_event\",\n DECLINED_PARTNER = \"declined_partner\",\n DECLINED_VENDOR = \"declined_vendor\",\n DOWNGRADED_EVENT = \"downgraded_event\",\n DOWNGRADED_PARTNER = \"downgraded_partner\",\n DOWNGRADED_VENDOR = \"downgraded_vendor\",\n EVENT_DATE_CANCELED = \"event_date_canceled\",\n EVENT_INFO_CREATED = \"event_info_created\",\n EVENT_INVITE_VENDOR = EnumRelationResource.EVENT_INVITE_VENDOR,\n EVENT_STARTING_SOON = \"event_starting_soon\",\n EVENT_UPDATE_RELATION_TO_VENDOR = EnumRelationResource.EVENT_UPDATE_RELATION_TO_VENDOR,\n EXPIRATION_REMINDER_EVENT = \"expiration_reminder_event\",\n EXPIRATION_REMINDER_PARTNER = \"expiration_reminder_partner\",\n EXPIRATION_REMINDER_VENDOR = \"expiration_reminder_vendor\",\n NEW_CHAT_MESSAGE = \"new_chat_message\",\n NEW_POST_CREATED = \"new_post_created\",\n REGISTERED_USER_BY_SCHOOL_CODE = \"registered_user_by_school_code\",\n SYSTEM_ALERT = \"system_alert\",\n USER_COUPON_REDEEMED = \"user_coupon_redeemed\",\n VENDOR_APPLICATION_TO_EVENT = EnumRelationResource.VENDOR_APPLICATION_TO_EVENT,\n VENDOR_UPDATE_RELATION_TO_EVENT = EnumRelationResource.VENDOR_UPDATE_RELATION_TO_EVENT,\n}\n\nexport enum EnumNotificationType {\n CHAT = \"chat\",\n EVENT = EnumResourceType.EVENT,\n RELATION = \"relation\",\n SYSTEM = \"system\",\n VENDOR = EnumResourceType.VENDOR,\n}\n\nexport enum EnumRegions {\n All = \"All Regions\",\n Auckland = \"Auckland\",\n BayOfPlentyGisborne = \"Bay of Plenty & Gisborne\",\n CanterburyWestCoast = \"Canterbury & West Coast\",\n HawkesBay = \"Hawke's Bay\",\n ManawatuWanganui = \"Manawatu-Wanganui\",\n MarlboroughNelsonTasman = \"Marlborough & Nelson & Tasman\",\n Northland = \"Northland\",\n Otago = \"Otago\",\n Southland = \"Southland\",\n Taranaki = \"Taranaki\",\n Waikato = \"Waikato\",\n Wellington = \"Wellington\",\n}\n\nexport enum ImageTypeEnum {\n AVATAR = \"avatar\",\n COVER = \"cover\",\n IMAGE = \"image\",\n LOGO = \"logo\",\n}\n\nexport enum EnumUserLicence {\n PRO_EVENT = \"pro_event\",\n PRO_PLUS_EVENT = \"pro_plus_event\",\n PRO_PLUS_VENDOR = \"pro_plus_vendor\",\n PRO_VENDOR = \"pro_vendor\",\n STANDARD_EVENT = \"standard_event\",\n STANDARD_VENDOR = \"standard_vendor\",\n STANDARD_PARTNER = \"standard_partner\",\n STANDARD_AFFILIATE = \"standard_affiliate\",\n STANDARD_SCHOOL = \"standard_school\",\n}\n\nexport enum LicenceExpiryDate {\n ONE_YEAR = \"1 year\",\n ONE_MONTH = \"1 month\",\n TWO_MONTHS = \"2 months\",\n THREE_MONTHS = \"3 months\",\n SIX_MONTHS = \"6 months\",\n}\n\nexport enum EnumUserRole {\n ADMIN = \"admin\",\n AFFILIATE = \"affiliate\",\n CUSTOMER = \"customer\",\n EVENT = \"event\",\n PARTNER = \"partner\",\n SCHOOL = \"school\",\n SUPER_ADMIN = \"super_admin\",\n VENDOR = \"vendor\",\n}\n\nexport enum EnumSocialMedia {\n FACEBOOK = \"facebook\",\n INSTAGRAM = \"instagram\",\n TIKTOK = \"tiktok\",\n TWITTER = \"twitter\",\n WEBSITE = \"website\",\n YOUTUBE = \"youtube\",\n}\n\nexport enum EnumEventDateStatus {\n STARTING_SOON = \"Starting_Soon\",\n STARTED = \"Started\",\n TODAY = \"Today\",\n TOMORROW = \"Tomorrow\",\n THIS_WEEK = \"This_Week\",\n NEXT_WEEK = \"Next_Week\",\n UPCOMING = \"Upcoming\",\n ENDED = \"Ended\",\n RE_SCHEDULED = \"Rescheduled\",\n CANCELED = \"Canceled\",\n INVALID = \"Invalid\",\n}\n\nexport enum EnumSubscriptionStatus {\n ACTIVE = \"active\",\n INACTIVE = \"inactive\",\n CANCELLED = \"cancelled\",\n NO_SUBSCRIPTION = \"no_subscription\",\n PAST_DUE = \"past_due\",\n TRIALING = \"trialing\",\n}\n\nexport enum EnumBillingPeriod {\n MONTHLY_CANCEL_ANYTIME = \"monthly_cancel_anytime\",\n YEARLY_ANNUAL_BILLED = \"yearly_annual_billed\",\n}\n\nexport enum EnumReward {\n MYSTERY_JOY_BOX = \"mystery_joy_box\",\n MYSTERY_SQUEEZE_BOX = \"mystery_squeeze_box\",\n MYSTERY_DELUXE_BOX = \"mystery_deluxe_box\",\n}\n\nexport enum EnumPointType {\n DAILY_CLUE_GAME = \"daily_clue_game\",\n EVENT_CHECKIN = \"event_checkin\",\n INITIAL = \"initial\",\n MINI_QUIZ_GAME = \"mini_quiz_game\",\n ODD_ONE_OUT_GAME = \"odd_one_out_game\",\n}\n","import { EnumRegions } from \"../enums\";\nimport type { Region } from \"../types\";\n\nexport type RegionCenterType = Region & { region: EnumRegions };\n\n/**\n * New Zealand region centres used to derive the nearest region from a\n * location and to map a region selection to map coordinates.\n *\n * The final `EnumRegions.All` entry is the NZ-wide default.\n */\nexport const REGION_CENTERS: RegionCenterType[] = [\n {\n latitude: -36.8485,\n latitudeDelta: 2,\n longitude: 174.7633,\n longitudeDelta: 2,\n region: EnumRegions.Auckland,\n },\n {\n latitude: -38.1387,\n latitudeDelta: 2,\n longitude: 176.2452,\n longitudeDelta: 2,\n region: EnumRegions.BayOfPlentyGisborne,\n },\n {\n latitude: -43.5321,\n latitudeDelta: 2,\n longitude: 172.6362,\n longitudeDelta: 2,\n region: EnumRegions.CanterburyWestCoast,\n },\n {\n latitude: -39.4928,\n latitudeDelta: 2,\n longitude: 176.912,\n longitudeDelta: 2,\n region: EnumRegions.HawkesBay,\n },\n {\n latitude: -40.3523,\n latitudeDelta: 2,\n longitude: 175.6082,\n longitudeDelta: 2,\n region: EnumRegions.ManawatuWanganui,\n },\n {\n latitude: -41.2706,\n latitudeDelta: 2,\n longitude: 173.284,\n longitudeDelta: 2,\n region: EnumRegions.MarlboroughNelsonTasman,\n },\n {\n latitude: -35.7251,\n latitudeDelta: 2,\n longitude: 174.3237,\n longitudeDelta: 2,\n region: EnumRegions.Northland,\n },\n {\n latitude: -45.8788,\n latitudeDelta: 2,\n longitude: 170.5028,\n longitudeDelta: 2,\n region: EnumRegions.Otago,\n },\n {\n latitude: -46.4132,\n latitudeDelta: 2,\n longitude: 168.3538,\n longitudeDelta: 2,\n region: EnumRegions.Southland,\n },\n {\n latitude: -39.0556,\n latitudeDelta: 2,\n longitude: 174.0752,\n longitudeDelta: 2,\n region: EnumRegions.Taranaki,\n },\n {\n latitude: -37.787,\n latitudeDelta: 2,\n longitude: 175.2793,\n longitudeDelta: 2,\n region: EnumRegions.Waikato,\n },\n {\n latitude: -41.2866,\n latitudeDelta: 2,\n longitude: 174.7756,\n longitudeDelta: 2,\n region: EnumRegions.Wellington,\n },\n {\n latitude: -36.8624942, // Default: New Zealand\n latitudeDelta: 2,\n longitude: 174.7450494,\n longitudeDelta: 2,\n region: EnumRegions.All,\n },\n];\n\n/**\n * All selectable NZ regions, excluding the `All` pseudo-region.\n */\nexport const nzRegions = Object.values(EnumRegions).filter(\n (region) => region !== EnumRegions.All,\n);\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useAuthUser } from \"../auth/UserProvider\";\nimport { EnumRegions } from \"../enums\";\nimport { getDistanceInMeters } from \"../utils/distance\";\n\nimport { REGION_CENTERS, type RegionCenterType } from \"./constants\";\n\nexport interface IUseRegionSelectionReturn {\n derivedRegion: RegionCenterType;\n handleResetRegion: () => void;\n handleSelectRegion: (region: EnumRegions) => void;\n loading: boolean;\n selectedRegion: RegionCenterType;\n}\n\ntype DeriveRegionLocation = {\n latitude: number;\n longitude: number;\n};\n\nexport interface UseRegionSelectionStateArgs {\n location: DeriveRegionLocation | null | undefined;\n}\n\n// AUDIT-DECISION: Fail fast if REGION_CENTERS is empty. It is a compile-time\n// constant, so an empty array means the import is broken — an invariant beats\n// a silent undefined that surfaces as a crash three screens away.\nfunction getDefaultRegion(): RegionCenterType {\n const [first] = REGION_CENTERS;\n if (first === undefined) {\n throw new Error(\n \"[useRegionSelectionState] REGION_CENTERS is empty — check the import.\",\n );\n }\n return first;\n}\n\n// AUDIT-DECISION: Pure helper — no side effects, no hook calls.\n// Accepts nullable inputs so callers never need to guard before calling it.\nfunction deriveRegion(\n location: DeriveRegionLocation | null | undefined,\n preferredRegion: EnumRegions | null | undefined,\n): RegionCenterType {\n // 1. Location available — pick nearest valid region.\n if (location) {\n const { latitude, longitude } = location;\n const userCoords: [number, number] = [longitude, latitude];\n\n let nearestRegion: RegionCenterType = getDefaultRegion();\n let nearestDistance = Infinity;\n\n for (const region of REGION_CENTERS) {\n const regionCoords: [number, number] = [\n region.longitude,\n region.latitude,\n ];\n const distance = getDistanceInMeters(userCoords, regionCoords);\n\n // AUDIT-DECISION: Skip non-finite distances — getDistanceInMeters\n // returns Infinity for invalid coords. We do not want a bogus distance to\n // \"win\" the reduce by being the first candidate with the starting Infinity\n // baseline, which was the original bug.\n if (Number.isFinite(distance) && distance < nearestDistance) {\n nearestDistance = distance;\n nearestRegion = region;\n }\n }\n\n // AUDIT-DECISION: If every distance was non-finite (malformed location),\n // fall through to the preference path rather than returning REGION_CENTERS[0]\n // unconditionally. This keeps the fallback chain consistent.\n if (Number.isFinite(nearestDistance)) {\n return nearestRegion;\n }\n }\n\n // 2. No usable location — use the user's stored preference.\n if (preferredRegion) {\n const preferred = REGION_CENTERS.find((r) => r.region === preferredRegion);\n if (preferred) return preferred;\n }\n\n // 3. Last resort — first entry in the array.\n return getDefaultRegion();\n}\n\nexport function useRegionSelectionState({\n location,\n}: UseRegionSelectionStateArgs): IUseRegionSelectionReturn {\n const { authUser } = useAuthUser();\n\n const preferredRegion = authUser?.preferredRegion as\n EnumRegions | null | undefined;\n\n // AUDIT-DECISION: Derive the initial region synchronously so the very first\n // render never sees undefined. deriveRegion is cheap (O(n) array scan).\n const [selectedRegion, setSelectedRegion] = useState<RegionCenterType>(() =>\n deriveRegion(location, preferredRegion ?? null),\n );\n // AUDIT-DECISION: Track the auto-derived default separately from the user's\n // manual selection. derivedRegion always reflects deriveRegion(location,\n // preferredRegion) — nearest location → preferred region → first region — so\n // consumers can detect divergence from the default and reset back to it.\n const [derivedRegion, setDerivedRegion] = useState<RegionCenterType>(() =>\n deriveRegion(location, preferredRegion ?? null),\n );\n const [loading, setLoading] = useState<boolean>(false);\n const [hasManualSelection, setHasManualSelection] = useState<boolean>(false);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // AUDIT-DECISION: Reset manual selection when the authenticated user changes\n // so the auto-derive re-runs for the new identity. Keying on authUser?._id\n // (not the full object) avoids false resets on reference-stable re-renders.\n const prevUserIdRef = useRef<string | undefined>(authUser?._id);\n useEffect(() => {\n if (prevUserIdRef.current !== authUser?._id) {\n prevUserIdRef.current = authUser?._id;\n setHasManualSelection(false);\n }\n }, [authUser?._id]);\n\n // AUDIT-DECISION: Compute and apply the derived region in a single effect —\n // no intermediate memo. This removes the two-cycle race window where the\n // memoised value existed before the state write committed.\n useEffect(() => {\n const derived = deriveRegion(location, preferredRegion ?? null);\n setDerivedRegion(derived);\n\n if (hasManualSelection) return;\n setSelectedRegion(derived);\n }, [location, preferredRegion, hasManualSelection]);\n\n const handleSelectRegion = useCallback((region: EnumRegions) => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n }\n\n const regionCenter =\n REGION_CENTERS.find((r) => r.region === region) ?? getDefaultRegion();\n\n setLoading(true);\n setSelectedRegion(regionCenter);\n setHasManualSelection(true);\n\n timerRef.current = setTimeout(() => {\n setLoading(false);\n timerRef.current = null;\n }, 500);\n }, []);\n\n const handleResetRegion = useCallback(() => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n\n setLoading(false);\n const derived = deriveRegion(location, preferredRegion ?? null);\n setDerivedRegion(derived);\n setSelectedRegion(derived);\n setHasManualSelection(false);\n }, [location, preferredRegion]);\n\n // Cleanup on unmount.\n useEffect(() => {\n return () => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n }\n };\n }, []);\n\n return {\n derivedRegion,\n handleResetRegion,\n handleSelectRegion,\n loading,\n selectedRegion,\n };\n}\n\nexport default useRegionSelectionState;\n","/**\n * Shared React context for auth session state.\n *\n * Apps keep a thin local UserProvider that calls their platform `useAuth`\n * (adapters) and pass the result here as `value`. Import `useAuthUser` from\n * this module (or re-export it) so consumers share one context instance.\n *\n * `\"use client\"`: required for Next.js; ignored by React Native.\n */\n\"use client\";\n\nimport React, { createContext, useContext, type ReactNode } from \"react\";\n\nimport type { UseAuthReturnType } from \"./types\";\n\nconst UserContext = createContext<UseAuthReturnType | undefined>(undefined);\n\n/**\n * Provides auth session value from the app's thin useAuth wrapper.\n */\nexport const UserProvider: React.FC<{\n children: ReactNode;\n value: UseAuthReturnType;\n}> = ({ children, value }) => {\n return <UserContext.Provider value={value}>{children}</UserContext.Provider>;\n};\n\nexport const useAuthUser = (): UseAuthReturnType => {\n const context = useContext(UserContext);\n if (!context) {\n throw new Error(\"useAuthUser must be used within a UserProvider\");\n }\n return context;\n};\n","export function getDistanceInMeters(\n coordinates1: number[],\n coordinates2: number[],\n): number {\n const [lon1, lat1] = coordinates1;\n const [lon2, lat2] = coordinates2;\n\n const values = [lon1, lat1, lon2, lat2];\n if (values.some((v) => !Number.isFinite(v))) {\n return Infinity;\n }\n\n const toRadians = (deg: number) => (deg * Math.PI) / 180;\n const R = 6371000; // Earth radius in meters\n\n const dLat = toRadians(lat2 - lat1);\n const dLon = toRadians(lon2 - lon1);\n const radLat1 = toRadians(lat1);\n const radLat2 = toRadians(lat2);\n\n const a =\n Math.sin(dLat / 2) ** 2 +\n Math.sin(dLon / 2) ** 2 * Math.cos(radLat1) * Math.cos(radLat2);\n\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n return R * c;\n}\n\nexport function getDistanceInKilometers(\n coordinates1: number[],\n coordinates2: number[],\n): number {\n const distanceInMeters = getDistanceInMeters(coordinates1, coordinates2);\n\n if (!Number.isFinite(distanceInMeters)) {\n return Infinity;\n }\n\n return Math.round(distanceInMeters / 1000);\n}\n\nexport function formatDistance(\n coordinates1: number[],\n coordinates2: number[],\n): string {\n const distanceInMeters = getDistanceInMeters(coordinates1, coordinates2);\n\n if (!Number.isFinite(distanceInMeters)) {\n return \"—\";\n }\n\n const roundedMeters = Math.round(distanceInMeters);\n\n if (roundedMeters < 1000) {\n return `${roundedMeters} m`;\n }\n\n return `${Math.round(roundedMeters / 1000)} km`;\n}\n"],"mappings":"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","names":["EnumRegions","import_react"]}
@@ -7,7 +7,7 @@ import {
7
7
  } from "../chunk-RE4NZPYY.mjs";
8
8
  import {
9
9
  EnumRegions
10
- } from "../chunk-XGGFB7HY.mjs";
10
+ } from "../chunk-A52C6JDT.mjs";
11
11
 
12
12
  // src/region/constants.ts
13
13
  var REGION_CENTERS = [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/sharing/index.ts","../../src/utils/date.ts","../../src/utils/dateFormats.ts","../../src/utils/eventDateStatus.ts","../../src/sharing/relationShareTypes.ts","../../src/sharing/constants.ts","../../src/sharing/formatCategoryLabel.ts","../../src/sharing/joinShareDescriptionSections.ts","../../src/sharing/buildRelationShareDescription.ts","../../src/sharing/buildPublicShareDescription.ts","../../src/sharing/buildShareUrl.ts","../../src/sharing/normalizeShareDescription.ts","../../src/sharing/normalizeShareRouteId.ts","../../src/sharing/shareSlugResource.ts","../../src/sharing/toShareSocialImageCacheVersion.ts"],"sourcesContent":["export * from \"./buildPublicShareDescription\";\nexport * from \"./buildRelationShareDescription\";\nexport * from \"./shareRelationTypes\";\nexport * from \"./buildShareUrl\";\nexport * from \"./constants\";\nexport * from \"./formatCategoryLabel\";\nexport * from \"./joinShareDescriptionSections\";\nexport * from \"./normalizeShareDescription\";\nexport * from \"./normalizeShareRouteId\";\nexport * from \"./relationShareTypes\";\nexport * from \"./shareSlugResource\";\nexport * from \"./toShareSocialImageCacheVersion\";\n","import dayjs from \"dayjs\";\nimport customParseFormat from \"dayjs/plugin/customParseFormat.js\";\nimport isSameOrAfter from \"dayjs/plugin/isSameOrAfter.js\";\nimport timezone from \"dayjs/plugin/timezone.js\";\nimport utc from \"dayjs/plugin/utc.js\";\n\nimport { EnumEventDateStatus } from \"src/enums\";\n\nimport { dateFormat, timeFormat, isoDateFormat } from \"./dateFormats\";\nimport {\n FUTURE_EVENT_DATE_STATUSES,\n EVENT_DATE_STATUS_LABELS,\n} from \"./eventDateStatus\";\n\nexport { dateFormat, timeFormat, isoDateFormat };\n\n// Enable custom format parsing\ndayjs.extend(customParseFormat);\ndayjs.extend(utc);\ndayjs.extend(timezone);\ndayjs.extend(isSameOrAfter);\n\nexport const NZ_TZ = \"Pacific/Auckland\";\n\nexport function toNZTime(date?: Date | string) {\n return date ? dayjs(date).tz(NZ_TZ) : dayjs().tz(NZ_TZ);\n}\n\n/** Start of the calendar day in Pacific/Auckland (daily games, streaks, etc.). */\nexport function nzStartOfDay(\n input?: Date | string | number | null,\n): dayjs.Dayjs {\n if (input == null) {\n return dayjs().tz(NZ_TZ).startOf(\"day\");\n }\n return dayjs.tz(input, NZ_TZ).startOf(\"day\");\n}\n\n/**\n * UTC-midnight of the given instant's NZ calendar day (today when omitted).\n * This is the canonical \"day key\" encoding used by ad bookings and metrics:\n * stored as a UTC Date, rendered with `.toISOString().slice(0, 10)`.\n */\nexport function nzDayKey(input?: Date | string | number | null): Date {\n const day =\n input == null\n ? dayjs().tz(NZ_TZ).format(isoDateFormat)\n : dayjs.tz(input, NZ_TZ).format(isoDateFormat);\n return new Date(`${day}T00:00:00Z`);\n}\n\n/**\n * Type guard for the canonical \"day key\" encoding: a valid Date that is\n * exactly UTC midnight. Accepts `unknown` so it can validate untrusted values\n * (GraphQL/JSON inputs) without a preceding cast.\n */\nexport function isUtcMidnight(value: unknown): value is Date {\n return (\n value instanceof Date &&\n !Number.isNaN(value.getTime()) &&\n value.getUTCHours() === 0 &&\n value.getUTCMinutes() === 0 &&\n value.getUTCSeconds() === 0 &&\n value.getUTCMilliseconds() === 0\n );\n}\n\ntype DateFormat = \"date\" | \"time\" | \"datetime\";\n\n/**\n * Format a date string to a more readable format.\n * @param dateStr - the date string\n * @param timeStr - optional time string\n * @param display - 'date' | 'time' | 'datetime'\n * @returns formatted string based on display option\n */\nexport const formatDate = (\n dateStr: string,\n display: DateFormat = \"datetime\",\n timeStr?: string,\n) => {\n // Combine date and time into a single string if time is provided\n const dateTimeStr = timeStr ? `${dateStr} ${timeStr}` : dateStr;\n\n // Parse with formats\n const dateTime = timeStr\n ? dayjs(dateTimeStr, `${dateFormat} ${timeFormat}`)\n : dayjs(dateStr, dateFormat);\n\n // Format parts\n const formattedDate = dateTime.format(\"dddd, D MMMM, YYYY\");\n const formattedTime = dateTime.format(\"h:mm a\");\n\n // Return based on display option\n switch (display) {\n case \"date\":\n return formattedDate;\n case \"time\":\n return formattedTime;\n case \"datetime\":\n return `${formattedDate} at ${formattedTime}`;\n default:\n return formattedDate;\n }\n};\n\n/**\n * Formats accepted for a stored slot. The canonical writer is\n * `dayjs(...).format(`${dateFormat} ${timeFormat}`)`; the variants cover legacy\n * rows with an unpadded hour or trailing seconds. Each is parsed **strictly**, so\n * impossible values (`31-02`, `25:00`) are rejected instead of rolling over into\n * a real instant.\n */\nconst SLOT_FORMATS = [\n `${dateFormat} ${timeFormat}`,\n `${dateFormat} H:mm`,\n `${dateFormat} ${timeFormat}:ss`,\n `${dateFormat} H:mm:ss`,\n];\n\nconst parseSlot = (date: {\n startDate?: string;\n startTime?: string;\n}): dayjs.Dayjs | null => {\n if (!date.startDate || !date.startTime) {\n return null;\n }\n\n const value = `${date.startDate} ${date.startTime}`;\n\n for (const format of SLOT_FORMATS) {\n const parsed = dayjs(value, format, true);\n\n if (parsed.isValid()) {\n return parsed;\n }\n }\n\n return null;\n};\n\n/**\n * Shared \"starts now or later\" rule for `dd-mm-yyyy` + `HH:mm` slots. `now` is\n * passed in so a caller filtering a list compares every slot against the same\n * instant. A slot with a missing or unparseable date/time is never future.\n */\nconst isFutureDateTime = (\n date: { startDate?: string; startTime?: string },\n now: dayjs.Dayjs,\n): boolean => {\n const slot = parseSlot(date);\n\n return slot ? slot.isSameOrAfter(now) : false;\n};\n\nexport const getCurrentAndFutureDates = <\n T extends { startDate?: string; startTime?: string },\n>(\n dates: T[],\n): T[] => {\n const now = dayjs(); // current date and time\n\n return dates.filter((dateObj) => isFutureDateTime(dateObj, now));\n};\n\n/**\n * True when the slot starts now or later. Status-agnostic: used to hide past\n * relation dates from reads while the records stay in the database.\n */\nexport const isFutureStartDate = (date: {\n startDate?: string;\n startTime?: string;\n}): boolean => isFutureDateTime(date, dayjs());\n\nexport const isFutureDatesBeforeThreshold = (\n date: {\n startDate?: string;\n startTime?: string;\n },\n minHoursFromNow: number,\n): boolean => {\n if (!date.startDate || !date.startTime) {\n return false;\n }\n\n const threshold = minHoursFromNow\n ? dayjs().add(minHoursFromNow, \"hour\")\n : dayjs().startOf(\"day\");\n\n const dateTime = dayjs(\n `${date.startDate} ${date.startTime}`,\n `${dateFormat} ${timeFormat}`,\n );\n\n return dateTime.isSameOrAfter(threshold);\n};\n\nexport const formatTimestamp = (timestamp: string) => {\n const formattedDate = toNZTime(timestamp).format(dateFormat);\n\n return formatDate(formattedDate, \"date\");\n};\n\nexport const isIsoDateString = (value: unknown): value is string => {\n return typeof value === \"string\" && !Number.isNaN(Date.parse(value));\n};\n\n/**\n * Sort an array of date strings by their proximity to the current date.\n * @param dates - The array of date strings to sort.\n * @returns - The sorted array of date strings.\n */\nexport function sortDatesChronologically<\n T extends { startDate?: string; startTime?: string },\n>(dates: T[]): T[] {\n if (!dates?.length) {\n return [];\n }\n\n return [...dates].sort((a, b) => {\n const dateTimeFormat = `${dateFormat} ${timeFormat}`;\n const dateA =\n a.startDate && a.startTime\n ? dayjs(`${a.startDate} ${a.startTime}`, dateTimeFormat)\n : null;\n const dateB =\n b.startDate && b.startTime\n ? dayjs(`${b.startDate} ${b.startTime}`, dateTimeFormat)\n : null;\n\n if (dateA && dateB) {\n return dateA.valueOf() - dateB.valueOf();\n }\n\n if (dateA) {\n return -1;\n }\n\n if (dateB) {\n return 1;\n }\n\n return 0;\n });\n}\n\n/**\n * Start dates of the given date/time slots, chronological and de-duplicated.\n * Slots that share a `startDate` (e.g. two stall slots on the same day) collapse\n * into a single entry.\n */\nexport function getUniqueStartDates(\n dates: Array<{ startDate?: string; startTime?: string }> | null | undefined,\n): string[] {\n const seenStartDates = new Set<string>();\n const uniqueStartDates: string[] = [];\n\n for (const { startDate } of sortDatesChronologically(dates ?? [])) {\n if (!startDate || seenStartDates.has(startDate)) {\n continue;\n }\n\n seenStartDates.add(startDate);\n uniqueStartDates.push(startDate);\n }\n\n return uniqueStartDates;\n}\n\n/**\n * UI filter options for user-activity event periods. Derived from the canonical\n * {@link FUTURE_EVENT_DATE_STATUSES} list, minus `STARTING_SOON` (a presentational\n * choice: \"starting soon\" is surfaced separately rather than as a filter).\n */\nexport const futureTimePeriods = FUTURE_EVENT_DATE_STATUSES.filter(\n (status) => status !== EnumEventDateStatus.STARTING_SOON,\n).map((status) => ({\n label: EVENT_DATE_STATUS_LABELS[status],\n value: status,\n}));\n","export const dateFormat = \"DD-MM-YYYY\";\nexport const timeFormat = \"HH:mm\";\n/** ISO 8601 machine/sortable format: day keys, Date construction, strict parsing, HTML date inputs. */\nexport const isoDateFormat = \"YYYY-MM-DD\";\n","import dayjs from \"dayjs\";\nimport customParseFormat from \"dayjs/plugin/customParseFormat.js\";\nimport isoWeek from \"dayjs/plugin/isoWeek.js\";\n\nimport { EnumEventDateStatus } from \"../enums\";\nimport type { DateTimeType } from \"../types/global\";\n\nimport { dateFormat, timeFormat } from \"./dateFormats\";\n\n// Enable dayjs plugins used by event date parsing/status logic\ndayjs.extend(customParseFormat);\ndayjs.extend(isoWeek);\n\n/**\n * Display label per date status. Single source of truth instead of deriving\n * the label with `replaceAll(\"_\", \" \")` / text-transform tricks, so each\n * status maps to an explicit, correctly-cased string (e.g. \"This Week\",\n * \"Starting Soon\", \"Rescheduled\"). Lives here (not in `calendar`) so both\n * `calendar/eventCalendar` and `utils/date` can consume it without a circular\n * import.\n */\nexport const EVENT_DATE_STATUS_LABELS: Record<EnumEventDateStatus, string> = {\n [EnumEventDateStatus.STARTING_SOON]: \"Starting Soon\",\n [EnumEventDateStatus.STARTED]: \"Started\",\n [EnumEventDateStatus.TODAY]: \"Today\",\n [EnumEventDateStatus.TOMORROW]: \"Tomorrow\",\n [EnumEventDateStatus.THIS_WEEK]: \"This Week\",\n [EnumEventDateStatus.NEXT_WEEK]: \"Next Week\",\n [EnumEventDateStatus.UPCOMING]: \"Upcoming\",\n [EnumEventDateStatus.ENDED]: \"Ended\",\n [EnumEventDateStatus.RE_SCHEDULED]: \"Rescheduled\",\n [EnumEventDateStatus.CANCELED]: \"Canceled\",\n [EnumEventDateStatus.INVALID]: \"Invalid\",\n};\n\nexport const EVENT_DATE_STATUS_PERIOD_MAP: Partial<\n Record<EnumEventDateStatus, EnumEventDateStatus[]>\n> = {\n [EnumEventDateStatus.TODAY]: [\n EnumEventDateStatus.STARTED,\n EnumEventDateStatus.STARTING_SOON,\n EnumEventDateStatus.TODAY,\n ],\n [EnumEventDateStatus.THIS_WEEK]: [\n EnumEventDateStatus.STARTED,\n EnumEventDateStatus.STARTING_SOON,\n EnumEventDateStatus.THIS_WEEK,\n EnumEventDateStatus.TODAY,\n EnumEventDateStatus.TOMORROW,\n ],\n};\n\nexport function getAllowedEventDateStatuses(\n period: EnumEventDateStatus,\n): EnumEventDateStatus[] {\n return EVENT_DATE_STATUS_PERIOD_MAP[period] ?? [period];\n}\n\nexport function eventMatchesDateStatusPeriod(\n dateTime: Pick<DateTimeType, \"dateStatus\">[] | null | undefined,\n period: EnumEventDateStatus,\n): boolean {\n const allowedStatuses = getAllowedEventDateStatuses(period);\n\n return (\n dateTime?.some((dt) => allowedStatuses.includes(dt.dateStatus)) ?? false\n );\n}\n\nexport function filterEventsByDateStatusPeriod<\n T extends { dateTime?: Pick<DateTimeType, \"dateStatus\">[] | null },\n>(events: T[], period: EnumEventDateStatus): T[] {\n return events.filter((event) =>\n eventMatchesDateStatusPeriod(event.dateTime, period),\n );\n}\n\n/**\n * Canonical list of date statuses whose start date/time is still in the future\n * (i.e. the slot has not started yet). `STARTED`, `ENDED`, `INVALID`,\n * `CANCELED` and `RE_SCHEDULED` are intentionally excluded.\n *\n * This is the single source of truth for \"is this slot still in the future\".\n * `futureTimePeriods` (in `utils/date.ts`) derives from this list for UI\n * filters, additionally excluding `STARTING_SOON`.\n */\nexport const FUTURE_EVENT_DATE_STATUSES: readonly EnumEventDateStatus[] = [\n EnumEventDateStatus.STARTING_SOON,\n EnumEventDateStatus.TODAY,\n EnumEventDateStatus.TOMORROW,\n EnumEventDateStatus.THIS_WEEK,\n EnumEventDateStatus.NEXT_WEEK,\n EnumEventDateStatus.UPCOMING,\n];\n\nexport function isFutureEventDateStatus(\n dateStatus: EnumEventDateStatus | null | undefined,\n): boolean {\n return Boolean(dateStatus && FUTURE_EVENT_DATE_STATUSES.includes(dateStatus));\n}\n\nexport function filterDatesByFutureStatus<\n T extends { dateStatus?: EnumEventDateStatus | null },\n>(dates: T[]): T[] {\n return dates.filter((date) => isFutureEventDateStatus(date.dateStatus));\n}\n\n/**\n * Statuses set by an explicit user action rather than derived from the\n * schedule clock. The status recomputation below must leave them untouched,\n * otherwise a canceled/rescheduled date would silently flip back to a\n * time-derived status on the next run.\n */\nexport const MANUAL_DATE_STATUSES: ReadonlySet<EnumEventDateStatus> = new Set([\n EnumEventDateStatus.CANCELED,\n EnumEventDateStatus.RE_SCHEDULED,\n]);\n\n/**\n * Builds the event schedule that relation dates should be matched against.\n *\n * Manual statuses (`MANUAL_DATE_STATUSES`: CANCELED and RE_SCHEDULED) stay on\n * the live `Event.dateTime` for visibility, but a relation date pointing at\n * them is treated as removed. Archived (ended) slots are appended separately\n * so relation dates for archived history still match instead of being wrongly\n * marked unavailable.\n *\n * Shared by the server's `processUnavailableRelations` sync and the cron\n * orphan-date cleanup so both use the same archive-aware schedule.\n */\nexport function buildRelationEligibleEventSchedule<\n T extends { dateStatus?: EnumEventDateStatus | null },\n>(liveDateTime: T[], archivedDateTime: T[] = []): T[] {\n const eligible = liveDateTime.filter(\n (slot) => !slot.dateStatus || !MANUAL_DATE_STATUSES.has(slot.dateStatus),\n );\n return [...eligible, ...archivedDateTime];\n}\n\n/**\n * Determines the dateStatus for a future event date\n * @param startDateTime The start date-time of the event\n * @param now The current date-time\n * @returns The appropriate EnumEventDateStatus\n */\nexport function getFutureEventStatus(\n startDateTime: dayjs.Dayjs,\n now: dayjs.Dayjs,\n): EnumEventDateStatus {\n const hoursUntilStart = startDateTime.diff(now, \"hour\", true);\n if (hoursUntilStart > 0 && hoursUntilStart <= 4) {\n return EnumEventDateStatus.STARTING_SOON;\n }\n\n if (startDateTime.isSame(now, \"day\")) {\n return EnumEventDateStatus.TODAY;\n }\n\n if (startDateTime.isSame(now.add(1, \"day\"), \"day\")) {\n return EnumEventDateStatus.TOMORROW;\n }\n\n if (startDateTime.isSame(now, \"isoWeek\")) {\n return EnumEventDateStatus.THIS_WEEK;\n }\n\n if (startDateTime.isSame(now.add(1, \"week\"), \"isoWeek\")) {\n return EnumEventDateStatus.NEXT_WEEK;\n }\n\n return EnumEventDateStatus.UPCOMING;\n}\n\n/**\n * Updates the dateStatus field for a single DateTimeType object based on the current date/time.\n * Manual statuses (CANCELED, RE_SCHEDULED) are preserved and returned as-is.\n * @param dateTime The date-time object to update\n * @returns A new object with an updated dateStatus value, or the original\n * object when the stored status is a manual override\n */\nexport function updateSingleDateTimeStatus<T extends DateTimeType>(\n dateTime: T,\n now: dayjs.Dayjs = dayjs(),\n): T {\n if (MANUAL_DATE_STATUSES.has(dateTime.dateStatus)) {\n return dateTime;\n }\n\n // Parse start and end date-time\n const dateTimeFormat = `${dateFormat} ${timeFormat}`;\n const startDateTime = dayjs(\n `${dateTime.startDate} ${dateTime.startTime}`,\n dateTimeFormat,\n true,\n );\n const endDateTime = dayjs(\n `${dateTime.endDate} ${dateTime.endTime}`,\n dateTimeFormat,\n true,\n );\n\n // Skip if dates are invalid\n if (!startDateTime.isValid() || !endDateTime.isValid()) {\n return {\n ...dateTime,\n dateStatus: EnumEventDateStatus.INVALID,\n };\n }\n\n if (endDateTime.isBefore(startDateTime)) {\n return {\n ...dateTime,\n dateStatus: EnumEventDateStatus.INVALID,\n };\n }\n\n let dateStatus: EnumEventDateStatus;\n\n if (endDateTime.isAfter(now)) {\n if (startDateTime.isAfter(now)) {\n // Event has not started yet\n dateStatus = getFutureEventStatus(startDateTime, now);\n } else {\n // Event is in progress\n dateStatus = EnumEventDateStatus.STARTED;\n }\n } else {\n // Event has ended\n dateStatus = EnumEventDateStatus.ENDED;\n }\n\n return {\n ...dateTime,\n dateStatus,\n };\n}\n","/** Path segments for relation share URLs — must match mobile `RelationTitle` values. */\nexport const RELATION_SHARE_INVITATION = \"invitation\" as const;\nexport const RELATION_SHARE_APPLICATION = \"application\" as const;\n\n/**\n * Bump when relation overlay SVG/layout changes so og:image URLs and API ETags\n * invalidate after deploy. Facebook caches preview JPEGs by exact URL — changing\n * this value forces crawlers to fetch a fresh image after server deploy.\n */\nexport const RELATION_OVERLAY_LAYOUT_VERSION = 43;\n\nexport const RELATION_SHARE_RESOURCE_TYPES = [\n RELATION_SHARE_INVITATION,\n RELATION_SHARE_APPLICATION,\n] as const;\n\nexport type RelationShareResourceType =\n (typeof RELATION_SHARE_RESOURCE_TYPES)[number];\n\nexport function isRelationShareResourceType(\n resourceType: string,\n): resourceType is RelationShareResourceType {\n return (RELATION_SHARE_RESOURCE_TYPES as readonly string[]).includes(\n resourceType,\n );\n}\n","import {\n RELATION_SHARE_APPLICATION,\n RELATION_SHARE_INVITATION,\n type RelationShareResourceType,\n} from \"./relationShareTypes\";\n\nexport {\n RELATION_SHARE_APPLICATION,\n RELATION_SHARE_INVITATION,\n RELATION_OVERLAY_LAYOUT_VERSION,\n RELATION_SHARE_RESOURCE_TYPES,\n isRelationShareResourceType,\n type RelationShareResourceType,\n} from \"./relationShareTypes\";\n\n/** Open Graph recommended preview size (must match admin `SHARE_OG_IMAGE_*`). */\nexport const SOCIAL_IMAGE_WIDTH = 1200;\n\n/** Open Graph recommended preview size (must match admin `SHARE_OG_IMAGE_*`). */\nexport const SOCIAL_IMAGE_HEIGHT = 630;\n\n/** Keep in sync with `CLUEMART_MAIN_DOMAIN_URL` in utils (avoid utils↔sharing import cycle). */\nexport const SHARE_SITE_URL = \"https://cluemart.co.nz\";\n\n/** Calendar marker for invitation market-dates share copy (U+1F4C5 📅). */\nexport const SHARE_CALENDAR_ICON = \"\\u{1F4C5}\";\n\nexport const SHARE_MARKET_DATES_SECTION_HEADING = `${SHARE_CALENDAR_ICON} Market dates:`;\n\n/** Check mark for invitation requirements share copy (U+2705 ✅). */\nexport const SHARE_CHECKMARK_ICON = \"\\u{2705}\";\n\nexport const SHARE_REQUIREMENTS_SECTION_HEADING = `${SHARE_CHECKMARK_ICON} Requirements:`;\n\n/** Information marker for tags share copy (U+2139 ℹ️). */\nexport const SHARE_INFO_ICON = \"\\u{2139}\\u{FE0F}\";\n\nexport const SHARE_TAGS_SECTION_HEADING = `${SHARE_INFO_ICON} Tags:`;\n\n/** Standalone line when `rainOrShine` is true (invitation share + OG section parsing). */\nexport const SHARE_RAIN_OR_SHINE_LINE = \"Rain or Shine\";\n\n/** Standalone line when `foodTruck` is true (application share + OG section parsing). */\nexport const SHARE_FOOD_TRUCK_LINE = \"Food Truck\";\n\n/** Prefix for application compliance line (share text + OG section parsing). */\nexport const SHARE_COMPLIANCE_PREFIX = `${SHARE_CHECKMARK_ICON} Compliance:`;\n\n/** Label tag for application categories share copy (U+1F3F7 🏷️). */\nexport const SHARE_CATEGORY_ICON = \"\\u{1F3F7}\\u{FE0F}\";\n\nexport const SHARE_CATEGORIES_SECTION_HEADING = `${SHARE_CATEGORY_ICON} Categories:`;\n\n/** Straight ruler for application stall-size share copy (U+1F4CF 📏). */\nexport const SHARE_RULER_ICON = \"\\u{1F4CF}\";\n\n/** Prefix for application stall-size line (share text + OG section parsing). */\nexport const SHARE_STALL_SIZE_PREFIX = `${SHARE_RULER_ICON} Stall size:`;\n\n/** Dollar marker for price-range share copy (U+0024 $ — text, not emoji, so it stays black). */\nexport const SHARE_DOLLAR_ICON = \"$\";\n\n/** Prefix for application price-range line (share text + OG section parsing). */\nexport const SHARE_PRICE_RANGE_PREFIX = `${SHARE_DOLLAR_ICON} Price range:`;\n\n/** Alarm clock for invitation application-deadline share copy (U+23F0 ⏰). */\nexport const SHARE_CLOCK_ICON = \"\\u{23F0}\";\n\n/** Prefix for the invitation application-deadline sentence (share text + OG section parsing). */\nexport const SHARE_APPLICATION_DEADLINE_PREFIX = `${SHARE_CLOCK_ICON} Application deadline:`;\n\n/** First line of `og:description` (and legacy share-message text). */\nexport const SHARE_MORE_INFO_LINE = \"Shared from ClueMart\";\n\nexport const DEFAULT_SHARE_OG_IMAGE = `${SHARE_SITE_URL}/assets/logo.webp`;\n\nexport const RESOURCE_SHARE_TYPES = [\n \"market\",\n \"stallholder\",\n \"partner\",\n \"affiliate\",\n \"school\",\n] as const;\n\nexport type ResourceShareType = (typeof RESOURCE_SHARE_TYPES)[number];\n\nexport const POST_SHARE_RESOURCE_TYPES = [\n \"market_faces\",\n \"clue_bites\",\n \"play_and_win\",\n] as const;\n\nexport type PostShareResourceType = (typeof POST_SHARE_RESOURCE_TYPES)[number];\n\nexport type ShareResourceType =\n ResourceShareType | PostShareResourceType | RelationShareResourceType;\n\nexport const SHARE_RESOURCE_LABEL: Record<ShareResourceType, string> = {\n [RELATION_SHARE_APPLICATION]: \"Application\",\n [RELATION_SHARE_INVITATION]: \"Invitation\",\n affiliate: \"Affiliate\",\n clue_bites: \"Clue Bites\",\n market_faces: \"Market Faces\",\n market: \"Market\",\n partner: \"Partner\",\n play_and_win: \"Play & Win\",\n school: \"School\",\n stallholder: \"Stallholder\",\n};\n\nexport function isPostShareResourceType(\n resourceType: ShareResourceType,\n): resourceType is PostShareResourceType {\n return (POST_SHARE_RESOURCE_TYPES as readonly string[]).includes(\n resourceType,\n );\n}\n","import type { Category } from \"../types/global\";\n\nexport function formatCategoryLabel(category: Category): string {\n const subcategoryNames = category.subcategories\n .map((subcategory) => subcategory.name)\n .filter(Boolean);\n\n if (subcategoryNames.length === 0) {\n return category.name;\n }\n\n return `${category.name} (${subcategoryNames.join(\", \")})`;\n}\n","import { SHARE_MORE_INFO_LINE } from \"./constants\";\n\n/** Blank line between share sections — title, address, dates, URL, etc. */\nexport const SHARE_DESCRIPTION_SECTION_BREAK = \"\\n\\n\";\n\n/**\n * Line break for `og:description` meta content. Use instead of `\\n` — Next.js\n * HTML serialisation collapses literal newlines to spaces; Facebook reads `&#10;`.\n */\nexport const OG_DESCRIPTION_LINE_BREAK = \"&#10;\";\n\n/** Joins OG description sections for Facebook link cards (and Twitter). */\nexport function joinShareOgDescriptionSections(\n sections: ReadonlyArray<string | false | null | undefined>,\n): string {\n return sections\n .map((section) => (typeof section === \"string\" ? section.trim() : \"\"))\n .filter((section): section is string => section.length > 0)\n .map((section) => section.replace(/\\n/g, OG_DESCRIPTION_LINE_BREAK))\n .join(OG_DESCRIPTION_LINE_BREAK);\n}\n\nexport function joinShareDescriptionSections(\n sections: ReadonlyArray<string | false | null | undefined>,\n): string {\n return sections\n .filter((section): section is string => Boolean(section))\n .join(SHARE_DESCRIPTION_SECTION_BREAK);\n}\n\n/** Prepends the standard share branding line when assembling display text. */\nexport function appendShareMoreInfoLine(description: string): string {\n const trimmed = description.trim();\n if (trimmed.startsWith(SHARE_MORE_INFO_LINE)) {\n return trimmed;\n }\n if (trimmed.length === 0) {\n return SHARE_MORE_INFO_LINE;\n }\n return joinShareDescriptionSections([SHARE_MORE_INFO_LINE, trimmed]);\n}\n","import type { StallType } from \"../types/event\";\nimport { formatDate } from \"../utils/date\";\n\nimport {\n SHARE_APPLICATION_DEADLINE_PREFIX,\n SHARE_CATEGORIES_SECTION_HEADING,\n SHARE_FOOD_TRUCK_LINE,\n SHARE_MARKET_DATES_SECTION_HEADING,\n SHARE_RAIN_OR_SHINE_LINE,\n SHARE_REQUIREMENTS_SECTION_HEADING,\n SHARE_COMPLIANCE_PREFIX,\n SHARE_PRICE_RANGE_PREFIX,\n SHARE_STALL_SIZE_PREFIX,\n SHARE_TAGS_SECTION_HEADING,\n} from \"./constants\";\nimport { formatCategoryLabel } from \"./formatCategoryLabel\";\nimport { joinShareDescriptionSections } from \"./joinShareDescriptionSections\";\nimport type {\n ShareEventForInvitation,\n ShareEventInfoForInvitation,\n ShareVendorForApplication,\n ShareVendorInfoForApplication,\n} from \"./shareRelationTypes\";\n\nexport type {\n ShareEventForInvitation,\n ShareEventInfoForInvitation,\n ShareVendorForApplication,\n ShareVendorInfoForApplication,\n} from \"./shareRelationTypes\";\n\n/** Formats event `startDate` (`DD-MM-YYYY`) for share copy — same as share preview UI. */\nexport function formatShareMarketDate(dateStr: string): string {\n return formatDate(dateStr, \"date\");\n}\n\nexport function formatStallCapacityLabel(stallCapacity: number): string {\n return stallCapacity > 0 ? ` (${stallCapacity} spaces available)` : \" - Full\";\n}\n\nexport function formatShareStallLine(stall: StallType): string {\n return ` - ${stall.label} — $${stall.price}${formatStallCapacityLabel(stall.stallCapacity)}`;\n}\n\n/** Nested market dates + per-date stall lines for invitation share copy. */\nexport function formatShareInvitationMarketDatesSection(\n dateTime: ShareEventInfoForInvitation[\"dateTime\"],\n): string {\n const dateBlocks = dateTime.map((date) => {\n const formattedDate = date.startDate\n ? formatShareMarketDate(date.startDate)\n : \"Unknown date\";\n const stallLines = date.stallTypes.map(formatShareStallLine);\n return [`- ${formattedDate}`, ...stallLines].join(\"\\n\");\n });\n\n return `${SHARE_MARKET_DATES_SECTION_HEADING}\\n${dateBlocks.join(\"\\n\")}`;\n}\n\nexport function formatShareRainOrShineLine(\n rainOrShine: boolean,\n): string | null {\n return rainOrShine ? SHARE_RAIN_OR_SHINE_LINE : null;\n}\n\nexport function formatShareIsFoodTrueLine(foodTruck: boolean): string | null {\n return foodTruck ? SHARE_FOOD_TRUCK_LINE : null;\n}\n\n/** Bulleted requirements list for invitation share copy. */\nexport function formatShareInvitationRequirementsSection(\n requirements: ShareEventInfoForInvitation[\"requirements\"] | null,\n): string | null {\n const bullets = (requirements ?? [])\n .filter((item) => item.value)\n .map((item) => item.label.trim())\n .filter((label) => label.length > 0)\n .map((label) => `- ${label}`);\n if (bullets.length === 0) {\n return null;\n }\n return `${SHARE_REQUIREMENTS_SECTION_HEADING}\\n${bullets.join(\"\\n\")}`;\n}\n\nexport function formatShareTagsSection(tags: string[]): string | null {\n const bullets = tags\n .map((tag) => tag.trim())\n .filter((tag) => tag.length > 0)\n .map((tag) => `- ${tag}`);\n if (bullets.length === 0) {\n return null;\n }\n return `${SHARE_TAGS_SECTION_HEADING}\\n${bullets.join(\"\\n\")}`;\n}\n\n/** Bulleted categories list for application share copy. */\nexport function formatShareApplicationCategoriesSection(\n categories: ShareVendorForApplication[\"categories\"],\n): string | null {\n const bullets = categories\n .map((category) => formatCategoryLabel(category).trim())\n .filter((label) => label.length > 0)\n .map((label) => `- ${label}`);\n if (bullets.length === 0) {\n return null;\n }\n return `${SHARE_CATEGORIES_SECTION_HEADING}\\n${bullets.join(\"\\n\")}`;\n}\n\n/** Stall dimensions line for application share copy. */\nexport function formatShareApplicationStallSizeLine(size: {\n width: string;\n depth: string;\n}): string {\n return `${SHARE_STALL_SIZE_PREFIX} ${size.width}m × ${size.depth}m`;\n}\n\n/** Bulleted compliance list for application share copy. */\nexport function formatShareApplicationComplianceSection(\n compliance: ShareVendorInfoForApplication[\"compliance\"],\n): string | null {\n const bullets = [\n compliance?.liabilityInsurance && \"Liability insurance\",\n compliance?.foodBeverageLicense && \"Food & beverage licence\",\n ]\n .filter((label): label is string => Boolean(label))\n .map((label) => `- ${label}`);\n if (bullets.length === 0) {\n return null;\n }\n return `${SHARE_COMPLIANCE_PREFIX}\\n${bullets.join(\"\\n\")}`;\n}\n\n/** Price range line for application share copy. */\nexport function formatShareApplicationPriceRangeLine(priceRange: {\n min: string;\n max: string;\n}): string {\n return `${SHARE_PRICE_RANGE_PREFIX} $${priceRange.min} – $${priceRange.max}`;\n}\n\n/** Application-deadline sentence for invitation share copy. */\nexport function formatShareInvitationApplicationDeadlineLine(\n applicationDeadlineHours: number,\n): string {\n return `${SHARE_APPLICATION_DEADLINE_PREFIX} vendors must apply at least ${applicationDeadlineHours} hours before each market date.`;\n}\n\nexport function buildInvitationShareDescription(\n event: ShareEventForInvitation,\n eventInfo: ShareEventInfoForInvitation | null | undefined,\n): string {\n const stallTypes =\n eventInfo?.dateTime?.flatMap((date) => date.stallTypes) ?? [];\n\n if (!eventInfo || stallTypes.length === 0) {\n return event.description?.trim() || \"\";\n }\n\n const sections = [\n event.location.fullAddress,\n formatShareRainOrShineLine(event.rainOrShine),\n formatShareTagsSection(event.tags),\n formatShareInvitationMarketDatesSection(eventInfo.dateTime),\n formatShareInvitationApplicationDeadlineLine(\n eventInfo.applicationDeadlineHours,\n ),\n formatShareInvitationRequirementsSection(eventInfo.requirements),\n ];\n\n return joinShareDescriptionSections(sections);\n}\n\nexport function buildApplicationShareDescription(\n vendor: ShareVendorForApplication,\n vendorInfo: ShareVendorInfoForApplication | null | undefined,\n): string {\n if (!vendorInfo) {\n return vendor.description?.trim() || \"\";\n }\n\n const profileDescription = vendor.description?.trim();\n\n const sections = [\n formatShareIsFoodTrueLine(vendor.foodTruck),\n formatShareApplicationCategoriesSection(vendor.categories),\n formatShareApplicationStallSizeLine(vendorInfo.stallInfo.size),\n formatShareApplicationComplianceSection(vendorInfo.compliance),\n formatShareApplicationPriceRangeLine(vendorInfo.product.priceRange),\n profileDescription,\n ];\n\n return joinShareDescriptionSections(sections);\n}\n","import {\n formatShareIsFoodTrueLine,\n formatShareRainOrShineLine,\n formatShareTagsSection,\n} from \"./buildRelationShareDescription\";\nimport { joinShareDescriptionSections } from \"./joinShareDescriptionSections\";\n\n/** Minimal event fields for public (market) share copy — matches in-app preview. */\nexport type ShareEventForPublic = {\n description?: string | null;\n rainOrShine: boolean;\n tags: string[];\n};\n\n/** Minimal vendor fields for public (stallholder) share copy — matches in-app preview. */\nexport type ShareVendorForPublic = {\n description?: string | null;\n foodTruck: boolean;\n};\n\nexport function buildPublicEventShareDescription(\n event: ShareEventForPublic,\n): string {\n return joinShareDescriptionSections([\n formatShareRainOrShineLine(event.rainOrShine),\n formatShareTagsSection(event.tags),\n event.description?.trim(),\n ]);\n}\n\nexport function buildPublicVendorShareDescription(\n vendor: ShareVendorForPublic,\n): string {\n return joinShareDescriptionSections([\n formatShareIsFoodTrueLine(vendor.foodTruck),\n vendor.description?.trim(),\n ]);\n}\n","import {\n POST_SHARE_RESOURCE_TYPES,\n RESOURCE_SHARE_TYPES,\n SHARE_SITE_URL,\n} from \"./constants\";\nimport {\n RELATION_SHARE_APPLICATION,\n RELATION_SHARE_INVITATION,\n} from \"./relationShareTypes\";\n\n/** Path segments for public resource share URLs (markets, posts, etc.). */\nexport const PUBLIC_SHARE_PATH_TYPES = [\n ...RESOURCE_SHARE_TYPES,\n ...POST_SHARE_RESOURCE_TYPES,\n] as const;\n\nexport type PublicSharePathType = (typeof PUBLIC_SHARE_PATH_TYPES)[number];\n\nexport type RelationSharePathType =\n | typeof RELATION_SHARE_INVITATION\n | typeof RELATION_SHARE_APPLICATION;\n\nexport type ShareType = PublicSharePathType | RelationSharePathType;\n\n/** Alternation for deep-link regexes — keep in sync with {@link ShareType}. */\nexport const SHARE_TYPE_PATH_REGEX = [\n ...PUBLIC_SHARE_PATH_TYPES,\n RELATION_SHARE_APPLICATION,\n RELATION_SHARE_INVITATION,\n].join(\"|\");\n\nexport function buildShareUrl(\n type: ShareType,\n slug: string,\n utmMedium: string = \"app\",\n): string {\n return `${SHARE_SITE_URL}/${type}/${encodeURIComponent(slug)}?utm_source=share&utm_medium=${utmMedium}&utm_campaign=${type}`;\n}\n","import { joinShareOgDescriptionSections } from \"./joinShareDescriptionSections\";\n\nexport {\n joinShareOgDescriptionSections,\n OG_DESCRIPTION_LINE_BREAK,\n SHARE_DESCRIPTION_SECTION_BREAK,\n} from \"./joinShareDescriptionSections\";\n\n/** Trims share text and collapses spaces per line; preserves `\\n\\n` section breaks. */\nconst LEADING_WHITESPACE = /^[ \\t]*/;\n\nexport function normalizeShareText(value: string | null | undefined): string {\n if (value == null) {\n return \"\";\n }\n return value\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const leading = LEADING_WHITESPACE.exec(line)?.[0] ?? \"\";\n const rest = line\n .slice(leading.length)\n .trim()\n .replace(/[ \\t]{2,}/g, \" \");\n return leading + rest;\n })\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\");\n}\n\nexport function splitShareDescriptionSections(value: string): string[] {\n const trimmed = normalizeShareText(value);\n if (!trimmed) {\n return [];\n }\n\n if (/\\n\\n/.test(trimmed)) {\n return trimmed\n .split(/\\n\\n+/)\n .map((section) => normalizeShareText(section))\n .filter((section) => section.length > 0);\n }\n\n if (/\\n/.test(trimmed)) {\n return trimmed\n .split(/\\n+/)\n .map((section) => normalizeShareText(section))\n .filter((section) => section.length > 0);\n }\n\n return [trimmed];\n}\n\n/**\n * Formats share descriptions for Open Graph / Twitter meta tags.\n * Sections are joined with {@link OG_DESCRIPTION_LINE_BREAK} (`&#10;`).\n */\nexport function normalizeShareOgDescription(value: string): string {\n return joinShareOgDescriptionSections(splitShareDescriptionSections(value));\n}\n\n/**\n * Open Graph / Twitter description from pre-built section strings.\n * Prefer {@link joinShareOgDescriptionSections} when sections are already known.\n */\nexport function buildShareOgDescriptionFromSections(\n sections: ReadonlyArray<string | false | null | undefined>,\n): string {\n return joinShareOgDescriptionSections(sections);\n}\n\n/** Open Graph / Twitter description — body sections only (`og:title` is separate). */\nexport function buildShareOgDescription(\n _title: string,\n description: string,\n): string {\n return joinShareOgDescriptionSections(\n splitShareDescriptionSections(description),\n );\n}\n\n/**\n * Strips emoji and symbols that do not render reliably in overlay SVG fonts.\n * Keeps letters, numbers, whitespace, and common sentence punctuation.\n */\nexport function stripOverlaySubtitleSpecialChars(text: string): string {\n const regex = /[^\\p{L}\\p{M}\\p{N}\\s.,!?'\"&\\-():;/]/gu;\n const result = text\n .replace(/\\p{Extended_Pictographic}/gu, \"\")\n .replace(regex, \"\");\n return result.trim();\n}\n","import type { ShareResourceType } from \"./constants\";\n\nexport function normalizeShareRouteId(id: string): string {\n try {\n return decodeURIComponent(id);\n } catch {\n return id;\n }\n}\n\n/** Relative path for a public share landing page (`/market/demo-market`). */\nexport function buildSharePagePath(\n resourceType: ShareResourceType,\n slug: string,\n): string {\n return `/${resourceType}/${encodeURIComponent(normalizeShareRouteId(slug))}`;\n}\n","export type ShareSlugResource = {\n _id: string;\n};\n\nexport function shareSlugResourceId(\n resource: ShareSlugResource | null | undefined,\n): string | null {\n return resource?._id ?? null;\n}\n","/**\n * Normalizes resource `updatedAt` to an epoch-ms string for Facebook/CDN cache busting\n * on social-image URLs (`?v=`). The API ignores this query param.\n */\nexport function toShareSocialImageCacheVersion(\n updatedAt: Date | string | number | null | undefined,\n): string | null {\n if (updatedAt == null || updatedAt === \"\") {\n return null;\n }\n\n let ms: number;\n if (typeof updatedAt === \"number\") {\n ms = updatedAt;\n } else if (updatedAt instanceof Date) {\n ms = updatedAt.getTime();\n } else {\n ms = Date.parse(String(updatedAt));\n }\n\n if (!Number.isFinite(ms)) {\n return null;\n }\n\n return String(Math.trunc(ms));\n}\n"],"mappings":"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","names":["import_dayjs","import_customParseFormat","dayjs","customParseFormat","isoWeek","dayjs","customParseFormat","utc","timezone","isSameOrAfter","dayjs"]}
1
+ {"version":3,"sources":["../../src/sharing/index.ts","../../src/utils/date.ts","../../src/utils/dateFormats.ts","../../src/utils/eventDateStatus.ts","../../src/sharing/relationShareTypes.ts","../../src/sharing/constants.ts","../../src/sharing/formatCategoryLabel.ts","../../src/sharing/joinShareDescriptionSections.ts","../../src/sharing/buildRelationShareDescription.ts","../../src/sharing/buildPublicShareDescription.ts","../../src/sharing/buildShareUrl.ts","../../src/sharing/normalizeShareDescription.ts","../../src/sharing/normalizeShareRouteId.ts","../../src/sharing/shareSlugResource.ts","../../src/sharing/toShareSocialImageCacheVersion.ts"],"sourcesContent":["export * from \"./buildPublicShareDescription\";\nexport * from \"./buildRelationShareDescription\";\nexport * from \"./shareRelationTypes\";\nexport * from \"./buildShareUrl\";\nexport * from \"./constants\";\nexport * from \"./formatCategoryLabel\";\nexport * from \"./joinShareDescriptionSections\";\nexport * from \"./normalizeShareDescription\";\nexport * from \"./normalizeShareRouteId\";\nexport * from \"./relationShareTypes\";\nexport * from \"./shareSlugResource\";\nexport * from \"./toShareSocialImageCacheVersion\";\n","import dayjs from \"dayjs\";\nimport customParseFormat from \"dayjs/plugin/customParseFormat.js\";\nimport isSameOrAfter from \"dayjs/plugin/isSameOrAfter.js\";\nimport timezone from \"dayjs/plugin/timezone.js\";\nimport utc from \"dayjs/plugin/utc.js\";\n\nimport { EnumEventDateStatus } from \"src/enums\";\n\nimport { dateFormat, timeFormat, isoDateFormat } from \"./dateFormats\";\nimport {\n FUTURE_EVENT_DATE_STATUSES,\n EVENT_DATE_STATUS_LABELS,\n} from \"./eventDateStatus\";\n\nexport { dateFormat, timeFormat, isoDateFormat };\n\n// Enable custom format parsing\ndayjs.extend(customParseFormat);\ndayjs.extend(utc);\ndayjs.extend(timezone);\ndayjs.extend(isSameOrAfter);\n\nexport const NZ_TZ = \"Pacific/Auckland\";\n\nexport function toNZTime(date?: Date | string) {\n return date ? dayjs(date).tz(NZ_TZ) : dayjs().tz(NZ_TZ);\n}\n\n/** Start of the calendar day in Pacific/Auckland (daily games, streaks, etc.). */\nexport function nzStartOfDay(\n input?: Date | string | number | null,\n): dayjs.Dayjs {\n if (input == null) {\n return dayjs().tz(NZ_TZ).startOf(\"day\");\n }\n return dayjs.tz(input, NZ_TZ).startOf(\"day\");\n}\n\n/**\n * UTC-midnight of the given instant's NZ calendar day (today when omitted).\n * This is the canonical \"day key\" encoding used by ad bookings and metrics:\n * stored as a UTC Date, rendered with `.toISOString().slice(0, 10)`.\n */\nexport function nzDayKey(input?: Date | string | number | null): Date {\n const day =\n input == null\n ? dayjs().tz(NZ_TZ).format(isoDateFormat)\n : dayjs.tz(input, NZ_TZ).format(isoDateFormat);\n return new Date(`${day}T00:00:00Z`);\n}\n\n/**\n * Type guard for the canonical \"day key\" encoding: a valid Date that is\n * exactly UTC midnight. Accepts `unknown` so it can validate untrusted values\n * (GraphQL/JSON inputs) without a preceding cast.\n */\nexport function isUtcMidnight(value: unknown): value is Date {\n return (\n value instanceof Date &&\n !Number.isNaN(value.getTime()) &&\n value.getUTCHours() === 0 &&\n value.getUTCMinutes() === 0 &&\n value.getUTCSeconds() === 0 &&\n value.getUTCMilliseconds() === 0\n );\n}\n\ntype DateFormat = \"date\" | \"time\" | \"datetime\";\n\n/**\n * Format a date string to a more readable format.\n * @param dateStr - the date string\n * @param timeStr - optional time string\n * @param display - 'date' | 'time' | 'datetime'\n * @returns formatted string based on display option\n */\nexport const formatDate = (\n dateStr: string,\n display: DateFormat = \"datetime\",\n timeStr?: string,\n) => {\n // Combine date and time into a single string if time is provided\n const dateTimeStr = timeStr ? `${dateStr} ${timeStr}` : dateStr;\n\n // Parse with formats\n const dateTime = timeStr\n ? dayjs(dateTimeStr, `${dateFormat} ${timeFormat}`)\n : dayjs(dateStr, dateFormat);\n\n // Format parts\n const formattedDate = dateTime.format(\"dddd, D MMMM, YYYY\");\n const formattedTime = dateTime.format(\"h:mm a\");\n\n // Return based on display option\n switch (display) {\n case \"date\":\n return formattedDate;\n case \"time\":\n return formattedTime;\n case \"datetime\":\n return `${formattedDate} at ${formattedTime}`;\n default:\n return formattedDate;\n }\n};\n\n/**\n * Formats accepted for a stored slot. The canonical writer is\n * `dayjs(...).format(`${dateFormat} ${timeFormat}`)`; the variants cover legacy\n * rows with an unpadded hour or trailing seconds. Each is parsed **strictly**, so\n * impossible values (`31-02`, `25:00`) are rejected instead of rolling over into\n * a real instant.\n */\nconst SLOT_FORMATS = [\n `${dateFormat} ${timeFormat}`,\n `${dateFormat} H:mm`,\n `${dateFormat} ${timeFormat}:ss`,\n `${dateFormat} H:mm:ss`,\n];\n\nconst parseSlot = (date: {\n startDate?: string;\n startTime?: string;\n}): dayjs.Dayjs | null => {\n if (!date.startDate || !date.startTime) {\n return null;\n }\n\n const value = `${date.startDate} ${date.startTime}`;\n\n for (const format of SLOT_FORMATS) {\n const parsed = dayjs(value, format, true);\n\n if (parsed.isValid()) {\n return parsed;\n }\n }\n\n return null;\n};\n\n/**\n * Shared \"starts now or later\" rule for `dd-mm-yyyy` + `HH:mm` slots. `now` is\n * passed in so a caller filtering a list compares every slot against the same\n * instant. A slot with a missing or unparseable date/time is never future.\n */\nconst isFutureDateTime = (\n date: { startDate?: string; startTime?: string },\n now: dayjs.Dayjs,\n): boolean => {\n const slot = parseSlot(date);\n\n return slot ? slot.isSameOrAfter(now) : false;\n};\n\nexport const getCurrentAndFutureDates = <\n T extends { startDate?: string; startTime?: string },\n>(\n dates: T[],\n): T[] => {\n const now = dayjs(); // current date and time\n\n return dates.filter((dateObj) => isFutureDateTime(dateObj, now));\n};\n\n/**\n * True when the slot starts now or later. Status-agnostic: used to hide past\n * relation dates from reads while the records stay in the database.\n */\nexport const isFutureStartDate = (date: {\n startDate?: string;\n startTime?: string;\n}): boolean => isFutureDateTime(date, dayjs());\n\nexport const isFutureDatesBeforeThreshold = (\n date: {\n startDate?: string;\n startTime?: string;\n },\n minHoursFromNow: number,\n): boolean => {\n if (!date.startDate || !date.startTime) {\n return false;\n }\n\n const threshold = minHoursFromNow\n ? dayjs().add(minHoursFromNow, \"hour\")\n : dayjs().startOf(\"day\");\n\n const dateTime = dayjs(\n `${date.startDate} ${date.startTime}`,\n `${dateFormat} ${timeFormat}`,\n );\n\n return dateTime.isSameOrAfter(threshold);\n};\n\nexport const formatTimestamp = (timestamp: string) => {\n const formattedDate = toNZTime(timestamp).format(dateFormat);\n\n return formatDate(formattedDate, \"date\");\n};\n\nexport const isIsoDateString = (value: unknown): value is string => {\n return typeof value === \"string\" && !Number.isNaN(Date.parse(value));\n};\n\n/**\n * Sort an array of date strings by their proximity to the current date.\n * @param dates - The array of date strings to sort.\n * @returns - The sorted array of date strings.\n */\nexport function sortDatesChronologically<\n T extends { startDate?: string; startTime?: string },\n>(dates: T[]): T[] {\n if (!dates?.length) {\n return [];\n }\n\n return [...dates].sort((a, b) => {\n const dateTimeFormat = `${dateFormat} ${timeFormat}`;\n const dateA =\n a.startDate && a.startTime\n ? dayjs(`${a.startDate} ${a.startTime}`, dateTimeFormat)\n : null;\n const dateB =\n b.startDate && b.startTime\n ? dayjs(`${b.startDate} ${b.startTime}`, dateTimeFormat)\n : null;\n\n if (dateA && dateB) {\n return dateA.valueOf() - dateB.valueOf();\n }\n\n if (dateA) {\n return -1;\n }\n\n if (dateB) {\n return 1;\n }\n\n return 0;\n });\n}\n\n/**\n * Start dates of the given date/time slots, chronological and de-duplicated.\n * Slots that share a `startDate` (e.g. two stall slots on the same day) collapse\n * into a single entry.\n */\nexport function getUniqueStartDates(\n dates: Array<{ startDate?: string; startTime?: string }> | null | undefined,\n): string[] {\n const seenStartDates = new Set<string>();\n const uniqueStartDates: string[] = [];\n\n for (const { startDate } of sortDatesChronologically(dates ?? [])) {\n if (!startDate || seenStartDates.has(startDate)) {\n continue;\n }\n\n seenStartDates.add(startDate);\n uniqueStartDates.push(startDate);\n }\n\n return uniqueStartDates;\n}\n\n/**\n * UI filter options for user-activity event periods. Derived from the canonical\n * {@link FUTURE_EVENT_DATE_STATUSES} list, minus `STARTING_SOON` (a presentational\n * choice: \"starting soon\" is surfaced separately rather than as a filter).\n */\nexport const futureTimePeriods = FUTURE_EVENT_DATE_STATUSES.filter(\n (status) => status !== EnumEventDateStatus.STARTING_SOON,\n).map((status) => ({\n label: EVENT_DATE_STATUS_LABELS[status],\n value: status,\n}));\n","export const dateFormat = \"DD-MM-YYYY\";\nexport const timeFormat = \"HH:mm\";\n/** ISO 8601 machine/sortable format: day keys, Date construction, strict parsing, HTML date inputs. */\nexport const isoDateFormat = \"YYYY-MM-DD\";\n","import dayjs from \"dayjs\";\nimport customParseFormat from \"dayjs/plugin/customParseFormat.js\";\nimport isoWeek from \"dayjs/plugin/isoWeek.js\";\n\nimport { EnumEventDateStatus } from \"../enums\";\nimport type { DateTimeType } from \"../types/global\";\n\nimport { dateFormat, timeFormat } from \"./dateFormats\";\n\n// Enable dayjs plugins used by event date parsing/status logic\ndayjs.extend(customParseFormat);\ndayjs.extend(isoWeek);\n\n/**\n * Display label per date status. Single source of truth instead of deriving\n * the label with `replaceAll(\"_\", \" \")` / text-transform tricks, so each\n * status maps to an explicit, correctly-cased string (e.g. \"This Week\",\n * \"Starting Soon\", \"Rescheduled\"). Lives here (not in `calendar`) so both\n * `calendar/eventCalendar` and `utils/date` can consume it without a circular\n * import.\n */\nexport const EVENT_DATE_STATUS_LABELS: Record<EnumEventDateStatus, string> = {\n [EnumEventDateStatus.STARTING_SOON]: \"Starting Soon\",\n [EnumEventDateStatus.STARTED]: \"Started\",\n [EnumEventDateStatus.TODAY]: \"Today\",\n [EnumEventDateStatus.TOMORROW]: \"Tomorrow\",\n [EnumEventDateStatus.THIS_WEEK]: \"This Week\",\n [EnumEventDateStatus.NEXT_WEEK]: \"Next Week\",\n [EnumEventDateStatus.UPCOMING]: \"Upcoming\",\n [EnumEventDateStatus.ENDED]: \"Ended\",\n [EnumEventDateStatus.RE_SCHEDULED]: \"Rescheduled\",\n [EnumEventDateStatus.CANCELED]: \"Canceled\",\n [EnumEventDateStatus.INVALID]: \"Invalid\",\n};\n\nexport const EVENT_DATE_STATUS_PERIOD_MAP: Partial<\n Record<EnumEventDateStatus, EnumEventDateStatus[]>\n> = {\n [EnumEventDateStatus.TODAY]: [\n EnumEventDateStatus.STARTED,\n EnumEventDateStatus.STARTING_SOON,\n EnumEventDateStatus.TODAY,\n ],\n [EnumEventDateStatus.THIS_WEEK]: [\n EnumEventDateStatus.STARTED,\n EnumEventDateStatus.STARTING_SOON,\n EnumEventDateStatus.THIS_WEEK,\n EnumEventDateStatus.TODAY,\n EnumEventDateStatus.TOMORROW,\n ],\n};\n\nexport function getAllowedEventDateStatuses(\n period: EnumEventDateStatus,\n): EnumEventDateStatus[] {\n return EVENT_DATE_STATUS_PERIOD_MAP[period] ?? [period];\n}\n\nexport function eventMatchesDateStatusPeriod(\n dateTime: Pick<DateTimeType, \"dateStatus\">[] | null | undefined,\n period: EnumEventDateStatus,\n): boolean {\n const allowedStatuses = getAllowedEventDateStatuses(period);\n\n return (\n dateTime?.some((dt) => allowedStatuses.includes(dt.dateStatus)) ?? false\n );\n}\n\nexport function filterEventsByDateStatusPeriod<\n T extends { dateTime?: Pick<DateTimeType, \"dateStatus\">[] | null },\n>(events: T[], period: EnumEventDateStatus): T[] {\n return events.filter((event) =>\n eventMatchesDateStatusPeriod(event.dateTime, period),\n );\n}\n\n/**\n * Canonical list of date statuses whose start date/time is still in the future\n * (i.e. the slot has not started yet). `STARTED`, `ENDED`, `INVALID`,\n * `CANCELED` and `RE_SCHEDULED` are intentionally excluded.\n *\n * This is the single source of truth for \"is this slot still in the future\".\n * `futureTimePeriods` (in `utils/date.ts`) derives from this list for UI\n * filters, additionally excluding `STARTING_SOON`.\n */\nexport const FUTURE_EVENT_DATE_STATUSES: readonly EnumEventDateStatus[] = [\n EnumEventDateStatus.STARTING_SOON,\n EnumEventDateStatus.TODAY,\n EnumEventDateStatus.TOMORROW,\n EnumEventDateStatus.THIS_WEEK,\n EnumEventDateStatus.NEXT_WEEK,\n EnumEventDateStatus.UPCOMING,\n];\n\nexport function isFutureEventDateStatus(\n dateStatus: EnumEventDateStatus | null | undefined,\n): boolean {\n return Boolean(dateStatus && FUTURE_EVENT_DATE_STATUSES.includes(dateStatus));\n}\n\nexport function filterDatesByFutureStatus<\n T extends { dateStatus?: EnumEventDateStatus | null },\n>(dates: T[]): T[] {\n return dates.filter((date) => isFutureEventDateStatus(date.dateStatus));\n}\n\n/**\n * Statuses set by an explicit user action rather than derived from the\n * schedule clock. The status recomputation below must leave them untouched,\n * otherwise a canceled/rescheduled date would silently flip back to a\n * time-derived status on the next run.\n */\nexport const MANUAL_DATE_STATUSES: ReadonlySet<EnumEventDateStatus> = new Set([\n EnumEventDateStatus.CANCELED,\n EnumEventDateStatus.RE_SCHEDULED,\n]);\n\n/**\n * Builds the event schedule that relation dates should be matched against.\n *\n * Manual statuses (`MANUAL_DATE_STATUSES`: CANCELED and RE_SCHEDULED) stay on\n * the live `Event.dateTime` for visibility, but a relation date pointing at\n * them is treated as removed. Archived (ended) slots are appended separately\n * so relation dates for archived history still match instead of being wrongly\n * marked canceled.\n *\n * Shared by the event-update relation sync and the cron orphan-date cleanup so\n * both use the same archive-aware schedule.\n */\nexport function buildRelationEligibleEventSchedule<\n T extends { dateStatus?: EnumEventDateStatus | null },\n>(liveDateTime: T[], archivedDateTime: T[] = []): T[] {\n const eligible = liveDateTime.filter(\n (slot) => !slot.dateStatus || !MANUAL_DATE_STATUSES.has(slot.dateStatus),\n );\n return [...eligible, ...archivedDateTime];\n}\n\n/**\n * Determines the dateStatus for a future event date\n * @param startDateTime The start date-time of the event\n * @param now The current date-time\n * @returns The appropriate EnumEventDateStatus\n */\nexport function getFutureEventStatus(\n startDateTime: dayjs.Dayjs,\n now: dayjs.Dayjs,\n): EnumEventDateStatus {\n const hoursUntilStart = startDateTime.diff(now, \"hour\", true);\n if (hoursUntilStart > 0 && hoursUntilStart <= 4) {\n return EnumEventDateStatus.STARTING_SOON;\n }\n\n if (startDateTime.isSame(now, \"day\")) {\n return EnumEventDateStatus.TODAY;\n }\n\n if (startDateTime.isSame(now.add(1, \"day\"), \"day\")) {\n return EnumEventDateStatus.TOMORROW;\n }\n\n if (startDateTime.isSame(now, \"isoWeek\")) {\n return EnumEventDateStatus.THIS_WEEK;\n }\n\n if (startDateTime.isSame(now.add(1, \"week\"), \"isoWeek\")) {\n return EnumEventDateStatus.NEXT_WEEK;\n }\n\n return EnumEventDateStatus.UPCOMING;\n}\n\n/**\n * Updates the dateStatus field for a single DateTimeType object based on the current date/time.\n * Manual statuses (CANCELED, RE_SCHEDULED) are preserved and returned as-is.\n * @param dateTime The date-time object to update\n * @returns A new object with an updated dateStatus value, or the original\n * object when the stored status is a manual override\n */\nexport function updateSingleDateTimeStatus<T extends DateTimeType>(\n dateTime: T,\n now: dayjs.Dayjs = dayjs(),\n): T {\n if (MANUAL_DATE_STATUSES.has(dateTime.dateStatus)) {\n return dateTime;\n }\n\n // Parse start and end date-time\n const dateTimeFormat = `${dateFormat} ${timeFormat}`;\n const startDateTime = dayjs(\n `${dateTime.startDate} ${dateTime.startTime}`,\n dateTimeFormat,\n true,\n );\n const endDateTime = dayjs(\n `${dateTime.endDate} ${dateTime.endTime}`,\n dateTimeFormat,\n true,\n );\n\n // Skip if dates are invalid\n if (!startDateTime.isValid() || !endDateTime.isValid()) {\n return {\n ...dateTime,\n dateStatus: EnumEventDateStatus.INVALID,\n };\n }\n\n if (endDateTime.isBefore(startDateTime)) {\n return {\n ...dateTime,\n dateStatus: EnumEventDateStatus.INVALID,\n };\n }\n\n let dateStatus: EnumEventDateStatus;\n\n if (endDateTime.isAfter(now)) {\n if (startDateTime.isAfter(now)) {\n // Event has not started yet\n dateStatus = getFutureEventStatus(startDateTime, now);\n } else {\n // Event is in progress\n dateStatus = EnumEventDateStatus.STARTED;\n }\n } else {\n // Event has ended\n dateStatus = EnumEventDateStatus.ENDED;\n }\n\n return {\n ...dateTime,\n dateStatus,\n };\n}\n","/** Path segments for relation share URLs — must match mobile `RelationTitle` values. */\nexport const RELATION_SHARE_INVITATION = \"invitation\" as const;\nexport const RELATION_SHARE_APPLICATION = \"application\" as const;\n\n/**\n * Bump when relation overlay SVG/layout changes so og:image URLs and API ETags\n * invalidate after deploy. Facebook caches preview JPEGs by exact URL — changing\n * this value forces crawlers to fetch a fresh image after server deploy.\n */\nexport const RELATION_OVERLAY_LAYOUT_VERSION = 43;\n\nexport const RELATION_SHARE_RESOURCE_TYPES = [\n RELATION_SHARE_INVITATION,\n RELATION_SHARE_APPLICATION,\n] as const;\n\nexport type RelationShareResourceType =\n (typeof RELATION_SHARE_RESOURCE_TYPES)[number];\n\nexport function isRelationShareResourceType(\n resourceType: string,\n): resourceType is RelationShareResourceType {\n return (RELATION_SHARE_RESOURCE_TYPES as readonly string[]).includes(\n resourceType,\n );\n}\n","import {\n RELATION_SHARE_APPLICATION,\n RELATION_SHARE_INVITATION,\n type RelationShareResourceType,\n} from \"./relationShareTypes\";\n\nexport {\n RELATION_SHARE_APPLICATION,\n RELATION_SHARE_INVITATION,\n RELATION_OVERLAY_LAYOUT_VERSION,\n RELATION_SHARE_RESOURCE_TYPES,\n isRelationShareResourceType,\n type RelationShareResourceType,\n} from \"./relationShareTypes\";\n\n/** Open Graph recommended preview size (must match admin `SHARE_OG_IMAGE_*`). */\nexport const SOCIAL_IMAGE_WIDTH = 1200;\n\n/** Open Graph recommended preview size (must match admin `SHARE_OG_IMAGE_*`). */\nexport const SOCIAL_IMAGE_HEIGHT = 630;\n\n/** Keep in sync with `CLUEMART_MAIN_DOMAIN_URL` in utils (avoid utils↔sharing import cycle). */\nexport const SHARE_SITE_URL = \"https://cluemart.co.nz\";\n\n/** Calendar marker for invitation market-dates share copy (U+1F4C5 📅). */\nexport const SHARE_CALENDAR_ICON = \"\\u{1F4C5}\";\n\nexport const SHARE_MARKET_DATES_SECTION_HEADING = `${SHARE_CALENDAR_ICON} Market dates:`;\n\n/** Check mark for invitation requirements share copy (U+2705 ✅). */\nexport const SHARE_CHECKMARK_ICON = \"\\u{2705}\";\n\nexport const SHARE_REQUIREMENTS_SECTION_HEADING = `${SHARE_CHECKMARK_ICON} Requirements:`;\n\n/** Information marker for tags share copy (U+2139 ℹ️). */\nexport const SHARE_INFO_ICON = \"\\u{2139}\\u{FE0F}\";\n\nexport const SHARE_TAGS_SECTION_HEADING = `${SHARE_INFO_ICON} Tags:`;\n\n/** Standalone line when `rainOrShine` is true (invitation share + OG section parsing). */\nexport const SHARE_RAIN_OR_SHINE_LINE = \"Rain or Shine\";\n\n/** Standalone line when `foodTruck` is true (application share + OG section parsing). */\nexport const SHARE_FOOD_TRUCK_LINE = \"Food Truck\";\n\n/** Prefix for application compliance line (share text + OG section parsing). */\nexport const SHARE_COMPLIANCE_PREFIX = `${SHARE_CHECKMARK_ICON} Compliance:`;\n\n/** Label tag for application categories share copy (U+1F3F7 🏷️). */\nexport const SHARE_CATEGORY_ICON = \"\\u{1F3F7}\\u{FE0F}\";\n\nexport const SHARE_CATEGORIES_SECTION_HEADING = `${SHARE_CATEGORY_ICON} Categories:`;\n\n/** Straight ruler for application stall-size share copy (U+1F4CF 📏). */\nexport const SHARE_RULER_ICON = \"\\u{1F4CF}\";\n\n/** Prefix for application stall-size line (share text + OG section parsing). */\nexport const SHARE_STALL_SIZE_PREFIX = `${SHARE_RULER_ICON} Stall size:`;\n\n/** Dollar marker for price-range share copy (U+0024 $ — text, not emoji, so it stays black). */\nexport const SHARE_DOLLAR_ICON = \"$\";\n\n/** Prefix for application price-range line (share text + OG section parsing). */\nexport const SHARE_PRICE_RANGE_PREFIX = `${SHARE_DOLLAR_ICON} Price range:`;\n\n/** Alarm clock for invitation application-deadline share copy (U+23F0 ⏰). */\nexport const SHARE_CLOCK_ICON = \"\\u{23F0}\";\n\n/** Prefix for the invitation application-deadline sentence (share text + OG section parsing). */\nexport const SHARE_APPLICATION_DEADLINE_PREFIX = `${SHARE_CLOCK_ICON} Application deadline:`;\n\n/** First line of `og:description` (and legacy share-message text). */\nexport const SHARE_MORE_INFO_LINE = \"Shared from ClueMart\";\n\nexport const DEFAULT_SHARE_OG_IMAGE = `${SHARE_SITE_URL}/assets/logo.webp`;\n\nexport const RESOURCE_SHARE_TYPES = [\n \"market\",\n \"stallholder\",\n \"partner\",\n \"affiliate\",\n \"school\",\n] as const;\n\nexport type ResourceShareType = (typeof RESOURCE_SHARE_TYPES)[number];\n\nexport const POST_SHARE_RESOURCE_TYPES = [\n \"market_faces\",\n \"clue_bites\",\n \"play_and_win\",\n] as const;\n\nexport type PostShareResourceType = (typeof POST_SHARE_RESOURCE_TYPES)[number];\n\nexport type ShareResourceType =\n ResourceShareType | PostShareResourceType | RelationShareResourceType;\n\nexport const SHARE_RESOURCE_LABEL: Record<ShareResourceType, string> = {\n [RELATION_SHARE_APPLICATION]: \"Application\",\n [RELATION_SHARE_INVITATION]: \"Invitation\",\n affiliate: \"Affiliate\",\n clue_bites: \"Clue Bites\",\n market_faces: \"Market Faces\",\n market: \"Market\",\n partner: \"Partner\",\n play_and_win: \"Play & Win\",\n school: \"School\",\n stallholder: \"Stallholder\",\n};\n\nexport function isPostShareResourceType(\n resourceType: ShareResourceType,\n): resourceType is PostShareResourceType {\n return (POST_SHARE_RESOURCE_TYPES as readonly string[]).includes(\n resourceType,\n );\n}\n","import type { Category } from \"../types/global\";\n\nexport function formatCategoryLabel(category: Category): string {\n const subcategoryNames = category.subcategories\n .map((subcategory) => subcategory.name)\n .filter(Boolean);\n\n if (subcategoryNames.length === 0) {\n return category.name;\n }\n\n return `${category.name} (${subcategoryNames.join(\", \")})`;\n}\n","import { SHARE_MORE_INFO_LINE } from \"./constants\";\n\n/** Blank line between share sections — title, address, dates, URL, etc. */\nexport const SHARE_DESCRIPTION_SECTION_BREAK = \"\\n\\n\";\n\n/**\n * Line break for `og:description` meta content. Use instead of `\\n` — Next.js\n * HTML serialisation collapses literal newlines to spaces; Facebook reads `&#10;`.\n */\nexport const OG_DESCRIPTION_LINE_BREAK = \"&#10;\";\n\n/** Joins OG description sections for Facebook link cards (and Twitter). */\nexport function joinShareOgDescriptionSections(\n sections: ReadonlyArray<string | false | null | undefined>,\n): string {\n return sections\n .map((section) => (typeof section === \"string\" ? section.trim() : \"\"))\n .filter((section): section is string => section.length > 0)\n .map((section) => section.replace(/\\n/g, OG_DESCRIPTION_LINE_BREAK))\n .join(OG_DESCRIPTION_LINE_BREAK);\n}\n\nexport function joinShareDescriptionSections(\n sections: ReadonlyArray<string | false | null | undefined>,\n): string {\n return sections\n .filter((section): section is string => Boolean(section))\n .join(SHARE_DESCRIPTION_SECTION_BREAK);\n}\n\n/** Prepends the standard share branding line when assembling display text. */\nexport function appendShareMoreInfoLine(description: string): string {\n const trimmed = description.trim();\n if (trimmed.startsWith(SHARE_MORE_INFO_LINE)) {\n return trimmed;\n }\n if (trimmed.length === 0) {\n return SHARE_MORE_INFO_LINE;\n }\n return joinShareDescriptionSections([SHARE_MORE_INFO_LINE, trimmed]);\n}\n","import type { StallType } from \"../types/event\";\nimport { formatDate } from \"../utils/date\";\n\nimport {\n SHARE_APPLICATION_DEADLINE_PREFIX,\n SHARE_CATEGORIES_SECTION_HEADING,\n SHARE_FOOD_TRUCK_LINE,\n SHARE_MARKET_DATES_SECTION_HEADING,\n SHARE_RAIN_OR_SHINE_LINE,\n SHARE_REQUIREMENTS_SECTION_HEADING,\n SHARE_COMPLIANCE_PREFIX,\n SHARE_PRICE_RANGE_PREFIX,\n SHARE_STALL_SIZE_PREFIX,\n SHARE_TAGS_SECTION_HEADING,\n} from \"./constants\";\nimport { formatCategoryLabel } from \"./formatCategoryLabel\";\nimport { joinShareDescriptionSections } from \"./joinShareDescriptionSections\";\nimport type {\n ShareEventForInvitation,\n ShareEventInfoForInvitation,\n ShareVendorForApplication,\n ShareVendorInfoForApplication,\n} from \"./shareRelationTypes\";\n\nexport type {\n ShareEventForInvitation,\n ShareEventInfoForInvitation,\n ShareVendorForApplication,\n ShareVendorInfoForApplication,\n} from \"./shareRelationTypes\";\n\n/** Formats event `startDate` (`DD-MM-YYYY`) for share copy — same as share preview UI. */\nexport function formatShareMarketDate(dateStr: string): string {\n return formatDate(dateStr, \"date\");\n}\n\nexport function formatStallCapacityLabel(stallCapacity: number): string {\n return stallCapacity > 0 ? ` (${stallCapacity} spaces available)` : \" - Full\";\n}\n\nexport function formatShareStallLine(stall: StallType): string {\n return ` - ${stall.label} — $${stall.price}${formatStallCapacityLabel(stall.stallCapacity)}`;\n}\n\n/** Nested market dates + per-date stall lines for invitation share copy. */\nexport function formatShareInvitationMarketDatesSection(\n dateTime: ShareEventInfoForInvitation[\"dateTime\"],\n): string {\n const dateBlocks = dateTime.map((date) => {\n const formattedDate = date.startDate\n ? formatShareMarketDate(date.startDate)\n : \"Unknown date\";\n const stallLines = date.stallTypes.map(formatShareStallLine);\n return [`- ${formattedDate}`, ...stallLines].join(\"\\n\");\n });\n\n return `${SHARE_MARKET_DATES_SECTION_HEADING}\\n${dateBlocks.join(\"\\n\")}`;\n}\n\nexport function formatShareRainOrShineLine(\n rainOrShine: boolean,\n): string | null {\n return rainOrShine ? SHARE_RAIN_OR_SHINE_LINE : null;\n}\n\nexport function formatShareIsFoodTrueLine(foodTruck: boolean): string | null {\n return foodTruck ? SHARE_FOOD_TRUCK_LINE : null;\n}\n\n/** Bulleted requirements list for invitation share copy. */\nexport function formatShareInvitationRequirementsSection(\n requirements: ShareEventInfoForInvitation[\"requirements\"] | null,\n): string | null {\n const bullets = (requirements ?? [])\n .filter((item) => item.value)\n .map((item) => item.label.trim())\n .filter((label) => label.length > 0)\n .map((label) => `- ${label}`);\n if (bullets.length === 0) {\n return null;\n }\n return `${SHARE_REQUIREMENTS_SECTION_HEADING}\\n${bullets.join(\"\\n\")}`;\n}\n\nexport function formatShareTagsSection(tags: string[]): string | null {\n const bullets = tags\n .map((tag) => tag.trim())\n .filter((tag) => tag.length > 0)\n .map((tag) => `- ${tag}`);\n if (bullets.length === 0) {\n return null;\n }\n return `${SHARE_TAGS_SECTION_HEADING}\\n${bullets.join(\"\\n\")}`;\n}\n\n/** Bulleted categories list for application share copy. */\nexport function formatShareApplicationCategoriesSection(\n categories: ShareVendorForApplication[\"categories\"],\n): string | null {\n const bullets = categories\n .map((category) => formatCategoryLabel(category).trim())\n .filter((label) => label.length > 0)\n .map((label) => `- ${label}`);\n if (bullets.length === 0) {\n return null;\n }\n return `${SHARE_CATEGORIES_SECTION_HEADING}\\n${bullets.join(\"\\n\")}`;\n}\n\n/** Stall dimensions line for application share copy. */\nexport function formatShareApplicationStallSizeLine(size: {\n width: string;\n depth: string;\n}): string {\n return `${SHARE_STALL_SIZE_PREFIX} ${size.width}m × ${size.depth}m`;\n}\n\n/** Bulleted compliance list for application share copy. */\nexport function formatShareApplicationComplianceSection(\n compliance: ShareVendorInfoForApplication[\"compliance\"],\n): string | null {\n const bullets = [\n compliance?.liabilityInsurance && \"Liability insurance\",\n compliance?.foodBeverageLicense && \"Food & beverage licence\",\n ]\n .filter((label): label is string => Boolean(label))\n .map((label) => `- ${label}`);\n if (bullets.length === 0) {\n return null;\n }\n return `${SHARE_COMPLIANCE_PREFIX}\\n${bullets.join(\"\\n\")}`;\n}\n\n/** Price range line for application share copy. */\nexport function formatShareApplicationPriceRangeLine(priceRange: {\n min: string;\n max: string;\n}): string {\n return `${SHARE_PRICE_RANGE_PREFIX} $${priceRange.min} – $${priceRange.max}`;\n}\n\n/** Application-deadline sentence for invitation share copy. */\nexport function formatShareInvitationApplicationDeadlineLine(\n applicationDeadlineHours: number,\n): string {\n return `${SHARE_APPLICATION_DEADLINE_PREFIX} vendors must apply at least ${applicationDeadlineHours} hours before each market date.`;\n}\n\nexport function buildInvitationShareDescription(\n event: ShareEventForInvitation,\n eventInfo: ShareEventInfoForInvitation | null | undefined,\n): string {\n const stallTypes =\n eventInfo?.dateTime?.flatMap((date) => date.stallTypes) ?? [];\n\n if (!eventInfo || stallTypes.length === 0) {\n return event.description?.trim() || \"\";\n }\n\n const sections = [\n event.location.fullAddress,\n formatShareRainOrShineLine(event.rainOrShine),\n formatShareTagsSection(event.tags),\n formatShareInvitationMarketDatesSection(eventInfo.dateTime),\n formatShareInvitationApplicationDeadlineLine(\n eventInfo.applicationDeadlineHours,\n ),\n formatShareInvitationRequirementsSection(eventInfo.requirements),\n ];\n\n return joinShareDescriptionSections(sections);\n}\n\nexport function buildApplicationShareDescription(\n vendor: ShareVendorForApplication,\n vendorInfo: ShareVendorInfoForApplication | null | undefined,\n): string {\n if (!vendorInfo) {\n return vendor.description?.trim() || \"\";\n }\n\n const profileDescription = vendor.description?.trim();\n\n const sections = [\n formatShareIsFoodTrueLine(vendor.foodTruck),\n formatShareApplicationCategoriesSection(vendor.categories),\n formatShareApplicationStallSizeLine(vendorInfo.stallInfo.size),\n formatShareApplicationComplianceSection(vendorInfo.compliance),\n formatShareApplicationPriceRangeLine(vendorInfo.product.priceRange),\n profileDescription,\n ];\n\n return joinShareDescriptionSections(sections);\n}\n","import {\n formatShareIsFoodTrueLine,\n formatShareRainOrShineLine,\n formatShareTagsSection,\n} from \"./buildRelationShareDescription\";\nimport { joinShareDescriptionSections } from \"./joinShareDescriptionSections\";\n\n/** Minimal event fields for public (market) share copy — matches in-app preview. */\nexport type ShareEventForPublic = {\n description?: string | null;\n rainOrShine: boolean;\n tags: string[];\n};\n\n/** Minimal vendor fields for public (stallholder) share copy — matches in-app preview. */\nexport type ShareVendorForPublic = {\n description?: string | null;\n foodTruck: boolean;\n};\n\nexport function buildPublicEventShareDescription(\n event: ShareEventForPublic,\n): string {\n return joinShareDescriptionSections([\n formatShareRainOrShineLine(event.rainOrShine),\n formatShareTagsSection(event.tags),\n event.description?.trim(),\n ]);\n}\n\nexport function buildPublicVendorShareDescription(\n vendor: ShareVendorForPublic,\n): string {\n return joinShareDescriptionSections([\n formatShareIsFoodTrueLine(vendor.foodTruck),\n vendor.description?.trim(),\n ]);\n}\n","import {\n POST_SHARE_RESOURCE_TYPES,\n RESOURCE_SHARE_TYPES,\n SHARE_SITE_URL,\n} from \"./constants\";\nimport {\n RELATION_SHARE_APPLICATION,\n RELATION_SHARE_INVITATION,\n} from \"./relationShareTypes\";\n\n/** Path segments for public resource share URLs (markets, posts, etc.). */\nexport const PUBLIC_SHARE_PATH_TYPES = [\n ...RESOURCE_SHARE_TYPES,\n ...POST_SHARE_RESOURCE_TYPES,\n] as const;\n\nexport type PublicSharePathType = (typeof PUBLIC_SHARE_PATH_TYPES)[number];\n\nexport type RelationSharePathType =\n | typeof RELATION_SHARE_INVITATION\n | typeof RELATION_SHARE_APPLICATION;\n\nexport type ShareType = PublicSharePathType | RelationSharePathType;\n\n/** Alternation for deep-link regexes — keep in sync with {@link ShareType}. */\nexport const SHARE_TYPE_PATH_REGEX = [\n ...PUBLIC_SHARE_PATH_TYPES,\n RELATION_SHARE_APPLICATION,\n RELATION_SHARE_INVITATION,\n].join(\"|\");\n\nexport function buildShareUrl(\n type: ShareType,\n slug: string,\n utmMedium: string = \"app\",\n): string {\n return `${SHARE_SITE_URL}/${type}/${encodeURIComponent(slug)}?utm_source=share&utm_medium=${utmMedium}&utm_campaign=${type}`;\n}\n","import { joinShareOgDescriptionSections } from \"./joinShareDescriptionSections\";\n\nexport {\n joinShareOgDescriptionSections,\n OG_DESCRIPTION_LINE_BREAK,\n SHARE_DESCRIPTION_SECTION_BREAK,\n} from \"./joinShareDescriptionSections\";\n\n/** Trims share text and collapses spaces per line; preserves `\\n\\n` section breaks. */\nconst LEADING_WHITESPACE = /^[ \\t]*/;\n\nexport function normalizeShareText(value: string | null | undefined): string {\n if (value == null) {\n return \"\";\n }\n return value\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const leading = LEADING_WHITESPACE.exec(line)?.[0] ?? \"\";\n const rest = line\n .slice(leading.length)\n .trim()\n .replace(/[ \\t]{2,}/g, \" \");\n return leading + rest;\n })\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\");\n}\n\nexport function splitShareDescriptionSections(value: string): string[] {\n const trimmed = normalizeShareText(value);\n if (!trimmed) {\n return [];\n }\n\n if (/\\n\\n/.test(trimmed)) {\n return trimmed\n .split(/\\n\\n+/)\n .map((section) => normalizeShareText(section))\n .filter((section) => section.length > 0);\n }\n\n if (/\\n/.test(trimmed)) {\n return trimmed\n .split(/\\n+/)\n .map((section) => normalizeShareText(section))\n .filter((section) => section.length > 0);\n }\n\n return [trimmed];\n}\n\n/**\n * Formats share descriptions for Open Graph / Twitter meta tags.\n * Sections are joined with {@link OG_DESCRIPTION_LINE_BREAK} (`&#10;`).\n */\nexport function normalizeShareOgDescription(value: string): string {\n return joinShareOgDescriptionSections(splitShareDescriptionSections(value));\n}\n\n/**\n * Open Graph / Twitter description from pre-built section strings.\n * Prefer {@link joinShareOgDescriptionSections} when sections are already known.\n */\nexport function buildShareOgDescriptionFromSections(\n sections: ReadonlyArray<string | false | null | undefined>,\n): string {\n return joinShareOgDescriptionSections(sections);\n}\n\n/** Open Graph / Twitter description — body sections only (`og:title` is separate). */\nexport function buildShareOgDescription(\n _title: string,\n description: string,\n): string {\n return joinShareOgDescriptionSections(\n splitShareDescriptionSections(description),\n );\n}\n\n/**\n * Strips emoji and symbols that do not render reliably in overlay SVG fonts.\n * Keeps letters, numbers, whitespace, and common sentence punctuation.\n */\nexport function stripOverlaySubtitleSpecialChars(text: string): string {\n const regex = /[^\\p{L}\\p{M}\\p{N}\\s.,!?'\"&\\-():;/]/gu;\n const result = text\n .replace(/\\p{Extended_Pictographic}/gu, \"\")\n .replace(regex, \"\");\n return result.trim();\n}\n","import type { ShareResourceType } from \"./constants\";\n\nexport function normalizeShareRouteId(id: string): string {\n try {\n return decodeURIComponent(id);\n } catch {\n return id;\n }\n}\n\n/** Relative path for a public share landing page (`/market/demo-market`). */\nexport function buildSharePagePath(\n resourceType: ShareResourceType,\n slug: string,\n): string {\n return `/${resourceType}/${encodeURIComponent(normalizeShareRouteId(slug))}`;\n}\n","export type ShareSlugResource = {\n _id: string;\n};\n\nexport function shareSlugResourceId(\n resource: ShareSlugResource | null | undefined,\n): string | null {\n return resource?._id ?? null;\n}\n","/**\n * Normalizes resource `updatedAt` to an epoch-ms string for Facebook/CDN cache busting\n * on social-image URLs (`?v=`). The API ignores this query param.\n */\nexport function toShareSocialImageCacheVersion(\n updatedAt: Date | string | number | null | undefined,\n): string | null {\n if (updatedAt == null || updatedAt === \"\") {\n return null;\n }\n\n let ms: number;\n if (typeof updatedAt === \"number\") {\n ms = updatedAt;\n } else if (updatedAt instanceof Date) {\n ms = updatedAt.getTime();\n } else {\n ms = Date.parse(String(updatedAt));\n }\n\n if (!Number.isFinite(ms)) {\n return null;\n }\n\n return String(Math.trunc(ms));\n}\n"],"mappings":"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","names":["import_dayjs","import_customParseFormat","dayjs","customParseFormat","isoWeek","dayjs","customParseFormat","utc","timezone","isSameOrAfter","dayjs"]}
@@ -24,7 +24,7 @@ import {
24
24
  normalizeShareRouteId,
25
25
  shareSlugResourceId,
26
26
  toShareSocialImageCacheVersion
27
- } from "../chunk-LHKJJ7SO.mjs";
27
+ } from "../chunk-CQ26N3W6.mjs";
28
28
  import {
29
29
  DEFAULT_SHARE_OG_IMAGE,
30
30
  OG_DESCRIPTION_LINE_BREAK,
@@ -68,8 +68,8 @@ import {
68
68
  normalizeShareText,
69
69
  splitShareDescriptionSections,
70
70
  stripOverlaySubtitleSpecialChars
71
- } from "../chunk-J72IMZ6E.mjs";
72
- import "../chunk-XGGFB7HY.mjs";
71
+ } from "../chunk-4CQLKIQ6.mjs";
72
+ import "../chunk-A52C6JDT.mjs";
73
73
  export {
74
74
  DEFAULT_SHARE_OG_IMAGE,
75
75
  OG_DESCRIPTION_LINE_BREAK,
@@ -1,5 +1,5 @@
1
1
  export { A as AdminUpdateResourceType, c as ChatMessageInput, d as ChatMessageReaction, e as ChatMessageReplyPreview, f as ChatMessageSeen, g as ChatMessageType, C as ChatType, h as CreateBulkNotificationInput, E as EnumActivity, a as NotificationCount, i as NotificationDataType, N as NotificationType, P as ParticipantType, R as ReportChatUser, j as ResourceActivityEntry, k as ResourceActivityInputType, b as ResourceActivityType } from '../resourceActivities-BytCHUvO.mjs';
2
- export { s as AdBookingType, t as AdDailyPriceType, l as AdFormData, u as AdFormState, v as AdMetricsType, w as AdResource, x as AdSettingsFormData, y as AdSettingsType, b as AdType, o as AppSettingsFormData, d as AppSettingsType, A as AuthMutationResponse, z as AuthPayloadType, B as CreateAdFormData, m as CreateAdFormState, p as CreateAppSettingsFormData, C as CreateLoginFormData, n as CreatePartnerFormData, g as CreateRegisterFormData, h as CreateRequestPasswordResetFormData, j as CreateResetPasswordFormData, r as CreateSchoolFormData, i as CreateValidateVerificationTokenFormData, D as EnumAdApprovalStatus, F as EnumAdBookingSlotKind, G as EnumAdBookingState, H as EnumAdPaymentStatus, E as EnumAdPlatform, c as EnumAdShowOn, I as EnumAdStatus, J as EnumAdStyle, K as EnumAdType, L as EnumVerificationType, M as LoginFormData, N as MarketingMaterialRequestInputType, k as PartnerFormData, P as PartnerType, O as PosterInputType, R as RefreshImpersonationMutationResponse, a as RefreshTokenMutationResponse, Q as RefreshTokenPayloadType, T as RegisterFormData, U as RequestPasswordResetFormData, V as ResetPasswordFormData, W as SchoolCampaignType, q as SchoolFormData, X as SchoolRegisteredUserType, f as SchoolReturnType, e as SchoolType, S as StartImpersonationMutationResponse, Y as StartImpersonationPayloadType, Z as ValidateVerificationTokenFormData } from '../ad-BSyVGv8q.mjs';
2
+ export { s as AdBookingType, t as AdDailyPriceType, l as AdFormData, u as AdFormState, v as AdMetricsType, w as AdResource, x as AdSettingsFormData, y as AdSettingsType, c as AdType, o as AppSettingsFormData, d as AppSettingsType, A as AuthMutationResponse, z as AuthPayloadType, B as CreateAdFormData, m as CreateAdFormState, p as CreateAppSettingsFormData, C as CreateLoginFormData, n as CreatePartnerFormData, g as CreateRegisterFormData, h as CreateRequestPasswordResetFormData, j as CreateResetPasswordFormData, r as CreateSchoolFormData, i as CreateValidateVerificationTokenFormData, D as EnumAdApprovalStatus, F as EnumAdBookingSlotKind, G as EnumAdBookingState, H as EnumAdPaymentStatus, b as EnumAdPlatform, E as EnumAdShowOn, I as EnumAdStatus, J as EnumAdStyle, K as EnumAdType, L as EnumVerificationType, M as LoginFormData, N as MarketingMaterialRequestInputType, k as PartnerFormData, P as PartnerType, O as PosterInputType, R as RefreshImpersonationMutationResponse, a as RefreshTokenMutationResponse, Q as RefreshTokenPayloadType, T as RegisterFormData, U as RequestPasswordResetFormData, V as ResetPasswordFormData, W as SchoolCampaignType, q as SchoolFormData, X as SchoolRegisteredUserType, f as SchoolReturnType, e as SchoolType, S as StartImpersonationMutationResponse, Y as StartImpersonationPayloadType, Z as ValidateVerificationTokenFormData } from '../ad-DB9gvcJW.mjs';
3
3
  export { C as ContactUsFormData, a as CreateContactUsFormData } from '../contactUs-BDJutwcx.mjs';
4
4
  import { d as EventListItemType } from '../global-EGbrTMk6.mjs';
5
5
  export { N as BaseGameMap, Q as BaseGameType, T as BaseResourceType, B as BaseResourceTypeFormData, C as Category, c as CategoryColorType, W as CouponCodeType, X as CouponDataProductsType, Y as CouponDataType, I as CouponDefaultsFormData, Z as CouponMarketPassportOption, _ as CouponMarketVisitOption, $ as CouponOption, a0 as CouponParticipantUserType, a1 as CouponPurchaseOption, p as CouponType, q as CouponTypeList, a2 as CouponVendorProduct, J as CreateCouponFormData, y as CreateEventFormData, A as CreateEventInfoFormData, a3 as CreateFormData, H as CreatePostFormData, w as CreateUnregisteredVendorFormData, s as CreateVendorFormData, u as CreateVendorInfoFormData, a4 as DailyClueBaseGame, a5 as DailyClueGameData, a6 as DateTimeFormValue, a7 as DateTimeType, a8 as DateTimeWithPriceFormValue, a9 as DateTimeWithPriceType, aa as DeviceInfo, ab as EnumCouponListType, ac as EnumCouponQualificationType, ad as EnumGameStatus, ae as EnumGameType, af as EnumPostContentType, ag as EnumPostType, x as EventFormData, z as EventInfoFormData, e as EventInfoType, ah as EventListFilter, ai as EventStatusType, E as EventType, a as FormDateField, F as FormField, aj as GameDate, G as GameDocType, ak as GameHistory, o as GameLeaderboard, al as GamePlacement, am as GamePlacementClue, an as GameType, ao as GeocodeLocation, ap as GlobalGameData, aq as ImageObjectType, ar as LocationGeoType, L as LocationType, M as MutationResponse, l as MyPendingVendorClaim, as as Nullable, O as OptionItem, at as OwnerType, au as PROMO_CODE_PREFIX, av as PaginationOptions, aw as PartnerListFilter, ax as PaymentInfoType, ay as PostContentData, az as PostContentFormData, aA as PostContentGame, aB as PostContentImage, aC as PostContentList, aD as PostContentTextarea, aE as PostContentType, aF as PostContentVideo, aG as PostFileInput, D as PostFormData, aH as PostListFilter, aI as PostResource, n as PostType, P as PosterUsageType, aJ as ProductCoupon, aK as PromoCodeType, aL as PuzzleAnswer, aM as PuzzleAnsweredQuestion, aN as PuzzleBaseGame, aO as PuzzleGameData, aP as PuzzleQuestion, R as RefundPolicy, K as Region, aQ as RelatedPostType, aR as RelationDate, g as RelationHistoryType, aS as RelationInputType, f as RelationType, b as Requirement, h as ResourceConnectionsType, aT as ResourceContactDetailsType, aU as ResourceDetails, aV as ResourceImageType, aW as ResourceRelationField, aX as SocialMediaType, aY as SocialShareResourceType, S as StallType, aZ as Subcategory, a_ as SubcategoryItems, a$ as TermsAgreement, v as UnregisteredVendorFormData, b0 as UnregisteredVendorInvitationType, i as UnregisteredVendorType, U as UseGetResourcesByRegionOptions, m as UserLicenceType, b1 as UserListFilter, b2 as VendorAttributes, b3 as VendorAvailability, b4 as VendorCalendarData, V as VendorClaimTokenValidation, b5 as VendorDowngradeSnapshot, r as VendorFormData, t as VendorInfoFormData, k as VendorInfoType, b6 as VendorListFilter, b7 as VendorProductList, j as VendorType, b8 as gameScreenIdentifierList, b9 as gameTypeToDisplayName } from '../global-EGbrTMk6.mjs';
@@ -1,5 +1,5 @@
1
1
  export { A as AdminUpdateResourceType, c as ChatMessageInput, d as ChatMessageReaction, e as ChatMessageReplyPreview, f as ChatMessageSeen, g as ChatMessageType, C as ChatType, h as CreateBulkNotificationInput, E as EnumActivity, a as NotificationCount, i as NotificationDataType, N as NotificationType, P as ParticipantType, R as ReportChatUser, j as ResourceActivityEntry, k as ResourceActivityInputType, b as ResourceActivityType } from '../resourceActivities-DlUEbCkT.js';
2
- export { s as AdBookingType, t as AdDailyPriceType, l as AdFormData, u as AdFormState, v as AdMetricsType, w as AdResource, x as AdSettingsFormData, y as AdSettingsType, b as AdType, o as AppSettingsFormData, d as AppSettingsType, A as AuthMutationResponse, z as AuthPayloadType, B as CreateAdFormData, m as CreateAdFormState, p as CreateAppSettingsFormData, C as CreateLoginFormData, n as CreatePartnerFormData, g as CreateRegisterFormData, h as CreateRequestPasswordResetFormData, j as CreateResetPasswordFormData, r as CreateSchoolFormData, i as CreateValidateVerificationTokenFormData, D as EnumAdApprovalStatus, F as EnumAdBookingSlotKind, G as EnumAdBookingState, H as EnumAdPaymentStatus, E as EnumAdPlatform, c as EnumAdShowOn, I as EnumAdStatus, J as EnumAdStyle, K as EnumAdType, L as EnumVerificationType, M as LoginFormData, N as MarketingMaterialRequestInputType, k as PartnerFormData, P as PartnerType, O as PosterInputType, R as RefreshImpersonationMutationResponse, a as RefreshTokenMutationResponse, Q as RefreshTokenPayloadType, T as RegisterFormData, U as RequestPasswordResetFormData, V as ResetPasswordFormData, W as SchoolCampaignType, q as SchoolFormData, X as SchoolRegisteredUserType, f as SchoolReturnType, e as SchoolType, S as StartImpersonationMutationResponse, Y as StartImpersonationPayloadType, Z as ValidateVerificationTokenFormData } from '../ad-DRI2CIzV.js';
2
+ export { s as AdBookingType, t as AdDailyPriceType, l as AdFormData, u as AdFormState, v as AdMetricsType, w as AdResource, x as AdSettingsFormData, y as AdSettingsType, c as AdType, o as AppSettingsFormData, d as AppSettingsType, A as AuthMutationResponse, z as AuthPayloadType, B as CreateAdFormData, m as CreateAdFormState, p as CreateAppSettingsFormData, C as CreateLoginFormData, n as CreatePartnerFormData, g as CreateRegisterFormData, h as CreateRequestPasswordResetFormData, j as CreateResetPasswordFormData, r as CreateSchoolFormData, i as CreateValidateVerificationTokenFormData, D as EnumAdApprovalStatus, F as EnumAdBookingSlotKind, G as EnumAdBookingState, H as EnumAdPaymentStatus, b as EnumAdPlatform, E as EnumAdShowOn, I as EnumAdStatus, J as EnumAdStyle, K as EnumAdType, L as EnumVerificationType, M as LoginFormData, N as MarketingMaterialRequestInputType, k as PartnerFormData, P as PartnerType, O as PosterInputType, R as RefreshImpersonationMutationResponse, a as RefreshTokenMutationResponse, Q as RefreshTokenPayloadType, T as RegisterFormData, U as RequestPasswordResetFormData, V as ResetPasswordFormData, W as SchoolCampaignType, q as SchoolFormData, X as SchoolRegisteredUserType, f as SchoolReturnType, e as SchoolType, S as StartImpersonationMutationResponse, Y as StartImpersonationPayloadType, Z as ValidateVerificationTokenFormData } from '../ad-C7GbDyUi.js';
3
3
  export { C as ContactUsFormData, a as CreateContactUsFormData } from '../contactUs-6b5g9DV-.js';
4
4
  import { d as EventListItemType } from '../global-LtQJ2t6c.js';
5
5
  export { N as BaseGameMap, Q as BaseGameType, T as BaseResourceType, B as BaseResourceTypeFormData, C as Category, c as CategoryColorType, W as CouponCodeType, X as CouponDataProductsType, Y as CouponDataType, I as CouponDefaultsFormData, Z as CouponMarketPassportOption, _ as CouponMarketVisitOption, $ as CouponOption, a0 as CouponParticipantUserType, a1 as CouponPurchaseOption, p as CouponType, q as CouponTypeList, a2 as CouponVendorProduct, J as CreateCouponFormData, y as CreateEventFormData, A as CreateEventInfoFormData, a3 as CreateFormData, H as CreatePostFormData, w as CreateUnregisteredVendorFormData, s as CreateVendorFormData, u as CreateVendorInfoFormData, a4 as DailyClueBaseGame, a5 as DailyClueGameData, a6 as DateTimeFormValue, a7 as DateTimeType, a8 as DateTimeWithPriceFormValue, a9 as DateTimeWithPriceType, aa as DeviceInfo, ab as EnumCouponListType, ac as EnumCouponQualificationType, ad as EnumGameStatus, ae as EnumGameType, af as EnumPostContentType, ag as EnumPostType, x as EventFormData, z as EventInfoFormData, e as EventInfoType, ah as EventListFilter, ai as EventStatusType, E as EventType, a as FormDateField, F as FormField, aj as GameDate, G as GameDocType, ak as GameHistory, o as GameLeaderboard, al as GamePlacement, am as GamePlacementClue, an as GameType, ao as GeocodeLocation, ap as GlobalGameData, aq as ImageObjectType, ar as LocationGeoType, L as LocationType, M as MutationResponse, l as MyPendingVendorClaim, as as Nullable, O as OptionItem, at as OwnerType, au as PROMO_CODE_PREFIX, av as PaginationOptions, aw as PartnerListFilter, ax as PaymentInfoType, ay as PostContentData, az as PostContentFormData, aA as PostContentGame, aB as PostContentImage, aC as PostContentList, aD as PostContentTextarea, aE as PostContentType, aF as PostContentVideo, aG as PostFileInput, D as PostFormData, aH as PostListFilter, aI as PostResource, n as PostType, P as PosterUsageType, aJ as ProductCoupon, aK as PromoCodeType, aL as PuzzleAnswer, aM as PuzzleAnsweredQuestion, aN as PuzzleBaseGame, aO as PuzzleGameData, aP as PuzzleQuestion, R as RefundPolicy, K as Region, aQ as RelatedPostType, aR as RelationDate, g as RelationHistoryType, aS as RelationInputType, f as RelationType, b as Requirement, h as ResourceConnectionsType, aT as ResourceContactDetailsType, aU as ResourceDetails, aV as ResourceImageType, aW as ResourceRelationField, aX as SocialMediaType, aY as SocialShareResourceType, S as StallType, aZ as Subcategory, a_ as SubcategoryItems, a$ as TermsAgreement, v as UnregisteredVendorFormData, b0 as UnregisteredVendorInvitationType, i as UnregisteredVendorType, U as UseGetResourcesByRegionOptions, m as UserLicenceType, b1 as UserListFilter, b2 as VendorAttributes, b3 as VendorAvailability, b4 as VendorCalendarData, V as VendorClaimTokenValidation, b5 as VendorDowngradeSnapshot, r as VendorFormData, t as VendorInfoFormData, k as VendorInfoType, b6 as VendorListFilter, b7 as VendorProductList, j as VendorType, b8 as gameScreenIdentifierList, b9 as gameTypeToDisplayName } from '../global-LtQJ2t6c.js';
@@ -27,7 +27,7 @@ import {
27
27
  TIER_DISPLAY_LABELS,
28
28
  TIER_FROM_LICENCE
29
29
  } from "../chunk-DQ7ZHIXW.mjs";
30
- import "../chunk-XGGFB7HY.mjs";
30
+ import "../chunk-A52C6JDT.mjs";
31
31
  export {
32
32
  EnumActivity,
33
33
  EnumAdApprovalStatus,
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/utils/index.ts
31
31
  var utils_exports = {};
32
32
  __export(utils_exports, {
33
+ AD_METRIC_IMPRESSION_RETRY_DELAY_MS: () => AD_METRIC_IMPRESSION_RETRY_DELAY_MS,
34
+ AD_METRIC_ROTATION_INTERVAL_MS: () => AD_METRIC_ROTATION_INTERVAL_MS,
33
35
  AFFILIATE_PARTICIPANT_TYPE_LABELS: () => AFFILIATE_PARTICIPANT_TYPE_LABELS,
34
36
  AFFILIATE_REWARDS: () => AFFILIATE_REWARDS,
35
37
  ANDROID_URL: () => ANDROID_URL,
@@ -59,6 +61,7 @@ __export(utils_exports, {
59
61
  availableRegionOptions: () => availableRegionOptions,
60
62
  availableRegionTypes: () => availableRegionTypes,
61
63
  billingCatalogDefinitionMatches: () => billingCatalogDefinitionMatches,
64
+ buildAdMetricSlotKey: () => buildAdMetricSlotKey,
62
65
  buildCalendarSheetContentData: () => buildCalendarSheetContentData,
63
66
  buildRelationEligibleEventSchedule: () => buildRelationEligibleEventSchedule,
64
67
  capitalizeFirstLetter: () => capitalizeFirstLetter,
@@ -90,6 +93,9 @@ __export(utils_exports, {
90
93
  formatTimestamp: () => formatTimestamp,
91
94
  fromCalendarIsoDate: () => fromCalendarIsoDate,
92
95
  futureTimePeriods: () => futureTimePeriods,
96
+ generateAdClickId: () => generateAdClickId,
97
+ getAdMetricRotationIndex: () => getAdMetricRotationIndex,
98
+ getAdMetricRotationSlot: () => getAdMetricRotationSlot,
93
99
  getAllowedEventDateStatuses: () => getAllowedEventDateStatuses,
94
100
  getBillingCatalogKeyForSelection: () => getBillingCatalogKeyForSelection,
95
101
  getCurrentAndFutureDates: () => getCurrentAndFutureDates,
@@ -277,12 +283,12 @@ var import_utc = __toESM(require("dayjs/plugin/utc.js"));
277
283
  // src/enums/index.ts
278
284
  var EnumInviteStatus = /* @__PURE__ */ ((EnumInviteStatus2) => {
279
285
  EnumInviteStatus2["ACCEPTED"] = "Accepted";
286
+ EnumInviteStatus2["CANCELED"] = "Canceled";
280
287
  EnumInviteStatus2["COMPLETED"] = "Completed";
281
288
  EnumInviteStatus2["EXPIRED"] = "Expired";
282
289
  EnumInviteStatus2["NO_STATUS"] = "No_Status";
283
290
  EnumInviteStatus2["PENDING"] = "Pending";
284
291
  EnumInviteStatus2["REJECTED"] = "Rejected";
285
- EnumInviteStatus2["UNAVAILABLE"] = "Unavailable";
286
292
  return EnumInviteStatus2;
287
293
  })(EnumInviteStatus || {});
288
294
  var EnumPaymentMethod = /* @__PURE__ */ ((EnumPaymentMethod2) => {
@@ -2344,6 +2350,28 @@ function isTransientNetworkError(err) {
2344
2350
  return TRANSIENT_NETWORK_PATTERNS.some((pattern) => pattern.test(message));
2345
2351
  }
2346
2352
 
2353
+ // src/utils/adMetrics.ts
2354
+ var AD_METRIC_ROTATION_INTERVAL_MS = 8e3;
2355
+ var AD_METRIC_IMPRESSION_RETRY_DELAY_MS = 1e3;
2356
+ function getAdMetricRotationSlot(epochMilliseconds) {
2357
+ return Math.floor(epochMilliseconds / AD_METRIC_ROTATION_INTERVAL_MS);
2358
+ }
2359
+ function getAdMetricRotationIndex(epochMilliseconds, count) {
2360
+ if (count <= 0) return 0;
2361
+ return getAdMetricRotationSlot(epochMilliseconds) % count;
2362
+ }
2363
+ function buildAdMetricSlotKey(adId, placement, slot) {
2364
+ return `${adId}:${placement}:${slot}`;
2365
+ }
2366
+ function generateAdClickId() {
2367
+ const cryptoApi = globalThis.crypto;
2368
+ if (typeof cryptoApi?.randomUUID === "function") {
2369
+ return cryptoApi.randomUUID();
2370
+ }
2371
+ const randomPart = Math.random().toString(36).slice(2) || "0";
2372
+ return `click-${Date.now()}-${randomPart}`;
2373
+ }
2374
+
2347
2375
  // src/utils/billingCatalog/index.ts
2348
2376
  var BILLING_CATALOG_KEYS = [
2349
2377
  "pro_vendor_monthly",
@@ -2513,6 +2541,7 @@ function getVendorEventsData({
2513
2541
  // src/utils/relations/preservedRelationDateStatuses.ts
2514
2542
  var PRESERVED_RELATION_DATE_STATUSES = /* @__PURE__ */ new Set([
2515
2543
  "Accepted" /* ACCEPTED */,
2544
+ "Canceled" /* CANCELED */,
2516
2545
  "Completed" /* COMPLETED */,
2517
2546
  "Rejected" /* REJECTED */
2518
2547
  ]);
@@ -2557,6 +2586,8 @@ function getErrorMessage(error, fallback = DEFAULT_USER_FACING_ERROR) {
2557
2586
  }
2558
2587
  // Annotate the CommonJS export names for ESM import in node:
2559
2588
  0 && (module.exports = {
2589
+ AD_METRIC_IMPRESSION_RETRY_DELAY_MS,
2590
+ AD_METRIC_ROTATION_INTERVAL_MS,
2560
2591
  AFFILIATE_PARTICIPANT_TYPE_LABELS,
2561
2592
  AFFILIATE_REWARDS,
2562
2593
  ANDROID_URL,
@@ -2586,6 +2617,7 @@ function getErrorMessage(error, fallback = DEFAULT_USER_FACING_ERROR) {
2586
2617
  availableRegionOptions,
2587
2618
  availableRegionTypes,
2588
2619
  billingCatalogDefinitionMatches,
2620
+ buildAdMetricSlotKey,
2589
2621
  buildCalendarSheetContentData,
2590
2622
  buildRelationEligibleEventSchedule,
2591
2623
  capitalizeFirstLetter,
@@ -2617,6 +2649,9 @@ function getErrorMessage(error, fallback = DEFAULT_USER_FACING_ERROR) {
2617
2649
  formatTimestamp,
2618
2650
  fromCalendarIsoDate,
2619
2651
  futureTimePeriods,
2652
+ generateAdClickId,
2653
+ getAdMetricRotationIndex,
2654
+ getAdMetricRotationSlot,
2620
2655
  getAllowedEventDateStatuses,
2621
2656
  getBillingCatalogKeyForSelection,
2622
2657
  getCurrentAndFutureDates,