/** * Copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { AsyncLocalStorage } from 'async_hooks'; export type ZoneType = 'apiZone' | 'expectZone' | 'stepZone'; class ZoneManager { private readonly _asyncLocalStorage = new AsyncLocalStorage(); run(type: ZoneType, data: T, func: () => R): R { const zone = Zone._createWithData(this._asyncLocalStorage, type, data); return this._asyncLocalStorage.run(zone, func); } zoneData(type: ZoneType): T | undefined { const zone = this._asyncLocalStorage.getStore(); return zone?.get(type); } currentZone(): Zone { return this._asyncLocalStorage.getStore() ?? Zone._createEmpty(this._asyncLocalStorage); } exitZones(func: () => R): R { return this._asyncLocalStorage.run(undefined, func); } } export class Zone { private readonly _asyncLocalStorage: AsyncLocalStorage; private readonly _data: Map; static _createWithData(asyncLocalStorage: AsyncLocalStorage, type: ZoneType, data: unknown) { const store = new Map(asyncLocalStorage.getStore()?._data); store.set(type, data); return new Zone(asyncLocalStorage, store); } static _createEmpty(asyncLocalStorage: AsyncLocalStorage) { return new Zone(asyncLocalStorage, new Map()); } private constructor(asyncLocalStorage: AsyncLocalStorage, store: Map) { this._asyncLocalStorage = asyncLocalStorage; this._data = store; } run(func: () => R): R { // Reset apiZone and expectZone, but restore stepZone. const entries = [...this._data.entries()].filter(([type]) => (type !== 'apiZone' && type !== 'expectZone')); const resetZone = new Zone(this._asyncLocalStorage, new Map(entries)); return this._asyncLocalStorage.run(resetZone, func); } get(type: ZoneType): T | undefined { return this._data.get(type) as T | undefined; } } export const zones = new ZoneManager();