@buoy-gg/clock 7.0.42

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 (65) hide show
  1. package/LICENSE +58 -0
  2. package/README.md +29 -0
  3. package/lib/commonjs/components/ClockBody.js +1 -0
  4. package/lib/commonjs/components/ClockChip.js +1 -0
  5. package/lib/commonjs/components/ClockToolView.js +1 -0
  6. package/lib/commonjs/components/TokenSection.js +1 -0
  7. package/lib/commonjs/components/format.js +1 -0
  8. package/lib/commonjs/components/useLiveClock.js +1 -0
  9. package/lib/commonjs/engine/clockMath.js +1 -0
  10. package/lib/commonjs/engine/clockStore.js +1 -0
  11. package/lib/commonjs/engine/patches.js +1 -0
  12. package/lib/commonjs/engine/persistence.js +1 -0
  13. package/lib/commonjs/engine/readSavedSync.js +1 -0
  14. package/lib/commonjs/engine/readSavedSync.web.js +1 -0
  15. package/lib/commonjs/engine/timers.js +1 -0
  16. package/lib/commonjs/index.js +1 -0
  17. package/lib/commonjs/package.json +1 -0
  18. package/lib/commonjs/preset.js +1 -0
  19. package/lib/commonjs/sync/clockSyncAdapter.js +1 -0
  20. package/lib/commonjs/tokens/decode.js +1 -0
  21. package/lib/commonjs/tokens/extract.js +1 -0
  22. package/lib/commonjs/tokens/tokenActions.js +1 -0
  23. package/lib/commonjs/tokens/tokenStore.js +1 -0
  24. package/lib/module/components/ClockBody.js +1 -0
  25. package/lib/module/components/ClockChip.js +1 -0
  26. package/lib/module/components/ClockToolView.js +1 -0
  27. package/lib/module/components/TokenSection.js +1 -0
  28. package/lib/module/components/format.js +1 -0
  29. package/lib/module/components/useLiveClock.js +1 -0
  30. package/lib/module/engine/clockMath.js +1 -0
  31. package/lib/module/engine/clockStore.js +1 -0
  32. package/lib/module/engine/patches.js +1 -0
  33. package/lib/module/engine/persistence.js +1 -0
  34. package/lib/module/engine/readSavedSync.js +1 -0
  35. package/lib/module/engine/readSavedSync.web.js +1 -0
  36. package/lib/module/engine/timers.js +1 -0
  37. package/lib/module/index.js +1 -0
  38. package/lib/module/preset.js +1 -0
  39. package/lib/module/sync/clockSyncAdapter.js +1 -0
  40. package/lib/module/tokens/decode.js +1 -0
  41. package/lib/module/tokens/extract.js +1 -0
  42. package/lib/module/tokens/tokenActions.js +1 -0
  43. package/lib/module/tokens/tokenStore.js +1 -0
  44. package/lib/typescript/components/ClockBody.d.ts +2 -0
  45. package/lib/typescript/components/ClockChip.d.ts +11 -0
  46. package/lib/typescript/components/ClockToolView.d.ts +10 -0
  47. package/lib/typescript/components/TokenSection.d.ts +10 -0
  48. package/lib/typescript/components/format.d.ts +18 -0
  49. package/lib/typescript/components/useLiveClock.d.ts +22 -0
  50. package/lib/typescript/engine/clockMath.d.ts +70 -0
  51. package/lib/typescript/engine/clockStore.d.ts +94 -0
  52. package/lib/typescript/engine/patches.d.ts +43 -0
  53. package/lib/typescript/engine/persistence.d.ts +32 -0
  54. package/lib/typescript/engine/readSavedSync.d.ts +9 -0
  55. package/lib/typescript/engine/readSavedSync.web.d.ts +4 -0
  56. package/lib/typescript/engine/timers.d.ts +44 -0
  57. package/lib/typescript/index.d.ts +19 -0
  58. package/lib/typescript/preset.d.ts +45 -0
  59. package/lib/typescript/sync/clockSyncAdapter.d.ts +30 -0
  60. package/lib/typescript/tokens/decode.d.ts +67 -0
  61. package/lib/typescript/tokens/extract.d.ts +75 -0
  62. package/lib/typescript/tokens/tokenActions.d.ts +28 -0
  63. package/lib/typescript/tokens/tokenStore.d.ts +116 -0
  64. package/lib/web/index.mjs +1 -0
  65. package/package.json +112 -0
