@buoy-gg/bug-links 0.0.0-stage → 7.0.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BuoyBugLinks.podspec +18 -0
- package/LICENSE +58 -0
- package/README.md +25 -2
- package/android/build.gradle +31 -0
- package/android/consumer-rules.pro +8 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/gg/buoy/buglinks/frames/BuoyFramesModule.kt +53 -0
- package/android/src/main/java/gg/buoy/buglinks/frames/BuoyFramesPackage.kt +11 -0
- package/android/src/main/java/gg/buoy/buglinks/frames/FrameEngine.kt +707 -0
- package/android/src/main/java/gg/buoy/buglinks/frames/FrameWalker.kt +181 -0
- package/ios/BuoyFrames.m +17 -0
- package/ios/BuoyFrames.swift +640 -0
- package/ios/BuoyFramesWalk.swift +213 -0
- package/lib/commonjs/components/BugLinksBody.js +1 -0
- package/lib/commonjs/components/TeamBugs.js +1 -0
- package/lib/commonjs/components/focus.js +1 -0
- package/lib/commonjs/components/overlays.js +1 -0
- package/lib/commonjs/components/topBanners.js +1 -0
- package/lib/commonjs/engine/client.js +1 -0
- package/lib/commonjs/engine/credential.js +1 -0
- package/lib/commonjs/engine/linksStore.js +1 -0
- package/lib/commonjs/engine/loader.js +1 -0
- package/lib/commonjs/engine/opener.js +1 -0
- package/lib/commonjs/engine/otherTabs.js +1 -0
- package/lib/commonjs/engine/phoneCheck.js +1 -0
- package/lib/commonjs/engine/phoneCrypto.js +1 -0
- package/lib/commonjs/engine/randomBytes.js +1 -0
- package/lib/commonjs/engine/reporter.js +1 -0
- package/lib/commonjs/engine/sandbox.js +1 -0
- package/lib/commonjs/engine/settings.js +1 -0
- package/lib/commonjs/engine/tabStorage.js +1 -0
- package/lib/commonjs/engine/teamKeys.js +1 -0
- package/lib/commonjs/engine/teamList.js +1 -0
- package/lib/commonjs/index.js +1 -0
- package/lib/commonjs/media/BuoyMask.js +1 -0
- package/lib/commonjs/media/frames.js +1 -0
- package/lib/commonjs/media/nativeFrames.js +1 -0
- package/lib/commonjs/media/nativeFrames.web.js +1 -0
- package/lib/commonjs/media/webFrames.js +14 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/preset.js +1 -0
- package/lib/commonjs/sync/bugLinksSyncAdapter.js +1 -0
- package/lib/module/components/BugLinksBody.js +1 -0
- package/lib/module/components/TeamBugs.js +1 -0
- package/lib/module/components/focus.js +1 -0
- package/lib/module/components/overlays.js +1 -0
- package/lib/module/components/topBanners.js +1 -0
- package/lib/module/engine/client.js +1 -0
- package/lib/module/engine/credential.js +1 -0
- package/lib/module/engine/linksStore.js +1 -0
- package/lib/module/engine/loader.js +1 -0
- package/lib/module/engine/opener.js +1 -0
- package/lib/module/engine/otherTabs.js +1 -0
- package/lib/module/engine/phoneCheck.js +1 -0
- package/lib/module/engine/phoneCrypto.js +1 -0
- package/lib/module/engine/randomBytes.js +1 -0
- package/lib/module/engine/reporter.js +1 -0
- package/lib/module/engine/sandbox.js +1 -0
- package/lib/module/engine/settings.js +1 -0
- package/lib/module/engine/tabStorage.js +1 -0
- package/lib/module/engine/teamKeys.js +1 -0
- package/lib/module/engine/teamList.js +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/media/BuoyMask.js +1 -0
- package/lib/module/media/frames.js +1 -0
- package/lib/module/media/nativeFrames.js +1 -0
- package/lib/module/media/nativeFrames.web.js +1 -0
- package/lib/module/media/webFrames.js +14 -0
- package/lib/module/preset.js +1 -0
- package/lib/module/sync/bugLinksSyncAdapter.js +1 -0
- package/lib/typescript/components/BugLinksBody.d.ts +2 -0
- package/lib/typescript/components/TeamBugs.d.ts +8 -0
- package/lib/typescript/components/focus.d.ts +6 -0
- package/lib/typescript/components/overlays.d.ts +10 -0
- package/lib/typescript/components/topBanners.d.ts +35 -0
- package/lib/typescript/engine/client.d.ts +78 -0
- package/lib/typescript/engine/credential.d.ts +11 -0
- package/lib/typescript/engine/linksStore.d.ts +132 -0
- package/lib/typescript/engine/loader.d.ts +172 -0
- package/lib/typescript/engine/opener.d.ts +21 -0
- package/lib/typescript/engine/otherTabs.d.ts +52 -0
- package/lib/typescript/engine/phoneCheck.d.ts +8 -0
- package/lib/typescript/engine/phoneCrypto.d.ts +16 -0
- package/lib/typescript/engine/randomBytes.d.ts +7 -0
- package/lib/typescript/engine/reporter.d.ts +53 -0
- package/lib/typescript/engine/sandbox.d.ts +83 -0
- package/lib/typescript/engine/settings.d.ts +42 -0
- package/lib/typescript/engine/tabStorage.d.ts +6 -0
- package/lib/typescript/engine/teamKeys.d.ts +37 -0
- package/lib/typescript/engine/teamList.d.ts +63 -0
- package/lib/typescript/index.d.ts +23 -0
- package/lib/typescript/media/BuoyMask.d.ts +19 -0
- package/lib/typescript/media/frames.d.ts +34 -0
- package/lib/typescript/media/nativeFrames.d.ts +2 -0
- package/lib/typescript/media/nativeFrames.web.d.ts +2 -0
- package/lib/typescript/media/webFrames.d.ts +178 -0
- package/lib/typescript/preset.d.ts +50 -0
- package/lib/typescript/sync/bugLinksSyncAdapter.d.ts +41 -0
- package/lib/web/index.mjs +20 -0
- package/package.json +119 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.pickTeamKey=pickTeamKey;exports.resetTeamKeysForTests=resetTeamKeysForTests;exports.trustTeamRoot=trustTeamRoot;var _sharedUi=require("@buoy-gg/shared-ui");var _client=require("./client");var _credential=require("./credential");var _phoneCrypto=require("./phoneCrypto");var _settings=require("./settings");var _otherTabs=require("./otherTabs");const trustKey=()=>`${_sharedUi.devToolsStorageKeys.bugLinks.root()}_team_trust`;const CACHE_MS=5*60*1e3;const cache=new Map;let trustAll=null;let trustLoading=null;async function readTrustRaw(){const r=(0,_otherTabs.tabsMatter)()?globalThis.localStorage:void 0;return r?r.getItem(trustKey()):(0,_sharedUi.getItemChecked)(trustKey())}function loadTrust(){if(trustAll)return Promise.resolve(trustAll);trustLoading??=(async()=>{let r;try{r=await readTrustRaw()}catch(e){trustLoading=null;throw e}try{const e=r?JSON.parse(r):{};const t=e&&typeof e==="object"?e:{};trustAll=(0,_otherTabs.tabsMatter)()&&unsavedTrust?mergeTrust(t,unsavedTrust):t}catch(e){trustLoading=null;if((0,_otherTabs.tabsMatter)())throw e;trustAll={}}return trustAll})();return trustLoading}let checking=Promise.resolve();const TRUST_LOCK="buoy-bug-links-team-trust";function oneAtATime(r){const e=()=>(0,_otherTabs.withTabLock)(TRUST_LOCK,async()=>{if((0,_otherTabs.tabsMatter)()){trustAll=null;trustLoading=null}return r()});const t=checking.then(e,e);checking=t.catch(()=>{});return t}let unsavedTrust=null;function mergeTrust(r,e){const t=JSON.parse(JSON.stringify(r));for(const[s,o]of Object.entries(e)){const n=t[s]??={roots:[],maxVersion:{}};for(const[a,u]of Object.entries(o))n.maxVersion[a]=Math.max(n.maxVersion[a]??0,u)}return t}async function saveTrust(){const r=JSON.stringify(trustAll??{});if((0,_otherTabs.tabsMatter)()){const e=await(0,_sharedUi.setItemChecked)(trustKey(),r,{strict:true}).catch(()=>false);if(e)unsavedTrust=null;else{const t=unsavedTrust??{};for(const[s,o]of Object.entries(trustAll??{})){const n=t[s]??={};for(const[a,u]of Object.entries(o.maxVersion))n[a]=Math.max(n[a]??0,u)}unsavedTrust=t}return e}try{return await(0,_sharedUi.setItemChecked)(trustKey(),r,{strict:true})}catch{return false}}const wait=(r,e,t)=>({kind:"wait",code:r,message:e,teamKeyId:t});function readBundle(r){const e=r??{};if(typeof e.owner!=="string"||!Array.isArray(e.roots)||!Array.isArray(e.versions))return null;const t=e.current;if(t!==null&&(typeof t?.teamKeyId!=="string"||!Number.isSafeInteger(t?.keyVersion)))return null;return{owner:e.owner,roots:e.roots,versions:e.versions,current:t}}async function pickTeamKey(r,e,t={}){if(!e)return wait("signed_out","Sign in to Buoy so this bug can be locked to your team.");const s=`${r}|${e.slice(-16)}`;const o=cache.get(s);if(o&&!t.fresh&&Date.now()-o.at<CACHE_MS&&!await belowMark(r,e,o.pick))return o.pick;const n=await oneAtATime(()=>pickNow(r,e));if(n.kind!=="wait"||n.code!=="offline")cache.set(s,{at:Date.now(),pick:n});return n}async function belowMark(r,e,t){if(t.kind!=="ok"||!(0,_otherTabs.tabsMatter)())return false;try{const s=(0,_settings.teamKeyPins)();if(s&&!s.includes(t.target.teamKeyId))return true;const o=await readTrustRaw();const n=o?JSON.parse(o):{};const a=n[`${r}|${(0,_credential.credentialOwner)(e)??"unknown"}`];if(!s&&a?.roots?.length&&!a.roots.includes(t.target.teamKeyId))return true;return t.target.keyVersion<(a?.maxVersion?.[t.target.teamKeyId]??0)}catch{return true}}async function pickNow(r,e){let t;try{const i=await(0,_client.fetchTeamKeys)(r,e);if(i===null)return{kind:"off"};t=readBundle(i)}catch(i){if(i instanceof _client.BugLinksHttpError&&i.code==="signed_out")return wait("signed_out","Sign in to Buoy so this bug can be locked to your team.");if(i instanceof _client.BugLinksHttpError&&i.code==="needs_pro")return{kind:"off"};return wait("offline","Couldn't reach the team key. Buoy will lock this bug to your team later.")}if(!t)return wait("untrusted","The server sent a team key Buoy can't read.");const s=t.owner;if(!t.roots.length)return{kind:"off",owner:s};const o=(i,w,k)=>({...wait(i,w,k),owner:s});const n=t.current;if(!n){return o("no_current","Your team's key was retired after a team change. Once an admin makes a new one, Buoy locks this bug to it.")}const a=t.roots.find(i=>i.teamKeyId===n.teamKeyId);const u=t.versions.find(i=>i.teamKeyId===n.teamKeyId&&i.keyVersion===n.keyVersion);const d=a?(0,_sharedUi.base64UrlDecode)(a.pub):null;if(!a||!u||!d)return o("untrusted","The server's team key is missing a part.");if(a.retiredAt||u.retiredAt)return o("no_current","Your team's key was retired. Once an admin makes a new one, Buoy locks this bug to it.");const l=(0,_settings.teamKeyPins)();let y;try{y=await loadTrust()}catch{return o("untrusted","Buoy couldn't read which team keys this device trusts. It will lock this bug to your team later.")}const f=`${r}|${(0,_credential.credentialOwner)(e)??"unknown"}`;const c=y[f]??{roots:[],maxVersion:{}};if(l){if(!l.includes(n.teamKeyId)){return o("pin","This app's Buoy setup pins another team key. Update the teamKey setting.",n.teamKeyId)}}else if(c.roots.length&&!c.roots.includes(n.teamKeyId)){return o("new_root","Your team has a new key root. Open Bug links to trust it.",n.teamKeyId)}let m;try{m=await(0,_sharedUi.verifyRecord)((0,_phoneCrypto.teamKeyCrypto)(),d,{...u,owner:t.owner})}catch{return o("untrusted","The team key isn't signed by your team's root. Buoy didn't use it.")}const h=c.maxVersion[n.teamKeyId]??0;if(n.keyVersion<h)return o("untrusted","The server offered an older team key. Buoy didn't use it.");if(!l&&!c.roots.length)c.roots.push(n.teamKeyId);c.maxVersion[n.teamKeyId]=n.keyVersion;y[f]=c;if(!await saveTrust())return o("untrusted","Buoy couldn't save which team keys this device trusts. It will lock this bug to your team later.");return{kind:"ok",target:{owner:t.owner,teamKeyId:n.teamKeyId,keyVersion:n.keyVersion,pub:m}}}function trustTeamRoot(r,e,t){return oneAtATime(()=>trustRootNow(r,e,t))}async function trustRootNow(r,e,t){const s=await loadTrust();const o=`${r}|${(0,_credential.credentialOwner)(t)??"unknown"}`;if(s[o])s[o].roots=[e];const n=await saveTrust();cache.clear();if(!n)throw new Error("This device couldn't save the new team key. Free some space, then try again.")}function resetTeamKeysForTests(){cache.clear();trustAll=null;trustLoading=null;unsavedTrust=null}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.canLoadTeamBugs=canLoadTeamBugs;exports.changeTeamBug=changeTeamBug;exports.forgetTeamBugs=forgetTeamBugs;exports.loadTeamBug=loadTeamBug;exports.loadTeamBugs=loadTeamBugs;exports.resetTeamBugsForTests=resetTeamBugsForTests;exports.subscribeTeamBugs=subscribeTeamBugs;exports.teamBugs=teamBugs;exports.teamView=teamView;exports.whoNow=whoNow;var _sharedUi=require("@buoy-gg/shared-ui");var _client=require("./client");var _credential=require("./credential");var _loader=require("./loader");var _phoneCrypto=require("./phoneCrypto");var _settings=require("./settings");const MAX_PAGES=1e3;const IDLE={kind:"idle"};let state=IDLE;let stateWho=null;let stateOwner=null;let opened=null;let openedWho=null;let people=new Map;let session=0;let reads=0;const listeners=new Set;function notify(){for(const e of listeners)e()}function whoNow(){const e=(0,_credential.currentCredential)();return e?`${(0,_settings.bugLinksServer)()}|${(0,_credential.credentialOwner)(e)}`:null}function teamBugs(){if(state.kind==="idle")return state;return stateWho===whoNow()?state:IDLE}function subscribeTeamBugs(e){listeners.add(e);return()=>listeners.delete(e)}function forgetTeamBugs(){session++;reads++;wipe(opened);opened=null;openedWho=null;people=new Map;stateWho=null;stateOwner=null;state=IDLE;notify()}function wipe(e){for(const r of e?.versions.values()??[])r.d.fill(0)}function canLoadTeamBugs(){return!(typeof __DEV__!=="undefined"&&!__DEV__)}const versionKey=(e,r)=>`${e}|${r}`;async function loadTeamBugs(e=0){const r=session;await(0,_settings.loadBugLinksSettings)();if(r!==session)return;if(!(0,_settings.bugLinksSettingsRead)()){stateWho=whoNow();state={kind:"blocked",code:"error",message:"Buoy couldn't read its settings just now. Tap Refresh to try again."};notify();return}const t=(0,_credential.currentCredential)();const o=whoNow();if(stateWho!==null&&o!==stateWho)forgetTeamBugs();if(stateWho===null&&opened&&openedWho!==o){wipe(opened);opened=null;openedWho=null}const i=session;const c=++reads;const a=()=>c===reads&&i===session&&whoNow()===o;const l=(n,g=stateOwner)=>{if(!a())return;state=n;stateWho=o;stateOwner=g;notify()};if(!t||!o)return l({kind:"blocked",code:"signed_out",message:"Sign in to Buoy to see your team's bugs."});const m=n=>{if(!a()){wipe(n);return null}if(opened&&openedWho===o&&opened.owner===n.owner){wipe(n);return opened}if(opened&&opened!==n)wipe(opened);opened=n;openedWho=o;return n};const s=(0,_settings.bugLinksServer)();l(state.kind==="ready"?{...state,refreshing:true}:{kind:"loading"});const f=n=>{wipe(n);if(opened===n){opened=null;openedWho=null}l({kind:"loading"},null)};const d=()=>{if(e>=2)return l({kind:"blocked",code:"error",message:"Your team just changed. Tap Refresh."},null);return loadTeamBugs(e+1)};try{let n=opened;if(!n)n=m(await openKit(s,t));if(!n||!a())return;const g=[];let u="";const h=new Map;let k=null;for(let w=0;w<MAX_PAGES;w++){const y=await(0,_client.teamJson)(s,`/api/bugs?limit=100${k?`&before=${encodeURIComponent(k)}`:""}`,t);if(!a())return;if(y.owner&&y.owner!==n.owner){f(n);return d()}g.push(...y.cases);u=y.me.accountId;for(const p of y.people??[])h.set(p.id,p.name);k=y.next;if(!k)break}people=h;const v=await openCards(s,t,n,a);if(!a())return;if(v.unknownVersion){const w=await openKit(s,t);if(!a()||opened!==n)return wipe(w);if(w.owner!==n.owner){wipe(w);f(n);return d()}for(const[y,p]of n.cases)w.cases.set(y,p);wipe(n);opened=n=w;openedWho=o;await openCards(s,t,n,a);if(!a())return}const b=await(0,_client.teamJson)(s,"/api/bugs?limit=1",t);if(!a())return;if(b.owner&&b.owner!==n.owner){f(n);return d()}const B=state.kind==="ready"&&stateOwner===n.owner?new Map(state.bugs.map(w=>[w.id,w])):new Map;const T=g.map(w=>{const y=bugOf(w,n,u);const p=B.get(w.id);if(!p||p.rowRevision<=y.rowRevision)return y;const R=p.assignee?p.assignee===u?"You":people.get(p.assignee)??p.assignee:null;return{...y,status:p.status,assignee:p.assignee,assigneeName:R,rowRevision:p.rowRevision}});l({kind:"ready",me:u,bugs:T,refreshing:false},n.owner)}catch(n){const g=blockOf(n);if((g.code==="signed_out"||g.code==="no_access"||g.code==="needs_pro")&&a()&&openedWho===o){wipe(opened);opened=null;openedWho=null}l({kind:"blocked",...g})}finally{if(whoNow()!==o&&openedWho===o){wipe(opened);opened=null;openedWho=null}}}async function openKit(e,r){try{return await openKitOnce(e,r)}catch(t){if(!(t instanceof KitMismatch))throw t;return openKitOnce(e,r)}}class KitMismatch extends Error{}async function openKitOnce(e,r){const t=(0,_phoneCrypto.teamKeyCrypto)();const{teamKit:o}=await(0,_client.teamJson)(e,"/api/bugs/team-key/kit",r);const i=await(0,_client.teamJson)(e,"/api/bugs/team-key",r);const c=await(0,_client.teamJson)(e,"/api/bugs/team-key/locked",r);if(c.owner!==i.owner)throw new KitMismatch("The server sent keys for another team.");const a=(0,_sharedUi.parseKit)(o,"team");let l=null;try{const m=await(0,_sharedUi.kitId)(t,a,"team");if(m!==c.kitId)throw new KitMismatch("Buoy sent a team kit that doesn't match your team's keys. Tap Refresh to try again.");l=await(0,_sharedUi.kitKey)(t,a,"team");const s=new Map;const f={owner:c.owner,versions:s,cases:new Map};try{for(const d of c.versions){const n=i.versions.find(h=>h.teamKeyId===d.teamKeyId&&h.keyVersion===d.keyVersion);const g=n?(0,_sharedUi.base64UrlDecode)(n.pub):null;if(!g)continue;const u=await(0,_sharedUi.openSecret)(t,l,(0,_sharedUi.versionLockAad)(c.owner,d.teamKeyId,d.keyVersion),d.locked);try{s.set(versionKey(d.teamKeyId,d.keyVersion),(0,_sharedUi.pairFrom)(u,g))}finally{u.fill(0)}}}catch(d){wipe(f);throw d}return f}finally{a.fill(0);l?.fill(0)}}async function openCards(e,r,t,o){const i=(0,_phoneCrypto.teamKeyCrypto)();let c=false;let a=null;for(let l=0;l<MAX_PAGES;l++){const m=await(0,_client.teamJson)(e,`/api/bugs/cards${a?`?before=${encodeURIComponent(a)}`:""}`,r);if(!o())return{unknownVersion:c};for(const s of m.cards){if(!o())return{unknownVersion:c};const f=t.cases.get(s.id);if(f&&f.cardRevision>=s.cardRevision&&(f.card||!s.card))continue;const d=()=>{if(o()&&f&&t.cases.get(s.id)===f&&f.cardRevision<s.cardRevision)t.cases.delete(s.id)};if(!s.wrap||!s.caseTag||(s.owner??t.owner)!==t.owner){d();continue}try{const n=(0,_sharedUi.parseWrap)(s.wrap);const g=t.versions.get(versionKey(n.teamKeyId,n.keyVersion));if(!g){c=true;d();continue}const u=await(0,_sharedUi.unwrapCaseKey)(i,n,t.owner,g,s.caseTag);let h=null;if(s.card)h=await(0,_sharedUi.openCard)(i,u,s.caseTag,s.cardRevision,s.card).catch(()=>null);if(!o())return{unknownVersion:c};const k=t.cases.get(s.id);if(k&&k.cardRevision>s.cardRevision)continue;t.cases.set(s.id,{key:u,card:h,cardRevision:s.cardRevision})}catch{d()}}a=m.next;if(!a)break}return{unknownVersion:c}}function bugOf(e,r,t){const o=r.cases.get(e.id);const i=o?.card??null;const c=l=>l===t?"You":people.get(l)??l;const a=i?.note?.split("\n")[0]?.trim()||i?.error||(i?.route?`Bug on ${i.route}`:o?`Bug from ${new Date(e.createdAt).toLocaleString()}`:"Locked bug");return{id:e.id,title:a,priority:e.priority&&_client.BUG_PRIORITIES.includes(e.priority)?e.priority:"normal",status:e.status,assignee:e.assignee,assigneeName:e.assignee?c(e.assignee):null,reporter:e.mine?"You":e.reporter??c(e.senderId),createdAt:e.createdAt,build:i?.version?`${i.version}${i.build?` (${i.build})`:""}`:null,error:i?.error??null,repeatOf:e.duplicateOf??null,canOpen:!!i?.blobSha256,rowRevision:e.rowRevision}}function blockOf(e){if(e instanceof _client.BugLinksHttpError){if(e.code==="signed_out")return{code:"signed_out",message:"Sign in to Buoy to see your team's bugs."};if(e.code==="needs_pro")return{code:"needs_pro",message:"The team list needs Buoy Pro or Business."};if(e.code==="offline")return{code:"offline",message:"Couldn't reach the bug links server. Tap Refresh to try again."};if(e.status===403)return{code:"no_access",message:"Your Buoy sign-in can send bugs but not read them. Ask your team admin, or sign in as yourself."};if(e.detail.error==="own_kits")return{code:"own_kits",message:"Your team keeps its own key. Open the list on buoy.gg or in Buoy Desktop."};if(e.status===404&&e.detail.error==="no_custody")return{code:"no_custody",message:"This server doesn't keep team keys. Open the list on the web or in Buoy Desktop."}}return{code:"error",message:e instanceof Error?e.message:String(e)}}async function afterRefused(e,r,t,o){if(!(e instanceof _client.BugLinksHttpError)||r!==session||whoNow()!==t||!o||opened!==o)return;if(e.status===404)return loadTeamBugs();if(!(e.status===401||e.status===402||e.status===403))return;wipe(o);opened=null;openedWho=null;reads++;state={kind:"blocked",...blockOf(e)};stateWho=t;notify()}const RANK=e=>_client.BUG_PRIORITIES.indexOf(e.priority);const isOpen=e=>e.status==="new"||e.status==="seen";function teamView(e,r,t){const o=e.filter(i=>r==="all"?true:isOpen(i)&&!i.repeatOf&&(r==="open"||i.assignee===t));return o.sort((i,c)=>(r==="all"?0:RANK(i)-RANK(c))||c.createdAt.localeCompare(i.createdAt))}async function changeTeamBug(e,r){const t=teamBugs();const o=t.kind==="ready"?t.bugs.find(u=>u.id===e):void 0;const i=(0,_credential.currentCredential)();if(!o||t.kind!=="ready"||!i)throw new Error("The list changed. Tap Refresh.");const c=session;const a=whoNow();const l=stateOwner;const m=opened;const s=()=>c===session&&whoNow()===a&&stateOwner===l;let f;try{f=await(0,_client.teamJson)((0,_settings.bugLinksServer)(),`/api/bugs/${encodeURIComponent(e)}`,i,{method:"PATCH",body:{...r,rowRevision:o.rowRevision}})}catch(u){if(u instanceof _client.BugLinksHttpError&&u.code==="conflict"){if(s())await loadTeamBugs();throw new Error("Someone changed this bug just now. This is the latest.")}await afterRefused(u,c,a,m);throw u}const d=state;if(!s()||d.kind!=="ready")return;const n=f.rowRevision??o.rowRevision+1;const g=d.me;state={...d,bugs:d.bugs.map(u=>{if(u.id!==e||u.rowRevision>=n)return u;const h="assignee"in r?r.assignee?r.assignee===g?"You":people.get(r.assignee)??r.assignee:null:u.assigneeName;return{...u,...r,assigneeName:h,rowRevision:n}})};notify()}async function loadTeamBug(e,r={}){if(!canLoadTeamBugs())throw new Error("Loading needs a dev build. Open this bug in your dev build to load it.");const t=(0,_credential.currentCredential)();const o=whoNow();const i=session;const c=opened;const a=t&&o&&o===stateWho&&o===openedWho?c?.cases.get(e):void 0;if(!t||!a)throw new Error("This phone can't open that bug. Tap Refresh to read the list again.");const l=()=>i===session&&whoNow()===o&&opened===c;const m=()=>new Error("The Bug Links screen closed, so Buoy stopped loading this bug.");const s=a.card;if(!s?.blobSha256)throw new Error("This bug's note doesn't check out, so Buoy won't load it.");let f;try{f=await(0,_client.fetchBlob)((0,_settings.bugLinksServer)(),e,t)}catch(u){await afterRefused(u,i,o,c);throw u}if(!l())throw m();const d=await(0,_sharedUi.blobSha256)((0,_phoneCrypto.teamKeyCrypto)(),f);if(d!==s.blobSha256)throw new Error("This bug's file doesn't match its note, so Buoy won't load it.");const{kase:n}=await(0,_sharedUi.unlockBugCase)(f,a.key,{crypto:(0,_phoneCrypto.caseCrypto)()});if(!l())throw m();const g=await(0,_loader.loadBugCase)(n,{...r,source:{id:e},stillWanted:l});if(!g.loaded&&!l())throw m();return g}function resetTeamBugsForTests(){forgetTeamBugs();listeners.clear()}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"BUG_LINKS_COLOR",{enumerable:true,get:function(){return _preset.BUG_LINKS_COLOR}});Object.defineProperty(exports,"BUG_PRIORITIES",{enumerable:true,get:function(){return _client.BUG_PRIORITIES}});Object.defineProperty(exports,"BugLinksBody",{enumerable:true,get:function(){return _BugLinksBody.BugLinksBody}});Object.defineProperty(exports,"BugLinksOverlay",{enumerable:true,get:function(){return _overlays.BugLinksOverlay}});Object.defineProperty(exports,"BuoyMask",{enumerable:true,get:function(){return _BuoyMask.BuoyMask}});Object.defineProperty(exports,"BuoyUnmask",{enumerable:true,get:function(){return _BuoyMask.BuoyUnmask}});Object.defineProperty(exports,"DEFAULT_SERVER",{enumerable:true,get:function(){return _settings.DEFAULT_SERVER}});Object.defineProperty(exports,"addNote",{enumerable:true,get:function(){return _reporter.addNote}});Object.defineProperty(exports,"bugLinksServer",{enumerable:true,get:function(){return _settings.bugLinksServer}});Object.defineProperty(exports,"bugLinksSyncAdapter",{enumerable:true,get:function(){return _bugLinksSyncAdapter.bugLinksSyncAdapter}});Object.defineProperty(exports,"bugLinksToolPreset",{enumerable:true,get:function(){return _preset.bugLinksToolPreset}});Object.defineProperty(exports,"buildSandbox",{enumerable:true,get:function(){return _sandbox.buildSandbox}});Object.defineProperty(exports,"caseCrypto",{enumerable:true,get:function(){return _phoneCrypto.caseCrypto}});Object.defineProperty(exports,"configureBugLinks",{enumerable:true,get:function(){return _settings.configureBugLinks}});Object.defineProperty(exports,"configureMedia",{enumerable:true,get:function(){return _frames.configureMedia}});Object.defineProperty(exports,"createBugLinksTool",{enumerable:true,get:function(){return _preset.createBugLinksTool}});Object.defineProperty(exports,"deleteLink",{enumerable:true,get:function(){return _reporter.deleteLink}});Object.defineProperty(exports,"fetchBugLink",{enumerable:true,get:function(){return _opener.fetchBugLink}});Object.defineProperty(exports,"getBugLinksSnapshot",{enumerable:true,get:function(){return _bugLinksSyncAdapter.getBugLinksSnapshot}});Object.defineProperty(exports,"leaveSandbox",{enumerable:true,get:function(){return _loader.leaveSandbox}});Object.defineProperty(exports,"loadAgain",{enumerable:true,get:function(){return _loader.loadAgain}});Object.defineProperty(exports,"loadBugCase",{enumerable:true,get:function(){return _loader.loadBugCase}});Object.defineProperty(exports,"loadedCase",{enumerable:true,get:function(){return _loader.loadedCase}});Object.defineProperty(exports,"markBugHere",{enumerable:true,get:function(){return _frames.markBugHere}});Object.defineProperty(exports,"mediaUnavailableReason",{enumerable:true,get:function(){return _frames.mediaUnavailableReason}});Object.defineProperty(exports,"myLinks",{enumerable:true,get:function(){return _linksStore.myLinks}});Object.defineProperty(exports,"openBugLink",{enumerable:true,get:function(){return _opener.openBugLink}});Object.defineProperty(exports,"phoneCaseCrypto",{enumerable:true,get:function(){return _phoneCrypto.phoneCaseCrypto}});Object.defineProperty(exports,"probeFrame",{enumerable:true,get:function(){return _frames.probeFrame}});Object.defineProperty(exports,"randomSource",{enumerable:true,get:function(){return _randomBytes.randomSource}});Object.defineProperty(exports,"reportBug",{enumerable:true,get:function(){return _reporter.reportBug}});Object.defineProperty(exports,"reportBugToolPreset",{enumerable:true,get:function(){return _preset.reportBugToolPreset}});Object.defineProperty(exports,"reportState",{enumerable:true,get:function(){return _reporter.reportState}});Object.defineProperty(exports,"retryWaiting",{enumerable:true,get:function(){return _reporter.retryWaiting}});Object.defineProperty(exports,"setBugLinksCredentialProvider",{enumerable:true,get:function(){return _credential.setBugLinksCredentialProvider}});Object.defineProperty(exports,"setBugLinksServer",{enumerable:true,get:function(){return _settings.setBugLinksServer}});Object.defineProperty(exports,"setLinkPriority",{enumerable:true,get:function(){return _reporter.setLinkPriority}});Object.defineProperty(exports,"startRecording",{enumerable:true,get:function(){return _frames.startRecording}});Object.defineProperty(exports,"stopRecording",{enumerable:true,get:function(){return _frames.stopRecording}});Object.defineProperty(exports,"subscribeLoadedCase",{enumerable:true,get:function(){return _loader.subscribeLoadedCase}});Object.defineProperty(exports,"subscribeMyLinks",{enumerable:true,get:function(){return _linksStore.subscribeMyLinks}});Object.defineProperty(exports,"subscribeReportState",{enumerable:true,get:function(){return _reporter.subscribeReportState}});Object.defineProperty(exports,"undoBugCase",{enumerable:true,get:function(){return _loader.undoBugCase}});var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _overlays=require("./components/overlays");var _loader=require("./engine/loader");var _linksStore=require("./engine/linksStore");var _otherTabs=require("./engine/otherTabs");var _reporter=require("./engine/reporter");var _settings=require("./engine/settings");var _preset=require("./preset");var _bugLinksSyncAdapter=require("./sync/bugLinksSyncAdapter");var _BugLinksBody=require("./components/BugLinksBody");var _BuoyMask=require("./media/BuoyMask");var _frames=require("./media/frames");var _client=require("./engine/client");var _opener=require("./engine/opener");var _sandbox=require("./engine/sandbox");var _phoneCrypto=require("./engine/phoneCrypto");var _randomBytes=require("./engine/randomBytes");var _credential=require("./engine/credential");const isReactNativeRuntime=typeof navigator!=="undefined"&&navigator.product==="ReactNative";const isBrowser=typeof window!=="undefined"&&typeof document!=="undefined";function bootRecorder(){let e=0;const r=()=>{e++;const t=e<20?100:1500;(0,_sharedUi.startBugCaseRecorder)().then(n=>{const o=n.watching.length>=3&&n.touches;if(!o&&e<60)setTimeout(r,t)}).catch(()=>{if(e<60)setTimeout(r,t)})};r()}function caseIdentity(e){if(!e)return"";try{const r=JSON.parse(e);const t=r.block??(r.options?.replay!==false&&!!r.report?.replay?.active);return[r.undo?.undoId,r.createdAt,r.source?.id,r.phase,t].join("|")}catch{return e}}const BOOTED="__buoyBugLinksBooted";const booted=globalThis[BOOTED]===true;if((isReactNativeRuntime||isBrowser)&&!booted){globalThis[BOOTED]=true;(0,_otherTabs.listenForOtherTabs)();bootRecorder();(0,_overlays.watchBugLinksOverlays)();void(0,_settings.loadBugLinksSettings)();void(0,_linksStore.loadMyLinks)().then(()=>(0,_reporter.retryWaiting)());void(0,_loader.restoreLoadedAfterReload)();_reactNative.AppState?.addEventListener?.("change",e=>{if(e==="active")void(0,_reporter.retryWaiting)()});if(!isReactNativeRuntime&&isBrowser){document.addEventListener("visibilitychange",()=>{if(document.visibilityState==="visible")void(0,_reporter.retryWaiting)()});window.addEventListener("pageshow",e=>{if(e.persisted)window.location.reload()});window.addEventListener("storage",e=>{if(e.key===null){if((0,_loader.loadedCase)())window.location.reload()}else if(e.key===_sharedUi.devToolsStorageKeys.bugLinks.loaded()&&caseIdentity(e.oldValue)!==caseIdentity(e.newValue)){window.location.reload()}else if(e.key===_sharedUi.devToolsStorageKeys.bugLinks.links()){void(0,_linksStore.reloadMyLinks)()}})}}(0,_frames.ensureMediaStarted)();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.BuoyMask=BuoyMask;exports.BuoyUnmask=BuoyUnmask;var _reactNative=require("react-native");var _jsxRuntime=require("react/jsx-runtime");function BuoyMask({children:e,style:t,testID:s}){return(0,_jsxRuntime.jsx)(_reactNative.View,{nativeID:"buoy-mask",collapsable:false,style:t,testID:s,children:e})}function BuoyUnmask({children:e,style:t,testID:s}){return(0,_jsxRuntime.jsx)(_reactNative.View,{nativeID:"buoy-unmask",collapsable:false,style:t,testID:s,children:e})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.addMediaToCase=addMediaToCase;exports.configureMedia=configureMedia;exports.ensureMediaStarted=ensureMediaStarted;exports.markBugHere=markBugHere;exports.mediaConfig=mediaConfig;exports.mediaUnavailableReason=mediaUnavailableReason;exports.probeFrame=probeFrame;exports.resumeMedia=resumeMedia;exports.shrinkMedia=shrinkMedia;exports.startRecording=startRecording;exports.stopRecording=stopRecording;var _reactNative=require("react-native");var _nativeFrames=require("./nativeFrames");var _sharedUi=require("@buoy-gg/shared-ui");let loadError=null;const native=(()=>{try{return(0,_nativeFrames.loadFramesModule)()}catch(t){loadError=t instanceof Error?t.message:String(t);return void 0}})();let config={};let started=false;function mediaUnavailableReason(){if(loadError)return"Pictures failed to load on this build.";if(!native){if(_reactNative.Platform.OS==="web")return"No pictures here: this page has no window to draw.";if(_reactNative.Platform.OS!=="ios"&&_reactNative.Platform.OS!=="android")return"No pictures on this platform yet.";return"Pictures need a dev build."}return null}function configureMedia(t={}){const s=config;config={...config,...t};const n=["maskAllText","maskAllImages","maskAllInputs"].some(a=>s[a]===false&&config[a]!==false);started=true;if(!native)return;const i=config.pictures!==false||config.video!=="off";try{native.configure({pictures:config.pictures!==false,video:config.video??"manual",maskAllText:config.maskAllText!==false,maskAllImages:config.maskAllImages!==false,maskAllInputs:config.maskAllInputs!==false,mode:config.mode==="customer"?"customer":"internal"});if(config.video==="off"||config.mode==="customer")native.stopVideo();if(!i||n||s.mode!=="customer"&&config.mode==="customer")native.clear()}catch{}}function mediaConfig(){return config}function ensureMediaStarted(){if(!started)configureMedia()}function startRecording(){if(config.video!=="off"&&config.mode!=="customer")native?.startVideo(false)}function stopRecording(){native?.stopVideo()}function markBugHere(){native?.mark()}function resumeMedia(){try{native?.resume()}catch{}}function probeFrame(){return native?native.probe():Promise.resolve({ok:false,reason:mediaUnavailableReason()})}async function addMediaToCase(t){const s=mediaUnavailableReason();if(s)return withMissing(t,s);if(config.pictures===false&&config.video==="off")return t;let n;let i=null;try{i=await withDeadline(native.captureLast(),CAPTURE_DEADLINE_MS).catch(e=>({ok:false,reason:e instanceof Error?e.message:String(e)}));n=await withDeadline(native.takeMedia(),CAPTURE_DEADLINE_MS)}catch(e){return withMissing(t,`Pictures failed: ${e instanceof Error?e.message:String(e)}`)}const a=t.clock?.active?t.clock.offsetMs??0:0;const c=t.touches.filter(e=>e.kind==="press"||e.kind==="longPress"||e.kind==="toggle"||e.kind==="slider");const m=n.frames.map(e=>({t:e.t+a,wall:e.t,kind:e.kind,w:e.w,h:e.h,jpeg:e.jpeg,boxes:e.boxes}));const g=new Set;const w=e=>{const r=[];for(const o of m){if(o.kind!=="tap"||o.tap!==void 0)continue;for(const d of c){if(d.seq===void 0||g.has(d.seq))continue;const h=e(o,d);if(h===void 0)continue;if(h>=0&&h<3e3)r.push({f:o,seq:d.seq,gap:h})}}r.sort((o,d)=>o.gap-d.gap||d.seq-o.seq||d.f.wall-o.f.wall);for(const o of r){if(o.f.tap!==void 0||g.has(o.seq))continue;o.f.tap=o.seq;g.add(o.seq)}};w((e,r)=>{if(typeof r.wall!=="number")return void 0;const o=e.wall-r.wall;if(o>=2e3||o<-1e3)return void 0;return o>=0?o:2e3-o});const y=e=>e<2e3?e:void 0;w((e,r)=>typeof r.wall==="number"?void 0:y(e.t-r.t));if(a)w((e,r)=>typeof r.wall==="number"?void 0:y(e.wall-r.t));const v=new Set(m.map(e=>e.tap).filter(e=>e!==void 0));const f=m.filter(e=>e.kind==="tap"&&e.tap===void 0).sort((e,r)=>(e.wall??e.t)-(r.wall??r.t));const p=c.filter(e=>e.seq!==void 0&&!v.has(e.seq)&&typeof e.wall!=="number").sort((e,r)=>e.seq-r.seq);if(f.length&&f.length===p.length){const e=!!t.clock?.active||f.every((r,o)=>Math.abs(r.t-p[o].t)>6e4);if(e)f.forEach((r,o)=>r.tap=p[o].seq)}else if(f.length&&f.length<p.length&&t.clock?.active){const e=p.slice(p.length-f.length);f.forEach((r,o)=>r.tap=e[o].seq)}const b=m.filter(e=>e.tap!==void 0).sort((e,r)=>e.wall-r.wall);const M=b.map(e=>e.tap).sort((e,r)=>e-r);b.forEach((e,r)=>e.tap=M[r]);const u=(0,_sharedUi.validateBugCaseMedia)({frames:m,marks:n.marks.map(e=>e+a),settings:n.settings,dropped:n.dropped,droppedWhy:droppedWhy(n)});let l=u&&u.frames.length?{...t,media:u}:t;if(!u||!u.frames.length){const e=Object.entries(u?.droppedWhy??droppedWhy(n));const r=e.length?e.map(([o,d])=>d>1?`${o} (${d} times)`:o).join("; "):"no frame was taken";l=withMissing(l,`No pictures: ${r}`)}else if(!u.frames.some(e=>e.kind==="last")){l=withMissing(l,"No picture from right before the report. The newest picture is from earlier.")}else if(i&&!i.ok&&typeof n.lastAgeMs==="number"&&n.lastAgeMs>STALE_LAST_MS){const e=i.reason?` (${i.reason})`:"";l=withMissing(l,`No new picture right before the report${e}. The main picture is ${Math.round(n.lastAgeMs/1e3)} s old.`)}if(n.stopped)l=withMissing(l,n.stopped);return l}function droppedWhy(t){let s={};let n=0;for(const[a,c]of Object.entries(t.reasons??{})){if(typeof c!=="number"||!(c>0))continue;s=(0,_sharedUi.addDroppedWhy)(s,NATIVE_REASONS[a]??a,c);n+=c}const i=typeof t.dropped==="number"&&Number.isFinite(t.dropped)?t.dropped:0;return(0,_sharedUi.addDroppedWhy)(s,_sharedUi.DROPPED_TO_FIT,i-n)}const NATIVE_REASONS={busy:"another picture was still being taken"};function shrinkMedia(t,s){if(!t.media)return t;const n=(0,_sharedUi.fitBugCaseMedia)(t.media.frames,t.media.marks,{frames:150,bytes:s});if(!n.dropped)return t;const i={...t.media,frames:n.frames,dropped:t.media.dropped+n.dropped,droppedWhy:(0,_sharedUi.addDroppedWhy)(t.media.droppedWhy??{},_sharedUi.DROPPED_TO_FIT,n.dropped)};const a={...t,media:i};if(i.frames.length)return withMissing(a,`Some pictures were left out to fit the case (${n.dropped}).`);delete a.media;return withMissing(a,`No pictures fit in the case. ${i.dropped} were left out: ${(0,_sharedUi.describeDroppedWhy)(i.droppedWhy)}.`)}const CAPTURE_DEADLINE_MS=8e3;function withDeadline(t,s){let n;return Promise.race([t,new Promise((i,a)=>{n=setTimeout(()=>a(new Error("the phone took too long")),s)})]).finally(()=>clearTimeout(n))}const STALE_LAST_MS=1e4;function withMissing(t,s){return{...t,missing:[...t.missing,{part:"media",reason:s}]}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.loadFramesModule=loadFramesModule;var _reactNative=require("react-native");function loadFramesModule(){if(_reactNative.Platform.OS!=="ios"&&_reactNative.Platform.OS!=="android")return void 0;return _reactNative.NativeModules.BuoyFrames}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.loadFramesModule=loadFramesModule;var _webFrames=require("./webFrames");let engine;function loadFramesModule(){if(typeof document==="undefined"||typeof window==="undefined")return void 0;if(!engine){engine=new _webFrames.WebFrameEngine;const r=globalThis;if(r.__BUOY_E2E__===true){const e=engine;r.__BUOY_FRAMES_TEST__={probe:()=>e.probe(),configure:n=>e.configure(n),takeMedia:()=>e.takeMedia(),resume:()=>e.resume()}}}return engine}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.WebFrameEngine=void 0;exports.buoyScreenOpen=buoyScreenOpen;exports.snapshotPage=snapshotPage;const BOX="#1F1F1F";const GROW=2;const QUALITY=.6;const MAX_LONG_SIDE=1280;const TAPS_KEPT=10;const VIDEO_KEEP_MS=3e4;const VIDEO_MAX_MS=6e4;const VIDEO_MAX_BYTES=6*1024*1024;const SLOW_MS=250;const EASE_MS=100;const BLANK="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";const BUOY_ROOTS="[data-buoy-root],[data-buoy-mount],[data-buoy-portal-host],[id='floating-devtools-root'],[id='dial-devtools-root'],[id='buoy-tool-host']";const BUOY_OPEN="[id='dial-devtools-root'],[id='buoy-tool-host'],[role='dialog'][aria-label='Dial Menu']";const MASK="[id='buoy-mask'],[data-buoy-mask],.buoy-mask,.sentry-mask,.sentry-block,.sentry-react-native-mask,[data-sentry-mask]";const UNMASK="[id='buoy-unmask'],[data-buoy-unmask],.buoy-unmask,.sentry-unmask,.sentry-react-native-unmask";const WRAPPED_MASK=new Set(["sentry-react-native-mask","sentry-react-native-unmask"]);const NOT_TEXT_INPUTS=new Set(["checkbox","radio","range","color","file","hidden"]);const BUTTON_INPUTS=new Set(["button","submit","reset"]);const SECRET_AUTOCOMPLETE=/(^|\s)(current-password|new-password|one-time-code|cc-[a-z-]+)(\s|$)/i;const IMAGE_TAGS=new Set(["IMG","PICTURE","VIDEO","CANVAS","svg","SVG","OBJECT","EMBED"]);const SKIP_TEXT_IN=new Set(["SCRIPT","STYLE","NOSCRIPT","TEMPLATE","TEXTAREA","SELECT"]);const SVG_SILENT=new Set(["TITLE","DESC","STYLE","SCRIPT","METADATA"]);const upper=e=>(e.localName||e.tagName).toUpperCase();const SVG_NS="http://www.w3.org/2000/svg";const TEXT_NODES=4|8;const wallNow=()=>typeof performance!=="undefined"&&performance.timeOrigin?performance.timeOrigin+performance.now():Date.now();class FrameError extends Error{}const blank=e=>e.replace(/\S/g,"n");const BOX_STYLE=[["background",BOX],["background-image","none"],["color","transparent"],["-webkit-text-fill-color","transparent"],["border-color",BOX],["box-shadow","none"],["text-shadow","none"],["outline","none"],["filter","none"],["opacity","1"]];function emptyInside(e){const t=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|TEXT_NODES);for(let s=t.nextNode();s;s=t.nextNode()){if(s.nodeType===Node.TEXT_NODE||s.nodeType===Node.CDATA_SECTION_NODE){s.data=blank(s.data);continue}const n=s;n.style?.setProperty("visibility","hidden","important");scrubAttributes(n)}}function scrubAttributes(e){for(const s of["srcset","poster","placeholder","title","href","xlink:href"])e.removeAttribute(s);const t=upper(e);if(t==="IMG"||t==="IMAGE"){e.setAttribute("src",BLANK);e.setAttribute("alt","")}else if(t==="INPUT"){e.setAttribute("value","");e.removeAttribute("src")}else if(t==="TEXTAREA")e.textContent="";else if(t==="OPTION")e.textContent=""}function clip(e,t,s){if(!e||!(e.width>0)||!(e.height>0))return null;const n=Math.max(0,Math.floor(e.left-GROW));const i=Math.max(0,Math.floor(e.top-GROW));const l=Math.min(t,Math.ceil(e.right+GROW));const d=Math.min(s,Math.ceil(e.bottom+GROW));return l>n&&d>i?{x:n,y:i,width:l-n,height:d-i}:null}function probeID(e,t){for(let s=e;s;s=s.parentElement){const n=s.getAttribute("data-testid");if(n){if(s===e)return n;const i=s.getBoundingClientRect();return t.width*t.height>=i.width*i.height*.8?n:""}}return""}function kindOf(e){const t=upper(e);if(t==="INPUT"){const n=e;const i=(n.type||"text").toLowerCase();if(i==="password"||SECRET_AUTOCOMPLETE.test(n.autocomplete??""))return"secure";if(i==="image")return"image";if(BUTTON_INPUTS.has(i))return"text";return NOT_TEXT_INPUTS.has(i)?"none":"input"}if(t==="TEXTAREA")return SECRET_AUTOCOMPLETE.test(e.autocomplete??"")?"secure":"input";if(t==="SELECT")return SECRET_AUTOCOMPLETE.test(e.autocomplete??"")?"secure":"input";if(e.isContentEditable&&!e.parentElement?.isContentEditable)return"input";if(t==="IFRAME"||t==="FRAME")return"web";if(IMAGE_TAGS.has(t))return"image";if(e.getAttribute("role")==="img")return"image";const s=e.style?.backgroundImage;if(s&&s.includes("url("))return"image";return"none"}function markerBox(e){const t=Array.from(e.classList).find(n=>WRAPPED_MASK.has(n));const s=e.parentElement;return t&&s&&s.childElementCount===1?s:e}function unescapeCss(e){return e.replace(/\\\r?\n/g,"").replace(/\\([0-9a-f]{1,6})\s?/gi,(t,s)=>{const n=parseInt(s,16);return n>0&&n<=1114111?String.fromCodePoint(n):"\uFFFD"}).replace(/\\(.)/g,"$1")}const isDataPicture=e=>/^\s*data:/i.test(e);const FONT_FACE=/@font-face\s*\{[^}]*\}/gi;function holdsDataPicture(e){if(!/data|\\/i.test(e))return false;const t=unescapeCss(e);return(t.match(/data:[^,]*/gi)??[]).some(s=>isDataPicture(s))}function styleWithoutDataPictures(e){let t="";for(let s=0;s<e.length;s++){const n=e[s];const i=e.getPropertyValue(n);if(holdsDataPicture(i))continue;const l=e.getPropertyPriority(n);t+=`${n}: ${i}${l?" !important":""}; `}return t}function ruleWithoutDataPictures(e){const t=e.cssText;if(!holdsDataPicture(t))return t;const s=e;if(e.type===5||/^\s*@font-face\b/i.test(t))return t;const n=i=>{let l="";for(let d=0;i&&d<i.length;d++)l+=ruleWithoutDataPictures(i[d])+"\n";return l};if(s.style&&s.selectorText!==void 0)return`${s.selectorText} { ${styleWithoutDataPictures(s.style)}${n(s.cssRules)}}`;if(s.cssRules&&t.includes("{"))return`${t.slice(0,t.indexOf("{"))}{
|
|
2
|
+
${n(s.cssRules)}}`;return""}function stripDataImages(e){const t=[];const s=e.replace(FONT_FACE,n=>`/*__buoy_font_${t.push(n)-1}__*/`);return stripAll(s).replace(/\/\*__buoy_font_(\d+)__\*\//g,(n,i)=>t[Number(i)]??"")}function stripAll(e){return e.replace(/(["'])((?:(?!\1)[^\\]|\\[\s\S])*)\1/g,(t,s,n)=>isDataPicture(unescapeCss(n))?'""':t).replace(/url\(\s*((?:[^"'\s)\\]|\\[\s\S])(?:[^)\\]|\\[\s\S])*)\)/gi,(t,s)=>isDataPicture(unescapeCss(s.trim()))?"none":t)}function pageCss(e){const t=[...Array.from(document.styleSheets),...document.adoptedStyleSheets??[]];let s="";for(const n of t){let i=null;try{i=n.disabled?null:n.cssRules}catch{i=null}if(!i)continue;let l="";for(let g=0;g<i.length;g++)l+=(e?ruleWithoutDataPictures(i[g]):i[g].cssText)+"\n";const d=n.media?.mediaText;s+=d&&d!=="all"?`@media ${d} {
|
|
3
|
+
${l}}
|
|
4
|
+
`:l}if(e)s=stripDataImages(s);return s}const STRONG=":not(#_bq1#_bq2#_bq3#_bq4#_bq5#_bq6)";const MASK_LAYER="__buoy_mask";const PSEUDO=e=>`${e}::before, ${e}::after, ${e}::marker`;const MASK_CSS=e=>`
|
|
5
|
+
head${STRONG}, head${STRONG} * { display: none !important; }
|
|
6
|
+
.__buoy_box${STRONG} { background: ${BOX} !important; background-image: none !important; color: transparent !important; -webkit-text-fill-color: transparent !important; border-color: ${BOX} !important; box-shadow: none !important; text-shadow: none !important; outline: none !important; caret-color: transparent !important; filter: none !important; opacity: 1 !important; mask: none !important; -webkit-mask: none !important; }
|
|
7
|
+
.__buoy_box${STRONG} *${STRONG} { visibility: hidden !important; }
|
|
8
|
+
${PSEUDO(`.__buoy_box${STRONG}`)}, ${PSEUDO(`.__buoy_box${STRONG} *${STRONG}`)} { content: none !important; visibility: hidden !important; }
|
|
9
|
+
.__buoy_box${STRONG}::placeholder { color: transparent !important; }
|
|
10
|
+
.__buoy_text${STRONG} { background: ${BOX} !important; color: transparent !important; -webkit-text-fill-color: transparent !important; text-shadow: none !important; border-radius: 0 !important; }
|
|
11
|
+
${e.text?`${PSEUDO(`*${STRONG}`)} { content: none !important; color: transparent !important; -webkit-text-fill-color: transparent !important; text-shadow: none !important; }`:""}
|
|
12
|
+
`;function drawn(e){const t=e.checkVisibility;if(typeof t==="function")return t.call(e,{visibilityProperty:true});try{return getComputedStyle(e).visibility!=="hidden"}catch{return true}}const imageCache=new Map;function buoyScreenOpen(e=window.innerWidth,t=window.innerHeight){const s=n=>n.getClientRects().length>0&&!!clip(n.getBoundingClientRect(),e,t);if(Array.from(document.querySelectorAll(BUOY_OPEN)).some(s))return true;for(const n of Array.from(document.querySelectorAll("[data-buoy-portal-host]"))){for(const i of Array.from(n.querySelectorAll("*"))){const l=i.getBoundingClientRect();if(l.width*l.height>=e*t*.2&&s(i)&&getComputedStyle(i).pointerEvents!=="none")return true}}return false}function imageData(e){const t=e.currentSrc||e.src;if(!t)return null;if(t.startsWith("data:image/"))return t;const s=imageCache.get(t);if(s!==void 0)return s;if(!e.complete||!e.naturalWidth)return null;let n=null;try{const i=document.createElement("canvas");i.width=Math.min(e.naturalWidth,1024);i.height=Math.round(e.naturalHeight/e.naturalWidth*i.width);i.getContext("2d").drawImage(e,0,0,i.width,i.height);n=i.toDataURL("image/png")}catch{n=null}if(imageCache.size>200)imageCache.clear();imageCache.set(t,n);return n}function snapshotPage(e,t=false){const s=performance.now();const n=document.documentElement.clientWidth||window.innerWidth;const i=window.innerHeight||document.documentElement.clientHeight;if(!(n>0)||!(i>0)||!document.body)throw new FrameError("no page to draw");const l=buoyScreenOpen(n,i);const d=document.documentElement;const g=document.implementation.createHTMLDocument("");const m=g.importNode(d,true);const y=[d,...Array.from(d.querySelectorAll("*"))];const w=[m,...Array.from(m.querySelectorAll("*"))];if(y.length!==w.length)throw new FrameError("the page changed while it was copied");const f=[];const x=document.createTreeWalker(d,TEXT_NODES);const P=document.createTreeWalker(m,TEXT_NODES);for(let a=x.nextNode(),o=P.nextNode();a&&o;a=x.nextNode(),o=P.nextNode()){if(a.data.trim())f.push([a,o])}const I=[];const O=[];const R=[];const A=new Set;const v=new Set;let $=0;const L=(a,o)=>{if(!a)return;I.push(a);if(t)O.push({testID:probeID(o,a),x:a.x,y:a.y,w:a.width,h:a.height})};const C=(a,o)=>{A.add(a);const r=drawn(a)?clip(a.getBoundingClientRect(),n,i):null;if(r)$++;L(r,a);o.classList.add("__buoy_box");for(const[u,h]of BOX_STYLE)o.style?.setProperty(u,h,"important");scrubAttributes(o);emptyInside(o);if(/^(VIDEO|CANVAS|IFRAME|FRAME|OBJECT|EMBED)$/.test(upper(o))){const u=a.getBoundingClientRect();const h=document.createElement("div");h.className=`${o.getAttribute("class")??""} __buoy_box`;h.setAttribute("style",`${o.getAttribute("style")??""};width:${u.width}px;height:${u.height}px;display:${getComputedStyle(a).display==="inline"?"inline-block":getComputedStyle(a).display}`);o.replaceWith(h)}};const k=(a,o)=>{for(let r=a.parentElement;r;r=r.parentElement)if(o.has(r))return true;return false};for(let a=0;a<y.length;a++){const o=y[a];const r=w[a];if(o.matches(BUOY_ROOTS)){v.add(o);R.push(r);continue}if(v.size&&k(o,v))continue;if(A.size&&k(o,A))continue;const u=upper(o);if(u==="SOURCE"){r.removeAttribute("srcset");r.removeAttribute("src");continue}if(u==="SCRIPT"||u==="NOSCRIPT"||u==="LINK"&&/stylesheet|preload|modulepreload/i.test(o.getAttribute("rel")??"")||u==="STYLE"){R.push(r);continue}if(e.images){r.removeAttribute("background");const c=r.getAttribute("style");if(c&&holdsDataPicture(c)){const p=o.style;r.setAttribute("style",p?styleWithoutDataPictures(p):stripDataImages(c))}}if(o.matches(MASK)){const c=markerBox(o);const p=c===o?a:y.lastIndexOf(c,a);if(p>=0&&!A.has(c))C(c,w[p]);if(!A.has(o))A.add(o);continue}if(u==="INPUT"){const c=o;const p=(c.type||"").toLowerCase();if(p==="checkbox"||p==="radio")c.checked?r.setAttribute("checked",""):r.removeAttribute("checked");else if(p==="range"||p==="color")r.setAttribute("value",c.value)}let h=kindOf(o);if(h==="image"&&!IMAGE_TAGS.has(u)&&o.getAttribute("role")!=="img"&&Array.from(o.children).some(c=>upper(c)!=="IMG")){if(e.images&&!o.closest(UNMASK))r.style.setProperty("background-image","none","important");h="none"}if(h==="none"){continue}const T=h!=="secure"&&o.closest(UNMASK)!==null;const b=h==="secure"||h==="input"&&e.inputs&&!T||h==="text"&&e.text&&!T||h==="image"&&e.images&&!T||h==="web"&&!T;if(b){C(o,r);continue}if(u==="INPUT"&&o.type==="image"){r.setAttribute("src",BLANK);r.setAttribute("alt","")}else if(u==="INPUT")r.setAttribute("value",o.value);else if(u==="TEXTAREA")r.textContent=o.value;else if(u==="SELECT"){const c=o;Array.from(r.querySelectorAll("option")).forEach((p,U)=>c.options[U]?.selected?p.setAttribute("selected",""):p.removeAttribute("selected"))}else if(u==="IMG"){const c=imageData(o);r.removeAttribute("srcset");r.setAttribute("alt","");r.setAttribute("src",c??BLANK)}else if(u==="CANVAS"){let c=null;try{c=o.toDataURL("image/png")}catch{c=null}const p=document.createElement("img");p.setAttribute("src",c??BLANK);p.setAttribute("style",`${r.getAttribute("style")??""};width:${o.clientWidth}px;height:${o.clientHeight}px`);p.className=r.getAttribute("class")??"";r.replaceWith(p)}else if(h==="image"&&o.style.backgroundImage.includes("url(")){const c=o.querySelector("img");const p=c?imageData(c):null;r.style.backgroundImage=p?`url("${p}")`:"none"}}const M=new Set;for(const[a,o]of f){const r=a.parentElement;if(!r||SKIP_TEXT_IN.has(upper(r)))continue;if(r.closest("head"))continue;if(r.namespaceURI===SVG_NS&&SVG_SILENT.has(upper(r)))continue;if(upper(r)==="OPTION"&&r.closest("select"))continue;if(v.size&&(v.has(r)||k(r,v)))continue;if(A.has(r)||A.size&&k(r,A))continue;if(!e.text||r.closest(UNMASK))continue;if(r.namespaceURI===SVG_NS){let b=r;while(b.parentElement&&b.parentElement.namespaceURI===SVG_NS)b=b.parentElement;const c=y.indexOf(b);if(c>=0&&!A.has(b))C(b,w[c]);continue}const u=document.createRange();u.selectNodeContents(a);const h=clip(u.getBoundingClientRect?.(),n,i);if(h){I.push(h);if(t&&!M.has(r))O.push({testID:probeID(r,h),x:h.x,y:h.y,w:h.width,h:h.height})}if(h&&!M.has(r)){M.add(r);$++}const T=document.createElement("span");T.className="__buoy_text";for(const[b,c]of BOX_STYLE)T.style.setProperty(b,c,"important");o.data=blank(o.data);o.replaceWith(T);T.appendChild(o)}for(let a=0;a<y.length;a++){const o=y[a];if(o===d||o===document.body)continue;if((o.scrollTop||o.scrollLeft)&&!v.has(o)){for(const r of Array.from(w[a].children)){const u=r.style;if(u)u.setProperty("translate",`${-o.scrollLeft}px ${-o.scrollTop}px`)}}}const S=window.scrollX;const _=window.scrollY;if(S||_){const a=w[y.indexOf(document.body)];if(a)a.style.setProperty("translate",`${-S}px ${-_}px`);const o=document.body.getBoundingClientRect();for(let r=0;r<y.length;r++){const u=y[r];if(!(u instanceof HTMLElement)||!document.body.contains(u)||v.has(u))continue;if(getComputedStyle(u).position==="fixed"){w[r].style.setProperty("translate",`${S-(o.left+S)}px ${_-(o.top+_)}px`)}}}for(const a of R)a.remove();const E=m.querySelector("head")??m.insertBefore(document.createElement("head"),m.firstChild);while(E.firstChild)E.removeChild(E.firstChild);for(const a of Array.from(m.querySelectorAll("head")))if(a!==E)a.remove();E.setAttribute("style","display: none !important");const N=document.createElement("style");N.textContent=`@layer ${MASK_LAYER};
|
|
13
|
+
${pageCss(e.images)}
|
|
14
|
+
@layer ${MASK_LAYER} {${MASK_CSS(e)}}`;E.appendChild(N);m.style.setProperty("overflow","hidden");m.style.setProperty("width",`${n}px`);m.style.setProperty("height",`${i}px`);const D=new XMLSerializer().serializeToString(m);const B=`<svg xmlns="http://www.w3.org/2000/svg" width="${n}" height="${i}"><foreignObject x="0" y="0" width="${n}" height="${i}">${D}</foreignObject></svg>`;return{svg:B,w:n,h:i,rects:I,parts:O,boxes:$,buoyOpen:l,mainMs:performance.now()-s}}const DRAW_DEADLINE_MS=5e3;function rasterize(e){let t;const s=new Promise((n,i)=>{t=setTimeout(()=>i(new FrameError("the picture took too long")),DRAW_DEADLINE_MS)});return Promise.race([draw(e),s]).finally(()=>clearTimeout(t))}async function draw(e){const t=Math.min(1,MAX_LONG_SIDE/Math.max(e.w,e.h));const s=new Image;s.decoding="async";const n=new Promise((f,x)=>{s.onload=()=>f();s.onerror=()=>x(new FrameError("the browser couldn't draw the page"))});s.src=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(e.svg)}`;await n;const i=document.createElement("canvas");i.width=Math.max(1,Math.round(e.w*t));i.height=Math.max(1,Math.round(e.h*t));const l=i.getContext("2d");if(!l)throw new FrameError("no canvas in this browser");const d=performance.now();l.fillStyle="#ffffff";l.fillRect(0,0,i.width,i.height);l.drawImage(s,0,0,i.width,i.height);l.fillStyle=BOX;for(const f of e.rects)l.fillRect(f.x*t,f.y*t,f.width*t,f.height*t);const g=performance.now()-d;let m;try{m=await new Promise(f=>i.toBlob(f,"image/jpeg",QUALITY))}catch{throw new FrameError("this browser can't take pictures of pages")}if(!m)throw new FrameError("couldn't make the picture");const y=new Uint8Array(await m.arrayBuffer());let w="";for(let f=0;f<y.length;f+=32768)w+=String.fromCharCode(...y.subarray(f,f+32768));return{jpeg:btoa(w),mainMs:g}}class WebFrameEngine{enabled=false;pictures=true;customer=false;masks={text:true,images:true,inputs:true};videoFps=2;taps=[];video=[];last=null;marks=[];dropped={};frozen=false;busy=false;run=0;slowInRow=0;stoppedReason=null;lastReason=null;buttonLastT=null;downOnBuoy=false;tapTimer=null;videoTimer=null;videoOn=false;videoAlways=false;videoUntil=0;listening=false;inflight=null;pendingLast=null;easeUntil=0;configure(t={}){this.pictures=t.pictures!==false;this.customer=t.mode==="customer";this.masks={text:t.maskAllText!==false,images:t.maskAllImages!==false,inputs:t.maskAllInputs!==false};if(typeof t.videoFps==="number"&&t.videoFps>0)this.videoFps=Math.min(t.videoFps,10);this.enabled=true;this.listen();if(t.video==="always"&&!this.customer)this.startVideo(true);else if(this.videoAlways||t.video==="off"||this.customer)this.stopVideo()}async status(){return{available:true,enabled:this.enabled,mode:this.customer?"customer":"internal",stopped:this.stoppedReason}}listen(){if(this.listening||typeof document==="undefined")return;this.listening=true;document.addEventListener("pointerdown",this.onDown,{capture:true,passive:true});document.addEventListener("pointerup",this.onUp,{capture:true,passive:true})}onDown=t=>{this.downOnBuoy=false;const s=t.target instanceof Element?t.target:null;if(!s?.closest(BUOY_ROOTS))return;this.downOnBuoy=true;if(!this.enabled||this.frozen)return;if(this.tapTimer){clearTimeout(this.tapTimer);this.tapTimer=null}const n=l=>{if(l)this.buttonLastT=this.last?.t??null};if(!this.busy){void this.take("last").then(n);return}const i=(this.inflight??Promise.resolve(true)).catch(()=>false).then(()=>this.take("last")).then(n).finally(()=>{if(this.pendingLast===i)this.pendingLast=null});this.pendingLast=i};onUp=()=>{if(this.downOnBuoy){this.downOnBuoy=false;return}if(!this.enabled||!this.pictures||this.customer||this.frozen||this.stoppedReason)return;if(this.tapTimer)clearTimeout(this.tapTimer);this.tapTimer=setTimeout(()=>{this.tapTimer=null;void this.take("tap")},300)};startVideo(t){if(!this.enabled||this.customer)return;this.videoOn=true;this.videoAlways=t;if(!t){this.video=[];this.marks=[]}this.videoUntil=t?Infinity:wallNow()+VIDEO_MAX_MS;if(this.videoTimer)clearInterval(this.videoTimer);this.videoTimer=setInterval(()=>{if(wallNow()>this.videoUntil)return this.stopVideo();if(this.videoOn&&!this.frozen&&!this.stoppedReason&&!this.busy)void this.take("video")},1e3/this.videoFps)}stopVideo(){this.videoOn=false;this.videoAlways=false;if(this.videoTimer)clearInterval(this.videoTimer);this.videoTimer=null}mark(){if(this.videoOn)this.marks.push(wallNow())}async captureLast(){for(let n=0;n<3&&(this.pendingLast||this.inflight);n++){await this.pendingLast?.catch(()=>false);await this.inflight?.catch(()=>false)}const t=this.buttonLastT;if(t!==null&&this.last?.t===t&&wallNow()-t<1e4)return{ok:true,reason:null};const s=await this.take("last");return{ok:s,reason:s?null:this.lastReason??"no frame"}}async takeMedia(){this.frozen=true;const t=[...this.last?[this.last]:[],...this.taps,...this.video].sort((s,n)=>s.t-n.t);return{frames:t.map(s=>({...s})),marks:[...this.marks],dropped:Object.values(this.dropped).reduce((s,n)=>s+n,0),reasons:{...this.dropped},stopped:this.stoppedReason,lastAgeMs:this.last?wallNow()-this.last.t:null,settings:{maskAllText:this.masks.text,maskAllImages:this.masks.images,maskAllInputs:this.masks.inputs}}}resume(){this.frozen=false}clear(){this.run++;this.busy=false;if(this.tapTimer)clearTimeout(this.tapTimer);this.tapTimer=null;this.inflight=null;this.pendingLast=null;this.taps=[];this.video=[];this.last=null;this.marks=[];this.dropped={};this.frozen=false;this.buttonLastT=null}stopAll(){this.run++;this.stopVideo();if(this.tapTimer)clearTimeout(this.tapTimer);this.tapTimer=null;if(this.listening){document.removeEventListener("pointerdown",this.onDown,{capture:true});document.removeEventListener("pointerup",this.onUp,{capture:true});this.listening=false}this.busy=false;this.enabled=false}async probe(){let t=null;let s=null;try{t=snapshotPage(this.masks,true)}catch(n){s=n instanceof Error?n.message:String(n)}if(!t)return{ok:false,reason:s,failed:s,parts:[],buoyOpen:false,jpeg:null,w:0,h:0};try{const{jpeg:n}=await rasterize(t);return{ok:true,reason:null,failed:null,parts:t.parts,buoyOpen:t.buoyOpen,jpeg:n,w:t.w,h:t.h}}catch(n){const i=n instanceof Error?n.message:String(n);return{ok:false,reason:i,failed:null,parts:t.parts,buoyOpen:t.buoyOpen,jpeg:null,w:t.w,h:t.h}}}drop(t){this.dropped[t]=(this.dropped[t]??0)+1;this.lastReason=t}take(t){const s=this.busy;const n=this.makeFrame(t);if(!s&&this.busy){const i=n.finally(()=>{if(this.inflight===i)this.inflight=null});this.inflight=i}return n}async makeFrame(t){if(!this.enabled||typeof document==="undefined")return false;if(t!=="last"&&this.frozen)return false;if(t==="tap"&&(this.customer||!this.pictures))return false;if(this.busy){this.drop("busy");return false}if(document.visibilityState==="hidden"){this.drop("app in background");return false}if(t!=="last"&&buoyScreenOpen()){this.drop("a Buoy screen was open");return false}if(t!=="last"&&wallNow()<this.easeUntil){this.drop("skipped to keep the page fast");return false}const s=this.run;this.busy=true;let n;try{n=snapshotPage(this.masks)}catch(g){this.busy=false;this.drop(g instanceof FrameError?g.message:"the page couldn't be checked");return false}if(n.buoyOpen&&t!=="last"){this.busy=false;this.drop("a Buoy screen was open");return false}const i=wallNow();let l;let d=0;try{({jpeg:l,mainMs:d}=await rasterize(n))}catch(g){if(s===this.run){this.busy=false;this.drop(g instanceof FrameError?g.message:"couldn't make the picture")}return false}if(s!==this.run)return false;this.busy=false;this.noteSpeed(n.mainMs+d);if(this.frozen)return false;this.keep({t:i,kind:t,w:n.w,h:n.h,jpeg:l,boxes:n.boxes});return true}noteSpeed(t){if(t>EASE_MS)this.easeUntil=wallNow()+t*4;if(t<=SLOW_MS){this.slowInRow=0;return}if(++this.slowInRow>=3&&!this.stoppedReason){this.stoppedReason="Pictures stopped: each one took too long on this page.";this.stopVideo()}}keep(t){if(t.kind==="last")this.last=t;else if(t.kind==="tap"){this.taps.push(t);if(this.taps.length>TAPS_KEPT)this.taps.shift()}else{this.video.push(t);const s=t.t-(this.videoAlways?VIDEO_KEEP_MS:VIDEO_MAX_MS);let n=this.video.reduce((i,l)=>i+l.jpeg.length,0);while(this.video.length&&(this.video[0].t<s||n>VIDEO_MAX_BYTES*4/3)){n-=this.video.shift().jpeg.length}}}}exports.WebFrameEngine=WebFrameEngine;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.reportBugToolPreset=exports.createBugLinksTool=exports.bugLinksToolPreset=exports.BUG_LINKS_COLOR=void 0;var _sharedUi=require("@buoy-gg/shared-ui");var _BugLinksBody=require("./components/BugLinksBody");var _reporter=require("./engine/reporter");var _jsxRuntime=require("react/jsx-runtime");const BUG_LINKS_COLOR=exports.BUG_LINKS_COLOR="#F43F5E";function Nothing(){return null}const reportBugToolPreset=exports.reportBugToolPreset=(0,_sharedUi.defineToolPreset)({id:"report-bug",name:"REPORT BUG",description:"One tap: save this bug and copy a locked link to it",slot:"both",color:BUG_LINKS_COLOR,icon:({size:e,color:o})=>(0,_jsxRuntime.jsx)(_sharedUi.Bug,{size:e,color:o??BUG_LINKS_COLOR}),component:Nothing,launchMode:"toggle-only",onPress:e=>{e?.closeMenu?.();void(0,_reporter.reportBug)()}});const bugLinksToolPreset=exports.bugLinksToolPreset=(0,_sharedUi.defineToolPreset)({id:"bug-links",name:"BUG LINKS",description:"Report with a note, open a teammate's bug link, and see your links",slot:"both",color:BUG_LINKS_COLOR,icon:({size:e,color:o})=>(0,_jsxRuntime.jsx)(_sharedUi.Lock,{size:e,color:o??BUG_LINKS_COLOR}),component:_BugLinksBody.BugLinksBody,modal:{subtitle:"locked bug reports your team can load"}});const createBugLinksTool=exports.createBugLinksTool=(0,_sharedUi.createToolFactory)(bugLinksToolPreset);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.bugLinksSyncAdapter=void 0;exports.getBugLinksSnapshot=getBugLinksSnapshot;var _sharedUi=require("@buoy-gg/shared-ui");var _linksStore=require("../engine/linksStore");var _loader=require("../engine/loader");var _opener=require("../engine/opener");var _randomBytes=require("../engine/randomBytes");var _reporter=require("../engine/reporter");var _client=require("../engine/client");var _settings=require("../engine/settings");function obj(e){return e&&typeof e==="object"?e:{}}function str(e,r){const t=e[r];return typeof t==="string"&&t.trim()?t.trim():void 0}function linkSummary(e,r=false){return{localId:e.localId,status:e.status,url:r?e.url:e.url?.split("#")[0],note:e.note,createdAt:e.createdAt,expiresAt:e.expiresAt,error:e.error,steps:e.header.steps,errors:e.header.errors,removed:e.removed.length,linkNeeded:e.status==="sent"&&!!e.wrapPending}}function getBugLinksSnapshot(){const e=(0,_loader.loadedCase)();return{server:(0,_settings.bugLinksServer)(),randomSource:(0,_randomBytes.randomSource)(),report:(()=>{const r=(0,_reporter.reportState)();return r?{...r,url:r.url?.split("#")[0]}:null})(),loaded:e?{...e,undo:void 0,source:e.source?.id?{id:e.source.id}:null}:null,links:(0,_linksStore.myLinks)().slice(0,20).map(r=>linkSummary(r,false))}}const bugLinksSyncAdapter=exports.bugLinksSyncAdapter=(0,_sharedUi.createSyncAdapter)({version:1,getSnapshot:getBugLinksSnapshot,subscribe(e){void(0,_linksStore.loadMyLinks)();void(0,_settings.loadBugLinksSettings)();const r=[(0,_linksStore.subscribeMyLinks)(e),(0,_loader.subscribeLoadedCase)(e),(0,_reporter.subscribeReportState)(e),(0,_settings.subscribeBugLinksSettings)(e)];return()=>r.forEach(t=>t())},actions:{getState:async()=>{await Promise.all([(0,_linksStore.loadMyLinks)(),(0,_settings.loadBugLinksSettings)()]);return{...getBugLinksSnapshot(),blocked:await(0,_loader.blockedCalls)()}},report:async e=>{const r=str(obj(e),"priority");const t=_client.BUG_PRIORITIES.find(n=>n===r);const o=await(0,_reporter.reportBug)({note:str(obj(e),"note"),priority:t});if(!o)return{ok:false,error:(0,_reporter.reportState)()?.message??"The report failed."};return{ok:o.status==="sent",...linkSummary(o,true)}},capture:async e=>{const r=await(0,_sharedUi.captureBugCase)({note:str(obj(e),"note"),rules:(0,_settings.bugLinksRules)()});return{ok:true,case:r}},load:async e=>{const r=obj(e);const t={replay:r.replay!==false,clock:r.clock!==false,sequence:r.sequence===true,restart:r.restart===true,otherApp:r.otherApp===true};const o=str(r,"link");if(o){const n=await(0,_opener.openBugLink)(o,t);return{ok:n.loaded,report:n}}if(r.case&&typeof r.case==="object"){const n=await(0,_loader.loadBugCase)(r.case,{...t,source:{id:str(r,"id")}});return{ok:n.loaded,report:n}}throw new Error("load needs `link` (a whole bug link) or `case` (an opened bug case).")},loadAgain:async()=>{const e=await(0,_loader.loadAgain)();return{ok:e.loaded,report:e}},undo:async()=>{const e=await(0,_loader.undoBugCase)();return{ok:!(0,_loader.loadedCase)()?.undoBlocked,steps:e}},end:async()=>(0,_loader.endBugCase)(),leaveSandbox:async()=>(0,_loader.leaveSandbox)(),listLinks:async()=>{await(0,_linksStore.loadMyLinks)();return{links:(0,_linksStore.myLinks)().map(e=>linkSummary(e,true))}},retry:async e=>{const r=str(obj(e),"localId");if(r)await(0,_reporter.sendLink)(r);else await(0,_reporter.retryWaiting)();return{ok:true,links:(0,_linksStore.myLinks)().map(t=>linkSummary(t))}},addNote:async e=>{const r=obj(e);const t=str(r,"localId");const o=typeof r.note==="string"?r.note:void 0;if(!t||o===void 0)throw new Error("addNote needs `localId` and `note`.");const n=await(0,_reporter.addNote)(t,o);return n?{ok:true,...linkSummary(n)}:{ok:false,error:"No link with that localId."}},deleteLink:async e=>{const r=str(obj(e),"localId");if(!r)throw new Error("deleteLink needs `localId`.");await(0,_reporter.deleteLink)(r);return{ok:true}},setServer:async e=>{const r=obj(e);const t=r.server===null?null:str(r,"server");if(t===void 0)throw new Error('setServer needs `server`, like "http://localhost:8898", or null.');return{ok:true,server:await(0,_settings.setBugLinksServer)(t)}}}});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{useEffect as U,useRef as ce,useState as p,useSyncExternalStore as ue}from"react";import{Keyboard as ge,ScrollView as pe,Share as he,StyleSheet as me,Text as d,TextInput as W,TouchableOpacity as ye,View as w}from"react-native";import{NightBadge as fe,NightButton as y,NightCard as v,NightFootnote as f,NightSectionLabel as D,NightSegmented as be,NightSwitch as we,BuoySignInCard as ve,copyToClipboard as Ie,readClipboardText as Se,night as c,useSafeAreaInsets as De}from"@buoy-gg/shared-ui";import{canAddNote as xe,loadMyLinks as Te,myLinks as Ce,subscribeMyLinks as Le}from"../engine/linksStore";import{blockedCalls as ke,endBugCase as Pe,leaveSandbox as $e,loadAgain as Be,loadedCase as q,subscribeLoadedCase as Re,undoBugCase as Ne}from"../engine/loader";import{openBugLink as We}from"../engine/opener";import{NO_RANDOM_MESSAGE as Ee,randomSource as ze}from"../engine/randomBytes";import{currentCredential as E}from"../engine/credential";import{addNote as Ae,deleteLink as J,reportBug as Oe,retryWaiting as Q,sendLink as X,setLinkPriority as _e,wrapLater as Z}from"../engine/reporter";import{BUG_PRIORITIES as Ve}from"../engine/client";import{trustTeamRoot as Ue}from"../engine/teamKeys";import{bugLinksServer as He,DEFAULT_SERVER as je,loadBugLinksSettings as Fe,setBugLinksServer as ee,subscribeBugLinksSettings as Me}from"../engine/settings";import{askSignIn as te,focusLink as oe,focusedLink as Ke,subscribeFocus as ne,wantsSignIn as Ge}from"./focus";import{TeamBugs as Ye}from"./TeamBugs";import{jsx as t,jsxs as l,Fragment as x}from"react/jsx-runtime";const H=(e,r)=>`${e} ${r}${e===1?"":"s"}`;const qe={sending:{label:"Sending",tone:"info"},waiting:{label:"Waiting",tone:"warning"},sent:{label:"Sent",tone:"accent"},failed:{label:"Not sent",tone:"danger"},deleting:{label:"Deleting",tone:"neutral"}};function P(e,r){return ue(e,r,r)}function j(e){const r=new Date(e);return Number.isNaN(r.getTime())?e:r.toLocaleString()}let z;function Je(){if(z===void 0){try{z=require("@buoy-gg/core")}catch{z=null}}return z?.useToolHost?.().close}let se="";export function BugLinksBody(){const e=De();const r=P(Le,Ce);const s=P(Re,q);const u=P(Me,He);const I=P(ne,Ke);const $=P(ne,Ge);const A=!E()||r.some(o=>o.errorCode==="signed_out");const S=I?r.find(o=>o.localId===I):void 0;const[T,B]=p("");const[C,L]=p("normal");const[m,F]=p(()=>se);const[R,ie]=p(false);const[k,M]=p(null);const[i,O]=p(null);const[_,K]=p(null);const[V,G]=p([]);const Y=Je();U(()=>{if(!s?.report.replay?.active)return G([]);let o=true;const a=()=>void ke().then(de=>o&&G(de));a();const b=setInterval(a,3e3);return()=>{o=false;clearInterval(b)}},[s?.loadId,s?.report.replay?.active]);U(()=>{void Te();void Fe();void Q();return()=>oe(null)},[]);const g=async(o,a)=>{ge.dismiss();M(o);O(null);try{const b=await a();if(b)O({text:b,tone:"secondary",from:o})}catch(b){O({text:b instanceof Error?b.message:String(b),tone:"danger",from:o})}finally{M(null)}};const N=ze();return l(pe,{style:n.scroll,contentContainerStyle:[n.content,{paddingBottom:e.bottom+32}],keyboardShouldPersistTaps:"handled",children:[$?t(v,{children:t(ve,{onClose:()=>{te(false);void Q()}})}):A?l(v,{children:[t(d,{style:n.title,children:"Sign in to send bug links"}),t(f,{children:"Your team opens bug links with their own Buoy sign-in. Cases you make now wait on this phone until you sign in."}),t(y,{testID:"bug-links-sign-in",label:"Sign in to Buoy",variant:"primary",onPress:()=>te(true)})]}):null,S?l(x,{children:[t(D,{children:"Add a note"}),t(le,{link:S,focused:true,run:g},S.localId)]}):null,t(D,{children:"Report a bug"}),l(v,{children:[t(W,{testID:"bug-links-note",value:T,onChangeText:B,placeholder:"What went wrong? (you can skip this)",placeholderTextColor:c.color.placeholder,style:n.input,multiline:true}),t(ae,{value:C,onChange:L,testIDPrefix:"bug-links-priority"}),t(y,{testID:"bug-links-report",label:k==="report"?"Saving\u2026":"Send a bug link",variant:"primary",disabled:!!k||!N,onPress:()=>void g("report",async()=>{const o=await Oe({note:T.trim()||void 0,priority:C});if(o){B("");L("normal")}})}),t(f,{children:N?"Buoy saves the app's state, web calls, taps and screen. It takes out private data and locks the case on this phone. Only your team can open the link.":Ee})]}),E()?t(Ye,{run:g,busy:k,onLoaded:()=>Y?.()}):null,E()&&(i?.from==="assign"||i?.from==="status"||i?.from?.startsWith("load-"))?t(f,{tone:i.tone,style:n.message,children:i.text}):null,s?l(x,{children:[t(D,{children:"Loaded bug case"}),l(v,{children:[t(d,{style:n.title,children:s.title}),t(d,{style:n.meta,children:`Reported ${j(s.createdAt)}`}),s.report.steps.map((o,a)=>t(d,{style:[n.step,(!o.ok||o.warn)&&n.bad],testID:`bug-links-step-${a}`,children:`${o.warn?"\u26A0":o.ok?"\u2713":"\u2717"} ${o.part}: ${o.detail}`},a)),l(w,{style:n.rowButtons,children:[s.canLoadAgain?t(y,{testID:"bug-links-body-load-again",label:"Load again",size:"md",fullWidth:false,onPress:()=>void g("again",async()=>{const o=await Be();if(!o.loaded)throw new Error(o.steps.find(a=>!a.ok)?.detail??"The case didn't load.")})}):null,s.report.replay?.active?t(y,{testID:"bug-links-body-live",label:"Use live requests",size:"md",fullWidth:false,onPress:()=>void g("live",async()=>{const o=await $e();if(!o.ok)throw new Error(o.detail)})}):null,s.undoGone?null:t(y,{testID:"bug-links-body-undo",label:"Restore my app",variant:"destructive",size:"md",fullWidth:false,onPress:()=>void g("undo",async()=>{const o=await Ne();if(q())throw new Error(o.find(a=>!a.ok&&a.part==="web calls")?.detail??o.find(a=>!a.ok)?.detail??"Restore didn't finish.");return"The app is back the way it was."})}),s.undoBlocked?t(y,{testID:"bug-links-body-end",label:"End (keep the app as it is)",variant:"destructive",size:"md",fullWidth:false,onPress:()=>void g("end",async()=>{const o=await Pe();if(!o.ok)throw new Error(o.detail)})}):null]}),V.length?l(x,{children:[t(d,{style:[n.step,n.bad],children:`Blocked ${V.length} calls the reporter's phone never made:`}),V.slice(0,8).map((o,a)=>t(d,{style:n.meta,numberOfLines:1,children:`${o.method} ${o.url}`},a))]}):null,t(f,{children:"Fixed it? Reload the app, then tap Load again to see if the bug is gone."})]})]}):null,t(D,{children:"Open a bug link"}),l(v,{children:[l(w,{style:n.pasteRow,children:[t(W,{testID:"bug-links-open-input",value:m,onChangeText:F,placeholder:"https://buoy.gg/bug/\u2026#\u2026",placeholderTextColor:c.color.placeholder,autoCapitalize:"none",autoCorrect:false,style:[n.input,n.pasteInput]}),t(h,{label:"Paste",testID:"bug-links-open-paste",onPress:()=>void Se().then(o=>{const a=o?.trim();if(a)F(a)})})]}),i?.from==="open"?t(f,{tone:i.tone,style:n.message,children:i.text}):null,t(y,{testID:"bug-links-open",label:k==="open"?"Opening\u2026":"Load bug",disabled:!!k||!m.trim(),onPress:()=>void g("open",async()=>{const o=await We(m.trim(),{restart:R});if(!o.loaded)throw new Error(o.steps.find(a=>!a.ok)?.detail??"The case didn't load.");se=m.trim();if(!R)Y?.();return R?"Loaded. The app restarts now, then loads it again.":"Loaded. The bar shows what came back."})}),l(w,{style:n.switchRow,children:[t(d,{style:n.meta,children:"Restart after loading (for values the app reads at start)"}),t(we,{testID:"bug-links-open-restart",value:R,onValueChange:ie})]}),t(f,{children:"Loading needs a dev build. Web calls are answered from the case, and every other call is blocked."})]}),i&&i.from!=="open"&&i.from!=="assign"&&i.from!=="status"&&!i.from?.startsWith("load-")?t(f,{tone:i.tone,style:n.message,children:i.text}):null,t(D,{children:`Your bug links (${r.length})`}),r.length===0?t(f,{children:"Links you send from this phone show up here, so you never lose one."}):null,r.filter(o=>o!==S).map(o=>t(le,{link:o,focused:false,run:g},o.localId)),t(D,{children:"Server"}),l(v,{children:[t(W,{testID:"bug-links-server",value:_??u,onChangeText:K,autoCapitalize:"none",autoCorrect:false,style:n.input}),l(w,{style:n.rowButtons,children:[t(y,{testID:"bug-links-server-save",label:"Save",size:"md",fullWidth:false,disabled:_===null,onPress:()=>void g("server",async()=>{await ee(_.trim());K(null);return"Saved."})}),u!==je?t(y,{testID:"bug-links-server-reset",label:"Use buoy.gg",size:"md",fullWidth:false,onPress:()=>void g("server",async()=>void await ee(null))}):null]}),t(f,{children:`Random numbers for the lock: ${N==="expo-uuid"?"Expo's native UUIDs":N==="getRandomValues"?"crypto.getRandomValues":"none found"}.`})]})]})}const re={urgent:"Urgent",high:"High",normal:"Normal",low:"Low"};function ae({value:e,onChange:r,testIDPrefix:s}){return l(w,{style:n.priority,children:[t(d,{style:n.meta,children:"How soon should it be fixed?"}),t(be,{size:"sm",tabs:Ve.map(u=>({key:u,label:re[u]})),activeKey:e,onChange:u=>r(u),testIDPrefix:s})]})}function le({link:e,focused:r,run:s}){const[u,I]=p(r);const[$,A]=p(e.note??"");const[S,T]=p(false);const B=xe(e);const C=ce(null);U(()=>{if(!r)return;I(true);const m=setTimeout(()=>C.current?.focus(),400);return()=>clearTimeout(m)},[r]);const L=qe[e.status];return l(v,{style:n.linkCard,children:[l(w,{style:n.linkTop,children:[t(d,{style:n.title,numberOfLines:2,children:e.note?.split("\n")[0]||(e.screen?`Bug on ${e.screen}`:`Bug from ${j(e.createdAt)}`)}),t(fe,{label:L.label,tone:L.tone})]}),t(d,{style:n.meta,children:`${e.priority&&e.priority!=="normal"?`${re[e.priority]} \xB7 `:""}${j(e.createdAt)} \xB7 ${H(e.header.steps,"step")} \xB7 ${H(e.header.errors,"error")} \xB7 ${H(e.removed.length,"private value")} removed`}),e.error&&e.status!=="sent"?t(d,{style:[n.meta,n.bad],children:e.error}):null,e.status==="sent"&&e.needsPut?t(d,{testID:`bug-links-note-pending-${e.localId}`,style:[n.meta,n.bad],children:`Note waiting to send. ${e.error??"Buoy will try again."}`}):null,e.status==="sent"&&e.wrapPending?t(d,{testID:`bug-links-wrap-wait-${e.localId}`,style:[n.meta,n.bad],children:`Not locked to your team yet, so only this link opens it. ${e.wrapWait?.message??"Buoy will try again."}`}):null,u?l(x,{children:[t(ae,{value:e.priority??"normal",onChange:m=>void s("priority",async()=>void await _e(e.localId,m)),testIDPrefix:`bug-links-priority-${e.localId}`}),t(W,{ref:C,testID:`bug-links-note-${e.localId}`,value:$,onChangeText:A,placeholder:"What went wrong?",placeholderTextColor:c.color.placeholder,style:n.input,multiline:true}),t(y,{testID:`bug-links-note-save-${e.localId}`,label:"Save note",size:"md",onPress:()=>void s("note",async()=>{const m=await Ae(e.localId,$);I(false);oe(null);return m?.needsPut?"Note saved on this phone. It goes up when Buoy can reach the server.":"Note saved. The link stays the same."})})]}):null,l(w,{style:n.rowButtons,children:[e.url?l(x,{children:[t(h,{label:"Copy",onPress:()=>void Ie(e.url),testID:`bug-links-copy-${e.localId}`}),t(h,{label:"Share",onPress:()=>void he.share({message:e.url}).catch(()=>{}),testID:`bug-links-share-${e.localId}`})]}):null,e.errorCode==="other_account"?t(h,{label:"Send as me",onPress:()=>void s("send",async()=>void await X(e.localId,{asMe:true})),testID:`bug-links-send-as-me-${e.localId}`}):e.status==="waiting"||e.status==="failed"?t(h,{label:"Send now",onPress:()=>void s("send",async()=>void await X(e.localId)),testID:`bug-links-send-${e.localId}`}):null,e.status==="sent"&&e.wrapPending&&e.wrapWait?.code==="new_root"&&e.wrapWait.teamKeyId&&e.server?t(h,{label:"Trust new team key",onPress:()=>void s("send",async()=>{await Ue(e.server,e.wrapWait.teamKeyId,E());await Z(e.localId);return"Locked to your team's new key."}),testID:`bug-links-trust-root-${e.localId}`}):e.status==="sent"&&e.wrapPending?t(h,{label:"Lock to team",onPress:()=>void s("send",async()=>void await Z(e.localId)),testID:`bug-links-wrap-${e.localId}`}):null,!u&&B?t(h,{label:"Note",onPress:()=>I(true),testID:`bug-links-edit-${e.localId}`}):null,S?l(x,{children:[t(h,{label:"Delete for everyone?",danger:true,onPress:()=>void s("delete",async()=>void await J(e.localId)),testID:`bug-links-delete-confirm-${e.localId}`}),t(h,{label:"Keep",onPress:()=>T(false),testID:`bug-links-delete-keep-${e.localId}`})]}):t(h,{label:"Delete",danger:true,onPress:()=>e.id?T(true):void s("delete",async()=>void await J(e.localId)),testID:`bug-links-delete-${e.localId}`})]})]})}function h({label:e,onPress:r,testID:s,danger:u}){return t(ye,{testID:s,onPress:r,accessibilityRole:"button",accessibilityLabel:e,style:n.small,children:t(d,{style:[n.smallText,u&&n.bad],children:e})})}const n=me.create({scroll:{flex:1},priority:{gap:6},content:{padding:c.gutter,gap:8},input:{color:c.color.text,backgroundColor:c.color.surfaceElevated,borderRadius:c.radius.input,paddingHorizontal:12,paddingVertical:10,fontSize:15,marginBottom:10,minHeight:44},title:{color:c.color.text,fontSize:15,fontWeight:"600",flexShrink:1},pasteRow:{flexDirection:"row",alignItems:"center",gap:6},pasteInput:{flex:1},meta:{color:c.color.textSecondary,fontSize:12,marginTop:2},step:{color:c.color.textSecondary,fontSize:13,marginTop:4},bad:{color:c.color.danger},rowButtons:{flexDirection:"row",flexWrap:"wrap",gap:8,marginTop:10},linkCard:{marginBottom:8},linkTop:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:8},small:{paddingHorizontal:10,paddingVertical:6,borderRadius:c.radius.chip,backgroundColor:c.color.fill},smallText:{color:c.color.accent,fontSize:13,fontWeight:"600"},message:{marginVertical:4},switchRow:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:8,marginTop:10}});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{useEffect as T,useState as L,useSyncExternalStore as B}from"react";import{StyleSheet as _,Text as h,TouchableOpacity as I,View as b}from"react-native";import{NightBadge as E,NightButton as M,NightCard as V,NightFootnote as $,NightSectionLabel as A,NightSegmented as C,night as g}from"@buoy-gg/shared-ui";import{bugLinksServer as v,subscribeBugLinksSettings as F}from"../engine/settings";import{canLoadTeamBugs as P,changeTeamBug as O,forgetTeamBugs as W,loadTeamBug as H,loadTeamBugs as R,subscribeTeamBugs as Y,teamBugs as S,teamView as N,whoNow as j}from"../engine/teamList";import{jsx as t,jsxs as d,Fragment as z}from"react/jsx-runtime";const U={open:"Open",mine:"Mine",all:"All"};const K={urgent:{label:"Urgent",tone:"danger"},high:{label:"High",tone:"warning"},normal:{label:"Normal",tone:"neutral"},low:{label:"Low",tone:"info"}};const q={new:"New",seen:"Seen",fixed:"Fixed",wont_fix:"Won't fix"};function G(e){const a=Math.max(0,(Date.now()-new Date(e).getTime())/1e3);if(Number.isNaN(a))return e;if(a<60)return"now";if(a<3600)return`${Math.floor(a/60)}m`;if(a<86400)return`${Math.floor(a/3600)}h`;return`${Math.floor(a/86400)}d`}export function TeamBugs({run:e,busy:a,onLoaded:c}){const s=B(Y,S,S);const[f,r]=L("open");const[w,u]=L(null);B(F,v,v);const[,k]=L(0);const p=j();T(()=>{const i=setInterval(()=>k(D=>j()!==p?D+1:D),1e3);return()=>clearInterval(i)},[p]);T(()=>{u(null);void R()},[p]);T(()=>()=>W(),[]);const o=s.kind==="ready"?s.me:"";const l=s.kind==="ready"?s.bugs:[];const m=N(l,f,o);const y={open:N(l,"open",o).length,mine:N(l,"mine",o).length,all:l.length};return d(z,{children:[d(b,{style:n.head,children:[t(A,{children:"Team bugs"}),t(I,{testID:"bug-links-team-refresh",accessibilityRole:"button",accessibilityLabel:"Refresh",onPress:()=>void R(),style:n.small,children:t(h,{style:n.smallText,children:s.kind==="loading"||s.kind==="ready"&&s.refreshing?"Loading\u2026":"Refresh"})})]}),s.kind==="blocked"?t(h,{testID:"bug-links-team-blocked",style:[n.meta,s.code!=="offline"&&n.bad],children:s.message}):d(z,{children:[t(C,{size:"sm",tabs:["open","mine","all"].map(i=>({key:i,label:`${U[i]} ${y[i]}`})),activeKey:f,onChange:i=>r(i),testIDPrefix:"bug-links-team-view"}),s.kind==="ready"&&m.length===0?t(h,{testID:"bug-links-team-empty",style:n.meta,children:f==="mine"?"Nothing is yours right now.":"No bugs here."}):null,m.map(i=>t(J,{bug:i,me:o,open:w===i.id,onPress:()=>u(w===i.id?null:i.id),run:e,busy:a,onLoaded:c},i.id)),P()?null:t($,{children:"This is a release build, so Load is off here. Open the bug in your dev build to load it."})]})]})}function J({bug:e,me:a,open:c,onPress:s,run:f,busy:r,onLoaded:w}){const u=e.status==="fixed"||e.status==="wont_fix";const k=K[e.priority];const p=[e.reporter,G(e.createdAt),e.build,e.assigneeName?`${e.assigneeName==="You"?"You have":`${e.assigneeName} has`} it`:null,u?q[e.status]:null,e.repeatOf?"Repeat":null].filter(Boolean).join(" \xB7 ");const o=(l,m,y)=>void f(l,async()=>{await O(e.id,m);return y});return d(V,{style:n.card,children:[d(I,{testID:`bug-links-team-row-${e.id}`,accessibilityRole:"button",accessibilityLabel:e.title,onPress:s,style:n.inset,children:[d(b,{style:n.top,children:[t(h,{style:n.title,numberOfLines:c?4:2,children:e.title}),e.priority!=="normal"?t(E,{label:k.label,tone:k.tone}):null]}),t(h,{style:n.meta,numberOfLines:2,children:p}),e.error&&e.error!==e.title?t(h,{style:[n.meta,n.bad],numberOfLines:c?3:1,children:e.error}):null]}),c?d(b,{style:n.actions,children:[P()?t(M,{testID:`bug-links-team-load-${e.id}`,label:r===`load-${e.id}`?"Loading\u2026":"Load",variant:"primary",disabled:!!r||!e.canOpen,onPress:()=>void f(`load-${e.id}`,async()=>{const l=await H(e.id);if(!l.loaded)throw new Error(l.steps.find(i=>!i.ok)?.detail??"The case didn't load.");const m=S();const y=m.kind==="ready"&&m.bugs.find(i=>i.id===e.id)?.status==="new";if(y)void O(e.id,{status:"seen"}).catch(()=>{});w();return"Loaded. The bar shows what came back."})}):null,e.canOpen?null:t($,{tone:"danger",children:"This phone can't open this bug, so it can't load here."}),d(b,{style:n.row,children:[e.assignee===a?t(x,{label:"Let go",disabled:!!r,testID:`bug-links-team-unassign-${e.id}`,onPress:()=>o("assign",{assignee:null},"Let go.")}):t(x,{label:e.assignee?"Take it from them":"Take it",disabled:!!r,testID:`bug-links-team-assign-${e.id}`,onPress:()=>o("assign",{assignee:a},"It's yours.")}),u?t(x,{label:"Still broken",disabled:!!r,testID:`bug-links-team-reopen-${e.id}`,onPress:()=>o("status",{status:"seen"},"Opened again.")}):t(x,{label:"Fixed",disabled:!!r,testID:`bug-links-team-fixed-${e.id}`,onPress:()=>o("status",{status:"fixed"},"Marked fixed.")})]})]}):null]})}function x({label:e,onPress:a,testID:c,disabled:s}){return t(I,{testID:c,onPress:a,disabled:s,accessibilityRole:"button",accessibilityLabel:e,style:[n.small,s&&n.off],children:t(h,{style:n.smallText,children:e})})}const n=_.create({head:{flexDirection:"row",alignItems:"center",justifyContent:"space-between"},card:{marginBottom:8},top:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:8},title:{color:g.color.text,fontSize:15,fontWeight:"600",flexShrink:1},meta:{color:g.color.textSecondary,fontSize:12,marginTop:2},bad:{color:g.color.danger},inset:{paddingHorizontal:6,paddingVertical:2},actions:{marginTop:10,gap:8,paddingHorizontal:6},row:{flexDirection:"row",flexWrap:"wrap",gap:8},small:{paddingHorizontal:10,paddingVertical:6,borderRadius:g.radius.chip,backgroundColor:g.color.fill},off:{opacity:.5},smallText:{color:g.color.accent,fontSize:13,fontWeight:"600"}});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";let o=null;let s=false;const t=new Set;export function focusLink(n){o=n;for(const e of t)e()}export function focusedLink(){return o}export function wantsSignIn(){return s}export function askSignIn(n){s=n;for(const e of t)e()}export function subscribeFocus(n){t.add(n);return()=>t.delete(n)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{useEffect as v,useState as L,useSyncExternalStore as x}from"react";import{ActivityIndicator as W,Share as le,StyleSheet as E,Text as g,TouchableOpacity as D,View as f,useWindowDimensions as $}from"react-native";import{Bug as R,FloatingTool as ie,X as j,dismissFloatingTool as m,getFloatingTools as se,night as s,openBuoyTool as T,openFloatingTool as ae,subscribeFloatingTools as re,useSafeAreaInsets as ce}from"@buoy-gg/shared-ui";import{blockedCalls as de,bugCaseBusy as A,endBugCase as F,leaveSandbox as ue,loadAgain as q,loadedCase as k,subscribeLoadedCase as B,undoBugCase as H}from"../engine/loader";import{deleteLink as G,dismissReportState as M,reportState as P,sendLink as N,subscribeReportState as V}from"../engine/reporter";import{askSignIn as ge,focusLink as he}from"./focus";import{pillTop as be,subscribeTopBanners as fe,topBannersKey as K}from"./topBanners";import{jsx as o,jsxs as h,Fragment as pe}from"react/jsx-runtime";export const REPORT_TOAST_ID="bug-links-toast";const _="clock-chip";const me=12e3;const ye=64;const C=t=>se().some(l=>l.id===t);function d({label:t,onPress:l,testID:e,tone:r,disabled:i}){return o(D,{testID:e,accessibilityRole:"button",accessibilityLabel:t,accessibilityState:{disabled:!!i},disabled:i,onPress:l,style:[u.button,i&&u.dim],hitSlop:{top:8,bottom:8,left:4,right:4},children:o(g,{style:[u.buttonText,r==="accent"&&u.accent,r==="danger"&&u.danger],children:t})})}const ke=typeof __DEV__!=="undefined"&&__DEV__?116:64;function Ie({id:t,revealToken:l}){const e=x(V,P,P);const[r,i]=L(null);const{width:c}=$();v(()=>{if(e?.phase!=="sent")return;const I=setTimeout(()=>{m(REPORT_TOAST_ID);M()},me);return()=>clearTimeout(I)},[e?.phase,e?.localId]);if(!e)return null;const p=e.phase==="capturing"||e.phase==="sending";const b=e.phase==="sent"?e.removed?`${e.removed} private ${e.removed===1?"value":"values"} removed`:"Locked on this phone":e.phase==="waiting"?"Saved on this phone":e.phase==="failed"?"Not sent":"Taking out private data";return o(ie,{id:t,revealToken:l,variant:"strip",dragFromSurface:true,defaultPlacement:"bottom",bottomClearance:ke,accessibilityLabel:"Move or hide the bug report",handleAdornment:o(R,{size:12,color:s.color.textSecondary}),children:h(f,{style:[u.row,{maxWidth:c-ye}],testID:"bug-links-toast",children:[p?o(W,{size:"small",color:s.color.accent}):o(R,{size:18,color:e.phase==="failed"?s.color.danger:s.color.accent}),h(f,{style:u.text,children:[o(g,{style:u.title,numberOfLines:2,testID:"bug-links-toast-title",children:e.message}),o(g,{style:u.detail,numberOfLines:2,children:b})]}),e.phase==="sent"&&e.url&&e.localId?h(pe,{children:[o(d,{testID:"bug-links-toast-share",label:"Share",tone:"accent",onPress:()=>void le.share({message:e.url}).catch(()=>{})}),o(d,{testID:"bug-links-toast-note",label:"Note",onPress:()=>{he(e.localId);m(REPORT_TOAST_ID);T("bug-links")}}),r===e.localId?o(d,{testID:"bug-links-toast-delete-confirm",label:"Delete for all?",tone:"danger",onPress:()=>void G(e.localId).catch(()=>{})}):o(d,{testID:"bug-links-toast-delete",label:"Delete",tone:"danger",onPress:()=>i(e.localId)})]}):null,e.phase==="waiting"&&e.localId&&e.code==="signed_out"?o(d,{testID:"bug-links-toast-sign-in",label:"Sign in",tone:"accent",onPress:()=>{ge(true);m(REPORT_TOAST_ID);T("bug-links")}}):e.phase==="waiting"&&e.localId&&e.code==="other_account"?o(d,{testID:"bug-links-toast-send-as-me",label:"Send as me",tone:"accent",onPress:()=>void N(e.localId,{asMe:true})}):e.phase==="waiting"&&e.localId?o(d,{testID:"bug-links-toast-retry",label:"Retry",tone:"accent",onPress:()=>void N(e.localId)}):null,e.phase==="sending"&&e.localId?o(d,{testID:"bug-links-toast-cancel",label:"Cancel",onPress:()=>void G(e.localId).catch(()=>{})}):null,!p?o(D,{testID:"bug-links-toast-close",accessibilityRole:"button",accessibilityLabel:"Close",onPress:()=>{m(REPORT_TOAST_ID);M()},style:u.icon,children:o(j,{size:16,color:s.color.textSecondary})}):null]})})}function X({testID:t,label:l,onPress:e,disabled:r,children:i,round:c}){return o(D,{testID:t,accessibilityRole:"button",accessibilityLabel:l,accessibilityState:{disabled:!!r},disabled:r,onPress:e,activeOpacity:.7,hitSlop:{top:8,bottom:8,left:4,right:4},style:[c?n.close:n.square,r&&u.dim],children:i})}export function BugLinksOverlay(){const t=x(B,k,k);const l=x(B,A,A);const e=x(fe,K,K);const r=ce({minBottom:0});const{width:i}=$();const[c,p]=L(false);const[b,I]=L(0);const w=!!t?.report.replay?.active;v(()=>{if(!w)return I(0);let a=true;const y=()=>void de().then(ne=>a&&I(ne.length));y();const oe=setInterval(y,3e3);return()=>{a=false;clearInterval(oe)}},[w,t?.loadId]);v(()=>{if(!t)p(false)},[t]);v(()=>{if(t?.notice)p(true)},[t?.notice]);if(!t)return null;const S=t.report.steps.filter(a=>!a.ok).length;const O=b>0||S>0||!!t.notice;const J=t.notice?t.notice:[b?`${b} ${b===1?"request":"requests"} blocked`:null,S?`${S} ${S===1?"part":"parts"} didn't load`:null,w?null:"Live requests"].filter(Boolean).join(" \xB7 ");const Q={"this phone":"This phone differs",clock:"No Clock tool"};const Y=[...new Set(t.report.steps.filter(a=>a.warn).map(a=>Q[a.part]??"See the details"))];const z=J||Y.join(" \xB7 ");const Z=t.title==="Bug case"?"Bug loaded":t.title;const ee=be(e,r.top);const te=t.undoGone?()=>void F():()=>{void H().then(()=>{if(k()?.undoBlocked)p(true)})};return h(f,{style:[n.wrap,{top:ee}],pointerEvents:"box-none",children:[h(f,{style:[n.banner,O&&n.bannerWarn],testID:"bug-links-loaded-bar",children:[h(D,{testID:"bug-links-loaded-open",style:n.who,onPress:()=>p(a=>!a),activeOpacity:.7,accessibilityRole:"button",accessibilityLabel:c?"Hide what loaded":"Show what loaded",children:[o(f,{style:n.avatar,children:o(R,{size:13,color:"#fff"})}),h(f,{style:{maxWidth:Math.min(200,i-150)},children:[o(g,{style:n.name,numberOfLines:1,children:Z}),z?o(g,{style:[n.detail,O&&n.warn],numberOfLines:1,testID:"bug-links-loaded-detail",children:z}):null]})]}),t.canLoadAgain?o(X,{testID:"bug-links-load-again",label:"Load again",disabled:l,onPress:()=>void q().catch(()=>{}),children:l?o(W,{size:"small",color:s.color.accent}):o(g,{style:n.again,children:"\u21BB"})}):null,o(X,{testID:"bug-links-undo",label:t.undoGone?"End the bug case":"Restore my app",disabled:l,onPress:te,round:true,children:o(j,{size:12,color:"rgba(255, 255, 255, 0.5)"})})]}),c?h(f,{style:[n.card,{width:Math.min(340,i-24)}],testID:"bug-links-loaded-details",children:[o(g,{style:n.cardTitle,numberOfLines:2,children:t.title}),t.notice?o(g,{style:[n.line,n.warn],children:t.notice}):null,t.report.steps.map((a,y)=>o(g,{style:[n.line,(!a.ok||a.warn)&&n.warn],testID:`bug-links-loaded-step-${y}`,children:`${a.ok&&!a.warn?"\u2713":"\u26A0"} ${a.part}: ${a.detail}`},y)),b?o(g,{style:[n.line,n.warn],children:`\u26A0 ${b} ${b===1?"request":"requests"} blocked`}):null,h(f,{style:n.cardButtons,children:[w?o(d,{testID:"bug-links-leave-sandbox",label:"Use live requests",disabled:l,onPress:()=>void ue()}):null,o(d,{testID:"bug-links-loaded-more",label:"Bug Links",onPress:()=>{p(false);T("bug-links")}})]}),h(f,{style:n.cardButtons,children:[t.canLoadAgain?o(d,{testID:"bug-links-details-load-again",label:l?"Working\u2026":"\u21BB Load again",disabled:l,onPress:()=>void q().catch(()=>{})}):null,t.undoGone?null:o(d,{testID:"bug-links-details-undo",label:"Restore my app",tone:"accent",disabled:l,onPress:()=>void H()}),t.undoBlocked||t.undoGone?o(d,{testID:"bug-links-end",label:"End",tone:"danger",disabled:l,onPress:()=>void F()}):null]})]}):null]})}const we={id:REPORT_TOAST_ID,render:t=>o(Ie,{...t})};let U=false;export function watchBugLinksOverlays(){if(U)return;U=true;let t=null;V(()=>{const i=P();const c=i?`${i.localId}:${i.phase}`:null;if(i&&c!==t&&!C(REPORT_TOAST_ID))ae(we);if(!i&&C(REPORT_TOAST_ID))m(REPORT_TOAST_ID);t=c});let l=null;let e=false;const r=()=>{if(e)return;const i=k()?.loadId??null;if(!i||l===i||!C(_))return;e=true;setTimeout(()=>{e=false;const c=k()?.loadId??null;if(c&&l!==c&&C(_)){l=c;m(_)}},0)};B(r);re(r);r()}const u=E.create({row:{flexDirection:"row",alignItems:"center",gap:4,paddingLeft:8,paddingRight:2,paddingVertical:6,flexShrink:1},column:{flexShrink:1,paddingBottom:4},buttons:{flexDirection:"row",justifyContent:"flex-end",gap:4,paddingRight:2},text:{flexShrink:1,flexGrow:1,minWidth:0,marginLeft:4},title:{color:s.color.text,fontSize:14,fontWeight:"600"},detail:{color:s.color.textSecondary,fontSize:11},button:{paddingHorizontal:7,height:32,alignItems:"center",justifyContent:"center",borderRadius:16},buttonText:{color:s.color.text,fontSize:13,fontWeight:"600"},accent:{color:s.color.accent},dim:{opacity:.4},danger:{color:s.color.danger},icon:{width:28,height:32,alignItems:"center",justifyContent:"center",borderRadius:16}});const n=E.create({wrap:{position:"absolute",left:0,right:0,alignItems:"center",zIndex:9999},banner:{flexDirection:"row",alignItems:"center",backgroundColor:"rgba(20, 20, 20, 0.94)",borderWidth:1,borderColor:"#34C75940",borderRadius:22,paddingVertical:7,paddingLeft:7,paddingRight:10,gap:10,marginTop:3,shadowColor:"#000",shadowOffset:{width:0,height:6},shadowOpacity:.3,shadowRadius:14,elevation:10},bannerWarn:{borderColor:s.color.warning+"80"},who:{flexDirection:"row",alignItems:"center",gap:10,flexShrink:1},avatar:{width:24,height:24,borderRadius:12,backgroundColor:s.color.warning,alignItems:"center",justifyContent:"center"},name:{fontSize:14,fontWeight:"600",color:"#fff"},detail:{fontSize:11,color:s.color.textSecondary},warn:{color:s.color.warning},again:{color:s.color.accent,fontSize:16,fontWeight:"700",lineHeight:19},square:{width:28,height:28,borderRadius:8,borderWidth:1,borderColor:"#34C75940",backgroundColor:"rgba(52, 199, 89, 0.15)",alignItems:"center",justifyContent:"center"},close:{width:22,height:22,borderRadius:11,backgroundColor:"rgba(128, 128, 128, 0.16)",alignItems:"center",justifyContent:"center",marginLeft:-4},card:{marginTop:8,backgroundColor:"rgba(20, 20, 20, 0.97)",borderWidth:1,borderColor:s.color.border,borderRadius:16,padding:12,gap:4,shadowColor:"#000",shadowOffset:{width:0,height:8},shadowOpacity:.35,shadowRadius:18,elevation:12},cardTitle:{color:"#fff",fontSize:14,fontWeight:"600",marginBottom:4},line:{color:s.color.textSecondary,fontSize:12,lineHeight:17},cardButtons:{flexDirection:"row",flexWrap:"wrap",justifyContent:"flex-end",gap:4,marginTop:4}});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{getRegisteredToolModule as u}from"@buoy-gg/shared-ui";let t=null;let r=null;let o=false;function i(){if(t||o)return t??null;let e=u("@buoy-gg/impersonate")?.impersonateStore??null;if(!e){try{e=require("@buoy-gg/impersonate").impersonateStore??null}catch{e=null}}t=e;return e}function l(){if(r||o)return r??null;let e=u("@buoy-gg/scenarios")?.scenariosStore??null;if(!e){try{e=require("@buoy-gg/scenarios").scenariosStore??null}catch{e=null}}r=e;return e}const a=e=>e?.getSnapshot?.()??e?.getState?.();export function subscribeTopBanners(e){const s=[i()?.subscribe(e),l()?.subscribe(e)];return()=>s.forEach(n=>n?.())}export function impersonateBannerUp(){const e=a(i());return!!(e?.isActive&&e.currentUser&&e.showBanner)}export function scenarioBannerUp(){return!!a(l())?.active}export function topBannersKey(){return(impersonateBannerUp()?1:0)|(scenarioBannerUp()?2:0)}export const PILL_TOP=50;export const PILL_STACK=46;const c=52;const p=50;export function pillTop(e,s){let n=PILL_TOP;if(e&1)n=Math.max(n,PILL_TOP+PILL_STACK);if(e&2)n=Math.max(n,s+c+p);return n}export function setTopBannerStoresForTests(e){o=true;t=e.impersonate??null;r=e.scenarios??null}export function resetTopBannersForTests(){o=false;t=null;r=null}export function setImpersonateStoreForTests(e){setTopBannerStoresForTests({impersonate:e,scenarios:null})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{base64UrlDecode as T,base64UrlEncode as l}from"@buoy-gg/shared-ui";export const BUG_PRIORITIES=["urgent","high","normal","low"];function w(e){const t={};if(e?.card)t["X-Buoy-Card"]=e.card;if(e?.wrap)t["X-Buoy-Wrap"]=l(new TextEncoder().encode(e.wrap));if(e?.cardRevision)t["X-Buoy-Card-Revision"]=String(e.cardRevision);if(e?.owner)t["X-Buoy-Owner"]=e.owner;if(e?.priority&&e.priority!=="normal")t["X-Buoy-Priority"]=e.priority;return t}export class BugLinksHttpError extends Error{constructor(t,n,o,s=false,r={}){super(n);this.code=t;this.status=o;this.retry=s;this.detail=r;this.name="BugLinksHttpError"}}const h=3e4;const m=e=>/^[a-z][a-z0-9+.-]*:\/\/[^/?#]+/i.exec(e)?.[0].toLowerCase()??null;function k(e,t){const n=e?m(e):null;return!!n&&n!==m(t)}async function a(e,t,n,o){const s=Date.now()+h;const r=typeof AbortController!=="undefined"?new AbortController:null;const i=r?setTimeout(()=>r.abort(),h):null;const f=()=>r?.abort();o?.signal?.addEventListener?.("abort",f);let c;let p;try{c=await b(fetch(`${e.replace(/\/+$/,"")}${t}`,{...n,redirect:"error",signal:r?.signal,__buoyInternal:true}),s);if(c.redirected||k(c.url,e))throw new BugLinksHttpError("redirected","The bug links server sent this call somewhere else, so Buoy stopped.");const d=c.status;const y=d===204||d===304?"":await b(c.text(),s);p={status:d,ok:d>=200&&d<300,text:async()=>y,json:async()=>JSON.parse(y)}}catch(d){if(d instanceof BugLinksHttpError)throw d;if(o?.signal?.aborted)throw new BugLinksHttpError("cancelled","Cancelled.");throw new BugLinksHttpError("offline","Couldn't reach the bug links server. Buoy will try again.",void 0,true)}finally{if(i)clearTimeout(i);o?.signal?.removeEventListener?.("abort",f)}return p}function b(e,t){return new Promise((n,o)=>{const s=setTimeout(()=>o(new Error("timed out")),Math.max(0,t-Date.now()));e.then(r=>{clearTimeout(s);n(r)},r=>{clearTimeout(s);o(r)})})}async function u(e){let t={};try{t=await e.json()}catch{}const n=typeof t.message==="string"?t.message:`The server said ${e.status}.`;if(e.status===401)throw new BugLinksHttpError("signed_out",n,401);if(e.status===402||t.error==="needs_pro")throw new BugLinksHttpError("needs_pro",n,e.status);if(e.status===404)throw new BugLinksHttpError("not_found",n,404,false,t);if(e.status===410)throw new BugLinksHttpError("expired",n,410);if(e.status===413)throw new BugLinksHttpError("too_big",n,413);if(e.status===429)throw new BugLinksHttpError("rate_limited",n,429,true);if(e.status===409)throw new BugLinksHttpError(t.error==="team_changed"||t.error==="priority_set"?t.error:"conflict",n,409,false,t);throw new BugLinksHttpError("server",n,e.status,e.status>=500)}const g={"Content-Type":"text/plain","X-Buoy-Encoding":"base64url"};export async function uploadBlob(e,t,n,o={}){const s=await a(e,"/api/bugs",{method:"POST",headers:{...g,"X-Buoy-Auth":t,...o.uploadId?{"X-Buoy-Upload-Id":o.uploadId}:{},...o.editToken?{"X-Buoy-Edit-Token":o.editToken}:{},...w(o.extras)},body:l(n)},{signal:o.signal});if(s.status===404)throw new BugLinksHttpError("not_found",`${e} doesn't take bug links. Check the server in the Bug links screen.`,404);if(s.status!==201&&s.status!==200)return u(s);const r=await s.json();if(typeof r.id!=="string"||typeof r.editToken!=="string"){throw new BugLinksHttpError("server","The server's answer had no link id.",s.status,true)}return{id:r.id,editToken:r.editToken,expiresAt:typeof r.expiresAt==="string"?r.expiresAt:null,cardRevision:typeof r.cardRevision==="number"?r.cardRevision:void 0,wrapStatus:typeof r.wrapStatus==="string"?r.wrapStatus:void 0,teamKeys:!!r.currentKey||r.teamKeys===true,owner:typeof r.owner==="string"&&r.owner?r.owner:void 0}}export async function replaceBlob(e,t,n,o,s){const r=await a(e,`/api/bugs/${encodeURIComponent(t)}`,{method:"PUT",headers:{...g,"X-Buoy-Edit-Token":n,...w(s)},body:l(o)});if(!r.ok)return u(r);const i=await r.json().catch(()=>({}));return{expiresAt:typeof i.expiresAt==="string"?i.expiresAt:i.expiresAt===null?null:void 0,cardRevision:typeof i.cardRevision==="number"?i.cardRevision:void 0}}export async function sendWrap(e,t,n,o,s){const r=await a(e,`/api/bugs/${encodeURIComponent(t)}`,{method:"PATCH",headers:{"Content-Type":"application/json","X-Buoy-Edit-Token":n},body:`{"wrap":${o},"caseTag":${JSON.stringify(s)}}`});if(!r.ok)return u(r)}export async function sendPriority(e,t,n,o){const s=await a(e,`/api/bugs/${encodeURIComponent(t)}`,{method:"PATCH",headers:{"Content-Type":"application/json","X-Buoy-Edit-Token":n},body:JSON.stringify({priority:o})});if(!s.ok)return u(s)}export async function teamJson(e,t,n,o={}){const s={"X-Buoy-Auth":n};if(o.body!==void 0)s["Content-Type"]="application/json";const r=await a(e,t,{method:o.method??"GET",headers:s,...o.body!==void 0?{body:JSON.stringify(o.body)}:{}});if(!r.ok)return u(r);if(r.status===204)return{};const i=await r.json();return i}export async function fetchTeamKeys(e,t){const n=await a(e,"/api/bugs/team-key",{method:"GET",headers:{"X-Buoy-Auth":t}});if(n.status===404)return null;if(!n.ok)return u(n);return n.json()}export async function deleteRemote(e,t,n){const o={};if(n.editToken)o["X-Buoy-Edit-Token"]=n.editToken;if(n.credential)o["X-Buoy-Auth"]=n.credential;const s=await a(e,`/api/bugs/${encodeURIComponent(t)}`,{method:"DELETE",headers:o});if(!s.ok&&s.status!==404&&s.status!==410)return u(s)}export async function deleteUpload(e,t,n){const o=await a(e,`/api/bugs?upload=${encodeURIComponent(t)}`,{method:"DELETE",headers:{"X-Buoy-Auth":n}});if(!o.ok&&o.status!==404)return u(o)}export async function fetchBlob(e,t,n){const o=await a(e,`/api/bugs/${encodeURIComponent(t)}`,{method:"GET",headers:{"X-Buoy-Auth":n,Accept:"text/plain","X-Buoy-Encoding":"base64url"}});if(!o.ok)return u(o);const s=T((await o.text()).trim());if(!s)throw new BugLinksHttpError("server","The server sent a case that isn't base64url.",o.status);return s}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import*as c from"@buoy-gg/license";import{base64UrlDecode as u}from"@buoy-gg/shared-ui";let i=null;export function setBugLinksCredentialProvider(t){i=t}export function currentCredential(){if(i)return i();try{const t=c.SessionManager;const e=t?.getAccessToken();if(e)return e}catch{}try{return c.LicenseManager.getState().licenseKey??null}catch{return null}}export function credentialOwner(t){if(!t)return null;const e=t.split(".");if(e.length===3){try{const r=u(e[1]);let o="";if(r)for(let s=0;s<r.length;s++)o+=String.fromCharCode(r[s]);const n=JSON.parse(o).sub;if(typeof n==="string"&&n)return`sub:${l(n)}`}catch{}}return`key:${l(t)}`}function l(t){let e=2166136261;for(let r=0;r<t.length;r++){e^=t.charCodeAt(r);e=Math.imul(e,16777619)>>>0}return e.toString(16).padStart(8,"0")}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{devToolsStorageKeys as R,getItemChecked as S,persistentStorage as y,setItemChecked as _,storageIsMemoryOnly as E}from"@buoy-gg/shared-ui";import{tabsMatter as c,withTabLock as H}from"./otherTabs";import{randomBytes as F}from"./randomBytes";const $={strict:true};const j=50;const T=10;const L=1e6;const V=12;let a=[];let g=null;let p=false;const d=new Map;const k=new Set;const I=new Set;const m=()=>R.bugLinks.links();const J="buoy-bug-links-links";const f=t=>`${R.bugLinks.links()}_blob_${t}`;function O(t){const e=t?JSON.parse(t):[];return Array.isArray(e)?e.filter(n=>!!n&&typeof n==="object"&&typeof n.localId==="string"&&typeof n.key==="string"):[]}function K(t){try{return O(t)}catch{if(t&&!M){M=true;void y.setItem(`${m()}_broken`,t).catch(()=>{})}return[]}}let M=false;function U(t){if(p)return;const e=t.map(n=>n.status==="sending"?{...n,status:"waiting"}:n);a=[...a,...e.filter(n=>!a.some(r=>r.localId===n.localId))];p=true}const X=/^(bl_[a-z0-9]+_[a-z0-9]+)(?:_[a-z0-9]+_\d{1,2})?$/;async function q(){if(c()||!p||M)return;const t=`${f("")}`;let e;try{e=await y.getAllKeys()}catch{return}for(const n of e){if(!p)return;if(!n.startsWith(t))continue;const r=X.exec(n.slice(t.length))?.[1];if(!r||a.some(s=>s.localId===r))continue;await y.removeItem(n).catch(()=>{})}}export function loadMyLinks(){if(!g){g=(async()=>{try{U(K(await S(m())));void w(q)}catch{g=null}x()})()}return g}const l=new Set;const v=new Set;export function saveRowNow(t){if(c()||!v.has(t)&&!l.has(t))return Promise.resolve(true);return w(async()=>{if(await E())return true;if(l.has(t)){let e;try{e=K(await S(m())).some(n=>n.localId===t)}catch{return false}if(!e)return true}return b()})}const h=new Map;function A(t){const e=h.get(t.localId);if(!e)return t;return{...t,...e,status:t.status==="deleting"?"deleting":e.status??t.status}}export function holdUploadResult(t,e){h.set(t,{...h.get(t),...e});a=a.map(A);x()}export function hasHeldUploadResult(t){return h.has(t)}export function saveHeldUploadResults(){return w(()=>b())}async function W(){return O(await B(m()))}async function z(){const t=await W();const e=new Set(t.map(r=>r.localId));const n=a.filter(r=>l.has(r.localId)&&!e.has(r.localId));for(const r of e)l.delete(r);return[...n,...t]}let D=Promise.resolve();function w(t){const e=D.catch(()=>{}).then(()=>G(t));D=e.catch(()=>{});return e}function G(t){return H(J,async()=>{await loadMyLinks();if(c()){try{a=(await z()).map(A)}catch(e){if(globalThis.localStorage)throw e}}return t()})}export function isUnsavedNew(t){return l.has(t)}export async function reloadMyLinks(){await w(async()=>x())}export function myLinks(){return a}export function canAddNote(t){if(t.status!=="sent"||t.needsPut)return true;if(k.has(t.localId))return false;const e=a.filter(n=>n.status==="sent"&&!n.needsPut);return e.findIndex(n=>n.localId===t.localId)<T}export function findMyLink(t){return a.find(e=>e.localId===t)}export function subscribeMyLinks(t){I.add(t);return()=>I.delete(t)}export function addMyLink(t,e){return w(async()=>{a=[t,...a.filter(s=>s.localId!==t.localId)];l.add(t.localId);const n=await writeBlob(t.localId,e,t.blobVersion??0);const r=await b();return n&&r})}export function updateMyLink(t,e){return w(async()=>{if(typeof e==="function"){const o=a.find(u=>u.localId===t);const i=o?e(o):null;if(!i)return o;e=i}const n=e;let r;a=a.map(o=>o.localId===t?r={...o,...n,...o.status==="deleting"?{status:"deleting"}:{}}:o);const s=await b();if(!s&&!c())v.add(t);return s||!c()?r:findMyLink(t)})}export function removeMyLink(t){return w(async()=>{const e=a.findIndex(r=>r.localId===t);const n=a[e];a=a.filter(r=>r.localId!==t);if(await b()){l.delete(t);h.delete(t);await C(t);return true}if(n&&!c()&&!findMyLink(t)){a=[...a.slice(0,e),n,...a.slice(e)];v.add(t);x()}return false})}async function B(t){const e=c()?globalThis.localStorage:void 0;return e?e.getItem(t):y.getItem(t)}export async function readBlob(t){return readBlobChecked(t).catch(()=>null)}export async function readBlobChecked(t){const e=d.get(t);const n=a.find(u=>u.localId===t);if(e&&(!n||e.version===(n.blobVersion??0)))return e.blob;const r=u=>c()?B(u):S(u);const s=await r(f(t));const o=s?/^chunks:(\d{1,2}):([a-z0-9]+)$/.exec(s):null;if(!o)return s;const i=[];for(let u=0;u<Number(o[1]);u++){const P=await r(`${f(t)}_${o[2]}_${u}`);if(P===null)return null;i.push(P)}return i.join("")}let Q=0;function Y(){try{return Array.from(F(6),t=>t.toString(36).padStart(2,"0")).join("")}catch{return`${Date.now().toString(36)}${(++Q).toString(36)}`}}export async function writeBlob(t,e,n=0){const r=d.get(t);const s=await Z(t,e,n);if(!s&&c()&&!l.has(t)){if(r)d.set(t,r);else d.delete(t)}return s}async function Z(t,e,n){d.set(t,{version:n,blob:e});k.delete(t);try{const r=await B(f(t)).catch(()=>null);if(e.length<=L){if(!await _(f(t),e,$))return false;d.delete(t);await N(t,r);return true}const s=Math.ceil(e.length/L);if(s>V)return false;const o=Y();try{for(let i=0;i<s;i++){if(!await _(`${f(t)}_${o}_${i}`,e.slice(i*L,(i+1)*L),$))throw new Error("not saved")}if(!await _(f(t),`chunks:${s}:${o}`,$))throw new Error("not saved")}catch{await N(t,`chunks:${s}:${o}`);return false}d.delete(t);await N(t,r);return true}catch{return false}}async function N(t,e){const n=e?/^chunks:(\d{1,2}):([a-z0-9]+)$/.exec(e):null;if(!n)return;for(let r=0;r<Number(n[1]);r++){await y.removeItem(`${f(t)}_${n[2]}_${r}`).catch(()=>{})}}async function C(t){d.delete(t);const e=await B(f(t)).catch(()=>null);await y.removeItem(f(t)).catch(()=>{});await N(t,e)}async function b(){let t=a;const e=[];while(t.length>j){const r=t.map(o=>o.status==="sent"&&!o.needsPut&&!o.wrapPending&&!o.priorityPending).lastIndexOf(true);if(r<0)break;const s=t[r].localId;e.push(s);t=t.filter(o=>o.localId!==s)}let n=true;try{if(!p&&!c()){const r=new Set(a.map(s=>s.localId));U(K(await S(m())));t=[...t,...a.filter(s=>!r.has(s.localId))]}n=await _(m(),JSON.stringify(t),$)}catch{n=false}if(n){l.clear();v.clear();h.clear();a=a.filter(s=>!e.includes(s.localId));for(const s of e)await C(s);const r=t.filter(s=>s.status==="sent"&&!s.needsPut);for(const s of r.slice(T)){if(k.has(s.localId))continue;k.add(s.localId);await C(s.localId)}}else if(c()){try{a=(await z()).map(A)}catch{}}x();return n}function x(){for(const t of I)t()}export function resetMyLinksForTests(){l.clear();v.clear();h.clear();M=false;a=[];g=null;p=false;d.clear();k.clear()}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{dispatchToolAction as Be,getStateSnapshotProviders as z,getToolAdapters as Ee,getSafeCurrentRoute as Ne,devToolsStorageKeys as I,persistentStorage as T,setItemChecked as Q,superJsonDeserialize as Z,superJsonSerialize as ue,validateBugCase as _e,readToolSnapshot as We,reloadApp as De,waitForToolRegistry as Me}from"@buoy-gg/shared-ui";import{otherAppReason as Pe,phoneDifferences as ze,thisPhone as de}from"./phoneCheck";import{tabStorage as H}from"./tabStorage";import{tabsMatter as fe,tabsProblem as pe,withStepLock as Je}from"./otherTabs";import{buildSandbox as je,BLOCK_RULE_NAME as he,isSandboxRule as Ke}from"./sandbox";const we=15e5;const J=()=>`${I.bugLinks.loaded()}_case`;const j=()=>`${I.bugLinks.loaded()}_undo_state`;const F=()=>`${I.bugLinks.loaded()}_restart`;const ye=[300,400,800];let o=null;let ke=Promise.resolve();let ee=0;function L(t,e={}){if(!e.quiet){ee++;v()}const a=()=>Je(t);const r=ke.then(a,a);ke=r.catch(()=>{});return r.finally(()=>{if(e.quiet)return;ee--;v()})}export function bugCaseBusy(){return ee>0}let b;let g=[];let A={};const ge=24;const te=new Set;export function loadedCase(){return o}export function subscribeLoadedCase(t){te.add(t);return()=>te.delete(t)}function v(){for(const t of te)t()}const $=t=>!!Ee()[t];async function y(t,e,a){return await Be(t,e,a)}const ae=t=>new Promise(e=>setTimeout(e,t));export function safeRoute(t){if(typeof t!=="string")return null;const e=t.trim();if(!e.startsWith("/")||e.startsWith("//")||e.length>2e3)return null;if(/[\u0000-\u001f\\]/.test(e)||/^\/[a-z][a-z0-9+.-]*:/i.test(e))return null;return e}export function checkCase(t){try{return _e(t)}catch(e){throw new Error(e instanceof Error?e.message:"This isn't a bug case Buoy can load.")}}function me(){return`ld_${Date.now().toString(36)}_${Math.floor(Math.random()*1e9).toString(36)}`}export async function loadBugCase(t,e={}){const a=checkCase(t);return L(()=>ve(a,e))}export function readableTime(t){try{const e=new Date(t).toLocaleString(void 0,{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit"});return e&&e!=="Invalid Date"?e:t}catch{return t}}export function skippedText(t){if(!t.length)return"";const e=n=>n.replace(/^[a-z]+:\/\/[^/]+/i,"").split("?")[0]||"/";const a=t.slice(0,3).map(n=>`${n.method.toUpperCase()} ${e(n.url)} (${n.reason})`);const r=t.length>3?`, and ${t.length-3} more`:"";return` ${t.length===1?"1 recorded call has":`${t.length} recorded calls have`} no answer, so ${t.length===1?"it's":"they're"} blocked: ${a.join("; ")}${r}.`}function be(){const t=[];const e=(a,r,n)=>{const i=t.findIndex(c=>c.part===a);if(i>=0)t[i]={part:a,ok:r,detail:n};else t.push({part:a,ok:r,detail:n})};return{steps:t,step:e}}function x(t){return`state: ${z().find(e=>e.id===t)?.label??t}`}async function ve(t,e){const a=new Date().toISOString();const{steps:r,step:n}=be();const i=e.replay!==false;if(e.stillWanted&&!e.stillWanted()){n("app",false,"The Bug Links screen closed, so Buoy stopped loading this bug.");return{loadedAt:a,steps:r,replay:null,loaded:false}}const c=e.otherApp?null:Pe(t.app,de());if(c){n("app",false,`${c} Its state could break this app, so nothing was loaded. Load it anyway only if this app shares its data.`);return{loadedAt:a,steps:r,replay:null,loaded:false}}const s=await pe();if(s){n("app",false,s);return{loadedAt:a,steps:r,replay:null,loaded:false}}await N();const u=!!o?.undo;const f=o?.undo??await He();if(u&&!b){const w=await $e(f);if(w){b=w.state;A=w.keys;g=w.late}}const k=(w,S,R)=>Fe(f,w,S,R);const h=me();const l=(t.note??"").split("\n")[0].slice(0,80)||(t.route?.pathname?`Bug on ${t.route.pathname.slice(0,70)}`:"Bug case");const p={replay:e.replay,clock:e.clock,sequence:e.sequence,otherApp:e.otherApp};if(!u){const w={source:e.source?.id?{id:e.source.id}:null,loadId:h,canLoadAgain:false,title:l,createdAt:t.createdAt,report:{loadedAt:a,steps:[],replay:null,loaded:false},undo:f,options:p,phase:"loading",block:i};if(!await X(w)&&Ce()){n("saved",false,"The phone couldn't save that a case is loading (its storage may be full), so nothing was loaded.");return{loadedAt:a,steps:r,replay:null,loaded:false}}}if(u&&o&&!await X({...o,phase:"loading"},i||K(o))&&Ce()){await O();n("saved",false,"The phone couldn't save that a case is loading (its storage may be full), so nothing new was loaded.");return{loadedAt:a,steps:r,replay:null,loaded:false}}const d=async()=>{if(!u)await T.removeItem(I.bugLinks.loaded()).catch(()=>{});else await O()};const m=await pe();if(m){await d();n("app",false,m);return{loadedAt:a,steps:r,replay:null,loaded:false}}if(e.stillWanted&&!e.stillWanted()){await d();n("app",false,"The Bug Links screen closed, so Buoy stopped loading this bug.");return{loadedAt:a,steps:r,replay:null,loaded:false}}if(!i&&o&&K(o)){if(!await B(f)){await d();n("web calls",false,"Couldn't put your Network rules back, so the last case's answers stay on. Nothing new was loaded.");return{loadedAt:a,steps:r,replay:null,loaded:false}}await X({...o,phase:"loading"},false)}let _=null;if(i){const w=async P=>{await d();n("web calls",false,`${P} Nothing was loaded, so no real server got this case's data. Load with replay off to use real web calls.`);return{loadedAt:a,steps:r,replay:null,loaded:false}};if(!$("network"))return w("The Network tool isn't installed, so Buoy can't block real web calls.");const S=je(t,{sequence:e.sequence});let R=null;if(u)R=await y("network","listOverrideRules").catch(()=>null);if(e.stillWanted&&!e.stillWanted()){await d();n("app",false,"The Bug Links screen closed, so Buoy stopped loading this bug.");return{loadedAt:a,steps:r,replay:null,loaded:false}}const M=await y("network","replaceOverrideRules",{rules:S.rules,enabled:true}).catch(P=>({ok:false,error:P instanceof Error?P.message:String(P),active:false}));if(!M||M.ok===false)return w(`Couldn't set up replay: ${M?.error??"unknown"}.`);if(M.active===false){if(!u)await B(f);return w("This build can't fake web calls (release builds never do).")}if(M.saved===false){if(!u)await B(f);else if(R?.rules){await y("network","replaceOverrideRules",{rules:R.rules,enabled:R.enabled!==false}).catch(()=>{})}return w("The phone couldn't save the block on real web calls (its storage may be full).")}_={answers:S.answers,skipped:S.skipped.length,active:true};n("web calls",true,`${S.answers} recorded answers. Every other call is blocked.${skippedText(S.skipped)}`)}if(e.clock!==false){const w=t.clock?.active?t.clock.virtualIso:t.createdAt;if(!$("clock")){if(t.clock?.active)n("clock",false,"The reporter's app had its clock set by Buoy, but the Clock tool isn't installed here, so the app keeps today's time.");else r.push({part:"clock",ok:true,warn:true,detail:"The Clock tool isn't installed, so the app keeps today's time."})}else if(typeof w==="string"&&!Number.isNaN(Date.parse(w))){await y("clock","setTime",{time:w,freeze:t.clock?.rate===0}).then(()=>n("clock",true,`App time set to ${readableTime(w)} (the reporter's time).`)).catch(S=>n("clock",false,`Couldn't set the clock: ${S instanceof Error?S.message:String(S)}`))}}const W=Ue(t,n);const Y=await Ie(W,n,k);const C=safeRoute(t.route?.href)??safeRoute(t.route?.pathname);if(C){if(!$("route-events")){n("screen",false,`The Routes tool isn't installed. Go to ${C} by hand.`)}else{await y("route-events","navigate",{path:C,reset:true}).catch(()=>y("route-events","navigate",{path:C,replace:true})).then(()=>n("screen",true,`Went to ${C}.`)).catch(w=>n("screen",false,`Couldn't go to ${C}: ${w instanceof Error?w.message:String(w)}`))}}else if(t.route){n("screen",false,"The case's screen isn't an app path, so Buoy didn't go there.")}await Se(W,Y,n,k);const ce=ze(t.app,de());if(ce.length){r.push({part:"this phone",ok:true,warn:true,detail:`Not like the reporter's, so a bug that needs these may not show. ${ce.join(" ")}`})}const D={loadedAt:a,steps:r,replay:_,loaded:true};o={source:e.source?.id?{id:e.source.id}:null,loadId:h,canLoadAgain:false,title:l,createdAt:t.createdAt,report:D,undo:f,options:p};let q=await O();const{canLoadAgain:G,onDisk:Re}=await Xe(h,t);if(G){o={...o,canLoadAgain:G};q=await O()&&q}if(!q&&_?.active){D.steps.push({part:"saved",ok:false,detail:"The phone couldn't save that a case is loaded. Restore your app before you restart it, or remove the bug case rules in the Network tool after."})}v();if(Y.size)void Ge(W,Y,h,k);const xe=G&&Re&&q&&f.stateSaved;if(e.restart&&xe){await H.setItem(F(),h).catch(()=>{});D.steps.push({part:"restart",ok:true,detail:"Restarting the app, then loading the case again."});void De("auto","bug links: load and restart").catch(()=>{void H.removeItem(F()).catch(()=>{})})}else if(e.restart){D.steps.push({part:"restart",ok:false,detail:G?"The phone couldn't save everything a restart needs, so Buoy didn't restart. Restore your app before you restart it by hand.":"This case is too big to keep on the phone, so Buoy can't load it again after a restart."})}return D}function Ue(t,e){try{const a=t.state?Z(JSON.parse(JSON.stringify(t.state)),{skipBadAnnotations:true}):{};if(a&&typeof a==="object"&&!Array.isArray(a)){const r=a;const n=r.storage;if(n&&typeof n==="object"&&!Array.isArray(n)&&"secure"in n){const{secure:i,...c}=n;r.storage=c}return r}e("state",false,"The case's state isn't an object, so none of it was loaded.")}catch{e("state",false,"The case's state couldn't be read.")}return{}}async function Ie(t,e,a){const r=new Map(Object.keys(t).map(n=>[n,{skipped:Infinity,ids:null}]));await ne(t,r,e,a);return r}async function ne(t,e,a,r){for(const n of z()){const i=e.get(n.id);if(!i||!n.status().canRestore)continue;try{const c=r?await r(n,t[n.id],i.ids):{ok:true,scope:i.ids??void 0,later:[]};if(!c.ok){e.delete(n.id);a?.(x(n.id),false,"Buoy couldn't save a copy of it first, so it wasn't loaded. (Restore couldn't have put it back.)");continue}const s=c.scope&&!c.scope.length?{ok:true,applied:0,skipped:[],warnings:[]}:await n.restore(t[n.id],c.scope?{includeKeys:c.scope}:void 0);if(c.unclaim)await c.unclaim(s.skipped.map(k=>k.id));const u=[...new Set([...s.skipped.map(k=>k.id),...c.later])];const f=u.length;if(f===0)e.delete(n.id);else e.set(n.id,{skipped:f,ids:u});if(a&&(i.skipped===Infinity||f===0)){a(x(n.id),s.ok,`${s.applied} loaded${f?`, ${f} not there yet`:""}.`)}}catch(c){e.delete(n.id);a?.(x(n.id),false,c instanceof Error?c.message:String(c))}}}async function Se(t,e,a,r){for(const i of ye){if(!e.size)break;await ae(i);await ne(t,e,a,r)}const n=new Map(z().map(i=>[i.id,i]));for(const[i,c]of e){const s=n.get(i);a(x(i),false,!s?"This app has no such source to load it into.":c.skipped!==Infinity?`${c.skipped} stores never mounted, so they weren't loaded.`:`It wasn't ready${s.status().detail?` (${s.status().detail})`:""}, so it wasn't loaded.`)}}const qe=2e4;async function Ge(t,e,a,r){const n=Date.now()+qe;while(e.size&&Date.now()<n){await ae(1e3);const i=await L(async()=>{if(o?.loadId!==a||!await N())return true;const c=new Map(e);const s=[];await ne(t,e,(h,l,p)=>s.push({part:h,ok:l,detail:p}),r);const u=[...c.keys()].some(h=>!e.has(h)||e.get(h).skipped<c.get(h).skipped);if(!u||!o||o.loadId!==a)return false;const f=new Set([...e.keys()].map(x));const k=o.report.steps.filter(h=>!s.some(l=>l.part===h.part)&&!(h.part.startsWith("state:")&&!h.ok&&!f.has(h.part)));o={...o,report:{...o.report,steps:[...k,...s]}};await O();v();return false},{quiet:true});if(i)return}}async function He(){const t={};const e={};for(const s of z()){if(!s.status().canCapture)continue;try{t[s.id]=(await s.capture()).data;e[s.id]=await oe(s,t[s.id])}catch{}}b=t;A=e;g=[];const a=me();const r=await V(a);const n=$("network")?await y("network","listOverrideRules").then(s=>({enabled:!!s?.enabled,rules:(s?.rules??[]).filter(u=>!Ke(u))})).catch(()=>null):null;const i=$("clock")?await y("clock","getState").then(s=>({active:!!s?.active,virtualIso:s?.virtualIso,rate:s?.rate})).catch(()=>null):null;const c=Ne();return{undoId:a,route:c?.href??c?.pathname??null,network:n,clock:i,stateSaved:r}}function Te(t){const e=t.storage;if(!e||typeof e!=="object"||Array.isArray(e)||!("secure"in e))return t;const{secure:a,...r}=e;return{...t,storage:r}}async function re(t){if(o){o={...o,notice:t};await O();v()}return{ok:false,detail:t}}async function B(t){if(!$("network"))return false;const e=await y("network","replaceOverrideRules",{rules:t?.network?.rules??[],enabled:t?.network?.enabled??true}).catch(()=>({ok:false}));return!!e&&e.ok!==false&&e.saved!==false}const se="Make sure the Network tool is on and you're signed in to Buoy, then try again.";export function leaveSandbox(){return L(async()=>{if(!await N())return{ok:true,detail:le};const t=o?.undo??(await U())?.undo;if(!await B(t))return re(`Web calls are still answered from the case. ${se}`);if(o){o={...o,phase:void 0,block:false,notice:void 0,report:{...o.report,replay:o.report.replay&&{...o.report.replay,active:false}}};await O();v()}return{ok:true}})}async function V(t){if(!b)return false;let e=false;try{const a=JSON.stringify({undoId:t,state:ue(Te(b??{})),keys:A,late:g.map(r=>({id:r.id,ids:r.ids,data:ue(Te({[r.id]:r.data})[r.id])}))});if(a.length<=we)e=await Q(j(),a)}catch{e=false}if(!e)await T.removeItem(j()).catch(()=>{});return e}async function $e(t){if(b)return{state:b,keys:A,late:g};if(!t.stateSaved)return void 0;try{const e=await T.getItem(j());if(!e)return void 0;const a=JSON.parse(e);if(a.undoId!==t.undoId||!a.state)return void 0;const r=Z(a.state);if(!r||typeof r!=="object")return void 0;const n=s=>Array.isArray(s)?s.filter(u=>typeof u==="string"):null;const i={};if(a.keys&&typeof a.keys==="object")for(const[s,u]of Object.entries(a.keys))i[s]=n(u);const c=Array.isArray(a.late)?a.late.flatMap(s=>typeof s?.id==="string"?[{id:s.id,ids:n(s.ids),data:Z(s.data)}]:[]):[];return{state:r,keys:i,late:c}}catch{return void 0}}async function oe(t,e){if(!t.previewItems)return null;try{return(await t.previewItems(e,e)).items.map(a=>a.key)}catch{return null}}function ie(t,e){return!!A[t]?.includes(e)||g.some(a=>a.id===t&&!!a.ids?.includes(e))}async function Ae(t,e,a){if(g.length>=ge||!e.status().canCapture)return;let r;try{r=(await e.capture()).data}catch{return}const n={id:e.id,ids:a,data:r};g.push(n);t.stateSaved=await V(t.undoId);return n}function Le(t,e){if(t.includes(e))return true;const a=e.indexOf("/");return e.startsWith("mmkv:")&&a>5&&t.includes(e.slice(0,a))}async function Fe(t,e,a,r){if(!b)return{ok:true,scope:r??void 0,later:[]};const n=e.id;if(!e.previewItems){if(r){const l=r.filter(d=>!ie(n,d));if(!l.length)return{ok:true,scope:r,later:[]};const p=await Ae(t,e,l);if(!p)return{ok:false,later:[]};return{ok:true,scope:r,later:[],unclaim:async d=>{if(!d.length||!p.ids)return;p.ids=p.ids.filter(m=>!Le(d,m));if(!p.ids.length)g.splice(g.indexOf(p),1);t.stateSaved=await V(t.undoId)}}}if(n in b||g.some(l=>l.id===n))return{ok:true,later:[]};return{ok:!!await Ae(t,e,null),later:[]}}let i;try{i=(await e.previewItems(a)).items}catch{return{ok:false,later:[]}}if(r)i=i.filter(l=>Le(r,l.key));const c=new Set(await oe(e,a)??i.map(l=>l.key));const s=i.filter(l=>l.verdict!=="wont-apply");const u=i.filter(l=>l.verdict==="wont-apply"&&c.has(l.key)).map(l=>l.key);const f=s.filter(l=>!ie(n,l.key));if(f.length){if(g.length>=ge||!e.status().canCapture)return{ok:false,later:[]};let l;try{l=(await e.capture()).data}catch{return{ok:false,later:[]}}const p=new Set(await oe(e,l)??[]);const d=f.filter(m=>p.has(m.key)||m.verdict==="added").map(m=>m.key);if(d.length){g.push({id:n,ids:d,data:l});t.stateSaved=await V(t.undoId)}}const k=s.map(l=>l.key).filter(l=>ie(n,l));const h=s.map(l=>l.key).filter(l=>!k.includes(l));return{ok:true,scope:k,later:[...u,...h]}}export function undoBugCase(){return L(async()=>{if(!await N())return[{part:"app",ok:true,detail:le}];return Ve()})}async function Oe(t,e,a){const r=new Map;const n=new Map(z().map(s=>[s.id,s]));const i=new Map;for(const s of t)i.set(s.id,(i.get(s.id)??0)+1);const c="isn't open now. Open its screen and tap Restore my app again, or tap End.";for(const[s,u]of[...t.entries()].reverse()){if(a&&!a.has(u))continue;const f=`${x(u.id)} (opened during the case${(i.get(u.id)??0)>1?`, copy ${s+1}`:""})`;const k=p=>{if(u.ids)r.set(u,p)};const h=n.get(u.id);if(!h||!h.status().canRestore){e(f,false,`It ${c}`);k(a?.get(u)??[]);continue}const l=a?.get(u);try{const p=await h.restore(u.data,l?.length?{includeKeys:l}:u.ids?{includeKeys:u.ids}:void 0);const d=p.skipped.map(m=>m.id);if(p.ok&&!d.length)e(f,true,`${p.applied} put back.`);else{e(f,false,`${d.length||"Some"} ${c}`);k(d)}}catch(p){e(f,false,p instanceof Error?p.message:String(p))}}return r}async function Ve(){const t=o??await U();const{steps:e,step:a}=be();if(!t)return e;const r=t.undo;const n=K(t);const i=async(d,m=false)=>{o={...t,undoBlocked:true,undoGone:m||t.undoGone,notice:d};await O();v();a("web calls",false,n?`${d} So web calls stay blocked. Tap End to keep the app as it is now and go back to real calls, or clear the app's data.`:`${d} Tap End to keep the app as it is now, or clear the app's data.`);return e};const c=r.undoId?await $e(r):void 0;if(!c)return i("The app's own state from before the load is gone.",true);const s=c.state;if(r.clock&&$("clock")){const d=r.clock.active&&r.clock.virtualIso?y("clock","setTime",{time:r.clock.virtualIso,freeze:r.clock.rate===0}).then(()=>r.clock.rate&&r.clock.rate!==1?y("clock","setRate",{rate:r.clock.rate}):void 0):y("clock","reset");await d.then(()=>a("clock",true,"The clock is back.")).catch(()=>{})}let u=await Oe(c.late,a);const f=async(d,m,_)=>{const W=c.keys[d.id];return{ok:true,scope:_??W??void 0,later:[]}};const k=await Ie(s,a,f);const h=safeRoute(r.route);let l=true;if(h&&$("route-events")){await y("route-events","navigate",{path:h,reset:true}).catch(()=>y("route-events","navigate",{path:h,replace:true})).catch(()=>{l=false})}await Se(s,k,a,f);for(const d of ye){if(!u.size)break;await ae(d);u=await Oe(c.late,a,u)}const p=e.filter(d=>!d.ok&&d.part.startsWith("state"));if(p.length||!l){return i(`${p.length?`${p.map(d=>d.part.replace("state: ","")).join(", ")} didn't come back.`:""}${l?"":" The app couldn't go back to its screen."}`.trim())}if(n){if(!await B(r))return i(`The app is back, but Buoy couldn't put your Network rules back. ${se}`);a("web calls",true,"Real web calls are back.")}o=null;b=void 0;g=[];A={};for(const d of[I.bugLinks.loaded(),J(),j()]){await T.removeItem(d).catch(()=>{})}v();return e}export function endBugCase(){return L(async()=>{if(!await N())return{ok:true,detail:le};const t=o??await U();const e=t?.undo.clock;if(e&&$("clock")){await(e.active&&e.virtualIso?y("clock","setTime",{time:e.virtualIso,freeze:e.rate===0}).then(()=>e.rate&&e.rate!==1?y("clock","setRate",{rate:e.rate}):void 0):y("clock","reset")).catch(()=>{})}const a=!!t&&K(t);if(a&&!await B(t.undo)){return re(`The case is still loaded and web calls stay blocked. ${se}`)}o=null;b=void 0;g=[];A={};for(const r of[I.bugLinks.loaded(),J(),j()]){await T.removeItem(r).catch(()=>{})}v();return{ok:true}})}async function Xe(t,e){try{const a=JSON.stringify({loadId:t,kase:e});if(a.length<=we){const r=await Q(J(),a);return{canLoadAgain:true,onDisk:r}}}catch{}await T.removeItem(J()).catch(()=>{});return{canLoadAgain:false,onDisk:false}}async function O(){if(!o)return false;E=await X(o);if(!E){const t=fe()?globalThis.localStorage:void 0;let e=null;try{e=t?t.getItem(I.bugLinks.loaded()):await T.getItem(I.bugLinks.loaded())}catch{e=null}E=!!e&&JSON.parse(e).loadId===o.loadId}return E}let E=true;function K(t){if(t.phase==="loading")return t.block===true;return t.options?.replay!==false&&!!t.report?.replay?.active}async function N(){if(!o||!fe())return true;if(!E)return true;let t;try{const a=globalThis.localStorage;t=a?a.getItem(I.bugLinks.loaded()):await T.getItem(I.bugLinks.loaded())}catch{return true}let e;if(t===null){if(!E)return true}else{try{e=JSON.parse(t).loadId}catch{return true}if(e===o.loadId)return true}o=null;b=void 0;g=[];A={};v();return false}const le="Another tab of this app already finished this bug case.";async function X(t,e){const a=e??K(t);try{return await Q(I.bugLinks.loaded(),JSON.stringify({...t,block:a}))}catch{return false}}function Ce(){if(typeof navigator!=="undefined"&&navigator.product==="ReactNative")return false;return typeof window!=="undefined"&&typeof document!=="undefined"}async function U(){try{const t=await T.getItem(I.bugLinks.loaded());return t?JSON.parse(t):null}catch{return null}}export function forgetLoadedForTests(){o=null;b=void 0;g=[];A={}}export async function restoreLoadedAfterReload(){const t=await L(async()=>{if(o)return false;const e=await U();if(!e)return false;const a=await H.getItem(F()).catch(()=>null);if(a){await H.removeItem(F()).catch(()=>{});if(a===e.loadId){o=e;v();return true}}o=e.undo?.undoId&&e.loadId?e:{...e,undoBlocked:true,undoGone:true};if(o.phase==="loading"){o={...o,notice:"This bug case didn't finish loading. Restore my app puts your app back the way it was."}}v();return false});if(t&&await Me(8e3)){const e=await loadAgain().catch(a=>({loaded:false,steps:[{part:"app",ok:false,detail:a instanceof Error?a.message:String(a)}]}));if(!e.loaded&&o){const a=e.steps.find(r=>!r.ok)?.detail??"The case didn't load again after the restart.";await L(async()=>{if(await N())await re(`${a} The app restarted with the case's saved state only.`)})}}return o}export function loadAgain(){return L(()=>Ye())}async function Ye(){const t=o??await U();if(!t)throw new Error("No bug case is loaded. Open the bug link again.");if(!t.canLoadAgain)throw new Error("This case was too big to keep on the phone. Open the bug link again.");let e=null;try{e=JSON.parse(await T.getItem(J())??"null")}catch{e=null}if(!e||e.loadId!==t.loadId)throw new Error("The saved case doesn't match the one loaded. Open the bug link again.");if(!o){o=t;v()}return ve(checkCase(e.kase),{...t.options,source:t.source})}export async function blockedCalls(){const t=o;if(!t?.report.replay?.active||!$("network"))return[];try{const e=await y("network","listOverrideRules");const a=e?.rules?.find(n=>n.name===he)?.id;if(!a)return[];const r=await We("network");return(r?.events??[]).filter(n=>n.override?.ruleId===a).map(n=>({method:n.method??"GET",url:n.url??"",at:n.timestamp??0})).sort((n,i)=>i.at-n.at).slice(0,50)}catch{return[]}}export{he as BLOCK_RULE_NAME};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{parseBugLink as u,unlockBugCase as a}from"@buoy-gg/shared-ui";import{fetchBlob as g}from"./client";import{currentCredential as c}from"./credential";import{loadBugCase as l}from"./loader";import{caseCrypto as k}from"./phoneCrypto";import{bugLinksServer as n,DEFAULT_SERVER as p,loadBugLinksSettings as f}from"./settings";function w(o){const t=/^(https?:\/\/[^/?#]+)\/bug\//i.exec(o.trim());return t?t[1].toLowerCase():null}export function trustedOrigin(o){const t=w(o);if(!t)throw new Error("That isn't a bug link. It looks like https://buoy.gg/bug/<id>#<key>.");const i=new Set([p,"https://www.buoy.gg",n()].map(r=>r.toLowerCase()));if(!i.has(t)){throw new Error(`This link is for ${t}. Buoy only sends your sign-in to ${n()}. If you trust ${t}, set it as the server in Bug links first.`)}return t}export async function fetchBugLink(o){const t=u(o);if(!t)throw new Error("That isn't a whole bug link. It should look like https://buoy.gg/bug/<id>#<key>.");await f();const i=trustedOrigin(o);const r=c();if(!r)throw new Error("Sign in to Buoy to open bug links.");const e=await g(i,t.id,r);const{kase:s}=await a(e,t.key,{crypto:k()});return{kase:s,id:t.id}}export async function openBugLink(o,t={}){const{kase:i,id:r}=await fetchBugLink(o);return l(i,{...t,source:{id:r}})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const u="buoy-bug-links-tab";const h="buoy-bug-links-step";let o=null;let c=false;function l(){if(typeof navigator!=="undefined"&&navigator.product==="ReactNative")return false;return typeof window!=="undefined"&&typeof document!=="undefined"}function f(){if(o)return c;try{return window.top!==window}catch{return true}}function i(){if(o)return o;if(!l())return null;return navigator.locks??null}export function tabsMatter(){return!!o||l()}let r=null;let s=false;export function listenForOtherTabs(){if(r)return r;const e=tabsMatter()?i():null;if(!e||f())return r=Promise.resolve();r=new Promise(t=>{void e.request(u,{mode:"shared"},()=>{s=true;t();return new Promise(()=>{})}).catch(()=>{s=false;t()})});return r}export async function tabsProblem(){if(!tabsMatter())return null;const e=i();if(!e)return"This browser can't keep the app's other tabs safe from a bug case (it has no Web Locks). Load it in a current browser.";if(f())return"This app is inside a frame. Open it in its own tab to load a bug case.";await listenForOtherTabs();let t;try{const b=await e.query();t=new Set((b.held??[]).filter(a=>a.name===u).map((a,d)=>a.clientId??`unknown-${d}`)).size}catch{return"Buoy couldn't count the app's tabs, so it didn't load the bug case."}const n=t-(s?1:0);if(n<=0)return null;return`This app is open in ${n===1?"another tab":`${n} other tabs`}. A bug case changes the data every tab shares, so close the other tabs, then load it.`}export function withStepLock(e){return withTabLock(h,e)}export function withTabLock(e,t){if(!tabsMatter())return t();const n=i();if(!n)return Promise.reject(new Error("This browser can't keep bug links safe across tabs. Update it to use bug links."));return n.request(e,{mode:"exclusive"},t)}export function setTabLocksForTests(e,t=false){o=e;c=t;r=null;s=false}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{Appearance as S,Dimensions as $,PixelRatio as i,Platform as s}from"react-native";import{bugCaseAppInfo as b}from"@buoy-gg/shared-ui";const h=.1;const u=o=>(o??"").split(".")[0];export function phoneDifferences(o,a){if(!o)return[];const n=[];const t=(d,f,e)=>n.push(`${d} was ${f}; here it's ${e}.`);if(o.id&&a.id&&o.id!==a.id)t("The app",o.id,a.id);if(o.platform&&a.platform&&o.platform!==a.platform)t("The platform",o.platform,a.platform);const r=!o.platform||!a.platform||o.platform===a.platform;const m=!!o.version&&!!a.version&&o.version!==a.version;const c=r&&!!o.build&&!!a.build&&o.build!==a.build;if(m||c){const d=!!o.version&&!!a.version;const f=e=>[d&&e.version,c&&e.build&&`build ${e.build}`].filter(Boolean).join(", ");t("The app version",f(o),f(a))}if(o.os&&a.os&&o.platform===a.platform&&u(o.os)!==u(a.os)){t(`${o.platform==="ios"?"iOS":o.platform==="android"?"Android":"The OS"}`,o.os,a.os)}if(o.locale&&a.locale&&o.locale!==a.locale)t("The language",o.locale,a.locale);if(o.timeZone&&a.timeZone&&o.timeZone!==a.timeZone)t("The time zone",o.timeZone,a.timeZone);if(o.appearance&&a.appearance&&o.appearance!==a.appearance){t("The phone",`in ${o.appearance} mode`,`in ${a.appearance} mode`)}const p=o.window;const l=a.window;if(p&&l&&p.width>0&&p.height>0&&l.width>0&&l.height>0){const d=(f,e)=>Math.abs(f-e)/f>h;if(d(p.width,l.width)||d(p.height,l.height)){t("The screen",`${Math.round(p.width)}x${Math.round(p.height)}`,`${Math.round(l.width)}x${Math.round(l.height)}`)}}if(p?.fontScale&&l?.fontScale&&Math.abs(p.fontScale-l.fontScale)>=.05){t("The text size",`${p.fontScale}x`,`${l.fontScale}x`)}return n}const g={ios:"an iOS app",android:"an Android app",macos:"a macOS app",windows:"a Windows app"};const w=o=>g[o]??`a ${o} app`;export function otherAppReason(o,a){if(!o)return null;const n=!o.platform||!a.platform||o.platform===a.platform;if(n&&o.id&&a.id&&o.id!==a.id)return`This case is from another app (${o.id}), and this is ${a.id}.`;const t=r=>r==="web";if(o.platform&&a.platform&&t(o.platform)!==t(a.platform)){return t(o.platform)?`This case is from a web app, and this is ${w(a.platform)}.`:`This case is from ${w(o.platform)}, and this is a web app.`}return null}export function thisPhone(){let o;let a;try{({locale:o,timeZone:a}=Intl.DateTimeFormat().resolvedOptions())}catch{}const n=$.get("window");return{platform:s.OS,os:String(s.Version),...b(),appearance:S.getColorScheme()??void 0,locale:o,timeZone:a,window:{width:n.width,height:n.height,scale:i.get(),fontScale:i.getFontScale()}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{gcm as c}from"@noble/ciphers/aes";import{p256 as s}from"@noble/curves/nist";import{hmac as g}from"@noble/hashes/hmac";import{sha256 as h}from"@noble/hashes/sha2";import{Gunzip as d,gzipSync as w}from"fflate";import{BugCaseLockError as f,webCaseCrypto as b,webTeamKeyCrypto as C}from"@buoy-gg/shared-ui";import{randomBytes as i}from"./randomBytes";export function phoneCaseCrypto(){return{randomBytes:i,seal:async(e,t,r,n)=>c(e,t,n).encrypt(r),open:async(e,t,r,n)=>c(e,t,n).decrypt(r),gzip:async e=>w(e,{level:6}),gunzip:async(e,t)=>gunzipCapped(e,t)}}export function caseCrypto(){try{return b()}catch{return phoneCaseCrypto()}}export function phoneTeamKeyCrypto(){return{randomBytes:i,sha256:async e=>h(e),hmacSha256:async(e,t)=>g(h,e.length?e:new Uint8Array(32),t),seal:async(e,t,r,n)=>c(e,t,n).encrypt(r),open:async(e,t,r,n)=>c(e,t,n).decrypt(r),p256Generate:async()=>{const e=s.utils.randomSecretKey(i(48));return{d:e,pub:s.getPublicKey(e,false)}},p256Ecdh:async(e,t)=>{if(!s.utils.isValidPublicKey(t,false))throw new Error("That public key isn't on the curve.");return s.getSharedSecret(e.d,t,true).slice(1)},p256Sign:async(e,t)=>{const r=s.sign(t,e.d,{prehash:true,lowS:false});return r instanceof Uint8Array?r:r.toCompactRawBytes()},p256Verify:async(e,t,r)=>s.verify(r,t,e,{prehash:true,lowS:false})}}export function teamKeyCrypto(){try{return C()}catch{return phoneTeamKeyCrypto()}}export function gunzipCapped(e,t){const r=[];let n=0;let p=false;const l=new d((o,m)=>{n+=o.length;if(n>t)throw new f("too_big",`This case unzips past ${t} bytes.`);r.push(o);if(m)p=true});const a=64*1024;for(let o=0;o<e.length;o+=a){l.push(e.subarray(o,o+a),o+a>=e.length)}if(!p)throw new f("bad_blob","This case ended before it finished unzipping.");const y=new Uint8Array(n);let u=0;for(const o of r){y.set(o,u);u+=o.length}return y}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{BugCaseLockError as l}from"@buoy-gg/shared-ui";const d=/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;export function randomSource(){const o=globalThis;if(typeof o.crypto?.getRandomValues==="function")return"getRandomValues";if(typeof o.expo?.uuidv4==="function")return"expo-uuid";return null}export const NO_RANDOM_MESSAGE="This app has no safe random numbers, so Buoy can't lock a bug case. Add expo-modules-core (any Expo app has it), react-native-get-random-values or react-native-quick-crypto.";export function randomBytes(o){const a=globalThis;const e=randomSource();if(e==="getRandomValues")return a.crypto.getRandomValues(new Uint8Array(o));if(e==="expo-uuid")return bytesFromUuids(o,()=>a.expo.uuidv4());throw new l("no_crypto",NO_RANDOM_MESSAGE)}export function bytesFromUuids(o,a){const e=new Uint8Array(o);let u=0;let s=0;let n=0;let i="";const p=(t,c)=>{s=s<<c|t;n+=c;while(n>=8&&u<o){n-=8;e[u++]=s>>n&255}s&=(1<<n)-1};while(u<o){const t=String(a());if(!d.test(t)||t===i){throw new l("no_crypto","Expo's UUID source gave a value that isn't a fresh v4 UUID.")}i=t;const c=t.replace(/-/g,"");for(let r=0;r<32&&u<o;r++){if(r===12)continue;const f=parseInt(c[r],16);if(r===16)p(f&3,2);else p(f,4)}}return e}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{bugLinkUrl as me,captureBugCase as le,copyToClipboard as be,lockBugCase as N,base64UrlDecode as ne,base64UrlEncode as j,scrubBugCase as ve,ScrubLimitError as Te,BugCaseLockError as re,unlockBugCase as J,newCaseKeyParts as ke,sealCard as ie,wrapCaseKey as se}from"@buoy-gg/shared-ui";import{BUG_PRIORITIES as Se,BugLinksHttpError as p,deleteRemote as ae,deleteUpload as q,replaceBlob as Be,sendPriority as _e,sendWrap as Pe,uploadBlob as xe}from"./client";import{credentialOwner as M,currentCredential as C}from"./credential";import{addMyLink as Oe,findMyLink as o,loadMyLinks as oe,myLinks as Ce,hasHeldUploadResult as Ee,holdUploadResult as ue,isUnsavedNew as P,readBlobChecked as de,saveRowNow as V,saveHeldUploadResults as Le,reloadMyLinks as Re,removeMyLink as ce,updateMyLink as a,writeBlob as fe}from"./linksStore";import{tabsMatter as B,withTabLock as Me}from"./otherTabs";import{caseCrypto as A,teamKeyCrypto as $}from"./phoneCrypto";import{pickTeamKey as G}from"./teamKeys";import{randomBytes as Ae}from"./randomBytes";import{bugLinksRules as we,bugLinksServer as X,bugLinksSettingsRead as We,loadBugLinksSettings as ge}from"./settings";import{addMediaToCase as Fe,resumeMedia as De,shrinkMedia as he}from"../media/frames";const Ke=7.5*1024*1024;let x=null;let W=null;const z=new Set;const F=new Map;const E=new Map;const v=new Set;function L(e,n){const t=E.get(e)??Promise.resolve(null);const r=()=>Me(`buoy-bug-links-link-${e}`,async()=>{if(B())await Re();return n()});const s=t.then(r,r);const d=s.then(()=>o(e)??null,()=>o(e)??null);E.set(e,d);void d.finally(()=>{if(E.get(e)===d)E.delete(e)});return s}const T=[5e3,3e4,12e4,6e5,18e5];const ye=1500;export async function copyWithin(e,n){let t;const r=new Promise(s=>{t=setTimeout(()=>s(false),n)});try{return await Promise.race([be(e).catch(()=>false),r])}finally{clearTimeout(t)}}export function reportState(){return x}export function subscribeReportState(e){z.add(e);return()=>z.delete(e)}export function dismissReportState(){x=null;pe()}function Q(e){x=e;pe()}function pe(){for(const e of z)e()}function Ue(){return`bl_${Date.now().toString(36)}_${Math.floor(Math.random()*1e9).toString(36)}`}function Ve(e){const n=e.console?.find(t=>t.level==="error")?.text;return{note:e.note||void 0,app:e.app?.id||void 0,version:e.app?.version||void 0,build:e.app?.build||void 0,route:e.route?.pathname||void 0,error:n?n.split("\n")[0]:void 0}}async function He(e,n,t,r){if(!e.caseTag||!e.card)return e.priority?{extras:{priority:e.priority}}:{};const s=$();const d={...e.card,note:(await J(n,e.key,{crypto:A()})).kase.note||void 0};const u=await ie(s,e.key,e.caseTag,1,d,n);const m=await G(t,r);const R=m.kind==="ok"?JSON.stringify(await se(s,m.target,e.key,e.caseTag)):void 0;return{extras:{card:u,wrap:R,owner:e.teamOwner,priority:e.priority},pick:m}}function Z(e){return e?.kind==="ok"?e.target.owner:e?.owner}function Ne(e,n,t,r){if(!e.caseTag||t==="saved")return{wrapPending:false,wrapWait:void 0};if(r&&(!n||n.kind==="off")){return{wrapPending:true,wrapWait:{code:"no_current",message:"Your team turned on its key. Buoy locks this bug to it soon."}}}if(!n||n.kind==="off")return{wrapPending:false,wrapWait:void 0};if(n.kind==="wait")return{wrapPending:true,wrapWait:{code:n.code,message:n.message,teamKeyId:n.teamKeyId}};return{wrapPending:true,wrapWait:{code:"no_current",message:"Your team's key changed. Buoy locks this bug to the new one soon."}}}function D(e,n,t,r={}){if(v.has(e))return;if(x&&x.localId!==e&&x.localId!==null)return;const s=o(e);Q({localId:e,phase:n,message:t,removed:s?.removed.length??0,url:s?.url,...r})}export function reportBug(e={}){if(W)return W;W=(async()=>{Q({localId:null,phase:"capturing",message:"Saving the bug\u2026",removed:0});try{await Promise.all([oe(),ge()]);let n=await Fe(await le({note:e.note,rules:we()}));const t=await ke($());let r;try{r=await N(n,A(),t)}catch(u){if(!(u instanceof re&&u.code==="too_big"&&n.media))throw u;n=he(n,0);r=await N(n,A(),t)}for(const u of[2*1024*1024,512*1024,0]){if(r.blob.length<=Ke||!n.media)break;n=he(n,u);r=await N(n,A(),t)}const s=Ue();const d=await Oe({localId:s,key:r.key,header:r.header,note:n.note,priority:e.priority&&e.priority!=="normal"?e.priority:void 0,screen:n.route?.pathname,createdAt:n.createdAt,status:"sending",attempts:0,server:X(),owner:M(C())??void 0,removed:n.scrub.items,caseTag:t.caseTag,keyProofHash:t.keyProofHash,card:Ve(n),cardRevision:1},j(r.blob));if(!d){await a(s,{error:"This phone's storage is full, so the case lives only while the app stays open."})}return await sendLink(s)}catch(n){const t=n instanceof Te?"This bug was too big to check for private data, so nothing was sent.":n instanceof re?n.message:`Couldn't save the bug: ${n instanceof Error?n.message:String(n)}`;Q({localId:null,phase:"failed",message:t,removed:0});return null}finally{W=null;De()}})();return W}export function sendLink(e,n={}){const t=E.get(e);if(t&&!n.asMe)return t;return L(e,()=>Y(e,n)).then(async r=>{if(r?.status==="sent"&&r.priorityPending)return await setLinkPriority(e).catch(()=>null)??o(e)??r;return r})}async function Y(e,n){await ge();let t=o(e);if(!t){v.delete(e);return null}if(t.status==="deleting")return te(t);if(t.id&&!t.needsPut){if(Ee(e)&&!await Le())S(e,T[1]);return o(e)??t}I(e);if(!t.id&&!We()){return g(t,"Buoy couldn't read its settings just now. It will try again.","storage_read")}const r=t.id?t.server??X():X();let s;try{s=await de(e)}catch{return g(t,"Buoy couldn't read this saved bug just now. It will try again.","storage_read")}const d=s?ne(s):null;if(!d){await a(e,{status:"failed",error:"The saved case is gone from this phone.",errorCode:"lost"});D(e,"failed","The saved case is gone from this phone.");return o(e)??null}if(t.id&&t.needsPut){const i=C();const h=i?M(i):null;if(h&&t.owner&&h!==t.owner&&!n.asMe){return g(t,"Someone else is signed in now. Open Bug links and tap Send as me to add the note anyway.","other_account",false)}const f=t.blobVersion??0;const y=t;let l;if(y.card){const{kase:c}=await J(d,y.key,{crypto:A()});l={...y.card,note:c.note||void 0}}const _=async c=>{let b;if(y.caseTag&&l){const w=await ie($(),y.key,y.caseTag,c,l,d);b={card:w,cardRevision:c}}return Be(r,y.id,y.editToken??"",d,b)};try{let c;try{c=await _((t.cardRevision??1)+1)}catch(H){const U=H instanceof p&&H.code==="conflict"?H.detail.cardRevision:void 0;if(typeof U!=="number")throw H;if(U!==t.cardRevision)t=await a(e,{cardRevision:U})??{...t,cardRevision:U};c=await _(U+1)}const{expiresAt:b,cardRevision:w}=c;const k=o(e);return await a(e,{needsPut:(k?.blobVersion??0)!==f,...l&&(k?.blobVersion??0)===f?{card:l,note:l.note}:{},expiresAt:b===void 0?t.expiresAt:b,cardRevision:w??t.cardRevision,error:void 0,errorCode:void 0,attempts:0})??null}catch(c){if(c instanceof p&&c.retry)return g(t,"The note will reach the server when this phone is back online.",c.code);if(c instanceof p&&c.code==="conflict")return g(t,"The bug changed on the server at the same time. Buoy will send the note again.","conflict");const b=c instanceof Error?c.message:String(c);return g(t,`${b} Buoy will send the note again.`,c instanceof p?c.code:"error")}}const u=C();if(!u)return g(t,"Sign in to Buoy to send this bug. It's saved on the phone.","signed_out");const m=M(u);if(!t.owner&&m){const i=await a(e,{owner:m});if(B()&&!P(e)&&i?.owner!==m){return g(t,"The browser's storage is full, so Buoy can't keep track of this send. Free some space; it will try again.","storage_full")}t=i??{...t,owner:m}}if(t.owner&&m!==t.owner){if(!n.asMe){return g(t,"Someone else is signed in now. Open Bug links and tap Send as me to send it anyway.","other_account",false)}const i=await a(e,{owner:m??void 0,teamOwner:void 0});if(B()&&!P(e)&&(i?.owner!==(m??void 0)||i?.teamOwner!==void 0)){return g(t,"The browser's storage is full, so Buoy can't keep track of this send. Free some space; it will try again.","storage_full")}t=i??{...t,owner:m??void 0,teamOwner:void 0}}const R=t.removed.length;if(!t.id&&t.triedServer&&t.triedServer!==r){try{await q(t.triedServer,e,u)}catch(i){if(i instanceof p&&i.code==="signed_out"){return g(t,"Buoy couldn't sign you in to the old server to move this bug. It's saved on the phone.","signed_out")}if(i instanceof p&&i.retry&&i.status!==void 0){return g(t,"Buoy needs the old server once more before it moves this bug. It will try again.",i.code)}}if(t.teamOwner){const i=await a(e,{teamOwner:void 0});if(B()&&!P(e)&&i?.teamOwner!==void 0){return g(t,"The browser's storage is full, so Buoy can't keep track of this send. Free some space; it will try again.","storage_full")}t=i??{...t,teamOwner:void 0}}}D(e,"sending","Sending the bug link\u2026");const K=await a(e,{status:"sending",server:r,triedServer:r});if(B()&&!P(e)&&K?.triedServer!==r){return g(t,"The browser's storage is full, so Buoy can't keep track of this send. Free some space; it will try again.","storage_full")}if(v.has(e)||!o(e)||o(e)?.status==="deleting"){v.delete(e);const i=o(e);return i?te(i):null}const O="The browser's storage is full, so Buoy can't keep track of this send. Free some space; it will try again.";if(!t.editToken){const i=j(Ae(32));const h=await a(e,{editToken:i});if(B()&&!P(e)&&h?.editToken!==i)return g(t,O,"storage_full");t=h??{...t,editToken:i}}try{if(t.caseTag&&!t.teamOwner){const w=Z(await G(r,u,{fresh:true}));if(w){const k=await a(e,{teamOwner:w});if(B()&&!P(e)&&k?.teamOwner!==w)return g(t,O,"storage_full");t=k??{...t,teamOwner:w}}}if(!await V(e))return g(t,O,"storage_full");const{extras:i,pick:h}=await He(t,d,r,u);const f=await xe(r,u,d,{uploadId:e,editToken:t.editToken,extras:i});const y=async()=>{const w={id:f.id,editToken:f.editToken,server:r,status:"deleting"};const k=o(e)?await a(e,w):void 0;if(k&&k.id!==f.id)ue(e,w);if(k&&!await V(e)){S(e,T[1]);return null}try{await ae(r,f.id,{editToken:f.editToken});v.delete(e);if(o(e)&&!await ce(e))S(e,T[1])}catch{if(k)S(e,T[1])}return null};if(v.has(e)||!o(e))return y();const l=me(r,f.id,t.key);const _={id:f.id,editToken:f.editToken,expiresAt:f.expiresAt,url:l,status:"sent",error:void 0,errorCode:void 0,nextTryAt:void 0,attempts:0,cardRevision:f.cardRevision??1,...Ne(t,h,f.wrapStatus,f.teamKeys),...!t.teamOwner&&f.owner?{teamOwner:f.owner}:{}};const c=await a(e,_);if(B()&&!P(e)&&c?.id!==f.id){ue(e,_);S(e,T[0]);const w=await copyWithin(l,ye);D(e,"sent",w?"Bug link copied":"Bug link ready",{url:l,removed:R,copied:w});return o(e)??{...t,..._}}if(!c||v.has(e)||c.status==="deleting")return y();const b=await copyWithin(l,ye);D(e,"sent",b?"Bug link copied":"Bug link ready",{url:l,removed:R,copied:b});if(o(e)?.needsPut)return Y(e,n);return c??null}catch(i){if(v.has(e)){v.delete(e);S(e,T[0]);return null}if(i instanceof p&&i.retry)return g(t,i.message,i.code);if(i instanceof p&&i.code==="signed_out"){return g(t,"Buoy couldn't sign you in to send this bug. It's saved on the phone.","signed_out")}if(i instanceof p&&i.code==="conflict"&&i.status===409&&!o(e)?.id){try{await q(r,e,u)}catch(y){return g(t,y instanceof Error?y.message:String(y),y instanceof p?y.code:"error")}return g(t,"Buoy is sending this bug again.","conflict")}const h=i instanceof p&&i.code==="needs_pro"?"Bug links need Buoy Pro. The case is saved on this phone.":i instanceof Error?i.message:String(i);const f=i instanceof p?i.code:"error";await a(e,{status:"failed",error:h,errorCode:f});D(e,"failed",h,{code:f});return o(e)??null}}async function g(e,n,t,r=true){const s=o(e.localId)??e;const d=s.attempts+1;const u=T[Math.min(d-1,T.length-1)];await a(e.localId,{status:s.id||s.status==="deleting"?s.status:"waiting",error:n,errorCode:t,attempts:d,nextTryAt:r?Date.now()+u:void 0});if(r)S(e.localId,u);D(e.localId,"waiting",n,{code:t});return o(e.localId)??null}function S(e,n){I(e);F.set(e,setTimeout(()=>{F.delete(e);void sendLink(e)},n))}function I(e){const n=F.get(e);if(n)clearTimeout(n);F.delete(e)}export async function retryWaiting(){await oe();for(const{localId:e}of Ce()){try{let n=o(e);if(!n)continue;if(n.errorCode==="other_account"&&n.status!=="deleting")continue;if(n.status==="waiting"||n.status==="deleting"||n.needsPut)await sendLink(e);else if(n.status==="sent"&&n.wrapPending)await wrapLater(e);n=o(e);if(n?.status==="sent"&&n.priorityPending)await setLinkPriority(e)}catch{}}}export function wrapLater(e){return L(e,async()=>{const n=o(e);if(!n?.id||!n.wrapPending||!n.caseTag||!n.editToken||!n.server||n.status!=="sent")return n??null;const t=C();if(!t||n.owner&&M(t)!==n.owner){return await a(e,{wrapWait:{code:"other_account",message:"Sign in as the person who sent this bug, so Buoy can lock it to their team."}})??null}const r=await G(n.server,t,{fresh:true});if(n.teamOwner&&Z(r)&&Z(r)!==n.teamOwner){return await a(e,{wrapWait:{code:"team_changed",message:"Your team changed since this bug was sent. Only its link opens it."}})??null}if(r.kind==="off"){if(r.owner&&n.teamOwner&&r.owner===n.teamOwner)return await a(e,{wrapPending:false,wrapWait:void 0})??null;return await a(e,{wrapWait:{code:"off",message:"Your team list isn't reachable right now. Buoy will try again."}})??null}if(r.kind==="wait")return await a(e,{wrapWait:{code:r.code,message:r.message,teamKeyId:r.teamKeyId}})??null;try{const s=await se($(),r.target,n.key,n.caseTag);await Pe(n.server,n.id,n.editToken,JSON.stringify(s),n.caseTag);return await a(e,{wrapPending:false,wrapWait:void 0})??null}catch(s){const d=s instanceof Error?s.message:String(s);return await a(e,{wrapWait:{code:s instanceof p?s.code:"error",message:d}})??null}})}export async function setLinkPriority(e,n){if(n){const t=await a(e,r=>r.status==="deleting"?null:{priority:n,priorityPending:true});if(!t||t.status==="deleting")return t??null}return L(e,async()=>{const t=o(e);if(!t||!t.priorityPending)return t??null;if(!t.id||!t.editToken||!t.server||t.status!=="sent")return t;if(t.owner&&M(C())!==t.owner){throw new Error("Someone else is signed in now. Sign in as the person who sent this bug to change its priority.")}const r=t.priority??"normal";try{await _e(t.server,t.id,t.editToken,r);return await a(e,s=>(s.priority??"normal")===r?{priorityPending:false}:null)??null}catch(s){if(s instanceof p&&s.code==="priority_set"){const d=Se.find(u=>u===s.detail.priority);await a(e,{priorityPending:false,...d?{priority:d}:{}});throw new Error("A teammate already set this bug's priority, so yours wasn't changed.")}if(s instanceof p&&(s.retry||s.code==="conflict"))return t;await a(e,d=>(d.priority??"normal")===r?{priorityPending:false}:null);throw s}})}export async function addNote(e,n){const t=await L(e,()=>$e(e,n));if(t?.id)return L(e,()=>Y(e,{}));return t}async function $e(e,n){const t=o(e);if(!t)return null;let r;try{r=await de(e)}catch{throw new Error("Buoy couldn't read this saved bug just now. Try again.")}const s=r?ne(r):null;if(!s)throw new Error("This case is no longer on the phone, so a note can't be added here.");const d=A();const{kase:u}=await J(s,t.key,{crypto:d});const m=ve({note:n.trim().slice(0,4e3)},we());u.note=m.value.note||void 0;const R=u.scrub.items.filter(w=>!w.path.startsWith("$.note"));const K=[...R,...m.removed.map(w=>({...w,path:`$.note${w.path.slice(1)}`}))];const O={};for(const w of K)O[w.kind]=(O[w.kind]??0)+1;u.scrub={removed:K.length,byKind:O,items:K};const i=await N(u,d,t.caseTag&&t.keyProofHash?{key:t.key,caseTag:t.caseTag,keyProofHash:t.keyProofHash}:{key:t.key});const h=o(e);if(!h||h.status==="deleting")return null;const f=(h.blobVersion??0)+1;const y="Buoy couldn't save the note (the storage may be full), so it wasn't added.";if((h.id||h.triedServer)&&!h.needsPut){const w=await a(e,{needsPut:true});if(!await ee(e,w,k=>!!k.needsPut))throw new Error(y)}else if(!await V(e)){throw new Error(y)}const l=B()&&!P(e);const _=async()=>{if(r)await fe(e,r,h.blobVersion??0);if((h.id||h.triedServer)&&!h.needsPut)await a(e,{needsPut:false})};const c=await fe(e,j(i.blob),f);if(!c&&l){await _();throw new Error(y)}const b=await a(e,{note:u.note,card:t.card?{...t.card,note:u.note||void 0}:void 0,header:i.header,removed:u.scrub.items,needsPut:!!h.id||!!h.triedServer,blobVersion:f,...c?{}:{error:"The phone couldn't save the note. It goes up if the app stays open."}});if(l&&b?.blobVersion!==f){await _();throw new Error(y)}return b??null}export async function deleteLink(e){const n=o(e);if(!n)return;I(e);if(x?.localId===e)dismissReportState();if(E.has(e)&&!n.id){v.add(e);const t=await a(e,{status:"deleting",errorCode:void 0});if(!await ee(e,t,r=>r.status==="deleting")){v.delete(e);throw new Error("The storage is full, so Buoy can't keep track of this cancel. Free some space, then cancel it again.")}void L(e,()=>Y(e,{}));return}await L(e,async()=>{const t=o(e);if(!t)return null;const r=t.status==="deleting"?t:await a(e,{status:"deleting",errorCode:void 0,nextTryAt:void 0});if(!await ee(e,r,s=>s.status==="deleting")){throw new Error("The storage is full, so Buoy can't keep track of this delete. Free some space, then delete it again.")}return r?te(r):null})}async function ee(e,n,t){if(B())return P(e)||!!n&&t(n);return V(e)}async function te(e){v.delete(e.localId);if(!await V(e.localId)){S(e.localId,T[1]);return o(e.localId)??e}if(!e.id&&e.takenBackOnce&&e.nextTryAt&&e.nextTryAt>Date.now()){S(e.localId,e.nextTryAt-Date.now());return e}try{if(e.id&&e.server){await ae(e.server,e.id,{editToken:e.editToken,credential:C()??void 0})}else if(e.triedServer){const n=C();if(!n)return g(e,"Sign in to Buoy to finish deleting this bug link.","signed_out");if(e.owner&&M(n)!==e.owner){return g(e,"Sign in as the person who sent this bug to finish deleting it.","other_account",false)}await q(e.triedServer,e.localId,n);if(!e.takenBackOnce){const t=await a(e.localId,{takenBackOnce:true,error:void 0,errorCode:void 0,nextTryAt:Date.now()+T[2]});S(e.localId,T[2]);return t??null}}if(!await ce(e.localId)){S(e.localId,T[1]);return o(e.localId)??null}return null}catch(n){if(n instanceof p&&n.code==="signed_out"){return g(e,"Sign in to Buoy to finish deleting this bug link.","signed_out")}const t=n instanceof p&&n.retry;if(t)S(e.localId,T[1]);await a(e.localId,{error:n instanceof Error?n.message:String(n),errorCode:n instanceof p?n.code:"error"});return o(e.localId)??null}}export function resetReporterForTests(){for(const e of F.values())clearTimeout(e);F.clear();E.clear();v.clear();x=null;W=null}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";export const SANDBOX_PREFIX="Bug case: ";export const BLOCK_RULE_NAME=`${SANDBOX_PREFIX}blocked (no recorded answer)`;const w=49;const D=1e4;const N=/^(content-length|content-encoding|transfer-encoding|set-cookie|connection|keep-alive)$/i;const H=/\[removed:[a-z]+\]/g;const y=/\[removed:[a-z]+\]/;const J=/^(_|t|ts|time|timestamp|cb|cachebust|cachebuster|nocache|rnd|rand|random|nonce|bust)$/i;export function urlGlob(t){const r=t.replace(H,"*");const n=r.indexOf("?");if(n<0)return r;const s=r.indexOf("#",n);const i=r.slice(n+1,s<0?void 0:s);const m=i.split("&").map(d=>{const f=d.indexOf("=");return f>0&&J.test(d.slice(0,f))?`${d.slice(0,f)}=*`:d});return`${r.slice(0,n+1)}${m.join("&")}${s<0?"":r.slice(s)}`}function U(t){if(!t)return false;const r=Object.entries(t).find(([n])=>n.toLowerCase()==="content-type")?.[1]??"";return/^application\/(?:[\w.-]+\+)?json\b/i.test(r.trim())}function _(t,r,n){if(t===void 0)return void 0;if(n===true){try{return JSON.stringify(t)}catch{return void 0}}if(typeof t==="string")return n===false||!U(r)?t:JSON.stringify(t);if(t===null)return"null";try{return JSON.stringify(t)}catch{return void 0}}export function graphqlOperation(t){const r=t;if(!r||typeof r!=="object"||Array.isArray(r))return void 0;const n=r;const s=!!n.extensions&&typeof n.extensions==="object"&&!Array.isArray(n.extensions);if(typeof n.query!=="string"&&!s)return void 0;const i=n.operationName;return typeof i==="string"&&/^[A-Za-z0-9_]{1,100}$/.test(i)?i:void 0}function S(t){const r=n=>Array.isArray(n)?n.map(r):n&&typeof n==="object"?Object.fromEntries(Object.keys(n).sort().map(s=>[s,r(n[s])])):n;return JSON.stringify(r(t))??"null"}export function graphqlVariablesKey(t){const r=M(t);return r!==void 0&&r.length<=4e3&&!y.test(r)?r:void 0}function B(t){const r=n=>{if(!n||typeof n!=="object"||Array.isArray(n))return false;const s=n;return typeof s.query==="string"||!!s.extensions&&typeof s.extensions==="object"&&!Array.isArray(s.extensions)};return Array.isArray(t)?t.some(r):r(t)}function M(t){const r=t;const n=r&&typeof r==="object"?r.variables??{}:void 0;return n===void 0?void 0:S(n)}const C=4e3;function P(t){if(t===void 0)return{kind:"none"};return{kind:"json",value:t}}function T(t,r){let n;if(r)n=M(t.requestData)??"{}";else{const s=P(t.requestData);if(s.kind==="none")return{raw:"\0none",why:"it had no body, and other calls to this URL had one"};n=S(s.value)}if(y.test(n))return{raw:n,why:"its input had private data taken out, so it can't be told apart from the others"};if(n.length>C)return{raw:n,why:"its input is too long to match"};return{raw:n,key:n}}const z=/^(application\/(octet-stream|pdf|zip|gzip|x-protobuf|protobuf|x-msgpack|msgpack|wasm|vnd\.[^;]*)|image\/(?!svg)|audio\/|video\/|font\/)/i;const L=/\+(json|xml|yaml|csv|text)\b|^text\//i;function X(t){const r=Object.entries(t.responseHeaders??{}).find(([s])=>s.toLowerCase()==="content-type")?.[1];if(typeof r!=="string")return false;const n=r.trim();return z.test(n)&&!L.test(n)}function v(t){if(!t.responseHeaders)return void 0;const r={};for(const[n,s]of Object.entries(t.responseHeaders)){if(typeof s!=="string"||N.test(n)||y.test(s))continue;r[n]=s}return Object.keys(r).length?r:void 0}function I(t){const r=t.url.replace(/^[a-z]+:\/\/[^/]+/i,"").split(/[?#]/)[0]||"/";return`${t.method.toUpperCase()} ${r.slice(0,60)}`}function V(t){const r=t.durationMs?Math.min(D,Math.max(0,Math.round(t.durationMs))):void 0;if(t.error){return{kind:"fail",failKind:/time(d)?[ -]?out/i.test(t.error)?"timeout":"network",delayMs:r}}return{kind:"respond",status:Math.min(599,Math.max(200,t.status??200)),headers:v(t),body:_(t.responseData,t.responseHeaders,t.responseJson)??"",delayMs:r}}function q(t){return t.urlPattern.replace(/\*/g,"").length+(t.graphqlOperation?1e5:0)+(t.graphqlVariables!==void 0?2e5:0)+(t.bodyJson!==void 0?4e5:0)}export function buildSandbox(t,r={}){const n=[...t.network??[]].reverse();const s=[];const i=new Map;n.forEach((e,o)=>{if(!e||typeof e.url!=="string"||typeof e.method!=="string")return;let a=true;if(e.bodyOmitted&&!e.error){s.push({method:e.method,url:e.url,reason:"its answer was too big to record"});a=false}else if(!e.error&&X(e)){s.push({method:e.method,url:e.url,reason:"its answer was binary, and Buoy records answers as text"});a=false}else if(typeof e.status!=="number"&&!e.error){s.push({method:e.method,url:e.url,reason:"it had no answer yet when the bug was sent"});a=false}const u=e.method.toUpperCase();const c=graphqlOperation(e.requestData);const h=T(e,c);const A=urlGlob(e.url);const O=`${u} ${A} ${c??""}`;const b=`${O} ${h.raw}`;const E=i.get(b)??{key:b,family:O,pattern:A,method:u,op:c,input:h,calls:[]};E.calls.push({call:e,at:o,usable:a});i.set(b,E)});const m=e=>`${e.call.status??""} ${e.call.error??""} ${_(e.call.responseData,e.call.responseHeaders,e.call.responseJson)??""}`;for(const[e,o]of[...i]){if(!y.test(o.input.raw)||o.calls.length<2)continue;const a=o.calls.filter(u=>u.usable);if(a.length===o.calls.length&&new Set(a.map(m)).size<=1)continue;i.delete(e);o.calls.forEach((u,c)=>i.set(`${e} #${c}`,{...o,key:`${e} #${c}`,calls:[u]}))}const d=new Map;for(const e of i.values())d.set(e.family,(d.get(e.family)??0)+1);const f=new Set([...d].filter(([,e])=>e>1).map(([e])=>e));const p=e=>`${e.method} ${e.pattern}`;const g=new Map;for(const e of i.values())g.set(p(e),(g.get(p(e))??0)+1);for(const e of i.values()){if(e.op)continue;if((g.get(p(e))??0)>1||e.calls.some(o=>B(o.call.requestData)))f.add(e.family)}const x=new Set;for(const e of i.values()){if(e.calls.some(o=>o.call.bodyOmitted&&o.call.requestData===void 0&&!/^(GET|HEAD)$/.test(e.method)))x.add(p(e))}for(const e of i.values())if(x.has(p(e)))f.add(e.family);for(const[e,o]of i){const a=o.calls.filter(u=>u.usable);if(f.has(o.family)&&o.input.key===void 0){for(const{call:u}of a)s.push({method:u.method,url:u.url,reason:o.input.why??"its input can't be matched"});i.delete(e)}else if(!a.length)i.delete(e);else o.calls=a}const $=[];let j=0;for(const e of i.values()){const o=r.sequence?e.calls:e.calls.slice(-1);o.forEach(({call:a,at:u},c)=>{const h=c===o.length-1;$.push({at:u,order:j++,rule:{name:`${SANDBOX_PREFIX}${I(a)}${o.length>1?` (${c+1} of ${o.length})`:""}`,enabled:true,sandbox:true,urlPattern:e.pattern,methods:[e.method],graphqlOperation:e.op,...e.op?{graphqlSingle:true}:{},...f.has(e.family)?e.op?{graphqlVariables:e.input.key}:{bodyJson:e.input.key}:{},times:h?null:1,...V(a)}})})}let l=[...$];if(l.length>w){const e=[...l].sort((o,a)=>a.at-o.at||(a.rule.times===null?0:1)-(o.rule.times===null?0:1));l=e.slice(0,w);for(const o of e.slice(w)){s.push({method:o.rule.methods?.[0]??"?",url:o.rule.urlPattern,reason:"over the 50-rule limit"})}}l.sort((e,o)=>q(o.rule)-q(e.rule)||e.order-o.order);const k=l.map(e=>e.rule);const R=l.length;k.push({name:BLOCK_RULE_NAME,enabled:true,sandbox:true,urlPattern:"*",kind:"fail",failKind:"network",times:null});return{rules:k,answers:R,skipped:s}}export function isSandboxRule(t){return typeof t?.name==="string"&&t.name.startsWith(SANDBOX_PREFIX)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{devToolsStorageKeys as f,getItemChecked as p,setItemChecked as h}from"@buoy-gg/shared-ui";import{configureMedia as m}from"../media/frames";export const DEFAULT_SERVER="https://buoy.gg";let s={};let n={};let o=null;let i=false;let a=0;export function bugLinksSettingsRead(){return i}const l=new Set;const v=/^(localhost|127(\.\d{1,3}){3}|10(\.\d{1,3}){3}|192\.168(\.\d{1,3}){2}|172\.(1[6-9]|2\d|3[01])(\.\d{1,3}){2}|\[::1\]|[a-z0-9-]+\.local)$/i;const d=/^(https?):\/\/([a-z0-9.-]+|\[[0-9a-f:]+\])(:\d{1,5})?(?:[/?#].*)?$/i;export function checkServer(e){const t=d.exec(e.trim());if(!t)return"That isn't a web address.";if(t[1].toLowerCase()==="https")return null;if(v.test(t[2]))return null;return"Use https, or http only for this Mac or your local network."}function c(e){const t=d.exec(e.trim());return`${t[1].toLowerCase()}://${t[2].toLowerCase()}${t[3]??""}`}export function configureBugLinks(e){if(e.server&&checkServer(e.server)){throw new Error(`configureBugLinks: ${checkServer(e.server)}`)}s={...s,...e,server:e.server?c(e.server):s.server};if(e.media)m(e.media);u()}export function loadBugLinksSettings(){if(!o){o=(async()=>{const e=a;let t;try{t=await p(f.bugLinks.settings())}catch{o=null;u();return}if(e!==a)return;try{const r=t?JSON.parse(t):{};if(typeof r.server==="string"&&!checkServer(r.server))n={server:c(r.server)}}catch{n={}}i=true;u()})()}return o}export function bugLinksServer(){return n.server??s.server??DEFAULT_SERVER}export function bugLinksRules(){return s.rules}const g=/^[A-Za-z0-9_-]{43}$/;export function teamKeyPins(){let e=s.teamKey;if(e===void 0){try{e=process.env.EXPO_PUBLIC_BUOY_TEAM_KEY}catch{e=void 0}}const t=(Array.isArray(e)?e:typeof e==="string"?e.split(","):[]).map(r=>String(r).trim()).filter(Boolean);if(!t.length)return null;if(t.some(r=>!g.test(r)))console.warn("[bug-links] teamKey must be the 43-letter key id from the team list's admin page.");return t.filter(r=>g.test(r))}export async function setBugLinksServer(e){let t={};if(e!==null){const r=checkServer(e);if(r)throw new Error(r);t={server:c(e)}}if(!await h(f.bugLinks.settings(),JSON.stringify(t),{strict:true}).catch(()=>false)){throw new Error("Buoy couldn't save the server (the storage may be full). Free some space, then try again.")}a++;n=t;i=true;u();return bugLinksServer()}export function subscribeBugLinksSettings(e){l.add(e);return()=>l.delete(e)}function u(){for(const e of l)e()}export function resetBugLinksSettingsForTests(){s={};n={};o=null;i=false}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{persistentStorage as r}from"@buoy-gg/shared-ui";function n(){if(typeof navigator!=="undefined"&&navigator.product==="ReactNative")return null;try{return globalThis.sessionStorage??null}catch{return null}}export const tabStorage={async getItem(t){const e=n();if(!e)return r.getItem(t);try{return e.getItem(t)}catch{return null}},async setItem(t,e){const o=n();if(!o)return r.setItem(t,e);try{o.setItem(t,e)}catch{}},async removeItem(t){const e=n();if(!e)return r.removeItem(t);try{e.removeItem(t)}catch{}}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{base64UrlDecode as C,devToolsStorageKeys as M,getItemChecked as R,setItemChecked as T,verifyRecord as j}from"@buoy-gg/shared-ui";import{BugLinksHttpError as v,fetchTeamKeys as J}from"./client";import{credentialOwner as p}from"./credential";import{teamKeyCrypto as L}from"./phoneCrypto";import{teamKeyPins as V}from"./settings";import{tabsMatter as y,withTabLock as D}from"./otherTabs";const h=()=>`${M.bugLinks.root()}_team_trust`;const P=5*60*1e3;const w=new Map;let u=null;let d=null;async function x(){const r=y()?globalThis.localStorage:void 0;return r?r.getItem(h()):R(h())}function B(){if(u)return Promise.resolve(u);d??=(async()=>{let r;try{r=await x()}catch(e){d=null;throw e}try{const e=r?JSON.parse(r):{};const t=e&&typeof e==="object"?e:{};u=y()&&f?F(t,f):t}catch(e){d=null;if(y())throw e;u={}}return u})();return d}let O=Promise.resolve();const E="buoy-bug-links-team-trust";function S(r){const e=()=>D(E,async()=>{if(y()){u=null;d=null}return r()});const t=O.then(e,e);O=t.catch(()=>{});return t}let f=null;function F(r,e){const t=JSON.parse(JSON.stringify(r));for(const[s,o]of Object.entries(e)){const n=t[s]??={roots:[],maxVersion:{}};for(const[a,c]of Object.entries(o))n.maxVersion[a]=Math.max(n.maxVersion[a]??0,c)}return t}async function _(){const r=JSON.stringify(u??{});if(y()){const e=await T(h(),r,{strict:true}).catch(()=>false);if(e)f=null;else{const t=f??{};for(const[s,o]of Object.entries(u??{})){const n=t[s]??={};for(const[a,c]of Object.entries(o.maxVersion))n[a]=Math.max(n[a]??0,c)}f=t}return e}try{return await T(h(),r,{strict:true})}catch{return false}}const m=(r,e,t)=>({kind:"wait",code:r,message:e,teamKeyId:t});function U(r){const e=r??{};if(typeof e.owner!=="string"||!Array.isArray(e.roots)||!Array.isArray(e.versions))return null;const t=e.current;if(t!==null&&(typeof t?.teamKeyId!=="string"||!Number.isSafeInteger(t?.keyVersion)))return null;return{owner:e.owner,roots:e.roots,versions:e.versions,current:t}}export async function pickTeamKey(r,e,t={}){if(!e)return m("signed_out","Sign in to Buoy so this bug can be locked to your team.");const s=`${r}|${e.slice(-16)}`;const o=w.get(s);if(o&&!t.fresh&&Date.now()-o.at<P&&!await Y(r,e,o.pick))return o.pick;const n=await S(()=>H(r,e));if(n.kind!=="wait"||n.code!=="offline")w.set(s,{at:Date.now(),pick:n});return n}async function Y(r,e,t){if(t.kind!=="ok"||!y())return false;try{const s=V();if(s&&!s.includes(t.target.teamKeyId))return true;const o=await x();const n=o?JSON.parse(o):{};const a=n[`${r}|${p(e)??"unknown"}`];if(!s&&a?.roots?.length&&!a.roots.includes(t.target.teamKeyId))return true;return t.target.keyVersion<(a?.maxVersion?.[t.target.teamKeyId]??0)}catch{return true}}async function H(r,e){let t;try{const i=await J(r,e);if(i===null)return{kind:"off"};t=U(i)}catch(i){if(i instanceof v&&i.code==="signed_out")return m("signed_out","Sign in to Buoy so this bug can be locked to your team.");if(i instanceof v&&i.code==="needs_pro")return{kind:"off"};return m("offline","Couldn't reach the team key. Buoy will lock this bug to your team later.")}if(!t)return m("untrusted","The server sent a team key Buoy can't read.");const s=t.owner;if(!t.roots.length)return{kind:"off",owner:s};const o=(i,$,N)=>({...m(i,$,N),owner:s});const n=t.current;if(!n){return o("no_current","Your team's key was retired after a team change. Once an admin makes a new one, Buoy locks this bug to it.")}const a=t.roots.find(i=>i.teamKeyId===n.teamKeyId);const c=t.versions.find(i=>i.teamKeyId===n.teamKeyId&&i.keyVersion===n.keyVersion);const b=a?C(a.pub):null;if(!a||!c||!b)return o("untrusted","The server's team key is missing a part.");if(a.retiredAt||c.retiredAt)return o("no_current","Your team's key was retired. Once an admin makes a new one, Buoy locks this bug to it.");const k=V();let g;try{g=await B()}catch{return o("untrusted","Buoy couldn't read which team keys this device trusts. It will lock this bug to your team later.")}const K=`${r}|${p(e)??"unknown"}`;const l=g[K]??{roots:[],maxVersion:{}};if(k){if(!k.includes(n.teamKeyId)){return o("pin","This app's Buoy setup pins another team key. Update the teamKey setting.",n.teamKeyId)}}else if(l.roots.length&&!l.roots.includes(n.teamKeyId)){return o("new_root","Your team has a new key root. Open Bug links to trust it.",n.teamKeyId)}let I;try{I=await j(L(),b,{...c,owner:t.owner})}catch{return o("untrusted","The team key isn't signed by your team's root. Buoy didn't use it.")}const A=l.maxVersion[n.teamKeyId]??0;if(n.keyVersion<A)return o("untrusted","The server offered an older team key. Buoy didn't use it.");if(!k&&!l.roots.length)l.roots.push(n.teamKeyId);l.maxVersion[n.teamKeyId]=n.keyVersion;g[K]=l;if(!await _())return o("untrusted","Buoy couldn't save which team keys this device trusts. It will lock this bug to your team later.");return{kind:"ok",target:{owner:t.owner,teamKeyId:n.teamKeyId,keyVersion:n.keyVersion,pub:I}}}export function trustTeamRoot(r,e,t){return S(()=>q(r,e,t))}async function q(r,e,t){const s=await B();const o=`${r}|${p(t)??"unknown"}`;if(s[o])s[o].roots=[e];const n=await _();w.clear();if(!n)throw new Error("This device couldn't save the new team key. Free some space, then try again.")}export function resetTeamKeysForTests(){w.clear();u=null;d=null;f=null}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{base64UrlDecode as Q,blobSha256 as Z,kitId as ee,kitKey as ne,openCard as te,openSecret as oe,pairFrom as se,parseKit as ie,parseWrap as re,unlockBugCase as ae,unwrapCaseKey as ce,versionLockAad as ue}from"@buoy-gg/shared-ui";import{BugLinksHttpError as A,BUG_PRIORITIES as Y,fetchBlob as le,teamJson as S}from"./client";import{credentialOwner as de,currentCredential as O}from"./credential";import{loadBugCase as fe}from"./loader";import{caseCrypto as we,teamKeyCrypto as L}from"./phoneCrypto";import{bugLinksServer as E,bugLinksSettingsRead as ge,loadBugLinksSettings as pe}from"./settings";const N=1e3;const V={kind:"idle"};let y=V;let T=null;let K=null;let l=null;let h=null;let x=new Map;let B=0;let C=0;const $=new Set;function I(){for(const e of $)e()}export function whoNow(){const e=O();return e?`${E()}|${de(e)}`:null}export function teamBugs(){if(y.kind==="idle")return y;return T===whoNow()?y:V}export function subscribeTeamBugs(e){$.add(e);return()=>$.delete(e)}export function forgetTeamBugs(){B++;C++;k(l);l=null;h=null;x=new Map;T=null;K=null;y=V;I()}function k(e){for(const i of e?.versions.values()??[])i.d.fill(0)}export function canLoadTeamBugs(){return!(typeof __DEV__!=="undefined"&&!__DEV__)}const U=(e,i)=>`${e}|${i}`;export async function loadTeamBugs(e=0){const i=B;await pe();if(i!==B)return;if(!ge()){T=whoNow();y={kind:"blocked",code:"error",message:"Buoy couldn't read its settings just now. Tap Refresh to try again."};I();return}const t=O();const o=whoNow();if(T!==null&&o!==T)forgetTeamBugs();if(T===null&&l&&h!==o){k(l);l=null;h=null}const r=B;const c=++C;const a=()=>c===C&&r===B&&whoNow()===o;const d=(n,g=K)=>{if(!a())return;y=n;T=o;K=g;I()};if(!t||!o)return d({kind:"blocked",code:"signed_out",message:"Sign in to Buoy to see your team's bugs."});const b=n=>{if(!a()){k(n);return null}if(l&&h===o&&l.owner===n.owner){k(n);return l}if(l&&l!==n)k(l);l=n;h=o;return n};const s=E();d(y.kind==="ready"?{...y,refreshing:true}:{kind:"loading"});const w=n=>{k(n);if(l===n){l=null;h=null}d({kind:"loading"},null)};const f=()=>{if(e>=2)return d({kind:"blocked",code:"error",message:"Your team just changed. Tap Refresh."},null);return loadTeamBugs(e+1)};try{let n=l;if(!n)n=b(await P(s,t));if(!n||!a())return;const g=[];let u="";const R=new Map;let _=null;for(let p=0;p<N;p++){const v=await S(s,`/api/bugs?limit=100${_?`&before=${encodeURIComponent(_)}`:""}`,t);if(!a())return;if(v.owner&&v.owner!==n.owner){w(n);return f()}g.push(...v.cases);u=v.me.accountId;for(const m of v.people??[])R.set(m.id,m.name);_=v.next;if(!_)break}x=R;const J=await j(s,t,n,a);if(!a())return;if(J.unknownVersion){const p=await P(s,t);if(!a()||l!==n)return k(p);if(p.owner!==n.owner){k(p);w(n);return f()}for(const[v,m]of n.cases)p.cases.set(v,m);k(n);l=n=p;h=o;await j(s,t,n,a);if(!a())return}const D=await S(s,"/api/bugs?limit=1",t);if(!a())return;if(D.owner&&D.owner!==n.owner){w(n);return f()}const X=y.kind==="ready"&&K===n.owner?new Map(y.bugs.map(p=>[p.id,p])):new Map;const q=g.map(p=>{const v=me(p,n,u);const m=X.get(p.id);if(!m||m.rowRevision<=v.rowRevision)return v;const z=m.assignee?m.assignee===u?"You":x.get(m.assignee)??m.assignee:null;return{...v,status:m.status,assignee:m.assignee,assigneeName:z,rowRevision:m.rowRevision}});d({kind:"ready",me:u,bugs:q,refreshing:false},n.owner)}catch(n){const g=F(n);if((g.code==="signed_out"||g.code==="no_access"||g.code==="needs_pro")&&a()&&h===o){k(l);l=null;h=null}d({kind:"blocked",...g})}finally{if(whoNow()!==o&&h===o){k(l);l=null;h=null}}}async function P(e,i){try{return await W(e,i)}catch(t){if(!(t instanceof M))throw t;return W(e,i)}}class M extends Error{}async function W(e,i){const t=L();const{teamKit:o}=await S(e,"/api/bugs/team-key/kit",i);const r=await S(e,"/api/bugs/team-key",i);const c=await S(e,"/api/bugs/team-key/locked",i);if(c.owner!==r.owner)throw new M("The server sent keys for another team.");const a=ie(o,"team");let d=null;try{const b=await ee(t,a,"team");if(b!==c.kitId)throw new M("Buoy sent a team kit that doesn't match your team's keys. Tap Refresh to try again.");d=await ne(t,a,"team");const s=new Map;const w={owner:c.owner,versions:s,cases:new Map};try{for(const f of c.versions){const n=r.versions.find(R=>R.teamKeyId===f.teamKeyId&&R.keyVersion===f.keyVersion);const g=n?Q(n.pub):null;if(!g)continue;const u=await oe(t,d,ue(c.owner,f.teamKeyId,f.keyVersion),f.locked);try{s.set(U(f.teamKeyId,f.keyVersion),se(u,g))}finally{u.fill(0)}}}catch(f){k(w);throw f}return w}finally{a.fill(0);d?.fill(0)}}async function j(e,i,t,o){const r=L();let c=false;let a=null;for(let d=0;d<N;d++){const b=await S(e,`/api/bugs/cards${a?`?before=${encodeURIComponent(a)}`:""}`,i);if(!o())return{unknownVersion:c};for(const s of b.cards){if(!o())return{unknownVersion:c};const w=t.cases.get(s.id);if(w&&w.cardRevision>=s.cardRevision&&(w.card||!s.card))continue;const f=()=>{if(o()&&w&&t.cases.get(s.id)===w&&w.cardRevision<s.cardRevision)t.cases.delete(s.id)};if(!s.wrap||!s.caseTag||(s.owner??t.owner)!==t.owner){f();continue}try{const n=re(s.wrap);const g=t.versions.get(U(n.teamKeyId,n.keyVersion));if(!g){c=true;f();continue}const u=await ce(r,n,t.owner,g,s.caseTag);let R=null;if(s.card)R=await te(r,u,s.caseTag,s.cardRevision,s.card).catch(()=>null);if(!o())return{unknownVersion:c};const _=t.cases.get(s.id);if(_&&_.cardRevision>s.cardRevision)continue;t.cases.set(s.id,{key:u,card:R,cardRevision:s.cardRevision})}catch{f()}}a=b.next;if(!a)break}return{unknownVersion:c}}function me(e,i,t){const o=i.cases.get(e.id);const r=o?.card??null;const c=d=>d===t?"You":x.get(d)??d;const a=r?.note?.split("\n")[0]?.trim()||r?.error||(r?.route?`Bug on ${r.route}`:o?`Bug from ${new Date(e.createdAt).toLocaleString()}`:"Locked bug");return{id:e.id,title:a,priority:e.priority&&Y.includes(e.priority)?e.priority:"normal",status:e.status,assignee:e.assignee,assigneeName:e.assignee?c(e.assignee):null,reporter:e.mine?"You":e.reporter??c(e.senderId),createdAt:e.createdAt,build:r?.version?`${r.version}${r.build?` (${r.build})`:""}`:null,error:r?.error??null,repeatOf:e.duplicateOf??null,canOpen:!!r?.blobSha256,rowRevision:e.rowRevision}}function F(e){if(e instanceof A){if(e.code==="signed_out")return{code:"signed_out",message:"Sign in to Buoy to see your team's bugs."};if(e.code==="needs_pro")return{code:"needs_pro",message:"The team list needs Buoy Pro or Business."};if(e.code==="offline")return{code:"offline",message:"Couldn't reach the bug links server. Tap Refresh to try again."};if(e.status===403)return{code:"no_access",message:"Your Buoy sign-in can send bugs but not read them. Ask your team admin, or sign in as yourself."};if(e.detail.error==="own_kits")return{code:"own_kits",message:"Your team keeps its own key. Open the list on buoy.gg or in Buoy Desktop."};if(e.status===404&&e.detail.error==="no_custody")return{code:"no_custody",message:"This server doesn't keep team keys. Open the list on the web or in Buoy Desktop."}}return{code:"error",message:e instanceof Error?e.message:String(e)}}async function G(e,i,t,o){if(!(e instanceof A)||i!==B||whoNow()!==t||!o||l!==o)return;if(e.status===404)return loadTeamBugs();if(!(e.status===401||e.status===402||e.status===403))return;k(o);l=null;h=null;C++;y={kind:"blocked",...F(e)};T=t;I()}const H=e=>Y.indexOf(e.priority);const ye=e=>e.status==="new"||e.status==="seen";export function teamView(e,i,t){const o=e.filter(r=>i==="all"?true:ye(r)&&!r.repeatOf&&(i==="open"||r.assignee===t));return o.sort((r,c)=>(i==="all"?0:H(r)-H(c))||c.createdAt.localeCompare(r.createdAt))}export async function changeTeamBug(e,i){const t=teamBugs();const o=t.kind==="ready"?t.bugs.find(u=>u.id===e):void 0;const r=O();if(!o||t.kind!=="ready"||!r)throw new Error("The list changed. Tap Refresh.");const c=B;const a=whoNow();const d=K;const b=l;const s=()=>c===B&&whoNow()===a&&K===d;let w;try{w=await S(E(),`/api/bugs/${encodeURIComponent(e)}`,r,{method:"PATCH",body:{...i,rowRevision:o.rowRevision}})}catch(u){if(u instanceof A&&u.code==="conflict"){if(s())await loadTeamBugs();throw new Error("Someone changed this bug just now. This is the latest.")}await G(u,c,a,b);throw u}const f=y;if(!s()||f.kind!=="ready")return;const n=w.rowRevision??o.rowRevision+1;const g=f.me;y={...f,bugs:f.bugs.map(u=>{if(u.id!==e||u.rowRevision>=n)return u;const R="assignee"in i?i.assignee?i.assignee===g?"You":x.get(i.assignee)??i.assignee:null:u.assigneeName;return{...u,...i,assigneeName:R,rowRevision:n}})};I()}export async function loadTeamBug(e,i={}){if(!canLoadTeamBugs())throw new Error("Loading needs a dev build. Open this bug in your dev build to load it.");const t=O();const o=whoNow();const r=B;const c=l;const a=t&&o&&o===T&&o===h?c?.cases.get(e):void 0;if(!t||!a)throw new Error("This phone can't open that bug. Tap Refresh to read the list again.");const d=()=>r===B&&whoNow()===o&&l===c;const b=()=>new Error("The Bug Links screen closed, so Buoy stopped loading this bug.");const s=a.card;if(!s?.blobSha256)throw new Error("This bug's note doesn't check out, so Buoy won't load it.");let w;try{w=await le(E(),e,t)}catch(u){await G(u,r,o,c);throw u}if(!d())throw b();const f=await Z(L(),w);if(f!==s.blobSha256)throw new Error("This bug's file doesn't match its note, so Buoy won't load it.");const{kase:n}=await ae(w,a.key,{crypto:we()});if(!d())throw b();const g=await fe(n,{...i,source:{id:e},stillWanted:d});if(!g.loaded&&!d())throw b();return g}export function resetTeamBugsForTests(){forgetTeamBugs();$.clear()}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{AppState as l}from"react-native";import{devToolsStorageKeys as n,startBugCaseRecorder as p}from"@buoy-gg/shared-ui";import{watchBugLinksOverlays as f}from"./components/overlays";import{loadedCase as m,restoreLoadedAfterReload as g}from"./engine/loader";import{loadMyLinks as k,reloadMyLinks as L}from"./engine/linksStore";import{listenForOtherTabs as y}from"./engine/otherTabs";import{retryWaiting as r}from"./engine/reporter";import{loadBugLinksSettings as b}from"./engine/settings";const a=typeof navigator!=="undefined"&&navigator.product==="ReactNative";const s=typeof window!=="undefined"&&typeof document!=="undefined";function B(){let e=0;const o=()=>{e++;const t=e<20?100:1500;p().then(i=>{const c=i.watching.length>=3&&i.touches;if(!c&&e<60)setTimeout(o,t)}).catch(()=>{if(e<60)setTimeout(o,t)})};o()}function d(e){if(!e)return"";try{const o=JSON.parse(e);const t=o.block??(o.options?.replay!==false&&!!o.report?.replay?.active);return[o.undo?.undoId,o.createdAt,o.source?.id,o.phase,t].join("|")}catch{return e}}const u="__buoyBugLinksBooted";const v=globalThis[u]===true;if((a||s)&&!v){globalThis[u]=true;y();B();f();void b();void k().then(()=>r());void g();l?.addEventListener?.("change",e=>{if(e==="active")void r()});if(!a&&s){document.addEventListener("visibilitychange",()=>{if(document.visibilityState==="visible")void r()});window.addEventListener("pageshow",e=>{if(e.persisted)window.location.reload()});window.addEventListener("storage",e=>{if(e.key===null){if(m())window.location.reload()}else if(e.key===n.bugLinks.loaded()&&d(e.oldValue)!==d(e.newValue)){window.location.reload()}else if(e.key===n.bugLinks.links()){void L()}})}}export{reportBugToolPreset,bugLinksToolPreset,createBugLinksTool,BUG_LINKS_COLOR}from"./preset";export{bugLinksSyncAdapter,getBugLinksSnapshot}from"./sync/bugLinksSyncAdapter";export{BugLinksBody}from"./components/BugLinksBody";export{BugLinksOverlay}from"./components/overlays";export{BuoyMask,BuoyUnmask}from"./media/BuoyMask";export{configureMedia,startRecording,stopRecording,markBugHere,probeFrame,mediaUnavailableReason}from"./media/frames";export{configureBugLinks,setBugLinksServer,bugLinksServer,DEFAULT_SERVER}from"./engine/settings";export{reportBug,addNote,setLinkPriority,deleteLink,retryWaiting,reportState,subscribeReportState}from"./engine/reporter";export{BUG_PRIORITIES}from"./engine/client";export{myLinks,subscribeMyLinks}from"./engine/linksStore";export{loadBugCase,loadAgain,undoBugCase,leaveSandbox,loadedCase,subscribeLoadedCase}from"./engine/loader";export{openBugLink,fetchBugLink}from"./engine/opener";export{buildSandbox}from"./engine/sandbox";export{phoneCaseCrypto,caseCrypto}from"./engine/phoneCrypto";export{randomSource}from"./engine/randomBytes";export{setBugLinksCredentialProvider}from"./engine/credential";import{ensureMediaStarted as h}from"./media/frames";h();
|