@buoy-gg/shared-ui 7.0.78 → 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.
Files changed (175) hide show
  1. package/lib/commonjs/JsModal.js +1 -1
  2. package/lib/commonjs/bugCase/appInfo.js +1 -0
  3. package/lib/commonjs/bugCase/appInfo.web.js +1 -0
  4. package/lib/commonjs/bugCase/capture.js +1 -1
  5. package/lib/commonjs/bugCase/format.js +2 -2
  6. package/lib/commonjs/bugCase/index.js +1 -1
  7. package/lib/commonjs/bugCase/lock.js +1 -1
  8. package/lib/commonjs/bugCase/media.js +1 -0
  9. package/lib/commonjs/bugCase/recorder.js +1 -1
  10. package/lib/commonjs/bugCase/scrub.js +1 -1
  11. package/lib/commonjs/bugCase/steps.js +1 -1
  12. package/lib/commonjs/bugCase/teamKey.js +1 -0
  13. package/lib/commonjs/bugCase/validate.js +1 -0
  14. package/lib/commonjs/clipboard/autoDetectClipboard.js +1 -1
  15. package/lib/commonjs/clipboard/clipboard-impl.js +1 -1
  16. package/lib/commonjs/clipboard/clipboard-impl.web.js +1 -1
  17. package/lib/commonjs/clipboard/index.js +1 -1
  18. package/lib/commonjs/dataViewer/LiveExplorer.js +1 -1
  19. package/lib/commonjs/dataViewer/VirtualizedDataExplorer.js +1 -1
  20. package/lib/commonjs/dataViewer/flattenTree.js +1 -0
  21. package/lib/commonjs/dataViewer/liveEntries.js +1 -0
  22. package/lib/commonjs/icons/index.js +1 -1
  23. package/lib/commonjs/index.js +1 -1
  24. package/lib/commonjs/license/BuoyUpgradeSheet.js +1 -1
  25. package/lib/commonjs/license/index.js +1 -1
  26. package/lib/commonjs/sync/keyedArrays.js +1 -1
  27. package/lib/commonjs/sync/valuePath.js +1 -1
  28. package/lib/commonjs/ui/components/DynamicFilterView.js +1 -1
  29. package/lib/commonjs/ui/components/EntityFilterView.js +1 -1
  30. package/lib/commonjs/ui/components/WindowControls.js +1 -1
  31. package/lib/commonjs/ui/floating/FloatingTool.js +1 -1
  32. package/lib/commonjs/ui/listPresets.js +1 -1
  33. package/lib/commonjs/utils/fiberRootAnchor.js +1 -0
  34. package/lib/commonjs/utils/index.js +1 -1
  35. package/lib/commonjs/utils/loadOptionalModule.js +1 -1
  36. package/lib/commonjs/utils/loadToolModule.js +1 -1
  37. package/lib/commonjs/utils/minimalSuperJSON.js +1 -1
  38. package/lib/commonjs/utils/persistentStorage.js +1 -1
  39. package/lib/commonjs/utils/persistentStorage.web.js +1 -1
  40. package/lib/commonjs/utils/reloadPlatform.js +1 -1
  41. package/lib/commonjs/utils/snapshotProviderKit.js +1 -1
  42. package/lib/module/JsModal.js +1 -1
  43. package/lib/module/bugCase/appInfo.js +1 -0
  44. package/lib/module/bugCase/appInfo.web.js +1 -0
  45. package/lib/module/bugCase/capture.js +1 -1
  46. package/lib/module/bugCase/format.js +2 -2
  47. package/lib/module/bugCase/index.js +1 -1
  48. package/lib/module/bugCase/lock.js +1 -1
  49. package/lib/module/bugCase/media.js +1 -0
  50. package/lib/module/bugCase/recorder.js +1 -1
  51. package/lib/module/bugCase/scrub.js +1 -1
  52. package/lib/module/bugCase/steps.js +1 -1
  53. package/lib/module/bugCase/teamKey.js +1 -0
  54. package/lib/module/bugCase/validate.js +1 -0
  55. package/lib/module/clipboard/autoDetectClipboard.js +1 -1
  56. package/lib/module/clipboard/clipboard-impl.js +1 -1
  57. package/lib/module/clipboard/clipboard-impl.web.js +1 -1
  58. package/lib/module/clipboard/index.js +1 -1
  59. package/lib/module/dataViewer/LiveExplorer.js +1 -1
  60. package/lib/module/dataViewer/VirtualizedDataExplorer.js +1 -1
  61. package/lib/module/dataViewer/flattenTree.js +1 -0
  62. package/lib/module/dataViewer/liveEntries.js +1 -0
  63. package/lib/module/icons/index.js +1 -1
  64. package/lib/module/index.js +1 -1
  65. package/lib/module/license/BuoyUpgradeSheet.js +1 -1
  66. package/lib/module/license/index.js +1 -1
  67. package/lib/module/sync/keyedArrays.js +1 -1
  68. package/lib/module/sync/valuePath.js +1 -1
  69. package/lib/module/ui/components/DynamicFilterView.js +1 -1
  70. package/lib/module/ui/components/EntityFilterView.js +1 -1
  71. package/lib/module/ui/components/WindowControls.js +1 -1
  72. package/lib/module/ui/floating/FloatingTool.js +1 -1
  73. package/lib/module/ui/listPresets.js +1 -1
  74. package/lib/module/utils/fiberRootAnchor.js +1 -0
  75. package/lib/module/utils/index.js +1 -1
  76. package/lib/module/utils/loadOptionalModule.js +1 -1
  77. package/lib/module/utils/loadToolModule.js +1 -1
  78. package/lib/module/utils/minimalSuperJSON.js +1 -1
  79. package/lib/module/utils/persistentStorage.js +1 -1
  80. package/lib/module/utils/persistentStorage.web.js +1 -1
  81. package/lib/module/utils/reloadPlatform.js +1 -1
  82. package/lib/module/utils/snapshotProviderKit.js +1 -1
  83. package/lib/typescript/commonjs/JsModal.d.ts +3 -4
  84. package/lib/typescript/commonjs/JsModal.web.d.ts +2 -0
  85. package/lib/typescript/commonjs/bugCase/appInfo.d.ts +17 -0
  86. package/lib/typescript/commonjs/bugCase/appInfo.web.d.ts +14 -0
  87. package/lib/typescript/commonjs/bugCase/format.d.ts +2 -1
  88. package/lib/typescript/commonjs/bugCase/index.d.ts +7 -0
  89. package/lib/typescript/commonjs/bugCase/lock.d.ts +13 -0
  90. package/lib/typescript/commonjs/bugCase/media.d.ts +90 -0
  91. package/lib/typescript/commonjs/bugCase/recorder.d.ts +1 -0
  92. package/lib/typescript/commonjs/bugCase/scrub.d.ts +2 -2
  93. package/lib/typescript/commonjs/bugCase/steps.d.ts +10 -0
  94. package/lib/typescript/commonjs/bugCase/teamKey.d.ts +185 -0
  95. package/lib/typescript/commonjs/bugCase/types.d.ts +10 -0
  96. package/lib/typescript/commonjs/bugCase/validate.d.ts +14 -0
  97. package/lib/typescript/commonjs/clipboard/autoDetectClipboard.d.ts +1 -1
  98. package/lib/typescript/commonjs/clipboard/clipboard-impl.d.ts +6 -0
  99. package/lib/typescript/commonjs/clipboard/clipboard-impl.web.d.ts +2 -0
  100. package/lib/typescript/commonjs/clipboard/index.d.ts +1 -1
  101. package/lib/typescript/commonjs/dataViewer/flattenTree.d.ts +35 -0
  102. package/lib/typescript/commonjs/dataViewer/liveEntries.d.ts +19 -0
  103. package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
  104. package/lib/typescript/commonjs/icons/index.d.ts +1 -0
  105. package/lib/typescript/commonjs/index.d.ts +3 -2
  106. package/lib/typescript/commonjs/license/BuoyUpgradeSheet.d.ts +8 -0
  107. package/lib/typescript/commonjs/license/index.d.ts +1 -1
  108. package/lib/typescript/commonjs/sync/keyedArrays.d.ts +6 -1
  109. package/lib/typescript/commonjs/ui/floating/FloatingTool.d.ts +6 -1
  110. package/lib/typescript/commonjs/ui/listPresets.d.ts +2 -2
  111. package/lib/typescript/commonjs/utils/fiberRootAnchor.d.ts +19 -0
  112. package/lib/typescript/commonjs/utils/index.d.ts +2 -1
  113. package/lib/typescript/commonjs/utils/minimalSuperJSON.d.ts +4 -2
  114. package/lib/typescript/commonjs/utils/persistentStorage.d.ts +19 -0
  115. package/lib/typescript/commonjs/utils/persistentStorage.web.d.ts +17 -0
  116. package/lib/typescript/module/JsModal.d.ts +3 -4
  117. package/lib/typescript/module/JsModal.web.d.ts +2 -0
  118. package/lib/typescript/module/bugCase/appInfo.d.ts +17 -0
  119. package/lib/typescript/module/bugCase/appInfo.web.d.ts +14 -0
  120. package/lib/typescript/module/bugCase/format.d.ts +2 -1
  121. package/lib/typescript/module/bugCase/index.d.ts +7 -0
  122. package/lib/typescript/module/bugCase/lock.d.ts +13 -0
  123. package/lib/typescript/module/bugCase/media.d.ts +90 -0
  124. package/lib/typescript/module/bugCase/recorder.d.ts +1 -0
  125. package/lib/typescript/module/bugCase/scrub.d.ts +2 -2
  126. package/lib/typescript/module/bugCase/steps.d.ts +10 -0
  127. package/lib/typescript/module/bugCase/teamKey.d.ts +185 -0
  128. package/lib/typescript/module/bugCase/types.d.ts +10 -0
  129. package/lib/typescript/module/bugCase/validate.d.ts +14 -0
  130. package/lib/typescript/module/clipboard/autoDetectClipboard.d.ts +1 -1
  131. package/lib/typescript/module/clipboard/clipboard-impl.d.ts +6 -0
  132. package/lib/typescript/module/clipboard/clipboard-impl.web.d.ts +2 -0
  133. package/lib/typescript/module/clipboard/index.d.ts +1 -1
  134. package/lib/typescript/module/dataViewer/flattenTree.d.ts +35 -0
  135. package/lib/typescript/module/dataViewer/liveEntries.d.ts +19 -0
  136. package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
  137. package/lib/typescript/module/icons/index.d.ts +1 -0
  138. package/lib/typescript/module/index.d.ts +3 -2
  139. package/lib/typescript/module/license/BuoyUpgradeSheet.d.ts +8 -0
  140. package/lib/typescript/module/license/index.d.ts +1 -1
  141. package/lib/typescript/module/sync/keyedArrays.d.ts +6 -1
  142. package/lib/typescript/module/ui/floating/FloatingTool.d.ts +6 -1
  143. package/lib/typescript/module/ui/listPresets.d.ts +2 -2
  144. package/lib/typescript/module/utils/fiberRootAnchor.d.ts +19 -0
  145. package/lib/typescript/module/utils/index.d.ts +2 -1
  146. package/lib/typescript/module/utils/minimalSuperJSON.d.ts +4 -2
  147. package/lib/typescript/module/utils/persistentStorage.d.ts +19 -0
  148. package/lib/typescript/module/utils/persistentStorage.web.d.ts +17 -0
  149. package/lib/web/chunks/chunk-36A4T5PL.mjs +1 -0
  150. package/lib/web/chunks/{chunk-XUYN4NOF.mjs → chunk-5UII72AQ.mjs} +1 -1
  151. package/lib/web/chunks/chunk-A3N3WVKC.mjs +1 -0
  152. package/lib/web/chunks/chunk-BPL65J3F.mjs +1 -0
  153. package/lib/web/chunks/chunk-EY7GXWGI.mjs +1 -1
  154. package/lib/web/chunks/chunk-IE2U5SN2.mjs +15 -0
  155. package/lib/web/chunks/chunk-IEAXJZCR.mjs +6 -0
  156. package/lib/web/chunks/chunk-OF3VWR6K.mjs +2 -2
  157. package/lib/web/chunks/{chunk-OVTASJUJ.mjs → chunk-SUG6TLF5.mjs} +1 -1
  158. package/lib/web/chunks/chunk-SXWOOYFL.mjs +1 -0
  159. package/lib/web/chunks/chunk-T44OT3CI.mjs +6 -0
  160. package/lib/web/chunks/{chunk-OQB3GW44.mjs → chunk-UVVRCZX5.mjs} +1 -1
  161. package/lib/web/chunks/chunk-Y7FM4WZ7.mjs +6 -0
  162. package/lib/web/chunks/persistentStorage.web-BFPYMY5C.mjs +1 -0
  163. package/lib/web/dataViewer.mjs +2 -2
  164. package/lib/web/history.mjs +1 -1
  165. package/lib/web/hooks.mjs +1 -1
  166. package/lib/web/index.mjs +12 -6
  167. package/lib/web/ui.mjs +1 -1
  168. package/lib/web/utils.mjs +1 -1
  169. package/package.json +5 -5
  170. package/scripts/detect-clipboard.js +18 -2
  171. package/lib/web/chunks/chunk-BMITMQVB.mjs +0 -6
  172. package/lib/web/chunks/chunk-DJD42EPL.mjs +0 -1
  173. package/lib/web/chunks/chunk-GN75GBYZ.mjs +0 -1
  174. package/lib/web/chunks/chunk-GTRVOJUH.mjs +0 -1
  175. package/lib/web/chunks/chunk-HE5ONCAL.mjs +0 -6
