<html lang="en">
<head></head>
<body>

<form id="mainForm" method="post" action="https://stackblitz.com/run" target="_self">
<input type="hidden" name="project[files][README.md]" value="# Basic

This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.0.5.

## Development server

To start a local development server, run:

```bash
ng serve
```

Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.

## Code scaffolding

Angular CLI includes powerful code scaffolding tools. To generate a new component, run:

```bash
ng generate component component-name
```

For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:

```bash
ng generate --help
```

## Building

To build the project run:

```bash
ng build
```

This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.

## Running unit tests

To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command:

```bash
ng test
```

## Running end-to-end tests

For end-to-end (e2e) testing, run:

```bash
ng e2e
```

Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.

## Additional Resources

For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
">
<input type="hidden" name="project[files][angular.json]" value="{
  &quot;$schema&quot;: &quot;./node_modules/@angular/cli/lib/config/schema.json&quot;,
  &quot;version&quot;: 1,
  &quot;cli&quot;: {
    &quot;packageManager&quot;: &quot;npm&quot;,
    &quot;analytics&quot;: false
  },
  &quot;newProjectRoot&quot;: &quot;projects&quot;,
  &quot;projects&quot;: {
    &quot;basic&quot;: {
      &quot;projectType&quot;: &quot;application&quot;,
      &quot;schematics&quot;: {},
      &quot;root&quot;: &quot;&quot;,
      &quot;sourceRoot&quot;: &quot;src&quot;,
      &quot;prefix&quot;: &quot;app&quot;,
      &quot;architect&quot;: {
        &quot;build&quot;: {
          &quot;builder&quot;: &quot;@angular/build:application&quot;,
          &quot;options&quot;: {
            &quot;browser&quot;: &quot;src/main.ts&quot;,
            &quot;tsConfig&quot;: &quot;tsconfig.app.json&quot;,
            &quot;assets&quot;: [
              {
                &quot;glob&quot;: &quot;**/*&quot;,
                &quot;input&quot;: &quot;public&quot;
              }
            ],
            &quot;styles&quot;: [&quot;src/styles.css&quot;]
          },
          &quot;configurations&quot;: {
            &quot;production&quot;: {
              &quot;budgets&quot;: [
                {
                  &quot;type&quot;: &quot;initial&quot;,
                  &quot;maximumWarning&quot;: &quot;500kB&quot;,
                  &quot;maximumError&quot;: &quot;1MB&quot;
                },
                {
                  &quot;type&quot;: &quot;anyComponentStyle&quot;,
                  &quot;maximumWarning&quot;: &quot;4kB&quot;,
                  &quot;maximumError&quot;: &quot;8kB&quot;
                }
              ],
              &quot;outputHashing&quot;: &quot;all&quot;
            },
            &quot;development&quot;: {
              &quot;optimization&quot;: false,
              &quot;extractLicenses&quot;: false,
              &quot;sourceMap&quot;: true
            }
          },
          &quot;defaultConfiguration&quot;: &quot;production&quot;
        },
        &quot;serve&quot;: {
          &quot;builder&quot;: &quot;@angular/build:dev-server&quot;,
          &quot;configurations&quot;: {
            &quot;production&quot;: {
              &quot;buildTarget&quot;: &quot;basic:build:production&quot;
            },
            &quot;development&quot;: {
              &quot;buildTarget&quot;: &quot;basic:build:development&quot;
            }
          },
          &quot;defaultConfiguration&quot;: &quot;development&quot;
        },
        &quot;test&quot;: {
          &quot;builder&quot;: &quot;@angular/build:unit-test&quot;
        }
      }
    }
  }
}
">
<input type="hidden" name="project[files][eslint.config.js]" value="// @ts-check

import rootConfig from &#39;../../../eslint.config.js&#39;

/** @type {import(&#39;eslint&#39;).Linter.Config[]} */
export default [
  ...rootConfig,
  {
    languageOptions: {
      parserOptions: {
        project: &#39;./tsconfig.app.json&#39;,
      },
    },
  },
]
">
<input type="hidden" name="project[files][package.json]" value="{
  &quot;name&quot;: &quot;@tanstack/pacer-example-angular-async-retry&quot;,
  &quot;scripts&quot;: {
    &quot;ng&quot;: &quot;ng&quot;,
    &quot;start&quot;: &quot;ng serve&quot;,
    &quot;build&quot;: &quot;ng build&quot;,
    &quot;watch&quot;: &quot;ng build --watch --configuration development&quot;,
    &quot;test&quot;: &quot;ng test&quot;,
    &quot;test:e2e&quot;: &quot;PLAYWRIGHT_TEST_DIR=$PWD/tests/e2e playwright test --config ../../../playwright.config.ts&quot;
  },
  &quot;prettier&quot;: {
    &quot;printWidth&quot;: 100,
    &quot;singleQuote&quot;: true,
    &quot;overrides&quot;: [
      {
        &quot;files&quot;: &quot;*.html&quot;,
        &quot;options&quot;: {
          &quot;parser&quot;: &quot;angular&quot;
        }
      }
    ]
  },
  &quot;private&quot;: true,
  &quot;packageManager&quot;: &quot;pnpm@12.6.0&quot;,
  &quot;dependencies&quot;: {
    &quot;@angular/common&quot;: &quot;^22.2.0&quot;,
    &quot;@angular/compiler&quot;: &quot;^22.2.0&quot;,
    &quot;@angular/core&quot;: &quot;^22.2.0&quot;,
    &quot;@angular/forms&quot;: &quot;^22.2.0&quot;,
    &quot;@angular/platform-browser&quot;: &quot;^22.2.0&quot;,
    &quot;@angular/router&quot;: &quot;^22.2.0&quot;,
    &quot;@tanstack/angular-pacer&quot;: &quot;https://pkg.pr.new/TanStack/pacer/@tanstack/angular-pacer@8777955&quot;,
    &quot;rxjs&quot;: &quot;~7.8.2&quot;,
    &quot;tslib&quot;: &quot;^2.8.1&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@angular/build&quot;: &quot;^22.2.0&quot;,
    &quot;@angular/cli&quot;: &quot;^22.2.0&quot;,
    &quot;@angular/compiler-cli&quot;: &quot;^22.2.0&quot;,
    &quot;jsdom&quot;: &quot;^30.1.1&quot;,
    &quot;typescript&quot;: &quot;6.0.3&quot;,
    &quot;vitest&quot;: &quot;^5.0.1&quot;
  }
}">
<input type="hidden" name="project[files][tsconfig.app.json]" value="/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
  &quot;extends&quot;: &quot;./tsconfig.json&quot;,
  &quot;compilerOptions&quot;: {
    &quot;outDir&quot;: &quot;./out-tsc/app&quot;,
    &quot;types&quot;: []
  },
  &quot;include&quot;: [&quot;./src/**/*.ts&quot;],
  &quot;exclude&quot;: [&quot;./src/**/*.spec.ts&quot;]
}
">
<input type="hidden" name="project[files][tsconfig.json]" value="/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
  &quot;compileOnSave&quot;: false,
  &quot;compilerOptions&quot;: {
    &quot;strict&quot;: true,
    &quot;noImplicitOverride&quot;: true,
    &quot;noPropertyAccessFromIndexSignature&quot;: true,
    &quot;noImplicitReturns&quot;: true,
    &quot;noFallthroughCasesInSwitch&quot;: true,
    &quot;skipLibCheck&quot;: true,
    &quot;isolatedModules&quot;: true,
    &quot;experimentalDecorators&quot;: true,
    &quot;importHelpers&quot;: true,
    &quot;target&quot;: &quot;ES2022&quot;,
    &quot;module&quot;: &quot;preserve&quot;
  },
  &quot;angularCompilerOptions&quot;: {
    &quot;enableI18nLegacyMessageIdFormat&quot;: false,
    &quot;strictInjectionParameters&quot;: true,
    &quot;strictInputAccessModifiers&quot;: true,
    &quot;strictTemplates&quot;: true
  },
  &quot;files&quot;: [],
  &quot;references&quot;: [
    {
      &quot;path&quot;: &quot;./tsconfig.app.json&quot;
    },
    {
      &quot;path&quot;: &quot;./tsconfig.spec.json&quot;
    }
  ]
}
">
<input type="hidden" name="project[files][tsconfig.spec.json]" value="/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
  &quot;extends&quot;: &quot;./tsconfig.json&quot;,
  &quot;compilerOptions&quot;: {
    &quot;outDir&quot;: &quot;./out-tsc/spec&quot;,
    &quot;types&quot;: [&quot;vitest/globals&quot;]
  },
  &quot;include&quot;: [&quot;src/**/*.d.ts&quot;, &quot;src/**/*.spec.ts&quot;]
}
">
<input type="hidden" name="project[files][public/favicon.ico]" value="https://pkg.pr.new/template/759e1414-5afa-454d-88dc-704d95a94289">
<input type="hidden" name="project[files][src/index.html]" value="&lt;!doctype html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot; /&gt;
    &lt;title&gt;Basic&lt;/title&gt;
    &lt;base href=&quot;/&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot; /&gt;
    &lt;link rel=&quot;icon&quot; type=&quot;image/x-icon&quot; href=&quot;favicon.ico&quot; /&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;app-root&gt;&lt;/app-root&gt;
  &lt;/body&gt;
&lt;/html&gt;
">
<input type="hidden" name="project[files][src/main.ts]" value="import { bootstrapApplication } from &#39;@angular/platform-browser&#39;
import { appConfig } from &#39;./app/app.config&#39;
import { App } from &#39;./app/app&#39;

bootstrapApplication(App, appConfig).catch((err) =&gt; console.error(err))
">
<input type="hidden" name="project[files][src/styles.css]" value="/* You can add global styles to this file, and also import other style files */
">
<input type="hidden" name="project[files][tests/e2e/smoke.spec.mts]" value="import { expect, test } from &#39;../../../../../tests/e2e/helpers/fixtures&#39;

test.beforeEach(async ({ page, exampleUrl }) =&gt; {
  await page.clock.install()
  await page.goto(exampleUrl)
  await expect(
    page.getByRole(&#39;heading&#39;, {
      name: &#39;TanStack Pacer asyncRetry Example&#39;,
      exact: true,
    }),
  ).toBeVisible()
  // Let Angular bootstrap before pausing timers; runFor also advances render frames.
  await page.clock.pauseAt(await page.evaluate(() =&gt; Date.now() + 100))
})

test(&#39;renders retry configuration and initial idle state&#39;, async ({ page }) =&gt; {
  await expect(page.getByRole(&#39;combobox&#39;, { name: &#39;Scenario:&#39;, exact: true })).toHaveValue(
    &#39;default&#39;,
  )
  await expect(page.getByText(&#39;Status: idle&#39;, { exact: true })).toBeVisible()
  await expect(page.getByText(&#39;No activity yet&#39;, { exact: true })).toBeVisible()
})

test(&#39;retries a failed request and displays the successful result&#39;, async ({ page }) =&gt; {
  await page.evaluate(() =&gt; {
    let requests = 0
    Math.random = () =&gt; (requests++ === 0 ? 0 : 1)
  })
  await page.getByRole(&#39;textbox&#39;, { name: &#39;User ID:&#39;, exact: true }).fill(&#39;456&#39;)
  await page.getByRole(&#39;button&#39;, { name: &#39;Fetch User&#39;, exact: true }).click()
  await page.clock.runFor(850)
  await expect(page.getByText(&#39;Current Attempt: 2 / 5&#39;, { exact: true })).toBeVisible()
  await expect(page.getByRole(&#39;button&#39;, { name: &#39;Fetching...&#39;, exact: true })).toBeDisabled()
  await page.clock.runFor(1900)
  await expect(
    page.getByText(&#39;ID: 456, Name: User 456, Email: user456@example.com&#39;, {
      exact: true,
    }),
  ).toBeVisible()
  await expect(page.getByText(&#39;Status: idle&#39;, { exact: true })).toBeVisible()
  await page.getByRole(&#39;button&#39;, { name: &#39;Reset&#39;, exact: true }).click()
  await page.clock.runFor(32)
  await expect(page.getByRole(&#39;heading&#39;, { name: &#39;User Data&#39;, exact: true })).toHaveCount(0)
})
">
<input type="hidden" name="project[files][src/app/app.config.ts]" value="import { ApplicationConfig, provideBrowserGlobalErrorListeners } from &#39;@angular/core&#39;
import { provideRouter } from &#39;@angular/router&#39;

import { routes } from &#39;./app.routes&#39;

export const appConfig: ApplicationConfig = {
  providers: [provideBrowserGlobalErrorListeners(), provideRouter(routes)],
}
">
<input type="hidden" name="project[files][src/app/app.css]" value=":host {
  display: block;
  padding: 2rem;
  max-width: 600px;
  margin: 0 auto;
}

input {
  width: 100%;
  padding: 0.5rem;
  font-size: 1rem;
  border: 1px solid #ccc;
  border-radius: 4px;
}

p {
  margin-top: 1rem;
  color: #666;
}
">
<input type="hidden" name="project[files][src/app/app.html]" value="&lt;h1&gt;TanStack Pacer asyncRetry Example&lt;/h1&gt;
&lt;p&gt;
  Compare exponential and linear backoff, jitter, per-attempt timeouts, and total timeouts. The
  simulated API fails 60% of requests.
&lt;/p&gt;
&lt;h2&gt;Configuration&lt;/h2&gt;
&lt;label
  &gt;Scenario:
  &lt;select
    [value]=&quot;scenario()&quot;
    (change)=&quot;setScenario($any($event.target).value)&quot;
    [disabled]=&quot;isLoading()&quot;
  &gt;
    &lt;option value=&quot;default&quot;&gt;Default (Exponential Backoff)&lt;/option&gt;
    &lt;option value=&quot;timeout&quot;&gt;With Timeouts&lt;/option&gt;
    &lt;option value=&quot;jitter&quot;&gt;With Jitter (30%)&lt;/option&gt;
    &lt;option value=&quot;linear&quot;&gt;Linear Backoff&lt;/option&gt;
  &lt;/select&gt;&lt;/label
&gt;
&lt;label
  &gt;User ID:
  &lt;input
    [value]=&quot;userId()&quot;
    (input)=&quot;userId.set($any($event.target).value)&quot;
    [disabled]=&quot;isLoading()&quot;
/&gt;&lt;/label&gt;
&lt;button (click)=&quot;fetchUser()&quot; [disabled]=&quot;isLoading()&quot;&gt;
  {{ isLoading() ? &#39;Fetching...&#39; : &#39;Fetch User&#39; }}
&lt;/button&gt;
&lt;button (click)=&quot;reset()&quot; [disabled]=&quot;isLoading()&quot;&gt;Reset&lt;/button&gt;
&lt;h3&gt;Current Options&lt;/h3&gt;
&lt;pre&gt;{{ options() | json }}&lt;/pre&gt;
&lt;h2&gt;State&lt;/h2&gt;
&lt;p&gt;Status: {{ isLoading() ? (currentAttempt() &gt; 1 ? &#39;retrying&#39; : &#39;executing&#39;) : &#39;idle&#39; }}&lt;/p&gt;
@if (currentAttempt()) {
&lt;p&gt;Current Attempt: {{ currentAttempt() }} / {{ options().maxAttempts }}&lt;/p&gt;
} @if (error()) {
&lt;p role=&quot;alert&quot;&gt;Error: {{ error() }}&lt;/p&gt;
} @if (userData(); as user) {
&lt;h3&gt;User Data&lt;/h3&gt;
&lt;p&gt;ID: {{ user.id }}, Name: {{ user.name }}, Email: {{ user.email }}&lt;/p&gt;
}
&lt;h2&gt;Activity Log&lt;/h2&gt;
@for (log of logs(); track $index) {
&lt;p&gt;{{ log }}&lt;/p&gt;
} @empty {
&lt;p&gt;No activity yet&lt;/p&gt;
}
&lt;p&gt;
  Each call to the returned function creates a fresh retry context. The timeout scenario uses a
  3000ms API delay and a 2000ms per-attempt timeout.
&lt;/p&gt;
">
<input type="hidden" name="project[files][src/app/app.routes.ts]" value="import type { Routes } from &#39;@angular/router&#39;

export const routes: Routes = []
">
<input type="hidden" name="project[files][src/app/app.spec.ts]" value="import { vi } from &#39;vitest&#39;
import { TestBed } from &#39;@angular/core/testing&#39;
import { App } from &#39;./app&#39;

describe(&#39;App&#39;, () =&gt; {
  beforeEach(async () =&gt; {
    await TestBed.configureTestingModule({
      imports: [App],
    }).compileComponents()
  })

  it(&#39;should create the app&#39;, () =&gt; {
    const fixture = TestBed.createComponent(App)
    const app = fixture.componentInstance
    expect(app).toBeTruthy()
  })

  it(&#39;should render title&#39;, async () =&gt; {
    const fixture = TestBed.createComponent(App)
    await fixture.whenStable()
    const compiled = fixture.nativeElement as HTMLElement
    expect(compiled.querySelector(&#39;h1&#39;)?.textContent).toContain(&#39;TanStack Pacer asyncRetry Example&#39;)
  })
})

// Exercise the real adapter and component with a deterministic clock.
describe(&#39;example behavior&#39;, () =&gt; {
  beforeEach(async () =&gt; {
    await TestBed.configureTestingModule({ imports: [App] }).compileComponents()
  })
  afterEach(() =&gt; {
    TestBed.resetTestingModule()
    vi.clearAllTimers()
    vi.useRealTimers()
    vi.restoreAllMocks()
  })
  it(&#39;uses each selected retry configuration and returns successful user data&#39;, async () =&gt; {
    vi.useFakeTimers({ toFake: [&#39;setTimeout&#39;, &#39;clearTimeout&#39;, &#39;Date&#39;] })
    const fixture = TestBed.createComponent(App)
    const app = fixture.componentInstance
    fixture.detectChanges()
    TestBed.tick()
    app.setScenario(&#39;linear&#39;)
    expect(app.options().backoff).toBe(&#39;linear&#39;)
    expect(app.options().maxAttempts).toBe(4)
    app.setScenario(&#39;timeout&#39;)
    expect(app.options().maxExecutionTime).toBe(2000)
    app.setScenario(&#39;jitter&#39;)
    expect(app.options().jitter).toBe(0.3)
    app.setScenario(&#39;default&#39;)
    vi.spyOn(Math, &#39;random&#39;).mockReturnValue(0.9)
    const result = app.fetchUser()
    await vi.advanceTimersByTimeAsync(800)
    await result
    expect(app.userData()?.id).toBe(123)
    expect(app.error()).toBe(&#39;&#39;)
    expect(app.isLoading()).toBe(false)
  })
})
">
<input type="hidden" name="project[files][src/app/app.ts]" value="import { Component, computed, signal } from &#39;@angular/core&#39;
import { JsonPipe } from &#39;@angular/common&#39;
import { asyncRetry } from &#39;@tanstack/angular-pacer&#39;

type Scenario = &#39;default&#39; | &#39;timeout&#39; | &#39;jitter&#39; | &#39;linear&#39;
type UserData = { id: number; name: string; email: string }

@Component({
  selector: &#39;app-root&#39;,
  templateUrl: &#39;./app.html&#39;,
  imports: [JsonPipe],
})
export class App {
  readonly userId = signal(&#39;123&#39;)
  readonly userData = signal&lt;UserData | null&gt;(null)
  readonly isLoading = signal(false)
  readonly error = signal(&#39;&#39;)
  readonly currentAttempt = signal(0)
  readonly scenario = signal&lt;Scenario&gt;(&#39;default&#39;)
  readonly logs = signal&lt;Array&lt;string&gt;&gt;([])
  readonly options = computed(() =&gt; {
    switch (this.scenario()) {
      case &#39;timeout&#39;:
        return {
          maxAttempts: 3,
          backoff: &#39;exponential&#39; as const,
          baseWait: 500,
          maxExecutionTime: 2000,
          maxTotalExecutionTime: 8000,
          jitter: 0,
        }
      case &#39;jitter&#39;:
        return {
          maxAttempts: 5,
          backoff: &#39;exponential&#39; as const,
          baseWait: 500,
          jitter: 0.3,
        }
      case &#39;linear&#39;:
        return {
          maxAttempts: 4,
          backoff: &#39;linear&#39; as const,
          baseWait: 1000,
          jitter: 0,
        }
      default:
        return {
          maxAttempts: 5,
          backoff: &#39;exponential&#39; as const,
          baseWait: 1000,
          jitter: 0,
        }
    }
  })
  private log(message: string): void {
    this.logs.update((logs) =&gt; [
      ...logs.slice(-9),
      `${new Date().toLocaleTimeString()}: ${message}`,
    ])
  }
  setScenario(value: string): void {
    if (value === &#39;default&#39; || value === &#39;timeout&#39; || value === &#39;jitter&#39; || value === &#39;linear&#39;)
      this.scenario.set(value)
  }
  async fetchUser(): Promise&lt;void&gt; {
    this.logs.set([])
    this.isLoading.set(true)
    this.error.set(&#39;&#39;)
    this.currentAttempt.set(1)
    this.log(&#39;Starting fetch operation&#39;)
    const scenario = this.scenario()
    const fetchWithRetry = asyncRetry(
      async (id: string): Promise&lt;UserData&gt; =&gt; {
        this.log(`Attempting to fetch user ${id}`)
        await new Promise((resolve) =&gt; setTimeout(resolve, scenario === &#39;timeout&#39; ? 3000 : 800))
        if (Math.random() &lt; 0.6) throw new Error(`Network error fetching user ${id}`)
        return {
          id: Number.parseInt(id),
          name: `User ${id}`,
          email: `user${id}@example.com`,
        }
      },
      {
        ...this.options(),
        onRetry: (attempt, error) =&gt; {
          this.log(`Retry attempt ${attempt} after error: ${error.message}`)
          this.currentAttempt.set(attempt + 1)
        },
        onError: (error) =&gt; this.log(`Request failed: ${error.message}`),
        onLastError: (error) =&gt; {
          this.log(`All retries exhausted: ${error.message}`)
          this.error.set(error.message)
          this.userData.set(null)
        },
        onSuccess: (result) =&gt; {
          this.log(`Request succeeded for user ${result.id}`)
          this.userData.set(result)
          this.error.set(&#39;&#39;)
        },
        onSettled: () =&gt; {
          this.log(&#39;Request settled&#39;)
          this.currentAttempt.set(0)
        },
      },
    )
    try {
      const result = await fetchWithRetry(this.userId())
      this.log(`Final result: ${result ? `User ${result.id}` : &#39;undefined&#39;}`)
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error)
      this.error.set(message)
      this.log(`Caught error: ${message}`)
    } finally {
      this.isLoading.set(false)
    }
  }
  reset(): void {
    this.userData.set(null)
    this.error.set(&#39;&#39;)
    this.logs.set([])
    this.currentAttempt.set(0)
    this.log(&#39;State reset&#39;)
  }
}
">
<input type="hidden" name="project[description]" value="generated by https://pkg.pr.new">
<input type="hidden" name="project[template]" value="node">
<input type="hidden" name="project[title]" value="@tanstack/pacer-example-angular-async-retry">
</form>
<script>document.getElementById("mainForm").submit();</script>

</body></html>