@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
@@ -0,0 +1,11 @@
1
+ import { type FloatingToolRenderContext } from "@buoy-gg/shared-ui";
2
+ export declare const CLOCK_CHIP_ID = "clock-chip";
3
+ /** Show the chip now (e.g. from the tool's header). */
4
+ export declare function openClockChip(): void;
5
+ /**
6
+ * Open the chip when an override starts and close it when the clock goes
7
+ * back to real time. Idempotent.
8
+ */
9
+ export declare function watchClockChip(): void;
10
+ export declare function ClockChip({ id, revealToken }: FloatingToolRenderContext): import("react").JSX.Element;
11
+ //# sourceMappingURL=ClockChip.d.ts.map
@@ -0,0 +1,10 @@
1
+ import type { ClockSnapshot } from "../engine/clockStore";
2
+ export type ClockActionHandler = (action: string, params?: Record<string, unknown>) => Promise<unknown>;
3
+ export interface ClockToolViewProps {
4
+ snapshot: ClockSnapshot | null;
5
+ onAction: ClockActionHandler;
6
+ /** Reload the app's JS so screens re-read the clock. Hidden when absent. */
7
+ onReload?: () => void;
8
+ }
9
+ export declare function ClockToolView({ snapshot, onAction, onReload }: ClockToolViewProps): import("react").JSX.Element;
10
+ //# sourceMappingURL=ClockToolView.d.ts.map
@@ -0,0 +1,10 @@
1
+ import type { ClockSnapshot } from "../engine/clockStore";
2
+ type Run = (action: string, params?: Record<string, unknown>) => void;
3
+ export declare function TokenSection({ snapshot, run, openId, onToggle, }: {
4
+ snapshot: ClockSnapshot;
5
+ run: Run;
6
+ openId: string | null;
7
+ onToggle: (id: string) => void;
8
+ }): import("react").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=TokenSection.d.ts.map
@@ -0,0 +1,18 @@
1
+ import type { ClockSnapshot } from "../engine/clockStore";
2
+ import type { NightBadgeTone } from "@buoy-gg/shared-ui";
3
+ /** "14:03:22" in the device's time zone. */
4
+ export declare function formatClockTime(ms: number): string;
5
+ /** "Tue, Sep 29, 2026". Built by hand so it reads the same on every engine. */
6
+ export declare function formatClockDate(ms: number): string;
7
+ /** "Sep 30 · 14:03" for the chip. */
8
+ export declare function formatChipTime(ms: number, withSeconds?: boolean): string;
9
+ /** "2026-09-29 14:03": what the time field shows and accepts. */
10
+ export declare function formatInputTime(ms: number): string;
11
+ /** "+1d 3h ahead" / "2h behind" / "matches real time". */
12
+ export declare function formatOffset(offsetMs: number): string;
13
+ export declare function clockStatus(snapshot: Pick<ClockSnapshot, "mode" | "rate">): {
14
+ label: string;
15
+ tone: NightBadgeTone;
16
+ };
17
+ export declare function jumpTestId(ms: number): string;
18
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1,22 @@
1
+ import type { ClockSnapshot } from "../engine/clockStore";
2
+ export interface LiveClock {
3
+ /** What the app's clock reads now. */
4
+ virtual: number;
5
+ /** The device's real clock now. */
6
+ real: number;
7
+ }
8
+ /**
9
+ * Keep a snapshot's clocks moving between snapshots. The store only emits on
10
+ * changes, so the display extrapolates from the last one: the app's clock
11
+ * advances `rate` times as fast as real time, or not at all when frozen.
12
+ *
13
+ * Elapsed time is measured from the snapshot's own `realNow`, not from when
14
+ * this component first saw it: a cached snapshot can be minutes old by the
15
+ * time the tool opens. On Buoy Desktop that assumes the desktop and device
16
+ * clocks agree, which NTP keeps within milliseconds for simulators and
17
+ * normally-configured phones.
18
+ *
19
+ * Only the component that calls this re-renders on the tick.
20
+ */
21
+ export declare function useLiveClock(snapshot: ClockSnapshot | null, everyMs?: number): LiveClock;
22
+ //# sourceMappingURL=useLiveClock.d.ts.map
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The clock model, as pure functions of (anchor, real time).
3
+ *
4
+ * One anchor describes every override: at real time `anchorReal` the app's
5
+ * clock read `anchorVirtual`, and since then it has moved `rate` times as
6
+ * fast as the real clock (or not at all when frozen). Every operation
7
+ * re-anchors at the current moment, so the virtual clock never jumps unless
8
+ * the operation asked it to.
9
+ *
10
+ * Nothing here reads the clock or touches globals. Callers pass `real` in,
11
+ * so the whole model is testable without patching anything.
12
+ */
13
+ export type ClockMode = "real" | "running" | "frozen";
14
+ export interface ClockAnchor {
15
+ mode: ClockMode;
16
+ /** Real epoch ms when this anchor was set. */
17
+ anchorReal: number;
18
+ /** What the app's clock read at `anchorReal`. */
19
+ anchorVirtual: number;
20
+ /** Virtual ms per real ms while running. 1 = normal speed. */
21
+ rate: number;
22
+ }
23
+ export declare const REAL_ANCHOR: ClockAnchor;
24
+ export declare const MIN_RATE = 0.1;
25
+ export declare const MAX_RATE = 86400;
26
+ export declare function virtualAt(anchor: ClockAnchor, real: number): number;
27
+ /** Virtual minus real, at `real`. 0 when the override is off. */
28
+ export declare function offsetAt(anchor: ClockAnchor, real: number): number;
29
+ /** Move the app's clock to `target`. Keeps the current speed. */
30
+ export declare function travelTo(anchor: ClockAnchor, real: number, target: number, freeze?: boolean): ClockAnchor;
31
+ /** Move the app's clock by `deltaMs`. A frozen clock stays frozen. */
32
+ export declare function shiftBy(anchor: ClockAnchor, real: number, deltaMs: number): ClockAnchor;
33
+ /** Stop the app's clock at `at`, or at whatever it reads now. */
34
+ export declare function freezeAt(anchor: ClockAnchor, real: number, at?: number): ClockAnchor;
35
+ /** Start a frozen clock again from the moment it was frozen at. */
36
+ export declare function resume(anchor: ClockAnchor, real: number): ClockAnchor;
37
+ /** Run the app's clock `rate` times as fast, starting from what it reads now. */
38
+ export declare function withRate(anchor: ClockAnchor, real: number, rate: number): ClockAnchor;
39
+ export declare function isAnchor(value: unknown): value is ClockAnchor;
40
+ /**
41
+ * "+1d", "-2h30m", "90s", "1.5 hours", "-1w 2d", or a number of ms.
42
+ * Months are 30 days and years 365 days; use setTime for calendar dates.
43
+ */
44
+ export declare function parseDuration(input: string | number): number;
45
+ /** "+2d 3h", "−45m", "+12s", "0s". Drops units below the two largest. */
46
+ export declare function formatDuration(ms: number, { signed }?: {
47
+ signed?: boolean | undefined;
48
+ }): string;
49
+ /**
50
+ * An absolute time from user or agent input: epoch ms (number or digits),
51
+ * an ISO 8601 string, anything the platform's Date.parse accepts, or a
52
+ * relative "+1d" / "-2h" measured from `now`.
53
+ */
54
+ export declare function parseTime(input: string | number, now: number, parse: (text: string) => number): number;
55
+ export interface TimePreset {
56
+ id: string;
57
+ label: string;
58
+ /** Short line under the label. */
59
+ hint: string;
60
+ at(now: number, DateCtor: DateConstructor): number;
61
+ }
62
+ export declare const TIME_PRESETS: readonly TimePreset[];
63
+ /** Jump sizes offered as one-tap chips. */
64
+ export declare const JUMPS: readonly {
65
+ label: string;
66
+ ms: number;
67
+ }[];
68
+ export declare const SPEEDS: readonly number[];
69
+ export declare function formatRate(rate: number): string;
70
+ //# sourceMappingURL=clockMath.d.ts.map
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The clock override's single source of truth on the device.
3
+ *
4
+ * Holds the anchor and settings, keeps the global patches in step with them
5
+ * (installed while an override is on, removed when it is reset), fires due
6
+ * timers on forward jumps, and saves every change. The UI, the sync adapter
7
+ * (desktop + MCP + Ask Buoy) and the floating chip all go through the
8
+ * actions here, so there is one behavior whichever surface changed the clock.
9
+ */
10
+ import { type ClockAnchor, type ClockMode } from "./clockMath";
11
+ import { type TimerSummary } from "./timers";
12
+ import { type ClockSettings } from "./persistence";
13
+ import { type TokenReport } from "../tokens/tokenStore";
14
+ export interface LastJump {
15
+ /** Signed ms the clock moved. */
16
+ byMs: number;
17
+ /** Timer callbacks the jump ran. */
18
+ firedTimers: number;
19
+ /** Real ms when it happened. */
20
+ at: number;
21
+ }
22
+ export declare function subscribeClock(listener: () => void): () => void;
23
+ export declare function getClockAnchor(): ClockAnchor;
24
+ export declare function getClockSettings(): ClockSettings;
25
+ /** What the app's clock reads now (real time when no override is on). */
26
+ export declare function virtualNow(): number;
27
+ export declare function isClockOverridden(): boolean;
28
+ export type TimeInput = string | number;
29
+ /** Move the app's clock to a moment. Keeps running unless `freeze`. */
30
+ export declare function setTime(time: TimeInput, freeze?: boolean): ClockSnapshot;
31
+ /**
32
+ * Move the app's clock by a duration ("+1d", "-2h", or ms). Moving forward
33
+ * runs the timers due inside the jump unless `fireTimers` is false (default:
34
+ * the `fireTimersOnJump` setting).
35
+ */
36
+ export declare function shift(by: TimeInput, fireTimers?: boolean): ClockSnapshot;
37
+ /** Stop the app's clock, at `time` if given, else at what it reads now. */
38
+ export declare function freeze(time?: TimeInput): ClockSnapshot;
39
+ /** Start a frozen clock again from where it stopped. */
40
+ export declare function resume(): ClockSnapshot;
41
+ /** Run the app's clock `rate` times as fast (1 = normal). */
42
+ export declare function setRate(rate: number): ClockSnapshot;
43
+ /** Back to the device's real clock. */
44
+ export declare function reset(): ClockSnapshot;
45
+ export declare function updateSettings(patch: Partial<ClockSettings>): ClockSnapshot;
46
+ /** Timer callbacks a forward jump of `byMs` would run right now. */
47
+ export declare function previewJump(byMs: number): number;
48
+ /**
49
+ * Start tracking timers and restore a saved override. Idempotent. Runs at
50
+ * package import in dev builds on a real React Native runtime; call it from
51
+ * your entry file to have a saved override in place before any app module
52
+ * reads the clock.
53
+ */
54
+ export declare function bootClock(): void;
55
+ export declare function isClockBooted(): boolean;
56
+ export interface ClockSnapshot {
57
+ mode: ClockMode;
58
+ /** Whether any override is on. */
59
+ active: boolean;
60
+ rate: number;
61
+ /** What the app's clock read when this snapshot was taken (epoch ms). */
62
+ virtualNow: number;
63
+ virtualIso: string;
64
+ /** The device's real clock at the same moment. */
65
+ realNow: number;
66
+ /** virtualNow − realNow. */
67
+ offsetMs: number;
68
+ /** The device's time zone, which the override never changes. */
69
+ timeZone: string;
70
+ timers: TimerSummary;
71
+ lastJump: LastJump | null;
72
+ changedAt: number | null;
73
+ settings: ClockSettings;
74
+ hydrated: boolean;
75
+ /** The app's sign-in tokens, read from Network's captured requests. */
76
+ tokens: TokenReport;
77
+ /** Which patches are live, for answering "why didn't X move". */
78
+ patched: {
79
+ date: boolean;
80
+ intl: boolean;
81
+ timers: boolean;
82
+ animatedOnRealClock: boolean;
83
+ };
84
+ }
85
+ export declare function getClockSnapshot(): ClockSnapshot;
86
+ /**
87
+ * A snapshot object that only changes identity when the store changes,
88
+ * what `useSyncExternalStore` needs. Its times are as of that change; use
89
+ * `useLiveClock` for a ticking display.
90
+ */
91
+ export declare function getStableClockSnapshot(): ClockSnapshot;
92
+ /** Test seam. */
93
+ export declare function __resetClockStoreForTests(): void;
94
+ //# sourceMappingURL=clockStore.d.ts.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The global patches: `Date`, no-argument `Intl.DateTimeFormat` formatting,
3
+ * and a real-clock scope for React Native's JS-driven animations.
4
+ *
5
+ * `Date` is replaced by a function that shares the real `Date.prototype`, so
6
+ * `instanceof Date`, `Object.prototype.toString` and every Date method keep
7
+ * working on dates made before, during and after an override. Only the two
8
+ * ways of asking "what time is it" change: `new Date()` with no arguments and
9
+ * `Date.now()`. `Date()` called without `new` returns the virtual time as a
10
+ * string, as it would natively.
11
+ *
12
+ * Everything is reversible: `uninstallDatePatches()` puts the originals back
13
+ * when the override is reset, so a clock at real time costs nothing.
14
+ */
15
+ /**
16
+ * The real Date, captured once. If another copy of this package already
17
+ * installed its patch, that copy published the real constructor first.
18
+ */
19
+ export declare const RealDate: DateConstructor;
20
+ /**
21
+ * The device's real clock. Published on `globalThis.__buoyRealNow` so Buoy's
22
+ * own packages (license checks, sync throttles, Ask Buoy deadlines) can keep
23
+ * measuring real time while the app's clock is overridden.
24
+ */
25
+ export declare function realNow(): number;
26
+ /** What `Date.now()` returns right now, honoring real-clock scopes. */
27
+ export declare function currentNow(): number;
28
+ /** What the app's clock reads now, even inside a real-clock scope. */
29
+ export declare function appNow(): number;
30
+ /** Point the patches at a virtual clock, or `null` for real time. */
31
+ export declare function setVirtualSource(source: ((real: number) => number) | null): void;
32
+ /** Run `fn` against the real clock even while an override is active. */
33
+ export declare function withRealClock<T>(fn: () => T): T;
34
+ export declare const PatchedDate: DateConstructor;
35
+ export declare function isDatePatched(): boolean;
36
+ export declare function isIntlPatched(): boolean;
37
+ /** Install the Date + Intl patches. Idempotent. */
38
+ export declare function installDatePatches(): void;
39
+ /** Put the real Date and Intl back. Idempotent. */
40
+ export declare function uninstallDatePatches(): void;
41
+ export declare function installAnimatedRealClock(Animated: unknown): boolean;
42
+ export declare function isAnimatedRealClock(): boolean;
43
+ //# sourceMappingURL=patches.d.ts.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Saving the override so it survives a reload.
3
+ *
4
+ * Writes go through Buoy's `persistentStorage` (a file under
5
+ * Documents/react-buoy/ when expo-file-system is installed, AsyncStorage
6
+ * otherwise). Reads try the same file SYNCHRONOUSLY first, through
7
+ * expo-file-system's `File.textSync()` (SDK 54+), so a saved override is back
8
+ * in place before the app's own modules read the clock. Without that API the
9
+ * read is async and the override lands a few milliseconds into startup.
10
+ */
11
+ import { type ClockAnchor } from "./clockMath";
12
+ export { readSavedSync } from "./readSavedSync";
13
+ export interface ClockSettings {
14
+ /** Keep the override across reloads and restarts. */
15
+ persist: boolean;
16
+ /** Show the floating clock chip while an override is on. */
17
+ showChip: boolean;
18
+ /** Forward jumps run the timers that come due inside them. */
19
+ fireTimersOnJump: boolean;
20
+ }
21
+ export declare const DEFAULT_SETTINGS: ClockSettings;
22
+ export interface SavedClock {
23
+ v: 1;
24
+ anchor: ClockAnchor | null;
25
+ settings: ClockSettings;
26
+ }
27
+ export declare const CLOCK_STORAGE_KEY: "@react_buoy_clock_state";
28
+ export declare function parseSaved(raw: string | null | undefined): SavedClock | null;
29
+ export declare function readSavedAsync(): Promise<SavedClock | null>;
30
+ /** Queue a write; the last call wins. */
31
+ export declare function writeSaved(saved: SavedClock): Promise<void>;
32
+ //# sourceMappingURL=persistence.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Read the saved override synchronously, from the file persistentStorage
3
+ * writes under Documents/react-buoy/, via expo-file-system's `File.textSync()`
4
+ * (SDK 54+). `undefined` means a synchronous read isn't possible here.
5
+ */
6
+ import { type SavedClock } from "./persistence";
7
+ /** The saved override, read synchronously, or `undefined` if that's impossible here. */
8
+ export declare function readSavedSync(): SavedClock | null | undefined;
9
+ //# sourceMappingURL=readSavedSync.d.ts.map
@@ -0,0 +1,4 @@
1
+ import type { SavedClock } from "./persistence";
2
+ /** Web and Buoy Desktop never patch their own clock, so there is nothing to read early. */
3
+ export declare function readSavedSync(): SavedClock | null | undefined;
4
+ //# sourceMappingURL=readSavedSync.web.d.ts.map
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Timer tracking, so a forward jump can fire the timers that would have come
3
+ * due inside it.
4
+ *
5
+ * `setTimeout` / `setInterval` are wrapped to remember each pending callback
6
+ * and when it is due on a "timer clock": real time plus every forward jump so
7
+ * far. A jump of Δ advances that clock and runs every callback now due, once,
8
+ * in due order, the same rule as Playwright's `clock.fastForward`. Timeouts
9
+ * still pending are rescheduled to their shortened remaining time. Timers
10
+ * created by those callbacks are scheduled from
11
+ * the new time and are not run by the same jump. Delays are never scaled:
12
+ * doing that at 60× would turn every tap into a long press, because
13
+ * Pressability times presses with setTimeout.
14
+ *
15
+ * Wrappers compose with other patches (Buoy's JS Top wraps the same globals):
16
+ * whatever was installed before is what gets called. Handles are the real
17
+ * handles, so code that stores or compares them sees no difference. Code that
18
+ * captured `setTimeout` before this module loaded is not tracked.
19
+ */
20
+ export declare function isTimerTrackingInstalled(): boolean;
21
+ /** Wrap the timer globals. Idempotent; safe to call before any override. */
22
+ export declare function installTimerTracking(): boolean;
23
+ /** Test seam: put the originals back and forget every handle. */
24
+ export declare function uninstallTimerTracking(): void;
25
+ export interface TimerSummary {
26
+ /** Pending timeouts plus live intervals. */
27
+ total: number;
28
+ timeouts: number;
29
+ intervals: number;
30
+ /** Real ms until the soonest one-shot timeout, if any. */
31
+ nextTimeoutInMs: number | null;
32
+ }
33
+ export declare function summarizeTimers(): TimerSummary;
34
+ /** How many tracked timers a jump of `windowMs` would run. */
35
+ export declare function countDueWithin(windowMs: number): number;
36
+ /**
37
+ * Advance the timer clock by `windowMs` and run every tracked timer that is
38
+ * now due, once, soonest first. Due timeouts are cancelled so they do not run
39
+ * again; intervals keep ticking. Timeouts that are still pending are
40
+ * rescheduled to fire when their shortened remaining time runs out, so a 2h
41
+ * timeout fires one real hour after a +1h jump. Returns how many ran.
42
+ */
43
+ export declare function fireDueWithin(windowMs: number): number;
44
+ //# sourceMappingURL=timers.d.ts.map
@@ -0,0 +1,19 @@
1
+ export { clockToolPreset, createClockTool } from "./preset";
2
+ export { ClockBody } from "./components/ClockBody";
3
+ export { ClockToolView } from "./components/ClockToolView";
4
+ export type { ClockToolViewProps, ClockActionHandler } from "./components/ClockToolView";
5
+ export { ClockChip, openClockChip, CLOCK_CHIP_ID } from "./components/ClockChip";
6
+ export { useLiveClock } from "./components/useLiveClock";
7
+ export { ClockToolIcon, CLOCK_TOOL_ICON_COLOR } from "@buoy-gg/shared-ui";
8
+ export { clockSyncAdapter } from "./sync/clockSyncAdapter";
9
+ export { bootClock, setTime, shift, freeze, resume, setRate, reset, updateSettings, virtualNow, isClockOverridden, getClockSnapshot, subscribeClock, previewJump, } from "./engine/clockStore";
10
+ export type { ClockSnapshot, LastJump, TimeInput } from "./engine/clockStore";
11
+ export type { ClockSettings } from "./engine/persistence";
12
+ export { realNow, withRealClock } from "./engine/patches";
13
+ export { parseDuration, formatDuration, TIME_PRESETS, type ClockMode, type ClockAnchor, } from "./engine/clockMath";
14
+ export { startTokenWatch, getTokenReport } from "./tokens/tokenStore";
15
+ export type { TokenReport, TokenSummary, TokenCheck } from "./tokens/tokenStore";
16
+ export { jumpToTokenExpiry, failNextRequest, stopTokenCheck } from "./tokens/tokenActions";
17
+ export { inspectToken, fingerprint as tokenFingerprint } from "./tokens/decode";
18
+ export type { TokenInfo, TokenFormat } from "./tokens/decode";
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Pre-configured Clock tool for FloatingDevTools.
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * import { clockToolPreset } from '@buoy-gg/clock';
7
+ *
8
+ * <FloatingDevTools apps={[clockToolPreset]} />
9
+ * ```
10
+ */
11
+ import { ClockBody } from "./components/ClockBody";
12
+ export declare const clockToolPreset: {
13
+ id: string;
14
+ name: string;
15
+ description: string;
16
+ slot: "both";
17
+ color: string;
18
+ icon: ({ size, color }: {
19
+ size: number;
20
+ color?: string;
21
+ slot?: string;
22
+ }) => import("react").JSX.Element;
23
+ component: typeof ClockBody;
24
+ modal: {
25
+ subtitle: string;
26
+ };
27
+ };
28
+ /** Create a custom Clock tool configuration. */
29
+ export declare const createClockTool: (options?: (Record<never, never> & import("@buoy-gg/shared-ui").ToolPresetOverrides) | undefined) => {
30
+ id: string;
31
+ name: string;
32
+ description: string;
33
+ slot: "both";
34
+ color: string;
35
+ icon: ({ size, color }: {
36
+ size: number;
37
+ color?: string;
38
+ slot?: string;
39
+ }) => import("react").JSX.Element;
40
+ component: typeof ClockBody;
41
+ modal: {
42
+ subtitle: string;
43
+ };
44
+ };
45
+ //# sourceMappingURL=preset.d.ts.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * clockSyncAdapter: the clock override over external-sync.
3
+ *
4
+ * One adapter serves Buoy Desktop, the MCP server (`get_clock` /
5
+ * `clock_action`) and Ask Buoy's agent, so every surface gets the same
6
+ * actions with the same validation. Auto-discovery in @buoy-gg/core picks it
7
+ * up by export name and announces the "clock" capability.
8
+ *
9
+ * The snapshot carries both clocks as of the moment it was taken, plus the
10
+ * mode and rate, so a viewer can keep the display ticking without a stream
11
+ * of snapshots: virtual = virtualNow + (elapsed since receipt) × rate.
12
+ */
13
+ import { type ClockSnapshot } from "../engine/clockStore";
14
+ type Params = unknown;
15
+ export declare const clockSyncAdapter: import("@buoy-gg/shared-ui").SyncAdapter<ClockSnapshot, {
16
+ getState(): ClockSnapshot;
17
+ setTime(params: Params): ClockSnapshot;
18
+ shift(params: Params): ClockSnapshot;
19
+ freeze(params: Params): ClockSnapshot;
20
+ resume(): ClockSnapshot;
21
+ setRate(params: Params): ClockSnapshot;
22
+ reset(): ClockSnapshot;
23
+ updateSettings(params: Params): ClockSnapshot;
24
+ jumpToTokenExpiry(params: Params): ClockSnapshot;
25
+ failNextRequest(params: Params): Promise<ClockSnapshot>;
26
+ stopTokenCheck(): Promise<ClockSnapshot>;
27
+ }>;
28
+ export type ClockAction = keyof NonNullable<typeof clockSyncAdapter.actions>;
29
+ export {};
30
+ //# sourceMappingURL=clockSyncAdapter.d.ts.map
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Read what a token says about itself, without any secret.
3
+ *
4
+ * Signed tokens carry their claims in plain (base64url) text: a JWT's middle
5
+ * part and a public PASETO's message are readable by anyone holding the token.
6
+ * The secret only proves the signature, which this never checks. Encrypted
7
+ * tokens (JWE, PASETO `local`) and opaque strings say nothing, so for those
8
+ * the expiry has to come from the response that issued them (see extract.ts).
9
+ *
10
+ * Pure, dependency-free and engine-agnostic: no `atob`, `TextDecoder` or
11
+ * `Buffer`, because none is guaranteed on every Hermes version.
12
+ */
13
+ export type TokenFormat = "jwt" | "jwe" | "paseto-public" | "paseto-local" | "fernet" | "opaque";
14
+ export interface TokenClaims {
15
+ /** Issuer, e.g. "https://securetoken.google.com/my-app". */
16
+ iss?: string;
17
+ aud?: string;
18
+ sub?: string;
19
+ scope?: string;
20
+ /** Signing algorithm from the JWT header (RS256, ES256, none...). */
21
+ alg?: string;
22
+ /** Cognito's `token_use`, or the JWT header `typ`. */
23
+ use?: string;
24
+ }
25
+ export interface TokenInfo {
26
+ format: TokenFormat;
27
+ /** "JWT · RS256", "Encrypted JWT", "PASETO v4", "Opaque". */
28
+ formatLabel: string;
29
+ /** Who likely issued it: "Firebase ID token", "Cognito access token"... */
30
+ hint?: string;
31
+ claims: TokenClaims;
32
+ /** Epoch ms. */
33
+ issuedAt?: number;
34
+ notBefore?: number;
35
+ expiresAt?: number;
36
+ /** Where `expiresAt` came from, in words ("exp claim"). */
37
+ expirySource?: string;
38
+ /**
39
+ * The token carries no expiry by design (an API key). Distinct from "we
40
+ * could not read one", which is every opaque token.
41
+ */
42
+ neverExpires?: boolean;
43
+ }
44
+ export declare function base64UrlToBytes(input: string): number[] | null;
45
+ export declare function utf8Decode(bytes: number[]): string | null;
46
+ /**
47
+ * An absolute time in whatever form a server chose: epoch seconds, epoch ms,
48
+ * or an ISO 8601 string. Numbers below 1e11 are seconds (1e11 s is the year
49
+ * 5138; 1e11 ms is 1973).
50
+ */
51
+ export declare function absoluteTime(value: unknown, parse: (text: string) => number): number | undefined;
52
+ /** A relative lifetime in seconds ("expires_in": 3600, Firebase's "3600"). */
53
+ export declare function lifetimeSeconds(value: unknown): number | undefined;
54
+ /**
55
+ * Everything a token says about itself. `null` when the string is not a
56
+ * token at all (too short, has spaces).
57
+ */
58
+ export declare function inspectToken(raw: string, parse?: (text: string) => number): TokenInfo | null;
59
+ /** Readable structure (not just a random string). */
60
+ export declare function isStructured(info: TokenInfo | null): boolean;
61
+ /**
62
+ * A short, stable id for a token that is not the token: FNV-1a over the whole
63
+ * string. Rows and refresh checks compare these, so the raw credential is
64
+ * never stored, synced or shown.
65
+ */
66
+ export declare function fingerprint(token: string): string;
67
+ //# sourceMappingURL=decode.d.ts.map
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Find tokens in captured network traffic. Pure: takes the Network tool's
3
+ * event shape (structurally, so this package never imports @buoy-gg/network)
4
+ * and returns what it saw.
5
+ *
6
+ * Two directions:
7
+ *
8
+ * - Requests USE tokens: the Authorization header, token-named headers,
9
+ * cookies and query parameters. Each becomes a candidate for a row.
10
+ * - Responses ISSUE them: a login or refresh body (`access_token` with
11
+ * `expires_in`, Supabase's `expires_at`, Cognito's `AuthenticationResult`,
12
+ * Firebase's `expiresIn`, AWS's `Expiration`) and `Set-Cookie`. These give
13
+ * opaque tokens an expiry they cannot carry themselves, and name the
14
+ * endpoint that issued them.
15
+ *
16
+ * Raw token strings leave this module only as fingerprints.
17
+ */
18
+ import { type TokenInfo } from "./decode";
19
+ /** The fields of @buoy-gg/network's NetworkEvent this reads. */
20
+ export interface CapturedRequest {
21
+ id: string;
22
+ method: string;
23
+ url: string;
24
+ status?: number;
25
+ requestHeaders?: Record<string, string>;
26
+ responseHeaders?: Record<string, string>;
27
+ responseData?: unknown;
28
+ /** Request start, epoch ms, as the app's clock read it. */
29
+ timestamp: number;
30
+ duration?: number;
31
+ override?: {
32
+ ruleId: string;
33
+ };
34
+ }
35
+ export type TokenLocation = "authorization" | "header" | "cookie" | "query";
36
+ export interface TokenUse {
37
+ location: TokenLocation;
38
+ /** Header, cookie or parameter name. "Authorization" for that header. */
39
+ name: string;
40
+ /** "Bearer", "DPoP", "Token"... for the Authorization header. */
41
+ scheme?: string;
42
+ fp: string;
43
+ info: TokenInfo;
44
+ }
45
+ export interface Issuance {
46
+ fp: string;
47
+ info: TokenInfo;
48
+ /** "access", "id", "refresh", "session", "cookie", or the raw key. */
49
+ role: string;
50
+ /** Epoch ms on the app's clock, when known. */
51
+ expiresAt?: number;
52
+ expirySource?: string;
53
+ /**
54
+ * Set when the expiry was RELATIVE (`expires_in`, `Max-Age`): the app adds
55
+ * it to its own clock, the server to real time, so the two views differ
56
+ * once the app's clock is moved.
57
+ */
58
+ lifetimeMs?: number;
59
+ /** "POST /oauth/token". */
60
+ issuedBy: string;
61
+ /** The refresh token issued alongside, if any. */
62
+ refresh?: {
63
+ fp: string;
64
+ expiresAt?: number;
65
+ formatLabel: string;
66
+ };
67
+ /** Cookie name, for Set-Cookie issuances. */
68
+ cookie?: string;
69
+ }
70
+ export declare function originOf(url: string): string | null;
71
+ export declare function hostOf(url: string): string;
72
+ export declare function pathOf(url: string): string;
73
+ export declare function tokensUsed(event: CapturedRequest, parse: (text: string) => number): TokenUse[];
74
+ export declare function tokensIssued(event: CapturedRequest, parse: (text: string) => number): Issuance[];
75
+ //# sourceMappingURL=extract.d.ts.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The two ways to make an app refresh its token, and the check that watches
3
+ * whether it did. Apps refresh in one of two ways, so there are two actions:
4
+ *
5
+ * - Proactively, by comparing the token's expiry with `Date.now()` before a
6
+ * request or on a timer. `jumpToTokenExpiry` moves the app's clock to just
7
+ * before the expiry (running due timers on the way), so the app's own
8
+ * check fires. The server still accepts the old token, which is the point:
9
+ * only the app's logic is under test.
10
+ * - Reactively, after the server answers 401. Moving the app's clock cannot
11
+ * cause that, because the server keeps real time, so `failNextRequest`
12
+ * adds a one-shot Network override that answers the next request to the
13
+ * token's API with a 401.
14
+ */
15
+ import { type ClockSnapshot } from "../engine/clockStore";
16
+ /**
17
+ * Move the app's clock to `leadMs` before the token expires (default 10 s,
18
+ * so the expiry happens in front of you), and watch for a new token.
19
+ */
20
+ export declare function jumpToTokenExpiry(tokenId?: string, leadMs?: number): ClockSnapshot;
21
+ /**
22
+ * Answer the next request to the token's API with `status` (401 by default),
23
+ * once, and watch for a new token.
24
+ */
25
+ export declare function failNextRequest(tokenId?: string, status?: number, times?: number): Promise<ClockSnapshot>;
26
+ /** Stop watching, and withdraw a 401 that has not reached the app yet. */
27
+ export declare function stopTokenCheck(): Promise<ClockSnapshot>;
28
+ //# sourceMappingURL=tokenActions.d.ts.map