1import{c as R,i as ze,r as f,j as s,B as y,_ as x,m as M,o as z,p as D,q as U,v as $,a as F,n as K,T as oe,ag as A,W as De,s as h,u as g}from"./index-Ct8eLu1I.js";import{s as J}from"./sanitizeUrl-CAP9M8eB.js";import{canonicalFirstNameKey as le,getNameVariants as Ue}from"./nicknameVariants-CT_QG-k6.js";import{t as de,i as ce}from"./searchTokens-CTUj4aZL.js";import{i as $e}from"./phone-Ltld9YKt.js";import{b as Fe}from"./payoutInvoicePdf-gjnl3NuR.js";import{d as I}from"./signupLabel-CoOzVGDj.js";import{A as Ke}from"./arrow-left-xu1INuH2.js";import{P as C}from"./play-ktVocCtn.js";import{C as q}from"./circle-x-DAvesdV7.js";import{T as Ae}from"./trash-2-LxvlgA12.js";import{R as G}from"./radio-tsTiTjcV.js";import{S as Je}from"./shopping-cart-Bqj-YZFe.js";import{M as W}from"./monitor-Chxxi-hB.js";import{C as ue}from"./clipboard-check-DIM5JbBn.js";import{T as me}from"./timer-BmWXyNuG.js";import{S as pe}from"./smartphone-BkakhVKE.js";import{A as qe}from"./activity-dhGYkICl.js";import{C as Ge}from"./calendar-check-DLZtpLCH.js";import{C as _}from"./credit-card-COtItE6V.js";import{U as j}from"./users-YMaWm21U.js";import{B as We}from"./banknote-1tZxZg_8.js";import{L as Ye}from"./layers-DmHX825Z.js";import{L as E}from"./lock-DoxhL5bS.js";import{S as Y}from"./shield-check-DDmMofdK.js";import{E as He}from"./eye-BX6Kc3UM.js";import{L as H}from"./list-checks-VUACUc5Z.js";import{C as Ve}from"./calendar-clock-r7IGAA3h.js";import{F as P}from"./flag-BttTACQc.js";import{F as Qe}from"./file-pen-TYd5rHoG.js";import{T as Xe}from"./tags-Bj30yghl.js";import{W as ge}from"./wrench-BSVL6hRJ.js";import{T as Ze}from"./trophy-Bwwh5BxN.js";import"./jspdf.plugin.autotable-B3ApGzWF.js";import"./format-ybceeo2k.js";import"./en-US-CGO0aXMi.js";/** 2 * @license lucide-react v0.462.0 - ISC 3 * 4 * This source code is licensed under the ISC license. 5 * See the LICENSE file in the root directory of this source tree. 6 */const es=R("CircleUser",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["circle",{cx:"12",cy:"10",r:"3",key:"ilqhr7"}],["path",{d:"M7 20.662V19a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v1.662",key:"154egf"}]]);/** 7 * @license lucide-react v0.462.0 - ISC 8 * 9 * This source code is licensed under the ISC license. 10 * See the LICENSE file in the root directory of this source tree. 11 */const V=R("Gauge",[["path",{d:"m12 14 4-4",key:"9kzdfg"}],["path",{d:"M3.34 19a10 10 0 1 1 17.32 0",key:"19p75a"}]]);/** 12 * @license lucide-react v0.462.0 - ISC 13 * 14 * This source code is licensed under the ISC license. 15 * See the LICENSE file in the root directory of this source tree. 16 */const ss=R("GitMerge",[["circle",{cx:"18",cy:"18",r:"3",key:"1xkwt0"}],["circle",{cx:"6",cy:"6",r:"3",key:"1lh9wr"}],["path",{d:"M6 21V9a9 9 0 0 0 9 9",key:"7kw0sc"}]]);/** 17 * @license lucide-react v0.462.0 - ISC 18 * 19 * This source code is licensed under the ISC license. 20 * See the LICENSE file in the root directory of this source tree. 21 */const ns=R("Terminal",[["polyline",{points:"4 17 10 11 4 5",key:"akl6gq"}],["line",{x1:"12",x2:"20",y1:"19",y2:"19",key:"q2wloq"}]]),Z=/!\[[^\]]*\]\((https?:\/\/[^\s)]+)\)/g;function fe(e){const n=[];if(!e)return n;const i=new RegExp(Z.source,"g");let d=0,l;for(;(l=i.exec(e))!==null;)l.index>
21d&&n.push({type:"text",value:e.slice(d,l.index)}),n.push({type:"image",value:l[1]}),d=l.index+l[0].length;return d<e.length&&n.push({type:"text",value:e.slice(d)}),n}function he(e){return new RegExp(Z.source,"g").test(e||"")}function ts(e){return e.replace(Z,"").replace(/\n{3,}/g,` 22 23`).trim()}const as=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;function ye(e){if(typeof e!="string")return!1;const n=e.trim();return!n||n.length>254?!1:as.test(n)}function be(e){if(typeof e!="string")return!1;const n=e.replace(/\D+/g,"");if(!n)return!1;const i=n.length===11&&n.startsWith("1")?n.slice(1):n;return i.length!==10?!1:$e(i)}const rs=1e5;function O(e){if(e==null||e==="")return null;const n=typeof e=="number"?e:parseInt(String(e),10);return Number.isFinite(n)&&n>0?n:null}function b({existing:e,bpNumber:n,sourceMember:i}){const d=O(e);if(d)return d;const l=O(n);return l||O(i)}function ke(e){const n=O(e);return n!==null&&n>=rs}const is={honda:"HON",yamaha:"YAM",kawasaki:"KAW",suzuki:"SUZ",ktm:"KTM",husqvarna:"HUS",gasgas:"GAS",beta:"BET",sherco:"SHE",cobra:"COB"};function ve(e){if(typeof e!="string")return"";const n=e.trim().toLowerCase();if(!n)return"";const i=is[n];return i||n.replace(/[^a-z]/g,"").slice(0,3).toUpperCase()}function Q(e){if(typeof e!="string")return"";const n=e.replace(/\D+/g,""),i=n.length===11&&n.startsWith("1")?n.slice(1):n;return i.length===10?i:""}function xe(e,n){const i=new Set(e.map(p=>p.classKey)),d=[],l=new Set;for(const p of n)!p.classKey||l.has(p.classKey)||(l.add(p.classKey),i.has(p.classKey)||d.push(p));return{toInsert:d,unchanged:e,toDelete:[]}}function _e(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function X(e){if(!_e(e))return{};const n=typeof e.linkUrl=="string"?e.linkUrl:void 0,i=typeof e.openInNewTab=="boolean"?e.openInNewTab:void 0;return{linkUrl:n,openInNewTab:i}}function we(e){if(!_e(e))return{};const n={};for(const i of["heading","description","buttonText","buttonUrl","backgroundColor","textColor","buttonColor","buttonTextColor"]){const d=e[i];typeof d=="string"&&(n[i]=d)}return(e.buttonTarget==="_self"||e.buttonTarget==="_blank")&&(n.buttonTarget=e.buttonTarget),(e.layout==="centered"||e.layout==="split")&&(n.layout=e.layout),n}function L(e){const{pgSynced:n,mysqlSynced:i,lastSyncedAt:d,minutesOld:l=0}=e;return n&&i?{label:"synced",driftCode:"SYNC_OK"}:!n&&!i?l<5?{label:"pending",driftCode:"MISSING"}:{label:"missing",driftCode:"MISSING"}:n&&!i?l<5?{label:"pending",driftCode:"PG_ONLY"}:{label:"drift",driftCode:"PG_ONLY"}:!d&&l<5?{label:"pending",driftCode:"MYSQL_ONLY"}:{label:"drift",driftCode:"MYSQL_ONLY"}}function a(e,n){if(!e)throw new Error(n)}function B(e,n){return JSON.stringify(e)===JSON.stringify(n)}const os=[{name:"sanitizeUrl accepts http(s) and relative paths",run:()=>{a(J("https://x.com")==="https://x.com/","https rejected"),a(J("/events")==="/events","relative rejected")}},{name:"sanitizeUrl rejects javascript:/data:/file:",run:()=>{for(const e of["javascript:alert(1)","data:text/html,x","file:///etc/passwd"])a(J(e)===null,`should reject ${e}`)}},{name:"readBannerOptions tolerates null / [] / {}",run:()=>{a(B(X(null),{}),"null"),a(B(X([]),{}),"array"),a(X({linkUrl:"/x"}).linkUrl==="/x","linkUrl")}},{name:"readCtaBannerOptions ignores bogus enums",run:()=>{a(we({layout:"bogus"}).layout===void 0,"layout"),a(we({buttonTarget:"top"}).buttonTarget===void 0,"target")}}],ls=[{name:"tokenize splits multi-word names",run:()=>a(B(de("john smith"),["john","smith"]),"split")},{name:"tokenize caps at max",run:()=>a(de("a b c d e",4).length===4,"cap")},{name:"isNumericToken recognizes bike numbers",run:()=>{a(ce("5"),"5"),a(!ce("5a"),"5a")}},{name:"PropagationState: both synced => SYNC_OK",run:()=>{const e=L({pgSynced:!0,mysqlSynced:!0});a(e.label==="synced"&&e.driftCode==="SYNC_OK",JSON.stringify(e))}},{name:"PropagationState: PG-only after grace => drift PG_ONLY",run:()=>{const e=L({pgSynced:!0,mysqlSynced:!1,minutesOld:30});a(e.label==="drift"&&e.driftCode==="PG_ONLY",JSON.stringify(e))}},{name:"signupLabel: season_pass form => Series variant",run:()=>{const e=I({is_child_registration:!1},JSON.stringify({registrationType:"season",seasonLabel:"2026 Series",selectedEvents:[{event_name:"R1"},{event_name:"R2"}]}),{name:"2026 Series"},2);a(e.variant==="series"&&e.badge==="Series",JSON.stringify(e))}},{name:"signupLabel: multi_event w/ 2 events => Multi-Event variant",run:()=>{const e=I({is_child_registration:!1},JSON.stringify({registrationType:"multi_event",selectedEvents:[{event_name:"R1"},{event_name:"R2"}]}),{name:"Series"},2);a(e.variant==="multi"&&e.badge==="Multi-Event",JSON.stringify(e))}},{name:"signupLabel: multi_event w/ 1 event => Single (Round)",run:()=>{const e=I({is_child_registration:!1},JSON.stringify({registrationType:"multi_event",selectedEvents:[{event_name:"R1"}]}),{name:"Series"},1);a(e.variant==="single"&&e.badge==="Round",JSON.stringify(e))}},{name:"signupLabel: 2+ child rounds w/o form_response => Multi-Event, NOT Series",run:()=>{const e=I({is_child_registration:!1},null,{name:"Series"},3);a(e.variant==="multi"&&e.badge==="Multi-Event",`should not promote to series: ${JSON.stringify(e)}`)}}],ds=[{name:"splitContent returns text-only segments for legacy posts",run:()=>{const e=fe("hello");a(e.length===1&&e[0].type==="text",JSON.stringify(e))}},{name:"splitContent preserves inline image order",run:()=>{const n=fe(` 24`).filter(i=>i.type==="image").map(i=>i.value);a(B(n,["https://a/1.jpg","https://a/2.jpg"]),JSON.stringify(n))}},{name:"stripImageTokens collapses blank lines",run:()=>{const e=ts(`hi 25 26 27 28 29world`);a(e===`hi 30 31world`,e)}},{name:"hasInlineImage detects token presence",run:()=>{a(he(""),"yes"),a(!he("plain"),"no")}},{name:"isValidEmail / isValidPhone gatekeep batch sends",run:()=>{a(ye("[email protected]"),"good email"),a(!ye("nope"),"bad email"),a(be("(555) 123-4567"),"good phone"),a(!be("123"),"bad phone")}}],cs=[{name:"nickname dedup groups Matt/Matthew",run:()=>{a(le("Matt")===le("Matthew"),"canonical"),a(Ue("Mike").includes("michael"),"variants")}},{name:"pickMemberNumber priority: existing > BP# > source",run:()=>{a(b({existing:42,bpNumber:100001,sourceMember:7})===42,"exist"),a(b({bpNumber:100123,sourceMember:7})===100123,"bp"),a(b({sourceMember:7})===7,"src"),a(b({})===null,"empty")}},{name:"isBpNumber range check",run:()=>{a(ke(1e5)&&!ke(42),"range")}},{name:"sanitizeBikeMake â 3 uppercase letters",run:()=>{a(ve("Honda")==="HON","honda"),a(ve("ktm")==="KTM","ktm")}},{name:"sanitizePhone normalizes US formats",run:()=>{a(Q("(555) 123-4567")==="5551234567","formatted"),a(Q("+1-555-123-4567")==="5551234567","+1"),a(Q("123")==="","short")}},{name:"diffClassEntries is additive-only (never deletes)",run:()=>{const e=xe([{classKey:"A"},{classKey:"B"}],[{classKey:"A"}]);a(e.toDelete.length===0,"deleted!"),a(e.toInsert.length===0,"wrong inserts")}}],us=[{name:"PropagationState: MySQL-only after grace => drift MYSQL_ONLY",run:()=>{const e=L({pgSynced:!1,mysqlSynced:!0,minutesOld:30});a(e.label==="drift"&&e.driftCode==="MYSQL_ONLY",JSON.stringify(e))}},{name:"PropagationState: neither synced within grace => pending (no drift)",run:()=>{const e=L({pgSynced:!1,mysqlSynced:!1,minutesOld:1});a(e.label!=="drift",JSON.stringify(e))}},{name:"diffClassEntries never deletes (MySQL class_entries SoT)",run:()=>{const e=xe([{classKey:"A"},{classKey:"B"},{classKey:"C"}],[{classKey:"A"}]);a(e.toDelete.length===0,`would delete ${e.toDelete.length}`)}},{name:"BP# member-number priority survives PG-only orgs",run:()=>{a(b({bpNumber:100500,sourceMember:42})===100500,"bp wins"),a(b({existing:100777})===100777,"existing kept")}},{name:"payout invoice PDF builds without MySQL data",run:()=>{const n=Fe({payout:{period_start:"2026-06-01",period_end:"2026-06-07",gross_amount:1e3,refund_amount:50,platform_fee_amount:30,stripe_fee_amount:25,timing_fee_amount:10,messaging_fee_amount:5,ai_fee_amount:2,adjustments_amount:0,carry_forward_amount:0,ach_fee_amount:0,net_amount:878,stripe_transfer_id:"tr_test",status:"paid"},organizationName:"Test Org",registrations:[{registrant_name:"Jane",amount:100,refunded:0,net:100}
31],ai_usage:[],clawbacks:[]}).output("blob");a(n.size>1e3,`pdf too small: ${n.size}b`)}},{name:"Org capability gate: non-MySQL org skips lab class filter",run:()=>{const e={eventType:"motorsports",hasMysqlHost:!1};a({eventType:"running",hasMysqlHost:!0}.eventType!=="motorsports","running org guard"),a(!e.hasMysqlHost,"no host => no mysql timing")}}],ee=[{id:"form_builder",label:"Form Builder + Registration",cases:os},{id:"kiosk",label:"Kiosk + Propagation",cases:ls},{id:"race_feed",label:"Race Feed + Comms",cases:ds},{id:"timing",label:"Timing + MySQL Sync",cases:cs},{id:"pg_cutover",label:"PG Cutover + Today's Work",cases:us}];function ms(){return ee.map(({id:e,label:n,cases:i})=>({id:e,label:n,total:i.length}))}function je(e){const n=ee.find(l=>l.id===e);if(!n)throw new Error(`Unknown suite: ${e}`);const i=n.cases.map(l=>{try{return l.run(),{name:l.name,passed:!0}}catch(p){return{name:l.name,passed:!1,error:p instanceof Error?p.message:String(p)}}}),d=i.filter(l=>l.passed).length;return{suite:e,label:n.label,total:i.length,passed:d,failed:i.length-d,cases:i}}function ps(){return ee.map(e=>je(e.id))}const T=[{kind:"rfid",fn:"regression-e2e-rfid-race",label:"RFID â cloud path",desc:"Bridge auth · sync-push · reads land · idempotent replay",Icon:G},{kind:"epc_identity",fn:"regression-e2e-epc-identity-journey",label:"EPC â rider identity â Timing Lab â race-time lookup",desc:"kiosk scan saves the tag against the rider's BP# + family record · EPC auto-propagates to the Lab rider and event roster · multi-helmet stacking with newest as primary · EVERY active EPC on the account resolves to the same rider name + BP# at race time · a bridge read on a non-primary helmet still lands and attributes · FIFO cap at 5 evicts the oldest and the evicted tag stops resolving (no ghost laps)",Icon:G},{kind:"kiosk",fn:"regression-e2e-kiosk-flow",label:"Kiosk + fan-out",desc:"EPC assign · fetch-rider-entries · peer-org fan-out via save-rider",Icon:Je},{kind:"standalone_kiosk",fn:"regression-e2e-standalone-kiosk",label:"Standalone kiosk parity",desc:"PIN auth · save_rider · transponders · save/fetch entries · admin parity",Icon:W},{kind:"staging_staff_auth",fn:"regression-e2e-staging-staff-auth",label:"Staging check-in â staff (PIN) access",desc:"kiosk-auth mints an org-scoped staff token · bad PIN mints nothing · anon/bogus/expired tokens 401 · cross-org 403 · staff scan â bind EPC â re-scan resolves · name search · check-in attributed to the staff name (no fake user id) and idempotent · registration edit accepts the token · admin JWT path unchanged · tokens unreadable with the anon key",Icon:W},{kind:"staging_tower_journey",fn:"regression-e2e-staging-to-tower",label:"Staging â tower journey",desc:"Only day-of/future rounds are offered · gate-pick feeds the moto + class pickers · unknown tag scans as unknown · staff bind lands on the BP profile (BP# + family record, active + primary) · the tag fans out and resolves in a peer org · re-scan returns the rider's REGISTERED classes through the labâregistration UUID link (wrong-class badge fires correctly) · unpaid balance warns · mark_checkin is staff-attributed and idempotent · TOWER: a read of that staged tag scores a lap on the rider's race entry, and auto-adds them in practice",Icon:G},{kind:"registration",fn:"regression-e2e-registration-mirror",label:"Registration â mirror",desc:"Public reg · UUID-first event link (names may differ) · event_riders + class_riders",Icon:ue},{kind:"lap",fn:"regression-e2e-lap-results",label:"Lap capture + results",desc:"Session · entry · 3 laps · public lap-times read · aggregates",Icon:me},{kind:"combined",fn:"regression-e2e-combined-link",label:"Combined race link",desc:"Create link · add source · verify · end + cleanup",Icon:ss},{kind:"handheld_device_auth",fn:"regression-e2e-handheld-device-auth",label:"Authorized handheld devices",desc:"race control authorizes a phone · wrong pairing code refused · right code returns a device token + its assigned station · a phone claiming a different station is stored under the ASSIGNED one · station changes reach the phone · unpaired + signed-out callers refused · revoke cuts scans off instantly · re-enable rotates the token · feature off refuses everything · devices unreadable signed out",Icon:pe},{kind:"handheld_race_run",fn:"regression-e2e-handheld-race-run",label:"Handheld race run (phone + R7: green flag â laps â results)",desc:"a scan before the green flag is REFUSED, never silently dropped (the real 'nothing scored' bug) · start loads the grid and stamps the clock · a tag registered only at another org still resolves to the rider on the grid · first read scores lap 1, a burst read inside the dupe window is ignored, the next read scores lap 2 with a real lap time · the on-screen 10-key pad scores a manual lap and updates the leaderboard · quick-add puts an unlisted rider in the right class and scores their lap · every beep lands in the raw read audit trail under the assigned station · pause refuses scans, resume takes them again · checkered stops, Save results places riders with unique positions · red-flag restart is blocked on finalized results until confirmed, then clears laps and re-arms the race",Icon:pe},{kind:"heartbeat",fn:"regression-e2e-bridge-heartbeat",label:"Bridge heartbeat",desc:"Valid auth · invali
31d rejected · revoked refused · last_hb set",Icon:qe},{kind:"season",fn:"regression-e2e-season-pass",label:"Season-pass fan-out",desc:"Parent + 2 rounds · form payload · resync fan-out · idempotent",Icon:Ge},{kind:"single_event",fn:"regression-e2e-single-event",label:"Single-event registration",desc:"registrationType:'single' + legacy payload · no phantom children · mirror lands both classes · idempotent",Icon:oe},{kind:"stripe",fn:"regression-e2e-stripe-webhook",label:"Stripe webhook",desc:"Signed event · pending â registration · idempotent · bad sig 400",Icon:_},{kind:"walkup_classes",fn:"regression-e2e-walkup-class-preservation",label:"Walk-up class preservation",desc:"2 classes selected â both mirror · phantom guard · logging alive",Icon:j},{kind:"kiosk_walkup_mirror",fn:"regression-e2e-kiosk-walkup-mirror",label:"Kiosk walk-up â lab mirror",desc:"Cash walk-up, no BP# · event_registration payload classes · family link mints BP# · roster lands class",Icon:j},{kind:"kiosk_cash_parity",fn:"regression-e2e-kiosk-cash-parity",label:"Kiosk cash == card parity",desc:"Same cart paid by card vs cash · registration/classes/add-ons identical · both mirror to the same Lab event + class · no token 401, wrong org 403, expired 401 · double tap makes no second order",Icon:We},{kind:"schedule_sync",fn:"regression-e2e-schedule-combine-sync",label:"Schedule combine sync",desc:"slot.classes â race.class_ids · unresolved names blocked · finalized skip",Icon:Ye},{kind:"soft_lock",fn:"regression-e2e-finalized-soft-lock",label:"Finalized soft-lock",desc:"Additive OK · reorder OK · removal blocked · delete blocked",Icon:E},{kind:"checkout_dedup",fn:"regression-e2e-checkout-dedup",label:"Checkout dedup guard (real Stripe sandbox)",desc:"real create-checkout calls · double-submit reuses one session · already-paid short-circuits · 15-min window · amount + email scoping",Icon:Y},{kind:"checkout_hardening",fn:"regression-e2e-checkout-hardening",label:"Checkout hardening (no double charge / no stuck cart)",desc:"2 + 5 concurrent submits â 1 Stripe session · coupon change expires stale session · line items sum to total · 24h expiry + unknown id + stuck 'processing' fail cleanly · $0 race â 1 registration",Icon:Y},{kind:"checkout_live",fn:"regression-e2e-checkout-live",label:"Live checkout (real $0)",desc:"real create-checkout call · concurrent double-submit â 1 registration · replay + stuck 'processing' return success · bad id still errors",Icon:_},{kind:"checkout_journey",fn:"regression-e2e-checkout-journey",label:"Full checkout journey (pay â confirmation â Timing Lab)",desc:"form â real create-checkout â real 4242 payment â real webhook â confirmed paid registration · guest confirmation page RPC + lookup-registration show the right status · wrong email blocked · rider auto-mirrors onto the Lab roster + class · replay makes no duplicates",Icon:ue},{kind:"stripe_sandbox",fn:"regression-e2e-stripe-sandbox",label:"Stripe sandbox (real paid checkout)",desc:"real test-mode Checkout Session · amount + metadata verified · 4242 card succeeds · declined card fails loudly · webhook â confirmed paid registration · replay safe · no real money",Icon:_},{kind:"stripe_timing",fn:"regression-e2e-stripe-timing",label:"Stripe timing / race conditions",desc:"create-checkout latency budget · webhookâregistration latency · no 'nothing found' window · out-of-order events · 5 concurrent retries â 1 registration · unpaid/expired reconcile makes no phantom · stale signature rejected",Icon:me},{kind:"refund",fn:"regression-e2e-refund",label:"Refunds (real test money)",desc:"real 4242 charge · real process-refund call · fee withholding cap · 50% partial â partially_refunded · over-refund rejected · full refund â refunded · audit log · double-refund refused · auth + non-test-org gates",Icon:_},{kind:"class_visibility",fn:"regression-e2e-class-visibility",label:"Class visibility",desc:"excludes hide · hidden_from_timing must NOT hide · registration intact",Icon:He},{kind:"edit_class_dropdown",fn:"regression-e2e-edit-class-dropdown",label:"Edit-registration class dropdown",desc:"own classes · series parent unions child rounds · classless round inherits parent · excludes never leak · rider's paid class always an option",Icon:H},{kind:"class_capacity",fn:"regression-e2e-class-capacity",label:"Class capacity guard",desc:"per-class + group cap · in-flight pending counts · cancelled excluded · no shrink",Icon:V},{kind:"price_adjustment",fn:"regression-e2e-class-price-adjustment",label:"Class price adjustments",desc:"negative fee discounts base · premium unchanged · mixed cart nets · no negative Stripe line items · $0 clamp",Icon:_},{kind:"lab_schedule_builder",fn:"regression-e2e-lab-schedule-builder",label:"Timing Lab schedule builder",desc:"add/reorder/delete slots · practice slot inert · finalized session blocks class swap",Icon:Ve},{kind:"manual_position_integrity",fn:"regression-e2e-manual-position-integrity",label:"Manual finishing positions",desc:"hand placement inserts-and-shifts (no duplicate 5th) · two edits in one save both land · placements survive re-finalize · zero-lap rider keeps a
31slot · existing duplicates + gaps repaired · DNF/DNS/DSQ never renumbered · laps untouched",Icon:P},{kind:"lab_event_classes_mirror",fn:"regression-e2e-lab-event-classes-mirror",label:"Timing Lab classes mirror",desc:"check/uncheck event_classes â timing_lab_event_classes · idempotent mirror · visibility follows",Icon:H},{kind:"lab_class_two_way_sync",fn:"regression-e2e-lab-class-two-way-sync",label:"Class sync both directions",desc:"check in Timing Lab â shows in signup dropdown · uncheck â disappears · re-check relinks same UUID · registration â Lab still mirrors · class with registrations can't be removed · rename follows",Icon:H},{kind:"endurance_journey",fn:"regression-e2e-endurance-journey",label:"Full endurance journey (build â team signup â race day)",desc:"endurance event + divisions reach the signup form · team settings + $237/3 = $79 split · captain creates team and picks how it pays · teammates join by code · 4th rider refused · solo entry stays solo · all entries mirror to Timing Lab · race day seeds ONE team row + the solo rider · entered division can't be unchecked",Icon:P},{kind:"endurance_race_run",fn:"regression-e2e-endurance-race-run",label:"Endurance race run (green flag â countdown â final results)",desc:"timed endurance session on a real schedule slot · grid collapses to ONE team row + the solo rider · countdown clock counts down from the race length · team laps recorded with the rider on track · solo laps unchanged · a crossing after the clock does not count · finalize produces team results with each rider's laps · finalized results stay protected",Icon:P},{kind:"lab_event_edit_propagation",fn:"regression-e2e-lab-event-edit-propagation",label:"Timing Lab event edits",desc:"location/date propagate · class rename keeps mirror · name-change guard fires after registrations",Icon:Qe},{kind:"class_rename_propagation",fn:"regression-e2e-class-rename-propagation",label:"Class rename propagation",desc:"rename flows to event_classes · registrations · form_data · form_responses · roster UID · past events + results stay frozen",Icon:Xe}
31,{kind:"rider_account",fn:"regression-e2e-rider-account",label:"Rider account surfaces",desc:"account + profile · family link · drafts save/resume/discard · register-again carry-over by class UID · SMS alert signup · email alert signup · photo upload",Icon:es},{kind:"kiosk_journey",fn:"regression-e2e-kiosk-journey",label:"Full kiosk journey (admin + standalone)",desc:"save-rider · EPC append/replace · single-race vs series class save · bike # + make edits · uncheck removal · rider rename · walk-up â check-in â edit · admin/standalone parity",Icon:W},{kind:"race_day_journey",fn:"regression-e2e-race-day-journey",label:"Full race day journey (roster â laps â results)",desc:"registration mirrors to the roster · ensure_for_slot picks the right class · start â manual laps â stop â finalize · posted order + points land · finalized session can't be deleted or re-finalized · rider account (real JWT) sees 'Xth out of N' · public/TV results include the winner",Icon:P},{kind:"cancel_refund_journey",fn:"regression-e2e-cancel-refund-journey",label:"Full cancel / refund journey (money + roster)",desc:"real 4242 charge · cancel pulls the rider off the Lab roster + class · undo snapshot written · rider WITH laps is never removed · dry run mutates nothing · refund cap = paid â BP fee â Stripe fee · Stripe agrees with the app · double refund refused · $0 left counted as collected",Icon:A},{kind:"organizer_setup",fn:"regression-e2e-organizer-setup",label:"Full organizer setup journey (create â publish â next round)",desc:"draft event stays private · form field + priced class · publish â anon client can load event, classes, form · slug uniqueness (and null slugs OK) · lab event linked · no double-link hijack · duplicate for round 2 carries classes, not riders · class rename propagates to the form by UID · signup trips the name lock",Icon:ge},{kind:"payouts_analytics",fn:"regression-e2e-payouts-analytics",label:"Payouts + analytics accuracy",desc:"draft payout math = gross â Stripe fee â BP fee â reversals · $1,000 holdback never breached · existing reversal never double-applied · org payout schedule readable by members only · payout RLS scoped to own org · analytics gross/net/refunds match a seeded ledger (refunds â $0)",Icon:V},{kind:"payout_reserve_freeze",fn:"regression-e2e-payout-reserve-freeze",label:"Payout reserves + frozen release amount",desc:"live-timing reserve counts paid class entries from form_responses with per-event tier reset · only the 7-day payout week counts · series parents excluded, rounds counted · unpaid never inflates · required = timing à (1 + buffer) + equipment · recompute-draft-reserve is idempotent and clamps to the releasable base · a frozen release amount is a hard cap (later deposits roll to next period) · scheduled payouts can't be recomputed · disabling reserves yields $0",Icon:V},{kind:"messaging_coupons",fn:"regression-e2e-messaging-coupons",label:"Scheduled messaging + coupons/wallet",desc:"future-dated send stays pending (never fires instantly) · dispatcher skips not-yet-due · due row claimed exactly once under concurrency · recipient targeting by event, class and extras · >5k blast blocked without the high-volume flag · coupon fine print persists to the view path · CSV mapping â correct recipients · redemption discounts the real charge · single-use can't redeem twice · coupon lands in the rider wallet",Icon:oe},{kind:"tenant_isolation",fn:"regression-e2e-tenant-isolation",label:"Multi-tenant isolation + concurrent live timing",desc:"two real org-admin JWTs · org A can't read org B registrations, riders, Lab roster, payouts or RFID reads · anon sees published events only, no PII, no writes · public slug + lab results never cross orgs · same EPC timed in both orgs at once with zero cross-talk · a bridge key can't post into another org · heartbeat + pairing stay org-scoped",Icon:Y},{kind:"standings_identity",fn:"regression-e2e-standings-identity",label:"Series standings + rider identity",desc:"moto â round â series points roll-up · DNF > DNS > DSQ ordering · tiebreaker by moto walk-back then best lap · mid-series class rename resolves by UID with zero orphans · nickname + DOB±1 duplicate grouping · 'separate people' exclusion sticks · merged loser never resurfaces · cross-org BP# collision flagged",Icon:j},{kind:"core_lib_contracts",fn:"regression-e2e-core-lib-contracts",label:"Core library contracts (BP# · search · URLs · form options · feed · diff · kiosk identity)",desc:"real ensureBpAndUse mints BP# ⥠100000 and re-resolves to the same number · live BP#s unique · tokenizer + escaping parity with the browser mirror · real rider-database-lookup survives O'Brien / commas / % input · javascript: and data: hrefs rejected and no live org logo or feed link stores one · option readers never throw on real form_fields.options · real feed posts split losslessly and share text str
31ips image tokens · class-entry diff never proposes a delete · kiosk person key collapses one rider's rounds but keeps siblings on a shared email apart",Icon:ge},{kind:"org_event_types",fn:"regression-e2e-org-event-types",label:"Org event-type naming + capabilities (motorsports / running / cycling)",desc:"Riders vs Runners vs Cyclists · Class vs Distance vs Category · Gate Pick vs Corral vs Start Position · Laps vs Splits · moto tools never show on running orgs · every live org has a valid event_type (no silent fallback) · anon branding read returns event_type · real fetch-bike-makes serves moto vs bicycle brands · live lab events only use race types their org can pick · running Lab classes read as Distance â Age Group",Icon:Ze},{kind:"rls_user_paths",fn:"regression-e2e-rls-user-paths",label:"RLS on real user paths (anon + rider + org-admin keys)",desc:"no service-role shortcuts · anon guest registration lands, unpublished + bad-email refused, no PII read-back · rider JWT owns profile, family, drafts and read-only wallet · cross-rider read/write refused · rider can't touch events, classes, payouts or Lab roster · org-admin writes own org, refused + blind on the other org",Icon:E},{kind:"duplicate_rider_guard",fn:"regression-e2e-duplicate-rider-guard",label:"Duplicate-rider guard (public form âis this you?â)",desc:"name alone never returns a rider · wrong DOB + right email still finds them with MASKED data only (no raw email/DOB/phone/id) · signed token adopt returns prefill · forged token rejected · checkout with typoâd DOB pins to the confirmed profile instead of minting a new BP# · matcher adopts on name+email and never overwrites the good DOB · twins flagged ambiguous · tag on a zero-registration ghost redirects to the canonical profile · lookups logged for rate limiting",Icon:E},{kind:"any_email_matching",fn:"regression-e2e-any-email-matching",label:"Rider matching on any known email",desc:"find-my-rider resolves a rider by an email that only exists on an old registration · and by a verified linked account email · payload stays masked and the adopt token releases the right profile · matcher adopts on a historical email with a badly wrong DOB and never overwrites the stored DOB/email · adopts on a linked-account email with no DOB at all · an unknown email never attaches to a stranger · twins sharing a parent email stay ambiguous · zero duplicate BP#s minted",Icon:j},{kind:"auth_caller_fidelity",fn:"regression-e2e-auth-caller-fidelity",label:"Caller / auth fidelity (anon · POS PIN · rider JWT · org-admin JWT)",desc:"every endpoint is called by the credential production actually uses · anon reaches create-checkout, fetch-rider-entries, fetch-bike-makes, lookup-registration and kiosk-data with a valid POS PIN · missing/fake PIN refused · org-admin JWT accepted by save-rider, bike makes and race readiness · rider JWT refused by transponder writes, class rename, payouts and SMS · org-admin refused by payout approval and full DB export · rider JWT accepted on their own alerts, anonymous refused",Icon:E},{kind:"cancellation_notice",fn:"regression-e2e-cancellation-notice",label:"Cancelled-event rider options (rain-out pop-up)",desc:"one card per rider on the account with each entry's own amount · another account's rider never leaks in · season-pass entries are credit-only at the prorated amount · a disabled option is refused with nothing written · credit issues a wallet transaction and cancels the entry · transfer rebuilds the rider on the makeup round with class answers remapped · double-click refused (no double credit) · resolved cards drop out · past deadline and disabled notice hide it entirely",Icon:A},{kind:"cancellation_multi_order",fn:"regression-e2e-cancellation-multi-order",label:"Cancelled round with more than one order",desc:"two separately paid entries are both claimable · two classes on one order settle with a single choice and a second click is refused · a multi-round order can still claim a round cancelled later · moving to the make-up round never swallows a class the rider already paid for (value comes back as org-locked credit)",Icon:A},{kind:"wallet_credit_journey",fn:"regression-e2e-wallet-credit-journey",label:"Wallet credit through a real checkout",desc:"partial-credit checkout reaches Stripe without touching the ledger · backing out leaves the credit spendable · payment completion deducts exactly once under webhook retries · full-credit cart finishes at $0 with no Stripe · double submit books one entry and one deduction · abandoned holds sweep back once and real payments are never released · overspend and cross-org spend refused",Icon:De}
31,{kind:"rider_identity_editor",fn:"regression-e2e-rider-identity-editor",label:"Rider Database lookup â identity edit persists everywhere",desc:"edit opened on a merged duplicate lands on the surviving profile · canonical record saves name, DOB (with split birth parts), phone, address and demographics · every org's Lab roster row renamed, including a legacy BP-only row with no UUID link · registration name/DOB cache refreshed · sibling in the same org untouched · audit row logs before/after + changed fields · search and detail read back the corrected identity · blank name refused with no partial write · unknown rider 404s instead of silently succeeding",Icon:j}];function Qs(){const e=ze(),n=ms(),[i,d]=f.useState({}),[l,p]=f.useState(null),[se,Se]=f.useState([]),[k,S]=f.useState(null),[ne,te]=f.useState(!1),ae=async()=>{try{await h.functions.invoke("regression-e2e-purge",{body:{older_than_minutes:0}})}catch{}},Ne=async()=>{te(!0);try{const{data:t,error:r}=await h.functions.invoke("regression-e2e-purge",{body:{older_than_minutes:0}});if(r)throw r;const o=(t==null?void 0:t.total)??0;g.success(o?`Removed ${o} leftover test rows`:"No leftover test data found")}catch(t){g.error(`Cleanup failed: ${t instanceof Error?t.message:String(t)}`)}finally{te(!1)}},[Ie,Ce]=f.useState({}),[Ee,Pe]=f.useState({});async function v(){const{data:t}=await h.from("regression_test_runs").select("id, suite, total, passed, failed, ran_at").order("ran_at",{ascending:!1}).limit(20);Se(t||[])}f.useEffect(()=>{v()},[]);async function re(t){const{data:{user:r}}=await h.auth.getUser();r&&await h.from("regression_test_runs").insert([{suite:t.suite,total:t.total,passed:t.passed,failed:t.failed,failures:t.cases.filter(o=>!o.passed),ran_by:r.id}])}async function Te(t){p(t);try{const r=je(t);d(o=>({...o,[t]:r})),await re(r),r.failed===0?g.success(`${r.label}: ${r.passed}/${r.total} passed`):g.error(`${r.label}: ${r.failed} failed`),await v()}finally{p(null)}}async function Oe(){p("all");try{const t=ps(),r={};for(const u of t)r[u.suite]=u,await re(u);d(r);const o=t.reduce((u,c)=>u+c.failed,0);o===0?g.success(`All suites green (${t.reduce((u,c)=>u+c.passed,0)} checks)`):g.error(`${o} failures across suites`),await v()}finally{p(null)}}async function ie(t){const r=T.find(o=>o.kind===t);try{let o=null,u=null;for(let m=0;m<3;m++){const N=await h.functions.invoke(r.fn,{body:{}});if(o=N.data,u=N.error,!u)break;const Re=String((u==null?void 0:u.message)??u);if(!/50[234]|Bad Gateway|Failed to fetch|NetworkError/i.test(Re)||m===2)break;await new Promise(Me=>setTimeout(Me,1500*(m+1)))}if(u)throw u;const c=o;Ce(m=>({...m,[t]:c}));const{data:{user:w}}=await h.auth.getUser();return w&&await h.from("regression_test_runs").insert([{suite:`e2e_${t}`,total:c.total,passed:c.passed,failed:c.failed,failures:c.steps.filter(m=>!m.ok),ran_by:w.id}]),c}catch(o){return g.error(`${r.label} error: ${o instanceof Error?o.message:String(o)}`),null}}async function Le(t){S(t);const r=T.find(o=>o.kind===t);try{const o=await ie(t);o&&(o.ok?g.success(`${r.label}: ${o.passed}/${o.total} passed`):g.error(`${r.label}: ${o.failed} failed`)),await ae(),await v()}finally{S(null)}}async function Be(){S("all-e2e");try{let t=0,r=0;for(const o of T){const u=await ie(o.kind);u&&(t+=u.passed,r+=u.failed)}r===0?g.success(`All E2E green (${t} steps)`):g.error(`${r} step failures across E2E suites`),await ae(),await v()}finally{S(null)}}return s.jsxs("div",{className:"container mx-auto p-6 max-w-5xl",children:[s.jsxs("div",{className:"flex items-center justify-between mb-6",children:[s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs(y,{variant:"ghost",size:"sm",onClick:()=>e("/super-admin"),children:[s.jsx(Ke,{className:"h-4 w-4 mr-1"})," Back"]}),s.jsxs("div",{children:[s.jsx("h1",{className:"text-2xl font-semibold",children:"Regression Tests"}),s.jsx("p",{className:"text-sm text-muted-foreground",children:"Pure-logic checks for the helpers powering form builder, kiosk, race feed, and timing sync."})]})]}),s.jsxs(y,{onClick:Oe,disabled:l!==null,children:[l==="all"?s.jsx(x,{className:"h-4 w-4 mr-2 animate-spin"}):s.jsx(C,{className:"h-4 w-4 mr-2"}),"Run all suites"]})]}),s.jsx("div",{className:"grid gap-4 md:grid-cols-2",children:n.map(t=>{const r=i[t.id];return s.jsxs(M,{children:[s.jsx(z,{children:s.jsxs("div",{className:"flex items-start justify-between gap-3",children:[s.jsxs("div",{children:[s.jsx(D,{className:"text-base",children:t.label}),s.jsxs(U,{children:[t.total," checks"]})]}),r&&s.jsx($,{variant:r.failed===0?"default":"destructive",children:r.failed===0?s.jsxs(s.Fragment,{children:[s.jsx(F,{className:"h-3 w-3 mr-1"})," ",r.passed,"/",r.total]}):s.jsxs(s.Fragment,{children:[s.jsx(q,{className:"h-3 w-3 mr-1"})," ",r.failed," failed"]})})]})}),s.jsxs(K,{children:[s.jsxs(y,{variant:"outline",size:"sm",disabled:l!==null,onClick:()=>Te(t.id),children:[l===t.id?s.jsx(x,{className:"h-4 w-4 mr-2 animate-spin"}):s.jsx(C,{className:"h-4 w-4 mr-2"}),"Run"]}),r&&r.failed>0&&s.jsx("ul",{className:"mt-3 space-y-1 text-xs text-destructive",children:r.cases.filter(o=>!o.passed).map(o=>s.jsxs("li",{children:[s.jsx("span",{className:"font-medium",children:o.name}),": ",o.error]},o.name))})]})]},t.id)})}),s.jsxs(M,{className:"mt-6",children:[s.jsx(z,{children:s.jsxs("div",{className:"flex items-start justify-between gap-3",children:[s.jsxs("div",{children:[s.jsx(D,{className:"text-base",children:"Live end-to-e
31nd (cloud)"}),s.jsxs(U,{children:["Runs realistic flows against a dedicated ",s.jsx("span",{className:"font-mono",children:"Beyond Pro Test"})," ","organization (auto-created on first run) with dummy data that's cleaned up in the same call. Real customer orgs are never touched. Expand the log on any run to see the full step trace with errors."]})]}),s.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[s.jsxs(y,{size:"sm",variant:"outline",onClick:Ne,disabled:ne||k!==null,children:[ne?s.jsx(x,{className:"h-4 w-4 mr-2 animate-spin"}):s.jsx(Ae,{className:"h-4 w-4 mr-2"}),"Clean up test data"]}),s.jsxs(y,{size:"sm",onClick:Be,disabled:k!==null,children:[k==="all-e2e"?s.jsx(x,{className:"h-4 w-4 mr-2 animate-spin"}):s.jsx(C,{className:"h-4 w-4 mr-2"}),"Run all E2E"]})]})]})}),s.jsx(K,{className:"space-y-4",children:T.map(({kind:t,label:r,desc:o,Icon:u})=>{const c=Ie[t],w=Ee[t];return s.jsxs("div",{className:"border rounded-md p-3",children:[s.jsxs("div",{className:"flex items-start justify-between gap-3",children:[s.jsxs("div",{className:"flex gap-3 items-start",children:[s.jsx(u,{className:"h-5 w-5 mt-0.5 text-muted-foreground"}),s.jsxs("div",{children:[s.jsx("div",{className:"font-medium text-sm",children:r}),s.jsx("div",{className:"text-xs text-muted-foreground",children:o})]})]}),s.jsxs("div",{className:"flex items-center gap-2",children:[c&&s.jsx($,{variant:c.ok?"default":"destructive",children:c.ok?s.jsxs(s.Fragment,{children:[s.jsx(F,{className:"h-3 w-3 mr-1"})," ",c.passed,"/",c.total]}):s.jsxs(s.Fragment,{children:[s.jsx(q,{className:"h-3 w-3 mr-1"})," ",c.failed," failed"]})}),s.jsxs(y,{size:"sm",variant:"outline",disabled:k!==null,onClick:()=>Le(t),children:[k===t?s.jsx(x,{className:"h-4 w-4 mr-2 animate-spin"}):s.jsx(C,{className:"h-4 w-4 mr-2"}),"Run"]})]})]}),c&&s.jsxs(s.Fragment,{children:[s.jsx("ul",{className:"mt-3 space-y-1 text-xs",children:c.steps.map((m,N)=>s.jsxs("li",{className:"flex items-start gap-2",children:[m.ok?s.jsx(F,{className:"h-3 w-3 mt-0.5 text-green-600 shrink-0"}):s.jsx(q,{className:"h-3 w-3 mt-0.5 text-destructive shrink-0"}),s.jsxs("span",{className:m.ok?"text-muted-foreground":"text-destructive",children:[m.name,m.ms!=null&&s.jsxs("span",{className:"ml-2 opacity-60",children:[m.ms,"ms"]}),m.detail&&s.jsxs("span",{className:"ml-2 opacity-70",children:["â ",m.detail]}),m.error&&s.jsx("div",{className:"mt-0.5 font-mono",children:m.error})]})]},N))}),c.log&&c.log.length>0&&s.jsxs("div",{className:"mt-3",children:[s.jsxs("button",{type:"button",className:"text-xs inline-flex items-center gap-1 text-muted-foreground hover:text-foreground",onClick:()=>Pe(m=>({...m,[t]:!m[t]})),children:[s.jsx(ns,{className:"h-3 w-3"}),w?"Hide":"Show"," verbose log (",c.log.length," lines)"]}),w&&s.jsx("pre",{className:"mt-2 max-h-64 overflow-auto rounded bg-muted p-2 text-[11px] leading-tight whitespace-pre-wrap break-all",children:c.log.join(` 32`)})]})]})]},t)})})]}),s.jsxs(M,{className:"mt-6",children:[s.jsxs(z,{children:[s.jsx(D,{className:"text-base",children:"Recent runs"}),s.jsx(U,{children:"Last 20 executions across all suites"})]}),s.jsx(K,{children:se.length===0?s.jsx("p",{className:"text-sm text-muted-foreground",children:"No runs yet."}):s.jsx("div",{className:"text-sm",children:se.map(t=>s.jsxs("div",{className:"flex items-center justify-between py-1.5 border-b last:border-0",children:[s.jsx("span",{className:"font-mono text-xs",children:t.suite}),s.jsx("span",{className:"text-muted-foreground text-xs",children:new Date(t.ran_at).toLocaleString()}),s.jsxs($,{variant:t.failed===0?"default":"destructive",children:[t.passed,"/",t.total]})]},t.id))})})]})]})}export{Qs as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.