1/* A fixed synthetic example. No customer upload, token or external action. */ 2(() => { 3 "use strict"; 4 const node = (id) => document.getElementById(id); 5 let session = null; 6 let current = null; 7 let generation = 0; 8 let pending = false; 9 let pollTimer = null; 10 let pollsLeft = 20; 11 const actions = ["pilot-create", "pilot-approve", "pilot-reject", "pilot-refresh"]; 12 function reset() { 13 generation += 1; 14 clearTimeout(pollTimer); 15 pollTimer = null; 16 pollsLeft = 20; 17 session = current = null; 18 pending = false; 19 node("guided-pilot").hidden = true; 20 node("pilot-result").textContent = "The guided example has not started."; 21 for (const id of actions) { node(id).hidden = id !== "pilot-refresh"; node(id).disabled = false; } 22 } 23 function render(value) { 24 if (!value || value.schema !== "guided-pilot-status-v1" || value.customer_data_enabled !== false 25 || value.production_activation_authorized !== false || value.production_readiness_complete !== false 26 || value.example?.template !== "evidence-review-v1" || value.example?.data_class !== "synthetic_only" 27 || !["awaiting_approval", "not_started", "pending", "approved", "rejected"].includes(value.state) 28 || (value.state === "pending" && !/^[a-f0-9]{64}$/.test(value.review_sha256 || "")) 29 || typeof value.can_create !== "boolean" || typeof value.can_review !== "boolean" 30 || (value.can_create && value.state !== "not_started") 31 || (value.can_review && value.state !== "pending") 32 || (value.state === "awaiting_approval" && (value.review_sha256 !== null || value.decision !== null)) 33 || value.pilot_example_complete !== (value.state === "approved")) throw new Error("invalid_pilot_status"); 34 current = value; 35 node("guided-pilot").hidden = false; 36 node("pilot-create").hidden = !value.can_create; 37 node("pilot-approve").hidden = node("pilot-reject").hidden = !value.can_review; 38 node("pilot-result").textContent = { 39 awaiting_approval: "Your workspace access is accepted. Waiting for your team to approve the synthetic pilot. Saved progress updates automatically for five minutes; you can also refresh it below.", 40 not_started: value.can_create ? "Create the provided example, then ask your designated reviewer to review it." : "Your operator has not created the example yet.", 41 pending: value.can_review ? "Review the synthetic facts below and record your decision." : "The example is saved and waiting for your designated reviewer.", 42 approved: "Your reviewer approved the example. Your synthetic pilot exercise is complete. Real customer data remains disabled.", 43 rejected: "Your reviewer rejected the example. The decision is saved. Contact your DigiTrust team to discuss it.", 44 }[value.state]; 45 const waiting = value.state === "awaiting_approval" 46 || (value.state === "not_started" && !value.can_create) 47 || (value.state === "pending" && !value.can_review); 48 if (waiting && pollsLeft > 0) { 49 const epoch = generation; 50 pollTimer = setTimeout(() => { 51 pollTimer = null; 52 if (epoch !== generation) return; 53 pollsLeft -= 1; 54 load(); 55 }, 15000); 56 } 57 } 58 async function send(path, options, epoch) { 59 for (let attempt = 0; attempt < 3; attempt += 1) { 60 if (epoch !== generation) throw new Error("session_changed"); 61 const controller = new AbortController(); 62 const timeout = setTimeout(() => controller.abort(), 10000); 63 let retry = false; 64 try { 65 const response = await fetch(path, { ...options, signal: controller.signal }); 66 if (response.ok) return response; 67 retry = [409, 502, 503, 504].includes(response.status); 68 } catch (error) { 69 retry = ["TypeError", "AbortError"].includes(error.name); 70 } finally { clearTimeout(timeout); } 71 // A write is never repeated, even after a transport failure. 72 if (options.method === "POST" || !retry || attempt === 2) throw new Error("pilot_unavailable"); 73 await new Promise((resolve) => setTimeout(resolve, 1000)); 74 } 75 } 76 async function load(path = "/api/onboarding/guided-pilot", body = null) { 77 if (!session || pending) return; 78 const epoch = generation; 79 pending = true; 80 clearTimeout(pollTimer); 81 pollTimer = null; 82 for (const id of actions) node(id).disabled = true; 83 try { 84 const options = { credentials: "same-origin", cache: "no-store" }; 85 if (body !== null) Object.assign(options, { method: "POST", headers: { 86 "Content-Type": "application/json", "X-CSRF-Token": session.csrf_token, 87 }, body: JSON.stringify(body) }); 88 const response = await send(path, options, epoch); 89 const value = await response.json(); 90 if (epoch !== generation) return; 91 render(value); 92 return true; 93 } catch (_) { 94 if (epoch !== generation) return; 95 current = null; 96 node("guided-pilot").hidden = false;
97 node("pilot-create").hidden = node("pilot-approve").hidden = node("pilot-reject").hidden = true; 98 node("pilot-result").textContent = body === null 99 ? "The guided pilot is not available for this session yet. Your DigiTrust team can check access." 100 : "The result has not been confirmed. Refresh the saved result before taking another action."; 101 return false; 102 } finally { 103 if (epoch === generation) { pending = false; for (const id of actions) node(id).disabled = false; } 104 } 105 } 106 node("pilot-create").addEventListener("click", () => { 107 if (current?.can_create) load("/api/onboarding/guided-pilot/example", {}); 108 }); 109 for (const [id, decision] of [["pilot-approve", "approved"], ["pilot-reject", "rejected"]]) { 110 node(id).addEventListener("click", () => { 111 if (current?.can_review) load("/api/onboarding/guided-pilot/decision", { 112 review_sha256: current.review_sha256, decision, 113 }); 114 }); 115 } 116 node("pilot-refresh").addEventListener("click", () => load()); 117 window.DigiTrustPilot = { reset, setSession(value) { 118 reset(); 119 if (!value?.authenticated || !value.tenant_id || !value.csrf_token 120 || !(value.roles || []).some((role) => ["operator", "approver"].includes(role))) return; 121 session = { csrf_token: value.csrf_token }; 122 return load(); 123 } }; 124})();
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.