Skip to content

Commit dc92ab9

Browse files
authored
Update the polyfill to the latest API (tc39#71)
* Update the polyfill to the lastest API * Fix filename case * Move wrap onto Snapshot * Fix polyfill
1 parent a8a2961 commit dc92ab9

10 files changed

+191
-132
lines changed

package-lock.json

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/fork.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Mapping } from "./mapping";
2-
import type { AsyncContext } from "./index";
2+
import type { Variable } from "./variable";
33

44
/**
55
* FrozenRevert holds a frozen Mapping that will be simply restored when the
@@ -40,11 +40,11 @@ export class FrozenRevert {
4040
* clone to the prior state.
4141
*/
4242
export class Revert<T> {
43-
#key: AsyncContext<T>;
43+
#key: Variable<T>;
4444
#has: boolean;
4545
#prev: T | undefined;
4646

47-
constructor(mapping: Mapping, key: AsyncContext<T>) {
47+
constructor(mapping: Mapping, key: Variable<T>) {
4848
this.#key = key;
4949
this.#has = mapping.has(key);
5050
this.#prev = mapping.get(key);

src/index.ts

+8-37
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,8 @@
1-
import { Storage } from "./storage";
2-
3-
type AnyFunc<T> = (this: T, ...args: any) => any;
4-
5-
export class AsyncContext<T> {
6-
static wrap<F extends AnyFunc<any>>(fn: F): F {
7-
const snapshot = Storage.snapshot();
8-
9-
function wrap(this: ThisType<F>, ...args: Parameters<F>): ReturnType<F> {
10-
const head = Storage.switch(snapshot);
11-
try {
12-
return fn.apply(this, args);
13-
} finally {
14-
Storage.restore(head);
15-
}
16-
}
17-
18-
return wrap as unknown as F;
19-
}
20-
21-
run<F extends AnyFunc<null>>(
22-
value: T,
23-
fn: F,
24-
...args: Parameters<F>
25-
): ReturnType<F> {
26-
const revert = Storage.set(this, value);
27-
try {
28-
return fn.apply(null, args);
29-
} finally {
30-
Storage.restore(revert);
31-
}
32-
}
33-
34-
get(): T | undefined {
35-
return Storage.get(this);
36-
}
37-
}
1+
import { Snapshot } from "./snapshot";
2+
import { Variable } from "./variable";
3+
4+
export const AsyncContext = {
5+
Snapshot,
6+
Variable,
7+
};
8+
export { Snapshot, Variable };

src/mapping.ts

+13-14
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,48 @@
1-
import type { AsyncContext } from "./index";
1+
import type { Variable } from "./variable";
22

33
/**
4-
* Stores all AsyncContext data, and tracks whether any snapshots have been
4+
* Stores all Variable data, and tracks whether any snapshots have been
55
* taken of the current data.
66
*/
77
export class Mapping {
8-
#data: Map<AsyncContext<unknown>, unknown> | null;
8+
#data: Map<Variable<unknown>, unknown>;
99

1010
/**
1111
* If a snapshot of this data is taken, then further modifications cannot be
1212
* made directly. Instead, set/delete will clone this Mapping and modify
1313
* _that_ instance.
1414
*/
15-
#frozen: boolean;
15+
#frozen = false;
1616

17-
constructor(data: Map<AsyncContext<unknown>, unknown> | null) {
17+
constructor(data: Map<Variable<unknown>, unknown>) {
1818
this.#data = data;
19-
this.#frozen = data === null;
2019
}
2120

22-
has<T>(key: AsyncContext<T>): boolean {
23-
return this.#data?.has(key) || false;
21+
has<T>(key: Variable<T>): boolean {
22+
return this.#data.has(key) || false;
2423
}
2524

26-
get<T>(key: AsyncContext<T>): T | undefined {
27-
return this.#data?.get(key) as T | undefined;
25+
get<T>(key: Variable<T>): T | undefined {
26+
return this.#data.get(key) as T | undefined;
2827
}
2928

3029
/**
3130
* Like the standard Map.p.set, except that we will allocate a new Mapping
3231
* instance if this instance is frozen.
3332
*/
34-
set<T>(key: AsyncContext<T>, value: T): Mapping {
33+
set<T>(key: Variable<T>, value: T): Mapping {
3534
const mapping = this.#fork();
36-
mapping.#data!.set(key, value);
35+
mapping.#data.set(key, value);
3736
return mapping;
3837
}
3938

4039
/**
4140
* Like the standard Map.p.delete, except that we will allocate a new Mapping
4241
* instance if this instance is frozen.
4342
*/
44-
delete<T>(key: AsyncContext<T>): Mapping {
43+
delete<T>(key: Variable<T>): Mapping {
4544
const mapping = this.#fork();
46-
mapping.#data!.delete(key);
45+
mapping.#data.delete(key);
4746
return mapping;
4847
}
4948

src/promise-polyfill.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { AsyncContext } from "./index";
22

3-
type AnyFunc = (...args: any) => any;
3+
import type { AnyFunc } from "./types";
44

55
export const nativeThen = Promise.prototype.then;
6+
const { wrap } = AsyncContext.Snapshot;
67

7-
function wrapFn<F extends AnyFunc>(fn: F | null | undefined) {
8+
function wrapFn<F extends AnyFunc<any>>(fn: F | null | undefined) {
89
if (typeof fn !== "function") return undefined;
9-
return AsyncContext.wrap(fn);
10+
return wrap(fn);
1011
}
1112

1213
export function then<T>(

src/snapshot.ts

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Storage } from "./storage";
2+
3+
import type { FrozenRevert } from "./fork";
4+
import type { AnyFunc } from "./types";
5+
6+
export class Snapshot {
7+
#snapshot = Storage.snapshot();
8+
9+
static wrap<F extends AnyFunc<any>>(fn: F): F {
10+
const snapshot = Storage.snapshot();
11+
12+
function wrap(this: ThisType<F>, ...args: Parameters<F>): ReturnType<F> {
13+
return run(fn, this, args, snapshot);
14+
}
15+
16+
return wrap as unknown as F;
17+
}
18+
19+
run<F extends AnyFunc<null>>(fn: F, ...args: Parameters<F>) {
20+
return run(fn, null as any, args, this.#snapshot);
21+
}
22+
}
23+
24+
function run<F extends AnyFunc<any>>(
25+
fn: F,
26+
context: ThisType<F>,
27+
args: any[],
28+
snapshot: FrozenRevert
29+
): ReturnType<F> {
30+
const revert = Storage.switch(snapshot);
31+
try {
32+
return fn.apply(context, args);
33+
} finally {
34+
Storage.restore(revert);
35+
}
36+
}

src/storage.ts

+19-11
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,44 @@
11
import { Mapping } from "./mapping";
22
import { FrozenRevert, Revert } from "./fork";
3-
import type { AsyncContext } from "./index";
3+
4+
import type { Variable } from "./variable";
45

56
/**
67
* Storage is the (internal to the language) storage container of all
7-
* AsyncContext data.
8+
* Variable data.
89
*
9-
* None of the methods here are exposed to users, they're only exposed to the AsyncContext class.
10+
* None of the methods here are exposed to users, they're only exposed internally.
1011
*/
1112
export class Storage {
12-
static #current: Mapping = new Mapping(null);
13+
static #current: Mapping = new Mapping(new Map());
14+
15+
/**
16+
* Has checks if the Variable has a value.
17+
*/
18+
static has<T>(key: Variable<T>): boolean {
19+
return this.#current.has(key);
20+
}
1321

1422
/**
15-
* Get retrieves the current value assigned to the AsyncContext.
23+
* Get retrieves the current value assigned to the Variable.
1624
*/
17-
static get<T>(key: AsyncContext<T>): T | undefined {
25+
static get<T>(key: Variable<T>): T | undefined {
1826
return this.#current.get(key);
1927
}
2028

2129
/**
22-
* Set assigns a new value to the AsyncContext, returning a revert that can
30+
* Set assigns a new value to the Variable, returning a revert that can
2331
* undo the modification at a later time.
2432
*/
25-
static set<T>(key: AsyncContext<T>, value: T): FrozenRevert | Revert<T> {
33+
static set<T>(key: Variable<T>, value: T): FrozenRevert | Revert<T> {
2634
// If the Mappings are frozen (someone has snapshot it), then modifying the
2735
// mappings will return a clone containing the modification.
2836
const current = this.#current;
29-
const undo = current.isFrozen()
37+
const revert = current.isFrozen()
3038
? new FrozenRevert(current)
31-
: new Revert(current, key);
39+
: new Revert<T>(current, key);
3240
this.#current = this.#current.set(key, value);
33-
return undo;
41+
return revert;
3442
}
3543

3644
/**

src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type AnyFunc<T> = (this: T, ...args: any) => any;

src/variable.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Storage } from "./storage";
2+
3+
import type { AnyFunc } from "./types";
4+
5+
export interface VariableOptions<T> {
6+
name?: string;
7+
defaultValue?: T;
8+
}
9+
10+
export class Variable<T> {
11+
#name = "";
12+
#defaultValue: T | undefined;
13+
14+
constructor(options?: VariableOptions<T>) {
15+
if (options) {
16+
if ("name" in options) {
17+
this.#name = String(options.name);
18+
}
19+
this.#defaultValue = options.defaultValue;
20+
}
21+
}
22+
23+
get name() {
24+
return this.#name;
25+
}
26+
27+
run<F extends AnyFunc<null>>(
28+
value: T,
29+
fn: F,
30+
...args: Parameters<F>
31+
): ReturnType<F> {
32+
const revert = Storage.set(this, value);
33+
try {
34+
return fn.apply(null, args);
35+
} finally {
36+
Storage.restore(revert);
37+
}
38+
}
39+
40+
get(): T | undefined {
41+
return Storage.has(this) ? Storage.get(this) : this.#defaultValue;
42+
}
43+
}

0 commit comments

Comments
 (0)