package/LICENSE ADDED
@@ -0,0 +1,58 @@
1
+ PROPRIETARY SOFTWARE LICENSE
2
+
3
+ Copyright (c) 2024-present Buoy. All rights reserved.
4
+
5
+ This software and its source code are proprietary and confidential.
6
+
7
+ NOTICE: This is NOT open source software. This software is licensed,
8
+ not sold, and is protected by copyright laws and international treaties.
9
+
10
+ TERMS AND CONDITIONS:
11
+
12
+ 1. LICENSE GRANT
13
+ Subject to the terms of this Agreement and payment of applicable fees,
14
+ Buoy grants you a limited, non-exclusive, non-transferable license
15
+ to use the compiled software packages in your applications.
16
+
17
+ 2. RESTRICTIONS
18
+ You may NOT:
19
+ - Copy, modify, or distribute the source code
20
+ - Reverse engineer, decompile, or disassemble the software
21
+ - Remove or alter any proprietary notices or labels
22
+ - Sublicense, rent, lease, or lend the software
23
+ - Use the software to create competing products
24
+ - Share access credentials with unauthorized parties
25
+
26
+ 3. OWNERSHIP
27
+ React Buoy retains all right, title, and interest in the software,
28
+ including all intellectual property rights. This license does not
29
+ grant you any rights to trademarks or service marks.
30
+
31
+ 4. TERMINATION
32
+ This license is effective until terminated. Your rights under this
33
+ license will terminate automatically without notice if you fail to
34
+ comply with any of its terms. Upon termination, you must cease all
35
+ use and destroy all copies of the software.
36
+
37
+ 5. DISCLAIMER OF WARRANTIES
38
+ THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
39
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
40
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
41
+
42
+ 6. LIMITATION OF LIABILITY
43
+ IN NO EVENT SHALL BUOY BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
44
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN
45
+ CONNECTION WITH THIS LICENSE OR THE USE OF THE SOFTWARE.
46
+
47
+ 7. GOVERNING LAW
48
+ This Agreement shall be governed by and construed in accordance with
49
+ the laws of the United States, without regard to its conflict of
50
+ law provisions.
51
+
52
+ For licensing inquiries and subscription information:
53
+ - Website: https://buoy.gg
54
+ - Email: AustinLovesWorking@gmail.com
55
+
56
+ Unauthorized reproduction or distribution of this software, or any
57
+ portion of it, may result in severe civil and criminal penalties,
58
+ and will be prosecuted to the maximum extent possible under the law.
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ <div align="center">
2
+
3
+ # @buoy-gg/clock
4
+
5
+ Move your React Native app's clock to any date, freeze it or speed it up, and jump ahead while the timers that come due still run. Test expiring offers, countdowns and renewals without changing the device's time, and check that your app refreshes its sign-in token before expiry and after a 401.
6
+
7
+ <a href="https://buoy.gg/buoy/latest/docs/tools/clock"><b>Clock guide</b></a> &nbsp;·&nbsp; <a href="https://buoy.gg">buoy.gg</a> &nbsp;·&nbsp; <a href="https://github.com/Buoy-gg/buoy">All tools</a>
8
+
9
+ [![npm version](https://img.shields.io/npm/v/@buoy-gg/clock?style=flat-square&labelColor=10302a&color=2a9d78)](https://www.npmjs.com/package/@buoy-gg/clock) [![npm downloads](https://img.shields.io/npm/dm/@buoy-gg/clock?style=flat-square&labelColor=10302a&color=2a9d78&label=downloads%2Fmonth)](https://www.npmjs.com/package/@buoy-gg/clock)
10
+
11
+ </div>
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install @buoy-gg/core @buoy-gg/clock
17
+ ```
18
+
19
+ The tool appears in `FloatingDevTools` on its own. If your app reads the clock while its modules load, import the package first in your entry file so a saved override applies before they run:
20
+
21
+ ```js
22
+ import "@buoy-gg/clock";
23
+ ```
24
+
25
+ The [Clock guide](https://buoy.gg/buoy/latest/docs/tools/clock) lists which time APIs follow the override and which stay on real time. New to Buoy? Start with the [Quick start](https://buoy.gg/buoy/latest/docs/quick-start).
26
+
27
+ ---
28
+
29
+ <sub>Part of <a href="https://buoy.gg">Buoy</a>, developer tools that live inside your app. Proprietary software. © Buoy LLC. <a href="https://buoy.gg/terms">Terms</a></sub>
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.ClockBody=ClockBody;var _react=require("react");var _sharedUi=require("@buoy-gg/shared-ui");var _clockStore=require("../engine/clockStore");var _clockSyncAdapter=require("../sync/clockSyncAdapter");var _tokenStore=require("../tokens/tokenStore");var _ClockToolView=require("./ClockToolView");var _jsxRuntime=require("react/jsx-runtime");const actions=_clockSyncAdapter.clockSyncAdapter.actions;async function runAction(o,e){const r=actions[o];if(!r)throw new Error(`Unknown clock action: ${o}`);return r(e)}function reload(){void(0,_sharedUi.reloadApp)("auto","buoy clock").catch(()=>{})}function ClockBody(){(0,_react.useEffect)(()=>{(0,_clockStore.bootClock)();(0,_tokenStore.startTokenWatch)()},[]);const[o,e]=(0,_react.useState)(_clockStore.getClockSnapshot);(0,_react.useEffect)(()=>(0,_clockStore.subscribeClock)(()=>e((0,_clockStore.getClockSnapshot)())),[]);return(0,_jsxRuntime.jsx)(_ClockToolView.ClockToolView,{snapshot:o,onAction:runAction,onReload:reload})}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.CLOCK_CHIP_ID=void 0;exports.ClockChip=ClockChip;exports.openClockChip=openClockChip;exports.watchClockChip=watchClockChip;var _react=require("react");var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _clockStore=require("../engine/clockStore");var _clockMath=require("../engine/clockMath");var _useLiveClock=require("./useLiveClock");var _format=require("./format");var _jsxRuntime=require("react/jsx-runtime");const CLOCK_CHIP_ID=exports.CLOCK_CHIP_ID="clock-chip";const registration={id:CLOCK_CHIP_ID,render:e=>(0,_jsxRuntime.jsx)(ClockChip,{...e})};const isChipOpen=()=>(0,_sharedUi.getFloatingTools)().some(e=>e.id===CLOCK_CHIP_ID);function openClockChip(){(0,_sharedUi.openFloatingTool)(registration)}let watching=false;function watchClockChip(){if(watching)return;watching=true;let e=false;const i=()=>{const t=(0,_clockStore.isClockOverridden)()&&(0,_clockStore.getClockSettings)().showChip;if(t&&!e)(0,_sharedUi.openFloatingTool)(registration);else if(!t&&isChipOpen())(0,_sharedUi.dismissFloatingTool)(CLOCK_CHIP_ID);e=t};(0,_clockStore.subscribeClock)(i);i()}function ClockChip({id:e,revealToken:i}){const t=(0,_react.useSyncExternalStore)(_clockStore.subscribeClock,_clockStore.getStableClockSnapshot,_clockStore.getStableClockSnapshot);const{virtual:r,real:s}=(0,_useLiveClock.useLiveClock)(t);const o=t.mode==="frozen";const l=(0,_format.clockStatus)(t);const c=r-s;const n=`${Math.abs(c)<1e3?"0s":(0,_clockMath.formatDuration)(c)} \xB7 ${l.label.toLowerCase()}`;return(0,_jsxRuntime.jsx)(_sharedUi.FloatingTool,{id:e,revealToken:i,variant:"strip",dragFromSurface:true,defaultPlacement:"top",accessibilityLabel:`Move or hide the clock chip. App time ${(0,_format.formatChipTime)(r)}`,handleAdornment:(0,_jsxRuntime.jsx)(_sharedUi.ClockToolIcon,{size:12}),children:(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.row,children:[(0,_jsxRuntime.jsx)(_sharedUi.ClockToolIcon,{size:18}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.text,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.time,numberOfLines:1,testID:"clock-chip-time",children:(0,_format.formatChipTime)(r)}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.detail,numberOfLines:1,testID:"clock-chip-detail",children:n})]}),(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{testID:o?"clock-chip-resume":"clock-chip-freeze",accessibilityRole:"button",accessibilityLabel:o?"Resume the app clock":"Freeze the app clock",onPress:()=>o?(0,_clockStore.resume)():(0,_clockStore.freeze)(),style:styles.button,hitSlop:{top:8,bottom:8,left:4,right:4},children:[o?(0,_jsxRuntime.jsx)(_sharedUi.Play,{size:16,color:_sharedUi.night.color.accent}):null,o?null:(0,_jsxRuntime.jsx)(_sharedUi.Pause,{size:16,color:_sharedUi.night.color.text})]}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.separator}),(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{testID:"clock-chip-reset",accessibilityRole:"button",accessibilityLabel:"Back to real time",onPress:()=>(0,_clockStore.reset)(),style:styles.button,hitSlop:{top:8,bottom:8,left:4,right:8},children:(0,_jsxRuntime.jsx)(_sharedUi.X,{size:16,color:_sharedUi.night.color.textSecondary})})]})})}const styles=_reactNative.StyleSheet.create({row:{flexDirection:"row",alignItems:"center",gap:10,paddingLeft:10,paddingRight:4,paddingVertical:6,minWidth:220},text:{flexShrink:1,minWidth:120},time:{color:"#F59E0B",fontSize:14,fontWeight:"600",fontVariant:["tabular-nums"]},detail:{color:_sharedUi.night.color.textSecondary,fontSize:11,fontVariant:["tabular-nums"]},button:{width:32,height:32,alignItems:"center",justifyContent:"center",borderRadius:16},separator:{width:_reactNative.StyleSheet.hairlineWidth,height:20,backgroundColor:_sharedUi.night.color.separator}});
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.ClockToolView=ClockToolView;var _react=require("react");var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _clockMath=require("../engine/clockMath");var _patches=require("../engine/patches");var _useLiveClock=require("./useLiveClock");var _TokenSection=require("./TokenSection");var _format=require("./format");var _jsxRuntime=require("react/jsx-runtime");const AMBER="#F59E0B";function ClockToolView({snapshot:e,onAction:t,onReload:i}){const o=(0,_sharedUi.useSafeAreaInsets)({minBottom:16});const[r,n]=(0,_react.useState)(null);const[s,d]=(0,_react.useState)(null);const l=(a,u)=>{n(null);t(a,u).catch(h=>{n(h instanceof Error?h.message:String(h))})};const c=a=>d(u=>u===a?null:a);if(!e){return(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.waiting,testID:"clock-waiting",children:(0,_jsxRuntime.jsx)(_sharedUi.NightFootnote,{centered:true,children:"Waiting for the app's clock\u2026"})})}return(0,_jsxRuntime.jsxs)(_reactNative.ScrollView,{style:styles.fill,contentContainerStyle:[styles.content,{paddingBottom:o.bottom}],testID:"clock-scroll",keyboardShouldPersistTaps:"handled",children:[(0,_jsxRuntime.jsx)(Hero,{snapshot:e}),r?(0,_jsxRuntime.jsx)(_sharedUi.NightFootnote,{tone:"danger",centered:true,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{testID:"clock-error",children:r})}):null,(0,_jsxRuntime.jsxs)(_reactNative.View,{children:[(0,_jsxRuntime.jsx)(_sharedUi.NightSectionLabel,{children:"Jump ahead"}),(0,_jsxRuntime.jsx)(JumpBar,{run:l})]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{children:[(0,_jsxRuntime.jsx)(_sharedUi.NightCard,{children:(0,_jsxRuntime.jsxs)(_sharedUi.NightRows,{children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{children:[(0,_jsxRuntime.jsx)(DisclosureRow,{title:"Date & Time",testID:"clock-date-row",open:s==="date",onPress:()=>c("date"),value:(0,_jsxRuntime.jsx)(LiveDateValue,{snapshot:e})}),s==="date"?(0,_jsxRuntime.jsx)(DateEditor,{snapshot:e,run:l,onDone:()=>d(null)}):null]},"date"),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.row,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.rowTitle,children:"Freeze"}),(0,_jsxRuntime.jsx)(_sharedUi.NightSwitch,{value:e.mode==="frozen",testID:"clock-freeze",accessibilityLabel:"Freeze the clock",onValueChange:a=>l(a?"freeze":"resume")})]},"freeze"),(0,_jsxRuntime.jsxs)(_reactNative.View,{children:[(0,_jsxRuntime.jsx)(DisclosureRow,{title:"Speed",testID:"clock-speed-row",open:s==="speed",onPress:()=>c("speed"),value:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.rowValue,children:speedLabel(e)})}),s==="speed"?(0,_jsxRuntime.jsx)(SpeedPicker,{snapshot:e,run:l}):null]},"speed")]})}),(0,_jsxRuntime.jsx)(_sharedUi.NightFootnote,{children:"Only this app's clock changes. Screens pick up the new time when they next render, or all at once when you reload."})]}),e.tokens?(0,_jsxRuntime.jsx)(_TokenSection.TokenSection,{snapshot:e,run:l,openId:s,onToggle:c}):null,(0,_jsxRuntime.jsx)(_sharedUi.NightCard,{children:(0,_jsxRuntime.jsxs)(_sharedUi.NightRows,{children:[i?(0,_jsxRuntime.jsx)(ActionRow,{label:"Reload App",testID:"clock-reload",onPress:i},"reload"):null,(0,_jsxRuntime.jsx)(ActionRow,{label:"Use Real Time",accessibilityLabel:"Reset to real time",testID:"clock-reset",destructive:true,disabled:e.mode==="real",onPress:()=>l("reset")},"reset")]})}),(0,_jsxRuntime.jsxs)(_reactNative.View,{children:[(0,_jsxRuntime.jsx)(_sharedUi.NightSectionLabel,{children:"Options"}),(0,_jsxRuntime.jsx)(OptionsCard,{settings:e.settings,run:l})]})]})}function Hero({snapshot:e}){const{virtual:t,real:i}=(0,_useLiveClock.useLiveClock)(e);const o=(0,_format.clockStatus)(e);const r=e.mode!=="real";const n=(0,_format.formatOffset)(t-i);const s=e.mode==="frozen"?" \xB7 Frozen":r&&e.rate!==1?` \xB7 ${speedLabel(e)}`:"";return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.hero,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.heroTime,r?styles.heroTimeActive:null],testID:"clock-hero-time",accessibilityLabel:`App clock ${(0,_format.formatClockTime)(t)}`,children:(0,_format.formatClockTime)(t)}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.heroDate,testID:"clock-hero-date",children:(0,_format.formatClockDate)(t)}),(0,_jsxRuntime.jsx)(_reactNative.View,{testID:"clock-status",accessibilityLabel:`Clock status: ${o.label}${r?`, ${n}`:""}`,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.heroStatus,r?styles.heroStatusActive:null],testID:"clock-offset",children:r?`${n}${s}`:"Real time"})})]})}const HOUR=36e5;const DAY=24*HOUR;const JUMP_BAR=[{ms:HOUR,unit:"hour"},{ms:DAY,unit:"day"},{ms:7*DAY,unit:"week"},{ms:30*DAY,unit:"month"}];function JumpBar({run:e}){return(0,_jsxRuntime.jsx)(_sharedUi.NightCard,{style:styles.jumpBar,children:JUMP_BAR.map((t,i)=>(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{testID:(0,_format.jumpTestId)(t.ms),accessibilityRole:"button",accessibilityLabel:`Jump ahead one ${t.unit}`,activeOpacity:.6,onPress:()=>e("shift",{by:t.ms}),style:[styles.jump,i>0?styles.jumpDivider:null],children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.jumpAmount,children:"+1"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.jumpUnit,children:t.unit})]},t.unit))})}function DisclosureRow({title:e,value:t,open:i,onPress:o,testID:r}){return(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{testID:r,accessibilityRole:"button",accessibilityLabel:e,accessibilityState:{expanded:i},activeOpacity:.6,onPress:o,style:styles.row,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.rowTitle,children:e}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.rowRight,children:[t,(0,_jsxRuntime.jsx)(_reactNative.View,{style:i?styles.chevronOpen:null,children:(0,_jsxRuntime.jsx)(_sharedUi.ChevronRight,{size:16,color:_sharedUi.night.color.textTertiary})})]})]})}function LiveDateValue({snapshot:e}){const{virtual:t}=(0,_useLiveClock.useLiveClock)(e,1e3);return(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.rowValue,styles.tabular],numberOfLines:1,children:(0,_format.formatChipTime)(t,false)})}function ActionRow({label:e,onPress:t,testID:i,destructive:o,disabled:r,accessibilityLabel:n}){return(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{testID:i,accessibilityRole:"button",accessibilityLabel:n??e,accessibilityState:{disabled:!!r},activeOpacity:.6,disabled:r,onPress:t,style:[styles.row,r?styles.rowDisabled:null],children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.actionLabel,o?styles.actionDestructive:null],children:e})})}const PRESETS=_clockMath.TIME_PRESETS.filter(e=>["midnight","month-end","year-end"].includes(e.id));function DateEditor({snapshot:e,run:t,onDone:i}){const[o,r]=(0,_react.useState)("");const{virtual:n}=(0,_useLiveClock.useLiveClock)(e,1e3);const s=()=>o.trim()||(0,_format.formatInputTime)(n);const d=e.mode==="frozen";const l=c=>{t("setTime",{time:c,freeze:d});i()};return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.editor,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.inputRow,children:[(0,_jsxRuntime.jsx)(_reactNative.TextInput,{testID:"clock-time-input",accessibilityLabel:"Date and time",value:o,onChangeText:r,placeholder:(0,_format.formatInputTime)(n),placeholderTextColor:_sharedUi.night.color.placeholder,autoCapitalize:"none",autoCorrect:false,returnKeyType:"done",onSubmitEditing:()=>l(s()),style:styles.input}),(0,_jsxRuntime.jsx)(_sharedUi.NightButton,{label:"Set",testID:"clock-time-apply",accessibilityLabel:"Set this time",size:"md",variant:"primary",fullWidth:false,onPress:()=>l(s())})]}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.hint,children:"Year-month-day and 24-hour time, or +3d, -2h"}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.presets,children:PRESETS.map(c=>(0,_jsxRuntime.jsx)(_sharedUi.NightChip,{label:c.label,testID:`clock-preset-${c.id}`,onPress:()=>l(c.at(n,_patches.RealDate))},c.id))})]})}const SPEED_TABS=[{key:"1",label:"Normal"},{key:"10",label:"10\xD7"},{key:"60",label:"1 min/s"},{key:"3600",label:"1 hr/s"}];function speedLabel(e){if(e.mode==="real"||e.rate===1)return"Normal";return SPEED_TABS.find(t=>Number(t.key)===e.rate)?.label??(0,_clockMath.formatRate)(e.rate)}function SpeedPicker({snapshot:e,run:t}){const i=e.mode==="real"?"1":String(e.rate);return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.editor,children:[(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.segmentRow,children:(0,_jsxRuntime.jsx)(_sharedUi.NightSegmented,{tabs:SPEED_TABS,activeKey:i,size:"sm",testIDPrefix:"clock-speed",onChange:o=>t("setRate",{rate:Number(o)})})}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.hint,children:"Taps, animations and timers keep real speed."})]})}const OPTIONS=[{key:"showChip",title:"Show Floating Clock"},{key:"persist",title:"Keep After Reload"}];function OptionsCard({settings:e,run:t}){return(0,_jsxRuntime.jsx)(_sharedUi.NightCard,{children:(0,_jsxRuntime.jsx)(_sharedUi.NightRows,{children:OPTIONS.map(i=>(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.row,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.rowTitle,children:i.title}),(0,_jsxRuntime.jsx)(_sharedUi.NightSwitch,{value:e[i.key],testID:`clock-setting-${i.key}`,accessibilityLabel:i.title,onValueChange:o=>t("updateSettings",{[i.key]:o})})]},i.key))})})}const styles=_reactNative.StyleSheet.create({fill:{flex:1},content:{paddingHorizontal:_sharedUi.night.gutter,paddingTop:8,gap:_sharedUi.night.groupGap},waiting:{flex:1,alignItems:"center",justifyContent:"center",padding:24},hero:{alignItems:"center",paddingTop:12},heroTime:{color:_sharedUi.night.color.text,fontSize:56,fontWeight:"200",fontVariant:["tabular-nums"],letterSpacing:1},heroTimeActive:{color:AMBER},heroDate:{color:_sharedUi.night.color.text,fontSize:17,fontWeight:"500",marginTop:2},heroStatus:{color:_sharedUi.night.color.textTertiary,fontSize:_sharedUi.night.font.caption,fontWeight:"500",marginTop:6},heroStatusActive:{color:_sharedUi.night.color.textSecondary},jumpBar:{flexDirection:"row"},jump:{flex:1,alignItems:"center",paddingVertical:12},jumpDivider:{borderLeftWidth:_reactNative.StyleSheet.hairlineWidth,borderLeftColor:_sharedUi.night.color.separator},jumpAmount:{color:AMBER,fontSize:17,fontWeight:"600",fontVariant:["tabular-nums"]},jumpUnit:{color:_sharedUi.night.color.textSecondary,fontSize:_sharedUi.night.font.caption,marginTop:1},row:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:12,minHeight:50,paddingHorizontal:_sharedUi.night.rowPadH,paddingVertical:10},rowDisabled:{opacity:.4},rowTitle:{color:_sharedUi.night.color.text,fontSize:_sharedUi.night.font.row},rowRight:{flexDirection:"row",alignItems:"center",gap:6,flexShrink:1},rowValue:{color:_sharedUi.night.color.textSecondary,fontSize:_sharedUi.night.font.row},tabular:{fontVariant:["tabular-nums"]},chevronOpen:{transform:[{rotate:"90deg"}]},actionLabel:{color:_sharedUi.night.color.accent,fontSize:_sharedUi.night.font.row},actionDestructive:{color:_sharedUi.night.color.danger},editor:{paddingHorizontal:_sharedUi.night.rowPadH,paddingBottom:14,gap:10},inputRow:{flexDirection:"row",alignItems:"center",gap:8},input:{flex:1,color:_sharedUi.night.color.text,fontFamily:_sharedUi.monoFont,fontSize:15,backgroundColor:_sharedUi.night.color.bg,borderRadius:_sharedUi.night.radius.input,borderWidth:1,borderColor:_sharedUi.night.color.border,paddingHorizontal:12,paddingVertical:8},hint:{color:_sharedUi.night.color.textTertiary,fontSize:_sharedUi.night.font.caption},presets:{flexDirection:"row",flexWrap:"wrap",gap:6},segmentRow:{flexDirection:"row"}});
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.TokenSection=TokenSection;var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _clockMath=require("../engine/clockMath");var _useLiveClock=require("./useLiveClock");var _format=require("./format");var _jsxRuntime=require("react/jsx-runtime");const size=e=>(0,_clockMath.formatDuration)(Math.abs(e),{signed:false});function TokenSection({snapshot:e,run:r,openId:o,onToggle:t}){const i=e.tokens;return(0,_jsxRuntime.jsxs)(_reactNative.View,{children:[(0,_jsxRuntime.jsx)(_sharedUi.NightSectionLabel,{children:i.tokens.length>1?"Sign-in tokens":"Sign-in token"}),(0,_jsxRuntime.jsx)(_sharedUi.NightCard,{children:(0,_jsxRuntime.jsxs)(_sharedUi.NightRows,{children:[i.tokens.map((n,s)=>(0,_jsxRuntime.jsxs)(_reactNative.View,{children:[(0,_jsxRuntime.jsx)(TokenRow,{token:n,snapshot:e,index:s,open:o===n.id,onPress:()=>t(n.id)}),o===n.id?(0,_jsxRuntime.jsx)(TokenDetail,{token:n,snapshot:e,run:r}):null]},n.id)),i.tokens.length===0?(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.row,testID:"clock-token-empty",children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.emptyText,children:i.available?"No token seen yet":"Needs the Network tool"})},"empty"):null]})}),(0,_jsxRuntime.jsx)(CheckNote,{check:i.check,tokens:i.tokens,snapshot:e,run:r}),i.tokens.length===0?(0,_jsxRuntime.jsx)(_sharedUi.NightFootnote,{children:i.available?"Tokens show up here when the app sends a signed-in request.":"Install @buoy-gg/network so the Clock tool can read the app's tokens."}):null]})}function TokenRow({token:e,snapshot:r,index:o,open:t,onPress:i}){const{virtual:n}=(0,_useLiveClock.useLiveClock)(r,1e3);const s=e.expiresAt!==void 0?e.expiresAt-n:void 0;const l=e.neverExpires?"No expiry":s===void 0?"Unknown":s<=0?"Expired":size(s);const a=s!==void 0&&s<=0?styles.valueDanger:s!==void 0&&s<6e4?styles.valueWarning:null;return(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{testID:`clock-token-${o}`,accessibilityRole:"button",accessibilityLabel:`${e.label} for ${e.host}: ${s!==void 0&&s>0?`expires in ${l}`:l}`,accessibilityState:{expanded:t},activeOpacity:.6,onPress:i,style:styles.row,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.rowText,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.rowTitle,numberOfLines:1,children:e.label}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.rowSubtitle,numberOfLines:1,children:`${e.host} \xB7 ${e.hint??e.formatLabel}`})]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.rowRight,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.value,a],testID:`clock-token-${o}-left`,children:l}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:t?styles.chevronOpen:null,children:(0,_jsxRuntime.jsx)(_sharedUi.ChevronRight,{size:16,color:_sharedUi.night.color.textTertiary})})]})]})}function TokenDetail({token:e,snapshot:r,run:o}){const{virtual:t,real:i}=(0,_useLiveClock.useLiveClock)(r,1e3);const n=detailLines(e,t,i);const s=e.expiresAt!==void 0&&e.expiresAt-t>1e4;return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.detail,testID:"clock-token-detail",children:[n.map(l=>(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.detailLine,l.tone==="warn"?styles.warn:null,l.tone==="bad"?styles.bad:null],children:l.text},l.text)),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.buttons,children:[(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.button,children:(0,_jsxRuntime.jsx)(_sharedUi.NightButton,{label:"Jump to Expiry",testID:"clock-token-jump",accessibilityLabel:"Jump the app's clock to ten seconds before this token expires",size:"md",disabled:!s,onPress:()=>o("jumpToTokenExpiry",{token:e.id})})}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.button,children:(0,_jsxRuntime.jsx)(_sharedUi.NightButton,{label:"Fail Next Request",testID:"clock-token-fail",accessibilityLabel:"Answer the app's next request to this API with 401",size:"md",onPress:()=>o("failNextRequest",{token:e.id})})})]}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.hint,children:s||e.expiresAt===void 0?"Jump tests an app that refreshes before expiry. Fail tests one that refreshes after a 401.":"This token has already expired on the app's clock. Use Real Time to jump to it again."})]})}function detailLines(e,r,o){const t=[];if(e.expiresAt!==void 0){t.push({text:`Expires ${(0,_format.formatChipTime)(e.expiresAt)}`});if(e.lifetimeMs!==void 0&&e.lifetimeMs>0){t.push({text:`Lasts ${size(e.lifetimeMs)}${e.expirySource?`, from ${e.expirySource}`:""}`})}if(e.serverExpiresAt!==void 0){const i=e.expiresAt-r;const n=e.serverExpiresAt-o;if(Math.abs(i-n)>2e3){t.push({text:n>0?`The server still accepts it for ${size(n)}`:`The server stopped accepting it ${size(n)} ago`})}}}else if(!e.neverExpires){t.push({text:"The token doesn't say when it expires, and no login response did either."})}if(e.hint)t.push({text:e.formatLabel});if(e.issuedBy)t.push({text:`Issued by ${e.issuedBy}`});if(e.refresh?.expiresAt!==void 0){const i=e.refresh.expiresAt-r;t.push({text:i>0?`Refresh token expires in ${size(i)}`:"Refresh token expired"})}if(e.refreshes>0&&e.lastRefreshAt!==void 0){t.push({text:`Refreshed ${e.refreshes===1?"once":`${e.refreshes} times`}, last at ${(0,_format.formatClockTime)(e.lastRefreshAt)}`})}t.push({text:`Sent with ${e.uses} request${e.uses===1?"":"s"}`});if(e.expiredSends>0){t.push({text:`Sent ${e.expiredSends} time${e.expiredSends===1?"":"s"} after it expired on the app's clock`,tone:"warn"})}if(e.rejected>0){t.push({text:`Rejected by the server ${e.rejected} time${e.rejected===1?"":"s"}`,tone:"warn"})}if(e.looping){t.push({text:`Refresh loop: ${e.recentRefreshes} new tokens in 20s`,tone:"bad"})}return t}function CheckNote({check:e,tokens:r,snapshot:o,run:t}){const{virtual:i,real:n}=(0,_useLiveClock.useLiveClock)(o,1e3);if(!e)return null;const s=r.find(d=>d.id===e.tokenId);const{text:l,tone:a}=verdict(e,s,i,n);return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.check,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.checkText,a==="good"?styles.good:null,a==="bad"?styles.bad:null,a==="warn"?styles.warn:null],testID:"clock-token-check",children:l}),(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{testID:"clock-token-check-done",accessibilityRole:"button",accessibilityLabel:e.status==="waiting"?"Stop the refresh check":"Dismiss",onPress:()=>t("stopTokenCheck"),hitSlop:{top:8,bottom:8,left:8,right:8},children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.checkDone,children:e.status==="waiting"?"Stop":"Done"})})]})}function verdict(e,r,o,t){if(e.status==="loop"){return{text:`Refresh loop: ${e.newTokens} new tokens since the check started. The new tokens may already look expired to the app's clock.`,tone:"bad"}}if(e.status==="refreshed"){const l=e.newIssuedBy?` by ${e.newIssuedBy}`:"";const a=e.newLifetimeMs&&e.newLifetimeMs>0?`. The new token lasts ${size(e.newLifetimeMs)}`:"";const d=e.kind==="401"&&e.forcedAt!==void 0?"The app got the 401 and refreshed":"Refreshed";const c=e.failedRefreshes>0?` ${e.failedRefreshes===1?"One token request":`${e.failedRefreshes} token requests`} failed first (${e.lastRefreshError}).`:"";return{text:`${d} at ${(0,_format.formatClockTime)(e.refreshedAt??t)}${l}${a}.${c}`,tone:c?"warn":"good"}}const i=t-e.startedAt;const n=(e.staleAfterFailure>0?` It sent the old token again ${e.staleAfterFailure} time${e.staleAfterFailure===1?"":"s"}.`:"")+(e.failedRefreshes>0?` Its token request failed (${e.lastRefreshError}).`:"");if(i>6e4){return{text:`No new token after ${size(i)}. The app may refresh on its next action, or not at all.${n}`,tone:"warn"}}if(e.kind==="401"){return e.forcedAt===void 0?{text:`The next request to ${r?.host??"the API"} will get a 401. Use the app to send one.`}:{text:`The app got the 401. Waiting for a new token\u2026${n}`,tone:n?"warn":void 0}}const s=r?.expiresAt!==void 0?r.expiresAt-o:void 0;return s!==void 0&&s>0?{text:`Expires in ${size(s)} on the app's clock. Waiting for a new token\u2026`}:{text:"Expired on the app's clock. Waiting for the app's next request\u2026"}}const styles=_reactNative.StyleSheet.create({row:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:12,minHeight:56,paddingHorizontal:_sharedUi.night.rowPadH,paddingVertical:10},rowText:{flex:1,minWidth:0},rowTitle:{color:_sharedUi.night.color.text,fontSize:_sharedUi.night.font.row},rowSubtitle:{color:_sharedUi.night.color.textTertiary,fontSize:_sharedUi.night.font.caption,marginTop:2},rowRight:{flexDirection:"row",alignItems:"center",gap:6},value:{color:_sharedUi.night.color.textSecondary,fontSize:_sharedUi.night.font.row,fontVariant:["tabular-nums"]},valueWarning:{color:_sharedUi.night.color.warning},valueDanger:{color:_sharedUi.night.color.danger},chevronOpen:{transform:[{rotate:"90deg"}]},emptyText:{color:_sharedUi.night.color.textSecondary,fontSize:_sharedUi.night.font.row},detail:{paddingHorizontal:_sharedUi.night.rowPadH,paddingBottom:14,gap:4},detailLine:{color:_sharedUi.night.color.textSecondary,fontSize:_sharedUi.night.font.caption},buttons:{flexDirection:"row",gap:8,marginTop:8},button:{flex:1},hint:{color:_sharedUi.night.color.textTertiary,fontSize:_sharedUi.night.font.caption,marginTop:6},check:{flexDirection:"row",alignItems:"flex-start",gap:12,marginTop:8,marginHorizontal:_sharedUi.night.labelInset},checkText:{flex:1,color:_sharedUi.night.color.textSecondary,fontSize:_sharedUi.night.font.caption,lineHeight:18},checkDone:{color:_sharedUi.night.color.accent,fontSize:_sharedUi.night.font.caption,fontWeight:"600"},good:{color:_sharedUi.night.color.accent},warn:{color:_sharedUi.night.color.warning},bad:{color:_sharedUi.night.color.danger}});
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.clockStatus=clockStatus;exports.formatChipTime=formatChipTime;exports.formatClockDate=formatClockDate;exports.formatClockTime=formatClockTime;exports.formatInputTime=formatInputTime;exports.formatOffset=formatOffset;exports.jumpTestId=jumpTestId;var _patches=require("../engine/patches");var _clockMath=require("../engine/clockMath");const pad=e=>String(e).padStart(2,"0");function formatClockTime(e){const t=new _patches.RealDate(e);return`${pad(t.getHours())}:${pad(t.getMinutes())}:${pad(t.getSeconds())}`}const WEEKDAYS=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];const MONTHS=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function formatClockDate(e){const t=new _patches.RealDate(e);return`${WEEKDAYS[t.getDay()]}, ${MONTHS[t.getMonth()]} ${t.getDate()}, ${t.getFullYear()}`}function formatChipTime(e,t=true){const r=new _patches.RealDate(e);const n=t?formatClockTime(e):`${pad(r.getHours())}:${pad(r.getMinutes())}`;return`${MONTHS[r.getMonth()]} ${r.getDate()} \xB7 ${n}`}function formatInputTime(e){const t=new _patches.RealDate(e);return`${t.getFullYear()}-${pad(t.getMonth()+1)}-${pad(t.getDate())} ${pad(t.getHours())}:${pad(t.getMinutes())}`}function formatOffset(e){if(Math.abs(e)<1e3)return"matches real time";const t=(0,_clockMath.formatDuration)(Math.abs(e),{signed:false});return e>0?`${t} ahead of real time`:`${t} behind real time`}function clockStatus(e){if(e.mode==="real")return{label:"Real time",tone:"neutral"};if(e.mode==="frozen")return{label:"Frozen",tone:"info"};if(e.rate!==1)return{label:`${(0,_clockMath.formatRate)(e.rate)} speed`,tone:"warning"};return{label:"Overridden",tone:"accent"}}function jumpTestId(e){const t=e<0?"minus":"plus";return`clock-jump-${t}-${(0,_clockMath.formatDuration)(Math.abs(e),{signed:false}).replace(/\s/g,"")}`}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useLiveClock=useLiveClock;var _react=require("react");var _patches=require("../engine/patches");function useLiveClock(e,t=250){const[,u]=(0,_react.useState)(0);(0,_react.useEffect)(()=>{const l=setInterval(()=>u(i=>i+1),t);return()=>clearInterval(l)},[t]);const r=(0,_patches.realNow)();if(!e)return{virtual:r,real:r};const a=r-e.realNow;const c=e.mode==="real"?1:e.rate;return{virtual:e.mode==="frozen"?e.virtualNow:e.virtualNow+a*c,real:r}}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.TIME_PRESETS=exports.SPEEDS=exports.REAL_ANCHOR=exports.MIN_RATE=exports.MAX_RATE=exports.JUMPS=void 0;exports.formatDuration=formatDuration;exports.formatRate=formatRate;exports.freezeAt=freezeAt;exports.isAnchor=isAnchor;exports.offsetAt=offsetAt;exports.parseDuration=parseDuration;exports.parseTime=parseTime;exports.resume=resume;exports.shiftBy=shiftBy;exports.travelTo=travelTo;exports.virtualAt=virtualAt;exports.withRate=withRate;const REAL_ANCHOR=exports.REAL_ANCHOR=Object.freeze({mode:"real",anchorReal:0,anchorVirtual:0,rate:1});const MIN_RATE=exports.MIN_RATE=.1;const MAX_RATE=exports.MAX_RATE=86400;const MAX_TIME=864e13;function virtualAt(e,t){if(e.mode==="real")return t;if(e.mode==="frozen")return e.anchorVirtual;return e.anchorVirtual+(t-e.anchorReal)*e.rate}function offsetAt(e,t){return virtualAt(e,t)-t}function assertTime(e){if(!Number.isFinite(e)||Math.abs(e)>MAX_TIME){throw new RangeError(`Not a valid time: ${e}`)}return Math.round(e)}function travelTo(e,t,r,n=false){return{mode:n?"frozen":"running",anchorReal:t,anchorVirtual:assertTime(r),rate:e.mode==="real"?1:e.rate}}function shiftBy(e,t,r){if(!Number.isFinite(r))throw new RangeError("Jump must be a number of ms");return travelTo(e,t,virtualAt(e,t)+r,e.mode==="frozen")}function freezeAt(e,t,r){return travelTo(e,t,r??virtualAt(e,t),true)}function resume(e,t){if(e.mode!=="frozen")return e;return{...e,mode:"running",anchorReal:t}}function withRate(e,t,r){if(!Number.isFinite(r)||r<MIN_RATE||r>MAX_RATE){throw new RangeError(`Speed must be between ${MIN_RATE}\xD7 and ${MAX_RATE}\xD7`)}if(e.mode==="real"&&r===1)return e;return{mode:"running",anchorReal:t,anchorVirtual:assertTime(virtualAt(e,t)),rate:r}}function isAnchor(e){const t=e;return!!t&&(t.mode==="real"||t.mode==="running"||t.mode==="frozen")&&Number.isFinite(t.anchorReal)&&Number.isFinite(t.anchorVirtual)&&Number.isFinite(t.rate)&&t.rate>=MIN_RATE&&t.rate<=MAX_RATE}const UNIT_MS={ms:1,millisecond:1,milliseconds:1,s:1e3,sec:1e3,secs:1e3,second:1e3,seconds:1e3,m:6e4,min:6e4,mins:6e4,minute:6e4,minutes:6e4,h:36e5,hr:36e5,hrs:36e5,hour:36e5,hours:36e5,d:864e5,day:864e5,days:864e5,w:6048e5,wk:6048e5,week:6048e5,weeks:6048e5,mo:30*864e5,month:30*864e5,months:30*864e5,y:365*864e5,yr:365*864e5,year:365*864e5,years:365*864e5};function parseDuration(e){if(typeof e==="number"){if(!Number.isFinite(e))throw new RangeError("Duration must be finite");return Math.round(e)}let t=String(e).trim().toLowerCase();if(!t)throw new RangeError("Duration is empty");let r=1;if(t[0]==="+"||t[0]==="-"||t[0]==="\u2212"){r=t[0]==="+"?1:-1;t=t.slice(1).trim()}if(/^\d+(\.\d+)?$/.test(t))return r*Math.round(Number(t));const n=/(\d+(?:\.\d+)?)\s*([a-z]+)/g;let s=0;let o="";let i;while(i=n.exec(t)){const a=UNIT_MS[i[2]];if(a===void 0){throw new RangeError(`Unknown time unit "${i[2]}" in "${e}"`)}s+=Number(i[1])*a;o+=i[0]}if(!o||o.replace(/\s/g,"")!==t.replace(/[\s,]/g,"")){throw new RangeError(`Could not read "${e}" as a duration. Try "+1d", "-2h30m" or "90s".`)}return r*Math.round(s)}function formatDuration(e,{signed:t=true}={}){const r=e<0?"\u2212":"+";let n=Math.abs(Math.round(e));if(n<1e3)return t&&n>0?`${r}${n}ms`:"0s";const s=[];const o=[["y",365*864e5],["d",864e5],["h",36e5],["m",6e4],["s",1e3]];for(const[i,a]of o){if(n>=a){s.push(`${Math.floor(n/a)}${i}`);n%=a}if(s.length===2)break}return`${t?r:""}${s.join(" ")}`}function parseTime(e,t,r){if(typeof e==="number")return assertTime(e);const n=String(e).trim();if(!n)throw new RangeError("Time is empty");if(/^now$/i.test(n))return t;if(/^[+\-−]/.test(n))return assertTime(t+parseDuration(n));if(/^\d{11,}$/.test(n))return assertTime(Number(n));const s=n.replace(/^(\d{4}-\d{2}-\d{2})[ ](\d{2}:\d{2})/,"$1T$2");const o=r(s);if(!Number.isFinite(o)){throw new RangeError(`Could not read "${e}" as a time. Use ISO 8601 like 2026-12-31T23:59:50, epoch ms, or "+1d".`)}return assertTime(o)}const LEAD_MS=1e4;const TIME_PRESETS=exports.TIME_PRESETS=[{id:"midnight",label:"Before midnight",hint:"23:59:50 tonight",at(e,t){const r=new t(e);r.setHours(24,0,0,0);return r.getTime()-LEAD_MS}},{id:"month-end",label:"Month end",hint:"last day, 23:59:50",at(e,t){const r=new t(e);r.setMonth(r.getMonth()+1,1);r.setHours(0,0,0,0);return r.getTime()-LEAD_MS}},{id:"year-end",label:"New Year's Eve",hint:"Dec 31, 23:59:50",at(e,t){const r=new t(e);r.setFullYear(r.getFullYear()+1,0,1);r.setHours(0,0,0,0);return r.getTime()-LEAD_MS}},{id:"tomorrow-9",label:"Tomorrow 9 AM",hint:"start of a business day",at(e,t){const r=new t(e);r.setDate(r.getDate()+1);r.setHours(9,0,0,0);return r.getTime()}},{id:"leap-day",label:"Leap day",hint:"next Feb 29, 09:00",at(e,t){const r=new t(e);let n=r.getFullYear();for(;;){const s=n%4===0&&n%100!==0||n%400===0;const o=new t(n,1,29,9,0,0,0).getTime();if(s&&o>e)return o;n+=1}}}];const JUMPS=exports.JUMPS=[{label:"1m",ms:6e4},{label:"15m",ms:15*6e4},{label:"1h",ms:36e5},{label:"1d",ms:864e5},{label:"7d",ms:7*864e5},{label:"30d",ms:30*864e5},{label:"1y",ms:365*864e5}];const SPEEDS=exports.SPEEDS=[1,2,10,60,600,3600];function formatRate(e){if(e===3600)return"1h/s";if(e===600)return"10m/s";if(e===60)return"1m/s";return`${Number.isInteger(e)?e:e.toFixed(1)}\xD7`}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.__resetClockStoreForTests=__resetClockStoreForTests;exports.bootClock=bootClock;exports.freeze=freeze;exports.getClockAnchor=getClockAnchor;exports.getClockSettings=getClockSettings;exports.getClockSnapshot=getClockSnapshot;exports.getStableClockSnapshot=getStableClockSnapshot;exports.isClockBooted=isClockBooted;exports.isClockOverridden=isClockOverridden;exports.previewJump=previewJump;exports.reset=reset;exports.resume=resume;exports.setRate=setRate;exports.setTime=setTime;exports.shift=shift;exports.subscribeClock=subscribeClock;exports.updateSettings=updateSettings;exports.virtualNow=virtualNow;var _clockMath=require("./clockMath");var _patches=require("./patches");var _timers=require("./timers");var _persistence=require("./persistence");var _tokenStore=require("../tokens/tokenStore");let state={anchor:_clockMath.REAL_ANCHOR,settings:_persistence.DEFAULT_SETTINGS,lastJump:null,changedAt:null,hydrated:false};const listeners=new Set;let snapshotCache=null;function emit(){snapshotCache=null;listeners.forEach(e=>e())}(0,_tokenStore.subscribeTokens)(emit);function subscribeClock(e){listeners.add(e);return()=>{listeners.delete(e)}}function getClockAnchor(){return state.anchor}function getClockSettings(){return state.settings}function virtualNow(){return(0,_clockMath.virtualAt)(state.anchor,(0,_patches.realNow)())}function isClockOverridden(){return state.anchor.mode!=="real"}function syncPatches(e){if(e.mode==="real"){(0,_patches.setVirtualSource)(null);(0,_patches.uninstallDatePatches)();return}(0,_timers.installTimerTracking)();(0,_patches.setVirtualSource)(t=>(0,_clockMath.virtualAt)(state.anchor,t));(0,_patches.installDatePatches)()}function persist(){const e={v:1,anchor:state.settings.persist?state.anchor:null,settings:state.settings};void(0,_persistence.writeSaved)(e)}function commit(e,{save:t=true}={}){state={...state,...e};if(e.anchor)syncPatches(state.anchor);if(t)persist();emit()}function setAnchor(e,t=state.lastJump){commit({anchor:e,lastJump:t,changedAt:(0,_patches.realNow)(),hydrated:true})}const parseDate=e=>_patches.RealDate.parse(e);function setTime(e,t=false){const r=(0,_patches.realNow)();const n=(0,_clockMath.virtualAt)(state.anchor,r);const o=(0,_clockMath.parseTime)(e,n,parseDate);const s=o-n;setAnchor((0,_clockMath.travelTo)(state.anchor,r,o,t),{byMs:s,firedTimers:0,at:r});return getClockSnapshot()}function shift(e,t){const r=(0,_clockMath.parseDuration)(e);const n=(0,_patches.realNow)();const o=(0,_clockMath.shiftBy)(state.anchor,n,r);const s=(t??state.settings.fireTimersOnJump)&&r>0;setAnchor(o,{byMs:r,firedTimers:0,at:n});if(s){const a=(0,_timers.fireDueWithin)(r);if(a>0){commit({lastJump:{byMs:r,firedTimers:a,at:n}},{save:false})}}return getClockSnapshot()}function freeze(e){const t=(0,_patches.realNow)();const r=(0,_clockMath.virtualAt)(state.anchor,t);const n=e===void 0||e===""?r:(0,_clockMath.parseTime)(e,r,parseDate);setAnchor((0,_clockMath.freezeAt)(state.anchor,t,n));return getClockSnapshot()}function resume(){if(state.anchor.mode==="frozen")setAnchor((0,_clockMath.resume)(state.anchor,(0,_patches.realNow)()));return getClockSnapshot()}function setRate(e){setAnchor((0,_clockMath.withRate)(state.anchor,(0,_patches.realNow)(),Number(e)));return getClockSnapshot()}function reset(){setAnchor(_clockMath.REAL_ANCHOR,null);return getClockSnapshot()}function updateSettings(e){const t={...state.settings};for(const r of Object.keys(_persistence.DEFAULT_SETTINGS)){if(typeof e[r]==="boolean")t[r]=e[r]}commit({settings:t});return getClockSnapshot()}function previewJump(e){return e>0?(0,_timers.countDueWithin)(e):0}let booted=false;function applySaved(e){if(!e){commit({hydrated:true},{save:false});return}commit({settings:e.settings,anchor:e.anchor??_clockMath.REAL_ANCHOR,changedAt:e.anchor?(0,_patches.realNow)():null,hydrated:true},{save:false})}function bootClock(){if(booted)return;booted=true;(0,_timers.installTimerTracking)();const e=(0,_persistence.readSavedSync)();if(e!==void 0){applySaved(e);return}void(0,_persistence.readSavedAsync)().then(t=>{if(state.changedAt===null)applySaved(t);else commit({hydrated:true},{save:false})})}function isClockBooted(){return booted}function deviceTimeZone(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone??"unknown"}catch{return"unknown"}}function getClockSnapshot(){const e=(0,_patches.realNow)();const t=(0,_clockMath.virtualAt)(state.anchor,e);return{mode:state.anchor.mode,active:state.anchor.mode!=="real",rate:state.anchor.rate,virtualNow:t,virtualIso:new _patches.RealDate(t).toISOString(),realNow:e,offsetMs:(0,_clockMath.offsetAt)(state.anchor,e),timeZone:deviceTimeZone(),timers:(0,_timers.summarizeTimers)(),lastJump:state.lastJump,changedAt:state.changedAt,settings:state.settings,hydrated:state.hydrated,tokens:(0,_tokenStore.getTokenReport)(),patched:{date:(0,_patches.isDatePatched)(),intl:(0,_patches.isIntlPatched)(),timers:(0,_timers.isTimerTrackingInstalled)(),animatedOnRealClock:(0,_patches.isAnimatedRealClock)()}}}function getStableClockSnapshot(){if(!snapshotCache)snapshotCache=getClockSnapshot();return snapshotCache}function __resetClockStoreForTests(){(0,_patches.setVirtualSource)(null);(0,_patches.uninstallDatePatches)();state={anchor:_clockMath.REAL_ANCHOR,settings:_persistence.DEFAULT_SETTINGS,lastJump:null,changedAt:null,hydrated:false};booted=false;emit()}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.RealDate=exports.PatchedDate=void 0;exports.appNow=appNow;exports.currentNow=currentNow;exports.installAnimatedRealClock=installAnimatedRealClock;exports.installDatePatches=installDatePatches;exports.isAnimatedRealClock=isAnimatedRealClock;exports.isDatePatched=isDatePatched;exports.isIntlPatched=isIntlPatched;exports.realNow=realNow;exports.setVirtualSource=setVirtualSource;exports.uninstallDatePatches=uninstallDatePatches;exports.withRealClock=withRealClock;const g=globalThis;const TAG="__buoyClock";const RealDate=exports.RealDate=g.__buoyRealDate??(Date[TAG]?Date.__buoyRealDate:Date);g.__buoyRealDate=RealDate;const realDateNow=RealDate.now.bind(RealDate);function realNow(){return realDateNow()}g.__buoyRealNow=realNow;let readVirtual=null;let realScopeDepth=0;function currentNow(){const e=realDateNow();if(!readVirtual||realScopeDepth>0)return e;return readVirtual(e)}function appNow(){const e=realDateNow();return readVirtual?readVirtual(e):e}function setVirtualSource(e){readVirtual=e}function withRealClock(e){realScopeDepth+=1;try{return e()}finally{realScopeDepth-=1}}g.__buoyWithRealClock=withRealClock;function ClockDate(...e){const t=new.target;if(!t)return new RealDate(currentNow()).toString();return Reflect.construct(RealDate,e.length===0?[currentNow()]:e,t)}ClockDate.prototype=RealDate.prototype;Object.defineProperty(ClockDate,"name",{value:"Date"});Object.defineProperty(ClockDate,"length",{value:7});Object.defineProperty(ClockDate,"now",{value:function e(){return currentNow()},writable:true,configurable:true});Object.defineProperty(ClockDate,"parse",{value:RealDate.parse,writable:true,configurable:true});Object.defineProperty(ClockDate,"UTC",{value:RealDate.UTC,writable:true,configurable:true});Object.defineProperty(ClockDate,TAG,{value:true});Object.defineProperty(ClockDate,"__buoyRealDate",{value:RealDate});const PatchedDate=exports.PatchedDate=ClockDate;let dateInstalled=false;let originalConstructorDesc;function isDatePatched(){return dateInstalled&&g.Date===PatchedDate}function installDate(){if(g.Date===PatchedDate){dateInstalled=true;return}const e=Object.getOwnPropertyDescriptor(g,"Date");Object.defineProperty(g,"Date",{value:PatchedDate,writable:e?.writable??true,enumerable:e?.enumerable??false,configurable:true});originalConstructorDesc=Object.getOwnPropertyDescriptor(RealDate.prototype,"constructor");try{Object.defineProperty(RealDate.prototype,"constructor",{value:PatchedDate,writable:true,enumerable:false,configurable:true})}catch{}dateInstalled=true}function uninstallDate(){if(g.Date===PatchedDate){const e=Object.getOwnPropertyDescriptor(g,"Date");Object.defineProperty(g,"Date",{value:RealDate,writable:e?.writable??true,enumerable:e?.enumerable??false,configurable:true})}if(originalConstructorDesc){try{Object.defineProperty(RealDate.prototype,"constructor",originalConstructorDesc)}catch{}originalConstructorDesc=void 0}dateInstalled=false}let intlPatches=[];function withDefaultDate(e){return function(t,...r){return e.call(this,t===void 0?currentNow():t,...r)}}function installIntl(){if(intlPatches.length)return;const e=g.Intl?.DateTimeFormat?.prototype;if(!e)return;for(const t of["format","formatToParts"]){const r=Object.getOwnPropertyDescriptor(e,t);if(!r||!r.configurable)continue;try{if(typeof r.get==="function"){const n=r.get;Object.defineProperty(e,t,{...r,get(){return withDefaultDate(n.call(this))}})}else if(typeof r.value==="function"){Object.defineProperty(e,t,{...r,value:withDefaultDate(r.value)})}else continue;intlPatches.push({proto:e,name:t,original:r})}catch{}}}function uninstallIntl(){for(const{proto:e,name:t,original:r}of intlPatches){try{Object.defineProperty(e,t,r)}catch{}}intlPatches=[]}function isIntlPatched(){return intlPatches.length>0}function installDatePatches(){installDate();installIntl()}function uninstallDatePatches(){uninstallDate();uninstallIntl()}let animatedInstalled=false;function wrapAnimationPrototype(e){let t=Object.getPrototypeOf(e);while(t&&t!==Object.prototype){if(!Object.prototype.hasOwnProperty.call(t,TAG)){for(const r of["start","onUpdate"]){const n=Object.getOwnPropertyDescriptor(t,r);if(n&&typeof n.value==="function"){const o=n.value;t[r]=function(...a){return withRealClock(()=>o.apply(this,a))}}}try{Object.defineProperty(t,TAG,{value:true})}catch{return}}t=Object.getPrototypeOf(t)}}function installAnimatedRealClock(e){if(animatedInstalled)return true;try{const t=e;if(typeof t?.Value!=="function")return false;const r=Object.getPrototypeOf(new t.Value(0));const n=r?.animate;if(typeof n!=="function")return false;r.animate=function(o,...a){if(o&&typeof o==="object"){wrapAnimationPrototype(o)}return n.call(this,o,...a)};animatedInstalled=true;return true}catch{return false}}function isAnimatedRealClock(){return animatedInstalled}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.DEFAULT_SETTINGS=exports.CLOCK_STORAGE_KEY=void 0;exports.parseSaved=parseSaved;exports.readSavedAsync=readSavedAsync;Object.defineProperty(exports,"readSavedSync",{enumerable:true,get:function(){return _readSavedSync.readSavedSync}});exports.writeSaved=writeSaved;var _sharedUi=require("@buoy-gg/shared-ui");var _clockMath=require("./clockMath");var _readSavedSync=require("./readSavedSync");const DEFAULT_SETTINGS=exports.DEFAULT_SETTINGS={persist:true,showChip:true,fireTimersOnJump:true};const CLOCK_STORAGE_KEY=exports.CLOCK_STORAGE_KEY=_sharedUi.devToolsStorageKeys.clock.state();function parseSaved(t){if(!t)return null;try{const e=JSON.parse(t);if(!e||e.v!==1)return null;const r=e.settings??{};return{v:1,anchor:(0,_clockMath.isAnchor)(e.anchor)?e.anchor:null,settings:{persist:typeof r.persist==="boolean"?r.persist:DEFAULT_SETTINGS.persist,showChip:typeof r.showChip==="boolean"?r.showChip:DEFAULT_SETTINGS.showChip,fireTimersOnJump:typeof r.fireTimersOnJump==="boolean"?r.fireTimersOnJump:DEFAULT_SETTINGS.fireTimersOnJump}}}catch{return null}}async function readSavedAsync(){try{return parseSaved(await _sharedUi.persistentStorage.getItem(CLOCK_STORAGE_KEY))}catch{return null}}let writes=Promise.resolve();function writeSaved(t){const e=JSON.stringify(t);writes=writes.then(()=>_sharedUi.persistentStorage.setItem(CLOCK_STORAGE_KEY,e)).catch(()=>{});return writes.then(()=>{})}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.readSavedSync=readSavedSync;var _persistence=require("./persistence");function fsNames(e){const n=encodeURIComponent(e.replace(/~/g,"~0").replace(/\//g,"~1"));return n===e?[e]:[n,e]}function readSavedSync(){try{const e=require("expo-file-system");if(typeof e?.File!=="function"||!e.Paths?.document)return void 0;const n=new e.File(e.Paths.document,"react-buoy");if(typeof n.textSync!=="function")return void 0;for(const r of fsNames(_persistence.CLOCK_STORAGE_KEY)){const t=new e.File(e.Paths.document,"react-buoy",`${r}.json`);if(t.exists)return(0,_persistence.parseSaved)(t.textSync())}return null}catch{return void 0}}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.readSavedSync=readSavedSync;function readSavedSync(){return void 0}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.countDueWithin=countDueWithin;exports.fireDueWithin=fireDueWithin;exports.installTimerTracking=installTimerTracking;exports.isTimerTrackingInstalled=isTimerTrackingInstalled;exports.summarizeTimers=summarizeTimers;exports.uninstallTimerTracking=uninstallTimerTracking;var _patches=require("./patches");const g=globalThis;const TAG="__buoyClockTimer";const pending=new Map;let originals=null;let jumpedMs=0;const timerClock=()=>(0,_patches.realNow)()+jumpedMs;function reportAsync(r){(originals?.setTimeout??setTimeout)(()=>{throw r},0)}function toDelay(r){const t=Number(r);return Number.isFinite(t)&&t>0?t:0}function isTimerTrackingInstalled(){return originals!==null}function installTimerTracking(){if(originals)return true;const{setTimeout:r,clearTimeout:t,setInterval:i,clearInterval:l}=g;if(typeof r!=="function"||typeof t!=="function")return false;if(r[TAG])return true;originals={setTimeout:r,clearTimeout:t,setInterval:typeof i==="function"?i:r,clearInterval:typeof l==="function"?l:t};const n=originals;const f=function(e,c,...u){if(typeof e!=="function")return n.setTimeout(e,c,...u);const m=toDelay(c);let a;const d=function(){pending.delete(a);return e.apply(this,u)};a=n.setTimeout(d,c);pending.set(a,{kind:"timeout",callback:e,args:u,delay:m,due:timerClock()+m,realHandle:a,run:d});return a};const o=function(e,c,...u){if(typeof e!=="function")return n.setInterval(e,c,...u);const m=toDelay(c);let a;const d=function(){const p=pending.get(a);if(p)p.due=timerClock()+m;return e.apply(this,u)};a=n.setInterval(d,c);pending.set(a,{kind:"interval",callback:e,args:u,delay:m,due:timerClock()+m,realHandle:a,run:d});return a};const s=(e,c)=>{const u=pending.get(e);if(!u)return c(e);pending.delete(e);return u.kind==="interval"?n.clearInterval(u.realHandle):n.clearTimeout(u.realHandle)};const T=function(e){return s(e,n.clearTimeout)};const v=function(e){return s(e,n.clearInterval)};globalThis.__buoyRealTimers={setTimeout:n.setTimeout,clearTimeout:T,setInterval:n.setInterval,clearInterval:v};for(const e of[f,o,T,v]){Object.defineProperty(e,TAG,{value:true})}g.setTimeout=f;g.clearTimeout=T;g.setInterval=o;g.clearInterval=v;return true}function uninstallTimerTracking(){if(!originals)return;g.setTimeout=originals.setTimeout;g.clearTimeout=originals.clearTimeout;g.setInterval=originals.setInterval;g.clearInterval=originals.clearInterval;originals=null;pending.clear();jumpedMs=0;delete globalThis.__buoyRealTimers}function summarizeTimers(){const r=timerClock();let t=0;let i=0;let l=null;for(const n of pending.values()){if(n.kind==="interval")i+=1;else{t+=1;const f=Math.max(0,n.due-r);if(l===null||f<l)l=f}}return{total:t+i,timeouts:t,intervals:i,nextTimeoutInMs:l}}function countDueWithin(r){const t=timerClock();let i=0;for(const l of pending.values())if(l.due-t<=r)i+=1;return i}function fireDueWithin(r){if(!originals||!(r>0))return 0;const t=originals;jumpedMs+=r;const i=timerClock();const l=[...pending.entries()].filter(([,o])=>o.due<=i).sort((o,s)=>o[1].due-s[1].due);let n=0;for(const[o,s]of l){if(!pending.has(o))continue;if(s.kind==="timeout"){pending.delete(o);t.clearTimeout(s.realHandle)}else{s.due=i+s.delay}try{s.callback(...s.args)}catch(T){reportAsync(T)}n+=1}const f=timerClock();for(const o of pending.values()){if(o.kind!=="timeout")continue;t.clearTimeout(o.realHandle);o.realHandle=t.setTimeout(o.run,Math.max(0,o.due-f))}return n}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"CLOCK_CHIP_ID",{enumerable:true,get:function(){return _ClockChip.CLOCK_CHIP_ID}});Object.defineProperty(exports,"CLOCK_TOOL_ICON_COLOR",{enumerable:true,get:function(){return _sharedUi.CLOCK_TOOL_ICON_COLOR}});Object.defineProperty(exports,"ClockBody",{enumerable:true,get:function(){return _ClockBody.ClockBody}});Object.defineProperty(exports,"ClockChip",{enumerable:true,get:function(){return _ClockChip.ClockChip}});Object.defineProperty(exports,"ClockToolIcon",{enumerable:true,get:function(){return _sharedUi.ClockToolIcon}});Object.defineProperty(exports,"ClockToolView",{enumerable:true,get:function(){return _ClockToolView.ClockToolView}});Object.defineProperty(exports,"TIME_PRESETS",{enumerable:true,get:function(){return _clockMath.TIME_PRESETS}});Object.defineProperty(exports,"bootClock",{enumerable:true,get:function(){return _clockStore.bootClock}});Object.defineProperty(exports,"clockSyncAdapter",{enumerable:true,get:function(){return _clockSyncAdapter.clockSyncAdapter}});Object.defineProperty(exports,"clockToolPreset",{enumerable:true,get:function(){return _preset.clockToolPreset}});Object.defineProperty(exports,"createClockTool",{enumerable:true,get:function(){return _preset.createClockTool}});Object.defineProperty(exports,"failNextRequest",{enumerable:true,get:function(){return _tokenActions.failNextRequest}});Object.defineProperty(exports,"formatDuration",{enumerable:true,get:function(){return _clockMath.formatDuration}});Object.defineProperty(exports,"freeze",{enumerable:true,get:function(){return _clockStore.freeze}});Object.defineProperty(exports,"getClockSnapshot",{enumerable:true,get:function(){return _clockStore.getClockSnapshot}});Object.defineProperty(exports,"getTokenReport",{enumerable:true,get:function(){return _tokenStore.getTokenReport}});Object.defineProperty(exports,"inspectToken",{enumerable:true,get:function(){return _decode.inspectToken}});Object.defineProperty(exports,"isClockOverridden",{enumerable:true,get:function(){return _clockStore.isClockOverridden}});Object.defineProperty(exports,"jumpToTokenExpiry",{enumerable:true,get:function(){return _tokenActions.jumpToTokenExpiry}});Object.defineProperty(exports,"openClockChip",{enumerable:true,get:function(){return _ClockChip.openClockChip}});Object.defineProperty(exports,"parseDuration",{enumerable:true,get:function(){return _clockMath.parseDuration}});Object.defineProperty(exports,"previewJump",{enumerable:true,get:function(){return _clockStore.previewJump}});Object.defineProperty(exports,"realNow",{enumerable:true,get:function(){return _patches.realNow}});Object.defineProperty(exports,"reset",{enumerable:true,get:function(){return _clockStore.reset}});Object.defineProperty(exports,"resume",{enumerable:true,get:function(){return _clockStore.resume}});Object.defineProperty(exports,"setRate",{enumerable:true,get:function(){return _clockStore.setRate}});Object.defineProperty(exports,"setTime",{enumerable:true,get:function(){return _clockStore.setTime}});Object.defineProperty(exports,"shift",{enumerable:true,get:function(){return _clockStore.shift}});Object.defineProperty(exports,"startTokenWatch",{enumerable:true,get:function(){return _tokenStore.startTokenWatch}});Object.defineProperty(exports,"stopTokenCheck",{enumerable:true,get:function(){return _tokenActions.stopTokenCheck}});Object.defineProperty(exports,"subscribeClock",{enumerable:true,get:function(){return _clockStore.subscribeClock}});Object.defineProperty(exports,"tokenFingerprint",{enumerable:true,get:function(){return _decode.fingerprint}});Object.defineProperty(exports,"updateSettings",{enumerable:true,get:function(){return _clockStore.updateSettings}});Object.defineProperty(exports,"useLiveClock",{enumerable:true,get:function(){return _useLiveClock.useLiveClock}});Object.defineProperty(exports,"virtualNow",{enumerable:true,get:function(){return _clockStore.virtualNow}});Object.defineProperty(exports,"withRealClock",{enumerable:true,get:function(){return _patches.withRealClock}});var _reactNative=require("react-native");var _clockStore=require("./engine/clockStore");var _patches=require("./engine/patches");var _ClockChip=require("./components/ClockChip");var _preset=require("./preset");var _ClockBody=require("./components/ClockBody");var _ClockToolView=require("./components/ClockToolView");var _useLiveClock=require("./components/useLiveClock");var _sharedUi=require("@buoy-gg/shared-ui");var _clockSyncAdapter=require("./sync/clockSyncAdapter");var _clockMath=require("./engine/clockMath");var _tokenStore=require("./tokens/tokenStore");var _tokenActions=require("./tokens/tokenActions");var _decode=require("./tokens/decode");const isReactNativeRuntime=typeof navigator!=="undefined"&&navigator.product==="ReactNative";if(isReactNativeRuntime){(0,_patches.installAnimatedRealClock)(_reactNative.Animated);(0,_ClockChip.watchClockChip)();if(typeof __DEV__!=="undefined"&&__DEV__)(0,_clockStore.bootClock)()}
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.createClockTool=exports.clockToolPreset=void 0;var _sharedUi=require("@buoy-gg/shared-ui");var _ClockBody=require("./components/ClockBody");var _jsxRuntime=require("react/jsx-runtime");const clockToolPreset=exports.clockToolPreset=(0,_sharedUi.defineToolPreset)({id:"clock",name:"CLOCK",description:"Travel to any date, freeze or speed up the app's clock",slot:"both",color:_sharedUi.CLOCK_TOOL_ICON_COLOR,icon:({size:e,color:o})=>(0,_jsxRuntime.jsx)(_sharedUi.ClockToolIcon,{size:e,color:o}),component:_ClockBody.ClockBody,modal:{subtitle:"override the app's date & time"}});const createClockTool=exports.createClockTool=(0,_sharedUi.createToolFactory)(clockToolPreset);
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.clockSyncAdapter=void 0;var _sharedUi=require("@buoy-gg/shared-ui");var _clockStore=require("../engine/clockStore");var _tokenStore=require("../tokens/tokenStore");var _tokenActions=require("../tokens/tokenActions");function obj(t){return t&&typeof t==="object"?t:{}}function timeParam(t,...e){for(const r of e){const n=t[r];if(typeof n==="string"||typeof n==="number")return n}return void 0}function boolParam(t,e){const r=t[e];if(typeof r==="boolean")return r;if(r==="true")return true;if(r==="false")return false;return void 0}const clockSyncAdapter=exports.clockSyncAdapter=(0,_sharedUi.createSyncAdapter)({version:1,getSnapshot(){return(0,_clockStore.getClockSnapshot)()},subscribe(t){return(0,_clockStore.subscribeClock)(t)},actions:{getState(){(0,_tokenStore.startTokenWatch)();return(0,_clockStore.getClockSnapshot)()},setTime(t){const e=obj(t);const r=timeParam(e,"time","at","date","iso");if(r===void 0){throw new Error('setTime needs `time`: ISO 8601 ("2026-12-31T23:59:50"), epoch ms, or relative ("+1d").')}return(0,_clockStore.setTime)(r,boolParam(e,"freeze")??false)},shift(t){const e=obj(t);const r=timeParam(e,"by","ms","duration","delta");if(r===void 0){throw new Error('shift needs `by`: a duration like "+1d", "-2h30m", "90s", or ms.')}return(0,_clockStore.shift)(r,boolParam(e,"fireTimers"))},freeze(t){return(0,_clockStore.freeze)(timeParam(obj(t),"time","at"))},resume(){return(0,_clockStore.resume)()},setRate(t){const e=obj(t);const r=Number(e.rate??e.speed);if(!Number.isFinite(r))throw new Error("setRate needs a numeric `rate` (1 = normal speed).");return(0,_clockStore.setRate)(r)},reset(){return(0,_clockStore.reset)()},updateSettings(t){const e=obj(t);return(0,_clockStore.updateSettings)({persist:boolParam(e,"persist"),showChip:boolParam(e,"showChip"),fireTimersOnJump:boolParam(e,"fireTimersOnJump")})},jumpToTokenExpiry(t){const e=obj(t);const r=typeof e.token==="string"?e.token:typeof e.id==="string"?e.id:void 0;const n=Number(e.leadMs??e.lead??1e4);return(0,_tokenActions.jumpToTokenExpiry)(r,Number.isFinite(n)?n:1e4)},failNextRequest(t){const e=obj(t);const r=typeof e.token==="string"?e.token:typeof e.id==="string"?e.id:void 0;const n=Number(e.status??401);const o=Number(e.times??1);return(0,_tokenActions.failNextRequest)(r,n,Number.isFinite(o)?o:1)},stopTokenCheck(){return(0,_tokenActions.stopTokenCheck)()}}});
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.absoluteTime=absoluteTime;exports.base64UrlToBytes=base64UrlToBytes;exports.fingerprint=fingerprint;exports.inspectToken=inspectToken;exports.isStructured=isStructured;exports.lifetimeSeconds=lifetimeSeconds;exports.utf8Decode=utf8Decode;const B64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";const LOOKUP={};for(let t=0;t<B64.length;t++)LOOKUP[B64[t]]=t;LOOKUP["-"]=62;LOOKUP["_"]=63;function base64UrlToBytes(t){const n=t.replace(/=+$/,"");const e=[];let r=0;let i=0;for(let s=0;s<n.length;s++){const o=LOOKUP[n[s]];if(o===void 0)return null;r=r<<6|o;i+=6;if(i>=8){i-=8;e.push(r>>i&255)}}return e}function utf8Decode(t){let n="";for(let e=0;e<t.length;){const r=t[e];let i;let s;if(r<128){i=r;s=0}else if(r>=192&&r<224){i=r&31;s=1}else if(r>=224&&r<240){i=r&15;s=2}else if(r>=240&&r<248){i=r&7;s=3}else{return null}for(let o=1;o<=s;o++){const f=t[e+o];if(f===void 0||(f&192)!==128)return null;i=i<<6|f&63}n+=String.fromCodePoint(i);e+=s+1}return n}function decodeJsonSegment(t){const n=base64UrlToBytes(t);if(!n)return null;return parseJsonObject(n)}function parseJsonObject(t){const n=utf8Decode(t);if(!n||n[0]!=="{")return null;try{const e=JSON.parse(n);return e&&typeof e==="object"&&!Array.isArray(e)?e:null}catch{return null}}function numericDate(t){const n=typeof t==="string"&&t.trim()!==""?Number(t):t;return typeof n==="number"&&Number.isFinite(n)&&n>0?n*1e3:void 0}function absoluteTime(t,n){if(typeof t==="number"&&Number.isFinite(t)&&t>0){return t<1e11?t*1e3:t}if(typeof t==="string"&&t.trim()!==""){const e=t.trim();if(/^\d+(\.\d+)?$/.test(e))return absoluteTime(Number(e),n);const r=n(e);return Number.isFinite(r)?r:void 0}return void 0}function lifetimeSeconds(t){const n=typeof t==="string"&&/^\d+(\.\d+)?$/.test(t.trim())?Number(t):t;return typeof n==="number"&&Number.isFinite(n)&&n>0?n:void 0}const JWS_RE=/^[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*$/;const JWE_RE=/^[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*$/;const PASETO_RE=/^v([1-4])\.(local|public)\.([A-Za-z0-9_-]+)(?:\.([A-Za-z0-9_-]+))?$/;const PASETO_SIG_BYTES={"1":256,"2":64,"3":96,"4":64};function stringClaim(t){if(typeof t==="string"&&t)return t;if(Array.isArray(t)){const n=t.filter(e=>typeof e==="string");return n.length?n.join(", "):void 0}return void 0}function hintFor(t,n){const e=t.iss??"";const r=t.use;if(e.includes("securetoken.google.com"))return"Firebase ID token";if(e.includes("firebaseappcheck"))return"Firebase App Check token";if(e.includes("cognito-idp"))return r?`Cognito ${r} token`:"Cognito token";if(e.includes("appleid.apple.com"))return"Sign in with Apple token";if(e.includes("accounts.google.com"))return"Google ID token";if(e.includes(".auth0.com")||e.includes("auth0"))return"Auth0 token";if(e.includes("clerk"))return"Clerk session token";if(e.includes("supabase")||e.includes("/auth/v1"))return"Supabase access token";if(e.includes("okta"))return"Okta token";if(e.includes("/realms/"))return"Keycloak token";if(e.includes("login.microsoftonline.com")||e.includes("sts.windows.net")){return"Microsoft Entra token"}const i=typeof n?.typ==="string"?n.typ.toLowerCase():"";if(i==="at+jwt"||i==="application/at+jwt")return"OAuth access token";return void 0}function fromClaims(t,n){const e={iss:stringClaim(t.iss),aud:stringClaim(t.aud),sub:stringClaim(t.sub),scope:stringClaim(t.scope??t.scp),use:stringClaim(t.token_use)};const r=numericDate(t.exp);return{claims:e,issuedAt:numericDate(t.iat),notBefore:numericDate(t.nbf),expiresAt:r,expirySource:r?n:void 0}}function inspectJws(t){const[n,e]=t.split(".");const r=decodeJsonSegment(n);if(!r||typeof r.alg!=="string"&&typeof r.typ!=="string")return null;const i=decodeJsonSegment(e);if(!i)return null;const s=typeof r.alg==="string"?r.alg:void 0;const o=fromClaims(i,"exp claim");o.claims.alg=s;if(!o.claims.use&&typeof r.typ==="string"&&r.typ.toUpperCase()!=="JWT"){o.claims.use=r.typ}return{format:"jwt",formatLabel:s?`JWT \xB7 ${s}`:"JWT",hint:hintFor(o.claims,r),...o}}function inspectJwe(t){const n=decodeJsonSegment(t.split(".")[0]);if(!n||typeof n.enc!=="string")return null;return{format:"jwe",formatLabel:"Encrypted JWT",claims:{alg:typeof n.alg==="string"?n.alg:void 0}}}function inspectPaseto(t,n){const e=PASETO_RE.exec(t);if(!e)return null;const[,r,i,s]=e;if(i==="local"){return{format:"paseto-local",formatLabel:`PASETO v${r} (encrypted)`,claims:{}}}const o=base64UrlToBytes(s);const f=PASETO_SIG_BYTES[r];const c=o&&o.length>f?parseJsonObject(o.slice(0,o.length-f)):null;const u={format:"paseto-public",formatLabel:`PASETO v${r}`,claims:{}};if(!c)return u;u.claims={iss:stringClaim(c.iss),aud:stringClaim(c.aud),sub:stringClaim(c.sub)};u.issuedAt=absoluteTime(c.iat,n);u.notBefore=absoluteTime(c.nbf,n);u.expiresAt=absoluteTime(c.exp,n);if(u.expiresAt)u.expirySource="exp claim";return u}function inspectFernet(t){if(!t.startsWith("gAAAAA")||t.length<76)return null;const n=base64UrlToBytes(t);if(!n||n[0]!==128||n.length<73||(n.length-57)%16!==0)return null;let e=0;for(let r=1;r<=8;r++)e=e*256+n[r];if(e<978307200||e>4102444800)return null;return{format:"fernet",formatLabel:"Fernet",claims:{},issuedAt:e*1e3}}const OPAQUE_HINTS=[{test:/^ya29\./,hint:"Google OAuth access token"},{test:/^gh[opsu]_/,hint:"GitHub token"},{test:/^github_pat_/,hint:"GitHub token"},{test:/^xox[abeprs]-/,hint:"Slack token"},{test:/^EAA[A-Za-z0-9]{40,}/,hint:"Facebook access token"},{test:/^shp(at|ca|pa|ss)_/,hint:"Shopify token"},{test:/^(sk|rk|pk)_(live|test)_/,hint:"Stripe API key",neverExpires:true},{test:/^AIza[0-9A-Za-z_-]{35}$/,hint:"Google API key",neverExpires:true}];function inspectToken(t,n=Date.parse){const e=t.trim().replace(/^"(.*)"$/,"$1");if(e.length<16||/\s/.test(e))return null;if(JWS_RE.test(e)){const s=inspectJws(e);if(s)return s}if(JWE_RE.test(e)){const s=inspectJwe(e);if(s)return s}if(e.startsWith("v")&&PASETO_RE.test(e)){const s=inspectPaseto(e,n);if(s)return s}const r=inspectFernet(e);if(r)return r;const i=OPAQUE_HINTS.find(s=>s.test.test(e));return{format:"opaque",formatLabel:"Opaque",hint:i?.hint,neverExpires:i?.neverExpires,claims:{}}}function isStructured(t){return!!t&&t.format!=="opaque"}function fingerprint(t){let n=2166136261;for(let e=0;e<t.length;e++){n^=t.charCodeAt(e);n=Math.imul(n,16777619)>>>0}return n.toString(16).padStart(8,"0")}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.hostOf=hostOf;exports.originOf=originOf;exports.pathOf=pathOf;exports.tokensIssued=tokensIssued;exports.tokensUsed=tokensUsed;var _decode=require("./decode");function originOf(e){const t=/^([a-z][a-z0-9+.-]*:\/\/[^/?#]+)/i.exec(e);return t?t[1].toLowerCase():null}function hostOf(e){const t=originOf(e);return t?t.replace(/^[a-z][a-z0-9+.-]*:\/\//,""):"unknown"}function pathOf(e){const t=e.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/?#]+/i,"");const n=t.split(/[?#]/)[0]||"/";return n}function queryParams(e){const t=e.split("#")[0].split("?")[1];if(!t)return[];const n=[];for(const s of t.split("&")){const i=s.indexOf("=");if(i<=0)continue;try{n.push([decodeURIComponent(s.slice(0,i)),decodeURIComponent(s.slice(i+1))])}catch{}}return n}const TOKEN_NAME=/(token|jwt|session|auth|appcheck|api[-_]?key|apikey|credential|bearer)/i;const NOT_TOKEN_HEADERS=new Set(["authorization","cookie","dpop","idempotency-key","x-request-id","x-correlation-id","www-authenticate","proxy-authenticate","x-csrf-token","x-xsrf-token"]);const TOKEN_QUERY=new Set(["access_token","accesstoken","id_token","token","auth","auth_token","jwt","session","session_token"]);const SCHEMES=new Set(["bearer","token","jwt","dpop","oauth","firebase"]);function lowerKeys(e){const t={};if(!e)return t;for(const[n,s]of Object.entries(e)){if(typeof s==="string")t[n.toLowerCase()]=s;else if(Array.isArray(s))t[n.toLowerCase()]=s.join(", ")}return t}function parseCookieHeader(e){const t=[];for(const n of e.split(";")){const s=n.indexOf("=");if(s<=0)continue;t.push([n.slice(0,s).trim(),n.slice(s+1).trim()])}return t}function candidate(e,t,n){const s=(0,_decode.inspectToken)(e,n);if(!s)return null;if((0,_decode.isStructured)(s)||s.hint)return s;return t?s:null}function tokensUsed(e,t){const n=[];const s=lowerKeys(e.requestHeaders);const i=s.authorization;if(i){const o=/^(\S+)\s+(\S+)$/.exec(i.trim());const r=o?o[1]:void 0;const c=o?o[2]:i.trim();if(!r||SCHEMES.has(r.toLowerCase())){const u=candidate(c,true,t);if(u){n.push({location:"authorization",name:"Authorization",scheme:r,fp:(0,_decode.fingerprint)(c),info:u})}}}for(const[o,r]of Object.entries(s)){if(NOT_TOKEN_HEADERS.has(o)||!TOKEN_NAME.test(o))continue;const c=r.replace(/^bearer\s+/i,"");const u=candidate(c,true,t);if(u)n.push({location:"header",name:o,fp:(0,_decode.fingerprint)(c),info:u})}if(s.cookie){for(const[o,r]of parseCookieHeader(s.cookie)){const c=candidate(r,TOKEN_NAME.test(o)||/^(sid|sess)/i.test(o),t);if(c)n.push({location:"cookie",name:o,fp:(0,_decode.fingerprint)(r),info:c})}}for(const[o,r]of queryParams(e.url)){const c=candidate(r,TOKEN_QUERY.has(o.toLowerCase()),t);if(c&&TOKEN_QUERY.has(o.toLowerCase())){n.push({location:"query",name:o,fp:(0,_decode.fingerprint)(r),info:c})}}return n}const ACCESS_KEYS=/^(access_?token|accesstoken|token|jwt|auth_?token|session_?token|sessiontoken)$/i;const ID_KEYS=/^(id_?token|idtoken)$/i;const REFRESH_KEYS=/^(refresh_?token|refreshtoken)$/i;const LIFETIME_KEYS=["expires_in","expiresIn","ExpiresIn","expires","ttl"];const ABSOLUTE_KEYS=["expires_at","expiresAt","ExpiresAt","expiration","Expiration","expiry","expirationTime","expiration_time","access_token_expires_at","accessTokenExpiresAt","accessTokenExpiry"];const REFRESH_LIFETIME_KEYS=["refresh_expires_in","refresh_token_expires_in","refreshTokenExpiresIn","refreshExpiresIn"];const REFRESH_ABSOLUTE_KEYS=["refresh_expires_at","refreshTokenExpiresAt","refresh_token_expires_at"];function parseBody(e){if(typeof e!=="string")return e;const t=e.trim();if(!(t.startsWith("{")||t.startsWith("[")))return null;try{return JSON.parse(t)}catch{return null}}function findTokens(e,t,n){if(t>4||!e||typeof e!=="object")return;if(Array.isArray(e)){for(const i of e.slice(0,5))findTokens(i,t+1,n);return}const s=e;for(const[i,o]of Object.entries(s)){if(typeof o==="string"&&(ACCESS_KEYS.test(i)||ID_KEYS.test(i)||REFRESH_KEYS.test(i))){n.push({key:i,value:o,holder:s})}else if(o&&typeof o==="object"){findTokens(o,t+1,n)}}}function roleOf(e){if(REFRESH_KEYS.test(e))return"refresh";if(ID_KEYS.test(e))return"id";if(/session/i.test(e))return"session";return"access"}function expiryIn(e,t,n,s,i){for(const o of t){const r=(0,_decode.lifetimeSeconds)(e[o]);if(r!==void 0){return{expiresAt:s+r*1e3,source:o,lifetimeMs:r*1e3}}}for(const o of n){const r=(0,_decode.absoluteTime)(e[o],i);if(r!==void 0)return{expiresAt:r,source:o}}return{}}function setCookieLines(e){const t=e["set-cookie"];if(!t)return[];return t.split(/,(?=\s*[A-Za-z0-9!#$%&'*+.^_`|~-]+=)/).map(n=>n.trim())}function tokensIssued(e,t){if(e.status===void 0||e.status>=400)return[];const n=`${e.method.toUpperCase()} ${pathOf(e.url)}`;const s=e.timestamp+(e.duration??0);const i=[];const o=parseBody(e.responseData);if(o){const c=[];findTokens(o,0,c);const u=new Map;for(const f of c.filter(a=>REFRESH_KEYS.test(a.key))){const a=(0,_decode.inspectToken)(f.value,t);if(!a)continue;const p=expiryIn(f.holder,REFRESH_LIFETIME_KEYS,REFRESH_ABSOLUTE_KEYS,s,t);u.set(f.holder,{fp:(0,_decode.fingerprint)(f.value),expiresAt:a.expiresAt??p.expiresAt,formatLabel:a.formatLabel})}for(const f of c){if(REFRESH_KEYS.test(f.key))continue;const a=(0,_decode.inspectToken)(f.value,t);if(!a)continue;const p=expiryIn(f.holder,LIFETIME_KEYS,ABSOLUTE_KEYS,s,t);i.push({fp:(0,_decode.fingerprint)(f.value),info:a,role:roleOf(f.key),expiresAt:p.expiresAt,expirySource:p.source,lifetimeMs:p.lifetimeMs,issuedBy:n,refresh:u.get(f.holder)})}}const r=lowerKeys(e.responseHeaders);for(const c of setCookieLines(r)){const[u,...f]=c.split(";");const a=u.indexOf("=");if(a<=0)continue;const p=u.slice(0,a).trim();const x=u.slice(a+1).trim();const l=candidate(x,TOKEN_NAME.test(p)||/^(sid|sess)/i.test(p),t);if(!l||x===""||x.toLowerCase()==="deleted")continue;let k;let d;let h;for(const y of f){const[A,...S]=y.split("=");const m=A.trim().toLowerCase();const _=S.join("=").trim();if(m==="max-age"&&/^-?\d+$/.test(_)){h=Number(_)*1e3;k=s+h;d="Max-Age"}else if(m==="expires"&&d!=="Max-Age"){const E=t(_);if(Number.isFinite(E)){k=E;d="Expires"}}}i.push({fp:(0,_decode.fingerprint)(x),info:l,role:"cookie",cookie:p,expiresAt:l.expiresAt??k,expirySource:l.expiresAt?l.expirySource:d,lifetimeMs:l.expiresAt?void 0:h,issuedBy:n})}return i}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.failNextRequest=failNextRequest;exports.jumpToTokenExpiry=jumpToTokenExpiry;exports.stopTokenCheck=stopTokenCheck;var _sharedUi=require("@buoy-gg/shared-ui");var _clockStore=require("../engine/clockStore");var _tokenStore=require("./tokenStore");const NO_TOKEN="No sign-in token seen yet. Use the app so it sends a signed-in request, then try again. Tokens are read from the Network tool's captured requests.";function pick(r){(0,_tokenStore.startTokenWatch)();const o=(0,_tokenStore.getTokenRow)(r);if(!o)throw new Error(r?`No token "${r}". Read getState for the current ids.`:NO_TOKEN);return o}function jumpToTokenExpiry(r,o=1e4){const e=pick(r);if(e.expiresAt===void 0){throw new Error(e.neverExpires?`${e.label} for ${e.host} is ${e.hint??"a key"} and never expires.`:`${e.label} for ${e.host} (${e.formatLabel}) has no expiry Buoy can read. Use failNextRequest to test the refresh instead.`)}const t=e.expiresAt-Math.max(0,o)-(0,_clockStore.virtualNow)();if(t<=0){throw new Error(`${e.label} for ${e.host} has already expired on the app's clock. Go back to real time, or use failNextRequest.`)}(0,_clockStore.shift)(t);(0,_tokenStore.beginTokenCheck)("expiry",e.id);return(0,_clockStore.getClockSnapshot)()}async function failNextRequest(r,o=401,e=1){const t=pick(r);const n=(0,_tokenStore.getTokenOrigin)(t.id);if(!n)throw new Error(NO_TOKEN);await removePendingRule();const c=o===403?403:401;const u=t.location==="authorization"||t.location==="header"?{urlPattern:`${n}/*`,requireHeader:t.name.toLowerCase()}:t.location==="query"?{urlPattern:`${n}/*${t.name}=*`}:{urlPattern:`${n}/*`};const i=await(0,_sharedUi.dispatchToolAction)("network","upsertOverrideRule",{name:"Clock: force a token refresh",...u,kind:"respond",status:c,headers:{"content-type":"application/json","www-authenticate":'Bearer error="invalid_token", error_description="The access token expired"'},body:JSON.stringify({error:"invalid_token",error_description:"The access token expired (forced by Buoy Clock to test token refresh)"}),times:Math.max(1,Math.min(10,Math.round(e)))}).catch(a=>{const s=a instanceof Error?a.message:String(a);throw new Error(s.includes("Unknown tool")?"Failing a request needs the Network tool (@buoy-gg/network).":s)});if(!i?.ok||!i.rule)throw new Error(i?.error??"Network did not add the override.");(0,_tokenStore.beginTokenCheck)("401",t.id,i.rule.id);return(0,_clockStore.getClockSnapshot)()}async function removePendingRule(){const r=(0,_tokenStore.getTokenReport)().check;if(r?.ruleId&&r.forcedAt===void 0){await(0,_sharedUi.dispatchToolAction)("network","deleteOverrideRule",{id:r.ruleId}).catch(()=>{})}}async function stopTokenCheck(){await removePendingRule();(0,_tokenStore.clearTokenCheck)();return(0,_clockStore.getClockSnapshot)()}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.__ingestForTests=__ingestForTests;exports.__resetTokensForTests=__resetTokensForTests;exports.beginTokenCheck=beginTokenCheck;exports.clearTokenCheck=clearTokenCheck;exports.getTokenOrigin=getTokenOrigin;exports.getTokenReport=getTokenReport;exports.getTokenRow=getTokenRow;exports.isTokenWatchOn=isTokenWatchOn;exports.startTokenWatch=startTokenWatch;exports.subscribeTokens=subscribeTokens;exports.tokenAppNow=void 0;var _sharedUi=require("@buoy-gg/shared-ui");var _utils=require("@buoy-gg/shared-ui/utils");var _patches=require("../engine/patches");var _extract=require("./extract");const parseDate=e=>_patches.RealDate.parse(e);const LOOP_COUNT=4;const LOOP_WINDOW_MS=2e4;const MAX_ROWS=8;const MAX_HISTORY=8;const MAX_ISSUANCES=200;const MAX_SEEN_IDS=1e3;const rows=new Map;const issuances=new Map;const seenRequests=new Set;const seenResponses=new Set;const issuingEndpoints=new Set;const TOKEN_PATH=/(token|refresh|oauth|session|auth\/)/i;let check=null;let available=true;let unsubscribeNetwork=null;const listeners=new Set;let emitQueued=false;function emit(){if(emitQueued)return;emitQueued=true;Promise.resolve().then(()=>{emitQueued=false;listeners.forEach(e=>e())})}function subscribeTokens(e){listeners.add(e);return()=>{listeners.delete(e)}}function remember(e,t){if(e.has(t))return false;if(e.size>=MAX_SEEN_IDS){const s=e.values().next().value;if(s!==void 0)e.delete(s)}e.add(t);return true}function rowKey(e,t){return`${e}|${t.location}:${t.name.toLowerCase()}`}function expiryFor(e,t){const s=t?.lifetimeMs!==void 0&&t.expiresAt!==void 0;const i=s?t.expiresAt:e.expiresAt??t?.expiresAt;const f=e.expiresAt??(s?t.observedReal+t.lifetimeMs:t?.expiresAt);const n=s?t.expirySource:e.expiresAt?e.expirySource:t?.expirySource;const r=s?t.lifetimeMs:e.expiresAt!==void 0&&e.issuedAt!==void 0?e.expiresAt-e.issuedAt:void 0;return{expiresAt:i,serverExpiresAt:f,source:n,lifetimeMs:r}}function applyToken(e,t,s,i,f){const n=issuances.get(t);const r=expiryFor(s,n);const o=e.fp!==t;if(o){e.history.unshift({fp:e.fp,firstSeenReal:e.firstSeenReal,expiresAt:e.expiresAt});e.history.length=Math.min(e.history.length,MAX_HISTORY);e.fp=t;e.firstSeenReal=i;e.firstSeenApp=f;e.uses=0;e.expiredSends=0;e.rejected=0}e.info=s;e.role=n?.role??(o?void 0:e.role);e.issuedBy=n?.issuedBy??(o?void 0:e.issuedBy);e.refresh=n?.refresh??(o?void 0:e.refresh);e.expiresAt=r.expiresAt;e.serverExpiresAt=r.serverExpiresAt;e.expirySource=r.source;e.lifetimeMs=r.lifetimeMs;if(o)onNewToken(e,i)}function newRow(e,t,s,i,f){const n=issuances.get(s.fp);const r=expiryFor(s.info,n);return{key:e,origin:t,host:(0,_extract.hostOf)(t),location:s.location,name:s.name,scheme:s.scheme,fp:s.fp,info:s.info,role:n?.role,issuedBy:n?.issuedBy,refresh:n?.refresh,expiresAt:r.expiresAt,serverExpiresAt:r.serverExpiresAt,expirySource:r.source,lifetimeMs:r.lifetimeMs,firstSeenReal:i,firstSeenApp:f,lastUsedReal:i,lastUsedApp:f,uses:0,expiredSends:0,rejected:0,history:[]}}function evictOldestRow(){if(rows.size<=MAX_ROWS)return;let e=null;for(const t of rows.values()){if(!e||t.lastUsedReal<e.lastUsedReal)e=t}if(e&&e.key!==check?.tokenId)rows.delete(e.key)}function onNewToken(e,t){if(!check||check.tokenId!==e.key)return;check.newTokens+=1;if(check.status==="waiting"){check.status="refreshed";check.refreshedAt=t;if(check.ruleId&&check.forcedAt===void 0){void(0,_sharedUi.dispatchToolAction)("network","deleteOverrideRule",{id:check.ruleId}).catch(()=>{})}}if(check.newTokens>=LOOP_COUNT&&t-(check.refreshedAt??t)<=LOOP_WINDOW_MS){check.status="loop"}}function ingestRequest(e){const t=(0,_extract.originOf)(e.url);if(!t)return;const s=(0,_extract.tokensUsed)(e,parseDate);if(s.length===0)return;const i=(0,_patches.realNow)();const f=e.timestamp;for(const n of s){const r=rowKey(t,n);let o=rows.get(r);if(!o){o=newRow(r,t,n,i,f);rows.set(r,o);evictOldestRow()}else{applyToken(o,n.fp,n.info,i,f)}o.uses+=1;o.lastUsedReal=i;o.lastUsedApp=f;if(o.expiresAt!==void 0&&f>=o.expiresAt)o.expiredSends+=1;if(check&&check.tokenId===r&&check.forcedAt!==void 0&&n.fp===check.startFp){check.staleAfterFailure+=1}}emit()}function ingestResponse(e){const t=(0,_extract.originOf)(e.url);if(!t)return;const s=(0,_patches.realNow)();let i=false;if(e.status===401||e.status===403){for(const n of(0,_extract.tokensUsed)(e,parseDate)){const r=rows.get(rowKey(t,n));if(r&&r.fp===n.fp){r.rejected+=1;i=true}}}const f=!!check?.ruleId&&e.override?.ruleId===check.ruleId;if(check&&(e.status??0)>=400&&!f){const n=`${e.method.toUpperCase()} ${(0,_extract.pathOf)(e.url)}`;const r=issuingEndpoints.has(n);if(r||e.method.toUpperCase()==="POST"&&TOKEN_PATH.test((0,_extract.pathOf)(e.url))){check.failedRefreshes+=1;check.lastRefreshError=`${e.status} ${n}`;i=true}}if(check?.ruleId&&e.override?.ruleId===check.ruleId&&check.forcedAt===void 0){check.forcedAt=s;i=true}for(const n of(0,_extract.tokensIssued)(e,parseDate)){issuingEndpoints.add(n.issuedBy);issuances.set(n.fp,{...n,observedReal:s});if(issuances.size>MAX_ISSUANCES){const r=issuances.keys().next().value;if(r!==void 0)issuances.delete(r)}i=true;if(n.cookie){const r={location:"cookie",name:n.cookie,fp:n.fp,info:n.info};const o=rowKey(t,r);const u=rows.get(o);if(u)applyToken(u,n.fp,n.info,s,e.timestamp);else{rows.set(o,newRow(o,t,r,s,e.timestamp));evictOldestRow()}continue}for(const r of rows.values()){if(r.fp===n.fp)applyToken(r,n.fp,r.info,s,r.lastUsedApp)}}if(i)emit()}function ingest(e){const t=e;if(!t||typeof t.id!=="string"||typeof t.url!=="string")return;if(remember(seenRequests,t.id))ingestRequest(t);if(t.status!==void 0&&remember(seenResponses,t.id))ingestResponse(t)}function startTokenWatch(){if(unsubscribeNetwork)return true;try{const{networkEventStore:e}=(0,_utils.loadToolModule)("@buoy-gg/network");if(!e?.onEvent)throw new Error("no event store");unsubscribeNetwork=e.onEvent(ingest,true);available=true}catch{available=false}emit();return available}function isTokenWatchOn(){return unsubscribeNetwork!==null}function beginTokenCheck(e,t,s){const i=rows.get(t);if(!i)throw new Error(`No token row "${t}".`);check={kind:e,tokenId:t,startedAt:(0,_patches.realNow)(),startFp:i.fp,ruleId:s,status:"waiting",newTokens:0,staleAfterFailure:0,failedRefreshes:0};emit();return check}function clearTokenCheck(){check=null;emit()}function labelFor(e){if(e.location==="cookie")return`${e.name} cookie`;if(e.location==="query")return`${e.name} parameter`;if(e.location==="header")return`${e.name} header`;if(e.role==="id"||e.info.claims.use==="id")return"ID token";return"Access token"}function summarize(e,t){const s=[e.firstSeenReal,...e.history.map(n=>n.firstSeenReal)].slice(0,-1);const i=s.filter(n=>t-n<=LOOP_WINDOW_MS).length;const f=e.info.issuedAt;return{id:e.key,label:labelFor(e),host:e.host,location:e.location,name:e.name,scheme:e.scheme,format:e.info.format,formatLabel:e.info.formatLabel,hint:e.info.hint,claims:e.info.claims,fingerprint:e.fp,issuedAt:f,notBefore:e.info.notBefore,expiresAt:e.expiresAt,serverExpiresAt:e.serverExpiresAt,expirySource:e.expirySource,lifetimeMs:e.lifetimeMs,neverExpires:e.info.neverExpires,issuedBy:e.issuedBy,refresh:e.refresh?{expiresAt:e.refresh.expiresAt,formatLabel:e.refresh.formatLabel}:void 0,firstUsed:e.firstSeenApp,lastUsed:e.lastUsedApp,lastUsedReal:e.lastUsedReal,uses:e.uses,refreshes:e.history.length,lastRefreshAt:e.history.length?e.firstSeenReal:void 0,recentRefreshes:i,looping:i>=LOOP_COUNT,expiredSends:e.expiredSends,rejected:e.rejected}}function getTokenReport(){const e=(0,_patches.realNow)();const t=[...rows.values()].sort((s,i)=>i.lastUsedReal-s.lastUsedReal).map(s=>summarize(s,e));if(check?.status==="refreshed"){const s=rows.get(check.tokenId);if(s){check.newLifetimeMs=s.lifetimeMs;check.newIssuedBy=s.issuedBy}}return{available,watching:unsubscribeNetwork!==null,tokens:t,check:check?{...check}:null}}function getTokenRow(e){const t=getTokenReport();if(e)return t.tokens.find(s=>s.id===e)??null;return t.tokens.find(s=>s.expiresAt!==void 0)??t.tokens[0]??null}function getTokenOrigin(e){return rows.get(e)?.origin??null}const tokenAppNow=exports.tokenAppNow=_patches.appNow;function __ingestForTests(e){ingest(e)}function __resetTokensForTests(){rows.clear();issuances.clear();seenRequests.clear();seenResponses.clear();issuingEndpoints.clear();check=null;available=true;unsubscribeNetwork?.();unsubscribeNetwork=null}
@@ -0,0 +1 @@
1
+ "use strict";import{useEffect as r,useState as s}from"react";import{reloadApp as i}from"@buoy-gg/shared-ui";import{bootClock as e,getClockSnapshot as c,subscribeClock as a}from"../engine/clockStore";import{clockSyncAdapter as p}from"../sync/clockSyncAdapter";import{startTokenWatch as f}from"../tokens/tokenStore";import{ClockToolView as m}from"./ClockToolView";import{jsx as l}from"react/jsx-runtime";const u=p.actions;async function k(o,t){const n=u[o];if(!n)throw new Error(`Unknown clock action: ${o}`);return n(t)}function h(){void i("auto","buoy clock").catch(()=>{})}export function ClockBody(){r(()=>{e();f()},[]);const[o,t]=s(c);r(()=>a(()=>t(c())),[]);return l(m,{snapshot:o,onAction:k,onReload:h})}
@@ -0,0 +1 @@
1
+ "use strict";import{useSyncExternalStore as z}from"react";import{StyleSheet as p,Text as m,TouchableOpacity as d,View as n}from"react-native";import{ClockToolIcon as f,FloatingTool as I,Pause as L,Play as T,X as D,dismissFloatingTool as F,getFloatingTools as v,night as l,openFloatingTool as u}from"@buoy-gg/shared-ui";import{freeze as O,getStableClockSnapshot as b,reset as P,resume as j,subscribeClock as g,getClockSettings as R,isClockOverridden as W}from"../engine/clockStore";import{formatDuration as V}from"../engine/clockMath";import{useLiveClock as _}from"./useLiveClock";import{clockStatus as $,formatChipTime as k}from"./format";import{jsx as t,jsxs as a}from"react/jsx-runtime";export const CLOCK_CHIP_ID="clock-chip";const y={id:CLOCK_CHIP_ID,render:e=>t(ClockChip,{...e})};const A=()=>v().some(e=>e.id===CLOCK_CHIP_ID);export function openClockChip(){u(y)}let C=false;export function watchClockChip(){if(C)return;C=true;let e=false;const c=()=>{const o=W()&&R().showChip;if(o&&!e)u(y);else if(!o&&A())F(CLOCK_CHIP_ID);e=o};g(c);c()}export function ClockChip({id:e,revealToken:c}){const o=z(g,b,b);const{virtual:s,real:x}=_(o);const r=o.mode==="frozen";const S=$(o);const h=s-x;const w=`${Math.abs(h)<1e3?"0s":V(h)} \xB7 ${S.label.toLowerCase()}`;return t(I,{id:e,revealToken:c,variant:"strip",dragFromSurface:true,defaultPlacement:"top",accessibilityLabel:`Move or hide the clock chip. App time ${k(s)}`,handleAdornment:t(f,{size:12}),children:a(n,{style:i.row,children:[t(f,{size:18}),a(n,{style:i.text,children:[t(m,{style:i.time,numberOfLines:1,testID:"clock-chip-time",children:k(s)}),t(m,{style:i.detail,numberOfLines:1,testID:"clock-chip-detail",children:w})]}),a(d,{testID:r?"clock-chip-resume":"clock-chip-freeze",accessibilityRole:"button",accessibilityLabel:r?"Resume the app clock":"Freeze the app clock",onPress:()=>r?j():O(),style:i.button,hitSlop:{top:8,bottom:8,left:4,right:4},children:[r?t(T,{size:16,color:l.color.accent}):null,r?null:t(L,{size:16,color:l.color.text})]}),t(n,{style:i.separator}),t(d,{testID:"clock-chip-reset",accessibilityRole:"button",accessibilityLabel:"Back to real time",onPress:()=>P(),style:i.button,hitSlop:{top:8,bottom:8,left:4,right:8},children:t(D,{size:16,color:l.color.textSecondary})})]})})}const i=p.create({row:{flexDirection:"row",alignItems:"center",gap:10,paddingLeft:10,paddingRight:4,paddingVertical:6,minWidth:220},text:{flexShrink:1,minWidth:120},time:{color:"#F59E0B",fontSize:14,fontWeight:"600",fontVariant:["tabular-nums"]},detail:{color:l.color.textSecondary,fontSize:11,fontVariant:["tabular-nums"]},button:{width:32,height:32,alignItems:"center",justifyContent:"center",borderRadius:16},separator:{width:p.hairlineWidth,height:20,backgroundColor:l.color.separator}});
@@ -0,0 +1 @@
1
+ "use strict";import{useState as S}from"react";import{ScrollView as N,StyleSheet as v,Text as d,TextInput as B,TouchableOpacity as k,View as n}from"react-native";import{ChevronRight as W,NightButton as $,NightCard as g,NightChip as H,NightFootnote as D,NightRows as T,NightSectionLabel as z,NightSegmented as F,NightSwitch as R,monoFont as _,night as r,useSafeAreaInsets as U}from"@buoy-gg/shared-ui";import{TIME_PRESETS as J,formatRate as K}from"../engine/clockMath";import{RealDate as M}from"../engine/patches";import{useLiveClock as x}from"./useLiveClock";import{TokenSection as Y}from"./TokenSection";import{clockStatus as G,formatClockDate as q,formatChipTime as Q,formatClockTime as C,formatInputTime as L,formatOffset as X,jumpTestId as Z}from"./format";import{jsx as e,jsxs as c}from"react/jsx-runtime";const P="#F59E0B";export function ClockToolView({snapshot:t,onAction:i,onReload:l}){const s=U({minBottom:16});const[a,u]=S(null);const[h,y]=S(null);const m=(f,b)=>{u(null);i(f,b).catch(w=>{u(w instanceof Error?w.message:String(w))})};const p=f=>y(b=>b===f?null:f);if(!t){return e(n,{style:o.waiting,testID:"clock-waiting",children:e(D,{centered:true,children:"Waiting for the app's clock\u2026"})})}return c(N,{style:o.fill,contentContainerStyle:[o.content,{paddingBottom:s.bottom}],testID:"clock-scroll",keyboardShouldPersistTaps:"handled",children:[e(ee,{snapshot:t}),a?e(D,{tone:"danger",centered:true,children:e(d,{testID:"clock-error",children:a})}):null,c(n,{children:[e(z,{children:"Jump ahead"}),e(oe,{run:m})]}),c(n,{children:[e(g,{children:c(T,{children:[c(n,{children:[e(O,{title:"Date & Time",testID:"clock-date-row",open:h==="date",onPress:()=>p("date"),value:e(re,{snapshot:t})}),h==="date"?e(le,{snapshot:t,run:m,onDone:()=>y(null)}):null]},"date"),c(n,{style:o.row,children:[e(d,{style:o.rowTitle,children:"Freeze"}),e(R,{value:t.mode==="frozen",testID:"clock-freeze",accessibilityLabel:"Freeze the clock",onValueChange:f=>m(f?"freeze":"resume")})]},"freeze"),c(n,{children:[e(O,{title:"Speed",testID:"clock-speed-row",open:h==="speed",onPress:()=>p("speed"),value:e(d,{style:o.rowValue,children:E(t)})}),h==="speed"?e(ne,{snapshot:t,run:m}):null]},"speed")]})}),e(D,{children:"Only this app's clock changes. Screens pick up the new time when they next render, or all at once when you reload."})]}),t.tokens?e(Y,{snapshot:t,run:m,openId:h,onToggle:p}):null,e(g,{children:c(T,{children:[l?e(V,{label:"Reload App",testID:"clock-reload",onPress:l},"reload"):null,e(V,{label:"Use Real Time",accessibilityLabel:"Reset to real time",testID:"clock-reset",destructive:true,disabled:t.mode==="real",onPress:()=>m("reset")},"reset")]})}),c(n,{children:[e(z,{children:"Options"}),e(ae,{settings:t.settings,run:m})]})]})}function ee({snapshot:t}){const{virtual:i,real:l}=x(t);const s=G(t);const a=t.mode!=="real";const u=X(i-l);const h=t.mode==="frozen"?" \xB7 Frozen":a&&t.rate!==1?` \xB7 ${E(t)}`:"";return c(n,{style:o.hero,children:[e(d,{style:[o.heroTime,a?o.heroTimeActive:null],testID:"clock-hero-time",accessibilityLabel:`App clock ${C(i)}`,children:C(i)}),e(d,{style:o.heroDate,testID:"clock-hero-date",children:q(i)}),e(n,{testID:"clock-status",accessibilityLabel:`Clock status: ${s.label}${a?`, ${u}`:""}`,children:e(d,{style:[o.heroStatus,a?o.heroStatusActive:null],testID:"clock-offset",children:a?`${u}${h}`:"Real time"})})]})}const A=36e5;const I=24*A;const te=[{ms:A,unit:"hour"},{ms:I,unit:"day"},{ms:7*I,unit:"week"},{ms:30*I,unit:"month"}];function oe({run:t}){return e(g,{style:o.jumpBar,children:te.map((i,l)=>c(k,{testID:Z(i.ms),accessibilityRole:"button",accessibilityLabel:`Jump ahead one ${i.unit}`,activeOpacity:.6,onPress:()=>t("shift",{by:i.ms}),style:[o.jump,l>0?o.jumpDivider:null],children:[e(d,{style:o.jumpAmount,children:"+1"}),e(d,{style:o.jumpUnit,children:i.unit})]},i.unit))})}function O({title:t,value:i,open:l,onPress:s,testID:a}){return c(k,{testID:a,accessibilityRole:"button",accessibilityLabel:t,accessibilityState:{expanded:l},activeOpacity:.6,onPress:s,style:o.row,children:[e(d,{style:o.rowTitle,children:t}),c(n,{style:o.rowRight,children:[i,e(n,{style:l?o.chevronOpen:null,children:e(W,{size:16,color:r.color.textTertiary})})]})]})}function re({snapshot:t}){const{virtual:i}=x(t,1e3);return e(d,{style:[o.rowValue,o.tabular],numberOfLines:1,children:Q(i,false)})}function V({label:t,onPress:i,testID:l,destructive:s,disabled:a,accessibilityLabel:u}){return e(k,{testID:l,accessibilityRole:"button",accessibilityLabel:u??t,accessibilityState:{disabled:!!a},activeOpacity:.6,disabled:a,onPress:i,style:[o.row,a?o.rowDisabled:null],children:e(d,{style:[o.actionLabel,s?o.actionDestructive:null],children:t})})}const ie=J.filter(t=>["midnight","month-end","year-end"].includes(t.id));function le({snapshot:t,run:i,onDone:l}){const[s,a]=S("");const{virtual:u}=x(t,1e3);const h=()=>s.trim()||L(u);const y=t.mode==="frozen";const m=p=>{i("setTime",{time:p,freeze:y});l()};return c(n,{style:o.editor,children:[c(n,{style:o.inputRow,children:[e(B,{testID:"clock-time-input",accessibilityLabel:"Date and time",value:s,onChangeText:a,placeholder:L(u),placeholderTextColor:r.color.placeholder,autoCapitalize:"none",autoCorrect:false,returnKeyType:"done",onSubmitEditing:()=>m(h()),style:o.input}),e($,{label:"Set",testID:"clock-time-apply",accessibilityLabel:"Set this time",size:"md",variant:"primary",fullWidth:false,onPress:()=>m(h())})]}),e(d,{style:o.hint,children:"Year-month-day and 24-hour time, or +3d, -2h"}),e(n,{style:o.presets,children:ie.map(p=>e(H,{label:p.label,testID:`clock-preset-${p.id}`,onPress:()=>m(p.at(u,M))},p.id))})]})}const j=[{key:"1",label:"Normal"},{key:"10",label:"10\xD7"},{key:"60",label:"1 min/s"},{key:"3600",label:"1 hr/s"}];function E(t){if(t.mode==="real"||t.rate===1)return"Normal";return j.find(i=>Number(i.key)===t.rate)?.label??K(t.rate)}function ne({snapshot:t,run:i}){const l=t.mode==="real"?"1":String(t.rate);return c(n,{style:o.editor,children:[e(n,{style:o.segmentRow,children:e(F,{tabs:j,activeKey:l,size:"sm",testIDPrefix:"clock-speed",onChange:s=>i("setRate",{rate:Number(s)})})}),e(d,{style:o.hint,children:"Taps, animations and timers keep real speed."})]})}const ce=[{key:"showChip",title:"Show Floating Clock"},{key:"persist",title:"Keep After Reload"}];function ae({settings:t,run:i}){return e(g,{children:e(T,{children:ce.map(l=>c(n,{style:o.row,children:[e(d,{style:o.rowTitle,children:l.title}),e(R,{value:t[l.key],testID:`clock-setting-${l.key}`,accessibilityLabel:l.title,onValueChange:s=>i("updateSettings",{[l.key]:s})})]},l.key))})})}const o=v.create({fill:{flex:1},content:{paddingHorizontal:r.gutter,paddingTop:8,gap:r.groupGap},waiting:{flex:1,alignItems:"center",justifyContent:"center",padding:24},hero:{alignItems:"center",paddingTop:12},heroTime:{color:r.color.text,fontSize:56,fontWeight:"200",fontVariant:["tabular-nums"],letterSpacing:1},heroTimeActive:{color:P},heroDate:{color:r.color.text,fontSize:17,fontWeight:"500",marginTop:2},heroStatus:{color:r.color.textTertiary,fontSize:r.font.caption,fontWeight:"500",marginTop:6},heroStatusActive:{color:r.color.textSecondary},jumpBar:{flexDirection:"row"},jump:{flex:1,alignItems:"center",paddingVertical:12},jumpDivider:{borderLeftWidth:v.hairlineWidth,borderLeftColor:r.color.separator},jumpAmount:{color:P,fontSize:17,fontWeight:"600",fontVariant:["tabular-nums"]},jumpUnit:{color:r.color.textSecondary,fontSize:r.font.caption,marginTop:1},row:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:12,minHeight:50,paddingHorizontal:r.rowPadH,paddingVertical:10},rowDisabled:{opacity:.4},rowTitle:{color:r.color.text,fontSize:r.font.row},rowRight:{flexDirection:"row",alignItems:"center",gap:6,flexShrink:1},rowValue:{color:r.color.textSecondary,fontSize:r.font.row},tabular:{fontVariant:["tabular-nums"]},chevronOpen:{transform:[{rotate:"90deg"}]},actionLabel:{color:r.color.accent,fontSize:r.font.row},actionDestructive:{color:r.color.danger},editor:{paddingHorizontal:r.rowPadH,paddingBottom:14,gap:10},inputRow:{flexDirection:"row",alignItems:"center",gap:8},input:{flex:1,color:r.color.text,fontFamily:_,fontSize:15,backgroundColor:r.color.bg,borderRadius:r.radius.input,borderWidth:1,borderColor:r.color.border,paddingHorizontal:12,paddingVertical:8},hint:{color:r.color.textTertiary,fontSize:r.font.caption},presets:{flexDirection:"row",flexWrap:"wrap",gap:6},segmentRow:{flexDirection:"row"}});
@@ -0,0 +1 @@
1
+ "use strict";import{StyleSheet as T,Text as h,TouchableOpacity as y,View as f}from"react-native";import{ChevronRight as S,NightButton as b,NightCard as A,NightFootnote as R,NightRows as v,NightSectionLabel as D,night as n}from"@buoy-gg/shared-ui";import{formatDuration as I}from"../engine/clockMath";import{useLiveClock as m}from"./useLiveClock";import{formatChipTime as L,formatClockTime as $}from"./format";import{jsx as s,jsxs as p}from"react/jsx-runtime";const u=e=>I(Math.abs(e),{signed:false});export function TokenSection({snapshot:e,run:a,openId:d,onToggle:t}){const r=e.tokens;return p(f,{children:[s(D,{children:r.tokens.length>1?"Sign-in tokens":"Sign-in token"}),s(A,{children:p(v,{children:[r.tokens.map((o,l)=>p(f,{children:[s(z,{token:o,snapshot:e,index:l,open:d===o.id,onPress:()=>t(o.id)}),d===o.id?s(N,{token:o,snapshot:e,run:a}):null]},o.id)),r.tokens.length===0?s(f,{style:i.row,testID:"clock-token-empty",children:s(h,{style:i.emptyText,children:r.available?"No token seen yet":"Needs the Network tool"})},"empty"):null]})}),s(E,{check:r.check,tokens:r.tokens,snapshot:e,run:a}),r.tokens.length===0?s(R,{children:r.available?"Tokens show up here when the app sends a signed-in request.":"Install @buoy-gg/network so the Clock tool can read the app's tokens."}):null]})}function z({token:e,snapshot:a,index:d,open:t,onPress:r}){const{virtual:o}=m(a,1e3);const l=e.expiresAt!==void 0?e.expiresAt-o:void 0;const c=e.neverExpires?"No expiry":l===void 0?"Unknown":l<=0?"Expired":u(l);const x=l!==void 0&&l<=0?i.valueDanger:l!==void 0&&l<6e4?i.valueWarning:null;return p(y,{testID:`clock-token-${d}`,accessibilityRole:"button",accessibilityLabel:`${e.label} for ${e.host}: ${l!==void 0&&l>0?`expires in ${c}`:c}`,accessibilityState:{expanded:t},activeOpacity:.6,onPress:r,style:i.row,children:[p(f,{style:i.rowText,children:[s(h,{style:i.rowTitle,numberOfLines:1,children:e.label}),s(h,{style:i.rowSubtitle,numberOfLines:1,children:`${e.host} \xB7 ${e.hint??e.formatLabel}`})]}),p(f,{style:i.rowRight,children:[s(h,{style:[i.value,x],testID:`clock-token-${d}-left`,children:c}),s(f,{style:t?i.chevronOpen:null,children:s(S,{size:16,color:n.color.textTertiary})})]})]})}function N({token:e,snapshot:a,run:d}){const{virtual:t,real:r}=m(a,1e3);const o=j(e,t,r);const l=e.expiresAt!==void 0&&e.expiresAt-t>1e4;return p(f,{style:i.detail,testID:"clock-token-detail",children:[o.map(c=>s(h,{style:[i.detailLine,c.tone==="warn"?i.warn:null,c.tone==="bad"?i.bad:null],children:c.text},c.text)),p(f,{style:i.buttons,children:[s(f,{style:i.button,children:s(b,{label:"Jump to Expiry",testID:"clock-token-jump",accessibilityLabel:"Jump the app's clock to ten seconds before this token expires",size:"md",disabled:!l,onPress:()=>d("jumpToTokenExpiry",{token:e.id})})}),s(f,{style:i.button,children:s(b,{label:"Fail Next Request",testID:"clock-token-fail",accessibilityLabel:"Answer the app's next request to this API with 401",size:"md",onPress:()=>d("failNextRequest",{token:e.id})})})]}),s(h,{style:i.hint,children:l||e.expiresAt===void 0?"Jump tests an app that refreshes before expiry. Fail tests one that refreshes after a 401.":"This token has already expired on the app's clock. Use Real Time to jump to it again."})]})}function j(e,a,d){const t=[];if(e.expiresAt!==void 0){t.push({text:`Expires ${L(e.expiresAt)}`});if(e.lifetimeMs!==void 0&&e.lifetimeMs>0){t.push({text:`Lasts ${u(e.lifetimeMs)}${e.expirySource?`, from ${e.expirySource}`:""}`})}if(e.serverExpiresAt!==void 0){const r=e.expiresAt-a;const o=e.serverExpiresAt-d;if(Math.abs(r-o)>2e3){t.push({text:o>0?`The server still accepts it for ${u(o)}`:`The server stopped accepting it ${u(o)} ago`})}}}else if(!e.neverExpires){t.push({text:"The token doesn't say when it expires, and no login response did either."})}if(e.hint)t.push({text:e.formatLabel});if(e.issuedBy)t.push({text:`Issued by ${e.issuedBy}`});if(e.refresh?.expiresAt!==void 0){const r=e.refresh.expiresAt-a;t.push({text:r>0?`Refresh token expires in ${u(r)}`:"Refresh token expired"})}if(e.refreshes>0&&e.lastRefreshAt!==void 0){t.push({text:`Refreshed ${e.refreshes===1?"once":`${e.refreshes} times`}, last at ${$(e.lastRefreshAt)}`})}t.push({text:`Sent with ${e.uses} request${e.uses===1?"":"s"}`});if(e.expiredSends>0){t.push({text:`Sent ${e.expiredSends} time${e.expiredSends===1?"":"s"} after it expired on the app's clock`,tone:"warn"})}if(e.rejected>0){t.push({text:`Rejected by the server ${e.rejected} time${e.rejected===1?"":"s"}`,tone:"warn"})}if(e.looping){t.push({text:`Refresh loop: ${e.recentRefreshes} new tokens in 20s`,tone:"bad"})}return t}function E({check:e,tokens:a,snapshot:d,run:t}){const{virtual:r,real:o}=m(d,1e3);if(!e)return null;const l=a.find(g=>g.id===e.tokenId);const{text:c,tone:x}=q(e,l,r,o);return p(f,{style:i.check,children:[s(h,{style:[i.checkText,x==="good"?i.good:null,x==="bad"?i.bad:null,x==="warn"?i.warn:null],testID:"clock-token-check",children:c}),s(y,{testID:"clock-token-check-done",accessibilityRole:"button",accessibilityLabel:e.status==="waiting"?"Stop the refresh check":"Dismiss",onPress:()=>t("stopTokenCheck"),hitSlop:{top:8,bottom:8,left:8,right:8},children:s(h,{style:i.checkDone,children:e.status==="waiting"?"Stop":"Done"})})]})}function q(e,a,d,t){if(e.status==="loop"){return{text:`Refresh loop: ${e.newTokens} new tokens since the check started. The new tokens may already look expired to the app's clock.`,tone:"bad"}}if(e.status==="refreshed"){const c=e.newIssuedBy?` by ${e.newIssuedBy}`:"";const x=e.newLifetimeMs&&e.newLifetimeMs>0?`. The new token lasts ${u(e.newLifetimeMs)}`:"";const g=e.kind==="401"&&e.forcedAt!==void 0?"The app got the 401 and refreshed":"Refreshed";const w=e.failedRefreshes>0?` ${e.failedRefreshes===1?"One token request":`${e.failedRefreshes} token requests`} failed first (${e.lastRefreshError}).`:"";return{text:`${g} at ${$(e.refreshedAt??t)}${c}${x}.${w}`,tone:w?"warn":"good"}}const r=t-e.startedAt;const o=(e.staleAfterFailure>0?` It sent the old token again ${e.staleAfterFailure} time${e.staleAfterFailure===1?"":"s"}.`:"")+(e.failedRefreshes>0?` Its token request failed (${e.lastRefreshError}).`:"");if(r>6e4){return{text:`No new token after ${u(r)}. The app may refresh on its next action, or not at all.${o}`,tone:"warn"}}if(e.kind==="401"){return e.forcedAt===void 0?{text:`The next request to ${a?.host??"the API"} will get a 401. Use the app to send one.`}:{text:`The app got the 401. Waiting for a new token\u2026${o}`,tone:o?"warn":void 0}}const l=a?.expiresAt!==void 0?a.expiresAt-d:void 0;return l!==void 0&&l>0?{text:`Expires in ${u(l)} on the app's clock. Waiting for a new token\u2026`}:{text:"Expired on the app's clock. Waiting for the app's next request\u2026"}}const i=T.create({row:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:12,minHeight:56,paddingHorizontal:n.rowPadH,paddingVertical:10},rowText:{flex:1,minWidth:0},rowTitle:{color:n.color.text,fontSize:n.font.row},rowSubtitle:{color:n.color.textTertiary,fontSize:n.font.caption,marginTop:2},rowRight:{flexDirection:"row",alignItems:"center",gap:6},value:{color:n.color.textSecondary,fontSize:n.font.row,fontVariant:["tabular-nums"]},valueWarning:{color:n.color.warning},valueDanger:{color:n.color.danger},chevronOpen:{transform:[{rotate:"90deg"}]},emptyText:{color:n.color.textSecondary,fontSize:n.font.row},detail:{paddingHorizontal:n.rowPadH,paddingBottom:14,gap:4},detailLine:{color:n.color.textSecondary,fontSize:n.font.caption},buttons:{flexDirection:"row",gap:8,marginTop:8},button:{flex:1},hint:{color:n.color.textTertiary,fontSize:n.font.caption,marginTop:6},check:{flexDirection:"row",alignItems:"flex-start",gap:12,marginTop:8,marginHorizontal:n.labelInset},checkText:{flex:1,color:n.color.textSecondary,fontSize:n.font.caption,lineHeight:18},checkDone:{color:n.color.accent,fontSize:n.font.caption,fontWeight:"600"},good:{color:n.color.accent},warn:{color:n.color.warning},bad:{color:n.color.danger}});
@@ -0,0 +1 @@
1
+ "use strict";import{RealDate as o}from"../engine/patches";import{formatDuration as a,formatRate as c}from"../engine/clockMath";const n=e=>String(e).padStart(2,"0");export function formatClockTime(e){const t=new o(e);return`${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}`}const l=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];const u=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];export function formatClockDate(e){const t=new o(e);return`${l[t.getDay()]}, ${u[t.getMonth()]} ${t.getDate()}, ${t.getFullYear()}`}export function formatChipTime(e,t=true){const r=new o(e);const i=t?formatClockTime(e):`${n(r.getHours())}:${n(r.getMinutes())}`;return`${u[r.getMonth()]} ${r.getDate()} \xB7 ${i}`}export function formatInputTime(e){const t=new o(e);return`${t.getFullYear()}-${n(t.getMonth()+1)}-${n(t.getDate())} ${n(t.getHours())}:${n(t.getMinutes())}`}export function formatOffset(e){if(Math.abs(e)<1e3)return"matches real time";const t=a(Math.abs(e),{signed:false});return e>0?`${t} ahead of real time`:`${t} behind real time`}export function clockStatus(e){if(e.mode==="real")return{label:"Real time",tone:"neutral"};if(e.mode==="frozen")return{label:"Frozen",tone:"info"};if(e.rate!==1)return{label:`${c(e.rate)} speed`,tone:"warning"};return{label:"Overridden",tone:"accent"}}export function jumpTestId(e){const t=e<0?"minus":"plus";return`clock-jump-${t}-${a(Math.abs(e),{signed:false}).replace(/\s/g,"")}`}
@@ -0,0 +1 @@
1
+ "use strict";import{useEffect as a,useState as n}from"react";import{realNow as f}from"../engine/patches";export function useLiveClock(e,t=250){const[,l]=n(0);a(()=>{const u=setInterval(()=>l(c=>c+1),t);return()=>clearInterval(u)},[t]);const r=f();if(!e)return{virtual:r,real:r};const o=r-e.realNow;const i=e.mode==="real"?1:e.rate;return{virtual:e.mode==="frozen"?e.virtualNow:e.virtualNow+o*i,real:r}}
@@ -0,0 +1 @@
1
+ "use strict";export const REAL_ANCHOR=Object.freeze({mode:"real",anchorReal:0,anchorVirtual:0,rate:1});export const MIN_RATE=.1;export const MAX_RATE=86400;const _=864e13;export function virtualAt(e,t){if(e.mode==="real")return t;if(e.mode==="frozen")return e.anchorVirtual;return e.anchorVirtual+(t-e.anchorReal)*e.rate}export function offsetAt(e,t){return virtualAt(e,t)-t}function u(e){if(!Number.isFinite(e)||Math.abs(e)>_){throw new RangeError(`Not a valid time: ${e}`)}return Math.round(e)}export function travelTo(e,t,r,n=false){return{mode:n?"frozen":"running",anchorReal:t,anchorVirtual:u(r),rate:e.mode==="real"?1:e.rate}}export function shiftBy(e,t,r){if(!Number.isFinite(r))throw new RangeError("Jump must be a number of ms");return travelTo(e,t,virtualAt(e,t)+r,e.mode==="frozen")}export function freezeAt(e,t,r){return travelTo(e,t,r??virtualAt(e,t),true)}export function resume(e,t){if(e.mode!=="frozen")return e;return{...e,mode:"running",anchorReal:t}}export function withRate(e,t,r){if(!Number.isFinite(r)||r<MIN_RATE||r>MAX_RATE){throw new RangeError(`Speed must be between ${MIN_RATE}\xD7 and ${MAX_RATE}\xD7`)}if(e.mode==="real"&&r===1)return e;return{mode:"running",anchorReal:t,anchorVirtual:u(virtualAt(e,t)),rate:r}}export function isAnchor(e){const t=e;return!!t&&(t.mode==="real"||t.mode==="running"||t.mode==="frozen")&&Number.isFinite(t.anchorReal)&&Number.isFinite(t.anchorVirtual)&&Number.isFinite(t.rate)&&t.rate>=MIN_RATE&&t.rate<=MAX_RATE}const d={ms:1,millisecond:1,milliseconds:1,s:1e3,sec:1e3,secs:1e3,second:1e3,seconds:1e3,m:6e4,min:6e4,mins:6e4,minute:6e4,minutes:6e4,h:36e5,hr:36e5,hrs:36e5,hour:36e5,hours:36e5,d:864e5,day:864e5,days:864e5,w:6048e5,wk:6048e5,week:6048e5,weeks:6048e5,mo:30*864e5,month:30*864e5,months:30*864e5,y:365*864e5,yr:365*864e5,year:365*864e5,years:365*864e5};export function parseDuration(e){if(typeof e==="number"){if(!Number.isFinite(e))throw new RangeError("Duration must be finite");return Math.round(e)}let t=String(e).trim().toLowerCase();if(!t)throw new RangeError("Duration is empty");let r=1;if(t[0]==="+"||t[0]==="-"||t[0]==="\u2212"){r=t[0]==="+"?1:-1;t=t.slice(1).trim()}if(/^\d+(\.\d+)?$/.test(t))return r*Math.round(Number(t));const n=/(\d+(?:\.\d+)?)\s*([a-z]+)/g;let i=0;let o="";let s;while(s=n.exec(t)){const a=d[s[2]];if(a===void 0){throw new RangeError(`Unknown time unit "${s[2]}" in "${e}"`)}i+=Number(s[1])*a;o+=s[0]}if(!o||o.replace(/\s/g,"")!==t.replace(/[\s,]/g,"")){throw new RangeError(`Could not read "${e}" as a duration. Try "+1d", "-2h30m" or "90s".`)}return r*Math.round(i)}export function formatDuration(e,{signed:t=true}={}){const r=e<0?"\u2212":"+";let n=Math.abs(Math.round(e));if(n<1e3)return t&&n>0?`${r}${n}ms`:"0s";const i=[];const o=[["y",365*864e5],["d",864e5],["h",36e5],["m",6e4],["s",1e3]];for(const[s,a]of o){if(n>=a){i.push(`${Math.floor(n/a)}${s}`);n%=a}if(i.length===2)break}return`${t?r:""}${i.join(" ")}`}export function parseTime(e,t,r){if(typeof e==="number")return u(e);const n=String(e).trim();if(!n)throw new RangeError("Time is empty");if(/^now$/i.test(n))return t;if(/^[+\-−]/.test(n))return u(t+parseDuration(n));if(/^\d{11,}$/.test(n))return u(Number(n));const i=n.replace(/^(\d{4}-\d{2}-\d{2})[ ](\d{2}:\d{2})/,"$1T$2");const o=r(i);if(!Number.isFinite(o)){throw new RangeError(`Could not read "${e}" as a time. Use ISO 8601 like 2026-12-31T23:59:50, epoch ms, or "+1d".`)}return u(o)}const m=1e4;export const TIME_PRESETS=[{id:"midnight",label:"Before midnight",hint:"23:59:50 tonight",at(e,t){const r=new t(e);r.setHours(24,0,0,0);return r.getTime()-m}},{id:"month-end",label:"Month end",hint:"last day, 23:59:50",at(e,t){const r=new t(e);r.setMonth(r.getMonth()+1,1);r.setHours(0,0,0,0);return r.getTime()-m}},{id:"year-end",label:"New Year's Eve",hint:"Dec 31, 23:59:50",at(e,t){const r=new t(e);r.setFullYear(r.getFullYear()+1,0,1);r.setHours(0,0,0,0);return r.getTime()-m}},{id:"tomorrow-9",label:"Tomorrow 9 AM",hint:"start of a business day",at(e,t){const r=new t(e);r.setDate(r.getDate()+1);r.setHours(9,0,0,0);return r.getTime()}},{id:"leap-day",label:"Leap day",hint:"next Feb 29, 09:00",at(e,t){const r=new t(e);let n=r.getFullYear();for(;;){const i=n%4===0&&n%100!==0||n%400===0;const o=new t(n,1,29,9,0,0,0).getTime();if(i&&o>e)return o;n+=1}}}];export const JUMPS=[{label:"1m",ms:6e4},{label:"15m",ms:15*6e4},{label:"1h",ms:36e5},{label:"1d",ms:864e5},{label:"7d",ms:7*864e5},{label:"30d",ms:30*864e5},{label:"1y",ms:365*864e5}];export const SPEEDS=[1,2,10,60,600,3600];export function formatRate(e){if(e===3600)return"1h/s";if(e===600)return"10m/s";if(e===60)return"1m/s";return`${Number.isInteger(e)?e:e.toFixed(1)}\xD7`}
@@ -0,0 +1 @@
1
+ "use strict";import{REAL_ANCHOR as l,freezeAt as C,offsetAt as b,parseDuration as D,parseTime as T,resume as J,shiftBy as R,travelTo as N,virtualAt as c,withRate as O}from"./clockMath";import{RealDate as x,installDatePatches as I,isAnimatedRealClock as P,isDatePatched as z,isIntlPatched as E,realNow as o,setVirtualSource as m,uninstallDatePatches as y}from"./patches";import{countDueWithin as F,fireDueWithin as _,installTimerTracking as A,isTimerTrackingInstalled as Z,summarizeTimers as B}from"./timers";import{DEFAULT_SETTINGS as p,readSavedAsync as L,readSavedSync as M,writeSaved as W}from"./persistence";import{getTokenReport as j,subscribeTokens as G}from"../tokens/tokenStore";let e={anchor:l,settings:p,lastJump:null,changedAt:null,hydrated:false};const g=new Set;let f=null;function v(){f=null;g.forEach(t=>t())}G(v);export function subscribeClock(t){g.add(t);return()=>{g.delete(t)}}export function getClockAnchor(){return e.anchor}export function getClockSettings(){return e.settings}export function virtualNow(){return c(e.anchor,o())}export function isClockOverridden(){return e.anchor.mode!=="real"}function H(t){if(t.mode==="real"){m(null);y();return}A();m(n=>c(e.anchor,n));I()}function U(){const t={v:1,anchor:e.settings.persist?e.anchor:null,settings:e.settings};void W(t)}function s(t,{save:n=true}={}){e={...e,...t};if(t.anchor)H(e.anchor);if(n)U();v()}function i(t,n=e.lastJump){s({anchor:t,lastJump:n,changedAt:o(),hydrated:true})}const S=t=>x.parse(t);export function setTime(t,n=false){const r=o();const a=c(e.anchor,r);const u=T(t,a,S);const d=u-a;i(N(e.anchor,r,u,n),{byMs:d,firedTimers:0,at:r});return getClockSnapshot()}export function shift(t,n){const r=D(t);const a=o();const u=R(e.anchor,a,r);const d=(n??e.settings.fireTimersOnJump)&&r>0;i(u,{byMs:r,firedTimers:0,at:a});if(d){const k=_(r);if(k>0){s({lastJump:{byMs:r,firedTimers:k,at:a}},{save:false})}}return getClockSnapshot()}export function freeze(t){const n=o();const r=c(e.anchor,n);const a=t===void 0||t===""?r:T(t,r,S);i(C(e.anchor,n,a));return getClockSnapshot()}export function resume(){if(e.anchor.mode==="frozen")i(J(e.anchor,o()));return getClockSnapshot()}export function setRate(t){i(O(e.anchor,o(),Number(t)));return getClockSnapshot()}export function reset(){i(l,null);return getClockSnapshot()}export function updateSettings(t){const n={...e.settings};for(const r of Object.keys(p)){if(typeof t[r]==="boolean")n[r]=t[r]}s({settings:n});return getClockSnapshot()}export function previewJump(t){return t>0?F(t):0}let h=false;function w(t){if(!t){s({hydrated:true},{save:false});return}s({settings:t.settings,anchor:t.anchor??l,changedAt:t.anchor?o():null,hydrated:true},{save:false})}export function bootClock(){if(h)return;h=true;A();const t=M();if(t!==void 0){w(t);return}void L().then(n=>{if(e.changedAt===null)w(n);else s({hydrated:true},{save:false})})}export function isClockBooted(){return h}function V(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone??"unknown"}catch{return"unknown"}}export function getClockSnapshot(){const t=o();const n=c(e.anchor,t);return{mode:e.anchor.mode,active:e.anchor.mode!=="real",rate:e.anchor.rate,virtualNow:n,virtualIso:new x(n).toISOString(),realNow:t,offsetMs:b(e.anchor,t),timeZone:V(),timers:B(),lastJump:e.lastJump,changedAt:e.changedAt,settings:e.settings,hydrated:e.hydrated,tokens:j(),patched:{date:z(),intl:E(),timers:Z(),animatedOnRealClock:P()}}}export function getStableClockSnapshot(){if(!f)f=getClockSnapshot();return f}export function __resetClockStoreForTests(){m(null);y();e={anchor:l,settings:p,lastJump:null,changedAt:null,hydrated:false};h=false;v()}
@@ -0,0 +1 @@
1
+ "use strict";const n=globalThis;const i="__buoyClock";export const RealDate=n.__buoyRealDate??(Date[i]?Date.__buoyRealDate:Date);n.__buoyRealDate=RealDate;const y=RealDate.now.bind(RealDate);export function realNow(){return y()}n.__buoyRealNow=realNow;let l=null;let b=0;export function currentNow(){const e=y();if(!l||b>0)return e;return l(e)}export function appNow(){const e=y();return l?l(e):e}export function setVirtualSource(e){l=e}export function withRealClock(e){b+=1;try{return e()}finally{b-=1}}n.__buoyWithRealClock=withRealClock;function o(...e){const t=new.target;if(!t)return new RealDate(currentNow()).toString();return Reflect.construct(RealDate,e.length===0?[currentNow()]:e,t)}o.prototype=RealDate.prototype;Object.defineProperty(o,"name",{value:"Date"});Object.defineProperty(o,"length",{value:7});Object.defineProperty(o,"now",{value:function e(){return currentNow()},writable:true,configurable:true});Object.defineProperty(o,"parse",{value:RealDate.parse,writable:true,configurable:true});Object.defineProperty(o,"UTC",{value:RealDate.UTC,writable:true,configurable:true});Object.defineProperty(o,i,{value:true});Object.defineProperty(o,"__buoyRealDate",{value:RealDate});export const PatchedDate=o;let f=false;let p;export function isDatePatched(){return f&&n.Date===PatchedDate}function d(){if(n.Date===PatchedDate){f=true;return}const e=Object.getOwnPropertyDescriptor(n,"Date");Object.defineProperty(n,"Date",{value:PatchedDate,writable:e?.writable??true,enumerable:e?.enumerable??false,configurable:true});p=Object.getOwnPropertyDescriptor(RealDate.prototype,"constructor");try{Object.defineProperty(RealDate.prototype,"constructor",{value:PatchedDate,writable:true,enumerable:false,configurable:true})}catch{}f=true}function P(){if(n.Date===PatchedDate){const e=Object.getOwnPropertyDescriptor(n,"Date");Object.defineProperty(n,"Date",{value:RealDate,writable:e?.writable??true,enumerable:e?.enumerable??false,configurable:true})}if(p){try{Object.defineProperty(RealDate.prototype,"constructor",p)}catch{}p=void 0}f=false}let u=[];function O(e){return function(t,...r){return e.call(this,t===void 0?currentNow():t,...r)}}function g(){if(u.length)return;const e=n.Intl?.DateTimeFormat?.prototype;if(!e)return;for(const t of["format","formatToParts"]){const r=Object.getOwnPropertyDescriptor(e,t);if(!r||!r.configurable)continue;try{if(typeof r.get==="function"){const a=r.get;Object.defineProperty(e,t,{...r,get(){return O(a.call(this))}})}else if(typeof r.value==="function"){Object.defineProperty(e,t,{...r,value:O(r.value)})}else continue;u.push({proto:e,name:t,original:r})}catch{}}}function w(){for(const{proto:e,name:t,original:r}of u){try{Object.defineProperty(e,t,r)}catch{}}u=[]}export function isIntlPatched(){return u.length>0}export function installDatePatches(){d();g()}export function uninstallDatePatches(){P();w()}let D=false;function h(e){let t=Object.getPrototypeOf(e);while(t&&t!==Object.prototype){if(!Object.prototype.hasOwnProperty.call(t,i)){for(const r of["start","onUpdate"]){const a=Object.getOwnPropertyDescriptor(t,r);if(a&&typeof a.value==="function"){const c=a.value;t[r]=function(...s){return withRealClock(()=>c.apply(this,s))}}}try{Object.defineProperty(t,i,{value:true})}catch{return}}t=Object.getPrototypeOf(t)}}export function installAnimatedRealClock(e){if(D)return true;try{const t=e;if(typeof t?.Value!=="function")return false;const r=Object.getPrototypeOf(new t.Value(0));const a=r?.animate;if(typeof a!=="function")return false;r.animate=function(c,...s){if(c&&typeof c==="object"){h(c)}return a.call(this,c,...s)};D=true;return true}catch{return false}}export function isAnimatedRealClock(){return D}
@@ -0,0 +1 @@
1
+ "use strict";import{devToolsStorageKeys as n,persistentStorage as o}from"@buoy-gg/shared-ui";import{isAnchor as i}from"./clockMath";export{readSavedSync}from"./readSavedSync";export const DEFAULT_SETTINGS={persist:true,showChip:true,fireTimersOnJump:true};export const CLOCK_STORAGE_KEY=n.clock.state();export function parseSaved(r){if(!r)return null;try{const e=JSON.parse(r);if(!e||e.v!==1)return null;const t=e.settings??{};return{v:1,anchor:i(e.anchor)?e.anchor:null,settings:{persist:typeof t.persist==="boolean"?t.persist:DEFAULT_SETTINGS.persist,showChip:typeof t.showChip==="boolean"?t.showChip:DEFAULT_SETTINGS.showChip,fireTimersOnJump:typeof t.fireTimersOnJump==="boolean"?t.fireTimersOnJump:DEFAULT_SETTINGS.fireTimersOnJump}}}catch{return null}}export async function readSavedAsync(){try{return parseSaved(await o.getItem(CLOCK_STORAGE_KEY))}catch{return null}}let s=Promise.resolve();export function writeSaved(r){const e=JSON.stringify(r);s=s.then(()=>o.setItem(CLOCK_STORAGE_KEY,e)).catch(()=>{});return s.then(()=>{})}
@@ -0,0 +1 @@
1
+ "use strict";import{CLOCK_STORAGE_KEY as r,parseSaved as c}from"./persistence";function u(e){const n=encodeURIComponent(e.replace(/~/g,"~0").replace(/\//g,"~1"));return n===e?[e]:[n,e]}export function readSavedSync(){try{const e=require("expo-file-system");if(typeof e?.File!=="function"||!e.Paths?.document)return void 0;const n=new e.File(e.Paths.document,"react-buoy");if(typeof n.textSync!=="function")return void 0;for(const o of u(r)){const t=new e.File(e.Paths.document,"react-buoy",`${o}.json`);if(t.exists)return c(t.textSync())}return null}catch{return void 0}}
@@ -0,0 +1 @@
1
+ "use strict";export function readSavedSync(){return void 0}
@@ -0,0 +1 @@
1
+ "use strict";import{realNow as C}from"./patches";const T=globalThis;const x="__buoyClockTimer";const u=new Map;let i=null;let k=0;const v=()=>C()+k;function H(r){(i?.setTimeout??setTimeout)(()=>{throw r},0)}function g(r){const t=Number(r);return Number.isFinite(t)&&t>0?t:0}export function isTimerTrackingInstalled(){return i!==null}export function installTimerTracking(){if(i)return true;const{setTimeout:r,clearTimeout:t,setInterval:l,clearInterval:c}=T;if(typeof r!=="function"||typeof t!=="function")return false;if(r[x])return true;i={setTimeout:r,clearTimeout:t,setInterval:typeof l==="function"?l:r,clearInterval:typeof c==="function"?c:t};const n=i;const d=function(e,m,...s){if(typeof e!=="function")return n.setTimeout(e,m,...s);const p=g(m);let f;const y=function(){u.delete(f);return e.apply(this,s)};f=n.setTimeout(y,m);u.set(f,{kind:"timeout",callback:e,args:s,delay:p,due:v()+p,realHandle:f,run:y});return f};const o=function(e,m,...s){if(typeof e!=="function")return n.setInterval(e,m,...s);const p=g(m);let f;const y=function(){const b=u.get(f);if(b)b.due=v()+p;return e.apply(this,s)};f=n.setInterval(y,m);u.set(f,{kind:"interval",callback:e,args:s,delay:p,due:v()+p,realHandle:f,run:y});return f};const a=(e,m)=>{const s=u.get(e);if(!s)return m(e);u.delete(e);return s.kind==="interval"?n.clearInterval(s.realHandle):n.clearTimeout(s.realHandle)};const I=function(e){return a(e,n.clearTimeout)};const h=function(e){return a(e,n.clearInterval)};globalThis.__buoyRealTimers={setTimeout:n.setTimeout,clearTimeout:I,setInterval:n.setInterval,clearInterval:h};for(const e of[d,o,I,h]){Object.defineProperty(e,x,{value:true})}T.setTimeout=d;T.clearTimeout=I;T.setInterval=o;T.clearInterval=h;return true}export function uninstallTimerTracking(){if(!i)return;T.setTimeout=i.setTimeout;T.clearTimeout=i.clearTimeout;T.setInterval=i.setInterval;T.clearInterval=i.clearInterval;i=null;u.clear();k=0;delete globalThis.__buoyRealTimers}export function summarizeTimers(){const r=v();let t=0;let l=0;let c=null;for(const n of u.values()){if(n.kind==="interval")l+=1;else{t+=1;const d=Math.max(0,n.due-r);if(c===null||d<c)c=d}}return{total:t+l,timeouts:t,intervals:l,nextTimeoutInMs:c}}export function countDueWithin(r){const t=v();let l=0;for(const c of u.values())if(c.due-t<=r)l+=1;return l}export function fireDueWithin(r){if(!i||!(r>0))return 0;const t=i;k+=r;const l=v();const c=[...u.entries()].filter(([,o])=>o.due<=l).sort((o,a)=>o[1].due-a[1].due);let n=0;for(const[o,a]of c){if(!u.has(o))continue;if(a.kind==="timeout"){u.delete(o);t.clearTimeout(a.realHandle)}else{a.due=l+a.delay}try{a.callback(...a.args)}catch(I){H(I)}n+=1}const d=v();for(const o of u.values()){if(o.kind!=="timeout")continue;t.clearTimeout(o.realHandle);o.realHandle=t.setTimeout(o.run,Math.max(0,o.due-d))}return n}
@@ -0,0 +1 @@
1
+ "use strict";import{Animated as o}from"react-native";import{bootClock as e}from"./engine/clockStore";import{installAnimatedRealClock as t}from"./engine/patches";import{watchClockChip as r}from"./components/ClockChip";const i=typeof navigator!=="undefined"&&navigator.product==="ReactNative";if(i){t(o);r();if(typeof __DEV__!=="undefined"&&__DEV__)e()}export{clockToolPreset,createClockTool}from"./preset";export{ClockBody}from"./components/ClockBody";export{ClockToolView}from"./components/ClockToolView";export{ClockChip,openClockChip,CLOCK_CHIP_ID}from"./components/ClockChip";export{useLiveClock}from"./components/useLiveClock";export{ClockToolIcon,CLOCK_TOOL_ICON_COLOR}from"@buoy-gg/shared-ui";export{clockSyncAdapter}from"./sync/clockSyncAdapter";export{bootClock,setTime,shift,freeze,resume,setRate,reset,updateSettings,virtualNow,isClockOverridden,getClockSnapshot,subscribeClock,previewJump}from"./engine/clockStore";export{realNow,withRealClock}from"./engine/patches";export{parseDuration,formatDuration,TIME_PRESETS}from"./engine/clockMath";export{startTokenWatch,getTokenReport}from"./tokens/tokenStore";export{jumpToTokenExpiry,failNextRequest,stopTokenCheck}from"./tokens/tokenActions";export{inspectToken,fingerprint as tokenFingerprint}from"./tokens/decode";
@@ -0,0 +1 @@
1
+ "use strict";import{ClockToolIcon as t,CLOCK_TOOL_ICON_COLOR as r,createToolFactory as c,defineToolPreset as l}from"@buoy-gg/shared-ui";import{ClockBody as s}from"./components/ClockBody";import{jsx as i}from"react/jsx-runtime";export const clockToolPreset=l({id:"clock",name:"CLOCK",description:"Travel to any date, freeze or speed up the app's clock",slot:"both",color:r,icon:({size:o,color:e})=>i(t,{size:o,color:e}),component:s,modal:{subtitle:"override the app's date & time"}});export const createClockTool=c(clockToolPreset);
@@ -0,0 +1 @@
1
+ "use strict";import{createSyncAdapter as m}from"@buoy-gg/shared-ui";import{freeze as a,getClockSnapshot as f,reset as c,resume as p,setRate as d,setTime as b,shift as h,subscribeClock as k,updateSettings as l}from"../engine/clockStore";import{startTokenWatch as y}from"../tokens/tokenStore";import{failNextRequest as T,jumpToTokenExpiry as g,stopTokenCheck as N}from"../tokens/tokenActions";function o(t){return t&&typeof t==="object"?t:{}}function i(t,...e){for(const r of e){const n=t[r];if(typeof n==="string"||typeof n==="number")return n}return void 0}function s(t,e){const r=t[e];if(typeof r==="boolean")return r;if(r==="true")return true;if(r==="false")return false;return void 0}export const clockSyncAdapter=m({version:1,getSnapshot(){return f()},subscribe(t){return k(t)},actions:{getState(){y();return f()},setTime(t){const e=o(t);const r=i(e,"time","at","date","iso");if(r===void 0){throw new Error('setTime needs `time`: ISO 8601 ("2026-12-31T23:59:50"), epoch ms, or relative ("+1d").')}return b(r,s(e,"freeze")??false)},shift(t){const e=o(t);const r=i(e,"by","ms","duration","delta");if(r===void 0){throw new Error('shift needs `by`: a duration like "+1d", "-2h30m", "90s", or ms.')}return h(r,s(e,"fireTimers"))},freeze(t){return a(i(o(t),"time","at"))},resume(){return p()},setRate(t){const e=o(t);const r=Number(e.rate??e.speed);if(!Number.isFinite(r))throw new Error("setRate needs a numeric `rate` (1 = normal speed).");return d(r)},reset(){return c()},updateSettings(t){const e=o(t);return l({persist:s(e,"persist"),showChip:s(e,"showChip"),fireTimersOnJump:s(e,"fireTimersOnJump")})},jumpToTokenExpiry(t){const e=o(t);const r=typeof e.token==="string"?e.token:typeof e.id==="string"?e.id:void 0;const n=Number(e.leadMs??e.lead??1e4);return g(r,Number.isFinite(n)?n:1e4)},failNextRequest(t){const e=o(t);const r=typeof e.token==="string"?e.token:typeof e.id==="string"?e.id:void 0;const n=Number(e.status??401);const u=Number(e.times??1);return T(r,n,Number.isFinite(u)?u:1)},stopTokenCheck(){return N()}}});
@@ -0,0 +1 @@
1
+ "use strict";const m="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";const a={};for(let t=0;t<m.length;t++)a[m[t]]=t;a["-"]=62;a["_"]=63;export function base64UrlToBytes(t){const n=t.replace(/=+$/,"");const e=[];let r=0;let s=0;for(let i=0;i<n.length;i++){const o=a[n[i]];if(o===void 0)return null;r=r<<6|o;s+=6;if(s>=8){s-=8;e.push(r>>s&255)}}return e}export function utf8Decode(t){let n="";for(let e=0;e<t.length;){const r=t[e];let s;let i;if(r<128){s=r;i=0}else if(r>=192&&r<224){s=r&31;i=1}else if(r>=224&&r<240){s=r&15;i=2}else if(r>=240&&r<248){s=r&7;i=3}else{return null}for(let o=1;o<=i;o++){const l=t[e+o];if(l===void 0||(l&192)!==128)return null;s=s<<6|l&63}n+=String.fromCodePoint(s);e+=i+1}return n}function p(t){const n=base64UrlToBytes(t);if(!n)return null;return g(n)}function g(t){const n=utf8Decode(t);if(!n||n[0]!=="{")return null;try{const e=JSON.parse(n);return e&&typeof e==="object"&&!Array.isArray(e)?e:null}catch{return null}}function d(t){const n=typeof t==="string"&&t.trim()!==""?Number(t):t;return typeof n==="number"&&Number.isFinite(n)&&n>0?n*1e3:void 0}export function absoluteTime(t,n){if(typeof t==="number"&&Number.isFinite(t)&&t>0){return t<1e11?t*1e3:t}if(typeof t==="string"&&t.trim()!==""){const e=t.trim();if(/^\d+(\.\d+)?$/.test(e))return absoluteTime(Number(e),n);const r=n(e);return Number.isFinite(r)?r:void 0}return void 0}export function lifetimeSeconds(t){const n=typeof t==="string"&&/^\d+(\.\d+)?$/.test(t.trim())?Number(t):t;return typeof n==="number"&&Number.isFinite(n)&&n>0?n:void 0}const b=/^[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*$/;const x=/^[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*$/;const h=/^v([1-4])\.(local|public)\.([A-Za-z0-9_-]+)(?:\.([A-Za-z0-9_-]+))?$/;const A={"1":256,"2":64,"3":96,"4":64};function c(t){if(typeof t==="string"&&t)return t;if(Array.isArray(t)){const n=t.filter(e=>typeof e==="string");return n.length?n.join(", "):void 0}return void 0}function k(t,n){const e=t.iss??"";const r=t.use;if(e.includes("securetoken.google.com"))return"Firebase ID token";if(e.includes("firebaseappcheck"))return"Firebase App Check token";if(e.includes("cognito-idp"))return r?`Cognito ${r} token`:"Cognito token";if(e.includes("appleid.apple.com"))return"Sign in with Apple token";if(e.includes("accounts.google.com"))return"Google ID token";if(e.includes(".auth0.com")||e.includes("auth0"))return"Auth0 token";if(e.includes("clerk"))return"Clerk session token";if(e.includes("supabase")||e.includes("/auth/v1"))return"Supabase access token";if(e.includes("okta"))return"Okta token";if(e.includes("/realms/"))return"Keycloak token";if(e.includes("login.microsoftonline.com")||e.includes("sts.windows.net")){return"Microsoft Entra token"}const s=typeof n?.typ==="string"?n.typ.toLowerCase():"";if(s==="at+jwt"||s==="application/at+jwt")return"OAuth access token";return void 0}function y(t,n){const e={iss:c(t.iss),aud:c(t.aud),sub:c(t.sub),scope:c(t.scope??t.scp),use:c(t.token_use)};const r=d(t.exp);return{claims:e,issuedAt:d(t.iat),notBefore:d(t.nbf),expiresAt:r,expirySource:r?n:void 0}}function _(t){const[n,e]=t.split(".");const r=p(n);if(!r||typeof r.alg!=="string"&&typeof r.typ!=="string")return null;const s=p(e);if(!s)return null;const i=typeof r.alg==="string"?r.alg:void 0;const o=y(s,"exp claim");o.claims.alg=i;if(!o.claims.use&&typeof r.typ==="string"&&r.typ.toUpperCase()!=="JWT"){o.claims.use=r.typ}return{format:"jwt",formatLabel:i?`JWT \xB7 ${i}`:"JWT",hint:k(o.claims,r),...o}}function S(t){const n=p(t.split(".")[0]);if(!n||typeof n.enc!=="string")return null;return{format:"jwe",formatLabel:"Encrypted JWT",claims:{alg:typeof n.alg==="string"?n.alg:void 0}}}function E(t,n){const e=h.exec(t);if(!e)return null;const[,r,s,i]=e;if(s==="local"){return{format:"paseto-local",formatLabel:`PASETO v${r} (encrypted)`,claims:{}}}const o=base64UrlToBytes(i);const l=A[r];const f=o&&o.length>l?g(o.slice(0,o.length-l)):null;const u={format:"paseto-public",formatLabel:`PASETO v${r}`,claims:{}};if(!f)return u;u.claims={iss:c(f.iss),aud:c(f.aud),sub:c(f.sub)};u.issuedAt=absoluteTime(f.iat,n);u.notBefore=absoluteTime(f.nbf,n);u.expiresAt=absoluteTime(f.exp,n);if(u.expiresAt)u.expirySource="exp claim";return u}function w(t){if(!t.startsWith("gAAAAA")||t.length<76)return null;const n=base64UrlToBytes(t);if(!n||n[0]!==128||n.length<73||(n.length-57)%16!==0)return null;let e=0;for(let r=1;r<=8;r++)e=e*256+n[r];if(e<978307200||e>4102444800)return null;return{format:"fernet",formatLabel:"Fernet",claims:{},issuedAt:e*1e3}}const z=[{test:/^ya29\./,hint:"Google OAuth access token"},{test:/^gh[opsu]_/,hint:"GitHub token"},{test:/^github_pat_/,hint:"GitHub token"},{test:/^xox[abeprs]-/,hint:"Slack token"},{test:/^EAA[A-Za-z0-9]{40,}/,hint:"Facebook access token"},{test:/^shp(at|ca|pa|ss)_/,hint:"Shopify token"},{test:/^(sk|rk|pk)_(live|test)_/,hint:"Stripe API key",neverExpires:true},{test:/^AIza[0-9A-Za-z_-]{35}$/,hint:"Google API key",neverExpires:true}];export function inspectToken(t,n=Date.parse){const e=t.trim().replace(/^"(.*)"$/,"$1");if(e.length<16||/\s/.test(e))return null;if(b.test(e)){const i=_(e);if(i)return i}if(x.test(e)){const i=S(e);if(i)return i}if(e.startsWith("v")&&h.test(e)){const i=E(e,n);if(i)return i}const r=w(e);if(r)return r;const s=z.find(i=>i.test.test(e));return{format:"opaque",formatLabel:"Opaque",hint:s?.hint,neverExpires:s?.neverExpires,claims:{}}}export function isStructured(t){return!!t&&t.format!=="opaque"}export function fingerprint(t){let n=2166136261;for(let e=0;e<t.length;e++){n^=t.charCodeAt(e);n=Math.imul(n,16777619)>>>0}return n.toString(16).padStart(8,"0")}
@@ -0,0 +1 @@
1
+ "use strict";import{absoluteTime as K,fingerprint as l,inspectToken as A,isStructured as g,lifetimeSeconds as R}from"./decode";export function originOf(e){const t=/^([a-z][a-z0-9+.-]*:\/\/[^/?#]+)/i.exec(e);return t?t[1].toLowerCase():null}export function hostOf(e){const t=originOf(e);return t?t.replace(/^[a-z][a-z0-9+.-]*:\/\//,""):"unknown"}export function pathOf(e){const t=e.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/?#]+/i,"");const n=t.split(/[?#]/)[0]||"/";return n}function H(e){const t=e.split("#")[0].split("?")[1];if(!t)return[];const n=[];for(const o of t.split("&")){const i=o.indexOf("=");if(i<=0)continue;try{n.push([decodeURIComponent(o.slice(0,i)),decodeURIComponent(o.slice(i+1))])}catch{}}return n}const S=/(token|jwt|session|auth|appcheck|api[-_]?key|apikey|credential|bearer)/i;const q=new Set(["authorization","cookie","dpop","idempotency-key","x-request-id","x-correlation-id","www-authenticate","proxy-authenticate","x-csrf-token","x-xsrf-token"]);const C=new Set(["access_token","accesstoken","id_token","token","auth","auth_token","jwt","session","session_token"]);const N=new Set(["bearer","token","jwt","dpop","oauth","firebase"]);function T(e){const t={};if(!e)return t;for(const[n,o]of Object.entries(e)){if(typeof o==="string")t[n.toLowerCase()]=o;else if(Array.isArray(o))t[n.toLowerCase()]=o.join(", ")}return t}function Y(e){const t=[];for(const n of e.split(";")){const o=n.indexOf("=");if(o<=0)continue;t.push([n.slice(0,o).trim(),n.slice(o+1).trim()])}return t}function k(e,t,n){const o=A(e,n);if(!o)return null;if(g(o)||o.hint)return o;return t?o:null}export function tokensUsed(e,t){const n=[];const o=T(e.requestHeaders);const i=o.authorization;if(i){const s=/^(\S+)\s+(\S+)$/.exec(i.trim());const r=s?s[1]:void 0;const c=s?s[2]:i.trim();if(!r||N.has(r.toLowerCase())){const u=k(c,true,t);if(u){n.push({location:"authorization",name:"Authorization",scheme:r,fp:l(c),info:u})}}}for(const[s,r]of Object.entries(o)){if(q.has(s)||!S.test(s))continue;const c=r.replace(/^bearer\s+/i,"");const u=k(c,true,t);if(u)n.push({location:"header",name:s,fp:l(c),info:u})}if(o.cookie){for(const[s,r]of Y(o.cookie)){const c=k(r,S.test(s)||/^(sid|sess)/i.test(s),t);if(c)n.push({location:"cookie",name:s,fp:l(r),info:c})}}for(const[s,r]of H(e.url)){const c=k(r,C.has(s.toLowerCase()),t);if(c&&C.has(s.toLowerCase())){n.push({location:"query",name:s,fp:l(r),info:c})}}return n}const $=/^(access_?token|accesstoken|token|jwt|auth_?token|session_?token|sessiontoken)$/i;const L=/^(id_?token|idtoken)$/i;const m=/^(refresh_?token|refreshtoken)$/i;const U=["expires_in","expiresIn","ExpiresIn","expires","ttl"];const F=["expires_at","expiresAt","ExpiresAt","expiration","Expiration","expiry","expirationTime","expiration_time","access_token_expires_at","accessTokenExpiresAt","accessTokenExpiry"];const v=["refresh_expires_in","refresh_token_expires_in","refreshTokenExpiresIn","refreshExpiresIn"];const B=["refresh_expires_at","refreshTokenExpiresAt","refresh_token_expires_at"];function D(e){if(typeof e!=="string")return e;const t=e.trim();if(!(t.startsWith("{")||t.startsWith("[")))return null;try{return JSON.parse(t)}catch{return null}}function w(e,t,n){if(t>4||!e||typeof e!=="object")return;if(Array.isArray(e)){for(const i of e.slice(0,5))w(i,t+1,n);return}const o=e;for(const[i,s]of Object.entries(o)){if(typeof s==="string"&&($.test(i)||L.test(i)||m.test(i))){n.push({key:i,value:s,holder:o})}else if(s&&typeof s==="object"){w(s,t+1,n)}}}function W(e){if(m.test(e))return"refresh";if(L.test(e))return"id";if(/session/i.test(e))return"session";return"access"}function I(e,t,n,o,i){for(const s of t){const r=R(e[s]);if(r!==void 0){return{expiresAt:o+r*1e3,source:s,lifetimeMs:r*1e3}}}for(const s of n){const r=K(e[s],i);if(r!==void 0)return{expiresAt:r,source:s}}return{}}function J(e){const t=e["set-cookie"];if(!t)return[];return t.split(/,(?=\s*[A-Za-z0-9!#$%&'*+.^_`|~-]+=)/).map(n=>n.trim())}export function tokensIssued(e,t){if(e.status===void 0||e.status>=400)return[];const n=`${e.method.toUpperCase()} ${pathOf(e.url)}`;const o=e.timestamp+(e.duration??0);const i=[];const s=D(e.responseData);if(s){const c=[];w(s,0,c);const u=new Map;for(const f of c.filter(a=>m.test(a.key))){const a=A(f.value,t);if(!a)continue;const p=I(f.holder,v,B,o,t);u.set(f.holder,{fp:l(f.value),expiresAt:a.expiresAt??p.expiresAt,formatLabel:a.formatLabel})}for(const f of c){if(m.test(f.key))continue;const a=A(f.value,t);if(!a)continue;const p=I(f.holder,U,F,o,t);i.push({fp:l(f.value),info:a,role:W(f.key),expiresAt:p.expiresAt,expirySource:p.source,lifetimeMs:p.lifetimeMs,issuedBy:n,refresh:u.get(f.holder)})}}const r=T(e.responseHeaders);for(const c of J(r)){const[u,...f]=c.split(";");const a=u.indexOf("=");if(a<=0)continue;const p=u.slice(0,a).trim();const h=u.slice(a+1).trim();const x=k(h,S.test(p)||/^(sid|sess)/i.test(p),t);if(!x||h===""||h.toLowerCase()==="deleted")continue;let _;let d;let E;for(const j of f){const[M,...z]=j.split("=");const O=M.trim().toLowerCase();const y=z.join("=").trim();if(O==="max-age"&&/^-?\d+$/.test(y)){E=Number(y)*1e3;_=o+E;d="Max-Age"}else if(O==="expires"&&d!=="Max-Age"){const b=t(y);if(Number.isFinite(b)){_=b;d="Expires"}}}i.push({fp:l(h),info:x,role:"cookie",cookie:p,expiresAt:x.expiresAt??_,expirySource:x.expiresAt?x.expirySource:d,lifetimeMs:x.expiresAt?void 0:E,issuedBy:n})}return i}
@@ -0,0 +1 @@
1
+ "use strict";import{dispatchToolAction as d}from"@buoy-gg/shared-ui";import{getClockSnapshot as s,shift as w,virtualNow as g}from"../engine/clockStore";import{beginTokenCheck as u,clearTokenCheck as m,getTokenOrigin as y,getTokenReport as x,getTokenRow as T,startTokenWatch as $}from"./tokenStore";const h="No sign-in token seen yet. Use the app so it sends a signed-in request, then try again. Tokens are read from the Network tool's captured requests.";function l(r){$();const o=T(r);if(!o)throw new Error(r?`No token "${r}". Read getState for the current ids.`:h);return o}export function jumpToTokenExpiry(r,o=1e4){const e=l(r);if(e.expiresAt===void 0){throw new Error(e.neverExpires?`${e.label} for ${e.host} is ${e.hint??"a key"} and never expires.`:`${e.label} for ${e.host} (${e.formatLabel}) has no expiry Buoy can read. Use failNextRequest to test the refresh instead.`)}const t=e.expiresAt-Math.max(0,o)-g();if(t<=0){throw new Error(`${e.label} for ${e.host} has already expired on the app's clock. Go back to real time, or use failNextRequest.`)}w(t);u("expiry",e.id);return s()}export async function failNextRequest(r,o=401,e=1){const t=l(r);const n=y(t.id);if(!n)throw new Error(h);await k();const f=o===403?403:401;const p=t.location==="authorization"||t.location==="header"?{urlPattern:`${n}/*`,requireHeader:t.name.toLowerCase()}:t.location==="query"?{urlPattern:`${n}/*${t.name}=*`}:{urlPattern:`${n}/*`};const i=await d("network","upsertOverrideRule",{name:"Clock: force a token refresh",...p,kind:"respond",status:f,headers:{"content-type":"application/json","www-authenticate":'Bearer error="invalid_token", error_description="The access token expired"'},body:JSON.stringify({error:"invalid_token",error_description:"The access token expired (forced by Buoy Clock to test token refresh)"}),times:Math.max(1,Math.min(10,Math.round(e)))}).catch(a=>{const c=a instanceof Error?a.message:String(a);throw new Error(c.includes("Unknown tool")?"Failing a request needs the Network tool (@buoy-gg/network).":c)});if(!i?.ok||!i.rule)throw new Error(i?.error??"Network did not add the override.");u("401",t.id,i.rule.id);return s()}async function k(){const r=x().check;if(r?.ruleId&&r.forcedAt===void 0){await d("network","deleteOverrideRule",{id:r.ruleId}).catch(()=>{})}}export async function stopTokenCheck(){await k();m();return s()}
@@ -0,0 +1 @@
1
+ "use strict";import{dispatchToolAction as $}from"@buoy-gg/shared-ui";import{loadToolModule as F}from"@buoy-gg/shared-ui/utils";import{RealDate as L,appNow as C,realNow as h}from"../engine/patches";import{hostOf as j,originOf as R,pathOf as T,tokensIssued as D,tokensUsed as E}from"./extract";const m=e=>L.parse(e);const M=4;const U=2e4;const P=8;const W=8;const z=200;const X=1e3;const u=new Map;const c=new Map;const I=new Set;const O=new Set;const x=new Set;const q=/(token|refresh|oauth|session|auth\/)/i;let i=null;let d=true;let a=null;const A=new Set;let k=false;function p(){if(k)return;k=true;Promise.resolve().then(()=>{k=false;A.forEach(e=>e())})}export function subscribeTokens(e){A.add(e);return()=>{A.delete(e)}}function b(e,t){if(e.has(t))return false;if(e.size>=X){const s=e.values().next().value;if(s!==void 0)e.delete(s)}e.add(t);return true}function y(e,t){return`${e}|${t.location}:${t.name.toLowerCase()}`}function v(e,t){const s=t?.lifetimeMs!==void 0&&t.expiresAt!==void 0;const o=s?t.expiresAt:e.expiresAt??t?.expiresAt;const l=e.expiresAt??(s?t.observedReal+t.lifetimeMs:t?.expiresAt);const n=s?t.expirySource:e.expiresAt?e.expirySource:t?.expirySource;const r=s?t.lifetimeMs:e.expiresAt!==void 0&&e.issuedAt!==void 0?e.expiresAt-e.issuedAt:void 0;return{expiresAt:o,serverExpiresAt:l,source:n,lifetimeMs:r}}function g(e,t,s,o,l){const n=c.get(t);const r=v(s,n);const f=e.fp!==t;if(f){e.history.unshift({fp:e.fp,firstSeenReal:e.firstSeenReal,expiresAt:e.expiresAt});e.history.length=Math.min(e.history.length,W);e.fp=t;e.firstSeenReal=o;e.firstSeenApp=l;e.uses=0;e.expiredSends=0;e.rejected=0}e.info=s;e.role=n?.role??(f?void 0:e.role);e.issuedBy=n?.issuedBy??(f?void 0:e.issuedBy);e.refresh=n?.refresh??(f?void 0:e.refresh);e.expiresAt=r.expiresAt;e.serverExpiresAt=r.serverExpiresAt;e.expirySource=r.source;e.lifetimeMs=r.lifetimeMs;if(f)H(e,o)}function _(e,t,s,o,l){const n=c.get(s.fp);const r=v(s.info,n);return{key:e,origin:t,host:j(t),location:s.location,name:s.name,scheme:s.scheme,fp:s.fp,info:s.info,role:n?.role,issuedBy:n?.issuedBy,refresh:n?.refresh,expiresAt:r.expiresAt,serverExpiresAt:r.serverExpiresAt,expirySource:r.source,lifetimeMs:r.lifetimeMs,firstSeenReal:o,firstSeenApp:l,lastUsedReal:o,lastUsedApp:l,uses:0,expiredSends:0,rejected:0,history:[]}}function B(){if(u.size<=P)return;let e=null;for(const t of u.values()){if(!e||t.lastUsedReal<e.lastUsedReal)e=t}if(e&&e.key!==i?.tokenId)u.delete(e.key)}function H(e,t){if(!i||i.tokenId!==e.key)return;i.newTokens+=1;if(i.status==="waiting"){i.status="refreshed";i.refreshedAt=t;if(i.ruleId&&i.forcedAt===void 0){void $("network","deleteOverrideRule",{id:i.ruleId}).catch(()=>{})}}if(i.newTokens>=M&&t-(i.refreshedAt??t)<=U){i.status="loop"}}function K(e){const t=R(e.url);if(!t)return;const s=E(e,m);if(s.length===0)return;const o=h();const l=e.timestamp;for(const n of s){const r=y(t,n);let f=u.get(r);if(!f){f=_(r,t,n,o,l);u.set(r,f);B()}else{g(f,n.fp,n.info,o,l)}f.uses+=1;f.lastUsedReal=o;f.lastUsedApp=l;if(f.expiresAt!==void 0&&l>=f.expiresAt)f.expiredSends+=1;if(i&&i.tokenId===r&&i.forcedAt!==void 0&&n.fp===i.startFp){i.staleAfterFailure+=1}}p()}function Q(e){const t=R(e.url);if(!t)return;const s=h();let o=false;if(e.status===401||e.status===403){for(const n of E(e,m)){const r=u.get(y(t,n));if(r&&r.fp===n.fp){r.rejected+=1;o=true}}}const l=!!i?.ruleId&&e.override?.ruleId===i.ruleId;if(i&&(e.status??0)>=400&&!l){const n=`${e.method.toUpperCase()} ${T(e.url)}`;const r=x.has(n);if(r||e.method.toUpperCase()==="POST"&&q.test(T(e.url))){i.failedRefreshes+=1;i.lastRefreshError=`${e.status} ${n}`;o=true}}if(i?.ruleId&&e.override?.ruleId===i.ruleId&&i.forcedAt===void 0){i.forcedAt=s;o=true}for(const n of D(e,m)){x.add(n.issuedBy);c.set(n.fp,{...n,observedReal:s});if(c.size>z){const r=c.keys().next().value;if(r!==void 0)c.delete(r)}o=true;if(n.cookie){const r={location:"cookie",name:n.cookie,fp:n.fp,info:n.info};const f=y(t,r);const S=u.get(f);if(S)g(S,n.fp,n.info,s,e.timestamp);else{u.set(f,_(f,t,r,s,e.timestamp));B()}continue}for(const r of u.values()){if(r.fp===n.fp)g(r,n.fp,r.info,s,r.lastUsedApp)}}if(o)p()}function N(e){const t=e;if(!t||typeof t.id!=="string"||typeof t.url!=="string")return;if(b(I,t.id))K(t);if(t.status!==void 0&&b(O,t.id))Q(t)}export function startTokenWatch(){if(a)return true;try{const{networkEventStore:e}=F("@buoy-gg/network");if(!e?.onEvent)throw new Error("no event store");a=e.onEvent(N,true);d=true}catch{d=false}p();return d}export function isTokenWatchOn(){return a!==null}export function beginTokenCheck(e,t,s){const o=u.get(t);if(!o)throw new Error(`No token row "${t}".`);i={kind:e,tokenId:t,startedAt:h(),startFp:o.fp,ruleId:s,status:"waiting",newTokens:0,staleAfterFailure:0,failedRefreshes:0};p();return i}export function clearTokenCheck(){i=null;p()}function Y(e){if(e.location==="cookie")return`${e.name} cookie`;if(e.location==="query")return`${e.name} parameter`;if(e.location==="header")return`${e.name} header`;if(e.role==="id"||e.info.claims.use==="id")return"ID token";return"Access token"}function G(e,t){const s=[e.firstSeenReal,...e.history.map(n=>n.firstSeenReal)].slice(0,-1);const o=s.filter(n=>t-n<=U).length;const l=e.info.issuedAt;return{id:e.key,label:Y(e),host:e.host,location:e.location,name:e.name,scheme:e.scheme,format:e.info.format,formatLabel:e.info.formatLabel,hint:e.info.hint,claims:e.info.claims,fingerprint:e.fp,issuedAt:l,notBefore:e.info.notBefore,expiresAt:e.expiresAt,serverExpiresAt:e.serverExpiresAt,expirySource:e.expirySource,lifetimeMs:e.lifetimeMs,neverExpires:e.info.neverExpires,issuedBy:e.issuedBy,refresh:e.refresh?{expiresAt:e.refresh.expiresAt,formatLabel:e.refresh.formatLabel}:void 0,firstUsed:e.firstSeenApp,lastUsed:e.lastUsedApp,lastUsedReal:e.lastUsedReal,uses:e.uses,refreshes:e.history.length,lastRefreshAt:e.history.length?e.firstSeenReal:void 0,recentRefreshes:o,looping:o>=M,expiredSends:e.expiredSends,rejected:e.rejected}}export function getTokenReport(){const e=h();const t=[...u.values()].sort((s,o)=>o.lastUsedReal-s.lastUsedReal).map(s=>G(s,e));if(i?.status==="refreshed"){const s=u.get(i.tokenId);if(s){i.newLifetimeMs=s.lifetimeMs;i.newIssuedBy=s.issuedBy}}return{available:d,watching:a!==null,tokens:t,check:i?{...i}:null}}export function getTokenRow(e){const t=getTokenReport();if(e)return t.tokens.find(s=>s.id===e)??null;return t.tokens.find(s=>s.expiresAt!==void 0)??t.tokens[0]??null}export function getTokenOrigin(e){return u.get(e)?.origin??null}export const tokenAppNow=C;export function __ingestForTests(e){N(e)}export function __resetTokensForTests(){u.clear();c.clear();I.clear();O.clear();x.clear();i=null;d=true;a?.();a=null}
@@ -0,0 +1,2 @@
1
+ export declare function ClockBody(): import("react").JSX.Element;
2
+ //# sourceMappingURL=ClockBody.d.ts.map