@cymohanshu/runtime 0.1.0

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/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@cymohanshu/runtime",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./src/index.ts",
10
+ "import": "./src/index.ts"
11
+ },
12
+ "./vue": {
13
+ "types": "./src/adapters/vue/index.ts",
14
+ "import": "./src/adapters/vue/index.ts"
15
+ }
16
+ },
17
+ "files": [
18
+ "src",
19
+ "!src/__tests__"
20
+ ],
21
+ "publishConfig": {
22
+ "registry": "https://registry.npmjs.org/",
23
+ "access": "public"
24
+ },
25
+ "peerDependencies": {
26
+ "vue": "^3.5.0"
27
+ }
28
+ }
@@ -0,0 +1,41 @@
1
+ import type { RuntimeBrowser, RuntimeEnv, RuntimeInputMode, RuntimePlatform } from '../types';
2
+
3
+ export function detectBrowser(userAgent: string): RuntimeBrowser {
4
+ const ua = userAgent.toLowerCase();
5
+ if (ua.includes('edg/')) return 'edge';
6
+ if (ua.includes('chrome/') || ua.includes('chromium/')) return 'chrome';
7
+ if (ua.includes('safari/') && !ua.includes('chrome/')) return 'safari';
8
+ return 'unknown';
9
+ }
10
+
11
+ export function detectPlatform(win: Window): RuntimePlatform {
12
+ const ua = win.navigator.userAgent.toLowerCase();
13
+ if (win.self !== win.top) return 'webview';
14
+ if (ua.includes('tablet') || ua.includes('ipad')) return 'tablet';
15
+ if (ua.includes('whiteboard')) return 'whiteboard';
16
+ return 'desktop';
17
+ }
18
+
19
+ export function detectInputMode(win: Window): RuntimeInputMode {
20
+ const nav = win.navigator as Navigator & { maxTouchPoints?: number };
21
+ const hasTouch = (nav.maxTouchPoints ?? 0) > 0;
22
+ const hasPointer = 'PointerEvent' in win;
23
+
24
+ if (hasTouch && hasPointer) return 'mixed';
25
+ if (hasTouch) return 'touch';
26
+ return 'mouse';
27
+ }
28
+
29
+ export function detectRuntimeEnv(win: Window): RuntimeEnv {
30
+ const documentElement = win.document;
31
+ return {
32
+ platform: detectPlatform(win),
33
+ inputMode: detectInputMode(win),
34
+ browser: detectBrowser(win.navigator.userAgent),
35
+ dpr: win.devicePixelRatio || 1,
36
+ supportsPointerEvent: 'PointerEvent' in win,
37
+ supportsFullscreen:
38
+ 'requestFullscreen' in documentElement.documentElement &&
39
+ 'fullscreenElement' in documentElement,
40
+ };
41
+ }
@@ -0,0 +1,54 @@
1
+ import { createApp, type App as VueApp, type Component } from 'vue';
2
+ import type { CoursewareModule, RuntimeContext } from '../../types';
3
+ import { runtimeContextKey } from './runtimeContext';
4
+
5
+ export interface VueCoursewareExpose {
6
+ resetCourseware?: () => void | Promise<void>;
7
+ pauseCourseware?: () => void | Promise<void>;
8
+ resumeCourseware?: () => void | Promise<void>;
9
+ }
10
+
11
+ export interface VueCoursewareOptions {
12
+ rootClassName?: string;
13
+ }
14
+
15
+ export function createVueCourseware(
16
+ rootComponent: Component,
17
+ options: VueCoursewareOptions = {},
18
+ ): CoursewareModule {
19
+ const rootClassName = options.rootClassName ?? 'courseware-root';
20
+ let app: VueApp<Element> | undefined;
21
+ let root: HTMLElement | undefined;
22
+ let exposed: VueCoursewareExpose | undefined;
23
+
24
+ return {
25
+ mount(context: RuntimeContext): void {
26
+ root = document.createElement('div');
27
+ root.className = rootClassName;
28
+ context.container.replaceChildren(root);
29
+
30
+ app = createApp(rootComponent);
31
+ app.provide(runtimeContextKey, context);
32
+ exposed = app.mount(root) as VueCoursewareExpose;
33
+ },
34
+
35
+ unmount(): void {
36
+ app?.unmount();
37
+ app = undefined;
38
+ root = undefined;
39
+ exposed = undefined;
40
+ },
41
+
42
+ async reset(): Promise<void> {
43
+ await exposed?.resetCourseware?.();
44
+ },
45
+
46
+ async pause(): Promise<void> {
47
+ await exposed?.pauseCourseware?.();
48
+ },
49
+
50
+ async resume(): Promise<void> {
51
+ await exposed?.resumeCourseware?.();
52
+ },
53
+ };
54
+ }
@@ -0,0 +1,3 @@
1
+ export { createVueCourseware } from './createVueCourseware';
2
+ export type { VueCoursewareExpose, VueCoursewareOptions } from './createVueCourseware';
3
+ export { runtimeContextKey } from './runtimeContext';
@@ -0,0 +1,4 @@
1
+ import type { InjectionKey } from 'vue';
2
+ import type { RuntimeContext } from '../../types';
3
+
4
+ export const runtimeContextKey: InjectionKey<RuntimeContext> = Symbol('RuntimeContext');
@@ -0,0 +1,100 @@
1
+ import { detectRuntimeEnv } from './adapters/browserAdapter';
2
+ import { createAssetService } from './services/assets';
3
+ import { createEventBus } from './services/eventBus';
4
+ import { createInputService } from './services/input';
5
+ import { createLogger } from './services/logger';
6
+ import { createStorageService } from './services/storage';
7
+ import { createViewportService } from './services/viewport';
8
+ import type { CoursewareModule, Runtime, RuntimeContext, RuntimeOptions } from './types';
9
+
10
+ export function createRuntime(options: RuntimeOptions): Runtime {
11
+ const windowRef = options.windowRef ?? window;
12
+ const logger = options.logger ?? createLogger();
13
+ let currentModule: CoursewareModule | undefined;
14
+
15
+ const input = createInputService({
16
+ root: options.container,
17
+ windowRef,
18
+ });
19
+ const viewport = createViewportService({
20
+ container: options.container,
21
+ designWidth: options.designWidth,
22
+ designHeight: options.designHeight,
23
+ windowRef,
24
+ });
25
+
26
+ const context: RuntimeContext = {
27
+ container: options.container,
28
+ env: detectRuntimeEnv(windowRef),
29
+ input,
30
+ viewport,
31
+ assets: createAssetService(options.assetBaseUrl),
32
+ storage: createStorageService(windowRef.localStorage, options.storagePrefix),
33
+ logger,
34
+ events: createEventBus(),
35
+ };
36
+
37
+ const handleRuntimeError = (event: ErrorEvent): void => {
38
+ logger.error('Unhandled runtime error.', event.error ?? event.message);
39
+ context.events.emit('runtime:error', event.error ?? event.message);
40
+ };
41
+
42
+ const handleUnhandledRejection = (event: PromiseRejectionEvent): void => {
43
+ logger.error('Unhandled runtime rejection.', event.reason);
44
+ context.events.emit('runtime:error', event.reason);
45
+ };
46
+
47
+ windowRef.addEventListener('error', handleRuntimeError);
48
+ windowRef.addEventListener('unhandledrejection', handleUnhandledRejection);
49
+
50
+ return {
51
+ context,
52
+
53
+ async mount(module: CoursewareModule): Promise<void> {
54
+ if (currentModule) await this.unmount();
55
+ currentModule = module;
56
+
57
+ try {
58
+ await module.mount(context);
59
+ } catch (error) {
60
+ logger.error('Failed to mount courseware module.', error);
61
+ context.events.emit('runtime:error', error);
62
+ throw error;
63
+ }
64
+ },
65
+
66
+ async unmount(): Promise<void> {
67
+ if (!currentModule) return;
68
+ const module = currentModule;
69
+ currentModule = undefined;
70
+ await module.unmount();
71
+ },
72
+
73
+ async reset(): Promise<void> {
74
+ await currentModule?.reset?.();
75
+ },
76
+
77
+ async destroy(): Promise<void> {
78
+ await this.unmount();
79
+ input.destroy();
80
+ viewport.destroy();
81
+ context.events.clear();
82
+ windowRef.removeEventListener('error', handleRuntimeError);
83
+ windowRef.removeEventListener('unhandledrejection', handleUnhandledRejection);
84
+ },
85
+ };
86
+ }
87
+
88
+ export type {
89
+ AssetService,
90
+ CoursewareModule,
91
+ EventBus,
92
+ InputService,
93
+ LoggerService,
94
+ Runtime,
95
+ RuntimeContext,
96
+ RuntimeEnv,
97
+ RuntimeOptions,
98
+ StorageService,
99
+ ViewportService,
100
+ } from './types';
package/src/index.ts ADDED
@@ -0,0 +1,22 @@
1
+ export { createRuntime } from './createRuntime';
2
+ export type {
3
+ AssetService,
4
+ CoursewareModule,
5
+ Dispose,
6
+ EventBus,
7
+ InputService,
8
+ LoggerService,
9
+ Point,
10
+ PointerHandler,
11
+ PointerPayload,
12
+ ResizeHandler,
13
+ Runtime,
14
+ RuntimeBrowser,
15
+ RuntimeContext,
16
+ RuntimeEnv,
17
+ RuntimeInputMode,
18
+ RuntimeOptions,
19
+ RuntimePlatform,
20
+ StorageService,
21
+ ViewportService,
22
+ } from './types';
@@ -0,0 +1,42 @@
1
+ import type { AssetService } from '../types';
2
+
3
+ export function createAssetService(baseUrl = '', fetcher: typeof fetch = fetch): AssetService {
4
+ const normalizedBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
5
+
6
+ const resolve = (path: string): string => {
7
+ if (/^(https?:)?\/\//.test(path) || path.startsWith('data:') || path.startsWith('blob:')) {
8
+ return path;
9
+ }
10
+
11
+ const normalizedPath = path.startsWith('/') ? path : `/${path}`;
12
+ return `${normalizedBase}${normalizedPath}`;
13
+ };
14
+
15
+ return {
16
+ resolve,
17
+
18
+ loadImage(url: string): Promise<HTMLImageElement> {
19
+ return new Promise((resolveImage, reject) => {
20
+ const image = new Image();
21
+ image.onload = () => resolveImage(image);
22
+ image.onerror = () => reject(new Error(`Failed to load image: ${url}`));
23
+ image.src = resolve(url);
24
+ });
25
+ },
26
+
27
+ async loadSvg(url: string): Promise<string> {
28
+ const response = await fetcher(resolve(url));
29
+ if (!response.ok) throw new Error(`Failed to load svg: ${url}`);
30
+ return response.text();
31
+ },
32
+
33
+ async preload(urls: string[]): Promise<void> {
34
+ await Promise.all(
35
+ urls.map((url) => {
36
+ if (url.toLowerCase().endsWith('.svg')) return this.loadSvg(url);
37
+ return this.loadImage(url);
38
+ }),
39
+ );
40
+ },
41
+ };
42
+ }
@@ -0,0 +1,28 @@
1
+ import type { Dispose, EventBus } from '../types';
2
+
3
+ export function createEventBus(): EventBus {
4
+ const handlers = new Map<string, Set<(payload: unknown) => void>>();
5
+
6
+ return {
7
+ on<T>(eventName: string, handler: (payload: T) => void): Dispose {
8
+ const bucket = handlers.get(eventName) ?? new Set();
9
+ bucket.add(handler as (payload: unknown) => void);
10
+ handlers.set(eventName, bucket);
11
+
12
+ return () => {
13
+ bucket.delete(handler as (payload: unknown) => void);
14
+ if (bucket.size === 0) handlers.delete(eventName);
15
+ };
16
+ },
17
+
18
+ emit<T>(eventName: string, payload: T): void {
19
+ for (const handler of handlers.get(eventName) ?? []) {
20
+ handler(payload);
21
+ }
22
+ },
23
+
24
+ clear(): void {
25
+ handlers.clear();
26
+ },
27
+ };
28
+ }
@@ -0,0 +1,115 @@
1
+ import type { Dispose, InputService, Point, PointerHandler, PointerPayload } from '../types';
2
+
3
+ interface InputOptions {
4
+ root: HTMLElement;
5
+ windowRef: Window;
6
+ }
7
+
8
+ type InputEventName = 'pointerdown' | 'pointermove' | 'pointerup';
9
+
10
+ export function createInputService(options: InputOptions): InputService {
11
+ const { root, windowRef } = options;
12
+ const disposers: Dispose[] = [];
13
+
14
+ const toPayload = (event: Event): PointerPayload => {
15
+ if (isPointerEvent(event)) {
16
+ return {
17
+ id: String(event.pointerId),
18
+ x: event.clientX,
19
+ y: event.clientY,
20
+ type: normalizePointerType(event.pointerType),
21
+ target: event.target,
22
+ originalEvent: event,
23
+ };
24
+ }
25
+
26
+ if (isTouchEvent(event)) {
27
+ const touch = event.changedTouches[0] ?? event.touches[0];
28
+ return {
29
+ id: touch ? String(touch.identifier) : 'touch',
30
+ x: touch?.clientX ?? 0,
31
+ y: touch?.clientY ?? 0,
32
+ type: 'touch',
33
+ target: event.target,
34
+ originalEvent: event,
35
+ };
36
+ }
37
+
38
+ const mouseEvent = event as MouseEvent;
39
+ return {
40
+ id: 'mouse',
41
+ x: mouseEvent.clientX,
42
+ y: mouseEvent.clientY,
43
+ type: 'mouse',
44
+ target: event.target,
45
+ originalEvent: event,
46
+ };
47
+ };
48
+
49
+ const bind = (eventName: InputEventName, fallbackNames: string[], handler: PointerHandler): Dispose => {
50
+ const listener = (event: Event): void => {
51
+ handler(toPayload(event));
52
+ };
53
+
54
+ if (typeof (windowRef as Window & { PointerEvent?: typeof PointerEvent }).PointerEvent === 'function') {
55
+ root.addEventListener(eventName, listener);
56
+ const dispose = () => root.removeEventListener(eventName, listener);
57
+ disposers.push(dispose);
58
+ return dispose;
59
+ }
60
+
61
+ for (const fallbackName of fallbackNames) {
62
+ root.addEventListener(fallbackName, listener, { passive: false });
63
+ }
64
+
65
+ const dispose = () => {
66
+ for (const fallbackName of fallbackNames) {
67
+ root.removeEventListener(fallbackName, listener);
68
+ }
69
+ };
70
+ disposers.push(dispose);
71
+ return dispose;
72
+ };
73
+
74
+ return {
75
+ onPointerDown(handler: PointerHandler): Dispose {
76
+ return bind('pointerdown', ['mousedown', 'touchstart'], handler);
77
+ },
78
+
79
+ onPointerMove(handler: PointerHandler): Dispose {
80
+ return bind('pointermove', ['mousemove', 'touchmove'], handler);
81
+ },
82
+
83
+ onPointerUp(handler: PointerHandler): Dispose {
84
+ return bind('pointerup', ['mouseup', 'touchend', 'touchcancel'], handler);
85
+ },
86
+
87
+ getLocalPoint(event: Event, target: HTMLElement): Point {
88
+ const payload = toPayload(event);
89
+ const rect = target.getBoundingClientRect();
90
+ return {
91
+ x: payload.x - rect.left,
92
+ y: payload.y - rect.top,
93
+ };
94
+ },
95
+
96
+ destroy(): void {
97
+ for (const dispose of disposers.splice(0)) {
98
+ dispose();
99
+ }
100
+ },
101
+ };
102
+ }
103
+
104
+ function normalizePointerType(pointerType: string): 'mouse' | 'touch' | 'pen' {
105
+ if (pointerType === 'touch' || pointerType === 'pen') return pointerType;
106
+ return 'mouse';
107
+ }
108
+
109
+ function isPointerEvent(event: Event): event is PointerEvent {
110
+ return 'pointerId' in event && 'pointerType' in event;
111
+ }
112
+
113
+ function isTouchEvent(event: Event): event is TouchEvent {
114
+ return 'changedTouches' in event || 'touches' in event;
115
+ }
@@ -0,0 +1,25 @@
1
+ import type { LoggerService } from '../types';
2
+
3
+ export function createLogger(enabled = true): LoggerService {
4
+ const write = (level: 'info' | 'warn' | 'error', message: string, data?: unknown): void => {
5
+ if (!enabled) return;
6
+ const prefix = `[@cymohanshu/runtime] ${message}`;
7
+ if (data === undefined) {
8
+ console[level](prefix);
9
+ return;
10
+ }
11
+ console[level](prefix, data);
12
+ };
13
+
14
+ return {
15
+ info(message, data) {
16
+ write('info', message, data);
17
+ },
18
+ warn(message, data) {
19
+ write('warn', message, data);
20
+ },
21
+ error(message, error) {
22
+ write('error', message, error);
23
+ },
24
+ };
25
+ }
@@ -0,0 +1,26 @@
1
+ import type { StorageService } from '../types';
2
+
3
+ export function createStorageService(storage: Storage, prefix = 'courseware'): StorageService {
4
+ const namespaced = (key: string) => `${prefix}:${key}`;
5
+
6
+ return {
7
+ get<T>(key: string, fallback: T): T {
8
+ const raw = storage.getItem(namespaced(key));
9
+ if (raw == null) return fallback;
10
+
11
+ try {
12
+ return JSON.parse(raw) as T;
13
+ } catch {
14
+ return fallback;
15
+ }
16
+ },
17
+
18
+ set<T>(key: string, value: T): void {
19
+ storage.setItem(namespaced(key), JSON.stringify(value));
20
+ },
21
+
22
+ remove(key: string): void {
23
+ storage.removeItem(namespaced(key));
24
+ },
25
+ };
26
+ }
@@ -0,0 +1,81 @@
1
+ import type { Dispose, Point, ResizeHandler, ViewportService } from '../types';
2
+
3
+ interface ViewportOptions {
4
+ container: HTMLElement;
5
+ designWidth: number;
6
+ designHeight: number;
7
+ windowRef: Window;
8
+ }
9
+
10
+ export function createViewportService(options: ViewportOptions): ViewportService {
11
+ const { container, designWidth, designHeight, windowRef } = options;
12
+ const handlers = new Set<ResizeHandler>();
13
+ let scale = 1;
14
+ let size = { width: designWidth, height: designHeight };
15
+
16
+ const measure = (): void => {
17
+ const rect = container.getBoundingClientRect();
18
+ const width = rect.width || designWidth;
19
+ const height = rect.height || designHeight;
20
+ scale = Math.min(width / designWidth, height / designHeight);
21
+ size = { width, height };
22
+ container.style.setProperty('--runtime-scale', String(scale));
23
+ container.style.setProperty('--runtime-design-width', `${designWidth}px`);
24
+ container.style.setProperty('--runtime-design-height', `${designHeight}px`);
25
+
26
+ for (const handler of handlers) {
27
+ handler({ ...size, scale });
28
+ }
29
+ };
30
+
31
+ const onWindowResize = (): void => measure();
32
+ windowRef.addEventListener('resize', onWindowResize);
33
+ measure();
34
+
35
+ return {
36
+ getSize() {
37
+ return size;
38
+ },
39
+
40
+ getScale() {
41
+ return scale;
42
+ },
43
+
44
+ toDesignPoint(point: Point): Point {
45
+ return {
46
+ x: point.x / scale,
47
+ y: point.y / scale,
48
+ };
49
+ },
50
+
51
+ onResize(handler: ResizeHandler): Dispose {
52
+ handlers.add(handler);
53
+ return () => {
54
+ handlers.delete(handler);
55
+ };
56
+ },
57
+
58
+ async requestFullscreen(): Promise<void> {
59
+ const fullscreenElement = container as HTMLElement & {
60
+ webkitRequestFullscreen?: () => Promise<void> | void;
61
+ };
62
+
63
+ if (fullscreenElement.requestFullscreen) {
64
+ await fullscreenElement.requestFullscreen();
65
+ return;
66
+ }
67
+
68
+ if (fullscreenElement.webkitRequestFullscreen) {
69
+ await fullscreenElement.webkitRequestFullscreen();
70
+ return;
71
+ }
72
+
73
+ throw new Error('Fullscreen is not supported in this environment.');
74
+ },
75
+
76
+ destroy(): void {
77
+ windowRef.removeEventListener('resize', onWindowResize);
78
+ handlers.clear();
79
+ },
80
+ };
81
+ }
package/src/types.ts ADDED
@@ -0,0 +1,110 @@
1
+ export type Dispose = () => void;
2
+
3
+ export interface Point {
4
+ x: number;
5
+ y: number;
6
+ }
7
+
8
+ export type RuntimePlatform = 'desktop' | 'tablet' | 'whiteboard' | 'webview';
9
+ export type RuntimeInputMode = 'mouse' | 'touch' | 'pen' | 'mixed';
10
+ export type RuntimeBrowser = 'chrome' | 'edge' | 'safari' | 'unknown';
11
+
12
+ export interface RuntimeEnv {
13
+ platform: RuntimePlatform;
14
+ inputMode: RuntimeInputMode;
15
+ browser: RuntimeBrowser;
16
+ dpr: number;
17
+ supportsPointerEvent: boolean;
18
+ supportsFullscreen: boolean;
19
+ }
20
+
21
+ export interface PointerPayload {
22
+ id: string;
23
+ x: number;
24
+ y: number;
25
+ type: 'mouse' | 'touch' | 'pen';
26
+ target?: EventTarget | null;
27
+ originalEvent: Event;
28
+ }
29
+
30
+ export type PointerHandler = (payload: PointerPayload) => void;
31
+ export type ResizeHandler = (size: { width: number; height: number; scale: number }) => void;
32
+
33
+ export interface InputService {
34
+ onPointerDown(handler: PointerHandler): Dispose;
35
+ onPointerMove(handler: PointerHandler): Dispose;
36
+ onPointerUp(handler: PointerHandler): Dispose;
37
+ getLocalPoint(event: Event, target: HTMLElement): Point;
38
+ destroy(): void;
39
+ }
40
+
41
+ export interface ViewportService {
42
+ getSize(): { width: number; height: number };
43
+ getScale(): number;
44
+ toDesignPoint(point: Point): Point;
45
+ onResize(handler: ResizeHandler): Dispose;
46
+ requestFullscreen(): Promise<void>;
47
+ destroy(): void;
48
+ }
49
+
50
+ export interface AssetService {
51
+ loadImage(url: string): Promise<HTMLImageElement>;
52
+ loadSvg(url: string): Promise<string>;
53
+ preload(urls: string[]): Promise<void>;
54
+ resolve(path: string): string;
55
+ }
56
+
57
+ export interface StorageService {
58
+ get<T>(key: string, fallback: T): T;
59
+ set<T>(key: string, value: T): void;
60
+ remove(key: string): void;
61
+ }
62
+
63
+ export interface LoggerService {
64
+ info(message: string, data?: unknown): void;
65
+ warn(message: string, data?: unknown): void;
66
+ error(message: string, error?: unknown): void;
67
+ }
68
+
69
+ export interface EventBus {
70
+ on<T>(eventName: string, handler: (payload: T) => void): Dispose;
71
+ emit<T>(eventName: string, payload: T): void;
72
+ clear(): void;
73
+ }
74
+
75
+ export interface RuntimeContext {
76
+ container: HTMLElement;
77
+ env: RuntimeEnv;
78
+ input: InputService;
79
+ viewport: ViewportService;
80
+ assets: AssetService;
81
+ storage: StorageService;
82
+ logger: LoggerService;
83
+ events: EventBus;
84
+ }
85
+
86
+ export interface CoursewareModule {
87
+ mount(context: RuntimeContext): void | Promise<void>;
88
+ unmount(): void | Promise<void>;
89
+ reset?(): void | Promise<void>;
90
+ pause?(): void | Promise<void>;
91
+ resume?(): void | Promise<void>;
92
+ }
93
+
94
+ export interface RuntimeOptions {
95
+ container: HTMLElement;
96
+ designWidth: number;
97
+ designHeight: number;
98
+ assetBaseUrl?: string;
99
+ storagePrefix?: string;
100
+ logger?: LoggerService;
101
+ windowRef?: Window;
102
+ }
103
+
104
+ export interface Runtime {
105
+ context: RuntimeContext;
106
+ mount(module: CoursewareModule): Promise<void>;
107
+ unmount(): Promise<void>;
108
+ reset(): Promise<void>;
109
+ destroy(): Promise<void>;
110
+ }