@glassly/engine 0.3.3-beta.639 → 0.3.3-beta.656

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 (50) hide show
  1. package/build/facades/glasses.d.ts +1 -0
  2. package/build/facades/glasses.d.ts.map +1 -1
  3. package/build/facades/glasses.js +1 -0
  4. package/build/facades/glasses.js.map +1 -1
  5. package/build/generated/releaseMetadata.js +3 -3
  6. package/build/generated/releaseMetadata.js.map +1 -1
  7. package/build/index.d.ts +1 -1
  8. package/build/index.d.ts.map +1 -1
  9. package/build/index.js +1 -1
  10. package/build/index.js.map +1 -1
  11. package/build/services/AiTierResolution.d.ts.map +1 -1
  12. package/build/services/AiTierResolution.js +11 -3
  13. package/build/services/AiTierResolution.js.map +1 -1
  14. package/build/services/GestureDotsService.d.ts.map +1 -1
  15. package/build/services/GestureDotsService.js +15 -6
  16. package/build/services/GestureDotsService.js.map +1 -1
  17. package/build/services/gestureDotGlyphs.d.ts +1 -1
  18. package/build/services/gestureDotGlyphs.d.ts.map +1 -1
  19. package/build/services/gestureDotGlyphs.js +1 -0
  20. package/build/services/gestureDotGlyphs.js.map +1 -1
  21. package/build/stores/glasses.d.ts.map +1 -1
  22. package/build/stores/glasses.js +1 -0
  23. package/build/stores/glasses.js.map +1 -1
  24. package/build/types/capabilities/even-realities-g2.js +1 -1
  25. package/build/types/capabilities/even-realities-g2.js.map +1 -1
  26. package/build/types/capabilities/mentra-display.js +1 -1
  27. package/build/types/capabilities/mentra-display.js.map +1 -1
  28. package/build/types/capabilities/rayneo-io.js +1 -1
  29. package/build/types/capabilities/rayneo-io.js.map +1 -1
  30. package/build/utils/timers/index.d.ts +1 -2
  31. package/build/utils/timers/index.d.ts.map +1 -1
  32. package/build/utils/timers/index.js +1 -2
  33. package/build/utils/timers/index.js.map +1 -1
  34. package/build/utils/timers/timers.d.ts +3 -5
  35. package/build/utils/timers/timers.d.ts.map +1 -1
  36. package/build/utils/timers/timers.js +78 -128
  37. package/build/utils/timers/timers.js.map +1 -1
  38. package/package.json +8 -8
  39. package/src/facades/glasses.ts +1 -0
  40. package/src/generated/releaseMetadata.ts +3 -3
  41. package/src/index.ts +1 -1
  42. package/src/services/AiTierResolution.ts +12 -3
  43. package/src/services/GestureDotsService.ts +16 -6
  44. package/src/services/gestureDotGlyphs.ts +2 -1
  45. package/src/stores/glasses.ts +1 -0
  46. package/src/types/capabilities/even-realities-g2.ts +1 -1
  47. package/src/types/capabilities/mentra-display.ts +1 -1
  48. package/src/types/capabilities/rayneo-io.ts +1 -1
  49. package/src/utils/timers/index.ts +1 -2
  50. package/src/utils/timers/timers.ts +82 -157
package/src/index.ts CHANGED
@@ -201,7 +201,7 @@ export {
201
201
  type DevManifest,
202
202
  } from "./utils/devMiniappLaunch"
203
203
  export {HardwareCompatibility, type CompatibilityResult} from "./utils/hardware"
204
- export {BgTimer, throttle, debounce} from "./utils/timers"
204
+ export {BgTimer} from "./utils/timers"
205
205
  // Meta Neural Band: the account sign-in a claim needs (the WebView lives in the host).
