import { effectScope, type EffectScope } from "vue"; export function isPosInt(maybePosInt: number): boolean { return (maybePosInt | 0) === maybePosInt && maybePosInt > 0; } export function greatestCommonDivisor(a: number, b: number): number { while (b !== 0) { const temp = b; b = a % b; a = temp; } return a; } export class Bound { constructor() { for (const propertyKey of Object.getOwnPropertyNames(Object.getPrototypeOf(this))) { // @ts-ignore const method = this[propertyKey]; if (method instanceof Function) { // @ts-ignore this[propertyKey] = (method as Function).bind(this); } } } } /** * Creates a constructor for a class that may contain vue reactive primitives, setting up an effect scope to capture * all effects, assigning a destroy function that will stop the effect scope and clean up effects. * * Wrapped classes containing reactive effects can be easily used without tying their lifetimes to a component instance * or the global application. This is effectively the equivalent of heap allocation instead of stack allocation. * * Advantages: * - less boilerplate (no need to remember to return all public methods and properties from a composable) * - makes it possible to manange reactive lifetimes that are entirely independent of UI state, such as objects that * are constantly instantiated and destroyed that manage their own reactive states, that need to be referenced * globally by multiple parts of a complex UI. * - The class syntax makes it easy to reference properites and functions before their declaration, avoiding annoying * forward declarations. */ export class EffectScoped { private effectScope?: EffectScope; /** * Creates an effectscoped version of the class */ static asScoped EffectScoped>(this: C, ...args: ConstructorParameters): InstanceType { const scope = effectScope(true); const instance = scope.run(() => new this(...args)) ?? null; if (instance === null) { throw new Error(`Failed to instantiate class ${ this.constructor.name }.`); } instance.effectScope = scope; return instance as InstanceType; } onDestroy() {} destroy() { this.onDestroy(); this.effectScope?.stop(); } }