Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | 6x 2250x 2250x 483x 483x 483x 483x 483x 483x 483x 1775x 1768x 5x 5x 1763x 1763x 1763x 1763x 1767x 1767x 1767x 1766x 1762x 1x 1x 1763x 483x 4861x 4211x 271x 267x 267x 267x 268x 264x 264x 1290x 1x 8x 486x 481x 481x 481x 481x 481x | import { createDisposableScope } from './disposables';
import { createEventEmitter } from '../events/emitter';
import type {
RuntimeController,
SnapshotListener,
RuntimeEventListener,
RuntimeEventSource,
Unsubscribe,
} from './types';
export interface ControllerHost<TSnapshot extends object, TEvents extends object>
extends RuntimeController<TSnapshot>, RuntimeEventSource<TEvents> {
readonly resources: ReturnType<typeof createDisposableScope>;
readonly alive: () => boolean;
update(next: TSnapshot): boolean;
emit<TKey extends keyof TEvents>(type: TKey, detail: TEvents[TKey]): boolean;
}
const freezeSnapshot = <TSnapshot extends object>(snapshot: TSnapshot): Readonly<TSnapshot> => {
if (!Object.isFrozen(snapshot)) Object.freeze(snapshot);
return snapshot;
};
export function createControllerHost<TSnapshot extends object, TEvents extends object>(
initial: TSnapshot,
): ControllerHost<TSnapshot, TEvents> {
let current = freezeSnapshot(initial);
let destroyed = false;
let publishing = false;
let pending: TSnapshot | undefined;
const listeners = new Set<SnapshotListener<TSnapshot>>();
const events = createEventEmitter<TEvents>();
const resources = createDisposableScope();
const update = (next: TSnapshot): boolean => {
if (destroyed || Object.is(current, next)) return false;
if (publishing) {
pending = next;
return true;
}
publishing = true;
try {
let candidate: TSnapshot | undefined = next;
while (candidate) {
pending = undefined;
current = freezeSnapshot(candidate);
for (const listener of [...listeners]) listener(current);
candidate = pending;
}
return true;
} catch (error) {
pending = undefined;
throw error;
} finally {
publishing = false;
}
};
return {
resources,
alive: () => !destroyed,
getSnapshot: () => current,
subscribe(listener) {
if (destroyed) return () => undefined;
listeners.add(listener);
let active = true;
return () => {
if (!active) return;
active = false;
listeners.delete(listener);
};
},
update,
emit: events.emit,
on<TKey extends keyof TEvents>(
type: TKey,
listener: RuntimeEventListener<TEvents[TKey]>,
): Unsubscribe {
return destroyed ? () => undefined : events.on(type, listener);
},
off<TKey extends keyof TEvents>(
type: TKey,
listener: RuntimeEventListener<TEvents[TKey]>,
): void {
events.off(type, listener);
},
once<TKey extends keyof TEvents>(
type: TKey,
listener: RuntimeEventListener<TEvents[TKey]>,
): Unsubscribe {
return destroyed ? () => undefined : events.once(type, listener);
},
destroy() {
if (destroyed) return;
destroyed = true;
pending = undefined;
listeners.clear();
events.clear();
resources.dispose();
},
};
}
|