Created
July 11, 2023 18:17
-
-
Save aleclarson/5d4928641f07ac57b0bfec45e1d34adb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var e=e=>"function"==typeof e,t=()=>{},s=DEV&&"undefined"!=typeof __OBSERVABLE_HOOKS__&&__OBSERVABLE_HOOKS__,r=e=>e._value,i=1,h=1,o=r,n=Symbol.for("alien:refType"),u=class{constructor(e){this._value=e,this._version=0,this._observers=new Set}get _depth(){return 0}_isObserved(e,t){DEV&&s&&s.isObserved(this,e,t),t?this._observers.add(e):this._observers.delete(e)}get[n](){return"ReadonlyRef"}get value(){return o(this)}get version(){return this._version}peek(){return this._value}},d=class extends u{get[n](){return"Ref"}get value(){return super.value}set value(e){const t=this._value;e!==t&&(this._value=e,this._version=i,this._observers.forEach((s=>{s.observe(this,e,t)})),h===i&&O()&&(h++,Promise.resolve().then(y)))}set(t){return e(t)&&(t=t(this._value)),this.value=t,t}},a=Object.getOwnPropertyDescriptor(d.prototype,"value");Object.defineProperties(d.prototype,{0:a,1:{get(){return this.set.bind(this)}},[Symbol.iterator]:{value:function*(){yield this[0],yield this[1]}}});var v=Symbol("empty"),l=class extends u{constructor(e){super(v),this.compute=e,this._refs=null,this._observer=null}get _depth(){return this._observer?.depth??0}_isObserved(e,t){super._isObserved(e,t),t?this._observer||this._setupObserver():(this._value=v,this._observer?.dispose(),this._observer=null,this._refs=null)}_setupObserver(){const e=this._observer=new c(f);this._refs=e.refs,e.onUpdate=a.set.bind(this),e.update(this.compute)}_update(){this._refs=new Set;const e=o;let t;o=e=>(this._refs.add(e),e._value);try{this._version=i,this._value=this.compute()}catch(e){t=e}if(o=e,DEV&&s&&s.didUpdate(this,t,this._value),t)throw t;return this._value}get[n](){return"ComputedRef"}get value(){if(this._observer)return super.value;if(o!==r)return this._setupObserver(),super.value;if(this._value===v)return this._update();for(const e of this._refs)if(e._version>this._version)return this._update();return this._value}},_=e=>e,p=1,c=class{constructor(e=b){this.queue=e,this.id=p++,this.refs=new Set,this.version=0,this.depth=0,this.nextCompute=t,this.onUpdate=_}update(e){const t=new Set(this.refs);this.refs.clear(),this.depth=0;const r=o;let i,h;o=e=>(e._isObserved(this,!0),t.delete(e),this.refs.add(e),this.depth=Math.max(this.depth,e._depth+1),e._value);try{h=(this.nextCompute=e)()}catch(e){i=e}if(o=r,t.forEach((e=>{e._isObserved(this,!1)})),DEV&&s&&s.didUpdate(this,i,h),i)throw i;return this.onUpdate(h)}observe(e,t,r){DEV&&s&&s.observe(this,e,t,r),this.version!==i&&(this.version=i,this.queue.add(this))}dispose(){this.queue.delete(this),this.refs.forEach((e=>{e._isObserved(this,!1)}))}get destructor(){return this.dispose.bind(this)}};var f=new Set,b=new Set;function O(){return f.size+b.size>0}function y(){i=h;const e=DEV&&Error("Cycle detected");let t,r;const n=o;o=e=>(e._isObserved(t,!0),r.delete(e),t.refs.add(e),t.depth=Math.max(t.depth,e._depth+1),e._value);for(let i=0;O();i++){if(i>100)throw DEV&&e||Error("Cycle detected");const h=e=>{let i,h;r=new Set(e.refs),e.refs.clear(),e.depth=0,t=e;try{h=e.nextCompute(),e.onUpdate(h)}catch(e){console.error(i=e)}r.forEach((t=>{t._isObserved(e,!1)})),DEV&&s&&s.didUpdate(e,i,h)},o=[...f].sort(((e,t)=>t.depth-e.depth));f.clear(),o.forEach(h);const n=[...b].sort(((e,t)=>e.id-t.id));b.clear(),n.forEach(h)}o=n}var E=e=>new d(e),w=e=>new l(e);function m(t,s){const r=new c;if(e(t))r.update(t);else{const e=t,i=s;r.update((()=>{o(e)})),r.observe=(e,t,s)=>{r.onUpdate=i.bind(null,t,s,e)}}return r}function S(e){return!!e&&void 0!==e[n]}function g(e){const t=o;o=r;try{return e()}finally{o=t}}export{l as ComputedRef,c as Observer,u as ReadonlyRef,d as Ref,w as computed,S as isRef,m as observe,g as peek,E as ref}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// project/index.ts | |
var isFunction = (value) => typeof value === "function"; | |
var noop = () => { | |
}; | |
var hooks = DEV && typeof __OBSERVABLE_HOOKS__ !== "undefined" && __OBSERVABLE_HOOKS__; | |
var unseenAccess = (ref2) => ref2._value; | |
var currentVersion = 1; | |
var nextVersion = 1; | |
var access = unseenAccess; | |
var kRefType = Symbol.for("alien:refType"); | |
var ReadonlyRef = class { | |
constructor(_value) { | |
this._value = _value; | |
this._version = 0; | |
this._observers = /* @__PURE__ */ new Set(); | |
} | |
get _depth() { | |
return 0; | |
} | |
_isObserved(observer, isObserved) { | |
if (DEV && hooks) { | |
hooks.isObserved(this, observer, isObserved); | |
} | |
if (isObserved) { | |
this._observers.add(observer); | |
} else { | |
this._observers.delete(observer); | |
} | |
} | |
get [kRefType]() { | |
return "ReadonlyRef"; | |
} | |
get value() { | |
return access(this); | |
} | |
get version() { | |
return this._version; | |
} | |
peek() { | |
return this._value; | |
} | |
}; | |
var Ref = class extends ReadonlyRef { | |
get [kRefType]() { | |
return "Ref"; | |
} | |
get value() { | |
return super.value; | |
} | |
set value(newValue) { | |
const oldValue = this._value; | |
if (newValue !== oldValue) { | |
this._value = newValue; | |
this._version = currentVersion; | |
this._observers.forEach((observer) => { | |
observer.observe(this, newValue, oldValue); | |
}); | |
onObservedUpdate(); | |
} | |
} | |
set(arg) { | |
if (isFunction(arg)) { | |
arg = arg(this._value); | |
} | |
this.value = arg; | |
return arg; | |
} | |
}; | |
var valueProperty = Object.getOwnPropertyDescriptor(Ref.prototype, "value"); | |
Object.defineProperties(Ref.prototype, { | |
0: valueProperty, | |
1: { | |
get() { | |
return this.set.bind(this); | |
} | |
}, | |
[Symbol.iterator]: { | |
value: function* () { | |
yield this[0]; | |
yield this[1]; | |
} | |
} | |
}); | |
var emptySymbol = Symbol("empty"); | |
var ComputedRef = class extends ReadonlyRef { | |
constructor(compute) { | |
super(emptySymbol); | |
this.compute = compute; | |
this._refs = null; | |
this._observer = null; | |
} | |
get _depth() { | |
return this._observer?.depth ?? 0; | |
} | |
_isObserved(observer, isObserved) { | |
super._isObserved(observer, isObserved); | |
if (!isObserved) { | |
this._value = emptySymbol; | |
this._observer?.dispose(); | |
this._observer = null; | |
this._refs = null; | |
} else if (!this._observer) { | |
this._setupObserver(); | |
} | |
} | |
_setupObserver() { | |
const observer = this._observer = new Observer(computeQueue); | |
this._refs = observer.refs; | |
observer.onUpdate = valueProperty.set.bind(this); | |
observer.update(this.compute); | |
} | |
_update() { | |
this._refs = /* @__PURE__ */ new Set(); | |
const parentAccess = access; | |
access = (ref2) => { | |
this._refs.add(ref2); | |
return ref2._value; | |
}; | |
let error; | |
try { | |
this._version = currentVersion; | |
this._value = this.compute(); | |
} catch (e) { | |
error = e; | |
} | |
access = parentAccess; | |
if (DEV && hooks) { | |
hooks.didUpdate(this, error, this._value); | |
} | |
if (error) | |
throw error; | |
return this._value; | |
} | |
get [kRefType]() { | |
return "ComputedRef"; | |
} | |
get value() { | |
if (this._observer) { | |
return super.value; | |
} | |
if (access !== unseenAccess) { | |
this._setupObserver(); | |
return super.value; | |
} | |
if (this._value === emptySymbol) { | |
return this._update(); | |
} | |
for (const ref2 of this._refs) { | |
if (ref2._version > this._version) { | |
return this._update(); | |
} | |
} | |
return this._value; | |
} | |
}; | |
var passThrough = (result) => result; | |
var nextObserverId = 1; | |
var Observer = class { | |
constructor(queue = updateQueue) { | |
this.queue = queue; | |
this.id = nextObserverId++; | |
this.refs = /* @__PURE__ */ new Set(); | |
this.version = 0; | |
this.depth = 0; | |
this.nextCompute = noop; | |
this.onUpdate = passThrough; | |
} | |
update(compute) { | |
const oldRefs = new Set(this.refs); | |
this.refs.clear(); | |
this.depth = 0; | |
const parentAccess = access; | |
access = (ref2) => { | |
ref2._isObserved(this, true); | |
oldRefs.delete(ref2); | |
this.refs.add(ref2); | |
this.depth = Math.max(this.depth, ref2._depth + 1); | |
return ref2._value; | |
}; | |
let error; | |
let result; | |
try { | |
result = (this.nextCompute = compute)(); | |
} catch (e) { | |
error = e; | |
} | |
access = parentAccess; | |
oldRefs.forEach((ref2) => { | |
ref2._isObserved(this, false); | |
}); | |
if (DEV && hooks) { | |
hooks.didUpdate(this, error, result); | |
} | |
if (error) | |
throw error; | |
return this.onUpdate(result); | |
} | |
observe(ref2, newValue, oldValue) { | |
if (DEV && hooks) { | |
hooks.observe(this, ref2, newValue, oldValue); | |
} | |
if (this.version !== currentVersion) { | |
this.version = currentVersion; | |
this.queue.add(this); | |
} | |
} | |
dispose() { | |
this.queue.delete(this); | |
this.refs.forEach((ref2) => { | |
ref2._isObserved(this, false); | |
}); | |
} | |
get destructor() { | |
return this.dispose.bind(this); | |
} | |
}; | |
function onObservedUpdate() { | |
if (nextVersion === currentVersion && hasQueuedObservers()) { | |
nextVersion++; | |
Promise.resolve().then(computeNextVersion); | |
} | |
} | |
var computeQueue = /* @__PURE__ */ new Set(); | |
var updateQueue = /* @__PURE__ */ new Set(); | |
function hasQueuedObservers() { | |
return computeQueue.size + updateQueue.size > 0; | |
} | |
function computeNextVersion() { | |
currentVersion = nextVersion; | |
const devError = DEV && Error("Cycle detected"); | |
let currentObserver; | |
let oldRefs; | |
const parentAccess = access; | |
access = (ref2) => { | |
ref2._isObserved(currentObserver, true); | |
oldRefs.delete(ref2); | |
currentObserver.refs.add(ref2); | |
currentObserver.depth = Math.max(currentObserver.depth, ref2._depth + 1); | |
return ref2._value; | |
}; | |
for (let loops = 0; hasQueuedObservers(); loops++) { | |
if (loops > 100) { | |
throw DEV && devError || Error("Cycle detected"); | |
} | |
const update = (observer) => { | |
oldRefs = new Set(observer.refs); | |
observer.refs.clear(); | |
observer.depth = 0; | |
let error; | |
let result; | |
currentObserver = observer; | |
try { | |
result = observer.nextCompute(); | |
observer.onUpdate(result); | |
} catch (e) { | |
console.error(error = e); | |
} | |
oldRefs.forEach((ref2) => { | |
ref2._isObserved(observer, false); | |
}); | |
if (DEV && hooks) { | |
hooks.didUpdate(observer, error, result); | |
} | |
}; | |
const computedRefs = [...computeQueue].sort((a, b) => b.depth - a.depth); | |
computeQueue.clear(); | |
computedRefs.forEach(update); | |
const updatedObservers = [...updateQueue].sort((a, b) => a.id - b.id); | |
updateQueue.clear(); | |
updatedObservers.forEach(update); | |
} | |
access = parentAccess; | |
} | |
var ref = (value) => new Ref(value); | |
var computed = (compute) => new ComputedRef(compute); | |
function observe(arg1, arg2) { | |
const observer = new Observer(); | |
if (isFunction(arg1)) { | |
observer.update(arg1); | |
} else { | |
const ref2 = arg1, onChange = arg2; | |
observer.update(() => { | |
access(ref2); | |
}); | |
observer.observe = (ref3, newValue, oldValue) => { | |
observer.onUpdate = onChange.bind(null, newValue, oldValue, ref3); | |
}; | |
} | |
return observer; | |
} | |
function isRef(value) { | |
return !!value && value[kRefType] !== void 0; | |
} | |
function peek(compute) { | |
const parentAccess = access; | |
access = unseenAccess; | |
try { | |
return compute(); | |
} finally { | |
access = parentAccess; | |
} | |
} | |
export { | |
ComputedRef, | |
Observer, | |
ReadonlyRef, | |
Ref, | |
computed, | |
isRef, | |
observe, | |
peek, | |
ref | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const isFunction = value => typeof value === 'function' | |
const noop = () => {} | |
// Debugging hooks are declared globally. | |
declare const __OBSERVABLE_HOOKS__: { | |
/** | |
* A ref was changed and the given observer was notified. | |
*/ | |
observe( | |
observer: Observer, | |
ref: ReadonlyRef<any>, | |
newValue: any, | |
oldValue: any | |
): void | |
/** | |
* A ref was observed or unobserved. | |
*/ | |
isObserved( | |
ref: ReadonlyRef<any>, | |
observer: Observer, | |
isObserved: boolean | |
): void | |
/** | |
* An update was completed or threw an error. | |
*/ | |
didUpdate(observer: Observer | ComputedRef, error: any, result: any): void | |
} | |
const hooks = (DEV && | |
typeof __OBSERVABLE_HOOKS__ !== 'undefined' && | |
__OBSERVABLE_HOOKS__) as typeof __OBSERVABLE_HOOKS__ | |
type InternalRef<T> = Ref<T> & { | |
_value: T | |
_version: number | |
_observers: Set<Observer> | |
_depth: number | |
_isObserved: (observer: Observer, isObserved: boolean) => void | |
} | |
const unseenAccess = (ref: InternalRef<any>) => ref._value | |
let currentVersion = 1 | |
let nextVersion = 1 | |
let access = unseenAccess | |
const kRefType = Symbol.for('alien:refType') | |
export class ReadonlyRef<T = any> { | |
protected _version = 0 | |
protected _observers = new Set<Observer>() | |
protected get _depth() { | |
return 0 | |
} | |
constructor(protected _value: T) {} | |
/** | |
* In addition to adding/removing an observer, computed refs use this method | |
* to switch between eager and lazy computation mode. | |
*/ | |
protected _isObserved(observer: Observer, isObserved: boolean) { | |
if (DEV && hooks) { | |
hooks.isObserved(this, observer, isObserved) | |
} | |
if (isObserved) { | |
this._observers.add(observer) | |
} else { | |
this._observers.delete(observer) | |
} | |
} | |
get [kRefType]() { | |
return 'ReadonlyRef' | |
} | |
get value() { | |
return access(this as any) | |
} | |
get version() { | |
return this._version | |
} | |
peek() { | |
return this._value | |
} | |
} | |
export class Ref<T = any> extends ReadonlyRef<T> { | |
get [kRefType]() { | |
return 'Ref' | |
} | |
get value() { | |
return super.value | |
} | |
set value(newValue: T) { | |
const oldValue = this._value | |
if (newValue !== oldValue) { | |
this._value = newValue | |
this._version = currentVersion | |
this._observers.forEach(observer => { | |
observer.observe(this, newValue, oldValue) | |
}) | |
onObservedUpdate() | |
} | |
} | |
set(arg: T | ((value: T) => T)) { | |
if (isFunction(arg)) { | |
arg = arg(this._value) | |
} | |
this.value = arg | |
return arg | |
} | |
} | |
export interface Ref<T> { | |
0: T | |
1: (arg: T | ((value: T) => T)) => T | |
} | |
const valueProperty = Object.getOwnPropertyDescriptor(Ref.prototype, 'value')! | |
Object.defineProperties(Ref.prototype, { | |
0: valueProperty, | |
1: { | |
get(this: Ref) { | |
return this.set.bind(this) | |
}, | |
}, | |
[Symbol.iterator]: { | |
value: function* () { | |
yield this[0] | |
yield this[1] | |
}, | |
}, | |
}) | |
const emptySymbol: any = Symbol('empty') | |
export class ComputedRef<T = any> extends ReadonlyRef<T> { | |
protected _refs: Set<InternalRef<any>> | null = null | |
protected _observer: Observer | null = null | |
protected get _depth() { | |
return this._observer?.depth ?? 0 | |
} | |
constructor(protected compute: () => T) { | |
super(emptySymbol) | |
} | |
protected _isObserved(observer: Observer, isObserved: boolean) { | |
super._isObserved(observer, isObserved) | |
// Destroy our own observer once the ref is no longer observed. | |
if (!isObserved) { | |
this._value = emptySymbol | |
this._observer?.dispose() | |
this._observer = null | |
this._refs = null | |
} | |
// Create our own observer once the ref is observed. | |
else if (!this._observer) { | |
this._setupObserver() | |
} | |
} | |
protected _setupObserver() { | |
const observer = (this._observer = new Observer(computeQueue)) | |
this._refs = observer.refs | |
observer.onUpdate = valueProperty.set!.bind(this) | |
observer.update(this.compute) | |
} | |
protected _update() { | |
this._refs = new Set<InternalRef<any>>() | |
const parentAccess = access | |
access = ref => { | |
this._refs!.add(ref) | |
return ref._value | |
} | |
let error: any | |
try { | |
this._version = currentVersion | |
this._value = this.compute() | |
} catch (e) { | |
error = e | |
} | |
access = parentAccess | |
if (DEV && hooks) { | |
hooks.didUpdate(this, error, this._value) | |
} | |
if (error) throw error | |
return this._value | |
} | |
get [kRefType]() { | |
return 'ComputedRef' | |
} | |
get value() { | |
if (this._observer) { | |
return super.value | |
} | |
if (access !== unseenAccess) { | |
this._setupObserver() | |
return super.value | |
} | |
if (this._value === emptySymbol) { | |
return this._update() | |
} | |
// Check if a dependency has changed since last access. | |
for (const ref of this._refs!) { | |
if (ref._version > this._version) { | |
return this._update() | |
} | |
} | |
return this._value | |
} | |
} | |
const passThrough = (result: any) => result | |
let nextObserverId = 1 | |
export class Observer { | |
readonly id = nextObserverId++ | |
refs = new Set<InternalRef<any>>() | |
version = 0 | |
depth = 0 | |
nextCompute: () => any = noop | |
onUpdate = passThrough | |
constructor(readonly queue = updateQueue) {} | |
update<T>(compute: () => T) { | |
const oldRefs = new Set(this.refs) | |
this.refs.clear() | |
this.depth = 0 | |
const parentAccess = access | |
access = ref => { | |
ref._isObserved(this, true) | |
oldRefs.delete(ref) | |
this.refs.add(ref) | |
this.depth = Math.max(this.depth, ref._depth + 1) | |
return ref._value | |
} | |
let error: any | |
let result: any | |
try { | |
result = (this.nextCompute = compute)() | |
} catch (e) { | |
error = e | |
} | |
access = parentAccess | |
oldRefs.forEach(ref => { | |
ref._isObserved(this, false) | |
}) | |
if (DEV && hooks) { | |
hooks.didUpdate(this, error, result) | |
} | |
if (error) throw error | |
return this.onUpdate(result) | |
} | |
/** Called when a ref has a new value. */ | |
observe(ref: ReadonlyRef<any>, newValue: any, oldValue: any) { | |
if (DEV && hooks) { | |
hooks.observe(this, ref, newValue, oldValue) | |
} | |
if (this.version !== currentVersion) { | |
this.version = currentVersion | |
this.queue.add(this) | |
} | |
} | |
dispose() { | |
this.queue.delete(this) | |
this.refs.forEach(ref => { | |
ref._isObserved(this, false) | |
}) | |
} | |
/** | |
* Returns a bound `dispose` method. | |
*/ | |
get destructor() { | |
return this.dispose.bind(this) | |
} | |
} | |
function onObservedUpdate() { | |
if (nextVersion === currentVersion && hasQueuedObservers()) { | |
nextVersion++ | |
Promise.resolve().then(computeNextVersion) | |
} | |
} | |
// Computed refs always run before plain observers. | |
const computeQueue = new Set<Observer>() | |
const updateQueue = new Set<Observer>() | |
function hasQueuedObservers() { | |
return computeQueue.size + updateQueue.size > 0 | |
} | |
function computeNextVersion() { | |
currentVersion = nextVersion | |
// Capture the stack trace before the infinite loop. | |
const devError = DEV && Error('Cycle detected') | |
let currentObserver: Observer | |
let oldRefs: Set<InternalRef<any>> | |
const parentAccess = access | |
access = (ref: InternalRef<any>) => { | |
ref._isObserved(currentObserver, true) | |
oldRefs.delete(ref) | |
currentObserver.refs.add(ref) | |
currentObserver.depth = Math.max(currentObserver.depth, ref._depth + 1) | |
return ref._value | |
} | |
for (let loops = 0; hasQueuedObservers(); loops++) { | |
if (loops > 100) { | |
throw (DEV && devError) || Error('Cycle detected') | |
} | |
const update = (observer: Observer) => { | |
oldRefs = new Set(observer.refs) | |
observer.refs.clear() | |
observer.depth = 0 | |
let error: any | |
let result: any | |
currentObserver = observer | |
try { | |
result = observer.nextCompute() | |
observer.onUpdate(result) | |
} catch (e) { | |
console.error((error = e)) | |
} | |
oldRefs.forEach(ref => { | |
ref._isObserved(observer, false) | |
}) | |
if (DEV && hooks) { | |
hooks.didUpdate(observer, error, result) | |
} | |
} | |
// Computed refs run in order of depth. | |
const computedRefs = [...computeQueue].sort((a, b) => b.depth - a.depth) | |
computeQueue.clear() | |
computedRefs.forEach(update) | |
// Plain observers run in order of creation. | |
const updatedObservers = [...updateQueue].sort((a, b) => a.id - b.id) | |
updateQueue.clear() | |
updatedObservers.forEach(update) | |
} | |
access = parentAccess | |
} | |
// | |
// Convenience functions | |
// | |
export const ref = <T>(value: T) => new Ref(value) | |
export const computed = <T>(compute: () => T) => new ComputedRef(compute) | |
/** Observe any refs accessed in the compute function. */ | |
export function observe(compute: () => void): Observer | |
/** Observe a single ref. */ | |
export function observe<T>( | |
ref: ReadonlyRef<T>, | |
compute: (newValue: T, oldValue: T, ref: ReadonlyRef<T>) => void | |
): Observer | |
/** @internal */ | |
export function observe( | |
arg1: ReadonlyRef | (() => void), | |
arg2?: (newValue: any, oldValue: any, ref: ReadonlyRef) => void | |
) { | |
const observer = new Observer() | |
if (isFunction(arg1)) { | |
observer.update(arg1) | |
} else { | |
const ref = arg1, | |
onChange = arg2! | |
observer.update(() => { | |
access(ref as any) | |
}) | |
observer.observe = (ref, newValue, oldValue) => { | |
// Capture the old value for the onChange callback. | |
observer.onUpdate = onChange.bind(null, newValue, oldValue, ref) | |
} | |
} | |
return observer | |
} | |
export function isRef<T = any>(value: any): value is ReadonlyRef<T> { | |
return !!value && value[kRefType] !== undefined | |
} | |
/** | |
* Like `ref.peek()` but applies to all access within the given `compute` | |
* callback. | |
*/ | |
export function peek<T>(compute: () => T) { | |
const parentAccess = access | |
access = unseenAccess | |
try { | |
return compute() | |
} finally { | |
access = parentAccess | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment