<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-inject-async-rate-limiter-with-persister&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;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@10.33.2&quot;,&quot;dependencies&quot;:{&quot;@angular/common&quot;:&quot;^21.2.10&quot;,&quot;@angular/compiler&quot;:&quot;^21.2.10&quot;,&quot;@angular/core&quot;:&quot;^21.2.10&quot;,&quot;@angular/forms&quot;:&quot;^21.2.10&quot;,&quot;@angular/platform-browser&quot;:&quot;^21.2.10&quot;,&quot;@angular/router&quot;:&quot;^21.2.10&quot;,&quot;@tanstack/angular-pacer&quot;:&quot;https://pkg.pr.new/TanStack/pacer/@tanstack/angular-pacer@059ba10&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;^21.2.8&quot;,&quot;@angular/cli&quot;:&quot;^21.2.8&quot;,&quot;@angular/compiler-cli&quot;:&quot;^21.2.10&quot;,&quot;jsdom&quot;:&quot;^29.1.0&quot;,&quot;typescript&quot;:&quot;6.0.3&quot;,&quot;vitest&quot;:&quot;^4.1.5&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/f6adfe7b-fcf9-4ad9-92dd-ed1ef00f7d86">
<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][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;div style=&quot;padding: 20px; max-width: 800px; margin: 0 auto&quot;&gt;
  &lt;h1&gt;TanStack Pacer - Rate Limiter Example&lt;/h1&gt;

  &lt;section style=&quot;margin-bottom: 40px; padding: 20px; border: 1px solid #ddd; border-radius: 8px&quot;&gt;
    &lt;h2&gt;Rate Limiter Example&lt;/h2&gt;
    &lt;p&gt;
      Rate limiting enforces a hard limit: allows executions until the limit is reached (5 per 5
      seconds), then blocks all subsequent calls until the window resets. Click the increment button
      rapidly to see the rate limiter in action.
    &lt;/p&gt;

    &lt;div style=&quot;margin-bottom: 20px&quot;&gt;
      &lt;h3&gt;Window Type&lt;/h3&gt;
      &lt;div style=&quot;display: flex; gap: 20px; margin-bottom: 15px&quot;&gt;
        &lt;label style=&quot;display: flex; align-items: center; gap: 8px; cursor: pointer&quot;&gt;
          &lt;input
            type=&quot;radio&quot;
            name=&quot;windowType&quot;
            value=&quot;fixed&quot;
            [checked]=&quot;windowType() === &#39;fixed&#39;&quot;
            (change)=&quot;setWindowType(&#39;fixed&#39;)&quot;
          /&gt;
          &lt;span&gt;Fixed Window&lt;/span&gt;
        &lt;/label&gt;
        &lt;label style=&quot;display: flex; align-items: center; gap: 8px; cursor: pointer&quot;&gt;
          &lt;input
            type=&quot;radio&quot;
            name=&quot;windowType&quot;
            value=&quot;sliding&quot;
            [checked]=&quot;windowType() === &#39;sliding&#39;&quot;
            (change)=&quot;setWindowType(&#39;sliding&#39;)&quot;
          /&gt;
          &lt;span&gt;Sliding Window&lt;/span&gt;
        &lt;/label&gt;
      &lt;/div&gt;
      &lt;p style=&quot;color: #666; font-size: 14px&quot;&gt;
        &lt;strong&gt;Fixed Window:&lt;/strong&gt; Strict window that resets completely after the period.&lt;br /&gt;
        &lt;strong&gt;Sliding Window:&lt;/strong&gt; Rolling window that allows executions as old ones expire.
      &lt;/p&gt;
    &lt;/div&gt;

    &lt;div style=&quot;margin-bottom: 20px&quot;&gt;
      &lt;h3&gt;Rate Limiter Status&lt;/h3&gt;
      &lt;div
        style=&quot;display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-bottom: 15px&quot;
      &gt;
        &lt;div&gt;&lt;strong&gt;Execution Count:&lt;/strong&gt; {{ rateLimiter.state().executionCount }}&lt;/div&gt;
        &lt;div&gt;&lt;strong&gt;Rejection Count:&lt;/strong&gt; {{ rateLimiter.state().rejectionCount }}&lt;/div&gt;
        &lt;div&gt;
          &lt;strong&gt;Remaining in Window:&lt;/strong&gt; {{ rateLimiter.getRemainingInWindow() }} / 5
        &lt;/div&gt;
        &lt;div&gt;&lt;strong&gt;Ms Until Next Window:&lt;/strong&gt; {{ rateLimiter.getMsUntilNextWindow() }}ms&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div style=&quot;margin-bottom: 20px&quot;&gt;
      &lt;h3&gt;Counters&lt;/h3&gt;
      &lt;div
        style=&quot;display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-bottom: 15px&quot;
      &gt;
        &lt;div style=&quot;padding: 15px; background: #e3f2fd; border-radius: 4px&quot;&gt;
          &lt;strong&gt;Instant Count:&lt;/strong&gt;
          &lt;div style=&quot;font-size: 24px; font-weight: bold; margin-top: 5px&quot;&gt;
            {{ instantCount() }}
          &lt;/div&gt;
          &lt;div style=&quot;font-size: 12px; color: #666; margin-top: 5px&quot;&gt;
            Updates immediately on every click
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div style=&quot;padding: 15px; background: #e8f5e9; border-radius: 4px&quot;&gt;
          &lt;strong&gt;Rate Limited Count:&lt;/strong&gt;
          &lt;div style=&quot;font-size: 24px; font-weight: bold; margin-top: 5px&quot;&gt;
            {{ limitedCount() }}
          &lt;/div&gt;
          &lt;div style=&quot;font-size: 12px; color: #666; margin-top: 5px&quot;&gt;
            Only updates when rate limit allows
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div style=&quot;margin-bottom: 20px&quot;&gt;
      &lt;h3&gt;Controls&lt;/h3&gt;
      &lt;div style=&quot;display: flex; gap: 10px; margin-bottom: 15px&quot;&gt;
        &lt;button
          (click)=&quot;increment()&quot;
          style=&quot;
            padding: 12px 24px;
            border: none;
            border-radius: 4px;
            background: #2196f3;
            color: white;
            cursor: pointer;
            font-size: 16px;
            font-weight: bold;
          &quot;
        &gt;
          Increment
        &lt;/button&gt;
        &lt;button
          (click)=&quot;reset()&quot;
          style=&quot;
            padding: 12px 24px;
            border: 1px solid #ddd;
            border-radius: 4px;
            background: white;
            cursor: pointer;
            font-size: 16px;
          &quot;
        &gt;
          Reset
        &lt;/button&gt;
      &lt;/div&gt;
      &lt;p style=&quot;color: #666; font-size: 14px&quot;&gt;
        Click &quot;Increment&quot; rapidly to see the rate limiter block executions after 5 clicks within 5
        seconds.
      &lt;/p&gt;
    &lt;/div&gt;

    &lt;div&gt;
      &lt;h3&gt;Execution History&lt;/h3&gt;
      &lt;div
        style=&quot;
          margin-top: 10px;
          padding: 10px;
          background: #f5f5f5;
          border-radius: 4px;
          max-height: 300px;
          overflow-y: auto;
        &quot;
      &gt;
        @if (executionHistory.length === 0) {
        &lt;span style=&quot;color: #999&quot;&gt;No executions yet. Click &quot;Increment&quot; to start.&lt;/span&gt;
        } @for (entry of executionHistory; track $index) {
        &lt;div
          style=&quot;
            margin-bottom: 8px;
            padding: 8px;
            background: white;
            border-radius: 4px;
            border-left: 4px solid;
          &quot;
          [style.border-left-color]=&quot;entry.rejected ? &#39;#f44336&#39; : &#39;#4caf50&#39;&quot;
        &gt;
          &lt;div style=&quot;display: flex; justify-content: space-between; align-items: center&quot;&gt;
            &lt;div&gt;&lt;strong&gt;{{ entry.timestamp }}&lt;/strong&gt; - Count: {{ entry.count }}&lt;/div&gt;
            &lt;span
              style=&quot;padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: bold&quot;
              [style.background]=&quot;entry.rejected ? &#39;#ffebee&#39; : &#39;#e8f5e9&#39;&quot;
              [style.color]=&quot;entry.rejected ? &#39;#c62828&#39; : &#39;#2e7d32&#39;&quot;
            &gt;
              {{ entry.rejected ? &#39;REJECTED&#39; : &#39;EXECUTED&#39; }}
            &lt;/span&gt;
          &lt;/div&gt;
        &lt;/div&gt;
        }
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/section&gt;

  &lt;router-outlet /&gt;
&lt;/div&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 { 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;Hello, basic&#39;)
  })
})
">
<input type="hidden" name="project[files][src/app/app.ts]" value="import { Component, signal } from &#39;@angular/core&#39;
import { RouterOutlet } from &#39;@angular/router&#39;
import { injectRateLimiter } from &#39;@tanstack/angular-pacer&#39;

@Component({
  selector: &#39;app-root&#39;,
  imports: [RouterOutlet],
  templateUrl: &#39;./app.html&#39;,
  styleUrl: &#39;./app.css&#39;,
})
export class App {
  protected readonly windowType = signal&lt;&#39;fixed&#39; | &#39;sliding&#39;&gt;(&#39;fixed&#39;)
  protected readonly instantCount = signal(0)
  protected readonly limitedCount = signal(0)
  protected readonly executionHistory: Array&lt;{
    timestamp: string
    count: number
    rejected: boolean
  }&gt; = []

  // Rate limiter: allows 5 executions per 5 seconds
  protected readonly rateLimiter = injectRateLimiter&lt;
    (count: number) =&gt; void,
    {
      executionCount: number
      rejectionCount: number
      executionTimes: Array&lt;number&gt;
    }
  &gt;(
    (count: number) =&gt; {
      console.log(&#39;Rate-limited execution:&#39;, count)
      this.limitedCount.set(count)
      this.executionHistory.push({
        timestamp: new Date().toLocaleTimeString(),
        count,
        rejected: false,
      })
    },
    {
      limit: 5,
      window: 5000, // 5 seconds
      windowType: this.windowType(),
      onReject: () =&gt; {
        console.log(&#39;Rejected by rate limiter&#39;, this.rateLimiter.getMsUntilNextWindow())
        this.executionHistory.push({
          timestamp: new Date().toLocaleTimeString(),
          count: this.instantCount(),
          rejected: true,
        })
      },
    },
    (state) =&gt; ({
      executionCount: state.executionCount,
      rejectionCount: state.rejectionCount,
      executionTimes: state.executionTimes,
    }),
  )

  protected increment(): void {
    // Update instant count immediately
    this.instantCount.update((c) =&gt; {
      const newCount = c + 1
      // Try to execute with rate limiter
      this.rateLimiter.maybeExecute(newCount)
      return newCount
    })
  }

  protected reset(): void {
    this.rateLimiter.reset()
    this.instantCount.set(0)
    this.limitedCount.set(0)
    this.executionHistory.length = 0
  }

  protected setWindowType(type: &#39;fixed&#39; | &#39;sliding&#39;): void {
    this.windowType.set(type)
    // Note: windowType change requires recreating the rate limiter in a real app
    // For this example, we&#39;ll just update the signal
  }
}
">
<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-inject-async-rate-limiter-with-persister">
</form>
<script>document.getElementById("mainForm").submit();</script>

</body></html>