@prtcl/plonk 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -38,7 +38,8 @@ __export(index_exports, {
38
38
  clamp: () => clamp,
39
39
  expo: () => expo,
40
40
  ms: () => ms,
41
- now: () => now
41
+ now: () => now,
42
+ wait: () => wait
42
43
  });
43
44
  module.exports = __toCommonJS(index_exports);
44
45
 
@@ -635,6 +636,11 @@ var Frames = class extends Metro {
635
636
  function expo(n) {
636
637
  return Math.pow(clamp(n, 0, 1), Math.E);
637
638
  }
639
+
640
+ // src/wait.ts
641
+ function wait(time) {
642
+ return new Promise((resolve) => setTimeout(resolve, time));
643
+ }
638
644
  // Annotate the CommonJS export names for ESM import in node:
639
645
  0 && (module.exports = {
640
646
  Drunk,
@@ -653,6 +659,7 @@ function expo(n) {
653
659
  clamp,
654
660
  expo,
655
661
  ms,
656
- now
662
+ now,
663
+ wait
657
664
  });
658
665
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/clamp.ts","../src/Rand.ts","../src/Drunk.ts","../src/Scale.ts","../src/now.ts","../src/Env.ts","../src/Sine.ts","../src/constants.ts","../src/Metro.ts","../src/ms.ts","../src/Frames.ts","../src/expo.ts"],"sourcesContent":["export { Drunk, type DrunkOptions, type DrunkState } from './Drunk';\nexport { Env, type EnvOptions, type EnvState } from './Env';\nexport { Rand, type RandOptions, type RandState } from './Rand';\nexport { Scale, type ScaleOptions, type ScaleRange, type ScaleState } from './Scale';\nexport { Sine, type SineOptions, type SineState, SINE_PERIOD } from './Sine';\nexport { Frames, type FramesOptions } from './Frames';\nexport { Metro, type MetroOptions, type TimerCallback } from './Metro';\nexport { clamp } from './clamp';\nexport { expo } from './expo';\nexport { ms, type FPS, TimeFormat } from './ms';\nexport { now } from './now';\nexport * from './constants';\n","export function clamp(n: number): number;\nexport function clamp(n: number, max: number): number;\nexport function clamp(n: number, min: number, max: number): number;\n\n/**\n * Constrains an input value to a min...max range.\n * @param n - The value to constrain.\n * @param min - Lower bound (defaults to 0).\n * @param max - Upper bound (defaults to 1).\n * @returns The clamped value.\n */\nexport function clamp(n: number, min?: number, max?: number) {\n let a = 0;\n let b = 1;\n\n if (typeof min === 'number') {\n if (typeof max === 'number') {\n a = min;\n b = max;\n } else {\n a = 0;\n b = min;\n }\n }\n\n return Math.min(Math.max(n, a), b);\n}\n","import { clamp } from './clamp';\n\n/** Options for configuring a Rand generator. */\nexport type RandOptions = {\n /** Lower bound of the range. Defaults to 0. */\n min?: number;\n /** Upper bound of the range. Defaults to 1. */\n max?: number;\n};\n\n/** Snapshot of a Rand generator's internal state. */\nexport type RandState = {\n min: number;\n max: number;\n value: number;\n};\n\nexport const parseOptions = (opts?: RandOptions): Required<RandOptions> => {\n return {\n min: 0,\n max: 1,\n ...opts,\n };\n};\n\n/**\n * Random number generator that produces values within a bounded range.\n * @param opts - {@link RandOptions} for configuring the range.\n */\nexport class Rand {\n state: RandState;\n\n static rand(opts?: RandOptions) {\n return new Rand(opts).value();\n }\n\n constructor(opts?: RandOptions) {\n const { min, max } = parseOptions(opts);\n\n this.state = { max, min, value: 0 };\n this.next();\n }\n\n setRange(partialOpts: RandOptions) {\n const { value = 0 } = this.state;\n const { min, max } = {\n ...this.state,\n ...partialOpts,\n };\n\n this.state = {\n ...this.state,\n max,\n min,\n value: clamp(value, min, max),\n };\n }\n\n value() {\n return this.state.value;\n }\n\n next() {\n const { min, max } = this.state;\n const updates = Math.random() * (max - min) + min;\n\n this.state.value = updates;\n\n return updates;\n }\n}\n","import { Rand } from './Rand';\nimport { clamp } from './clamp';\n\nexport const DEFAULT_DRUNK_STEP = 0.1;\n\n/** Options for configuring a Drunk random walk. */\nexport type DrunkOptions = {\n /** Upper bound of the range. Defaults to 1. */\n max?: number;\n /** Lower bound of the range. Defaults to 0. */\n min?: number;\n /** Initial value. If omitted, a random value within the range is used. */\n startsAt?: number;\n /** Maximum step size as a fraction of the range (0-1). Defaults to 0.1. */\n step?: number;\n};\n\n/** Snapshot of a Drunk walk's internal state. */\nexport type DrunkState = {\n initialValue: number;\n max: number;\n min: number;\n step: number;\n value: number;\n};\n\nexport const parseStepSize = (step?: number): number =>\n typeof step !== 'undefined' ? clamp(step, 0, 1) : DEFAULT_DRUNK_STEP;\n\nexport const parseOptions = (opts?: DrunkOptions): Required<DrunkOptions> => {\n const { step, ...restOpts } = opts || {};\n const parsedStepSize = parseStepSize(step);\n\n return {\n max: 1,\n min: 0,\n startsAt: 0,\n step: parsedStepSize,\n ...restOpts,\n };\n};\n\n/**\n * Stochastic random walk generator that produces values within a bounded range.\n * @param opts - {@link DrunkOptions} for configuring the walk.\n */\nexport class Drunk {\n state: DrunkState;\n protected _initialValue: Rand;\n protected _step: Rand;\n\n constructor(opts?: DrunkOptions) {\n const { min, max, step, startsAt } = parseOptions(opts);\n\n this._initialValue = new Rand({ min, max });\n this._step = new Rand({ min: -1, max: 1 });\n\n const initialValue =\n typeof opts?.startsAt !== 'undefined' ? startsAt : this._initialValue.value();\n\n this.state = {\n initialValue,\n max,\n min,\n step,\n value: initialValue,\n };\n }\n\n setRange(partialOpts?: Pick<DrunkOptions, 'min' | 'max'>) {\n const { max, min } = {\n min: this.state.min,\n max: this.state.max,\n ...partialOpts,\n };\n\n this._initialValue.setRange({ min, max });\n this.state = {\n ...this.state,\n ...(min !== this.state.min || max !== this.state.max\n ? {\n initialValue: clamp(this._initialValue.value(), min, max),\n max,\n min,\n value: clamp(this.state.value, min, max),\n }\n : {\n max,\n min,\n }),\n };\n }\n\n setStepSize(partialOpts?: Pick<DrunkOptions, 'step'>) {\n const step = parseStepSize(partialOpts?.step);\n\n this.state = {\n ...this.state,\n step,\n };\n }\n\n reset(opts?: DrunkOptions) {\n const { min, max, startsAt, step } = parseOptions(opts);\n\n this.setRange({ min, max });\n this.setStepSize({ step });\n\n const initialValue =\n typeof opts?.startsAt !== 'undefined' ? startsAt : this._initialValue.next();\n\n this.state = {\n ...this.state,\n initialValue,\n value: initialValue,\n };\n }\n\n value() {\n return this.state.value;\n }\n\n next() {\n const { min, max, step, value } = this.state;\n const updates = clamp(value + max * this._step.next() * step, min, max);\n\n this.state.value = updates;\n\n return updates;\n }\n}\n","import { clamp } from './clamp';\n\n/** A numeric range with min and max bounds. */\nexport type ScaleRange = {\n min?: number;\n max: number;\n};\n\n/** Options for configuring a Scale mapper. */\nexport type ScaleOptions = {\n /** Input range. Defaults to { min: 0, max: 1 }. */\n from?: ScaleRange;\n /** Output range. Defaults to { min: 0, max: 1 }. */\n to?: ScaleRange;\n};\n\n/** Snapshot of a Scale mapper's internal state. */\nexport type ScaleState = {\n /** Input range. */\n from: Required<ScaleRange>;\n /** Precomputed (to.max - to.min) / (from.max - from.min), updated when ranges change. */\n ratio: number;\n /** Output range. */\n to: Required<ScaleRange>;\n /** Last scaled value. */\n value: number;\n};\n\n/** Precompute the scale factor so the hot path avoids a division per call. */\nconst computeRatio = (from: Required<ScaleRange>, to: Required<ScaleRange>) =>\n (to.max - to.min) / (from.max - from.min);\n\n/** Build initial state from options, applying defaults and computing the ratio. */\nexport const parseInitialState = (opts?: ScaleOptions): ScaleState => {\n const initialRange: Pick<ScaleState, 'from' | 'to'> = {\n from: {\n min: 0,\n max: 1,\n ...opts?.from,\n },\n to: {\n min: 0,\n max: 1,\n ...opts?.to,\n },\n };\n\n return {\n ...initialRange,\n ratio: computeRatio(initialRange.from, initialRange.to),\n value: initialRange.to.min,\n };\n};\n\n/** Merge partial range updates into existing state, recomputing the ratio. */\nexport const updateStateFromOptions = (opts: ScaleOptions, prevState: ScaleState): ScaleState => {\n const { from, to } = opts;\n const updatedFrom: Required<ScaleRange> = {\n ...prevState.from,\n ...from,\n };\n const updatedTo: Required<ScaleRange> = {\n ...prevState.to,\n ...to,\n };\n\n return {\n ...prevState,\n from: updatedFrom,\n ratio: computeRatio(updatedFrom, updatedTo),\n to: updatedTo,\n value: clamp(prevState.value, updatedTo.min, updatedTo.max),\n };\n};\n\n/**\n * Linear map of values from one range to another, supports negative values and inversion.\n * @param opts - {@link ScaleOptions} for configuring input and output ranges.\n */\nexport class Scale {\n state: ScaleState;\n\n static scale(n: number, opts?: ScaleOptions) {\n return new Scale(opts).scale(n);\n }\n\n constructor(opts?: ScaleOptions) {\n this.state = parseInitialState(opts);\n }\n\n setRanges(opts: ScaleOptions) {\n this.state = updateStateFromOptions(opts, this.state);\n }\n\n reset(opts: ScaleOptions) {\n this.state = parseInitialState(opts);\n }\n\n value() {\n return this.state.value;\n }\n\n scale(n: number) {\n const { from, to, ratio } = this.state;\n const updates = to.min + (clamp(n, from.min, from.max) - from.min) * ratio;\n\n this.state.value = updates;\n\n return updates;\n }\n}\n","type InnerNow = () => number;\n\n/** Resolve the best available clock once at module load. */\nconst innerNow = ((): InnerNow => {\n // Browser or modern Node (>= 16)\n if (typeof performance !== 'undefined' && 'now' in performance) {\n return () => performance.now();\n }\n\n // Older Node — use process.hrtime, offset from first call\n if (typeof process === 'object' && process.toString() === '[object process]') {\n const ts = () => {\n const hr = process.hrtime();\n return hr[0] * 1e9 + hr[1];\n };\n const initialNow = ts();\n return () => (ts() - initialNow) / 1e6;\n }\n\n // Fallback — Date.now with manual offset\n const initialNow = Date.now();\n return () => Date.now() - initialNow;\n})();\n\n/**\n * Cross-environment high-resolution timestamp (performance.now polyfill).\n * @returns Elapsed milliseconds since initialization.\n */\nexport function now(): number {\n return innerNow();\n}\n","import { Scale } from './Scale';\nimport { now } from './now';\n\n/** Snapshot of an Env's internal state. */\nexport type EnvState = {\n duration: number;\n from: number;\n prev: number;\n to: number;\n totalElapsed: number;\n value: number;\n};\n\n/** Options for configuring an Env envelope. */\nexport type EnvOptions = {\n /** Duration of the envelope in milliseconds. */\n duration: number;\n /** Starting value. Defaults to 0. */\n from?: number;\n /** Ending value. Defaults to 1. */\n to?: number;\n};\n\nexport const parseOptions = (opts?: EnvOptions): Required<EnvOptions> => {\n return {\n duration: 0,\n from: 0,\n to: 1,\n ...opts,\n };\n};\n\nconst getInitialState = ({ from, to, duration }: Required<EnvOptions>): EnvState => {\n return {\n duration,\n from,\n prev: now(),\n to,\n totalElapsed: 0,\n value: from,\n };\n};\n\nexport const updateStateFromOptions = (\n opts: EnvOptions | undefined,\n prevState: EnvState\n): EnvState => {\n const { from, to, duration } = {\n ...prevState,\n ...opts,\n };\n\n return {\n ...prevState,\n duration,\n from,\n to,\n totalElapsed: 0,\n };\n};\n\n/**\n * Linear envelope which interpolates between two values over a duration. Useful for audio envelopes, transitions, and animations.\n * @param opts - {@link EnvOptions} for configuring the envelope.\n */\nexport class Env {\n state: EnvState;\n protected _interpolator: Scale;\n\n constructor(opts: EnvOptions) {\n const { from, to, duration } = parseOptions(opts);\n\n this.state = getInitialState({ from, to, duration });\n this._interpolator = new Scale({\n from: {\n min: 0,\n max: duration,\n },\n to: {\n min: from,\n max: to,\n },\n });\n }\n\n setDuration(duration: number) {\n const { to, totalElapsed } = this.state;\n\n this.state = {\n ...this.state,\n ...(duration <= totalElapsed\n ? {\n duration,\n value: to,\n }\n : { duration }),\n };\n }\n\n reset(opts?: EnvOptions) {\n const updates = updateStateFromOptions(opts, this.state);\n\n this.state = {\n ...updates,\n prev: now(),\n value: updates.from,\n };\n this._interpolator.setRanges({\n from: {\n min: 0,\n max: updates.duration,\n },\n to: {\n min: updates.from,\n max: updates.to,\n },\n });\n }\n\n done() {\n return this.state.duration <= this.state.totalElapsed;\n }\n\n value() {\n const { to, value } = this.state;\n\n if (this.done()) {\n return to;\n }\n\n return value;\n }\n\n next() {\n if (this.done()) {\n return this.value();\n }\n\n const { prev, totalElapsed: prevTotalElapsed } = this.state;\n\n const curr = now();\n const tickInterval = curr - prev;\n const totalElapsed = prevTotalElapsed + tickInterval;\n const updates = this._interpolator.scale(totalElapsed);\n\n this.state.prev = curr;\n this.state.totalElapsed = totalElapsed;\n this.state.value = updates;\n\n return updates;\n }\n}\n","import { Scale } from './Scale';\nimport { clamp } from './clamp';\nimport { now } from './now';\n\nexport const SINE_PERIOD = Math.PI * 2 - 0.0001;\n\n/** Options for configuring a Sine oscillator. */\nexport type SineOptions = {\n /** Duration of one full cycle in milliseconds. */\n duration: number;\n};\n\n/** Snapshot of a Sine oscillator's internal state. */\nexport type SineState = {\n cycle: number;\n duration: number;\n prev: number;\n totalElapsed: number;\n value: number;\n};\n\nconst getInitialState = (duration: number): SineState => ({\n cycle: 0,\n duration,\n prev: now(),\n totalElapsed: 0,\n value: 0,\n});\n\n/**\n * Time-based sine wave oscillator that outputs values between -1 and 1.\n * @param opts - {@link SineOptions} for configuring the oscillator.\n */\nexport class Sine {\n state: SineState;\n protected _interpolator: Scale;\n\n constructor(opts: SineOptions) {\n const { duration } = opts;\n\n this._interpolator = new Scale({\n from: {\n min: 0,\n max: duration,\n },\n to: {\n min: 0,\n max: SINE_PERIOD,\n },\n });\n this.state = getInitialState(duration);\n }\n\n setDuration(duration: number) {\n this.state = {\n ...this.state,\n duration,\n };\n }\n\n reset(opts?: SineOptions) {\n const { duration } = {\n ...this.state,\n ...opts,\n };\n\n this.state = getInitialState(duration);\n }\n\n value() {\n return this.state.value;\n }\n\n next() {\n const { cycle, duration, prev, totalElapsed: prevTotalElapsed } = this.state;\n const curr = now();\n const tickInterval = curr - prev;\n const totalElapsed = prevTotalElapsed + tickInterval;\n\n const updates = clamp(Math.sin(this._interpolator.scale(totalElapsed)), -1, 1);\n\n if (cycle >= duration) {\n this.state.cycle = 0;\n } else {\n this.state.cycle = cycle + tickInterval;\n }\n\n this.state.prev = curr;\n this.state.totalElapsed = totalElapsed;\n this.state.value = updates;\n\n return updates;\n }\n}\n","export const SIXTY_FPS = 1000 / 60;\nexport const MS_IN_SECOND = 1000;\nexport const MS_IN_MINUTE = 60 * MS_IN_SECOND;\nexport const MS_IN_HOUR = MS_IN_MINUTE * 60;\n","import { SIXTY_FPS } from './constants';\nimport { now } from './now';\n\n/** Snapshot of a running timer's internal state. */\nexport type TimerState = {\n initialTime: number;\n isRunning: boolean;\n iterations: number;\n prev: number;\n tickInterval: number;\n time: number;\n totalElapsed: number;\n};\n\nexport const getInitialState = (initialTime: number): TimerState => {\n return {\n initialTime,\n isRunning: false,\n iterations: -1,\n prev: 0,\n tickInterval: 0,\n time: initialTime,\n totalElapsed: 0,\n };\n};\n\nexport const processTimerState = (state: TimerState): TimerState | null => {\n const { time, prev, totalElapsed, iterations } = state;\n const curr = now();\n\n if (iterations === -1) {\n return {\n ...state,\n prev: curr,\n iterations: 0,\n };\n }\n\n const tickInterval = curr - prev;\n\n if (tickInterval <= time) {\n return null;\n }\n\n return {\n ...state,\n iterations: iterations + 1,\n prev: curr,\n tickInterval,\n totalElapsed: totalElapsed + tickInterval,\n };\n};\n\n/** Options for configuring a Metro timer. */\nexport type MetroOptions = {\n /** Interval between ticks in milliseconds. Defaults to ~16.67ms (60fps). */\n time?: number;\n};\n\nexport const parseOptions = (opts?: MetroOptions): Required<MetroOptions> => {\n return {\n time: SIXTY_FPS,\n ...opts,\n };\n};\n\n/** Callback invoked on each timer tick with the timer instance. */\nexport type TimerCallback<T extends Metro> = (timer: T) => void;\n\n/**\n * High-resolution recursive timer with variable interval, provides runtime metrics via callback for time-based calculations.\n * @param callback - {@link TimerCallback} called on each tick with the timer instance.\n * @param opts - {@link MetroOptions} for configuring the timer interval.\n */\nexport class Metro {\n state: TimerState;\n protected _listeners: TimerCallback<Metro>[];\n declare protected _timerId: ReturnType<typeof setTimeout> | number;\n\n constructor(callback: TimerCallback<Metro>, opts?: MetroOptions) {\n const { time } = parseOptions(opts);\n this.state = getInitialState(time);\n this._listeners = [callback];\n }\n\n protected asyncHandler(callback: () => void) {\n this._timerId = setTimeout(callback, SIXTY_FPS);\n }\n\n protected clearAsyncHandler() {\n clearTimeout(this._timerId);\n }\n\n stop = () => {\n const { totalElapsed } = this.state;\n this.reset();\n this.clearAsyncHandler();\n\n return totalElapsed;\n };\n\n reset = () => {\n const { initialTime } = this.state;\n this.state = getInitialState(initialTime);\n };\n\n setTime = (updatedTime = this.state.time) => {\n const time = Math.max(updatedTime, 0);\n\n this.state = {\n ...this.state,\n time,\n initialTime: time,\n };\n };\n\n run = () => {\n if (this.state.isRunning) {\n this.stop();\n }\n\n this.state = {\n ...this.state,\n isRunning: true,\n prev: now(),\n };\n\n const tick = () => {\n const updates = processTimerState(this.state);\n\n if (updates) {\n this.state = updates;\n this._listeners.forEach((listener) => {\n listener(this);\n });\n }\n\n if (this.state.isRunning) {\n this.asyncHandler(tick);\n }\n };\n\n tick();\n };\n}\n","import { MS_IN_SECOND, MS_IN_MINUTE, MS_IN_HOUR } from './constants';\n\nexport type FPS = 15 | 30 | 60;\n\nexport enum TimeFormat {\n FPS = 'fps',\n HOURS = 'h',\n HZ = 'hz',\n MILLISECONDS = 'ms',\n MINUTES = 'm',\n SECONDS = 's',\n}\n\nexport type AvailableTimeFormats = `${TimeFormat}`;\n\nexport const FORMAT_IDENTIFIERS: AvailableTimeFormats[] = [\n TimeFormat.FPS,\n TimeFormat.HOURS,\n TimeFormat.HZ,\n TimeFormat.MILLISECONDS,\n TimeFormat.MINUTES,\n TimeFormat.SECONDS,\n]\n // Desc length sort so that `ms` matches prior to `m`\n .sort((a, b) => b.length - a.length);\n\ntype FormatGetter = (val: number) => number;\n\nconst FORMATTERS = new Map<AvailableTimeFormats, FormatGetter>([\n [TimeFormat.FPS, (val: number) => MS_IN_SECOND / val],\n [TimeFormat.HOURS, (val: number) => val * MS_IN_HOUR],\n [TimeFormat.HZ, (val: number) => (1 / val) * MS_IN_SECOND],\n [TimeFormat.MILLISECONDS, (val: number) => val],\n [TimeFormat.MINUTES, (val: number) => val * MS_IN_MINUTE],\n [TimeFormat.SECONDS, (val: number) => val * MS_IN_SECOND],\n]);\n\nconst sanitizeStringVal = (val: string) => val.toLocaleLowerCase().trim();\n\nconst parseStringValAndFormat = (val: string) => {\n for (let i = 0; i < FORMAT_IDENTIFIERS.length; i += 1) {\n const format = FORMAT_IDENTIFIERS[i];\n\n if (val.includes(format)) {\n const value = Number(val.replace(' ', '').replace(format, ''));\n\n return {\n format,\n value,\n };\n }\n }\n\n return {\n format: undefined,\n value: undefined,\n };\n};\n\nexport function ms(val: string | null | undefined): number | undefined;\nexport function ms(\n val: string | number | null | undefined,\n format?: AvailableTimeFormats | TimeFormat\n): number | undefined;\n\n/**\n * Converts time format strings or numeric values to their corresponding value in milliseconds.\n * @param val - A string like `'60fps'`, `'2s'`, or a numeric value.\n * @param format - Explicit time format when `val` is a number.\n * @returns Milliseconds, or undefined if the input is invalid.\n */\nexport function ms(\n val: string | number | null | undefined,\n format?: AvailableTimeFormats | TimeFormat\n): number | undefined {\n let parsedValue: number | null | undefined = null;\n let parsedFormat: AvailableTimeFormats = format || TimeFormat.MILLISECONDS;\n\n if (typeof val === 'string') {\n const parsed = parseStringValAndFormat(sanitizeStringVal(val));\n\n if (typeof parsed.value !== 'undefined') {\n parsedValue = parsed.value;\n }\n\n if (parsed.format) {\n parsedFormat = parsed.format;\n }\n } else {\n parsedValue = val;\n }\n\n if (typeof parsedValue === 'undefined' || parsedValue === null || Number.isNaN(parsedValue)) {\n return undefined;\n }\n\n const formatter = FORMATTERS.get(parsedFormat);\n\n if (!formatter) {\n return undefined;\n }\n\n return formatter(parsedValue);\n}\n","import { Metro, type TimerCallback, type MetroOptions } from './Metro';\nimport { ms, TimeFormat, type FPS } from './ms';\n\n/** Options for configuring a Frames timer. */\nexport type FramesOptions = {\n /** Target frames per second (15, 30, or 60). Defaults to 60. */\n fps: FPS;\n};\n\nexport const DEFAULT_FPS: FPS = 60;\n\nexport const parseOptions = (opts?: FramesOptions): MetroOptions => {\n const { fps } = {\n fps: DEFAULT_FPS,\n ...opts,\n };\n\n return {\n time: ms(fps, TimeFormat.FPS),\n };\n};\n\n/**\n * Animation-loop timer that uses requestAnimationFrame when available.\n * @param callback - {@link TimerCallback} called on each frame tick.\n * @param opts - {@link FramesOptions} for configuring the target frame rate.\n */\nexport class Frames extends Metro {\n declare protected _timerId: ReturnType<typeof requestAnimationFrame>;\n\n constructor(callback: TimerCallback<Frames>, opts?: FramesOptions) {\n super(() => callback(this), parseOptions(opts));\n }\n\n protected asyncHandler(callback: () => void) {\n if (typeof window === 'undefined' || !('requestAnimationFrame' in window)) {\n super.asyncHandler(callback);\n } else {\n this._timerId = requestAnimationFrame(callback);\n }\n }\n\n protected clearAsyncHandler() {\n if (typeof window === 'undefined' || !('cancelAnimationFrame' in window)) {\n super.clearAsyncHandler();\n } else {\n cancelAnimationFrame(this._timerId);\n }\n }\n\n setFPS = (fps = DEFAULT_FPS) => {\n this.setTime(ms(fps, TimeFormat.FPS));\n };\n}\n","import { clamp } from './clamp';\n\n/**\n * Scales 0...1 by Euler's number to produce a natural feeling curve.\n * @param n - Input value (clamped to 0-1).\n * @returns The exponentially scaled value.\n */\nexport function expo(n: number): number {\n return Math.pow(clamp(n, 0, 1), Math.E);\n}\n"],"mappings":"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","names":["parseOptions","initialNow","parseOptions","updateStateFromOptions","getInitialState","getInitialState","parseOptions","TimeFormat","parseOptions"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/clamp.ts","../src/Rand.ts","../src/Drunk.ts","../src/Scale.ts","../src/now.ts","../src/Env.ts","../src/Sine.ts","../src/constants.ts","../src/Metro.ts","../src/ms.ts","../src/Frames.ts","../src/expo.ts","../src/wait.ts"],"sourcesContent":["export { Drunk, type DrunkOptions, type DrunkState } from './Drunk';\nexport { Env, type EnvOptions, type EnvState } from './Env';\nexport { Rand, type RandOptions, type RandState } from './Rand';\nexport { Scale, type ScaleOptions, type ScaleRange, type ScaleState } from './Scale';\nexport { Sine, type SineOptions, type SineState, SINE_PERIOD } from './Sine';\nexport { Frames, type FramesOptions } from './Frames';\nexport { Metro, type MetroOptions, type TimerCallback } from './Metro';\nexport { clamp } from './clamp';\nexport { expo } from './expo';\nexport { ms, type FPS, TimeFormat } from './ms';\nexport { now } from './now';\nexport { wait } from './wait';\nexport * from './constants';\n","export function clamp(n: number): number;\nexport function clamp(n: number, max: number): number;\nexport function clamp(n: number, min: number, max: number): number;\n\n/**\n * Constrains an input value to a min...max range.\n * @param n - The value to constrain.\n * @param min - Lower bound (defaults to 0).\n * @param max - Upper bound (defaults to 1).\n * @returns The clamped value.\n */\nexport function clamp(n: number, min?: number, max?: number) {\n let a = 0;\n let b = 1;\n\n if (typeof min === 'number') {\n if (typeof max === 'number') {\n a = min;\n b = max;\n } else {\n a = 0;\n b = min;\n }\n }\n\n return Math.min(Math.max(n, a), b);\n}\n","import { clamp } from './clamp';\n\n/** Options for configuring a Rand generator. */\nexport type RandOptions = {\n /** Lower bound of the range. Defaults to 0. */\n min?: number;\n /** Upper bound of the range. Defaults to 1. */\n max?: number;\n};\n\n/** Snapshot of a Rand generator's internal state. */\nexport type RandState = {\n min: number;\n max: number;\n value: number;\n};\n\nexport const parseOptions = (opts?: RandOptions): Required<RandOptions> => {\n return {\n min: 0,\n max: 1,\n ...opts,\n };\n};\n\n/**\n * Random number generator that produces values within a bounded range.\n * @param opts - {@link RandOptions} for configuring the range.\n */\nexport class Rand {\n state: RandState;\n\n static rand(opts?: RandOptions) {\n return new Rand(opts).value();\n }\n\n constructor(opts?: RandOptions) {\n const { min, max } = parseOptions(opts);\n\n this.state = { max, min, value: 0 };\n this.next();\n }\n\n setRange(partialOpts: RandOptions) {\n const { value = 0 } = this.state;\n const { min, max } = {\n ...this.state,\n ...partialOpts,\n };\n\n this.state = {\n ...this.state,\n max,\n min,\n value: clamp(value, min, max),\n };\n }\n\n value() {\n return this.state.value;\n }\n\n next() {\n const { min, max } = this.state;\n const updates = Math.random() * (max - min) + min;\n\n this.state.value = updates;\n\n return updates;\n }\n}\n","import { Rand } from './Rand';\nimport { clamp } from './clamp';\n\nexport const DEFAULT_DRUNK_STEP = 0.1;\n\n/** Options for configuring a Drunk random walk. */\nexport type DrunkOptions = {\n /** Upper bound of the range. Defaults to 1. */\n max?: number;\n /** Lower bound of the range. Defaults to 0. */\n min?: number;\n /** Initial value. If omitted, a random value within the range is used. */\n startsAt?: number;\n /** Maximum step size as a fraction of the range (0-1). Defaults to 0.1. */\n step?: number;\n};\n\n/** Snapshot of a Drunk walk's internal state. */\nexport type DrunkState = {\n initialValue: number;\n max: number;\n min: number;\n step: number;\n value: number;\n};\n\nexport const parseStepSize = (step?: number): number =>\n typeof step !== 'undefined' ? clamp(step, 0, 1) : DEFAULT_DRUNK_STEP;\n\nexport const parseOptions = (opts?: DrunkOptions): Required<DrunkOptions> => {\n const { step, ...restOpts } = opts || {};\n const parsedStepSize = parseStepSize(step);\n\n return {\n max: 1,\n min: 0,\n startsAt: 0,\n step: parsedStepSize,\n ...restOpts,\n };\n};\n\n/**\n * Stochastic random walk generator that produces values within a bounded range.\n * @param opts - {@link DrunkOptions} for configuring the walk.\n */\nexport class Drunk {\n state: DrunkState;\n protected _initialValue: Rand;\n protected _step: Rand;\n\n constructor(opts?: DrunkOptions) {\n const { min, max, step, startsAt } = parseOptions(opts);\n\n this._initialValue = new Rand({ min, max });\n this._step = new Rand({ min: -1, max: 1 });\n\n const initialValue =\n typeof opts?.startsAt !== 'undefined' ? startsAt : this._initialValue.value();\n\n this.state = {\n initialValue,\n max,\n min,\n step,\n value: initialValue,\n };\n }\n\n setRange(partialOpts?: Pick<DrunkOptions, 'min' | 'max'>) {\n const { max, min } = {\n min: this.state.min,\n max: this.state.max,\n ...partialOpts,\n };\n\n this._initialValue.setRange({ min, max });\n this.state = {\n ...this.state,\n ...(min !== this.state.min || max !== this.state.max\n ? {\n initialValue: clamp(this._initialValue.value(), min, max),\n max,\n min,\n value: clamp(this.state.value, min, max),\n }\n : {\n max,\n min,\n }),\n };\n }\n\n setStepSize(partialOpts?: Pick<DrunkOptions, 'step'>) {\n const step = parseStepSize(partialOpts?.step);\n\n this.state = {\n ...this.state,\n step,\n };\n }\n\n reset(opts?: DrunkOptions) {\n const { min, max, startsAt, step } = parseOptions(opts);\n\n this.setRange({ min, max });\n this.setStepSize({ step });\n\n const initialValue =\n typeof opts?.startsAt !== 'undefined' ? startsAt : this._initialValue.next();\n\n this.state = {\n ...this.state,\n initialValue,\n value: initialValue,\n };\n }\n\n value() {\n return this.state.value;\n }\n\n next() {\n const { min, max, step, value } = this.state;\n const updates = clamp(value + max * this._step.next() * step, min, max);\n\n this.state.value = updates;\n\n return updates;\n }\n}\n","import { clamp } from './clamp';\n\n/** A numeric range with min and max bounds. */\nexport type ScaleRange = {\n min?: number;\n max: number;\n};\n\n/** Options for configuring a Scale mapper. */\nexport type ScaleOptions = {\n /** Input range. Defaults to { min: 0, max: 1 }. */\n from?: ScaleRange;\n /** Output range. Defaults to { min: 0, max: 1 }. */\n to?: ScaleRange;\n};\n\n/** Snapshot of a Scale mapper's internal state. */\nexport type ScaleState = {\n /** Input range. */\n from: Required<ScaleRange>;\n /** Precomputed (to.max - to.min) / (from.max - from.min), updated when ranges change. */\n ratio: number;\n /** Output range. */\n to: Required<ScaleRange>;\n /** Last scaled value. */\n value: number;\n};\n\n/** Precompute the scale factor so the hot path avoids a division per call. */\nconst computeRatio = (from: Required<ScaleRange>, to: Required<ScaleRange>) =>\n (to.max - to.min) / (from.max - from.min);\n\n/** Build initial state from options, applying defaults and computing the ratio. */\nexport const parseInitialState = (opts?: ScaleOptions): ScaleState => {\n const initialRange: Pick<ScaleState, 'from' | 'to'> = {\n from: {\n min: 0,\n max: 1,\n ...opts?.from,\n },\n to: {\n min: 0,\n max: 1,\n ...opts?.to,\n },\n };\n\n return {\n ...initialRange,\n ratio: computeRatio(initialRange.from, initialRange.to),\n value: initialRange.to.min,\n };\n};\n\n/** Merge partial range updates into existing state, recomputing the ratio. */\nexport const updateStateFromOptions = (opts: ScaleOptions, prevState: ScaleState): ScaleState => {\n const { from, to } = opts;\n const updatedFrom: Required<ScaleRange> = {\n ...prevState.from,\n ...from,\n };\n const updatedTo: Required<ScaleRange> = {\n ...prevState.to,\n ...to,\n };\n\n return {\n ...prevState,\n from: updatedFrom,\n ratio: computeRatio(updatedFrom, updatedTo),\n to: updatedTo,\n value: clamp(prevState.value, updatedTo.min, updatedTo.max),\n };\n};\n\n/**\n * Linear map of values from one range to another, supports negative values and inversion.\n * @param opts - {@link ScaleOptions} for configuring input and output ranges.\n */\nexport class Scale {\n state: ScaleState;\n\n static scale(n: number, opts?: ScaleOptions) {\n return new Scale(opts).scale(n);\n }\n\n constructor(opts?: ScaleOptions) {\n this.state = parseInitialState(opts);\n }\n\n setRanges(opts: ScaleOptions) {\n this.state = updateStateFromOptions(opts, this.state);\n }\n\n reset(opts: ScaleOptions) {\n this.state = parseInitialState(opts);\n }\n\n value() {\n return this.state.value;\n }\n\n scale(n: number) {\n const { from, to, ratio } = this.state;\n const updates = to.min + (clamp(n, from.min, from.max) - from.min) * ratio;\n\n this.state.value = updates;\n\n return updates;\n }\n}\n","type InnerNow = () => number;\n\n/** Resolve the best available clock once at module load. */\nconst innerNow = ((): InnerNow => {\n // Browser or modern Node (>= 16)\n if (typeof performance !== 'undefined' && 'now' in performance) {\n return () => performance.now();\n }\n\n // Older Node — use process.hrtime, offset from first call\n if (typeof process === 'object' && process.toString() === '[object process]') {\n const ts = () => {\n const hr = process.hrtime();\n return hr[0] * 1e9 + hr[1];\n };\n const initialNow = ts();\n return () => (ts() - initialNow) / 1e6;\n }\n\n // Fallback — Date.now with manual offset\n const initialNow = Date.now();\n return () => Date.now() - initialNow;\n})();\n\n/**\n * Cross-environment high-resolution timestamp (performance.now polyfill).\n * @returns Elapsed milliseconds since initialization.\n */\nexport function now(): number {\n return innerNow();\n}\n","import { Scale } from './Scale';\nimport { now } from './now';\n\n/** Snapshot of an Env's internal state. */\nexport type EnvState = {\n duration: number;\n from: number;\n prev: number;\n to: number;\n totalElapsed: number;\n value: number;\n};\n\n/** Options for configuring an Env envelope. */\nexport type EnvOptions = {\n /** Duration of the envelope in milliseconds. */\n duration: number;\n /** Starting value. Defaults to 0. */\n from?: number;\n /** Ending value. Defaults to 1. */\n to?: number;\n};\n\nexport const parseOptions = (opts?: EnvOptions): Required<EnvOptions> => {\n return {\n duration: 0,\n from: 0,\n to: 1,\n ...opts,\n };\n};\n\nconst getInitialState = ({ from, to, duration }: Required<EnvOptions>): EnvState => {\n return {\n duration,\n from,\n prev: now(),\n to,\n totalElapsed: 0,\n value: from,\n };\n};\n\nexport const updateStateFromOptions = (\n opts: EnvOptions | undefined,\n prevState: EnvState\n): EnvState => {\n const { from, to, duration } = {\n ...prevState,\n ...opts,\n };\n\n return {\n ...prevState,\n duration,\n from,\n to,\n totalElapsed: 0,\n };\n};\n\n/**\n * Linear envelope which interpolates between two values over a duration. Useful for audio envelopes, transitions, and animations.\n * @param opts - {@link EnvOptions} for configuring the envelope.\n */\nexport class Env {\n state: EnvState;\n protected _interpolator: Scale;\n\n constructor(opts: EnvOptions) {\n const { from, to, duration } = parseOptions(opts);\n\n this.state = getInitialState({ from, to, duration });\n this._interpolator = new Scale({\n from: {\n min: 0,\n max: duration,\n },\n to: {\n min: from,\n max: to,\n },\n });\n }\n\n setDuration(duration: number) {\n const { to, totalElapsed } = this.state;\n\n this.state = {\n ...this.state,\n ...(duration <= totalElapsed\n ? {\n duration,\n value: to,\n }\n : { duration }),\n };\n }\n\n reset(opts?: EnvOptions) {\n const updates = updateStateFromOptions(opts, this.state);\n\n this.state = {\n ...updates,\n prev: now(),\n value: updates.from,\n };\n this._interpolator.setRanges({\n from: {\n min: 0,\n max: updates.duration,\n },\n to: {\n min: updates.from,\n max: updates.to,\n },\n });\n }\n\n done() {\n return this.state.duration <= this.state.totalElapsed;\n }\n\n value() {\n const { to, value } = this.state;\n\n if (this.done()) {\n return to;\n }\n\n return value;\n }\n\n next() {\n if (this.done()) {\n return this.value();\n }\n\n const { prev, totalElapsed: prevTotalElapsed } = this.state;\n\n const curr = now();\n const tickInterval = curr - prev;\n const totalElapsed = prevTotalElapsed + tickInterval;\n const updates = this._interpolator.scale(totalElapsed);\n\n this.state.prev = curr;\n this.state.totalElapsed = totalElapsed;\n this.state.value = updates;\n\n return updates;\n }\n}\n","import { Scale } from './Scale';\nimport { clamp } from './clamp';\nimport { now } from './now';\n\nexport const SINE_PERIOD = Math.PI * 2 - 0.0001;\n\n/** Options for configuring a Sine oscillator. */\nexport type SineOptions = {\n /** Duration of one full cycle in milliseconds. */\n duration: number;\n};\n\n/** Snapshot of a Sine oscillator's internal state. */\nexport type SineState = {\n cycle: number;\n duration: number;\n prev: number;\n totalElapsed: number;\n value: number;\n};\n\nconst getInitialState = (duration: number): SineState => ({\n cycle: 0,\n duration,\n prev: now(),\n totalElapsed: 0,\n value: 0,\n});\n\n/**\n * Time-based sine wave oscillator that outputs values between -1 and 1.\n * @param opts - {@link SineOptions} for configuring the oscillator.\n */\nexport class Sine {\n state: SineState;\n protected _interpolator: Scale;\n\n constructor(opts: SineOptions) {\n const { duration } = opts;\n\n this._interpolator = new Scale({\n from: {\n min: 0,\n max: duration,\n },\n to: {\n min: 0,\n max: SINE_PERIOD,\n },\n });\n this.state = getInitialState(duration);\n }\n\n setDuration(duration: number) {\n this.state = {\n ...this.state,\n duration,\n };\n }\n\n reset(opts?: SineOptions) {\n const { duration } = {\n ...this.state,\n ...opts,\n };\n\n this.state = getInitialState(duration);\n }\n\n value() {\n return this.state.value;\n }\n\n next() {\n const { cycle, duration, prev, totalElapsed: prevTotalElapsed } = this.state;\n const curr = now();\n const tickInterval = curr - prev;\n const totalElapsed = prevTotalElapsed + tickInterval;\n\n const updates = clamp(Math.sin(this._interpolator.scale(totalElapsed)), -1, 1);\n\n if (cycle >= duration) {\n this.state.cycle = 0;\n } else {\n this.state.cycle = cycle + tickInterval;\n }\n\n this.state.prev = curr;\n this.state.totalElapsed = totalElapsed;\n this.state.value = updates;\n\n return updates;\n }\n}\n","export const SIXTY_FPS = 1000 / 60;\nexport const MS_IN_SECOND = 1000;\nexport const MS_IN_MINUTE = 60 * MS_IN_SECOND;\nexport const MS_IN_HOUR = MS_IN_MINUTE * 60;\n","import { SIXTY_FPS } from './constants';\nimport { now } from './now';\n\n/** Snapshot of a running timer's internal state. */\nexport type TimerState = {\n initialTime: number;\n isRunning: boolean;\n iterations: number;\n prev: number;\n tickInterval: number;\n time: number;\n totalElapsed: number;\n};\n\nexport const getInitialState = (initialTime: number): TimerState => {\n return {\n initialTime,\n isRunning: false,\n iterations: -1,\n prev: 0,\n tickInterval: 0,\n time: initialTime,\n totalElapsed: 0,\n };\n};\n\nexport const processTimerState = (state: TimerState): TimerState | null => {\n const { time, prev, totalElapsed, iterations } = state;\n const curr = now();\n\n if (iterations === -1) {\n return {\n ...state,\n prev: curr,\n iterations: 0,\n };\n }\n\n const tickInterval = curr - prev;\n\n if (tickInterval <= time) {\n return null;\n }\n\n return {\n ...state,\n iterations: iterations + 1,\n prev: curr,\n tickInterval,\n totalElapsed: totalElapsed + tickInterval,\n };\n};\n\n/** Options for configuring a Metro timer. */\nexport type MetroOptions = {\n /** Interval between ticks in milliseconds. Defaults to ~16.67ms (60fps). */\n time?: number;\n};\n\nexport const parseOptions = (opts?: MetroOptions): Required<MetroOptions> => {\n return {\n time: SIXTY_FPS,\n ...opts,\n };\n};\n\n/** Callback invoked on each timer tick with the timer instance. */\nexport type TimerCallback<T extends Metro> = (timer: T) => void;\n\n/**\n * High-resolution recursive timer with variable interval, provides runtime metrics via callback for time-based calculations.\n * @param callback - {@link TimerCallback} called on each tick with the timer instance.\n * @param opts - {@link MetroOptions} for configuring the timer interval.\n */\nexport class Metro {\n state: TimerState;\n protected _listeners: TimerCallback<Metro>[];\n declare protected _timerId: ReturnType<typeof setTimeout> | number;\n\n constructor(callback: TimerCallback<Metro>, opts?: MetroOptions) {\n const { time } = parseOptions(opts);\n this.state = getInitialState(time);\n this._listeners = [callback];\n }\n\n protected asyncHandler(callback: () => void) {\n this._timerId = setTimeout(callback, SIXTY_FPS);\n }\n\n protected clearAsyncHandler() {\n clearTimeout(this._timerId);\n }\n\n stop = () => {\n const { totalElapsed } = this.state;\n this.reset();\n this.clearAsyncHandler();\n\n return totalElapsed;\n };\n\n reset = () => {\n const { initialTime } = this.state;\n this.state = getInitialState(initialTime);\n };\n\n setTime = (updatedTime = this.state.time) => {\n const time = Math.max(updatedTime, 0);\n\n this.state = {\n ...this.state,\n time,\n initialTime: time,\n };\n };\n\n run = () => {\n if (this.state.isRunning) {\n this.stop();\n }\n\n this.state = {\n ...this.state,\n isRunning: true,\n prev: now(),\n };\n\n const tick = () => {\n const updates = processTimerState(this.state);\n\n if (updates) {\n this.state = updates;\n this._listeners.forEach((listener) => {\n listener(this);\n });\n }\n\n if (this.state.isRunning) {\n this.asyncHandler(tick);\n }\n };\n\n tick();\n };\n}\n","import { MS_IN_SECOND, MS_IN_MINUTE, MS_IN_HOUR } from './constants';\n\nexport type FPS = 15 | 30 | 60;\n\nexport enum TimeFormat {\n FPS = 'fps',\n HOURS = 'h',\n HZ = 'hz',\n MILLISECONDS = 'ms',\n MINUTES = 'm',\n SECONDS = 's',\n}\n\nexport type AvailableTimeFormats = `${TimeFormat}`;\n\nexport const FORMAT_IDENTIFIERS: AvailableTimeFormats[] = [\n TimeFormat.FPS,\n TimeFormat.HOURS,\n TimeFormat.HZ,\n TimeFormat.MILLISECONDS,\n TimeFormat.MINUTES,\n TimeFormat.SECONDS,\n]\n // Desc length sort so that `ms` matches prior to `m`\n .sort((a, b) => b.length - a.length);\n\ntype FormatGetter = (val: number) => number;\n\nconst FORMATTERS = new Map<AvailableTimeFormats, FormatGetter>([\n [TimeFormat.FPS, (val: number) => MS_IN_SECOND / val],\n [TimeFormat.HOURS, (val: number) => val * MS_IN_HOUR],\n [TimeFormat.HZ, (val: number) => (1 / val) * MS_IN_SECOND],\n [TimeFormat.MILLISECONDS, (val: number) => val],\n [TimeFormat.MINUTES, (val: number) => val * MS_IN_MINUTE],\n [TimeFormat.SECONDS, (val: number) => val * MS_IN_SECOND],\n]);\n\nconst sanitizeStringVal = (val: string) => val.toLocaleLowerCase().trim();\n\nconst parseStringValAndFormat = (val: string) => {\n for (let i = 0; i < FORMAT_IDENTIFIERS.length; i += 1) {\n const format = FORMAT_IDENTIFIERS[i];\n\n if (val.includes(format)) {\n const value = Number(val.replace(' ', '').replace(format, ''));\n\n return {\n format,\n value,\n };\n }\n }\n\n return {\n format: undefined,\n value: undefined,\n };\n};\n\nexport function ms(val: string | null | undefined): number | undefined;\nexport function ms(\n val: string | number | null | undefined,\n format?: AvailableTimeFormats | TimeFormat\n): number | undefined;\n\n/**\n * Converts time format strings or numeric values to their corresponding value in milliseconds.\n * @param val - A string like `'60fps'`, `'2s'`, or a numeric value.\n * @param format - Explicit time format when `val` is a number.\n * @returns Milliseconds, or undefined if the input is invalid.\n */\nexport function ms(\n val: string | number | null | undefined,\n format?: AvailableTimeFormats | TimeFormat\n): number | undefined {\n let parsedValue: number | null | undefined = null;\n let parsedFormat: AvailableTimeFormats = format || TimeFormat.MILLISECONDS;\n\n if (typeof val === 'string') {\n const parsed = parseStringValAndFormat(sanitizeStringVal(val));\n\n if (typeof parsed.value !== 'undefined') {\n parsedValue = parsed.value;\n }\n\n if (parsed.format) {\n parsedFormat = parsed.format;\n }\n } else {\n parsedValue = val;\n }\n\n if (typeof parsedValue === 'undefined' || parsedValue === null || Number.isNaN(parsedValue)) {\n return undefined;\n }\n\n const formatter = FORMATTERS.get(parsedFormat);\n\n if (!formatter) {\n return undefined;\n }\n\n return formatter(parsedValue);\n}\n","import { Metro, type TimerCallback, type MetroOptions } from './Metro';\nimport { ms, TimeFormat, type FPS } from './ms';\n\n/** Options for configuring a Frames timer. */\nexport type FramesOptions = {\n /** Target frames per second (15, 30, or 60). Defaults to 60. */\n fps: FPS;\n};\n\nexport const DEFAULT_FPS: FPS = 60;\n\nexport const parseOptions = (opts?: FramesOptions): MetroOptions => {\n const { fps } = {\n fps: DEFAULT_FPS,\n ...opts,\n };\n\n return {\n time: ms(fps, TimeFormat.FPS),\n };\n};\n\n/**\n * Animation-loop timer that uses requestAnimationFrame when available.\n * @param callback - {@link TimerCallback} called on each frame tick.\n * @param opts - {@link FramesOptions} for configuring the target frame rate.\n */\nexport class Frames extends Metro {\n declare protected _timerId: ReturnType<typeof requestAnimationFrame>;\n\n constructor(callback: TimerCallback<Frames>, opts?: FramesOptions) {\n super(() => callback(this), parseOptions(opts));\n }\n\n protected asyncHandler(callback: () => void) {\n if (typeof window === 'undefined' || !('requestAnimationFrame' in window)) {\n super.asyncHandler(callback);\n } else {\n this._timerId = requestAnimationFrame(callback);\n }\n }\n\n protected clearAsyncHandler() {\n if (typeof window === 'undefined' || !('cancelAnimationFrame' in window)) {\n super.clearAsyncHandler();\n } else {\n cancelAnimationFrame(this._timerId);\n }\n }\n\n setFPS = (fps = DEFAULT_FPS) => {\n this.setTime(ms(fps, TimeFormat.FPS));\n };\n}\n","import { clamp } from './clamp';\n\n/**\n * Scales 0...1 by Euler's number to produce a natural feeling curve.\n * @param n - Input value (clamped to 0-1).\n * @returns The exponentially scaled value.\n */\nexport function expo(n: number): number {\n return Math.pow(clamp(n, 0, 1), Math.E);\n}\n","/**\n * Promisified setTimeout.\n * @param time - Delay in milliseconds.\n * @returns A promise that resolves after the delay.\n */\nexport function wait(time: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, time));\n}\n"],"mappings":"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","names":["parseOptions","initialNow","parseOptions","updateStateFromOptions","getInitialState","getInitialState","parseOptions","TimeFormat","parseOptions"]}
package/dist/index.d.cts CHANGED
@@ -240,9 +240,16 @@ declare function expo(n: number): number;
240
240
  */
241
241
  declare function now(): number;
242
242
 
243
+ /**
244
+ * Promisified setTimeout.
245
+ * @param time - Delay in milliseconds.
246
+ * @returns A promise that resolves after the delay.
247
+ */
248
+ declare function wait(time: number): Promise<void>;
249
+
243
250
  declare const SIXTY_FPS: number;
244
251
  declare const MS_IN_SECOND = 1000;
245
252
  declare const MS_IN_MINUTE: number;
246
253
  declare const MS_IN_HOUR: number;
247
254
 
248
- export { Drunk, type DrunkOptions, type DrunkState, Env, type EnvOptions, type EnvState, type FPS, Frames, type FramesOptions, MS_IN_HOUR, MS_IN_MINUTE, MS_IN_SECOND, Metro, type MetroOptions, Rand, type RandOptions, type RandState, SINE_PERIOD, SIXTY_FPS, Scale, type ScaleOptions, type ScaleRange, type ScaleState, Sine, type SineOptions, type SineState, TimeFormat, type TimerCallback, clamp, expo, ms, now };
255
+ export { Drunk, type DrunkOptions, type DrunkState, Env, type EnvOptions, type EnvState, type FPS, Frames, type FramesOptions, MS_IN_HOUR, MS_IN_MINUTE, MS_IN_SECOND, Metro, type MetroOptions, Rand, type RandOptions, type RandState, SINE_PERIOD, SIXTY_FPS, Scale, type ScaleOptions, type ScaleRange, type ScaleState, Sine, type SineOptions, type SineState, TimeFormat, type TimerCallback, clamp, expo, ms, now, wait };
package/dist/index.d.ts CHANGED
@@ -240,9 +240,16 @@ declare function expo(n: number): number;
240
240
  */
241
241
  declare function now(): number;
242
242
 
243
+ /**
244
+ * Promisified setTimeout.
245
+ * @param time - Delay in milliseconds.
246
+ * @returns A promise that resolves after the delay.
247
+ */
248
+ declare function wait(time: number): Promise<void>;
249
+
243
250
  declare const SIXTY_FPS: number;
244
251
  declare const MS_IN_SECOND = 1000;
245
252
  declare const MS_IN_MINUTE: number;
246
253
  declare const MS_IN_HOUR: number;
247
254
 
248
- export { Drunk, type DrunkOptions, type DrunkState, Env, type EnvOptions, type EnvState, type FPS, Frames, type FramesOptions, MS_IN_HOUR, MS_IN_MINUTE, MS_IN_SECOND, Metro, type MetroOptions, Rand, type RandOptions, type RandState, SINE_PERIOD, SIXTY_FPS, Scale, type ScaleOptions, type ScaleRange, type ScaleState, Sine, type SineOptions, type SineState, TimeFormat, type TimerCallback, clamp, expo, ms, now };
255
+ export { Drunk, type DrunkOptions, type DrunkState, Env, type EnvOptions, type EnvState, type FPS, Frames, type FramesOptions, MS_IN_HOUR, MS_IN_MINUTE, MS_IN_SECOND, Metro, type MetroOptions, Rand, type RandOptions, type RandState, SINE_PERIOD, SIXTY_FPS, Scale, type ScaleOptions, type ScaleRange, type ScaleState, Sine, type SineOptions, type SineState, TimeFormat, type TimerCallback, clamp, expo, ms, now, wait };
package/dist/index.js CHANGED
@@ -595,6 +595,11 @@ var Frames = class extends Metro {
595
595
  function expo(n) {
596
596
  return Math.pow(clamp(n, 0, 1), Math.E);
597
597
  }
598
+
599
+ // src/wait.ts
600
+ function wait(time) {
601
+ return new Promise((resolve) => setTimeout(resolve, time));
602
+ }
598
603
  export {
599
604
  Drunk,
600
605
  Env,
@@ -612,6 +617,7 @@ export {
612
617
  clamp,
613
618
  expo,
614
619
  ms,
615
- now
620
+ now,
621
+ wait
616
622
  };
617
623
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/clamp.ts","../src/Rand.ts","../src/Drunk.ts","../src/Scale.ts","../src/now.ts","../src/Env.ts","../src/Sine.ts","../src/constants.ts","../src/Metro.ts","../src/ms.ts","../src/Frames.ts","../src/expo.ts"],"sourcesContent":["export function clamp(n: number): number;\nexport function clamp(n: number, max: number): number;\nexport function clamp(n: number, min: number, max: number): number;\n\n/**\n * Constrains an input value to a min...max range.\n * @param n - The value to constrain.\n * @param min - Lower bound (defaults to 0).\n * @param max - Upper bound (defaults to 1).\n * @returns The clamped value.\n */\nexport function clamp(n: number, min?: number, max?: number) {\n let a = 0;\n let b = 1;\n\n if (typeof min === 'number') {\n if (typeof max === 'number') {\n a = min;\n b = max;\n } else {\n a = 0;\n b = min;\n }\n }\n\n return Math.min(Math.max(n, a), b);\n}\n","import { clamp } from './clamp';\n\n/** Options for configuring a Rand generator. */\nexport type RandOptions = {\n /** Lower bound of the range. Defaults to 0. */\n min?: number;\n /** Upper bound of the range. Defaults to 1. */\n max?: number;\n};\n\n/** Snapshot of a Rand generator's internal state. */\nexport type RandState = {\n min: number;\n max: number;\n value: number;\n};\n\nexport const parseOptions = (opts?: RandOptions): Required<RandOptions> => {\n return {\n min: 0,\n max: 1,\n ...opts,\n };\n};\n\n/**\n * Random number generator that produces values within a bounded range.\n * @param opts - {@link RandOptions} for configuring the range.\n */\nexport class Rand {\n state: RandState;\n\n static rand(opts?: RandOptions) {\n return new Rand(opts).value();\n }\n\n constructor(opts?: RandOptions) {\n const { min, max } = parseOptions(opts);\n\n this.state = { max, min, value: 0 };\n this.next();\n }\n\n setRange(partialOpts: RandOptions) {\n const { value = 0 } = this.state;\n const { min, max } = {\n ...this.state,\n ...partialOpts,\n };\n\n this.state = {\n ...this.state,\n max,\n min,\n value: clamp(value, min, max),\n };\n }\n\n value() {\n return this.state.value;\n }\n\n next() {\n const { min, max } = this.state;\n const updates = Math.random() * (max - min) + min;\n\n this.state.value = updates;\n\n return updates;\n }\n}\n","import { Rand } from './Rand';\nimport { clamp } from './clamp';\n\nexport const DEFAULT_DRUNK_STEP = 0.1;\n\n/** Options for configuring a Drunk random walk. */\nexport type DrunkOptions = {\n /** Upper bound of the range. Defaults to 1. */\n max?: number;\n /** Lower bound of the range. Defaults to 0. */\n min?: number;\n /** Initial value. If omitted, a random value within the range is used. */\n startsAt?: number;\n /** Maximum step size as a fraction of the range (0-1). Defaults to 0.1. */\n step?: number;\n};\n\n/** Snapshot of a Drunk walk's internal state. */\nexport type DrunkState = {\n initialValue: number;\n max: number;\n min: number;\n step: number;\n value: number;\n};\n\nexport const parseStepSize = (step?: number): number =>\n typeof step !== 'undefined' ? clamp(step, 0, 1) : DEFAULT_DRUNK_STEP;\n\nexport const parseOptions = (opts?: DrunkOptions): Required<DrunkOptions> => {\n const { step, ...restOpts } = opts || {};\n const parsedStepSize = parseStepSize(step);\n\n return {\n max: 1,\n min: 0,\n startsAt: 0,\n step: parsedStepSize,\n ...restOpts,\n };\n};\n\n/**\n * Stochastic random walk generator that produces values within a bounded range.\n * @param opts - {@link DrunkOptions} for configuring the walk.\n */\nexport class Drunk {\n state: DrunkState;\n protected _initialValue: Rand;\n protected _step: Rand;\n\n constructor(opts?: DrunkOptions) {\n const { min, max, step, startsAt } = parseOptions(opts);\n\n this._initialValue = new Rand({ min, max });\n this._step = new Rand({ min: -1, max: 1 });\n\n const initialValue =\n typeof opts?.startsAt !== 'undefined' ? startsAt : this._initialValue.value();\n\n this.state = {\n initialValue,\n max,\n min,\n step,\n value: initialValue,\n };\n }\n\n setRange(partialOpts?: Pick<DrunkOptions, 'min' | 'max'>) {\n const { max, min } = {\n min: this.state.min,\n max: this.state.max,\n ...partialOpts,\n };\n\n this._initialValue.setRange({ min, max });\n this.state = {\n ...this.state,\n ...(min !== this.state.min || max !== this.state.max\n ? {\n initialValue: clamp(this._initialValue.value(), min, max),\n max,\n min,\n value: clamp(this.state.value, min, max),\n }\n : {\n max,\n min,\n }),\n };\n }\n\n setStepSize(partialOpts?: Pick<DrunkOptions, 'step'>) {\n const step = parseStepSize(partialOpts?.step);\n\n this.state = {\n ...this.state,\n step,\n };\n }\n\n reset(opts?: DrunkOptions) {\n const { min, max, startsAt, step } = parseOptions(opts);\n\n this.setRange({ min, max });\n this.setStepSize({ step });\n\n const initialValue =\n typeof opts?.startsAt !== 'undefined' ? startsAt : this._initialValue.next();\n\n this.state = {\n ...this.state,\n initialValue,\n value: initialValue,\n };\n }\n\n value() {\n return this.state.value;\n }\n\n next() {\n const { min, max, step, value } = this.state;\n const updates = clamp(value + max * this._step.next() * step, min, max);\n\n this.state.value = updates;\n\n return updates;\n }\n}\n","import { clamp } from './clamp';\n\n/** A numeric range with min and max bounds. */\nexport type ScaleRange = {\n min?: number;\n max: number;\n};\n\n/** Options for configuring a Scale mapper. */\nexport type ScaleOptions = {\n /** Input range. Defaults to { min: 0, max: 1 }. */\n from?: ScaleRange;\n /** Output range. Defaults to { min: 0, max: 1 }. */\n to?: ScaleRange;\n};\n\n/** Snapshot of a Scale mapper's internal state. */\nexport type ScaleState = {\n /** Input range. */\n from: Required<ScaleRange>;\n /** Precomputed (to.max - to.min) / (from.max - from.min), updated when ranges change. */\n ratio: number;\n /** Output range. */\n to: Required<ScaleRange>;\n /** Last scaled value. */\n value: number;\n};\n\n/** Precompute the scale factor so the hot path avoids a division per call. */\nconst computeRatio = (from: Required<ScaleRange>, to: Required<ScaleRange>) =>\n (to.max - to.min) / (from.max - from.min);\n\n/** Build initial state from options, applying defaults and computing the ratio. */\nexport const parseInitialState = (opts?: ScaleOptions): ScaleState => {\n const initialRange: Pick<ScaleState, 'from' | 'to'> = {\n from: {\n min: 0,\n max: 1,\n ...opts?.from,\n },\n to: {\n min: 0,\n max: 1,\n ...opts?.to,\n },\n };\n\n return {\n ...initialRange,\n ratio: computeRatio(initialRange.from, initialRange.to),\n value: initialRange.to.min,\n };\n};\n\n/** Merge partial range updates into existing state, recomputing the ratio. */\nexport const updateStateFromOptions = (opts: ScaleOptions, prevState: ScaleState): ScaleState => {\n const { from, to } = opts;\n const updatedFrom: Required<ScaleRange> = {\n ...prevState.from,\n ...from,\n };\n const updatedTo: Required<ScaleRange> = {\n ...prevState.to,\n ...to,\n };\n\n return {\n ...prevState,\n from: updatedFrom,\n ratio: computeRatio(updatedFrom, updatedTo),\n to: updatedTo,\n value: clamp(prevState.value, updatedTo.min, updatedTo.max),\n };\n};\n\n/**\n * Linear map of values from one range to another, supports negative values and inversion.\n * @param opts - {@link ScaleOptions} for configuring input and output ranges.\n */\nexport class Scale {\n state: ScaleState;\n\n static scale(n: number, opts?: ScaleOptions) {\n return new Scale(opts).scale(n);\n }\n\n constructor(opts?: ScaleOptions) {\n this.state = parseInitialState(opts);\n }\n\n setRanges(opts: ScaleOptions) {\n this.state = updateStateFromOptions(opts, this.state);\n }\n\n reset(opts: ScaleOptions) {\n this.state = parseInitialState(opts);\n }\n\n value() {\n return this.state.value;\n }\n\n scale(n: number) {\n const { from, to, ratio } = this.state;\n const updates = to.min + (clamp(n, from.min, from.max) - from.min) * ratio;\n\n this.state.value = updates;\n\n return updates;\n }\n}\n","type InnerNow = () => number;\n\n/** Resolve the best available clock once at module load. */\nconst innerNow = ((): InnerNow => {\n // Browser or modern Node (>= 16)\n if (typeof performance !== 'undefined' && 'now' in performance) {\n return () => performance.now();\n }\n\n // Older Node — use process.hrtime, offset from first call\n if (typeof process === 'object' && process.toString() === '[object process]') {\n const ts = () => {\n const hr = process.hrtime();\n return hr[0] * 1e9 + hr[1];\n };\n const initialNow = ts();\n return () => (ts() - initialNow) / 1e6;\n }\n\n // Fallback — Date.now with manual offset\n const initialNow = Date.now();\n return () => Date.now() - initialNow;\n})();\n\n/**\n * Cross-environment high-resolution timestamp (performance.now polyfill).\n * @returns Elapsed milliseconds since initialization.\n */\nexport function now(): number {\n return innerNow();\n}\n","import { Scale } from './Scale';\nimport { now } from './now';\n\n/** Snapshot of an Env's internal state. */\nexport type EnvState = {\n duration: number;\n from: number;\n prev: number;\n to: number;\n totalElapsed: number;\n value: number;\n};\n\n/** Options for configuring an Env envelope. */\nexport type EnvOptions = {\n /** Duration of the envelope in milliseconds. */\n duration: number;\n /** Starting value. Defaults to 0. */\n from?: number;\n /** Ending value. Defaults to 1. */\n to?: number;\n};\n\nexport const parseOptions = (opts?: EnvOptions): Required<EnvOptions> => {\n return {\n duration: 0,\n from: 0,\n to: 1,\n ...opts,\n };\n};\n\nconst getInitialState = ({ from, to, duration }: Required<EnvOptions>): EnvState => {\n return {\n duration,\n from,\n prev: now(),\n to,\n totalElapsed: 0,\n value: from,\n };\n};\n\nexport const updateStateFromOptions = (\n opts: EnvOptions | undefined,\n prevState: EnvState\n): EnvState => {\n const { from, to, duration } = {\n ...prevState,\n ...opts,\n };\n\n return {\n ...prevState,\n duration,\n from,\n to,\n totalElapsed: 0,\n };\n};\n\n/**\n * Linear envelope which interpolates between two values over a duration. Useful for audio envelopes, transitions, and animations.\n * @param opts - {@link EnvOptions} for configuring the envelope.\n */\nexport class Env {\n state: EnvState;\n protected _interpolator: Scale;\n\n constructor(opts: EnvOptions) {\n const { from, to, duration } = parseOptions(opts);\n\n this.state = getInitialState({ from, to, duration });\n this._interpolator = new Scale({\n from: {\n min: 0,\n max: duration,\n },\n to: {\n min: from,\n max: to,\n },\n });\n }\n\n setDuration(duration: number) {\n const { to, totalElapsed } = this.state;\n\n this.state = {\n ...this.state,\n ...(duration <= totalElapsed\n ? {\n duration,\n value: to,\n }\n : { duration }),\n };\n }\n\n reset(opts?: EnvOptions) {\n const updates = updateStateFromOptions(opts, this.state);\n\n this.state = {\n ...updates,\n prev: now(),\n value: updates.from,\n };\n this._interpolator.setRanges({\n from: {\n min: 0,\n max: updates.duration,\n },\n to: {\n min: updates.from,\n max: updates.to,\n },\n });\n }\n\n done() {\n return this.state.duration <= this.state.totalElapsed;\n }\n\n value() {\n const { to, value } = this.state;\n\n if (this.done()) {\n return to;\n }\n\n return value;\n }\n\n next() {\n if (this.done()) {\n return this.value();\n }\n\n const { prev, totalElapsed: prevTotalElapsed } = this.state;\n\n const curr = now();\n const tickInterval = curr - prev;\n const totalElapsed = prevTotalElapsed + tickInterval;\n const updates = this._interpolator.scale(totalElapsed);\n\n this.state.prev = curr;\n this.state.totalElapsed = totalElapsed;\n this.state.value = updates;\n\n return updates;\n }\n}\n","import { Scale } from './Scale';\nimport { clamp } from './clamp';\nimport { now } from './now';\n\nexport const SINE_PERIOD = Math.PI * 2 - 0.0001;\n\n/** Options for configuring a Sine oscillator. */\nexport type SineOptions = {\n /** Duration of one full cycle in milliseconds. */\n duration: number;\n};\n\n/** Snapshot of a Sine oscillator's internal state. */\nexport type SineState = {\n cycle: number;\n duration: number;\n prev: number;\n totalElapsed: number;\n value: number;\n};\n\nconst getInitialState = (duration: number): SineState => ({\n cycle: 0,\n duration,\n prev: now(),\n totalElapsed: 0,\n value: 0,\n});\n\n/**\n * Time-based sine wave oscillator that outputs values between -1 and 1.\n * @param opts - {@link SineOptions} for configuring the oscillator.\n */\nexport class Sine {\n state: SineState;\n protected _interpolator: Scale;\n\n constructor(opts: SineOptions) {\n const { duration } = opts;\n\n this._interpolator = new Scale({\n from: {\n min: 0,\n max: duration,\n },\n to: {\n min: 0,\n max: SINE_PERIOD,\n },\n });\n this.state = getInitialState(duration);\n }\n\n setDuration(duration: number) {\n this.state = {\n ...this.state,\n duration,\n };\n }\n\n reset(opts?: SineOptions) {\n const { duration } = {\n ...this.state,\n ...opts,\n };\n\n this.state = getInitialState(duration);\n }\n\n value() {\n return this.state.value;\n }\n\n next() {\n const { cycle, duration, prev, totalElapsed: prevTotalElapsed } = this.state;\n const curr = now();\n const tickInterval = curr - prev;\n const totalElapsed = prevTotalElapsed + tickInterval;\n\n const updates = clamp(Math.sin(this._interpolator.scale(totalElapsed)), -1, 1);\n\n if (cycle >= duration) {\n this.state.cycle = 0;\n } else {\n this.state.cycle = cycle + tickInterval;\n }\n\n this.state.prev = curr;\n this.state.totalElapsed = totalElapsed;\n this.state.value = updates;\n\n return updates;\n }\n}\n","export const SIXTY_FPS = 1000 / 60;\nexport const MS_IN_SECOND = 1000;\nexport const MS_IN_MINUTE = 60 * MS_IN_SECOND;\nexport const MS_IN_HOUR = MS_IN_MINUTE * 60;\n","import { SIXTY_FPS } from './constants';\nimport { now } from './now';\n\n/** Snapshot of a running timer's internal state. */\nexport type TimerState = {\n initialTime: number;\n isRunning: boolean;\n iterations: number;\n prev: number;\n tickInterval: number;\n time: number;\n totalElapsed: number;\n};\n\nexport const getInitialState = (initialTime: number): TimerState => {\n return {\n initialTime,\n isRunning: false,\n iterations: -1,\n prev: 0,\n tickInterval: 0,\n time: initialTime,\n totalElapsed: 0,\n };\n};\n\nexport const processTimerState = (state: TimerState): TimerState | null => {\n const { time, prev, totalElapsed, iterations } = state;\n const curr = now();\n\n if (iterations === -1) {\n return {\n ...state,\n prev: curr,\n iterations: 0,\n };\n }\n\n const tickInterval = curr - prev;\n\n if (tickInterval <= time) {\n return null;\n }\n\n return {\n ...state,\n iterations: iterations + 1,\n prev: curr,\n tickInterval,\n totalElapsed: totalElapsed + tickInterval,\n };\n};\n\n/** Options for configuring a Metro timer. */\nexport type MetroOptions = {\n /** Interval between ticks in milliseconds. Defaults to ~16.67ms (60fps). */\n time?: number;\n};\n\nexport const parseOptions = (opts?: MetroOptions): Required<MetroOptions> => {\n return {\n time: SIXTY_FPS,\n ...opts,\n };\n};\n\n/** Callback invoked on each timer tick with the timer instance. */\nexport type TimerCallback<T extends Metro> = (timer: T) => void;\n\n/**\n * High-resolution recursive timer with variable interval, provides runtime metrics via callback for time-based calculations.\n * @param callback - {@link TimerCallback} called on each tick with the timer instance.\n * @param opts - {@link MetroOptions} for configuring the timer interval.\n */\nexport class Metro {\n state: TimerState;\n protected _listeners: TimerCallback<Metro>[];\n declare protected _timerId: ReturnType<typeof setTimeout> | number;\n\n constructor(callback: TimerCallback<Metro>, opts?: MetroOptions) {\n const { time } = parseOptions(opts);\n this.state = getInitialState(time);\n this._listeners = [callback];\n }\n\n protected asyncHandler(callback: () => void) {\n this._timerId = setTimeout(callback, SIXTY_FPS);\n }\n\n protected clearAsyncHandler() {\n clearTimeout(this._timerId);\n }\n\n stop = () => {\n const { totalElapsed } = this.state;\n this.reset();\n this.clearAsyncHandler();\n\n return totalElapsed;\n };\n\n reset = () => {\n const { initialTime } = this.state;\n this.state = getInitialState(initialTime);\n };\n\n setTime = (updatedTime = this.state.time) => {\n const time = Math.max(updatedTime, 0);\n\n this.state = {\n ...this.state,\n time,\n initialTime: time,\n };\n };\n\n run = () => {\n if (this.state.isRunning) {\n this.stop();\n }\n\n this.state = {\n ...this.state,\n isRunning: true,\n prev: now(),\n };\n\n const tick = () => {\n const updates = processTimerState(this.state);\n\n if (updates) {\n this.state = updates;\n this._listeners.forEach((listener) => {\n listener(this);\n });\n }\n\n if (this.state.isRunning) {\n this.asyncHandler(tick);\n }\n };\n\n tick();\n };\n}\n","import { MS_IN_SECOND, MS_IN_MINUTE, MS_IN_HOUR } from './constants';\n\nexport type FPS = 15 | 30 | 60;\n\nexport enum TimeFormat {\n FPS = 'fps',\n HOURS = 'h',\n HZ = 'hz',\n MILLISECONDS = 'ms',\n MINUTES = 'm',\n SECONDS = 's',\n}\n\nexport type AvailableTimeFormats = `${TimeFormat}`;\n\nexport const FORMAT_IDENTIFIERS: AvailableTimeFormats[] = [\n TimeFormat.FPS,\n TimeFormat.HOURS,\n TimeFormat.HZ,\n TimeFormat.MILLISECONDS,\n TimeFormat.MINUTES,\n TimeFormat.SECONDS,\n]\n // Desc length sort so that `ms` matches prior to `m`\n .sort((a, b) => b.length - a.length);\n\ntype FormatGetter = (val: number) => number;\n\nconst FORMATTERS = new Map<AvailableTimeFormats, FormatGetter>([\n [TimeFormat.FPS, (val: number) => MS_IN_SECOND / val],\n [TimeFormat.HOURS, (val: number) => val * MS_IN_HOUR],\n [TimeFormat.HZ, (val: number) => (1 / val) * MS_IN_SECOND],\n [TimeFormat.MILLISECONDS, (val: number) => val],\n [TimeFormat.MINUTES, (val: number) => val * MS_IN_MINUTE],\n [TimeFormat.SECONDS, (val: number) => val * MS_IN_SECOND],\n]);\n\nconst sanitizeStringVal = (val: string) => val.toLocaleLowerCase().trim();\n\nconst parseStringValAndFormat = (val: string) => {\n for (let i = 0; i < FORMAT_IDENTIFIERS.length; i += 1) {\n const format = FORMAT_IDENTIFIERS[i];\n\n if (val.includes(format)) {\n const value = Number(val.replace(' ', '').replace(format, ''));\n\n return {\n format,\n value,\n };\n }\n }\n\n return {\n format: undefined,\n value: undefined,\n };\n};\n\nexport function ms(val: string | null | undefined): number | undefined;\nexport function ms(\n val: string | number | null | undefined,\n format?: AvailableTimeFormats | TimeFormat\n): number | undefined;\n\n/**\n * Converts time format strings or numeric values to their corresponding value in milliseconds.\n * @param val - A string like `'60fps'`, `'2s'`, or a numeric value.\n * @param format - Explicit time format when `val` is a number.\n * @returns Milliseconds, or undefined if the input is invalid.\n */\nexport function ms(\n val: string | number | null | undefined,\n format?: AvailableTimeFormats | TimeFormat\n): number | undefined {\n let parsedValue: number | null | undefined = null;\n let parsedFormat: AvailableTimeFormats = format || TimeFormat.MILLISECONDS;\n\n if (typeof val === 'string') {\n const parsed = parseStringValAndFormat(sanitizeStringVal(val));\n\n if (typeof parsed.value !== 'undefined') {\n parsedValue = parsed.value;\n }\n\n if (parsed.format) {\n parsedFormat = parsed.format;\n }\n } else {\n parsedValue = val;\n }\n\n if (typeof parsedValue === 'undefined' || parsedValue === null || Number.isNaN(parsedValue)) {\n return undefined;\n }\n\n const formatter = FORMATTERS.get(parsedFormat);\n\n if (!formatter) {\n return undefined;\n }\n\n return formatter(parsedValue);\n}\n","import { Metro, type TimerCallback, type MetroOptions } from './Metro';\nimport { ms, TimeFormat, type FPS } from './ms';\n\n/** Options for configuring a Frames timer. */\nexport type FramesOptions = {\n /** Target frames per second (15, 30, or 60). Defaults to 60. */\n fps: FPS;\n};\n\nexport const DEFAULT_FPS: FPS = 60;\n\nexport const parseOptions = (opts?: FramesOptions): MetroOptions => {\n const { fps } = {\n fps: DEFAULT_FPS,\n ...opts,\n };\n\n return {\n time: ms(fps, TimeFormat.FPS),\n };\n};\n\n/**\n * Animation-loop timer that uses requestAnimationFrame when available.\n * @param callback - {@link TimerCallback} called on each frame tick.\n * @param opts - {@link FramesOptions} for configuring the target frame rate.\n */\nexport class Frames extends Metro {\n declare protected _timerId: ReturnType<typeof requestAnimationFrame>;\n\n constructor(callback: TimerCallback<Frames>, opts?: FramesOptions) {\n super(() => callback(this), parseOptions(opts));\n }\n\n protected asyncHandler(callback: () => void) {\n if (typeof window === 'undefined' || !('requestAnimationFrame' in window)) {\n super.asyncHandler(callback);\n } else {\n this._timerId = requestAnimationFrame(callback);\n }\n }\n\n protected clearAsyncHandler() {\n if (typeof window === 'undefined' || !('cancelAnimationFrame' in window)) {\n super.clearAsyncHandler();\n } else {\n cancelAnimationFrame(this._timerId);\n }\n }\n\n setFPS = (fps = DEFAULT_FPS) => {\n this.setTime(ms(fps, TimeFormat.FPS));\n };\n}\n","import { clamp } from './clamp';\n\n/**\n * Scales 0...1 by Euler's number to produce a natural feeling curve.\n * @param n - Input value (clamped to 0-1).\n * @returns The exponentially scaled value.\n */\nexport function expo(n: number): number {\n return Math.pow(clamp(n, 0, 1), Math.E);\n}\n"],"mappings":"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","names":["parseOptions","initialNow","parseOptions","updateStateFromOptions","getInitialState","getInitialState","parseOptions","TimeFormat","parseOptions"]}
1
+ {"version":3,"sources":["../src/clamp.ts","../src/Rand.ts","../src/Drunk.ts","../src/Scale.ts","../src/now.ts","../src/Env.ts","../src/Sine.ts","../src/constants.ts","../src/Metro.ts","../src/ms.ts","../src/Frames.ts","../src/expo.ts","../src/wait.ts"],"sourcesContent":["export function clamp(n: number): number;\nexport function clamp(n: number, max: number): number;\nexport function clamp(n: number, min: number, max: number): number;\n\n/**\n * Constrains an input value to a min...max range.\n * @param n - The value to constrain.\n * @param min - Lower bound (defaults to 0).\n * @param max - Upper bound (defaults to 1).\n * @returns The clamped value.\n */\nexport function clamp(n: number, min?: number, max?: number) {\n let a = 0;\n let b = 1;\n\n if (typeof min === 'number') {\n if (typeof max === 'number') {\n a = min;\n b = max;\n } else {\n a = 0;\n b = min;\n }\n }\n\n return Math.min(Math.max(n, a), b);\n}\n","import { clamp } from './clamp';\n\n/** Options for configuring a Rand generator. */\nexport type RandOptions = {\n /** Lower bound of the range. Defaults to 0. */\n min?: number;\n /** Upper bound of the range. Defaults to 1. */\n max?: number;\n};\n\n/** Snapshot of a Rand generator's internal state. */\nexport type RandState = {\n min: number;\n max: number;\n value: number;\n};\n\nexport const parseOptions = (opts?: RandOptions): Required<RandOptions> => {\n return {\n min: 0,\n max: 1,\n ...opts,\n };\n};\n\n/**\n * Random number generator that produces values within a bounded range.\n * @param opts - {@link RandOptions} for configuring the range.\n */\nexport class Rand {\n state: RandState;\n\n static rand(opts?: RandOptions) {\n return new Rand(opts).value();\n }\n\n constructor(opts?: RandOptions) {\n const { min, max } = parseOptions(opts);\n\n this.state = { max, min, value: 0 };\n this.next();\n }\n\n setRange(partialOpts: RandOptions) {\n const { value = 0 } = this.state;\n const { min, max } = {\n ...this.state,\n ...partialOpts,\n };\n\n this.state = {\n ...this.state,\n max,\n min,\n value: clamp(value, min, max),\n };\n }\n\n value() {\n return this.state.value;\n }\n\n next() {\n const { min, max } = this.state;\n const updates = Math.random() * (max - min) + min;\n\n this.state.value = updates;\n\n return updates;\n }\n}\n","import { Rand } from './Rand';\nimport { clamp } from './clamp';\n\nexport const DEFAULT_DRUNK_STEP = 0.1;\n\n/** Options for configuring a Drunk random walk. */\nexport type DrunkOptions = {\n /** Upper bound of the range. Defaults to 1. */\n max?: number;\n /** Lower bound of the range. Defaults to 0. */\n min?: number;\n /** Initial value. If omitted, a random value within the range is used. */\n startsAt?: number;\n /** Maximum step size as a fraction of the range (0-1). Defaults to 0.1. */\n step?: number;\n};\n\n/** Snapshot of a Drunk walk's internal state. */\nexport type DrunkState = {\n initialValue: number;\n max: number;\n min: number;\n step: number;\n value: number;\n};\n\nexport const parseStepSize = (step?: number): number =>\n typeof step !== 'undefined' ? clamp(step, 0, 1) : DEFAULT_DRUNK_STEP;\n\nexport const parseOptions = (opts?: DrunkOptions): Required<DrunkOptions> => {\n const { step, ...restOpts } = opts || {};\n const parsedStepSize = parseStepSize(step);\n\n return {\n max: 1,\n min: 0,\n startsAt: 0,\n step: parsedStepSize,\n ...restOpts,\n };\n};\n\n/**\n * Stochastic random walk generator that produces values within a bounded range.\n * @param opts - {@link DrunkOptions} for configuring the walk.\n */\nexport class Drunk {\n state: DrunkState;\n protected _initialValue: Rand;\n protected _step: Rand;\n\n constructor(opts?: DrunkOptions) {\n const { min, max, step, startsAt } = parseOptions(opts);\n\n this._initialValue = new Rand({ min, max });\n this._step = new Rand({ min: -1, max: 1 });\n\n const initialValue =\n typeof opts?.startsAt !== 'undefined' ? startsAt : this._initialValue.value();\n\n this.state = {\n initialValue,\n max,\n min,\n step,\n value: initialValue,\n };\n }\n\n setRange(partialOpts?: Pick<DrunkOptions, 'min' | 'max'>) {\n const { max, min } = {\n min: this.state.min,\n max: this.state.max,\n ...partialOpts,\n };\n\n this._initialValue.setRange({ min, max });\n this.state = {\n ...this.state,\n ...(min !== this.state.min || max !== this.state.max\n ? {\n initialValue: clamp(this._initialValue.value(), min, max),\n max,\n min,\n value: clamp(this.state.value, min, max),\n }\n : {\n max,\n min,\n }),\n };\n }\n\n setStepSize(partialOpts?: Pick<DrunkOptions, 'step'>) {\n const step = parseStepSize(partialOpts?.step);\n\n this.state = {\n ...this.state,\n step,\n };\n }\n\n reset(opts?: DrunkOptions) {\n const { min, max, startsAt, step } = parseOptions(opts);\n\n this.setRange({ min, max });\n this.setStepSize({ step });\n\n const initialValue =\n typeof opts?.startsAt !== 'undefined' ? startsAt : this._initialValue.next();\n\n this.state = {\n ...this.state,\n initialValue,\n value: initialValue,\n };\n }\n\n value() {\n return this.state.value;\n }\n\n next() {\n const { min, max, step, value } = this.state;\n const updates = clamp(value + max * this._step.next() * step, min, max);\n\n this.state.value = updates;\n\n return updates;\n }\n}\n","import { clamp } from './clamp';\n\n/** A numeric range with min and max bounds. */\nexport type ScaleRange = {\n min?: number;\n max: number;\n};\n\n/** Options for configuring a Scale mapper. */\nexport type ScaleOptions = {\n /** Input range. Defaults to { min: 0, max: 1 }. */\n from?: ScaleRange;\n /** Output range. Defaults to { min: 0, max: 1 }. */\n to?: ScaleRange;\n};\n\n/** Snapshot of a Scale mapper's internal state. */\nexport type ScaleState = {\n /** Input range. */\n from: Required<ScaleRange>;\n /** Precomputed (to.max - to.min) / (from.max - from.min), updated when ranges change. */\n ratio: number;\n /** Output range. */\n to: Required<ScaleRange>;\n /** Last scaled value. */\n value: number;\n};\n\n/** Precompute the scale factor so the hot path avoids a division per call. */\nconst computeRatio = (from: Required<ScaleRange>, to: Required<ScaleRange>) =>\n (to.max - to.min) / (from.max - from.min);\n\n/** Build initial state from options, applying defaults and computing the ratio. */\nexport const parseInitialState = (opts?: ScaleOptions): ScaleState => {\n const initialRange: Pick<ScaleState, 'from' | 'to'> = {\n from: {\n min: 0,\n max: 1,\n ...opts?.from,\n },\n to: {\n min: 0,\n max: 1,\n ...opts?.to,\n },\n };\n\n return {\n ...initialRange,\n ratio: computeRatio(initialRange.from, initialRange.to),\n value: initialRange.to.min,\n };\n};\n\n/** Merge partial range updates into existing state, recomputing the ratio. */\nexport const updateStateFromOptions = (opts: ScaleOptions, prevState: ScaleState): ScaleState => {\n const { from, to } = opts;\n const updatedFrom: Required<ScaleRange> = {\n ...prevState.from,\n ...from,\n };\n const updatedTo: Required<ScaleRange> = {\n ...prevState.to,\n ...to,\n };\n\n return {\n ...prevState,\n from: updatedFrom,\n ratio: computeRatio(updatedFrom, updatedTo),\n to: updatedTo,\n value: clamp(prevState.value, updatedTo.min, updatedTo.max),\n };\n};\n\n/**\n * Linear map of values from one range to another, supports negative values and inversion.\n * @param opts - {@link ScaleOptions} for configuring input and output ranges.\n */\nexport class Scale {\n state: ScaleState;\n\n static scale(n: number, opts?: ScaleOptions) {\n return new Scale(opts).scale(n);\n }\n\n constructor(opts?: ScaleOptions) {\n this.state = parseInitialState(opts);\n }\n\n setRanges(opts: ScaleOptions) {\n this.state = updateStateFromOptions(opts, this.state);\n }\n\n reset(opts: ScaleOptions) {\n this.state = parseInitialState(opts);\n }\n\n value() {\n return this.state.value;\n }\n\n scale(n: number) {\n const { from, to, ratio } = this.state;\n const updates = to.min + (clamp(n, from.min, from.max) - from.min) * ratio;\n\n this.state.value = updates;\n\n return updates;\n }\n}\n","type InnerNow = () => number;\n\n/** Resolve the best available clock once at module load. */\nconst innerNow = ((): InnerNow => {\n // Browser or modern Node (>= 16)\n if (typeof performance !== 'undefined' && 'now' in performance) {\n return () => performance.now();\n }\n\n // Older Node — use process.hrtime, offset from first call\n if (typeof process === 'object' && process.toString() === '[object process]') {\n const ts = () => {\n const hr = process.hrtime();\n return hr[0] * 1e9 + hr[1];\n };\n const initialNow = ts();\n return () => (ts() - initialNow) / 1e6;\n }\n\n // Fallback — Date.now with manual offset\n const initialNow = Date.now();\n return () => Date.now() - initialNow;\n})();\n\n/**\n * Cross-environment high-resolution timestamp (performance.now polyfill).\n * @returns Elapsed milliseconds since initialization.\n */\nexport function now(): number {\n return innerNow();\n}\n","import { Scale } from './Scale';\nimport { now } from './now';\n\n/** Snapshot of an Env's internal state. */\nexport type EnvState = {\n duration: number;\n from: number;\n prev: number;\n to: number;\n totalElapsed: number;\n value: number;\n};\n\n/** Options for configuring an Env envelope. */\nexport type EnvOptions = {\n /** Duration of the envelope in milliseconds. */\n duration: number;\n /** Starting value. Defaults to 0. */\n from?: number;\n /** Ending value. Defaults to 1. */\n to?: number;\n};\n\nexport const parseOptions = (opts?: EnvOptions): Required<EnvOptions> => {\n return {\n duration: 0,\n from: 0,\n to: 1,\n ...opts,\n };\n};\n\nconst getInitialState = ({ from, to, duration }: Required<EnvOptions>): EnvState => {\n return {\n duration,\n from,\n prev: now(),\n to,\n totalElapsed: 0,\n value: from,\n };\n};\n\nexport const updateStateFromOptions = (\n opts: EnvOptions | undefined,\n prevState: EnvState\n): EnvState => {\n const { from, to, duration } = {\n ...prevState,\n ...opts,\n };\n\n return {\n ...prevState,\n duration,\n from,\n to,\n totalElapsed: 0,\n };\n};\n\n/**\n * Linear envelope which interpolates between two values over a duration. Useful for audio envelopes, transitions, and animations.\n * @param opts - {@link EnvOptions} for configuring the envelope.\n */\nexport class Env {\n state: EnvState;\n protected _interpolator: Scale;\n\n constructor(opts: EnvOptions) {\n const { from, to, duration } = parseOptions(opts);\n\n this.state = getInitialState({ from, to, duration });\n this._interpolator = new Scale({\n from: {\n min: 0,\n max: duration,\n },\n to: {\n min: from,\n max: to,\n },\n });\n }\n\n setDuration(duration: number) {\n const { to, totalElapsed } = this.state;\n\n this.state = {\n ...this.state,\n ...(duration <= totalElapsed\n ? {\n duration,\n value: to,\n }\n : { duration }),\n };\n }\n\n reset(opts?: EnvOptions) {\n const updates = updateStateFromOptions(opts, this.state);\n\n this.state = {\n ...updates,\n prev: now(),\n value: updates.from,\n };\n this._interpolator.setRanges({\n from: {\n min: 0,\n max: updates.duration,\n },\n to: {\n min: updates.from,\n max: updates.to,\n },\n });\n }\n\n done() {\n return this.state.duration <= this.state.totalElapsed;\n }\n\n value() {\n const { to, value } = this.state;\n\n if (this.done()) {\n return to;\n }\n\n return value;\n }\n\n next() {\n if (this.done()) {\n return this.value();\n }\n\n const { prev, totalElapsed: prevTotalElapsed } = this.state;\n\n const curr = now();\n const tickInterval = curr - prev;\n const totalElapsed = prevTotalElapsed + tickInterval;\n const updates = this._interpolator.scale(totalElapsed);\n\n this.state.prev = curr;\n this.state.totalElapsed = totalElapsed;\n this.state.value = updates;\n\n return updates;\n }\n}\n","import { Scale } from './Scale';\nimport { clamp } from './clamp';\nimport { now } from './now';\n\nexport const SINE_PERIOD = Math.PI * 2 - 0.0001;\n\n/** Options for configuring a Sine oscillator. */\nexport type SineOptions = {\n /** Duration of one full cycle in milliseconds. */\n duration: number;\n};\n\n/** Snapshot of a Sine oscillator's internal state. */\nexport type SineState = {\n cycle: number;\n duration: number;\n prev: number;\n totalElapsed: number;\n value: number;\n};\n\nconst getInitialState = (duration: number): SineState => ({\n cycle: 0,\n duration,\n prev: now(),\n totalElapsed: 0,\n value: 0,\n});\n\n/**\n * Time-based sine wave oscillator that outputs values between -1 and 1.\n * @param opts - {@link SineOptions} for configuring the oscillator.\n */\nexport class Sine {\n state: SineState;\n protected _interpolator: Scale;\n\n constructor(opts: SineOptions) {\n const { duration } = opts;\n\n this._interpolator = new Scale({\n from: {\n min: 0,\n max: duration,\n },\n to: {\n min: 0,\n max: SINE_PERIOD,\n },\n });\n this.state = getInitialState(duration);\n }\n\n setDuration(duration: number) {\n this.state = {\n ...this.state,\n duration,\n };\n }\n\n reset(opts?: SineOptions) {\n const { duration } = {\n ...this.state,\n ...opts,\n };\n\n this.state = getInitialState(duration);\n }\n\n value() {\n return this.state.value;\n }\n\n next() {\n const { cycle, duration, prev, totalElapsed: prevTotalElapsed } = this.state;\n const curr = now();\n const tickInterval = curr - prev;\n const totalElapsed = prevTotalElapsed + tickInterval;\n\n const updates = clamp(Math.sin(this._interpolator.scale(totalElapsed)), -1, 1);\n\n if (cycle >= duration) {\n this.state.cycle = 0;\n } else {\n this.state.cycle = cycle + tickInterval;\n }\n\n this.state.prev = curr;\n this.state.totalElapsed = totalElapsed;\n this.state.value = updates;\n\n return updates;\n }\n}\n","export const SIXTY_FPS = 1000 / 60;\nexport const MS_IN_SECOND = 1000;\nexport const MS_IN_MINUTE = 60 * MS_IN_SECOND;\nexport const MS_IN_HOUR = MS_IN_MINUTE * 60;\n","import { SIXTY_FPS } from './constants';\nimport { now } from './now';\n\n/** Snapshot of a running timer's internal state. */\nexport type TimerState = {\n initialTime: number;\n isRunning: boolean;\n iterations: number;\n prev: number;\n tickInterval: number;\n time: number;\n totalElapsed: number;\n};\n\nexport const getInitialState = (initialTime: number): TimerState => {\n return {\n initialTime,\n isRunning: false,\n iterations: -1,\n prev: 0,\n tickInterval: 0,\n time: initialTime,\n totalElapsed: 0,\n };\n};\n\nexport const processTimerState = (state: TimerState): TimerState | null => {\n const { time, prev, totalElapsed, iterations } = state;\n const curr = now();\n\n if (iterations === -1) {\n return {\n ...state,\n prev: curr,\n iterations: 0,\n };\n }\n\n const tickInterval = curr - prev;\n\n if (tickInterval <= time) {\n return null;\n }\n\n return {\n ...state,\n iterations: iterations + 1,\n prev: curr,\n tickInterval,\n totalElapsed: totalElapsed + tickInterval,\n };\n};\n\n/** Options for configuring a Metro timer. */\nexport type MetroOptions = {\n /** Interval between ticks in milliseconds. Defaults to ~16.67ms (60fps). */\n time?: number;\n};\n\nexport const parseOptions = (opts?: MetroOptions): Required<MetroOptions> => {\n return {\n time: SIXTY_FPS,\n ...opts,\n };\n};\n\n/** Callback invoked on each timer tick with the timer instance. */\nexport type TimerCallback<T extends Metro> = (timer: T) => void;\n\n/**\n * High-resolution recursive timer with variable interval, provides runtime metrics via callback for time-based calculations.\n * @param callback - {@link TimerCallback} called on each tick with the timer instance.\n * @param opts - {@link MetroOptions} for configuring the timer interval.\n */\nexport class Metro {\n state: TimerState;\n protected _listeners: TimerCallback<Metro>[];\n declare protected _timerId: ReturnType<typeof setTimeout> | number;\n\n constructor(callback: TimerCallback<Metro>, opts?: MetroOptions) {\n const { time } = parseOptions(opts);\n this.state = getInitialState(time);\n this._listeners = [callback];\n }\n\n protected asyncHandler(callback: () => void) {\n this._timerId = setTimeout(callback, SIXTY_FPS);\n }\n\n protected clearAsyncHandler() {\n clearTimeout(this._timerId);\n }\n\n stop = () => {\n const { totalElapsed } = this.state;\n this.reset();\n this.clearAsyncHandler();\n\n return totalElapsed;\n };\n\n reset = () => {\n const { initialTime } = this.state;\n this.state = getInitialState(initialTime);\n };\n\n setTime = (updatedTime = this.state.time) => {\n const time = Math.max(updatedTime, 0);\n\n this.state = {\n ...this.state,\n time,\n initialTime: time,\n };\n };\n\n run = () => {\n if (this.state.isRunning) {\n this.stop();\n }\n\n this.state = {\n ...this.state,\n isRunning: true,\n prev: now(),\n };\n\n const tick = () => {\n const updates = processTimerState(this.state);\n\n if (updates) {\n this.state = updates;\n this._listeners.forEach((listener) => {\n listener(this);\n });\n }\n\n if (this.state.isRunning) {\n this.asyncHandler(tick);\n }\n };\n\n tick();\n };\n}\n","import { MS_IN_SECOND, MS_IN_MINUTE, MS_IN_HOUR } from './constants';\n\nexport type FPS = 15 | 30 | 60;\n\nexport enum TimeFormat {\n FPS = 'fps',\n HOURS = 'h',\n HZ = 'hz',\n MILLISECONDS = 'ms',\n MINUTES = 'm',\n SECONDS = 's',\n}\n\nexport type AvailableTimeFormats = `${TimeFormat}`;\n\nexport const FORMAT_IDENTIFIERS: AvailableTimeFormats[] = [\n TimeFormat.FPS,\n TimeFormat.HOURS,\n TimeFormat.HZ,\n TimeFormat.MILLISECONDS,\n TimeFormat.MINUTES,\n TimeFormat.SECONDS,\n]\n // Desc length sort so that `ms` matches prior to `m`\n .sort((a, b) => b.length - a.length);\n\ntype FormatGetter = (val: number) => number;\n\nconst FORMATTERS = new Map<AvailableTimeFormats, FormatGetter>([\n [TimeFormat.FPS, (val: number) => MS_IN_SECOND / val],\n [TimeFormat.HOURS, (val: number) => val * MS_IN_HOUR],\n [TimeFormat.HZ, (val: number) => (1 / val) * MS_IN_SECOND],\n [TimeFormat.MILLISECONDS, (val: number) => val],\n [TimeFormat.MINUTES, (val: number) => val * MS_IN_MINUTE],\n [TimeFormat.SECONDS, (val: number) => val * MS_IN_SECOND],\n]);\n\nconst sanitizeStringVal = (val: string) => val.toLocaleLowerCase().trim();\n\nconst parseStringValAndFormat = (val: string) => {\n for (let i = 0; i < FORMAT_IDENTIFIERS.length; i += 1) {\n const format = FORMAT_IDENTIFIERS[i];\n\n if (val.includes(format)) {\n const value = Number(val.replace(' ', '').replace(format, ''));\n\n return {\n format,\n value,\n };\n }\n }\n\n return {\n format: undefined,\n value: undefined,\n };\n};\n\nexport function ms(val: string | null | undefined): number | undefined;\nexport function ms(\n val: string | number | null | undefined,\n format?: AvailableTimeFormats | TimeFormat\n): number | undefined;\n\n/**\n * Converts time format strings or numeric values to their corresponding value in milliseconds.\n * @param val - A string like `'60fps'`, `'2s'`, or a numeric value.\n * @param format - Explicit time format when `val` is a number.\n * @returns Milliseconds, or undefined if the input is invalid.\n */\nexport function ms(\n val: string | number | null | undefined,\n format?: AvailableTimeFormats | TimeFormat\n): number | undefined {\n let parsedValue: number | null | undefined = null;\n let parsedFormat: AvailableTimeFormats = format || TimeFormat.MILLISECONDS;\n\n if (typeof val === 'string') {\n const parsed = parseStringValAndFormat(sanitizeStringVal(val));\n\n if (typeof parsed.value !== 'undefined') {\n parsedValue = parsed.value;\n }\n\n if (parsed.format) {\n parsedFormat = parsed.format;\n }\n } else {\n parsedValue = val;\n }\n\n if (typeof parsedValue === 'undefined' || parsedValue === null || Number.isNaN(parsedValue)) {\n return undefined;\n }\n\n const formatter = FORMATTERS.get(parsedFormat);\n\n if (!formatter) {\n return undefined;\n }\n\n return formatter(parsedValue);\n}\n","import { Metro, type TimerCallback, type MetroOptions } from './Metro';\nimport { ms, TimeFormat, type FPS } from './ms';\n\n/** Options for configuring a Frames timer. */\nexport type FramesOptions = {\n /** Target frames per second (15, 30, or 60). Defaults to 60. */\n fps: FPS;\n};\n\nexport const DEFAULT_FPS: FPS = 60;\n\nexport const parseOptions = (opts?: FramesOptions): MetroOptions => {\n const { fps } = {\n fps: DEFAULT_FPS,\n ...opts,\n };\n\n return {\n time: ms(fps, TimeFormat.FPS),\n };\n};\n\n/**\n * Animation-loop timer that uses requestAnimationFrame when available.\n * @param callback - {@link TimerCallback} called on each frame tick.\n * @param opts - {@link FramesOptions} for configuring the target frame rate.\n */\nexport class Frames extends Metro {\n declare protected _timerId: ReturnType<typeof requestAnimationFrame>;\n\n constructor(callback: TimerCallback<Frames>, opts?: FramesOptions) {\n super(() => callback(this), parseOptions(opts));\n }\n\n protected asyncHandler(callback: () => void) {\n if (typeof window === 'undefined' || !('requestAnimationFrame' in window)) {\n super.asyncHandler(callback);\n } else {\n this._timerId = requestAnimationFrame(callback);\n }\n }\n\n protected clearAsyncHandler() {\n if (typeof window === 'undefined' || !('cancelAnimationFrame' in window)) {\n super.clearAsyncHandler();\n } else {\n cancelAnimationFrame(this._timerId);\n }\n }\n\n setFPS = (fps = DEFAULT_FPS) => {\n this.setTime(ms(fps, TimeFormat.FPS));\n };\n}\n","import { clamp } from './clamp';\n\n/**\n * Scales 0...1 by Euler's number to produce a natural feeling curve.\n * @param n - Input value (clamped to 0-1).\n * @returns The exponentially scaled value.\n */\nexport function expo(n: number): number {\n return Math.pow(clamp(n, 0, 1), Math.E);\n}\n","/**\n * Promisified setTimeout.\n * @param time - Delay in milliseconds.\n * @returns A promise that resolves after the delay.\n */\nexport function wait(time: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, time));\n}\n"],"mappings":"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","names":["parseOptions","initialNow","parseOptions","updateStateFromOptions","getInitialState","getInitialState","parseOptions","TimeFormat","parseOptions"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prtcl/plonk",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Tiny library that provides timers, envelopes, and random generators",
5
5
  "author": "Cory O'Brien <cory@prtcl.cc>",
6
6
  "license": "MIT",