# Changelog All notable changes to Meridian are documented here. --- ## [0.5.0] — 2026-06-28 An engineering-hardening pass across performance, reliability, and security — no new provider adapters. See the priority order in the project roadmap; this release covers lazy-loading/tree-shaking, stricter TypeScript, mutation/property/fuzz testing, and several real bugs the new testing surfaced. ### Breaking - **Built-in adapter classes are no longer exported from the `meridianjs` root.** `import { StripeAdapter } from "meridianjs"` used to force-load all 46 provider adapter modules on every `import "meridianjs"`, even if you only use one provider — the actual root cause of the "library import" startup cost. Import from the category subpath instead: ```ts // Before import { StripeAdapter } from "meridianjs"; // After import { StripeAdapter } from "meridianjs/providers/payments"; ``` New subpaths: `meridianjs/providers/{ai,crm,healthcare,identity,logistics,maps,messaging,monitoring,payments,storage,tax}`. Config-driven usage (`providers: { stripe: { auth } }`, no adapter import) is unaffected. See [docs/adapters.md](docs/adapters.md). ### Added - **`meridian doctor`** — a read-only, severity-ranked health check. It reads what already lives on disk inside `.meridian/` (the contract registry's drift history and the reliability recordings) plus the runtime, and reports a single ranked list of findings: schemas with breaking changes or that have gone stale, recorded sessions where a circuit breaker opened / most requests failed / retries stormed / latency spiked, and which optional integrations (`ai`, `@grpc/grpc-js`, `@opentelemetry/api`) are installed. No network calls or running app required — the same disk-only contract as `meridian studio`. Exits non-zero on critical findings; `--strict` also fails on warnings (a drop-in CI gate), `--json` emits the full report. See [docs/doctor.md](docs/doctor.md). - **`AbortSignal` support on every request.** `RequestOptions.signal` cancels an in-flight request independently of the timeout; surfaces as a `MeridianError` (category `network`, `retryable: false`), distinct from a timeout error. - **Per-request `timeout` is now actually honored.** It was a documented `RequestOptions` field the pipeline silently ignored, always falling back to the pipeline-level default regardless of what a caller passed per-call. - **Pattern-based secret redaction** (`src/core/secret-redactor.ts`), layered on top of the existing key-name redaction in both the request sanitizer and the observability sanitizer. A `sk-...`, `Bearer ...`, AWS access key, JWT, or PEM private-key block embedded under any key name — not just `authorization`/`token`/`apiKey` — is now redacted from logs, headers, query params, and bodies unconditionally (not gated behind PII/India-mode flags). Verified with `fast-check` property tests fuzzing arbitrary key names and credential shapes. - **`fast-check`-based property and fuzz tests**: secret redaction (above), pagination's `extractTotal`, and a pipeline-level header fuzz test that throws hostile/oversized/CRLF-laced header values through the real request path and asserts every failure surfaces as a `MeridianError`, never a raw crash. - **Mutation testing** via Stryker (`npm run test:mutation`), scoped to `src/core` and `src/resilience` — the reliability primitives, not the 46 largely-mechanical provider adapters (already covered by the per-provider contract suite). Not wired into CI as a gate yet; current baseline and next steps are noted inline in `stryker.conf.json`. - **API reference generation** via TypeDoc (`npm run docs:api`), entry-pointed at `src/public.ts`. Surfaced and fixed several public types referenced by the public API but not themselves exported (`CircuitBreakerConfig`, `RateLimitConfig`, `RetryConfig`, `AdapterInput`, `BuiltRequest`, `RawResponse`, `StudioServerHandle`, `StudioServerOptions`, `ConsoleObservabilityConfig`). - **`PrometheusObservability` is now exported from the public API** (`meridianjs`) and has test coverage for the first time — it was fully implemented but unreachable from the documented `public.ts` surface and untested. - `benchmarks/startup.ts` (`npm run benchmark:startup`) — cold-subprocess measurement of library-import and CLI-startup time against the <50ms / <100ms targets, net of the Node process floor. Wired into CI as an informational report (not a hard gate — absolute-ms thresholds are sensitive to shared-runner contention). - **`AbortSignal` now also cancels `.stream()` and `batch()`**, not just `get`/`post`/`put`/`patch`/`delete`. `batch(requests, concurrencyLimit, signal)` stops *starting* new requests once aborted (already-in-flight ones complete naturally); `ServiceClient.batch()` forwards it too. - **Chaos testing** (`src/core/chaos.test.ts`) — fast-check-driven random injection of 5xx/429/timeouts/connection resets/corrupted JSON across the request pipeline, asserting the one invariant that must always hold: every failure surfaces as a `MeridianError`, never a raw error, and the circuit breaker still opens correctly under randomized fault sequences. - `benchmarks/memory.ts` (`npm run benchmark:memory`) — drives many requests through one long-lived instance with every buffering feature on and checks heap usage plateaus rather than growing linearly. `benchmarks/provider-init.ts` (`npm run benchmark:provider-init`) — quantifies the lazy-loading change directly: init cost scales with *configured* providers, not with the 46 that exist. - `Meridian.providers()` / `findProviders()` results are now cached and invalidated only on `registerProvider()` — previously rebuilt a `Set` + array from every adapter's `capabilities()` on every call, including from Studio endpoints that call it more than once per request. ### Fixed - **Streaming requests (`.stream()`) leaked raw, unwrapped errors.** Unlike the regular pipeline path, the streaming code's `fetch()` call had no error handling at all — a network failure or a synchronous `Headers` validation throw propagated to the caller as a raw `TypeError` instead of the `MeridianError` every other failure path in the SDK returns. - **Any already-classified internal error (timeout, caller-aborted signal) was silently reclassified.** The pipeline's outer error handler always re-ran every error through `adapter.parseError()`, even one that was already a `MeridianError` — so, for example, a timeout's specific "Request timeout after Nms" message was being discarded and replaced with a generic adapter fallback message (e.g. GitHub's "Network request failed. Check your connection and try again.") on every timeout, silently, since the timeout feature was added. - **A malformed pagination `total` header could drive pagination to the hard page cap instead of stopping.** `Number.parseInt("garbage", 10)` is `NaN`, which is `!== null` but compares `false` against every number — `extractCursor`'s end-of-results check could then never fire. Affects both `OffsetPaginationStrategy` and `CursorPaginationStrategy`. - **`PrometheusObservability`'s cardinality-reduction endpoint normalization mangled UUIDs starting with a digit** (≈62.5% of random v4 UUIDs) — the numeric-ID regex ran first and partially consumed the UUID segment before the UUID regex got a chance to match. - **`DebugRecorder` and `ReliabilityRecorder` grew without bound.** Left enabled/recording in a long-running process, both accumulated entries forever. `DebugRecorder` now caps at `maxEntries` (default 1000) with FIFO eviction — safe, since each entry is independent. `ReliabilityRecorder` caps at `maxEvents` (default 50,000) and marks the session `truncated` instead of evicting — FIFO eviction would have silently corrupted the *middle* of an incident timeline used for replay/outage analysis. - **`ProviderCircuitBreaker.recentResults` grew without bound under sustained high throughput.** It was pruned by age (`rollingWindowMs`, default 60s) but had no count cap — traffic completing faster than the window elapses meant nothing aged out, so the array grew proportionally to throughput × window with no upper bound. Found via an actual heap-growth probe (`benchmarks/memory.ts`), not just code review: heap climbed ~0.6 MB/1,000 requests before the fix, flat at ~0.0 MB/1,000 after. Now hard-capped at 1,000 most-recent results. - **`.stream()` silently ignored a caller-supplied `AbortSignal`** — the type allowed passing one, but the direct `fetch()` call never read it. - Pagination's cycle-detection and page-limit errors now throw `MeridianError` instead of a raw `Error`, matching every other failure path in the SDK. ### Changed - All 46 built-in provider adapters now load via dynamic `import()` on first use (`BUILTIN_ADAPTER_LOADERS` in `src/index.ts`), instead of being eagerly imported at module scope — combined with the breaking change above, library-import time now passes the <50ms-over-floor target (was failing it). - `noImplicitOverride` and `useUnknownInCatchVariables` enabled in `tsconfig.json`. (`noPropertyAccessFromIndexSignature` was evaluated and rejected — it produced 500+ mechanical `body.x` → `body['x']` rewrites across provider response parsing, for no real safety gain over the existing `unknown`-typed index signatures.) - CI's hardcoded-secret grep now matches the same AWS-key/JWT/PEM-key patterns as the new runtime redactor, instead of only `sk-`/`AIza`/`Bearer`. - JSDoc added to the `Meridian` class and its previously-undocumented public methods (`provider`, `registerProvider`, `service`, `analytics`, `health`, `cost`, `providers`, `findProviders`, `getCircuitStatus`, `transaction`), plus `SharedCooldownManager`, `StudioServerHandle`, `TransactionError`/`TransactionStep`/`TransactionResult`. ### Known limitation (not fixed, documented for a future decision) - **Bundler tree-shaking can't reduce shipped bundle size for `Meridian`, even with the lazy-loading change above.** `BUILTIN_ADAPTER_LOADERS` (the dynamic-import dispatch table) lives in the same module as the `Meridian` class itself, so any bundler doing static analysis sees all 46 dynamic-import targets as reachable the moment `Meridian` is imported at all — regardless of which provider you actually configure, or whether you bypass auto-registration entirely by passing an explicit adapter instance. Confirmed with an esbuild `--splitting` bundle: 1.5MB / 46 chunks shipped either way. The Node-runtime win (confirmed via `benchmarks/startup.ts`) is real and unaffected — this only affects consumers who bundle Meridian for browser/edge shipping, which is not its primary use case (it's a Node backend reliability middleware). Fixing it for real would mean restructuring auto-registration so it's not statically reachable from `Meridian.create()` itself — a bigger API change than this pass made unilaterally. --- ## [0.4.0] — 2026-06-22 ### Added - **Meridian Studio** — a local dashboard for provider health, costs, circuit states, failovers, replay timelines, and schema drift. `await meridian.studio({ port, authToken })` starts the API server in-process with live data; `meridian studio` starts it standalone (disk-only — replay sessions and schema-drift history work without a running app). The dashboard itself ships as a separate app, not part of this package. New supporting methods: `Meridian.recordingStatus()`, `ContractRegistry.listProviders()`. See [docs/studio.md](docs/studio.md). - **`meridianjs/ai`** — Vercel AI SDK middleware. `meridianReliability()` is a `LanguageModelV3Middleware` (use with `wrapLanguageModel`) that wraps language-model calls with retries, circuit breaking, and failover across models, plus the same `ObservabilityAdapter` interface used for HTTP calls. No request/response translation needed — the AI SDK already normalizes every provider into one `doGenerate`/`doStream` interface. Defaults `failoverOn` to `["rate_limit", "network", "provider"]`, excluding `auth`/`validation` (a bad key is a config problem, not an outage — matches `ServiceClient`'s existing default). `ai`/`@ai-sdk/provider` are new optional peer dependencies. See [docs/ai-sdk.md](docs/ai-sdk.md). - **`scripts/check-release-facts.mjs`** — verifies the provider count, contract-test count, total test count, and SECURITY.md's supported-version range against the real registry/test suite; wired into CI (`npm run check:release-facts`) so docs can't silently drift from reality again. - All four `demo:*` scripts (`failover`, `circuit-breaker`, `schema-drift`, `service-routing`) are now a mandatory CI gate — none were previously run in CI. ### Fixed - **The flagship README/demo failover example didn't work.** POST/PATCH failover was correctly disabled back in 0.3.3 to prevent duplicate side effects, but the README's quick-start and `demo:failover` still called `.post()` and claimed automatic OpenAI→Anthropic failover — so `npm run demo:failover` crashed with an unhandled `MeridianError`, and anyone following the README hit the same wall. `demo:failover` now demonstrates the real, current behavior: a `GET` fails over to Anthropic automatically (idempotent, safe), while a `POST` is correctly refused failover with a clear error explaining why. `docs/failover/index.md` now states the idempotent-only failover rule explicitly, since its payments example had the same false-safety implication. - **README and package.json provider/test counts were stale.** README claimed 47 adapters and 1637 contract tests; the actual registry has 46 adapters and 874 contract tests (19 invariants × 46 providers, verified via `vitest list`). SECURITY.md still listed `0.2.x` as supported. `biome.json`'s `$schema` pointed at an older Biome version than the one installed. ### Changed - README headline changed from "One contract. Every API." to "Reliability middleware for third-party APIs." — the original implied Meridian translates request/response bodies between different providers' APIs, which it doesn't (and structurally can't, generically, across unrelated business domains — that's what the new `meridianjs/ai` middleware solves for the one domain where it's actually possible, because the AI SDK already did the normalization). - npm package `description` and `keywords` expanded for discoverability (`failover`, `resilience`, `fault-tolerance`, `incident-replay`, `schema-drift`, `opentelemetry`, `openai`, `anthropic`, `llm`, `nodejs`, etc.) — previously only 6 generic keywords. --- ## [0.3.4] — 2026-06-20 ### Changed - Rust client version aligned to engine version (`0.1.0` → `0.3.4`). - Python client version bumped to `0.3.4` to match engine. --- ## [0.3.3] — 2026-06-19 ### Fixed - **RateLimiter adaptive backoff permanently collapses throughput** — the adaptive backoff algorithm only ratcheted the rate downward with no recovery path, and used an inverted formula (consumed tokens as numerator instead of remaining), causing the rate to collapse to ~1-2 req/s after any spike and never recover. The rate limiter now maintains a baseline ceiling, throttles relative to the baseline, and recovers when utilization drops below 80%. Regression tests assert recovery curve and no permanent degradation. - **Failover replays non-idempotent writes** — both the payment router and service client auto-failed POST/PATCH requests to secondary providers on network/rate-limit/provider errors, causing duplicate charges and state mutations (idempotency keys are provider-scoped and don't prevent second-provider submission). POST and PATCH now throw their original error without failover, requiring callers to explicitly reconcile state before manual retry. GET, PUT, DELETE continue to failover normally as they are idempotent by design. - **Stripe webhook verifier lacks timestamp freshness check** — `verifyWebhook` validated the HMAC but never compared the signed `t` timestamp to the current time, accepting 6-year-old captured webhooks and enabling indefinite replay. The verifier now rejects events older than 300 seconds (matching Stripe's own 5-minute tolerance); the tolerance is configurable per call and can be disabled with `Infinity` if needed. - **OffsetPaginationStrategy advances forever without a total** — when no total count is available, `extractCursor` always returned offset+limit, keeping `hasNext: true` and iterating until the 1000-page hard cap instead of terminating cleanly. The strategy now detects empty pages and stops when there are no more results, preventing wasteful requests and exceptions. - **IdempotencyResolver crashes on override patterns with regex metacharacters** — the pattern `/:[\w-]+/g` replacement fed directly into `new RegExp` without escaping literal segments, so override keys like `POST /charge(v2` produced invalid regex and threw during idempotency lookup. The resolver now escapes metacharacters before substitution and wraps in try/catch; uncompilable patterns simply don't match. - **Circuit breaker counts each retry attempt as a separate failure** — the architecture placed the breaker inside the retry loop, so a request that failed and retried 3 times counted as 3 failures rather than 1, opening the breaker 3-6× faster than `failureThreshold` implied. The breaker now wraps the retry strategy (not individual attempts), counting logical requests only; this is both more correct semantically and prevents retry inflation of the failure counter. - **`paginate()` throws spurious "infinite pagination loop" error on the boundary page** — when results ended naturally on exactly the 1000th page, the post-loop hard-cap guard fired after all data had already been yielded, making a complete run indistinguishable from a genuine runaway loop. The generator now returns cleanly on natural completion; the guard only fires when the cap truncates an ongoing sequence. - **`OffsetPaginationStrategy` cycle-detects on page 2 when provider does not echo offset** — `extractCursor` read the current offset from `response.body.offset`; providers that omit it caused `currentOffset` to reset to 0 on every page, returning the same cursor repeatedly and triggering the paginator's cycle guard. The strategy now threads the offset through its own state, updated in `buildNextRequest`, so pagination advances correctly regardless of response shape. - **`handle429` does not drain existing tokens** — `handle429` pushed `lastRefill` into the future to pause new token generation, but tokens already in the bucket let requests bypass the retryAfter window entirely. The bucket is now drained to zero alongside the refill-pause so all subsequent callers are queued until the window elapses. --- ## [0.3.1] — 2026-06-14 ### Changed - Set homepage to `https://meridianjs.raghav-verma.com` in `package.json`. --- ## [0.3.0] — 2026-06-14 ### Added - **Docker Boundary Proxy** — `Dockerfile`, `docker-compose.yml`, and `.env.example` ship in the repo root. `docker compose up -d` starts the gRPC engine on `127.0.0.1:4242` with a built-in healthcheck — no Node or npm required on the host. See [docs/polyglot.md](docs/polyglot.md). - **Go reference client** ([`clients/go`](clients/go)) — a complete, end-to-end-tested binding with pre-generated protobuf stubs, a typed `Client` struct, and a conformance suite that boots the proxy in Docker and asserts auth enforcement, SSRF guards, and error normalization. `go get github.com/Raghaverma/meridianjs/clients/go/meridian`. - **Rust client** ([`clients/rust`](clients/rust)) — async `tonic`-based binding; generates stubs from `proto/meridian.proto` at build time via `build.rs`. Normalized errors and the same call shape as the Go client. - **`StreamCall` gRPC RPC** — streams SSE token deltas (Anthropic, OpenAI, Gemini, Cohere, Mistral, …) to any gRPC client language-by-language, with no JS runtime required. Emits one `StreamChunk` per upstream delta; the terminal chunk carries `done: true`. See `proto/meridian.proto`. - **Proto CI job** — `buf lint`, breaking-change detection against `origin/main` (on PRs), Go stub freshness check (`clients/go/genproto` must match `make generate`), Go build + vet, and the full conformance suite run on every push. Uses the same `bufbuild/buf:1.70.0` image as the `Makefile` so CI and local never drift. - **`meridian add `** — one-command provider generation: resolves the OpenAPI spec (curated registry: slack, github, stripe, twilio, box, sendgrid — or `--openapi `), then generates the adapter, unit tests, **contract tests** (the same 19-invariant battery every built-in adapter passes — green out of the box), a pagination strategy inferred from the spec's query parameters (cursor/page/offset style + exact param name), retry classification grounded in the spec's documented status codes, and a `GENERATED.md` completeness report scoring what was inferred vs assumed (every heuristic carries a `TODO(meridian-generator)` marker). - **OpenTelemetry auto-instrumentation** — `telemetry: { provider: "opentelemetry" }` (or `meridian.instrumentOpenTelemetry()`) binds spans/metrics/errors to `@opentelemetry/api` (new optional peer dependency) with one line; exporter recipes for Datadog, Grafana, Honeycomb, and New Relic in [docs/opentelemetry.md](docs/opentelemetry.md). - **Reliability replay** — `meridian.startRecording(name)` / `stopRecording()` capture the pipeline's behavior timeline (outcomes, retries, breaker states, latencies — never payloads) to `.meridian/recordings/.json`; `meridian replay ` re-renders the outage locally with derived failovers, breaker transitions, and latency stats; `meridian.replaySession()` re-emits the timeline through observability adapters. See [docs/reliability-replay.md](docs/reliability-replay.md). - **Adaptive routing** — `strategy: "adaptive"` for services scores providers on observed success rate + latency + circuit-breaker state with explicit `adaptiveWeights`; ranking is deterministic (ties explore unmeasured providers once, then settle to config order). - **`meridian migrate `** — scans a codebase for direct SDK imports, client constructions, mapped method calls, and hand-rolled HTTP calls (openai, anthropic, stripe, github, twilio, sendgrid, razorpay), reporting what maps cleanly to Meridian and what needs manual attention, plus a suggested provider config. Read-only — no rewriting. - **Local contract registry** — `meridian.registry.snapshot/check/report/history` and the `meridian registry` CLI: versioned response-schema snapshots with append-only drift history under `.meridian/registry/`, designed to be committed to git; `registry check` exits non-zero on breaking drift for CI gating. See [docs/registry.md](docs/registry.md). - **Unified CLI** — the `meridian` binary now dispatches `add`, `generate`, `migrate`, `replay`, and `registry` subcommands. ### Fixed - **Anthropic and OpenAI auth in the Boundary Proxy** — `shared.ts` was placing the API key in `auth.apiKey`, but those adapters read `auth.token`; the mismatch silently broke all proxied calls to both providers (including `StreamCall`). The key is now placed in `auth.token` as the adapters expect. - **HTTP errors were never retried.** `executeHttpRequest` throws raw `{status, headers, body}` objects, but the retry strategy only retried errors already carrying `retryable: true` — so a real 429/503 from a provider failed immediately regardless of retry config (only timeouts and pre-classified mock errors ever retried). The pipeline now classifies raw HTTP failures through the adapter's `parseError` at the retry decision point, while propagating the original error unchanged. - **OpenTelemetry metric corruption** — `OpenTelemetryObservability.recordMetric()` funneled every pipeline metric into the `meridian.requests` counter, inflating request counts; named metrics now get their own counters. - **Flat-config key collision** — top-level `services`, `policies`, `providerCosts`, and the new `telemetry` keys were treated as provider configs when using the flat config style. --- ## [0.2.12] ### Fixed - **Python client tests** — `clients/python/tests` is now a proper package (`tests/__init__.py`), so `pytest` can resolve `from tests.conftest import ...` in `test_client.py` and `test_grpc.py`. --- ## [0.2.11] ### Added - **Hunter provider** — full request/response normalization, auth handling, and error mapping, bringing the adapter count to 46 (874 contract tests). - **Polyglot contract** — the Meridian contract is now defined as a language-neutral gRPC IDL ([`proto/meridian.proto`](proto/meridian.proto)), covering `RequestOptions`, `NormalizedResponse`, `ResponseMeta`, and `MeridianError`. - **gRPC Boundary Proxy** — `src/proxy/grpc-server.ts` replaces the previous HTTP proxy, serving `meridian.v1.Meridian` (`Call`, `Paginate`, `Health`) backed by the TS engine. `@grpc/grpc-js` and `@grpc/proto-loader` are optional peer dependencies, loaded lazily so the SDK core stays dependency-free. - **Native Python engine** ([`clients/python`](clients/python)) — a from-scratch Python port of the pipeline (retry, circuit breaking, rate limiting, sanitization, normalization) with reference adapters for GitHub, OpenAI, Anthropic, and Stripe. It runs standalone and speaks the same proto, so it can serve the contract or consume either engine. ### Changed - **Boundary Proxy** — `npx boundary-proxy` now starts a gRPC server instead of an HTTP server; the previous Node-based HTTP proxy is deprecated in favor of the gRPC bridge. --- ## [0.2.10] ### Fixed - **Observability** — `error.message` and `error.name` were silently dropped from the `error` object passed to `ObservabilityAdapter.logError()` (Console, OTel, Prometheus adapters). `Error` properties are non-enumerable, so a plain object spread in the pipeline's error handler discarded them, leaving every logged error with `message: undefined`. Both fields are now preserved. ### Added - `docs/comparisons/` — Meridian vs. Raw SDKs, LangChain, OpenRouter, and API Gateways, plus an overview index - `docs/what-is-meridian.md` — category-definition doc (reliability layer vs. wrapper / gateway / iPaaS) - `npm run demo:failover`, `demo:circuit-breaker`, `demo:schema-drift`, `demo:service-routing` — narrative demo scripts showing each failure mode end-to-end --- ## [0.2.5] — Adoption Sprint ### Added **Service Routing — Weighted & Geo** - `strategy: "weighted"` — probabilistic load distribution across providers using a `weights` map (e.g. `{ stripe: 70, razorpay: 30 }`) - `strategy: "geo"` — region-aware routing via `MERIDIAN_REGION` env var or `defaultRegion`; `regions` maps region names to ordered provider lists - Both strategies select a primary provider via `selectIndex()` and failover through remaining providers on retryable errors **Policy Engine — Three New Built-ins** - `redact(fields, providers?)` — redacts dot-notation field paths (e.g. `"user.ssn"`) from the request body before it reaches the provider; does not block the request - `requireFields(fields, providers?)` — blocks requests missing required body fields; returns `MeridianError` with `category: "validation"` - `denyCountries(codes, field?)` — blocks requests where `body.country`, `body.country_code`, or `body.countryCode` matches a denied ISO 3166-1 alpha-2 code **Schema Monitor — Three New Methods** - `meridian.schema.diff(provider, endpoint, data)` — returns drift between current data and stored schema baseline - `meridian.schema.report(provider)` — returns structured `SchemaReport` with all snapshotted endpoints, field counts, and schemas - `meridian.schema.alert(provider, endpoint, data, callback)` — runs drift check and invokes callback with drifts if any are detected; returns the drifts array **Documentation** - `docs/payments/` — Stripe/Razorpay/Cashfree unified interface, failover, analytics - `docs/llms/` — OpenAI→Anthropic→Gemini failover, production chat endpoints - `docs/communications/` — Twilio→MSG91 SMS fallback, email fallback patterns - `docs/failover/` — all 7 routing strategies with runnable examples - `docs/policies/` — all policy built-ins with fintech compliance example - `docs/schema-drift/` — full snapshot→diff→report→alert workflow - `docs/transactions/` — saga pattern with multi-step rollback example **Cost Intelligence** - `MeridianConfig.providerCosts` — declare per-request cost for each provider (e.g. `{ openai: 0.03, anthropic: 0.01 }`) - `meridian.cost(currency?)` — returns `CostReport` with per-provider request counts, cost-per-request, estimated spend, and a total; resets with `analytics.reset()` - `CostReport`, `CostEntry` exported from `meridianjs` **Examples** - `examples/nextjs-openai-failover/` — Next.js 14 App Router LLM endpoint with failover - `examples/express-stripe/` — Express server with pagination, idempotency, health endpoint - `examples/nestjs-payments/` — NestJS module with DI, weighted routing, saga transaction - `examples/fastify-webhooks/` — Fastify webhook verification with raw body parsing - `examples/multi-provider-llm/` — Node.js script demonstrating failover, cheapest routing, drift detection, analytics --- ## [0.2.4] — Operational Intelligence This release transforms Meridian from an API normalisation SDK into an operational layer for third-party integrations. Every feature below works with zero configuration beyond what you already have. ### Added **Service Abstraction & Failover** - `meridian.service("name")` — logical service client that routes across multiple providers - `MeridianConfig.services` — configure provider groups with a routing strategy - Routing strategies: `"failover"`, `"round-robin"`, `"lowest-latency"`, `"cheapest"`, `"highest-success-rate"` - `"lowest-latency"` self-calibrates via EWMA over `meta.trace.latency` - `"highest-success-rate"` routes dynamically using live analytics data - `"cheapest"` accepts a `costs` map and fails over in ascending cost order **Request Trace** - `result.meta.trace` — always present: `retries`, `latency`, `circuitBreaker`, `rateLimitRemaining` - `ResponseContext.trace` — trace data now visible to all observability adapters **Analytics & Health** - `meridian.analytics()` — per-provider: `requests`, `errors`, `errorRate`, `successRate`, `avgLatency`, `p95Latency` - `meridian.health()` — per-provider status (`healthy`/`degraded`/`down`) combining analytics + circuit breaker - `AnalyticsCollector` always active, zero configuration required **Debug Recording & Replay** - `meridian.debug.enable()` / `.disable()` / `.clear()` - `meridian.debug.recordings()` — full log with trace data and original request options - `meridian.replay(requestId)` — re-execute any recorded request with identical options **Policy Engine** - `MeridianConfig.policies` — evaluate before every request, block or allow - Built-ins: `blockPII()`, `allowedProviders()`, `blockedProviders()`, `readOnly()`, `customPolicy()` - PII detection covers: credit cards, SSNs, emails, phone numbers, Aadhaar, PAN - Blocked requests throw `MeridianError` with `category: "validation"` — no network round-trip wasted **Multi-Provider Transactions** - `meridian.transaction(steps)` — saga pattern across multiple providers - Per-step `rollback` function; executed in reverse order on failure - `TransactionError` carries `failed`, `succeeded`, `rolledBack`, `rollbackErrors`, `results` **Schema Drift Detection (enhanced)** - `meridian.schema.snapshot(provider, endpoint, data)` — baseline any live response - `meridian.schema.check(provider, endpoint, data)` — detect field removals, type changes, required changes - `DriftDetector` now recurses into nested object and array schemas **Provider Capability Registry** - `meridian.providers()` — all configured providers with capability arrays - `meridian.findProviders({ capability })` — filter by capability string - 39 providers mapped: chat, embeddings, streaming, payments, kyc, upi, shipping, maps, and more **Adapter Generator** - `npx meridian generate --provider [--openapi spec.json]` - Parses OpenAPI 3.x JSON: extracts base URL, auth type, endpoint list - Generates `adapter.ts`, `adapter.test.ts` (8 passing tests out of the box), `pagination.ts`, `index.ts` ### Changed - `DriftDetector.detect()` now recursively compares nested schemas (previously top-level only) - `ResponseContext` gains optional `trace?: RequestTrace` for observability adapter use --- ## [0.2.3] — SDK Prototype - 39 provider adapters with unified contract - Normalized responses, errors, pagination, rate limits - Circuit breaker, retry, rate limiting - Webhook verification - Contract testing suite (19 invariants per adapter) - Streaming (SSE) for OpenAI, Anthropic, Gemini, Mistral, Cohere - Batch requests with concurrency control - India compliance mode (DPDPA PII redaction) - Proxy server (`boundary-proxy`) - Observability adapters: Console, NoOp, OpenTelemetry, Prometheus