Repository: puppeteer/puppeteer
File: packages/puppeteer-core/src/common/bidi/Realm.ts (TypeScript)

Code before:
    };
  }

  handleRealmDestroyed = async (
    params: Bidi.Script.RealmDestroyed['params']
  ): Promise<void> => {
    if (params.realm === this.#id) {
      const promise = this.internalPuppeteerUtil;
      this.internalPuppeteerUtil = undefined;
      try {
        await (await promise)?.dispose();
      } catch (error) {
        debugError(error);
      }
    }
  };

  handleRealmCreated = (params: Bidi.Script.RealmCreated['params']): void => {
    if (
      params.type === 'window' &&
      params.context === this.#sandbox.environment._id &&
      params.sandbox === this.#sandbox.name
    ) {
      this.#id = params.realm;
      void this.#sandbox.taskManager.rerunAll();
      this.connection.off(
        Bidi.ChromiumBidi.Script.EventNames.RealmCreated,
        this.handleRealmCreated
      );
      this.connection.on(
        Bidi.ChromiumBidi.Script.EventNames.RealmDestroyed,
        this.handleRealmDestroyed
      );
    }
  };

  setSandbox(sandbox: Sandbox): void {
    this.#sandbox = sandbox;
    this.connection.on(
      Bidi.ChromiumBidi.Script.EventNames.RealmCreated,
      this.handleRealmCreated
    );
  }

  protected internalPuppeteerUtil?: Promise<BidiJSHandle<PuppeteerUtil>>;
  get puppeteerUtil(): Promise<BidiJSHandle<PuppeteerUtil>> {
    const promise = Promise.resolve() as Promise<unknown>;
    scriptInjector.inject(script => {
      if (this.internalPuppeteerUtil) {
        void this.internalPuppeteerUtil.then(handle => {
          void handle.dispose();

Review comment (suggestion):
Let use the same thing we do in Page where we have map between the two.
We can subscribe to both events here as the second will be No-Op if the Id is undefined. 
Also we need a way to unsubscribe so that the object has no reference from `connection`.

Code after:
  }

  handleRealmDestroyed = async (
    params: Bidi.Script.RealmDestroyed['params']
  ): Promise<void> => {
    if (params.realm === this.#id) {
      // Note: The Realm is destroyed, so in theory the handle should be as
      // well.
      this.internalPuppeteerUtil = undefined;
    }
  };

  handleRealmCreated = (params: Bidi.Script.RealmCreated['params']): void => {
    if (
      params.type === 'window' &&
      params.context === this.#sandbox.environment._id &&
      params.sandbox === this.#sandbox.name
    ) {
      this.#id = params.realm;
      void this.#sandbox.taskManager.rerunAll();
    }
  };

  setSandbox(sandbox: Sandbox): void {
    this.#sandbox = sandbox;
    this.connection.on(
      Bidi.ChromiumBidi.Script.EventNames.RealmCreated,
      this.handleRealmCreated
    );
    this.connection.on(
      Bidi.ChromiumBidi.Script.EventNames.RealmDestroyed,
      this.handleRealmDestroyed
    );
  }

  protected internalPuppeteerUtil?: Promise<BidiJSHandle<PuppeteerUtil>>;
  get puppeteerUtil(): Promise<BidiJSHandle<PuppeteerUtil>> {
    const promise = Promise.resolve() as Promise<unknown>;
    scriptInjector.inject(script => {
      if (this.internalPuppeteerUtil) {
        void this.internalPuppeteerUtil.then(handle => {
          void handle.dispose();
        });
      }
      this.internalPuppeteerUtil = promise.then(() => {
        return this.evaluateHandle(script) as Promise<
          BidiJSHandle<PuppeteerUtil>
        >;
      });
    }, !this.internalPuppeteerUtil);
    return this.internalPuppeteerUtil as Promise<BidiJSHandle<PuppeteerUtil>>;