206
206
  export {
207
207
  MetaAuthError,
@@ -42,7 +42,7 @@ export const TIER_MODEL_OPTIONS: Record<AiTier, TierModelOption[]> = {
42
42
  ],
43
43
  frontier: [
44
44
  {provider: "anthropic", model: "claude-sonnet-5", label: "Claude Sonnet 5"},
45
- {provider: "gemini", model: "gemini-3.5-pro", label: "Gemini 3.5 Pro"},
45
+ {provider: "gemini", model: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro"},
46
46
  {provider: "openai", model: "gpt-5.1", label: "GPT-5.1"},
47
47
  ],
48
48
  }
@@ -60,6 +60,14 @@ export interface ResolvedTierModel {
60
60
  model?: string
61
61
  }
62
62
 
63
+ /**
64
+ * Stored dropdown picks for model ids the provider never served, mapped to the
65
+ * curated replacement. gemini-3.5-pro 404s on generateContent.
66
+ */
67
+ const RETIRED_MODELS: Record<string, string> = {
68
+ "gemini-3.5-pro": "gemini-3.1-pro-preview",
69
+ }
70
+
63
71
  /** Coerce an untrusted wire value into a tier; anything else is "balanced". */
64
72
  export function normalizeAiTier(value: unknown): AiTier {
65
73
  return value === "quick" || value === "frontier" ? value : "balanced"
@@ -71,8 +79,9 @@ export function parseTierSetting(value: unknown): {provider: AiTierProvider; mod
71
79
  const sep = value.indexOf(":")
72
80
  if (sep <= 0) return null
73
81
  const provider = value.slice(0, sep)
74
- const model = value.slice(sep + 1).trim()
75
- if (!model) return null
82
+ const stored = value.slice(sep + 1).trim()
83
+ if (!stored) return null
84
+ const model = RETIRED_MODELS[stored] ?? stored
76
85
  if (provider !== "anthropic" && provider !== "gemini" && provider !== "openai") return null
77
86
  return {provider, model}
78
87
  }
@@ -5,7 +5,8 @@
5
5
  * (cleared after a short pulse), a bullseye for a recognized long press.
6
6
  * Controllers that report press edges (touch_down … tap_end) show the press
7
7
  * itself instead: a dot while pressed, the bullseye once held, cleared at
8
- * release. The Neural Band's middle finger (index 1) draws the same shapes hollow. Extracted from
8
+ * release. The Neural Band's middle finger (index 1) draws the same shapes
9
+ * hollow: a ring for its tap, the bullseye without its center for its hold. Extracted from
9
10
  * the CFW dashboard service; it renders through
10
11
  * the ordinary scene pipeline, so it works on any positioning device (stock
11
12
  * G2 included), and is gated by the gesture_dots debug setting.
@@ -41,7 +42,16 @@ const FLASH_GLYPHS: Record<string, GestureDotGlyph> = {
41
42
  swipe_right: "right",
42
43
  }
43
44
  /** The band's middle finger draws the filled shapes hollow. */
44
- const MIDDLE_FINGER_GLYPHS: Partial<Record<GestureDotGlyph, GestureDotGlyph>> = {dot: "ring", double: "doubleRing"}
45
+ const MIDDLE_FINGER_GLYPHS: Partial<Record<GestureDotGlyph, GestureDotGlyph>> = {
46
+ dot: "ring",
47
+ double: "doubleRing",
48
+ hold: "holdRing",
49
+ }
50
+
51
+ /** `glyph` as `finger` draws it: the middle finger's shapes are hollow. */
52
+ function fingerGlyph(glyph: GestureDotGlyph, finger: number): GestureDotGlyph {
53
+ return finger === 1 ? (MIDDLE_FINGER_GLYPHS[glyph] ?? glyph) : glyph
54
+ }
45
55
 
46
56
  class GestureDotsService {
47
57
  private started = false
@@ -135,12 +145,12 @@ class GestureDotsService {
135
145
  if (press) {
136
146
  // Both reports describe one press: after touch_down, tap_start means it is held.
137
147
  if (gestureName === "touch_down") press.raw = true
138
- else if (press.raw) press.glyph = "hold"
148
+ else if (press.raw) press.glyph = fingerGlyph("hold", finger)
139
149
  this.render()
140
150
  return
141
151
  }
142
152
  this.clearFlash()
143
- this.presses.set(finger, {raw: gestureName === "touch_down", glyph: finger === 1 ? "ring" : "dot"})
153
+ this.presses.set(finger, {raw: gestureName === "touch_down", glyph: fingerGlyph("dot", finger)})
144
154
  this.render()
145
155
  return
146
156
  }
@@ -152,14 +162,14 @@ class GestureDotsService {
152
162
  return
153
163
  }
154
164
  const shape = FLASH_GLYPHS[gestureName]
155
- const glyph = shape && finger === 1 ? MIDDLE_FINGER_GLYPHS[shape] ?? shape : shape
165
+ const glyph = shape ? fingerGlyph(shape, finger) : shape
156
166
  // The press already showed its own tap and hold; the device's report of
157
167
  // them follows after its double-tap window, often after release.
158
168
  const shownByPress = gestureName === "single_tap" || gestureName === "long_press"
159
169
  if (press?.raw && (!glyph || shownByPress)) {
160
170
  // Pinch-and-turn reports do not release a finger. A recognized hold
161
171
  // stays visible until tap_end instead of expiring halfway through it.
162
- if (gestureName === "long_press") press.glyph = "hold"
172
+ if (gestureName === "long_press") press.glyph = fingerGlyph("hold", finger)
163
173
  this.render()
164
174
  return
165
175
  }
@@ -4,7 +4,7 @@
4
4
  */
5
5
  export const GESTURE_DOT_GLYPH_SIZE = 24
6
6
 
7
- export type GestureDotGlyph = "dot" | "double" | "ring" | "doubleRing" | "up" | "down" | "left" | "right" | "hold" | "blank"
7
+ export type GestureDotGlyph = "dot" | "double" | "ring" | "doubleRing" | "up" | "down" | "left" | "right" | "hold" | "holdRing" | "blank"
8
8
 
9
9
  export const GESTURE_DOT_GLYPHS: Record<GestureDotGlyph, string> = {
10
10
  dot: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAALklEQVR4nM2QsQ0AAAjC/P/pmrhqZ+xIF6DqBQyWbwO3gduAmKjQuj7QL/ETEzQmMptl7Q9NBAAAAABJRU5ErkJggg==",
@@ -16,5 +16,6 @@ export const GESTURE_DOT_GLYPHS: Record<GestureDotGlyph, string> = {
16
16
  left: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAI0lEQVR4nGNgoCv4j0sch8RAiTP8RwPESOAwajDI4JKgGQAAKO05x7DqJxIAAAAASUVORK5CYII=",
17
17
  right: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAIElEQVR4nGNgoDf4j1NiMMn8RwOEJYizZKDEcQc7bQAAS7w5x+ro2lsAAAAASUVORK5CYII=",
18
18
  hold: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAR0lEQVR4nGNgIAj+gwF2UUy5//+xyyDzcLGRedjMhTEwXAKl0Z0Lof7DKbgMhBpQCZzOxedBHEGCMxAJBTXhKGRAA9hFMQEANr7jHQU9fO0AAAAASUVORK5CYII=",
19
+ holdRing: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAQ0lEQVR4nGNgIAj+gwF2UUy5//+xyyDzcLGRedjMhTEwXAKlMd2IVQNMaEAlcDoXnwdxBAnOQCQU1ISjEIvjsYhiAgAo9KdZzePY3AAAAABJRU5ErkJggg==",
19
20
  blank: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAD0lEQVR4nGNgGAWjgPoAAAJYAAGgxYwDAAAAAElFTkSuQmCC",
20
21
  }
@@ -146,6 +146,7 @@ const initialState: GlassesStore = {
146
146
  sceneVector: false,
147
147
  sceneSettle: false,
148
148
  volumeGate: false,
149
+ firmwareUpdateAvailable: false,
149
150
  // wifi info
150
151
  wifi: {state: "disconnected"},
151
152
  wifiStatusKnown: false,
@@ -42,7 +42,7 @@ export const evenRealitiesG2: Capabilities = {
42
42
  // envelope). Pathological sizes needing more than the 4-container pool are
43
43
  // dropped SGC-side with a log.
44
44
  shapes: ["rect"], // bordered empty container ≈ rect
45
- intensityLevels: 2,
45
+ intensityLevels: 16,
46
46
  partialUpdate: true,
47
47
  },
48
48
 
@@ -39,7 +39,7 @@ export const mentraDisplay: Capabilities = {
39
39
  maxImageElements: 4, // firmware BITMAP pool ids 10-13
40
40
  maxImagePx: {width: 200, height: 200}, // CANVAS_IMAGE_MAX_I1_BYTES / NAV_IMAGE_MAX_W/H
41
41
  shapes: ["rect"], // bordered empty TEXTBOX ≈ rect (no shape primitive)
42
- intensityLevels: 2, // 1-bpp panel today
42
+ intensityLevels: 16,
43
43
  partialUpdate: true,
44
44
  },
45
45
 
@@ -39,7 +39,7 @@ export const rayneoIO: Capabilities = {
39
39
  canPosition: true,
40
40
  maxTextElements: 6,
41
41
  maxImageElements: 4,
42
- intensityLevels: 2,
42
+ intensityLevels: 16,
43
43
  partialUpdate: true,
44
44
  },
45
45
 
@@ -1,2 +1 @@
1
- import {BgTimer, throttle, debounce} from "./timers"
2
- export {BgTimer, throttle, debounce}
1
+ export {BgTimer} from "./timers"
@@ -1,203 +1,128 @@
1
- // BgTimer — background-safe timers for the engine.
1
+ // BgTimer: every engine timer (miniapp liveness watchdog, display expiries,
2
+ // boot windows, token refresh) goes through here. Android pauses plain JS
3
+ // timers while the app is backgrounded, so on a device these run on the
4
+ // native react-native-nitro-bg-timer instead. iOS needs that package at
5
+ // >= 0.2.0: earlier versions cleared each timer right after scheduling it
6
+ // (https://github.com/tconns/react-native-nitro-bg-timer/issues/2).
2
7
  //
3
- // Everything timing-critical in the engine runs through this class: the
4
- // miniapp liveness watchdog, display durationMs expiries, boot windows,
5
- // auth-token refresh. On Android, React Native PAUSES plain JS timers while
6
- // the app is backgrounded, so those features only keep working in the
7
- // background when the native implementation (react-native-nitro-bg-timer)
8
- // is active.
8
+ // Dev and release behave the same; there is no env switch. A missing native
9
+ // module falls back to JS timers so the foreground keeps working, and says so
10
+ // once, loudly: a quiet fallback already cost a day of phantom "zombie
11
+ // miniapp" bugs (OS-1790). Never downgrade that report to a warning.
9
12
  //
10
- // Who actually gets native timers:
11
- // - Android, dev AND release: always attempted — dev must match production
12
- // background behavior. There is no opt-in/opt-out env var. If the native
13
- // module is unavailable (a dev client whose binary predates it, or a
14
- // broken release binary), the failure is LOUD: a console.error with the
15
- // remediation, plus a blocking dev alert. The JS-timer fallback still
16
- // engages so foreground behavior keeps working, but backgrounded
17
- // behavior is broken until the native rebuild — the error says exactly
18
- // that. (A silent dev fallback previously cost a full debugging day of
19
- // phantom "zombie miniapp" bugs; see OS-1790.)
20
- // - iOS: never, dev or prod — the package is disabled pending
21
- // https://github.com/tconns/react-native-nitro-bg-timer/issues/2. iOS
22
- // suspends the whole process in background anyway, so background timing
23
- // there is handled by native per-device queues, not JS timers.
24
- //
25
- // react-native is loaded lazily, not imported. Every engine service runs its
26
- // timers through this module, and the engine's bun test suites load those
27
- // services in a plain JS runtime that cannot parse react-native's Flow
28
- // source. A static import would force every such suite to mock this module;
29
- // with the lazy load, a runtime without react-native simply gets the JS
30
- // timers, which is the correct answer there anyway.
13
+ // react-native and the nitro packages are require()d lazily. The engine's bun
14
+ // suites run services in a plain JS runtime that cannot parse react-native;
15
+ // there the native lookup finds no platform and JS timers are the right answer.
16
+
17
+ type TimerApi = {
18
+ setTimeout(callback: () => void, delay: number): number
19
+ clearTimeout(id: number): void
20
+ setInterval(callback: () => void, delay: number): number
21
+ clearInterval(id: number): void
22
+ }
31
23
 
32
24
  type ReactNativeSubset = {
33
25
  Platform: {OS: string}
34
- Alert?: {alert: (title: string, message?: string) => void}
26
+ Alert?: {alert(title: string, message?: string): void}
35
27
  }
36
28
 
37
- let reactNative: ReactNativeSubset | null | undefined
29
+ const TIMER_METHODS = ["setTimeout", "clearTimeout", "setInterval", "clearInterval"] as const
38
30
 
39
- function getReactNative(): ReactNativeSubset | null {
40
- if (reactNative !== undefined) {
41
- return reactNative
42
- }
43
- try {
44
- reactNative = require("react-native") as ReactNativeSubset
45
- } catch {
46
- reactNative = null
47
- }
48
- return reactNative
31
+ const jsTimers: TimerApi = {
32
+ setTimeout: (callback, delay) => setTimeout(callback, delay) as unknown as number,
33
+ clearTimeout: (id) => clearTimeout(id),
34
+ setInterval: (callback, delay) => setInterval(callback, delay) as unknown as number,
35
+ clearInterval: (id) => clearInterval(id),
49
36
  }
50
37
 
51
- function isDev(): boolean {
52
- return typeof __DEV__ !== "undefined" && __DEV__
53
- }
38
+ let reactNative: ReactNativeSubset | null | undefined
39
+ // undefined = not looked up yet, null = unavailable (JS timers from here on).
40
+ let nativeTimers: TimerApi | null | undefined
41
+ let reported = false
54
42
 
55
- type NitroTimerApi = {
56
- setInterval: (callback: () => void, delay: number) => number
57
- clearInterval: (intervalId: number) => void
58
- setTimeout: (callback: () => void, delay: number) => number
59
- clearTimeout: (timeoutId: number) => void
43
+ function loadReactNative(): ReactNativeSubset | null {
44
+ if (reactNative === undefined) {
45
+ try {
46
+ reactNative = require("react-native") as ReactNativeSubset
47
+ } catch {
48
+ reactNative = null
49
+ }
50
+ }
51
+ return reactNative
60
52
  }
61
53
 
62
- let nitroTimer: NitroTimerApi | null | undefined
63
- let nitroDisabled = false
64
- let failedLoud = false
65
-
66
- /**
67
- * The native background timer is unavailable — say so ONCE, loudly, with the
68
- * remediation. The JS-timer fallback keeps foreground behavior working, but
69
- * everything timing-driven freezes while the app is backgrounded, which
70
- * presents as phantom miniapp bugs (frozen captions, missed wake words,
71
- * watchdog kills on resume). A quiet warning here has already cost a full
72
- * debugging day; never downgrade this to console.warn.
73
- */
74
- function failUnavailable(reason: string) {
75
- if (failedLoud) {
76
- return
77
- }
78
- failedLoud = true
54
+ function reportUnavailable(reason: string): void {
55
+ if (reported) return
56
+ reported = true
57
+ const rn = loadReactNative()
58
+ const platform = rn?.Platform.OS === "ios" ? "ios" : "android"
79
59
  const message =
80
60
  `BgTimer: native background timer unavailable (${reason}). ` +
81
- "Backgrounded behavior IS BROKEN until you rebuild the Android app: run `bun android`. " +
61
+ `Backgrounded behavior IS BROKEN until you rebuild the app: run \`bun ${platform}\`. ` +
82
62
  "Engine timers and local miniapps (captions, wake words) freeze whenever the app is not foregrounded."
83
63
  console.error(message)
84
- if (isDev()) {
85
- // A stale dev client may ALSO show the library's own require-time red box
86
- // (bridgeless LogBox surfaces it even though we catch below) — this alert
87
- // is the actionable one: it names the fix.
64
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
65
+ // A stale dev client may also red-box inside the library itself; this
66
+ // alert is the one that names the fix.
88
67
  try {
89
- getReactNative()?.Alert?.alert("Background timers unavailable", message)
68
+ rn?.Alert?.alert("Background timers unavailable", message)
90
69
  } catch {
91
- /* Alert not available (tests / headless) — the console.error stands. */
70
+ // Headless runtime: the console.error stands.
92
71
  }
93
72
  }
94
73
  }
95
74
 
96
- function getNitroTimer(): NitroTimerApi | null {
97
- if (nitroDisabled || getReactNative()?.Platform.OS !== "android") {
75
+ function loadNativeTimers(): TimerApi | null {
76
+ if (nativeTimers !== undefined) return nativeTimers
77
+ const platform = loadReactNative()?.Platform.OS
78
+ if (platform !== "android" && platform !== "ios") {
79
+ nativeTimers = null
98
80
  return null
99
81
  }
100
-
101
- if (nitroTimer !== undefined) {
102
- return nitroTimer
103
- }
104
-
105
82
  try {
106
83
  const {isRuntimeAlive} = require("react-native-nitro-modules") as {isRuntimeAlive?: () => boolean}
107
- if (typeof isRuntimeAlive === "function" && !isRuntimeAlive()) {
108
- nitroTimer = null
109
- failUnavailable("nitro runtime is not alive")
110
- return nitroTimer
84
+ if (typeof isRuntimeAlive === "function" && !isRuntimeAlive()) throw new Error("nitro runtime is not alive")
85
+ const {BackgroundTimer} = require("react-native-nitro-bg-timer") as {BackgroundTimer?: Partial<TimerApi>}
86
+ for (const method of TIMER_METHODS) {
87
+ if (typeof BackgroundTimer?.[method] !== "function") throw new Error(`BackgroundTimer.${method} missing`)
111
88
  }
112
-
113
- const {BackgroundTimer} = require("react-native-nitro-bg-timer") as {BackgroundTimer?: NitroTimerApi}
114
- nitroTimer =
115
- BackgroundTimer &&
116
- typeof BackgroundTimer.setTimeout === "function" &&
117
- typeof BackgroundTimer.setInterval === "function" &&
118
- typeof BackgroundTimer.clearTimeout === "function" &&
119
- typeof BackgroundTimer.clearInterval === "function"
120
- ? BackgroundTimer
121
- : null
122
- } catch {
123
- nitroTimer = null
124
- nitroDisabled = true
125
- }
126
-
127
- if (!nitroTimer) {
128
- failUnavailable("react-native-nitro-bg-timer failed to load")
89
+ nativeTimers = BackgroundTimer as TimerApi
90
+ } catch (error) {
91
+ nativeTimers = null
92
+ reportUnavailable(error instanceof Error ? error.message : String(error))
129
93
  }
130
- return nitroTimer
94
+ return nativeTimers
131
95
  }
132
96
 
133
- function withNitro<T>(run: (api: NitroTimerApi) => T, fallback: () => T): T {
134
- const api = getNitroTimer()
135
- if (!api) {
136
- return fallback()
137
- }
97
+ // A native call that throws mid-session is new information: drop to JS timers
98
+ // and announce again.
99
+ function call<T>(run: (timers: TimerApi) => T): T {
100
+ const native = loadNativeTimers()
101
+ if (!native) return run(jsTimers)
138
102
  try {
139
- return run(api)
103
+ return run(native)
140
104
  } catch (error) {
141
- nitroDisabled = true
142
- nitroTimer = null
143
- failedLoud = false // a mid-session failure is new information — re-announce
144
- failUnavailable(`nitro timer call threw: ${String(error)}`)
145
- return fallback()
105
+ nativeTimers = null
106
+ reported = false
107
+ reportUnavailable(`native timer call threw: ${String(error)}`)
108
+ return run(jsTimers)
146
109
  }
147
110
  }
148
111
 
149
112
  export class BgTimer {
150
- static setInterval(callback: () => void, delay: number): number {
151
- return withNitro(
152
- (api) => api.setInterval(callback, delay),
153
- () => setInterval(callback, delay) as unknown as number,
154
- )
155
- }
156
-
157
- static clearInterval(intervalId: number): void {
158
- withNitro(
159
- (api) => api.clearInterval(intervalId),
160
- () => clearInterval(intervalId),
161
- )
162
- }
163
-
164
113
  static setTimeout(callback: () => void, delay: number): number {
165
- return withNitro(
166
- (api) => api.setTimeout(callback, delay),
167
- () => setTimeout(callback, delay) as unknown as number,
168
- )
114
+ return call((timers) => timers.setTimeout(callback, delay))
169
115
  }
170
116
 
171
- static clearTimeout(timeoutId: number): void {
172
- withNitro(
173
- (api) => api.clearTimeout(timeoutId),
174
- () => clearTimeout(timeoutId),
175
- )
117
+ static clearTimeout(id: number): void {
118
+ call((timers) => timers.clearTimeout(id))
176
119
  }
177
- }
178
120
 
179
- export function throttle<T extends (...args: any[]) => any>(fn: T, ms: number): (...args: Parameters<T>) => void {
180
- let lastCalled = 0
181
-
182
- return (...args: Parameters<T>) => {
183
- const now = Date.now()
184
- if (now - lastCalled >= ms) {
185
- lastCalled = now
186
- fn(...args)
187
- }
121
+ static setInterval(callback: () => void, delay: number): number {
122
+ return call((timers) => timers.setInterval(callback, delay))
188
123
  }
189
- }
190
124
 
191
- export function debounce<T extends (...args: any[]) => any>(fn: T, ms: number): (...args: Parameters<T>) => void {
192
- let timeoutId: number | null = null
193
-
194
- return (...args: Parameters<T>) => {
195
- if (timeoutId) {
196
- BgTimer.clearTimeout(timeoutId)
197
- }
198
- timeoutId = BgTimer.setTimeout(() => {
199
- fn(...args)
200
- timeoutId = null
201
- }, ms)
125
+ static clearInterval(id: number): void {
126
+ call((timers) => timers.clearInterval(id))
202
127
  }
203
128
  }