From 82fd89ce6d5986f0c6060102e039ab7268d8eec8 Mon Sep 17 00:00:00 2001 From: Musa Musa Date: Sat, 29 Aug 2026 21:22:37 +0100 Subject: [PATCH] feat(examples): a runnable LLM gateway showing the policies under load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Show me the code" is the first thing anyone asks, and adaptive concurrency limiting is close to impossible to believe from prose. This is the same claim as a program that prints what it is doing. A simulated provider degrades from ~140ms to seconds at a FLAT error rate, then pushes back with 429s, then recovers. Roughly 25 requests/second across three tenants, one in five background. Per-second table: t phase limit inflight p90ms | ok 4xx shed fail 7s healthy 25 0 143 | 148 27 0 0 14s degrading 23 56 1186 | 190 34 68 2 19s degrading 21 42 3358 | 203 38 190 2 23s overloaded 5 36 7880 | 215 40 272 12 Between 7s and 19s latency rises 23x while failures stay at 2. That is the incident this library exists for, and it is the window a failure-rate breaker cannot see — it is watching errors and there are none. The limiter is watching latency, so concurrency goes 25 → 21 → 5 and the excess is shed before the timeouts start. Fifty healthy 4xx arrive across the run and none of them counts against the breaker, which is the verdict model earning its place. Covered in three files rather than one, so each use case has an obvious home: gateway.ts is policy ordering, verdicts, keys, tenants, criticality and the shared retry budget; run.ts is ctx.mark() and RejectedError.reason; upstream.ts is why concurrency rather than rate is the right lever, since latency there rises with concurrency. Type-checked and run in CI — tsconfig now includes examples/, and CI runs it for nine seconds at 4x speed, which traverses all four provider phases. An example that does not compile is worse than no example, and this is the first code anyone will read. Deliberately not a benchmark. Seeded PRNG, tuned so the transitions are visible in under a minute; the README and the docs page both say the shape is the point and the numbers are not for quoting. --- .changeset/example-gateway.md | 17 ++ .github/workflows/ci.yml | 5 + README.md | 11 ++ biome.json | 2 +- docs/.vitepress/config.ts | 1 + docs/guide/example.md | 47 +++++ examples/llm-gateway/README.md | 51 ++++++ examples/llm-gateway/gateway.ts | 74 ++++++++ examples/llm-gateway/run.ts | 125 +++++++++++++ examples/llm-gateway/upstream.ts | 93 ++++++++++ package.json | 2 + pnpm-lock.yaml | 291 ++++++++++++++++++++++++++++++- tsconfig.json | 4 +- 13 files changed, 717 insertions(+), 6 deletions(-) create mode 100644 .changeset/example-gateway.md create mode 100644 docs/guide/example.md create mode 100644 examples/llm-gateway/README.md create mode 100644 examples/llm-gateway/gateway.ts create mode 100644 examples/llm-gateway/run.ts create mode 100644 examples/llm-gateway/upstream.ts diff --git a/.changeset/example-gateway.md b/.changeset/example-gateway.md new file mode 100644 index 0000000..4a0131f --- /dev/null +++ b/.changeset/example-gateway.md @@ -0,0 +1,17 @@ +--- +"resilix": patch +--- + +Adds a runnable example: `pnpm example:gateway`. + +A simulated LLM provider degrades from ~140ms to seconds **at a flat error rate**, starts +returning `429`s, then recovers, while ~25 requests/second flow through a full pipeline across +three tenants. It prints a per-second table so the adaptation is visible rather than described: +between 7s and 19s latency rises **23× while failures stay at 2**, and the limiter walks +concurrency from 25 down to 5 and sheds the excess before the timeouts start. + +Covers policy ordering, the verdict model, per-model isolation keys, tenant fairness, criticality +shedding, a shared retry budget, `ctx.mark()` for time-to-first-token, and `RejectedError.reason`. + +It is type-checked and run in CI (nine seconds at 4× speed) — an example that does not compile is +worse than no example, and it is the first code anyone reads. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f34109c..7978c86 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,5 +27,10 @@ jobs: - run: pnpm test:paths - run: pnpm test:perf - run: pnpm test:compat + + # The example is the first code most people read, so a broken one is worse + # than none. Nine seconds at 4x speed traverses all four provider phases — + # enough to prove it runs, not enough to slow the build. + - run: RESILIX_EXAMPLE_MS=9000 RESILIX_EXAMPLE_SPEED=4 pnpm example:gateway - run: pnpm build - run: pnpm check:package diff --git a/README.md b/README.md index 7ecb9a9..1a87708 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,17 @@ because customers submitted bad input. resilix classifies outcomes into verdicts +## See it work + +```bash +pnpm example:gateway +``` + +A simulated provider degrades from ~140ms to seconds **at a flat error rate**, then recovers. +Watch the limiter walk concurrency down before any failures appear — +[examples/llm-gateway](examples/llm-gateway), written up at +[resilix.js.org/guide/example](https://resilix.js.org/guide/example). + ## Quick start diff --git a/biome.json b/biome.json index 9ebdec4..db07832 100644 --- a/biome.json +++ b/biome.json @@ -52,7 +52,7 @@ }, "overrides": [ { - "include": ["src/**/*.test.ts", "scripts/**"], + "include": ["src/**/*.test.ts", "scripts/**", "examples/**"], "linter": { "rules": { "style": { diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 3a947de..acc24dc 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -134,6 +134,7 @@ export default defineConfig({ items: [ { text: "What resilix is", link: "/guide/" }, { text: "Getting started", link: "/guide/getting-started" }, + { text: "A worked example", link: "/guide/example" }, { text: "The verdict model", link: "/guide/verdicts" }, ], }, diff --git a/docs/guide/example.md b/docs/guide/example.md new file mode 100644 index 0000000..ec8237e --- /dev/null +++ b/docs/guide/example.md @@ -0,0 +1,47 @@ +--- +description: "A runnable LLM gateway example: watch the adaptive limiter walk concurrency down as an upstream degrades at a flat error rate, then recover." +--- + +# A worked example + +Prose about adaptive concurrency limiting is hard to believe. This is the same thing as a program +you can run: + +```bash +git clone https://github.com/lintdeveloper/resilix +cd resilix && pnpm install +pnpm example:gateway +``` + +A simulated provider degrades from ~140ms to seconds **at a flat error rate**, starts returning +`429`s, then recovers. Around 25 requests/second flow through a full pipeline across three tenants. + +## The output + +``` + t phase limit inflight p90ms | ok 4xx shed fail + 7s healthy 25 0 143 | 148 27 0 0 +14s degrading 23 56 1186 | 190 34 68 2 +19s degrading 21 42 3358 | 203 38 190 2 +23s overloaded 5 36 7880 | 215 40 272 12 +``` + +Between 7s and 19s, **latency rises 23× while failures stay at 2**. A failure-rate circuit breaker +sees nothing in that window — it is watching errors, and there are none. The limiter is watching +latency, so it walks concurrency from 25 down to 5 and sheds the excess before the timeouts start. + +The `4xx` column is the [verdict model](./verdicts) doing its job: fifty healthy rejections from a +validating upstream, none of which counted against the breaker. + +## What each file shows + +| File | Use case | +|---|---| +| `gateway.ts` | policy ordering, verdicts, isolation key, tenant fairness, criticality, a shared retry budget | +| `run.ts` | `ctx.mark()` for time-to-first-token, and `RejectedError.reason` | +| `upstream.ts` | why concurrency is the right lever — latency there rises *with* concurrency | + +Source: [examples/llm-gateway](https://github.com/lintdeveloper/resilix/tree/main/examples/llm-gateway) + +It is a simulation with a seeded PRNG, tuned so the transitions are visible in under a minute — +the *shape* of the behaviour, not numbers to quote. diff --git a/examples/llm-gateway/README.md b/examples/llm-gateway/README.md new file mode 100644 index 0000000..c97b217 --- /dev/null +++ b/examples/llm-gateway/README.md @@ -0,0 +1,51 @@ +# Example — an LLM gateway + +```bash +pnpm example:gateway +``` + +A simulated provider that **degrades from ~140ms to seconds at a flat error rate**, starts pushing +back with `429`s, then recovers. Roughly 25 requests/second are offered through a resilix pipeline +across three tenants, one in five of them background work. + +It runs for 44 seconds. `RESILIX_EXAMPLE_MS` and `RESILIX_EXAMPLE_SPEED` compress it — CI runs it +for nine seconds at 4× purely to prove it still works. + +## What to watch + +The **`limit`** column against the **`p90ms`** and **`fail`** columns: + +``` + t phase limit inflight p90ms | ok 4xx shed fail + 7s healthy 25 0 143 | 148 27 0 0 +14s degrading 23 56 1186 | 190 34 68 2 ← 8x slower, still 2 failures +19s degrading 21 42 3358 | 203 38 190 2 ← 23x slower, still 2 failures +23s overloaded 5 36 7880 | 215 40 272 12 +``` + +Between 7s and 19s latency rises **23×** while failures stay at **2**. That is the incident this +library was built for, and a failure-rate circuit breaker sees nothing in that window — it is +watching errors, and there are none. The limiter is watching latency, so it walks concurrency down +from 25 to 21 to 5 and sheds the excess *before* the timeouts start. + +The **`4xx` column** is the other half. Fifty of those arrive across the run — a validating +upstream rejecting bad prompts. Every one is `answered`: healthy, never counted against the +breaker. A library whose failure predicate is *"did the promise reject?"* opens the circuit on +them, which is a self-inflicted outage on a working provider. + +## Which use case is where + +| File | What it demonstrates | +|---|---| +| `gateway.ts` | policy **ordering**, and why cheapest-refusal-first is not arbitrary | +| `gateway.ts` | **verdicts** — `classifyHttp`, so a `422` is `answered` and a `429` is `overload` | +| `gateway.ts` | **isolation key** per model, **tenant** fairness, **priority** so background sheds first | +| `gateway.ts` | a **shared retry budget** — one instance for the process, not one per pipeline | +| `run.ts` | **`ctx.mark()`** — latency is time to first token, not time to drain | +| `run.ts` | **`RejectedError.reason`**, so "why was I refused?" always has an answer | +| `upstream.ts` | why **concurrency** is the right lever: latency here rises with concurrency | + +## What it is not + +Not a benchmark. The provider is a simulation with a seeded PRNG, tuned so the transitions are +visible in under a minute. It shows the *shape* of the behaviour, not numbers you should quote. diff --git a/examples/llm-gateway/gateway.ts b/examples/llm-gateway/gateway.ts new file mode 100644 index 0000000..5112518 --- /dev/null +++ b/examples/llm-gateway/gateway.ts @@ -0,0 +1,74 @@ +/** + * The gateway: one pipeline, every policy resilix ships, wired the way you would + * actually wire them. + * + * Order matters and is not arbitrary. Cheapest and most decisive refusals go + * first, so an obviously-doomed call never occupies a slot it would only have to + * release: + * + * throttler → the upstream is refusing most of what we send; shed a fraction + * breaker → the upstream looks wholly down; fail fast + * limiter → it is up, but this is more concurrency than it can absorb + */ +import { + type Priority, + breaker, + budget, + classifyHttp, + limiter, + pipeline, + throttler, +} from "../../src/index.ts"; +import type { Reply } from "./upstream.ts"; + +export interface Job { + /** Which model — the isolation key. One bad model must not shed the others. */ + model: string; + /** Which customer, for fairness under pressure. */ + tenant: string; + /** Background work is shed before anything a user is waiting on. */ + background: boolean; +} + +/** + * ONE budget for the whole process. A per-pipeline cap cannot bound system-wide + * retry amplification, which is the entire point of having one. + */ +export const retryBudget = budget({ ratio: 0.1 }); + +export const gateway = pipeline({ + key: (job) => job.model, + tenant: (job) => job.tenant, + priority: (job): Priority => (job.background ? "bulk" : "critical"), + + // A Response is a value, not a throw, so the classifier must see it either + // way. This is what makes a 422 `answered` rather than a failure. + classify: classifyHttp, + + policies: [ + throttler(), + breaker({ + // ~3x the healthy p95. No default exists for this on purpose: "slow" is + // meaningless without your own baseline, and a wrong guess is worse than + // a required argument. + slowCallMs: 600, + slowCallRate: 0.5, + window: { calls: 60, minCalls: 8, maxAgeMs: 20_000 }, + openForMs: 4_000, + consecutiveBackstop: 12, + }), + limiter({ initialLimit: 16, minLimit: 4 }), + ], + + // Bounds the WHOLE sequence, not each attempt. Most libraries bound each + // attempt, so a caller asking for 12s can wait maxAttempts x (12s + backoff). + timeoutMs: 12_000, + retry: { maxAttempts: 3, jitter: "full", budget: retryBudget }, +}); + +/** Shape a provider reply the way the classifier expects to see it. */ +export const toResponse = (reply: Reply): Response => + new Response(null, { + status: reply.status, + headers: reply.retryAfterS ? { "retry-after": String(reply.retryAfterS) } : undefined, + }); diff --git a/examples/llm-gateway/run.ts b/examples/llm-gateway/run.ts new file mode 100644 index 0000000..7450426 --- /dev/null +++ b/examples/llm-gateway/run.ts @@ -0,0 +1,125 @@ +/** + * Drive traffic through the gateway and print what the policies are doing. + * + * pnpm example:gateway + * + * Watch the `limit` column. The provider degrades from ~120ms to seconds at a + * flat error rate; the limiter sees latency rise and walks the concurrency down + * before the errors start, and walks it back up on recovery. That adaptation is + * the thing that has no equivalent in npm, and it is hard to believe from prose. + */ +import { RejectedError, type RejectionReason } from "../../src/index.ts"; +import { type Job, gateway, toResponse } from "./gateway.ts"; +import { FakeProvider, sleep } from "./upstream.ts"; + +const MODEL = "gpt-oss-120b"; +const TENANTS = ["acme", "globex", "initech"] as const; +/** Default long enough to show degradation AND recovery; CI overrides it. */ +const RUN_MS = Number(process.env.RESILIX_EXAMPLE_MS ?? "44000"); + +const provider = new FakeProvider(); +const tally = { ok: 0, answered: 0, shed: 0, failed: 0 }; +const shedBy = new Map(); + +async function once(job: Job): Promise { + try { + const reply = await gateway.execute(job, async (ctx) => { + const r = await provider.call(); + // Time to first token, not time to drain. Judge a stream end-to-end and a + // healthy 45-second completion looks like saturation. + ctx.mark(); + return toResponse(r); + }); + if (reply.status === 200) tally.ok++; + else if (reply.status < 500) tally.answered++; + else tally.failed++; + } catch (error) { + if (error instanceof RejectedError) { + tally.shed++; + shedBy.set(error.reason, (shedBy.get(error.reason) ?? 0) + 1); + } else { + tally.failed++; + } + } +} + +function header(): void { + console.log( + "\n t phase limit inflight p90ms | ok 4xx shed fail | shed reason", + ); + console.log(` ${"─".repeat(88)}`); +} + +function row(second: number): void { + const m = gateway.metrics().find((x) => x.policy === "limiter")?.values ?? {}; + const top = [...shedBy.entries()].sort((a, b) => b[1] - a[1])[0]; + const cell = (v: number, w: number) => String(Math.round(v)).padStart(w); + const cols = [ + ` ${String(second).padStart(2)}s ${provider.phase().padEnd(11)}`, + cell(m.limit ?? 0, 6), + cell(m.inFlight ?? 0, 10), + cell(m.recentMs ?? 0, 8), + " |", + cell(tally.ok, 6), + cell(tally.answered, 5), + cell(tally.shed, 6), + cell(tally.failed, 6), + ` | ${top ? `${top[0]} x${top[1]}` : "—"}`, + ]; + console.log(cols.join("")); +} + +async function main(): Promise { + console.log(" resilix — LLM gateway example"); + console.log(" A provider that degrades at a FLAT error rate, then recovers."); + console.log(" Watch `limit` fall before the failures start, and rise again after."); + header(); + + const started = Date.now(); + const inFlight = new Set>(); + let tick = 0; + + const printer = setInterval(() => row(++tick), 1_000); + + while (Date.now() - started < RUN_MS) { + // ~25 requests/sec offered load, mixed tenants, one in five is background. + for (let i = 0; i < 5; i++) { + const job: Job = { + model: MODEL, + tenant: TENANTS[Math.floor(Math.random() * TENANTS.length)] ?? "acme", + background: Math.random() < 0.2, + }; + const p = once(job).finally(() => inFlight.delete(p)); + inFlight.add(p); + } + await sleep(200); + } + + clearInterval(printer); + await Promise.allSettled([...inFlight]); + + const total = tally.ok + tally.answered + tally.shed + tally.failed; + console.log(` ${"─".repeat(88)}`); + console.log(`\n ${total} requests offered\n`); + console.log(` ${String(tally.ok).padStart(5)} succeeded`); + console.log( + ` ${String(tally.answered).padStart(5)} answered 4xx — healthy, never opened the circuit`, + ); + console.log( + ` ${String(tally.shed).padStart(5)} shed by resilix, fast, without touching the provider`, + ); + console.log(` ${String(tally.failed).padStart(5)} failed`); + console.log("\n shed by reason:"); + for (const [reason, n] of [...shedBy.entries()].sort((a, b) => b[1] - a[1])) { + console.log(` ${String(n).padStart(5)} ${reason}`); + } + console.log(` + The 4xx column is the point of the verdict model: a boolean + 'did it reject?' breaker would have opened the circuit on those. +`); +} + +main().catch((error: unknown) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/examples/llm-gateway/upstream.ts b/examples/llm-gateway/upstream.ts new file mode 100644 index 0000000..35e9de1 --- /dev/null +++ b/examples/llm-gateway/upstream.ts @@ -0,0 +1,93 @@ +/** + * A simulated LLM provider that misbehaves the way real ones do. + * + * The point of the demo is the *transition*: a provider that stays healthy or + * stays broken teaches you nothing. This one degrades gradually, starts pushing + * back, then recovers — which is the shape resilix is built for and the shape a + * failure-rate breaker cannot see until it is too late. + */ + +export type Phase = "healthy" | "degrading" | "overloaded" | "recovering"; + +/** + * Compresses the whole scenario when the run is short. CI runs this for a few + * seconds purely to prove it does not crash; a human wants to watch it happen. + */ +export const SPEED = Number(process.env.RESILIX_EXAMPLE_SPEED ?? "1"); + +export interface Reply { + status: number; + /** Time to first token. The only latency that means anything for a stream. */ + ttfbMs: number; + retryAfterS?: number; +} + +/** Deterministic PRNG so two runs of the demo tell the same story. */ +const mulberry32 = (seed: number) => { + let state = seed | 0; + return () => { + state = (state + 0x6d2b79f5) | 0; + let t = Math.imul(state ^ (state >>> 15), 1 | state); + t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + }; +}; + +export class FakeProvider { + private readonly rand = mulberry32(0xc0ffee); + private inFlight = 0; + + constructor(private readonly startedAt = Date.now()) {} + + phase(now = Date.now()): Phase { + const s = (now - this.startedAt) / 1000; + const t = s * SPEED; + if (t < 8) return "healthy"; + if (t < 18) return "degrading"; + if (t < 26) return "overloaded"; + return "recovering"; + } + + /** + * Serve one request. Latency rises with concurrency once degraded — which is + * what makes a concurrency limit the right lever rather than a rate limit. + */ + async call(now = Date.now()): Promise { + const phase = this.phase(now); + this.inFlight++; + try { + const queueing = Math.max(0, this.inFlight - 4); + let ttfb: number; + switch (phase) { + case "healthy": + ttfb = 90 + this.rand() * 60; + break; + case "degrading": + // The incident this library came from: ~25x slower at a FLAT error rate. + ttfb = 400 + queueing * 260 + this.rand() * 400; + break; + case "overloaded": + ttfb = 700 + queueing * 300 + this.rand() * 600; + break; + default: + ttfb = 140 + queueing * 40 + this.rand() * 120; + } + + await sleep(ttfb); + + // Healthy traffic contains a lot of 4xx — bad prompts, oversized inputs. + // A boolean "did it reject?" breaker opens on this. It must not. + if (this.rand() < 0.15) return { status: 422, ttfbMs: ttfb }; + if (phase === "overloaded" && this.rand() < 0.45) { + return { status: 429, ttfbMs: ttfb, retryAfterS: 2 }; + } + if (phase !== "healthy" && this.rand() < 0.05) return { status: 500, ttfbMs: ttfb }; + return { status: 200, ttfbMs: ttfb }; + } finally { + this.inFlight--; + } + } +} + +export const sleep = (ms: number): Promise => + new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/package.json b/package.json index 2091285..888074b 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "docs:check": "vitepress build docs && node scripts/check-links.mjs", "docs:preview": "vitepress preview docs", "dev": "tsup --watch", + "example:gateway": "tsx examples/llm-gateway/run.ts", "test": "vitest run", "test:paths": "node scripts/check-test-scripts.mjs", "test:watch": "vitest", @@ -127,6 +128,7 @@ "pg": "^8.23.0", "publint": "^0.3.2", "tsup": "^8.3.5", + "tsx": "^4.23.12", "typescript": "^5.7.2", "undici": "^8.10.0", "vitepress": "^1.6.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a371977..00f7d89 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,7 +35,10 @@ importers: version: 0.3.23 tsup: specifier: ^8.3.5 - version: 8.5.1(jiti@2.7.0)(postcss@8.5.26)(typescript@5.9.3) + version: 8.5.1(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.12)(typescript@5.9.3) + tsx: + specifier: ^4.23.12 + version: 4.23.12 typescript: specifier: ^5.7.2 version: 5.9.3 @@ -317,6 +320,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.21.5': resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} @@ -329,6 +338,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.21.5': resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} @@ -341,6 +356,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.21.5': resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} @@ -353,6 +374,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.21.5': resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} @@ -365,6 +392,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.21.5': resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} @@ -377,6 +410,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} @@ -389,6 +428,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} @@ -401,6 +446,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.21.5': resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} @@ -413,6 +464,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.21.5': resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} @@ -425,6 +482,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.21.5': resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} @@ -437,6 +500,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.21.5': resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} @@ -449,6 +518,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.21.5': resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} @@ -461,6 +536,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.21.5': resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} @@ -473,6 +554,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.21.5': resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} @@ -485,6 +572,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.21.5': resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} @@ -497,6 +590,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.21.5': resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} @@ -509,12 +608,24 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.27.7': resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} @@ -527,12 +638,24 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.27.7': resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} @@ -545,12 +668,24 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.27.7': resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} @@ -563,6 +698,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.21.5': resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} @@ -575,6 +716,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.21.5': resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} @@ -587,6 +734,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.21.5': resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} @@ -599,6 +752,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@iconify-json/simple-icons@1.2.93': resolution: {integrity: sha512-/XhANjfGYOuqvSR3TmUnkQkINvQ4GVjVuukvymRbxtVFBvIq/yiXJqCDycKcQPT401OYT9H2vIY6ihAlz1QIAw==} @@ -1231,6 +1390,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1971,6 +2135,11 @@ packages: typescript: optional: true + tsx@4.23.12: + resolution: {integrity: sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==} + engines: {node: '>=18.0.0'} + hasBin: true + typescript@5.6.1-rc: resolution: {integrity: sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ==} engines: {node: '>=14.17'} @@ -2512,147 +2681,225 @@ snapshots: '@esbuild/aix-ppc64@0.27.7': optional: true + '@esbuild/aix-ppc64@0.28.2': + optional: true + '@esbuild/android-arm64@0.21.5': optional: true '@esbuild/android-arm64@0.27.7': optional: true + '@esbuild/android-arm64@0.28.2': + optional: true + '@esbuild/android-arm@0.21.5': optional: true '@esbuild/android-arm@0.27.7': optional: true + '@esbuild/android-arm@0.28.2': + optional: true + '@esbuild/android-x64@0.21.5': optional: true '@esbuild/android-x64@0.27.7': optional: true + '@esbuild/android-x64@0.28.2': + optional: true + '@esbuild/darwin-arm64@0.21.5': optional: true '@esbuild/darwin-arm64@0.27.7': optional: true + '@esbuild/darwin-arm64@0.28.2': + optional: true + '@esbuild/darwin-x64@0.21.5': optional: true '@esbuild/darwin-x64@0.27.7': optional: true + '@esbuild/darwin-x64@0.28.2': + optional: true + '@esbuild/freebsd-arm64@0.21.5': optional: true '@esbuild/freebsd-arm64@0.27.7': optional: true + '@esbuild/freebsd-arm64@0.28.2': + optional: true + '@esbuild/freebsd-x64@0.21.5': optional: true '@esbuild/freebsd-x64@0.27.7': optional: true + '@esbuild/freebsd-x64@0.28.2': + optional: true + '@esbuild/linux-arm64@0.21.5': optional: true '@esbuild/linux-arm64@0.27.7': optional: true + '@esbuild/linux-arm64@0.28.2': + optional: true + '@esbuild/linux-arm@0.21.5': optional: true '@esbuild/linux-arm@0.27.7': optional: true + '@esbuild/linux-arm@0.28.2': + optional: true + '@esbuild/linux-ia32@0.21.5': optional: true '@esbuild/linux-ia32@0.27.7': optional: true + '@esbuild/linux-ia32@0.28.2': + optional: true + '@esbuild/linux-loong64@0.21.5': optional: true '@esbuild/linux-loong64@0.27.7': optional: true + '@esbuild/linux-loong64@0.28.2': + optional: true + '@esbuild/linux-mips64el@0.21.5': optional: true '@esbuild/linux-mips64el@0.27.7': optional: true + '@esbuild/linux-mips64el@0.28.2': + optional: true + '@esbuild/linux-ppc64@0.21.5': optional: true '@esbuild/linux-ppc64@0.27.7': optional: true + '@esbuild/linux-ppc64@0.28.2': + optional: true + '@esbuild/linux-riscv64@0.21.5': optional: true '@esbuild/linux-riscv64@0.27.7': optional: true + '@esbuild/linux-riscv64@0.28.2': + optional: true + '@esbuild/linux-s390x@0.21.5': optional: true '@esbuild/linux-s390x@0.27.7': optional: true + '@esbuild/linux-s390x@0.28.2': + optional: true + '@esbuild/linux-x64@0.21.5': optional: true '@esbuild/linux-x64@0.27.7': optional: true + '@esbuild/linux-x64@0.28.2': + optional: true + '@esbuild/netbsd-arm64@0.27.7': optional: true + '@esbuild/netbsd-arm64@0.28.2': + optional: true + '@esbuild/netbsd-x64@0.21.5': optional: true '@esbuild/netbsd-x64@0.27.7': optional: true + '@esbuild/netbsd-x64@0.28.2': + optional: true + '@esbuild/openbsd-arm64@0.27.7': optional: true + '@esbuild/openbsd-arm64@0.28.2': + optional: true + '@esbuild/openbsd-x64@0.21.5': optional: true '@esbuild/openbsd-x64@0.27.7': optional: true + '@esbuild/openbsd-x64@0.28.2': + optional: true + '@esbuild/openharmony-arm64@0.27.7': optional: true + '@esbuild/openharmony-arm64@0.28.2': + optional: true + '@esbuild/sunos-x64@0.21.5': optional: true '@esbuild/sunos-x64@0.27.7': optional: true + '@esbuild/sunos-x64@0.28.2': + optional: true + '@esbuild/win32-arm64@0.21.5': optional: true '@esbuild/win32-arm64@0.27.7': optional: true + '@esbuild/win32-arm64@0.28.2': + optional: true + '@esbuild/win32-ia32@0.21.5': optional: true '@esbuild/win32-ia32@0.27.7': optional: true + '@esbuild/win32-ia32@0.28.2': + optional: true + '@esbuild/win32-x64@0.21.5': optional: true '@esbuild/win32-x64@0.27.7': optional: true + '@esbuild/win32-x64@0.28.2': + optional: true + '@iconify-json/simple-icons@1.2.93': dependencies: '@iconify/types': 2.0.0 @@ -3297,6 +3544,35 @@ snapshots: '@esbuild/win32-ia32': 0.27.7 '@esbuild/win32-x64': 0.27.7 + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + escalade@3.2.0: {} esprima@4.0.1: {} @@ -3730,12 +4006,13 @@ snapshots: mlly: 1.8.2 pathe: 2.0.3 - postcss-load-config@6.0.1(jiti@2.7.0)(postcss@8.5.26): + postcss-load-config@6.0.1(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.12): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 2.7.0 postcss: 8.5.26 + tsx: 4.23.12 postcss@8.5.26: dependencies: @@ -3986,7 +4263,7 @@ snapshots: ts-interface-checker@0.1.13: {} - tsup@8.5.1(jiti@2.7.0)(postcss@8.5.26)(typescript@5.9.3): + tsup@8.5.1(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.12)(typescript@5.9.3): dependencies: bundle-require: 5.1.0(esbuild@0.27.7) cac: 6.7.14 @@ -3997,7 +4274,7 @@ snapshots: fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.7.0)(postcss@8.5.26) + postcss-load-config: 6.0.1(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.12) resolve-from: 5.0.0 rollup: 4.62.4 source-map: 0.7.6 @@ -4014,6 +4291,12 @@ snapshots: - tsx - yaml + tsx@4.23.12: + dependencies: + esbuild: 0.28.2 + optionalDependencies: + fsevents: 2.3.3 + typescript@5.6.1-rc: {} typescript@5.9.3: {} diff --git a/tsconfig.json b/tsconfig.json index 77ac7ca..6d9fdc9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,5 +26,7 @@ "forceConsistentCasingInFileNames": true, "outDir": "dist" }, - "include": ["src"] + // examples/ is type-checked too: an example that does not compile is worse + // than no example, and it is the first code anyone reads. + "include": ["src", "examples"] }