@@ -1 +1 @@
1
- "use strict";import{devToolsStorageKeys as h}from"../storage/devToolsStorageKeys.js";import{notifyStorageWrite as v}from"./storageWrites.js";import{hasAsyncStorageNative as K}from"./asyncStorageNative.js";let i=null;let A=null;let w=false;let o=null;let y=null;const n=new Map;function m(){return typeof window!=="undefined"&&window?.localStorage!==void 0}const R="react-buoy";const D=".health-check";function d(){if(!o?.documentDirectory)return null;return`${o.documentDirectory}${R}/`}function F(t){return encodeURIComponent(t.replace(/~/g,"~0").replace(/\//g,"~1"))}function B(t){return decodeURIComponent(t).replace(/~1/g,"/").replace(/~0/g,"~")}function I(t){const e=d();if(!e)return null;return`${e}${F(t)}.json`}function g(){if(o)return o;try{const t=require("expo-file-system/legacy");o=t?.default||t;if(o&&o.documentDirectory){return o}o=null}catch{}try{const t=require("expo-file-system");o=t?.default||t;if(o&&o.documentDirectory){return o}o=null}catch{}return null}function l(){if(y)return y;if(!K())return null;try{const t=require("@react-native-async-storage/async-storage");y=t?.default||t;if(y&&typeof y.getItem==="function"){return y}y=null}catch{}return null}async function x(){const t=g();if(!t||!t.documentDirectory){return false}const e=d();if(!e)return false;const r=`${e}${D}`;try{const a=await t.getInfoAsync(e);if(!a.exists){await t.makeDirectoryAsync(e,{intermediates:true})}const s=`health-check-${Date.now()}`;await t.writeAsStringAsync(r,s);const c=await t.readAsStringAsync(r);if(c!==s){return false}await t.deleteAsync(r,{idempotent:true});return true}catch(a){console.warn("[Buoy]","[React Buoy] FileSystem health check failed:",a);return false}}async function b(){const t=l();if(!t)return;try{const e=await t.getAllKeys();const r=e.filter(c=>c.startsWith(h.base));if(r.length===0){return}const a=await t.multiGet(r);let s=0;for(const[c,p]of a){if(p!==null){await S.setItem(c,p);s++}}void s}catch(e){console.warn("[Buoy]","[React Buoy] Migration failed:",e)}}const S={async getItem(t){const e=g();if(!e)return null;const r=I(t);if(!r)return null;try{const a=await e.getInfoAsync(r);if(!a.exists){return null}const s=await e.readAsStringAsync(r);return s}catch{return null}},async setItem(t,e){const r=g();if(!r)return;const a=d();const s=I(t);if(!a||!s)return;try{const c=await r.getInfoAsync(a);if(!c.exists){await r.makeDirectoryAsync(a,{intermediates:true})}await r.writeAsStringAsync(s,e)}catch(c){console.warn("[Buoy]","[React Buoy] FileSystem write failed:",c)}},async removeItem(t){const e=g();if(!e)return;const r=I(t);if(!r)return;try{await e.deleteAsync(r,{idempotent:true})}catch{}},async getAllKeys(){const t=g();if(!t)return[];const e=d();if(!e)return[];try{const r=await t.getInfoAsync(e);if(!r.exists)return[];const a=await t.readDirectoryAsync(e);return a.filter(s=>s.endsWith(".json")).map(s=>B(s.replace(".json","")))}catch{return[]}},async multiGet(t){const e=[];for(const r of t){const a=await this.getItem(r);e.push([r,a])}return e},async multiRemove(t){for(const e of t){await this.removeItem(e)}},async clear(){const t=g();if(!t)return;const e=d();if(!e)return;try{await t.deleteAsync(e,{idempotent:true})}catch{}}};const E={async getItem(t){const e=l();if(!e)return n.get(t)??null;try{return await e.getItem(t)}catch{return n.get(t)??null}},async setItem(t,e){const r=l();if(!r){n.set(t,e);return}try{await r.setItem(t,e)}catch{n.set(t,e)}},async removeItem(t){const e=l();n.delete(t);if(!e)return;try{await e.removeItem(t)}catch{}},async getAllKeys(){const t=l();if(!t)return Array.from(n.keys());try{const e=await t.getAllKeys();return e.filter(r=>r.startsWith(h.base))}catch{return Array.from(n.keys())}},async multiGet(t){const e=l();if(!e){return t.map(r=>[r,n.get(r)??null])}try{const r=await e.multiGet(t);return r}catch{return t.map(r=>[r,n.get(r)??null])}},async multiRemove(t){t.forEach(r=>n.delete(r));const e=l();if(!e)return;try{await e.multiRemove(t)}catch{}},async clear(){n.clear();const t=l();if(!t)return;try{const e=await t.getAllKeys();const r=e.filter(a=>a.startsWith(h.base));if(r.length>0){await t.multiRemove(r)}}catch{}}};const G={async getItem(t){if(!m())return n.get(t)??null;try{return window.localStorage.getItem(t)}catch{return n.get(t)??null}},async setItem(t,e){if(!m()){n.set(t,e);return}try{window.localStorage.setItem(t,e)}catch{n.set(t,e)}},async removeItem(t){n.delete(t);if(!m())return;try{window.localStorage.removeItem(t)}catch{}},async getAllKeys(){if(!m())return Array.from(n.keys());try{const t=[];const e=window.localStorage;for(let r=0;r<e.length;r++){const a=e.key(r);if(a&&a.startsWith(h.base)){t.push(a)}}return t}catch{return Array.from(n.keys())}},async multiGet(t){const e=[];for(const r of t){const a=await this.getItem(r);e.push([r,a])}return e},async multiRemove(t){for(const e of t){await this.removeItem(e)}},async clear(){n.clear();if(!m())return;try{const t=await this.getAllKeys();const e=window.localStorage;for(const r of t){e.removeItem(r)}}catch{}}};const W={async getItem(t){return n.get(t)??null},async setItem(t,e){n.set(t,e)},async removeItem(t){n.delete(t)},async getAllKeys(){return Array.from(n.keys())},async multiGet(t){return t.map(e=>[e,n.get(e)??null])},async multiRemove(t){t.forEach(e=>n.delete(e))},async clear(){n.clear()}};async function k(){if(i!==null)return;const t=await x();if(t){const e=await S.getAllKeys();if(e.length===0){await b()}i="filesystem"}else if(m()){i="localstorage"}else{const e=l();if(e){i="asyncstorage";if(!w){w=true;console.warn("[Buoy]","[React Buoy] Using AsyncStorage fallback. Settings may be lost if AsyncStorage is cleared. Install expo-file-system for persistent storage.")}}else{i="memory";if(!w){w=true;console.warn("[Buoy]","[React Buoy] Using in-memory storage. Settings will be lost on app restart.")}}}}function u(){if(!A){A=k()}return A}function f(){switch(i){case"filesystem":return S;case"asyncstorage":return E;case"localstorage":return G;case"memory":default:return W}}export const persistentStorage={async getItem(t){await u();return f().getItem(t)},async setItem(t,e){await u();await f().setItem(t,e);v(t,e)},async removeItem(t){await u();await f().removeItem(t);v(t,null)},async getAllKeys(){await u();return f().getAllKeys()},async multiGet(t){await u();return f().multiGet(t)},async multiRemove(t){await u();return f().multiRemove(t)},async clear(){await u();return f().clear()}};export async function isUsingPersistentStorage(){await u();return i==="filesystem"}export async function getStorageBackendType(){await u();return i??"memory"}export function resetStorageInit(){i=null;A=null;w=false}
1
+ "use strict";import{devToolsStorageKeys as I}from"../storage/devToolsStorageKeys.js";import{notifyStorageWrite as x}from"./storageWrites.js";import{hasAsyncStorageNative as G}from"./asyncStorageNative.js";let i=null;let S=null;let h=false;let c=null;let m=null;const a=new Map;function g(){return typeof window!=="undefined"&&window?.localStorage!==void 0}const $="react-buoy";const C=".health-check";function w(){if(!c?.documentDirectory)return null;return`${c.documentDirectory}${$}/`}function P(t){return encodeURIComponent(t.replace(/~/g,"~0").replace(/\//g,"~1"))}function M(t){return decodeURIComponent(t).replace(/~1/g,"/").replace(/~0/g,"~")}function A(t){const e=w();if(!e)return null;return`${e}${P(t)}.json`}function f(){if(c)return c;try{const t=require("expo-file-system/legacy");c=t?.default||t;if(c&&c.documentDirectory){return c}c=null}catch{}try{const t=require("expo-file-system");c=t?.default||t;if(c&&c.documentDirectory){return c}c=null}catch{}return null}function u(){if(m)return m;if(!G())return null;try{const t=require("@react-native-async-storage/async-storage");m=t?.default||t;if(m&&typeof m.getItem==="function"){return m}m=null}catch{}return null}async function T(){const t=f();if(!t||!t.documentDirectory){return false}const e=w();if(!e)return false;const r=`${e}${C}`;try{const n=await t.getInfoAsync(e);if(!n.exists){await t.makeDirectoryAsync(e,{intermediates:true})}const o=`health-check-${Date.now()}`;await t.writeAsStringAsync(r,o);const s=await t.readAsStringAsync(r);if(s!==o){return false}await t.deleteAsync(r,{idempotent:true});return true}catch(n){console.warn("[Buoy]","[React Buoy] FileSystem health check failed:",n);return false}}async function j(){const t=u();if(!t)return;try{const e=await t.getAllKeys();const r=e.filter(s=>s.startsWith(I.base));if(r.length===0){return}const n=await t.multiGet(r);let o=0;for(const[s,y]of n){if(y!==null){await K.setItem(s,y);o++}}void o}catch(e){console.warn("[Buoy]","[React Buoy] Migration failed:",e)}}const p=new Map;function U(t,e){const r=(p.get(t)??Promise.resolve()).catch(()=>{}).then(()=>_(t,e));const n=r.catch(()=>{});p.set(t,n);void n.then(()=>{if(p.get(t)===n)p.delete(t)});return r}async function _(t,e){const r=f();const n=w();const o=A(t);if(!r||!n||!o)return false;if(!r.moveAsync)return W(t,e);const s=`${o}.tmp`;let y=false;let D=false;try{const v=await r.getInfoAsync(n);if(!v.exists)await r.makeDirectoryAsync(n,{intermediates:true});if(!(await r.getInfoAsync(o)).exists&&(await r.getInfoAsync(s)).exists){try{await r.moveAsync({from:s,to:o})}catch{return false}}D=true;await r.writeAsStringAsync(s,e);try{await r.moveAsync({from:s,to:o})}catch{await r.deleteAsync(o,{idempotent:true});y=true;await r.moveAsync({from:s,to:o})}return true}catch(v){console.warn("[Buoy]","[React Buoy] FileSystem write failed:",v);if(!y){if(D)await r.deleteAsync(s,{idempotent:true}).catch(()=>{});return false}return r.getInfoAsync(s).then(E=>E.exists,()=>false)}}async function F(t,e){if((await t.getInfoAsync(e)).exists)return t.readAsStringAsync(e);const r=`${e}.tmp`;if(t.moveAsync&&(await t.getInfoAsync(r)).exists)return t.readAsStringAsync(r);return null}function R(){return i!=="filesystem"&&!!f()&&!!w()}async function W(t,e){const r=f();if(!r)return false;const n=w();const o=A(t);if(!n||!o)return false;try{const s=await r.getInfoAsync(n);if(!s.exists){await r.makeDirectoryAsync(n,{intermediates:true})}await r.writeAsStringAsync(o,e);return true}catch(s){console.warn("[Buoy]","[React Buoy] FileSystem write failed:",s);return false}}async function b(t,e){const r=u();if(!r){a.set(t,e);return false}try{await r.setItem(t,e);return true}catch{a.set(t,e);return false}}async function B(t,e){if(!g()){a.set(t,e);return false}try{window.localStorage.setItem(t,e);return true}catch{a.set(t,e);return false}}const K={async getItem(t){const e=f();if(!e)return null;const r=A(t);if(!r)return null;try{return await F(e,r)}catch{return null}},async setItem(t,e){await W(t,e)},async removeItem(t){const e=f();if(!e)return;const r=A(t);if(!r)return;try{await e.deleteAsync(`${r}.tmp`,{idempotent:true});await e.deleteAsync(r,{idempotent:true})}catch{}},async getAllKeys(){const t=f();if(!t)return[];const e=w();if(!e)return[];try{const r=await t.getInfoAsync(e);if(!r.exists)return[];const n=await t.readDirectoryAsync(e);const o=new Set;for(const s of n){const y=s.endsWith(".json")?s.slice(0,-".json".length):s.endsWith(".json.tmp")?s.slice(0,-".json.tmp".length):null;if(y!==null)o.add(M(y))}return[...o]}catch{return[]}},async multiGet(t){const e=[];for(const r of t){const n=await this.getItem(r);e.push([r,n])}return e},async multiRemove(t){for(const e of t){await this.removeItem(e)}},async clear(){const t=f();if(!t)return;const e=w();if(!e)return;try{await t.deleteAsync(e,{idempotent:true})}catch{}}};const k={async getItem(t){const e=u();if(!e)return a.get(t)??null;try{return await e.getItem(t)}catch{return a.get(t)??null}},async setItem(t,e){await b(t,e)},async removeItem(t){const e=u();a.delete(t);if(!e)return;try{await e.removeItem(t)}catch{}},async getAllKeys(){const t=u();if(!t)return Array.from(a.keys());try{const e=await t.getAllKeys();return e.filter(r=>r.startsWith(I.base))}catch{return Array.from(a.keys())}},async multiGet(t){const e=u();if(!e){return t.map(r=>[r,a.get(r)??null])}try{const r=await e.multiGet(t);return r}catch{return t.map(r=>[r,a.get(r)??null])}},async multiRemove(t){t.forEach(r=>a.delete(r));const e=u();if(!e)return;try{await e.multiRemove(t)}catch{}},async clear(){a.clear();const t=u();if(!t)return;try{const e=await t.getAllKeys();const r=e.filter(n=>n.startsWith(I.base));if(r.length>0){await t.multiRemove(r)}}catch{}}};const N={async getItem(t){if(!g())return a.get(t)??null;try{return window.localStorage.getItem(t)}catch{return a.get(t)??null}},async setItem(t,e){await B(t,e)},async removeItem(t){a.delete(t);if(!g())return;try{window.localStorage.removeItem(t)}catch{}},async getAllKeys(){if(!g())return Array.from(a.keys());try{const t=[];const e=window.localStorage;for(let r=0;r<e.length;r++){const n=e.key(r);if(n&&n.startsWith(I.base)){t.push(n)}}return t}catch{return Array.from(a.keys())}},async multiGet(t){const e=[];for(const r of t){const n=await this.getItem(r);e.push([r,n])}return e},async multiRemove(t){for(const e of t){await this.removeItem(e)}},async clear(){a.clear();if(!g())return;try{const t=await this.getAllKeys();const e=window.localStorage;for(const r of t){e.removeItem(r)}}catch{}}};const q={async getItem(t){return a.get(t)??null},async setItem(t,e){a.set(t,e)},async removeItem(t){a.delete(t)},async getAllKeys(){return Array.from(a.keys())},async multiGet(t){return t.map(e=>[e,a.get(e)??null])},async multiRemove(t){t.forEach(e=>a.delete(e))},async clear(){a.clear()}};async function H(){if(i!==null)return;const t=await T();if(t){const e=await K.getAllKeys();if(e.length===0){await j()}i="filesystem"}else if(g()){i="localstorage"}else{const e=u();if(e){i="asyncstorage";if(!h){h=true;console.warn("[Buoy]","[React Buoy] Using AsyncStorage fallback. Settings may be lost if AsyncStorage is cleared. Install expo-file-system for persistent storage.")}}else{i="memory";if(!h){h=true;console.warn("[Buoy]","[React Buoy] Using in-memory storage. Settings will be lost on app restart.")}}}}function l(){if(!S){S=H()}return S}function d(){switch(i){case"filesystem":return K;case"asyncstorage":return k;case"localstorage":return N;case"memory":default:return q}}export const persistentStorage={async getItem(t){await l();return d().getItem(t)},async setItem(t,e){await l();await d().setItem(t,e);x(t,e)},async removeItem(t){await l();await d().removeItem(t);x(t,null)},async getAllKeys(){await l();return d().getAllKeys()},async multiGet(t){await l();return d().multiGet(t)},async multiRemove(t){await l();return d().multiRemove(t)},async clear(){await l();return d().clear()}};export async function setItemChecked(t,e,r){await l();if(r?.strict&&R())return false;let n;switch(i){case"filesystem":n=await U(t,e);break;case"asyncstorage":n=await b(t,e);break;case"localstorage":n=await B(t,e);break;default:await persistentStorage.setItem(t,e);return false}x(t,e);return n}export async function getItemChecked(t){await l();if(R())throw new Error("The saved files can't be used right now.");switch(i){case"filesystem":{const e=f();const r=A(t);if(!e||!r)throw new Error("Storage isn't ready.");return F(e,r)}case"asyncstorage":{const e=u();if(!e)throw new Error("Storage isn't ready.");return e.getItem(t)}case"localstorage":if(!g())throw new Error("Storage isn't ready.");return window.localStorage.getItem(t);default:return a.get(t)??null}}export async function isUsingPersistentStorage(){await l();return i==="filesystem"}export async function storageIsMemoryOnly(){await l();return(i??"memory")==="memory"}export async function getStorageBackendType(){await l();return i??"memory"}export function resetStorageInit(){i=null;S=null;h=false}export function setFileSystemForTests(t){c=t??null;resetStorageInit()}
@@ -1 +1 @@
1
- "use strict";import{devToolsStorageKeys as o}from"../storage/devToolsStorageKeys.js";import{notifyStorageWrite as c}from"./storageWrites.js";const r=new Map;function a(){try{return typeof window==="undefined"?null:window.localStorage}catch{return null}}export const persistentStorage={async getItem(t){if(r.has(t))return r.get(t)??null;try{return a()?.getItem(t)??null}catch{return null}},async setItem(t,e){r.set(t,e);try{const n=a();if(n){n.setItem(t,e);r.delete(t)}}catch{}c(t,e)},async removeItem(t){r.set(t,null);try{const e=a();if(e){e.removeItem(t);r.delete(t)}}catch{}c(t,null)},async getAllKeys(){const t=new Set;try{const e=a();if(e)for(let n=0;n<e.length;n++){const s=e.key(n);if(s?.startsWith(o.base))t.add(s)}}catch{}for(const[e,n]of r){if(!e.startsWith(o.base))continue;if(n===null)t.delete(e);else t.add(e)}return[...t]},async multiGet(t){return Promise.all(t.map(async e=>[e,await persistentStorage.getItem(e)]))},async multiRemove(t){await Promise.all(t.map(e=>persistentStorage.removeItem(e)))},async clear(){await persistentStorage.multiRemove(await persistentStorage.getAllKeys())}};export async function isUsingPersistentStorage(){return a()!==null}export async function getStorageBackendType(){return a()?"localstorage":"memory"}
1
+ "use strict";import{devToolsStorageKeys as s}from"../storage/devToolsStorageKeys.js";import{notifyStorageWrite as c}from"./storageWrites.js";const a=new Map;function r(){try{return typeof window==="undefined"?null:window.localStorage}catch{return null}}export const persistentStorage={async getItem(t){if(a.has(t))return a.get(t)??null;try{return r()?.getItem(t)??null}catch{return null}},async setItem(t,e){a.set(t,e);try{const n=r();if(n){n.setItem(t,e);a.delete(t)}}catch{}c(t,e)},async removeItem(t){a.set(t,null);try{const e=r();if(e){e.removeItem(t);a.delete(t)}}catch{}c(t,null)},async getAllKeys(){const t=new Set;try{const e=r();if(e)for(let n=0;n<e.length;n++){const o=e.key(n);if(o?.startsWith(s.base))t.add(o)}}catch{}for(const[e,n]of a){if(!e.startsWith(s.base))continue;if(n===null)t.delete(e);else t.add(e)}return[...t]},async multiGet(t){return Promise.all(t.map(async e=>[e,await persistentStorage.getItem(e)]))},async multiRemove(t){await Promise.all(t.map(e=>persistentStorage.removeItem(e)))},async clear(){await persistentStorage.multiRemove(await persistentStorage.getAllKeys())}};export async function isUsingPersistentStorage(){return r()!==null}export async function getStorageBackendType(){return r()?"localstorage":"memory"}export async function setItemChecked(t,e,n){await persistentStorage.setItem(t,e);try{return r()?.getItem(t)===e}catch{return false}}export async function getItemChecked(t){const e=r();if(!e)return a.get(t)??null;return e.getItem(t)}export async function storageIsMemoryOnly(){return r()===null}
@@ -1 +1 @@
1
- "use strict";function t(){const o=globalThis.expo;if(!o?.modules?.ExpoGo||typeof o.reloadAppAsync!=="function")return null;const n=o.reloadAppAsync.bind(o);return e=>{void n(e)}}export function loadDevSettings(){const o=t();if(o)return{reload:o};try{const n=require("react-native");const e=n?.DevSettings;if(e&&typeof e.reload==="function"){return e}}catch{}return null}export function loadExpoUpdates(){try{const o=require("expo-updates");if(o&&typeof o.reloadAsync==="function"){return o}}catch{}return null}export function loadBrowserReload(){return null}
1
+ "use strict";function t(){const o=globalThis.expo;if(!o?.modules?.ExpoGo||typeof o.reloadAppAsync!=="function")return null;const e=o.reloadAppAsync.bind(o);return n=>{void e(n)}}export function loadDevSettings(){const o=t();if(o)return{reload:o};try{const e=require("react-native");const n=e?.DevSettings;if(n&&typeof n.reload==="function"){return n}}catch{}return null}export function loadExpoUpdates(){const o=globalThis.expo?.modules;if(!o?.ExpoUpdates)return null;try{const e=require("expo-updates");if(e&&typeof e.reloadAsync==="function"){return e}}catch{}return null}export function loadBrowserReload(){return null}
@@ -1 +1 @@
1
- "use strict";import{boundedDeepEqual as s,jsonLikelyLargerThan as p}from"../dataViewer/objectDiff.js";export const PREVIEW_ITEM_RENDER_BYTE_LIMIT=256*1024;export function createRestoreScope(t){const n=new Set(t?.excludeKeys??[]);const u=t?.includeKeys?new Set(t.includeKeys):null;let e=0;const o=r=>!n.has(r)&&(!u||u.has(r));return{touch:o,allows(r){if(o(r))return true;e+=1;return false},countExcluded(r){e+=r},get excludedHits(){return e},excludedWarning(r){return e>0?`${e} ${r} excluded by you`:null}}}export function emptyRestoreResult(){return{ok:true,applied:0,skipped:[],warnings:[]}}export function failedRestore(t){return{ok:false,applied:0,skipped:[],warnings:[t]}}export function skippedRestore(t,n){return{ok:false,applied:0,skipped:[{id:t,reason:n}],warnings:[]}}export function classifyPreviewItem(t,n={}){const{key:u,label:e,oldValue:o,newValue:r,inOld:i,inNew:l}=t;if(!l)return{key:u,label:e,verdict:"removed",oldValue:o};if(!i)return{key:u,label:e,verdict:"added",newValue:r};const c=n.equal??s;if(c(o,r))return{key:u,label:e,verdict:"unchanged"};const d=n.tooLarge??(a=>p(a,PREVIEW_ITEM_RENDER_BYTE_LIMIT));if(d(o)||d(r)){return{key:u,label:e,verdict:"too-large",reason:n.tooLargeReason??"value too large to render a diff \u2014 restore still applies it"}}return{key:u,label:e,verdict:"changed",oldValue:o,newValue:r}}export function wontApply(t,n){if(t.verdict==="unchanged")return t;return{...t,verdict:"wont-apply",reason:n}}export function unionKeys(t,n){const u=e=>e===void 0?[]:e instanceof Map?Array.from(e.keys()):Object.keys(e);return new Set([...u(t),...u(n)])}
1
+ "use strict";import{boundedDeepEqual as a,jsonLikelyLargerThan as p}from"../dataViewer/objectDiff.js";export const PREVIEW_ITEM_RENDER_BYTE_LIMIT=256*1024;export function createRestoreScope(t){const n=new Set(t?.excludeKeys??[]);const u=t?.includeKeys?new Set(t.includeKeys):null;let e=0;const o=(r,d)=>{if(r.has(d))return true;const s=d.indexOf("/");return d.startsWith("mmkv:")&&s>5&&r.has(d.slice(0,s))};const i=r=>!o(n,r)&&(!u||o(u,r));return{touch:i,allows(r){if(i(r))return true;e+=1;return false},countExcluded(r){e+=r},get excludedHits(){return e},excludedWarning(r){return e>0?`${e} ${r} excluded by you`:null}}}export function emptyRestoreResult(){return{ok:true,applied:0,skipped:[],warnings:[]}}export function failedRestore(t){return{ok:false,applied:0,skipped:[],warnings:[t]}}export function skippedRestore(t,n){return{ok:false,applied:0,skipped:[{id:t,reason:n}],warnings:[]}}export function classifyPreviewItem(t,n={}){const{key:u,label:e,oldValue:o,newValue:i,inOld:r,inNew:d}=t;if(!d)return{key:u,label:e,verdict:"removed",oldValue:o};if(!r)return{key:u,label:e,verdict:"added",newValue:i};const s=n.equal??a;if(s(o,i))return{key:u,label:e,verdict:"unchanged"};const l=n.tooLarge??(c=>p(c,PREVIEW_ITEM_RENDER_BYTE_LIMIT));if(l(o)||l(i)){return{key:u,label:e,verdict:"too-large",reason:n.tooLargeReason??"value too large to render a diff \u2014 restore still applies it"}}return{key:u,label:e,verdict:"changed",oldValue:o,newValue:i}}export function wontApply(t,n){if(t.verdict==="unchanged")return t;return{...t,verdict:"wont-apply",reason:n}}export function unionKeys(t,n){const u=e=>e===void 0?[]:e instanceof Map?Array.from(e.keys()):Object.keys(e);return new Set([...u(t),...u(n)])}
@@ -100,10 +100,9 @@ interface JsModalProps {
100
100
  /** Chrome variant — see {@link ModalVariant}. */
101
101
  variant?: ModalVariant;
102
102
  /**
103
- * iOS, bottom sheet only: move the sheet up while the keyboard is open, so
104
- * a field in the sheet and the buttons under it stay in view. On by
105
- * default. Pass false when the body handles the keyboard itself (Ask
106
- * Buoy's composer pads for it), or the two would lift it twice.
103
+ * Lift the bottom sheet above the iOS keyboard. On by default. A body that
104
+ * keeps its own input above the keyboard (Ask Buoy's KeyboardPad) passes
105
+ * false, or it would move twice.
107
106
  */
108
107
  avoidKeyboard?: boolean;
109
108
  }
@@ -85,6 +85,8 @@ export interface ModalState {
85
85
  };
86
86
  }
87
87
  export interface JsModalProps {
88
+ /** Native only (lifts the sheet above the iOS keyboard). Accepted here for parity. */
89
+ avoidKeyboard?: boolean;
88
90
  visible: boolean;
89
91
  onClose: () => void;
90
92
  children: ReactNode;
@@ -0,0 +1,17 @@
1
+ export interface BugCaseAppInfo {
2
+ /** Bundle id (iOS) or package name (Android). */
3
+ id?: string;
4
+ /** The version people see, like "4.2.0". */
5
+ version?: string;
6
+ /** The build number, like "4210". */
7
+ build?: string;
8
+ }
9
+ type Modules = {
10
+ application?: unknown;
11
+ constants?: unknown;
12
+ };
13
+ /** Tests pass stand-ins for expo-application and expo-constants (undefined: not installed). */
14
+ export declare function setAppInfoModulesForTests(modules: Modules | null): void;
15
+ export declare function bugCaseAppInfo(): BugCaseAppInfo;
16
+ export {};
17
+ //# sourceMappingURL=appInfo.d.ts.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The web build's app facts: none. A browser page has no bundle id or build
3
+ * number to read, and the native modules appInfo.ts reads don't exist here
4
+ * (the web build refuses them). The case leaves these fields out.
5
+ */
6
+ export interface BugCaseAppInfo {
7
+ id?: string;
8
+ version?: string;
9
+ build?: string;
10
+ }
11
+ export declare function bugCaseAppInfo(): BugCaseAppInfo;
12
+ /** The same test hook as the native file, so shared tests import either. */
13
+ export declare function setAppInfoModulesForTests(_modules: unknown): void;
14
+ //# sourceMappingURL=appInfo.web.d.ts.map
@@ -10,6 +10,7 @@ import type { BugCase, BugCaseNetworkCall } from "./types";
10
10
  export declare function isFailedCall(call: BugCaseNetworkCall): boolean;
11
11
  export declare function formatBugCase(kase: BugCase, options?: {
12
12
  link?: string;
13
- maxCalls?: number;
13
+ maxCalls?: number; /** Saved picture files, in picture order (MCP). */
14
+ pictureFiles?: readonly string[];
14
15
  }): string;
15
16
  //# sourceMappingURL=format.d.ts.map
@@ -1,3 +1,5 @@
1
+ export { bugCaseAppInfo } from "./appInfo";
2
+ export type { BugCaseAppInfo } from "./appInfo";
1
3
  export { captureBugCase, DEFAULT_BUG_CASE_BUDGET } from "./capture";
2
4
  export type { BugCaseBudget, CaptureBugCaseOptions } from "./capture";
3
5
  export { base64UrlDecode, base64UrlEncode, BUG_CASE_LOCK_FORMAT, bugCaseHeader, BugCaseLockError, bugLinkUrl, lockBugCase, MAX_BUG_CASE_BYTES, parseBugLink, readBugCaseHeader, unlockBugCase, webCaseCrypto, } from "./lock";
@@ -5,10 +7,15 @@ export type { BugCaseCrypto, BugCaseHeader, BugCaseLockErrorCode, LockedBugCase
5
7
  export { bugCaseRecorderStatus, startBugCaseRecorder, stopBugCaseRecorder } from "./recorder";
6
8
  export type { BugCaseRecorderStatus } from "./recorder";
7
9
  export { formatBugCase, isFailedCall } from "./format";
10
+ export { BugCaseShapeError, validateBugCase } from "./validate";
11
+ export { addDroppedWhy, base64Bytes, cleanDroppedWhy, DEFAULT_MEDIA_BUDGET, describeDroppedWhy, DROPPED_NOT_JPEG, DROPPED_TO_FIT, fitBugCaseMedia, frameSteps, validateBugCaseMedia, } from "./media";
12
+ export type { BugCaseFrame, BugCaseFrameKind, BugCaseMedia, BugCaseMediaBudget, BugCaseMediaSettings } from "./media";
8
13
  export { classifyKey, keyWords, REMOVED_MARK, removedMark, ScrubLimitError, scrubBugCase } from "./scrub";
9
14
  export type { ScrubbedItem, ScrubKind, ScrubResult, ScrubRules } from "./scrub";
10
15
  export { bugCaseSteps, formatBugCaseSteps, safeTouch } from "./steps";
11
16
  export type { BugCaseRouteVisit, BugCaseStep, BugCaseTouch } from "./steps";
12
17
  export { BUG_CASE_VERSION } from "./types";
13
18
  export type { BugCase, BugCaseConsoleLine, BugCaseMissing, BugCaseNetworkCall, BugCaseScreenElement, } from "./types";
19
+ export { blobSha256, deviceCode, deviceCommit, newDeviceNonce, DEVICE_NONCE_BYTES, fitCard, keyProof, keyProofHash, kitId, kitKey, lockSecret, makeKit, MAX_CARD_JSON_BYTES, newCaseKeyParts, openCard, openKitForDevice, openSecret, pairBytes, pairFrom, checkPair, parseKit, parseWrap, rootLockAad, sealCard, sealKitForDevice, signRecord, teamKeyIdOf, TeamKeyError, unwrapCaseKey, verifyRecord, versionLockAad, webTeamKeyCrypto, wrapCaseKey, WRAP_SUITE, } from "./teamKey";
20
+ export type { BugCard, BugCardFields, BugWrap, CaseKeyParts, KitKind, P256Pair, TeamKeyCrypto, TeamKeyErrorCode, TeamKeyRecord } from "./teamKey";
14
21
  //# sourceMappingURL=index.d.ts.map
@@ -39,6 +39,13 @@ export interface BugCaseHeader {
39
39
  errors: number;
40
40
  removed: number;
41
41
  missing: number;
42
+ /**
43
+ * 16 random bytes, base64url. The team wrap and the card are bound to it.
44
+ * Optional: blobs from before team keys have none, and still open.
45
+ */
46
+ caseTag?: string;
47
+ /** SHA-256 of the key proof (teamKey.ts `keyProof`), base64url. Comes with `caseTag`. */
48
+ keyProofHash?: string;
42
49
  }
43
50
  export interface LockedBugCase {
44
51
  blob: Uint8Array;
@@ -74,9 +81,15 @@ export declare function bugCaseHeader(kase: BugCase, bytes: number): BugCaseHead
74
81
  * `options.key` locks under an existing link's key (with a fresh nonce), so a
75
82
  * case can be replaced, say to add a note, and the link people already have
76
83
  * keeps working.
84
+ *
85
+ * `caseTag` and `keyProofHash` (teamKey.ts `newCaseKeyParts`) go in the plain
86
+ * header for team keys. A note edit passes the first tag again, so it never
87
+ * changes.
77
88
  */
78
89
  export declare function lockBugCase(kase: BugCase, crypto?: BugCaseCrypto, options?: {
79
90
  key?: string;
91
+ caseTag?: string;
92
+ keyProofHash?: string;
80
93
  }): Promise<LockedBugCase>;
81
94
  /** For the server: the plain header, read without the key. */
82
95
  export declare function readBugCaseHeader(blob: Uint8Array): BugCaseHeader;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Frames in a bug case: pictures and video
3
+ * (research/bug-links-2026-10/media/SPEC.md, rules C1 to C5).
4
+ *
5
+ * A frame is a boxed JPEG from the phone. The boxes went on in native code
6
+ * before the frame was kept (H1), so nothing here looks inside the pixels.
7
+ * The text scrubber never sees `jpeg` either (C4): capture adds media after
8
+ * the scrub.
9
+ */
10
+ export type BugCaseFrameKind = "last" | "tap" | "video";
11
+ export interface BugCaseFrame {
12
+ /** Steady clock, ms (F10). Frames, taps, errors and calls share it. */
13
+ t: number;
14
+ /** Wall time, ms since 1970, for people. */
15
+ wall?: number;
16
+ /** "last" is the app just before Buoy opened (F8). */
17
+ kind: BugCaseFrameKind;
18
+ /** The touch's seq, for kind "tap". */
19
+ tap?: number;
20
+ /** Size in points. */
21
+ w: number;
22
+ h: number;
23
+ /** Base64 JPEG. */
24
+ jpeg: string;
25
+ /** How many parts were boxed. */
26
+ boxes: number;
27
+ }
28
+ export interface BugCaseMediaSettings {
29
+ maskAllText: boolean;
30
+ maskAllImages: boolean;
31
+ maskAllInputs: boolean;
32
+ }
33
+ export interface BugCaseMedia {
34
+ frames: BugCaseFrame[];
35
+ /** "Bug here" marks on the steady clock (C2a). */
36
+ marks: number[];
37
+ settings: BugCaseMediaSettings;
38
+ /** How many frames C2 left out, so the viewer can say so. */
39
+ dropped: number;
40
+ /** Why they were left out, as short reasons with a count each (C5). Old cases have none. */
41
+ droppedWhy?: Record<string, number>;
42
+ }
43
+ export interface BugCaseMediaBudget {
44
+ frames: number;
45
+ bytes: number;
46
+ }
47
+ /** C2. */
48
+ export declare const DEFAULT_MEDIA_BUDGET: BugCaseMediaBudget;
49
+ /** Bytes a base64 string decodes to. */
50
+ export declare function base64Bytes(b64: string): number;
51
+ /**
52
+ * C2: keep the frames that matter most, inside the budget. In order: the
53
+ * "last" frame, tap frames (newest first), video frames near a mark (closest
54
+ * first), then the other video frames spread across the clip.
55
+ */
56
+ export declare function fitBugCaseMedia(frames: BugCaseFrame[], marks: number[], budget?: BugCaseMediaBudget): {
57
+ frames: BugCaseFrame[];
58
+ dropped: number;
59
+ };
60
+ /** Reasons the case itself adds when it leaves frames out. */
61
+ export declare const DROPPED_TO_FIT = "left out to fit the case";
62
+ export declare const DROPPED_NOT_JPEG = "not a picture Buoy can show";
63
+ /** Adds `n` to one reason, keeping the list short and every reason plain. */
64
+ export declare function addDroppedWhy(why: Record<string, number>, reason: string, n: number): Record<string, number>;
65
+ /** The phone's reasons, kept only when each is plain words with a whole count. */
66
+ export declare function cleanDroppedWhy(raw: unknown): Record<string, number>;
67
+ /**
68
+ * Which step each frame belongs to (V1): a tap frame goes with the step its
69
+ * tap made. A frame with no tap link has no step.
70
+ */
71
+ export declare function frameSteps(frames: readonly BugCaseFrame[], touches: readonly {
72
+ seq?: number;
73
+ t: number;
74
+ }[], steps: readonly {
75
+ t: number;
76
+ taps?: number[];
77
+ }[]): (number | null)[];
78
+ /** "4 the screen was moving, 1 left out to fit the case", biggest first. */
79
+ export declare function describeDroppedWhy(why: Record<string, number> | undefined): string;
80
+ /**
81
+ * 0..n-1 in an order where every start of the list is spread across the
82
+ * range: the two ends, then the middle, then the quarters, and so on.
83
+ */
84
+ export declare function spreadOrder(n: number): number[];
85
+ /**
86
+ * C3: keep `media` only with frames that are JPEGs. A bad frame is dropped,
87
+ * not the case. Then C2 again, since a hand-made case can hold anything.
88
+ */
89
+ export declare function validateBugCaseMedia(raw: unknown): BugCaseMedia | undefined;
90
+ //# sourceMappingURL=media.d.ts.map
@@ -2,6 +2,7 @@ export interface BugCaseRecorderStatus {
2
2
  watching: string[];
3
3
  touches: boolean;
4
4
  }
5
+ /** Start recording, or add the tools that registered since the last call. */
5
6
  export declare function startBugCaseRecorder(): Promise<BugCaseRecorderStatus>;
6
7
  export declare function stopBugCaseRecorder(): void;
7
8
  export declare function bugCaseRecorderStatus(): BugCaseRecorderStatus;
@@ -22,8 +22,8 @@
22
22
  * NO "LOOKS RANDOM" RULE. Entropy caught nothing the rules above missed and
23
23
  * broke order ids, hashes and commit SHAs. It was dropped on the evidence.
24
24
  *
25
- * Removed values become `[removed:secret]` / `[removed:pii]` so the loader can
26
- * graft the teammate's own value back into the same spot (see graft.ts).
25
+ * Removed values become `[removed:secret]` / `[removed:pii]`. A load writes
26
+ * the marks as they are; Restore puts the teammate's own values back.
27
27
  *
28
28
  * Pure and synchronous so it can be argued with in a unit test. Callers on a
29
29
  * phone run it after the capture has been shown as done, off the tap.
@@ -14,6 +14,8 @@ export interface BugCaseTouch {
14
14
  /** Capture order. Wins over `t`, which moves when the app clock is faked. */
15
15
  seq?: number;
16
16
  t: number;
17
+ /** Real time, which Buoy's clock tool never moves. Tap pictures are matched by it. Old cases have none. */
18
+ wall?: number;
17
19
  kind: "press" | "longPress" | "text" | "toggle" | "slider";
18
20
  target: {
19
21
  name: string;
@@ -32,7 +34,15 @@ export interface BugCaseRouteVisit {
32
34
  export interface BugCaseStep {
33
35
  t: number;
34
36
  text: string;
37
+ /** The touches (by seq) this step stands for, so a tap picture finds its step. Old cases have none. */
38
+ taps?: number[];
35
39
  }
40
+ /**
41
+ * A tap on React Native's own dev chrome (the LogBox warning banners and
42
+ * their inspector), not on the app. It showed up on the link page as
43
+ * "Tapped LogBoxButton" when a tester closed a warning.
44
+ */
45
+ export declare function isDevChromeTouch(touch: BugCaseTouch): boolean;
36
46
  /**
37
47
  * Drop what a case must never carry: the text a field held or received.
38
48
  * Toggle and slider values stay; they are what the step is about.
@@ -0,0 +1,185 @@
1
+ /** A P-256 key pair: the 32-byte private scalar and the 65-byte uncompressed public point. */
2
+ export interface P256Pair {
3
+ d: Uint8Array;
4
+ pub: Uint8Array;
5
+ }
6
+ export interface TeamKeyCrypto {
7
+ randomBytes(length: number): Uint8Array;
8
+ sha256(data: Uint8Array): Promise<Uint8Array>;
9
+ hmacSha256(key: Uint8Array, data: Uint8Array): Promise<Uint8Array>;
10
+ /** AES-GCM with a 16- or 32-byte key; returns ciphertext with the tag at the end. */
11
+ seal(key: Uint8Array, nonce: Uint8Array, data: Uint8Array, aad: Uint8Array): Promise<Uint8Array>;
12
+ /** Throws when the key is wrong or a byte changed. */
13
+ open(key: Uint8Array, nonce: Uint8Array, sealed: Uint8Array, aad: Uint8Array): Promise<Uint8Array>;
14
+ p256Generate(): Promise<P256Pair>;
15
+ /** The shared x coordinate (32 bytes). Throws on a point that isn't on the curve. */
16
+ p256Ecdh(own: P256Pair, peerPub: Uint8Array): Promise<Uint8Array>;
17
+ /** ECDSA over SHA-256 of `message`; 64 bytes, r then s. */
18
+ p256Sign(own: P256Pair, message: Uint8Array): Promise<Uint8Array>;
19
+ /** Takes high-S signatures too: WebCrypto makes them. */
20
+ p256Verify(pub: Uint8Array, message: Uint8Array, signature: Uint8Array): Promise<boolean>;
21
+ }
22
+ export type TeamKeyErrorCode =
23
+ /** A kit that isn't a kit, or the wrong kind. */
24
+ "bad_kit"
25
+ /** The kit is fine but doesn't open this lock. */
26
+ | "wrong_kit"
27
+ /** A record, wrap or card that isn't the right shape. */
28
+ | "bad_shape"
29
+ /** A signature or a key id that doesn't check out. */
30
+ | "untrusted"
31
+ /** Won't open: the wrong key, or a byte changed. */
32
+ | "locked" | "too_big";
33
+ export declare class TeamKeyError extends Error {
34
+ readonly code: TeamKeyErrorCode;
35
+ constructor(code: TeamKeyErrorCode, message: string);
36
+ }
37
+ export declare function concatBytes(...parts: Uint8Array[]): Uint8Array;
38
+ export declare function hkdfExtract(c: TeamKeyCrypto, salt: Uint8Array, ikm: Uint8Array): Promise<Uint8Array>;
39
+ export declare function hkdfExpand(c: TeamKeyCrypto, prk: Uint8Array, info: Uint8Array, length: number): Promise<Uint8Array>;
40
+ export declare function hkdf(c: TeamKeyCrypto, ikm: Uint8Array, info: string, length?: number): Promise<Uint8Array>;
41
+ export declare const HPKE_AES128_GCM = 1;
42
+ export declare const HPKE_AES256_GCM = 2;
43
+ /** Exposed for the RFC vector test. */
44
+ export declare function hpkeKeySchedule(c: TeamKeyCrypto, sharedSecret: Uint8Array, info: Uint8Array, aeadId: number): Promise<{
45
+ key: Uint8Array<ArrayBufferLike>;
46
+ baseNonce: Uint8Array<ArrayBufferLike>;
47
+ }>;
48
+ /**
49
+ * One message to `pkR` (sequence 0). `ephemeral` is for test vectors only;
50
+ * a real seal always makes a fresh one.
51
+ */
52
+ export declare function hpkeSeal(c: TeamKeyCrypto, pkR: Uint8Array, info: Uint8Array, aad: Uint8Array, plaintext: Uint8Array, options?: {
53
+ aeadId?: number;
54
+ ephemeral?: P256Pair;
55
+ }): Promise<{
56
+ enc: Uint8Array;
57
+ ct: Uint8Array;
58
+ sharedSecret: Uint8Array;
59
+ }>;
60
+ export declare function hpkeOpen(c: TeamKeyCrypto, skR: P256Pair, encapped: Uint8Array, info: Uint8Array, aad: Uint8Array, ct: Uint8Array, aeadId?: number): Promise<Uint8Array>;
61
+ export type KitKind = "team" | "admin";
62
+ /** A new kit as text, like `buoy-team-7F3Q-K2M9-…`. Shown once; never sent to Buoy. */
63
+ export declare function makeKit(c: TeamKeyCrypto, kind: KitKind): string;
64
+ export declare function formatKit(kind: KitKind, bytes: Uint8Array): string;
65
+ /** Reads a pasted kit. Spaces, dashes and case don't matter; O reads as 0 and I or L as 1. */
66
+ export declare function parseKit(text: string, kind: KitKind): Uint8Array;
67
+ /** The kit's lock key. */
68
+ export declare function kitKey(c: TeamKeyCrypto, kit: Uint8Array, kind: KitKind): Promise<Uint8Array>;
69
+ /** A short id for a kit, so the server can say which kit locked a key. It can't open anything. */
70
+ export declare function kitId(c: TeamKeyCrypto, kit: Uint8Array, kind: KitKind): Promise<string>;
71
+ /** `nonce | sealed`, as base64url. */
72
+ export declare function lockSecret(c: TeamKeyCrypto, key: Uint8Array, aad: string, secret: Uint8Array): Promise<string>;
73
+ export declare function openSecret(c: TeamKeyCrypto, key: Uint8Array, aad: string, locked: string): Promise<Uint8Array>;
74
+ export declare const rootLockAad: (owner: string, teamKeyId: string) => string;
75
+ export declare const versionLockAad: (owner: string, teamKeyId: string, keyVersion: number) => string;
76
+ /** A private key and its public key, as stored inside a lock. */
77
+ export declare const pairBytes: (pair: P256Pair) => Uint8Array<ArrayBufferLike>;
78
+ export declare function pairFrom(bytes: Uint8Array, expectPub?: Uint8Array): P256Pair;
79
+ /**
80
+ * Checks a private key really belongs to its public key: a pair that doesn't
81
+ * would make wraps no one can open. ECDH with itself and with a fresh key
82
+ * from each side must agree.
83
+ */
84
+ export declare function checkPair(c: TeamKeyCrypto, pair: P256Pair): Promise<void>;
85
+ /** A root's id: the SHA-256 of its public key. Apps can pin it. */
86
+ export declare function teamKeyIdOf(c: TeamKeyCrypto, rootPub: Uint8Array): Promise<string>;
87
+ export interface TeamKeyRecord {
88
+ owner: string;
89
+ teamKeyId: string;
90
+ keyVersion: number;
91
+ /** The version's ECDH public key, base64url. */
92
+ pub: string;
93
+ /** The root's signature over `recordMessage`, base64url. */
94
+ sig: string;
95
+ }
96
+ export declare function recordMessage(owner: string, teamKeyId: string, keyVersion: number, pub: string): Uint8Array;
97
+ export declare function signRecord(c: TeamKeyCrypto, root: P256Pair, owner: string, keyVersion: number, versionPub: Uint8Array): Promise<TeamKeyRecord>;
98
+ /** Throws unless `record` is signed by `rootPub` and names that root. Returns the version's public key. */
99
+ export declare function verifyRecord(c: TeamKeyCrypto, rootPub: Uint8Array, record: TeamKeyRecord): Promise<Uint8Array>;
100
+ export interface CaseKeyParts {
101
+ /** The link's key, base64url (32 bytes). */
102
+ key: string;
103
+ /** 16 random bytes, base64url: goes in the blob's plain header. */
104
+ caseTag: string;
105
+ /** SHA-256 of the key proof, base64url: goes in the blob's plain header. */
106
+ keyProofHash: string;
107
+ }
108
+ export declare function newCaseTag(c: TeamKeyCrypto): string;
109
+ export declare const isCaseTag: (text: unknown) => text is string;
110
+ export declare const isKeyHash: (text: unknown) => text is string;
111
+ /**
112
+ * Proof that someone holds a case's key, without showing the key. The blob's
113
+ * header has its hash, so the server can check a late wrap comes from someone
114
+ * who can open the case.
115
+ */
116
+ export declare function keyProof(c: TeamKeyCrypto, caseKey: string): Promise<string>;
117
+ export declare function keyProofHash(c: TeamKeyCrypto, proof: string): Promise<string>;
118
+ /** A fresh key, a fresh tag, and the key proof's hash, for a new case. */
119
+ export declare function newCaseKeyParts(c: TeamKeyCrypto, existing?: {
120
+ key?: string;
121
+ caseTag?: string;
122
+ }): Promise<CaseKeyParts>;
123
+ export declare const WRAP_SUITE = "P256-SHA256-A256GCM";
124
+ export interface BugWrap {
125
+ v: 1;
126
+ suite: typeof WRAP_SUITE;
127
+ teamKeyId: string;
128
+ keyVersion: number;
129
+ /** The HPKE key share, base64url (65 bytes). */
130
+ enc: string;
131
+ /** The sealed case key, base64url (48 bytes). */
132
+ ct: string;
133
+ }
134
+ export declare const wrapInfo: (owner: string, teamKeyId: string, keyVersion: number, caseTag: string) => Uint8Array<ArrayBufferLike>;
135
+ export declare function wrapCaseKey(c: TeamKeyCrypto, target: {
136
+ owner: string;
137
+ teamKeyId: string;
138
+ keyVersion: number;
139
+ pub: Uint8Array;
140
+ }, caseKey: string, caseTag: string): Promise<BugWrap>;
141
+ export declare function unwrapCaseKey(c: TeamKeyCrypto, wrap: BugWrap, owner: string, versionKey: P256Pair, caseTag: string): Promise<string>;
142
+ /** Rebuilt field by field from untrusted JSON. */
143
+ export declare function parseWrap(raw: unknown): BugWrap;
144
+ export interface BugCardFields {
145
+ note?: string;
146
+ app?: string;
147
+ version?: string;
148
+ build?: string;
149
+ route?: string;
150
+ error?: string;
151
+ sender?: string;
152
+ }
153
+ export interface BugCard extends BugCardFields {
154
+ v: 1;
155
+ caseTag: string;
156
+ /** SHA-256 of the blob this card goes with, base64url. */
157
+ blobSha256: string;
158
+ }
159
+ /** The whole card as JSON stays under this, so the locked card fits in 4 KB. */
160
+ export declare const MAX_CARD_JSON_BYTES = 3800;
161
+ /** Cuts text to at most `maxBytes` of UTF-8, at a whole letter, with "…" when cut. */
162
+ export declare function cutUtf8(text: string, maxBytes: number): string;
163
+ /** Caps each field, then trims the note, the error and the route until the JSON fits. */
164
+ export declare function fitCard(card: BugCard): BugCard;
165
+ export declare function blobSha256(c: TeamKeyCrypto, blob: Uint8Array): Promise<string>;
166
+ /** `nonce | sealed`, as base64url. */
167
+ export declare function sealCard(c: TeamKeyCrypto, caseKey: string, caseTag: string, cardRevision: number, fields: BugCardFields, blob: Uint8Array): Promise<string>;
168
+ export declare function openCard(c: TeamKeyCrypto, caseKey: string, caseTag: string, cardRevision: number, sealed: string): Promise<BugCard>;
169
+ export declare const DEVICE_NONCE_BYTES = 16;
170
+ export declare function newDeviceNonce(c: TeamKeyCrypto): string;
171
+ /** What the new device sends first: a hash of its key and nonce. */
172
+ export declare function deviceCommit(c: TeamKeyCrypto, devicePub: Uint8Array, deviceNonce: string): Promise<string>;
173
+ /** The code both screens show. If they match, no one swapped the device's key. */
174
+ export declare function deviceCode(c: TeamKeyCrypto, check: {
175
+ owner: string;
176
+ requestId: string;
177
+ devicePub: Uint8Array;
178
+ deviceNonce: string;
179
+ approverNonce: string;
180
+ }): Promise<string>;
181
+ /** The approving device seals the team kit to the new device's public key. */
182
+ export declare function sealKitForDevice(c: TeamKeyCrypto, owner: string, requestId: string, devicePub: Uint8Array, kit: Uint8Array): Promise<string>;
183
+ export declare function openKitForDevice(c: TeamKeyCrypto, owner: string, requestId: string, device: P256Pair, sealed: string): Promise<Uint8Array>;
184
+ export declare function webTeamKeyCrypto(): TeamKeyCrypto;
185
+ //# sourceMappingURL=teamKey.d.ts.map
@@ -6,6 +6,7 @@
6
6
  import type { SuperJSONResult } from "../utils/minimalSuperJSON";
7
7
  import type { ScrubbedItem } from "./scrub";
8
8
  import type { BugCaseStep, BugCaseTouch } from "./steps";
9
+ import type { BugCaseMedia } from "./media";
9
10
  export declare const BUG_CASE_VERSION = 1;
10
11
  export interface BugCaseNetworkCall {
11
12
  t: number;
@@ -19,6 +20,8 @@ export interface BugCaseNetworkCall {
19
20
  requestData?: unknown;
20
21
  responseHeaders?: Record<string, string>;
21
22
  responseData?: unknown;
23
+ /** The answer was parsed from JSON text, so a string was a JSON string. Missing in older cases. */
24
+ responseJson?: boolean;
22
25
  /** True when a body was over the case budget and left out. */
23
26
  bodyOmitted?: boolean;
24
27
  }
@@ -48,6 +51,11 @@ export interface BugCase {
48
51
  app: {
49
52
  platform: string;
50
53
  os: string;
54
+ /** Bundle id or package name, when the app could say. */
55
+ id?: string;
56
+ /** The version people see ("4.2.0") and the build number ("4210"), so a load on another build can say so. */
57
+ version?: string;
58
+ build?: string;
51
59
  appearance?: string;
52
60
  locale?: string;
53
61
  timeZone?: string;
@@ -82,6 +90,8 @@ export interface BugCase {
82
90
  /** B2: newest first, with answers for replay. */
83
91
  network: BugCaseNetworkCall[];
84
92
  console: BugCaseConsoleLine[];
93
+ /** Pictures and video, boxed on the phone (media/SPEC.md C1). Old cases and old phones have none. */
94
+ media?: BugCaseMedia;
85
95
  missing: BugCaseMissing[];
86
96
  scrub: {
87
97
  removed: number;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Check an opened bug case before anyone uses it.
3
+ *
4
+ * A case comes from someone else's phone, and a hand-made blob can say
5
+ * anything. Every reader (the link page, the Mac, the phone's loader) gets the
6
+ * same shapes: wrong-typed parts become empty, oversize text is cut, and a
7
+ * case without its core (version, date, app) is refused.
8
+ */
9
+ import { type BugCase } from "./types";
10
+ export declare class BugCaseShapeError extends Error {
11
+ constructor(part: string);
12
+ }
13
+ export declare function validateBugCase(raw: unknown): BugCase;
14
+ //# sourceMappingURL=validate.d.ts.map
@@ -15,7 +15,7 @@
15
15
  * - Metro sees static imports only
16
16
  * - Works with any Metro version
17
17
  */
18
- export { clipboardFunction, clipboardType, isClipboardAvailable, type ClipboardFunction, } from "./clipboard-impl";
18
+ export { clipboardFunction, clipboardType, isClipboardAvailable, readClipboardText, type ClipboardFunction, } from "./clipboard-impl";
19
19
  import { clipboardFunction } from "./clipboard-impl";
20
20
  /**
21
21
  * Attempts to auto-detect and return a clipboard implementation.
@@ -21,4 +21,10 @@ export type ClipboardFunction = (text: string) => Promise<boolean>;
21
21
  export declare const clipboardType: () => "expo" | "react-native" | "web" | null;
22
22
  export declare const isClipboardAvailable: () => boolean;
23
23
  export declare const clipboardFunction: ClipboardFunction;
24
+ /**
25
+ * The clipboard's text, for a Paste button. Only ever called from a tap: iOS
26
+ * shows its "pasted from" banner for each read, and a silent read on open
27
+ * would show it for nothing (the bug links panels ruled that out).
28
+ */
29
+ export declare const readClipboardText: () => Promise<string | null>;
24
30
  //# sourceMappingURL=clipboard-impl.d.ts.map
@@ -2,4 +2,6 @@ export type ClipboardFunction = (text: string) => Promise<boolean>;
2
2
  export declare const isClipboardAvailable: () => boolean;
3
3
  export declare const clipboardType: () => "web" | null;
4
4
  export declare const clipboardFunction: ClipboardFunction;
5
+ /** The clipboard's text, for a Paste button. Only ever called from a tap. */
6
+ export declare const readClipboardText: () => Promise<string | null>;
5
7
  //# sourceMappingURL=clipboard-impl.web.d.ts.map
@@ -1,4 +1,4 @@
1
1
  export { copyToClipboard } from "./copyToClipboard";
2
- export { createAutoDetectedClipboard, getAutoDetectedClipboard, } from "./autoDetectClipboard";
2
+ export { createAutoDetectedClipboard, getAutoDetectedClipboard, readClipboardText, } from "./autoDetectClipboard";
3
3
  export type { ClipboardFunction } from "./autoDetectClipboard";
4
4
  //# sourceMappingURL=index.d.ts.map