meridianjs 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/CHANGELOG.md +97 -150
  2. package/README.md +214 -122
  3. package/dist/analytics/collector.d.ts +44 -0
  4. package/dist/analytics/collector.d.ts.map +1 -0
  5. package/dist/analytics/collector.js +82 -0
  6. package/dist/analytics/collector.js.map +1 -0
  7. package/dist/analytics/index.d.ts +3 -0
  8. package/dist/analytics/index.d.ts.map +1 -0
  9. package/dist/analytics/index.js +2 -0
  10. package/dist/analytics/index.js.map +1 -0
  11. package/dist/capabilities/index.d.ts +3 -0
  12. package/dist/capabilities/index.d.ts.map +1 -0
  13. package/dist/capabilities/index.js +2 -0
  14. package/dist/capabilities/index.js.map +1 -0
  15. package/dist/capabilities/registry.d.ts +6 -0
  16. package/dist/capabilities/registry.d.ts.map +1 -0
  17. package/dist/capabilities/registry.js +61 -0
  18. package/dist/capabilities/registry.js.map +1 -0
  19. package/dist/core/pipeline.d.ts +3 -1
  20. package/dist/core/pipeline.d.ts.map +1 -1
  21. package/dist/core/pipeline.js +42 -0
  22. package/dist/core/pipeline.js.map +1 -1
  23. package/dist/core/types.d.ts +42 -1
  24. package/dist/core/types.d.ts.map +1 -1
  25. package/dist/core/types.js +1 -1
  26. package/dist/core/types.js.map +1 -1
  27. package/dist/debug/index.d.ts +3 -0
  28. package/dist/debug/index.d.ts.map +1 -0
  29. package/dist/debug/index.js +2 -0
  30. package/dist/debug/index.js.map +1 -0
  31. package/dist/debug/recorder.d.ts +32 -0
  32. package/dist/debug/recorder.d.ts.map +1 -0
  33. package/dist/debug/recorder.js +76 -0
  34. package/dist/debug/recorder.js.map +1 -0
  35. package/dist/generator/cli.d.ts +20 -0
  36. package/dist/generator/cli.d.ts.map +1 -0
  37. package/dist/generator/cli.js +56 -0
  38. package/dist/generator/cli.js.map +1 -0
  39. package/dist/generator/index.d.ts +9 -0
  40. package/dist/generator/index.d.ts.map +1 -0
  41. package/dist/generator/index.js +49 -0
  42. package/dist/generator/index.js.map +1 -0
  43. package/dist/generator/openapi.d.ts +13 -0
  44. package/dist/generator/openapi.d.ts.map +1 -0
  45. package/dist/generator/openapi.js +49 -0
  46. package/dist/generator/openapi.js.map +1 -0
  47. package/dist/generator/templates.d.ts +16 -0
  48. package/dist/generator/templates.d.ts.map +1 -0
  49. package/dist/generator/templates.js +292 -0
  50. package/dist/generator/templates.js.map +1 -0
  51. package/dist/index.d.ts +33 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +101 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/policies/builtin.d.ts +40 -0
  56. package/dist/policies/builtin.d.ts.map +1 -0
  57. package/dist/policies/builtin.js +186 -0
  58. package/dist/policies/builtin.js.map +1 -0
  59. package/dist/policies/index.d.ts +2 -0
  60. package/dist/policies/index.d.ts.map +1 -0
  61. package/dist/policies/index.js +2 -0
  62. package/dist/policies/index.js.map +1 -0
  63. package/dist/public.d.ts +17 -0
  64. package/dist/public.d.ts.map +1 -1
  65. package/dist/public.js +15 -0
  66. package/dist/public.js.map +1 -1
  67. package/dist/schema/index.d.ts +2 -0
  68. package/dist/schema/index.d.ts.map +1 -0
  69. package/dist/schema/index.js +2 -0
  70. package/dist/schema/index.js.map +1 -0
  71. package/dist/schema/monitor.d.ts +23 -0
  72. package/dist/schema/monitor.d.ts.map +1 -0
  73. package/dist/schema/monitor.js +70 -0
  74. package/dist/schema/monitor.js.map +1 -0
  75. package/dist/services/index.d.ts +2 -0
  76. package/dist/services/index.d.ts.map +1 -0
  77. package/dist/services/index.js +2 -0
  78. package/dist/services/index.js.map +1 -0
  79. package/dist/services/service-client.d.ts +41 -0
  80. package/dist/services/service-client.d.ts.map +1 -0
  81. package/dist/services/service-client.js +243 -0
  82. package/dist/services/service-client.js.map +1 -0
  83. package/dist/transactions/index.d.ts +3 -0
  84. package/dist/transactions/index.d.ts.map +1 -0
  85. package/dist/transactions/index.js +2 -0
  86. package/dist/transactions/index.js.map +1 -0
  87. package/dist/transactions/saga.d.ts +22 -0
  88. package/dist/transactions/saga.d.ts.map +1 -0
  89. package/dist/transactions/saga.js +58 -0
  90. package/dist/transactions/saga.js.map +1 -0
  91. package/dist/validation/drift-detector.d.ts.map +1 -1
  92. package/dist/validation/drift-detector.js +17 -8
  93. package/dist/validation/drift-detector.js.map +1 -1
  94. package/package.json +7 -3
package/README.md CHANGED
@@ -2,28 +2,56 @@
2
2
 
3
3
  # Meridian
4
4
 
5
- **One SDK. Every API. Zero inconsistency.**
5
+ **Integration Reliability SDK**
6
6
 
7
- [![npm version](https://img.shields.io/npm/v/meridianjs?color=0070f3&label=npm)](https://www.npmjs.com/package/meridianjs)
8
- [![npm downloads](https://img.shields.io/npm/dm/meridianjs?color=0070f3)](https://www.npmjs.com/package/meridianjs)
9
- [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE.md)
10
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.5-blue?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
11
- [![Tests](https://img.shields.io/badge/tests-1489%20passing-brightgreen)](https://vitest.dev)
12
- [![Adapters](https://img.shields.io/badge/adapters-39-blueviolet)](#provider-status-matrix)
7
+ [![npm](https://img.shields.io/npm/v/meridianjs?color=0070f3)](https://www.npmjs.com/package/meridianjs)
8
+ [![version](https://img.shields.io/badge/version-0.2.5-blue)](CHANGELOG.md)
9
+ [![tests](https://img.shields.io/badge/tests-1602%20passing-brightgreen)](https://vitest.dev)
10
+ [![adapters](https://img.shields.io/badge/adapters-39-blueviolet)](#providers)
11
+ [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE.md)
13
12
 
14
- A TypeScript-first SDK that gives every third-party API the same interface normalized errors, rate limits, pagination, and response shapes, regardless of provider.
13
+ One interface for 39 APIs. Automatic failover. Built-in observability. Zero runtime dependencies.
15
14
 
16
15
  </div>
17
16
 
18
- ---
19
-
20
- ## Install
21
-
22
17
  ```bash
23
18
  npm install meridianjs
24
19
  ```
25
20
 
26
- Requires **Node.js ≥ 18**. Zero runtime dependencies.
21
+ ---
22
+
23
+ ## Why Meridian Exists
24
+
25
+ Every integration team rewrites the same things.
26
+
27
+ Retries. Pagination. Rate limit parsing. Error normalization. Failover logic.
28
+
29
+ They write it once for Stripe. Then again for Razorpay. Then again for OpenAI. The code is never shared because every provider has a different shape.
30
+
31
+ Meridian provides a single reliability layer between your application and third-party APIs. Write your integration once. Every provider gets the same retry behavior, the same error format, the same pagination interface, the same circuit breaker, the same trace data.
32
+
33
+ The two features that make it more than a wrapper:
34
+
35
+ **Service abstraction** — your application calls `service("payments")`, not `provider("stripe")`. Meridian decides which provider handles the request. Your application is never coupled to a specific vendor.
36
+
37
+ **Schema drift detection** — providers silently change their API responses. Meridian detects the change before it reaches production.
38
+
39
+ ---
40
+
41
+ ## Architecture
42
+
43
+ ```mermaid
44
+ graph TD
45
+ App[Your Application] --> M[Meridian]
46
+ M --> P[Policy Engine]
47
+ P --> CB[Circuit Breaker]
48
+ CB --> RL[Rate Limiter]
49
+ RL --> RT[Retry]
50
+ RT --> S[Stripe]
51
+ RT --> O[OpenAI]
52
+ RT --> R[Razorpay]
53
+ RT --> More[···36 more]
54
+ ```
27
55
 
28
56
  ---
29
57
 
@@ -33,156 +61,220 @@ Requires **Node.js ≥ 18**. Zero runtime dependencies.
33
61
  import { Meridian } from "meridianjs";
34
62
 
35
63
  const meridian = await Meridian.create({
64
+ localUnsafe: true,
36
65
  providers: {
37
- stripe: { auth: { apiKey: process.env.STRIPE_SECRET_KEY } },
38
- razorpay: {
39
- auth: {
40
- username: process.env.RAZORPAY_KEY_ID,
41
- password: process.env.RAZORPAY_KEY_SECRET,
42
- },
43
- },
66
+ stripe: { auth: { apiKey: process.env.STRIPE_KEY } },
67
+ openai: { auth: { apiKey: process.env.OPENAI_KEY } },
44
68
  },
45
69
  });
46
70
 
47
- // Same API for every provider
48
- const { data, meta } = await meridian.provider("stripe").get("/v1/customers");
71
+ const { data, meta } = await meridian.provider("stripe")!.get("/v1/customers");
49
72
 
50
- console.log(meta.provider); // "stripe"
51
- console.log(meta.rateLimit.remaining); // always normalized
52
- console.log(meta.pagination.hasNext); // always normalized
73
+ meta.rateLimit.remaining // always normalized
74
+ meta.pagination?.hasNext // always normalized
75
+ meta.trace.latency // ms, always present
76
+ meta.trace.retries // how many retries
77
+ meta.trace.circuitBreaker // CLOSED | OPEN | HALF_OPEN
53
78
  ```
54
79
 
55
- Switch providers — your app code doesn't change.
56
-
57
80
  ---
58
81
 
59
- ## What It Does
82
+ ## What Meridian Does
60
83
 
61
- | Feature | Description |
62
- |---|---|
63
- | **Normalized responses** | Every provider returns `{ data, meta }` with consistent shape |
64
- | **Typed errors** | `MeridianError` with `category`, `retryable`, `retryAfter` — always |
65
- | **Auto-retry** | Exponential backoff with jitter on 429s and 5xx errors |
66
- | **Circuit breaker** | Opens automatically on repeated failures, closes after cooldown |
67
- | **Rate limit awareness** | Tracks limits from response headers, delays before hitting 429 |
68
- | **Pagination** | Unified `meta.pagination` across cursor, offset, and link-based strategies |
69
- | **Webhook verification** | Timing-safe HMAC verification on every payment/comms adapter |
70
- | **Streaming** | SSE streaming for OpenAI, Anthropic, Mistral, Cohere |
71
- | **Batch requests** | Fan-out with concurrency control via `.batch()` |
72
- | **India Compliance** | DPDPA-compliant PII redaction (Aadhaar, PAN, UPI VPA, bank accounts) |
84
+ | | Without | With |
85
+ |---|---|---|
86
+ | Errors | Different shape per provider | `MeridianError` always `category`, `retryable`, `retryAfter` |
87
+ | Rate limits | Parse per provider | `meta.rateLimit` — normalized |
88
+ | Pagination | cursor / offset / link per provider | `meta.pagination` — normalized |
89
+ | Retries | Manual | Exponential backoff, idempotency-safe |
90
+ | Circuit breaking | Manual | Automatic, per-provider |
91
+ | Provider outage | App breaks | Automatic failover |
92
+ | API drift | Silent breakage | `meridian.schema.check()` |
73
93
 
74
94
  ---
75
95
 
76
- ## Provider Status Matrix
96
+ ## Provider Failover
77
97
 
78
- Every adapter in Meridian is held to the **same** contract — adapters are just data sources, so the guarantees Meridian makes (error normalization, retry semantics, rate-limit parsing, pagination, request shaping) must hold identically across all of them. A single, provider-agnostic suite (`runProviderContract`) runs **19 invariants against every registered adapter** in CI, so a provider is only listed here if it upholds the full contract.
98
+ Your app calls `"llm"`. It never touches `"openai"` or `"anthropic"` directly.
79
99
 
80
- ```bash
81
- npm run test:contracts # run the contract against all 39 adapters
82
- npm run test:contracts stripe # focus a single provider
100
+ ```typescript
101
+ const meridian = await Meridian.create({
102
+ localUnsafe: true,
103
+ providers: {
104
+ openai: { auth: { apiKey: "..." } },
105
+ anthropic: { auth: { apiKey: "..." } },
106
+ gemini: { auth: { apiKey: "..." } },
107
+ },
108
+ services: {
109
+ llm: { providers: ["openai", "anthropic", "gemini"], strategy: "failover" },
110
+ },
111
+ });
112
+
113
+ await meridian.service("llm")!.post("/v1/chat/completions", { body: { ... } });
83
114
  ```
84
115
 
85
- | Provider | Category | Contract Tests | Status |
86
- |:---|:---|:---:|:---:|
87
- | **Adyen** | Payments | 19/19 Passed | ✅ Production-Ready |
88
- | **Anthropic** | AI / LLM | 19/19 Passed | ✅ Production-Ready |
89
- | **Apollo.io** | CRM / Sales | 19/19 Passed | ✅ Production-Ready |
90
- | **Auth0** | Auth / Identity | 19/19 Passed | ✅ Production-Ready |
91
- | **Braintree** | Payments | 19/19 Passed | ✅ Production-Ready |
92
- | **Cashfree** | Payments | 19/19 Passed | ✅ Production-Ready |
93
- | **Checkout.com** | Payments | 19/19 Passed | ✅ Production-Ready |
94
- | **Cleartax** | Tax / Compliance | 19/19 Passed | ✅ Production-Ready |
95
- | **Cohere** | AI / LLM | 19/19 Passed | Production-Ready |
96
- | **Decentro** | Banking / Fintech | 19/19 Passed | ✅ Production-Ready |
97
- | **Delhivery** | Logistics | 19/19 Passed | ✅ Production-Ready |
98
- | **Digio** | eSign / KYC | 19/19 Passed | ✅ Production-Ready |
99
- | **Exotel** | Communications | 19/19 Passed | Production-Ready |
100
- | **Google Gemini** | AI / LLM | 19/19 Passed | ✅ Production-Ready |
101
- | **Gupshup** | Communications | 19/19 Passed | ✅ Production-Ready |
102
- | **GitHub** | Developer Tools | 19/19 Passed | Production-Ready |
103
- | **HubSpot** | CRM | 19/19 Passed | ✅ Production-Ready |
104
- | **HyperVerge** | KYC / Identity | 19/19 Passed | ✅ Production-Ready |
105
- | **IDfy** | KYC / Identity | 19/19 Passed | ✅ Production-Ready |
106
- | **Juspay** | Payments | 19/19 Passed | ✅ Production-Ready |
107
- | **Karza** | KYC / Verification | 19/19 Passed | ✅ Production-Ready |
108
- | **Klarna** | Payments | 19/19 Passed | ✅ Production-Ready |
109
- | **Mailgun** | Communications | 19/19 Passed | ✅ Production-Ready |
110
- | **MapMyIndia** | Maps / Geo | 19/19 Passed | ✅ Production-Ready |
111
- | **Mistral** | AI / LLM | 19/19 Passed | ✅ Production-Ready |
112
- | **Mollie** | Payments | 19/19 Passed | ✅ Production-Ready |
113
- | **MSG91** | Communications | 19/19 Passed | ✅ Production-Ready |
114
- | **OpenAI** | AI / LLM | 19/19 Passed | ✅ Production-Ready |
115
- | **PayU** | Payments | 19/19 Passed | ✅ Production-Ready |
116
- | **Perfios** | Financial Data | 19/19 Passed | ✅ Production-Ready |
117
- | **PhonePe** | Payments | 19/19 Passed | ✅ Production-Ready |
118
- | **Razorpay** | Payments | 19/19 Passed | ✅ Production-Ready |
119
- | **SendGrid** | Communications | 19/19 Passed | ✅ Production-Ready |
120
- | **Setu** | Banking / UPI | 19/19 Passed | ✅ Production-Ready |
121
- | **Shiprocket** | Logistics | 19/19 Passed | ✅ Production-Ready |
122
- | **Stripe** | Payments | 19/19 Passed | ✅ Production-Ready |
123
- | **Supabase** | Database / Auth | 19/19 Passed | ✅ Production-Ready |
124
- | **Twilio** | Communications | 19/19 Passed | ✅ Production-Ready |
125
- | **Vonage** | Communications | 19/19 Passed | ✅ Production-Ready |
116
+ ```mermaid
117
+ sequenceDiagram
118
+ App->>Meridian: service("llm").post(...)
119
+ Meridian->>OpenAI: attempt
120
+ OpenAI-->>Meridian: 503 outage
121
+ Meridian->>Anthropic: failover
122
+ Anthropic-->>Meridian: 200 OK
123
+ Meridian-->>App: result (meta.provider = "anthropic")
124
+ ```
125
+
126
+ **Routing strategies:** `failover` · `round-robin` · `lowest-latency` · `cheapest` · `highest-success-rate` · `weighted` · `geo`
127
+
128
+ ```typescript
129
+ // weighted: 70% Stripe, 30% Razorpay
130
+ payments: { providers: ["stripe", "razorpay"], strategy: "weighted", weights: { stripe: 70, razorpay: 30 } }
131
+
132
+ // geo: route by region (MERIDIAN_REGION env var)
133
+ payments: { providers: ["razorpay", "stripe"], strategy: "geo", regions: { "ap-south-1": ["razorpay"], "us-east-1": ["stripe"] } }
134
+ ```
126
135
 
127
136
  ---
128
137
 
129
- ## Error Handling
138
+ ## Observability
130
139
 
131
- Every error has the same shape no more `try/catch` archaeology per provider:
140
+ Every response includes a trace. No configuration needed.
132
141
 
133
142
  ```typescript
134
- try {
135
- const result = await meridian.provider("stripe").post("/v1/charges", { body });
136
- } catch (err) {
137
- if (err instanceof MeridianError) {
138
- err.category; // "auth" | "rate_limit" | "validation" | "network" | "provider"
139
- err.retryable; // boolean
140
- err.retryAfter; // Date | undefined
141
- err.provider; // "stripe"
142
- }
143
- }
143
+ result.meta.trace
144
+ // { retries: 2, latency: 341, circuitBreaker: "CLOSED", rateLimitRemaining: 91 }
145
+
146
+ meridian.analytics()
147
+ // { stripe: { requests: 12431, errorRate: "0.3%", avgLatency: 240, p95Latency: 480 } }
148
+
149
+ meridian.health()
150
+ // { stripe: { status: "healthy", successRate: "99.7%", circuitBreaker: "CLOSED" } }
151
+
152
+ meridian.cost()
153
+ // { providers: { openai: { requests: 1243, costPerRequest: 0.03, estimatedSpend: 37.29 } },
154
+ // total: { requests: 1243, estimatedSpend: 37.29 }, since: "2026-06-05T...", currency: "USD" }
144
155
  ```
145
156
 
146
157
  ---
147
158
 
148
- ## Webhook Verification
159
+ ## Policy Engine
160
+
161
+ Runs before every request. No network round-trip on block.
149
162
 
150
163
  ```typescript
151
- import { StripeAdapter } from "meridianjs";
152
-
153
- const adapter = new StripeAdapter();
154
- const valid = adapter.verifyWebhook(
155
- req.rawBody,
156
- req.headers["stripe-signature"],
157
- process.env.STRIPE_WEBHOOK_SECRET,
158
- );
164
+ import { blockPII, redact, requireFields, denyCountries, allowedProviders, readOnly, customPolicy } from "meridianjs";
165
+
166
+ policies: [
167
+ blockPII(["openai"]), // blocks credit cards, SSNs, emails, Aadhaar, PAN
168
+ redact(["user.ssn", "card.number"]), // redacts fields in-place — request still goes through
169
+ requireFields(["tenantId"]), // blocks if required fields missing
170
+ denyCountries(["KP", "IR"]), // blocks by ISO 3166-1 country code
171
+ allowedProviders(["openai", "stripe"]), // provider whitelist
172
+ readOnly(["github"]), // no writes
173
+ customPolicy("require-tenant", (ctx) =>
174
+ "tenantId" in (ctx.body as object)
175
+ ? { allow: true }
176
+ : { allow: false, reason: "tenantId required" }
177
+ ),
178
+ ]
159
179
  ```
160
180
 
161
- Works identically for Razorpay, Cashfree, Braintree, Twilio, Adyen, and more.
181
+ ---
182
+
183
+ ## Transactions
184
+
185
+ Saga pattern. Failed steps trigger compensating rollbacks in reverse order.
186
+
187
+ ```typescript
188
+ await meridian.transaction([
189
+ {
190
+ name: "charge",
191
+ execute: () => stripe.post("/v1/charges", { body: { amount: 2000 } }),
192
+ rollback: (r) => stripe.post(`/v1/charges/${r.data.id}/refund`),
193
+ },
194
+ {
195
+ name: "email",
196
+ execute: () => sendgrid.post("/v3/mail/send", { body: { ... } }),
197
+ },
198
+ ]);
199
+ ```
200
+
201
+ ```mermaid
202
+ flowchart LR
203
+ A[charge ✓] --> B[email ✗]
204
+ B -- failure --> C[rollback: charge]
205
+ C --> D[TransactionError]
206
+ ```
162
207
 
163
208
  ---
164
209
 
165
- ## Batch Requests
210
+ ## Schema Drift Detection
211
+
212
+ Snapshot a response. Check later. Get alerted when the provider changes their API silently.
166
213
 
167
214
  ```typescript
168
- const results = await meridian.provider("stripe").batch([
169
- { method: "GET", endpoint: "/v1/customers/cu_1" },
170
- { method: "GET", endpoint: "/v1/customers/cu_2" },
171
- { method: "GET", endpoint: "/v1/customers/cu_3" },
172
- ], 5); // max 5 concurrent
215
+ await meridian.schema.snapshot("stripe", "/v1/customers", response.data);
173
216
 
174
- // Results are always in input order; errors are MeridianError, never thrown
217
+ const drifts = await meridian.schema.check("stripe", "/v1/customers", laterResponse.data);
218
+ // [{ type: "FIELD_REMOVED", field: "customer_name", severity: "ERROR" }]
219
+
220
+ await meridian.schema.alert("stripe", "/v1/customers", newData, (drifts, provider, endpoint) => {
221
+ pagerDuty.trigger(`Schema drift on ${provider}${endpoint}`);
222
+ });
223
+
224
+ const report = await meridian.schema.report("stripe");
225
+ // { provider: "stripe", endpoints: [{ endpoint: "/v1/customers", fieldCount: 12, version: "..." }] }
175
226
  ```
176
227
 
177
- ## Support & Contact
228
+ ---
178
229
 
179
- If you encounter any issues or have questions, please reach out directly at [raghav.verma.work@gmail.com](mailto:raghav.verma.work@gmail.com).
230
+ ## More Features
231
+
232
+ ```typescript
233
+ // Debug & replay
234
+ meridian.debug.enable();
235
+ await meridian.replay(requestId); // re-runs with exact original options
236
+
237
+ // Capability registry
238
+ meridian.findProviders({ capability: "streaming" });
239
+ // [{ name: "openai" }, { name: "anthropic" }, { name: "gemini" }, ...]
240
+
241
+ // Adapter generator
242
+ // npx meridian generate --provider acme --openapi ./acme.json
243
+ // → adapter.ts adapter.test.ts pagination.ts index.ts (8 tests pass immediately)
244
+
245
+ // Pagination
246
+ for await (const page of meridian.provider("stripe")!.paginate("/v1/customers")) { ... }
247
+
248
+ // Streaming (OpenAI, Anthropic, Gemini, Mistral, Cohere)
249
+ for await (const chunk of meridian.provider("openai")!.stream("/v1/chat/completions", { body })) { ... }
250
+
251
+ // Batch
252
+ await meridian.provider("stripe")!.batch([{ method: "GET", endpoint: "/v1/customers/1" }, ...], 5);
253
+
254
+ // Webhook verification
255
+ new StripeAdapter().verifyWebhook(req.rawBody, req.headers["stripe-signature"], secret);
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Providers
261
+
262
+ 39 adapters, each passing 19 contract invariants. `npm run test:contracts stripe`
263
+
264
+ | Category | Providers |
265
+ |---|---|
266
+ | **Payments** | Stripe · Razorpay · Cashfree · PayU · Juspay · Braintree · Adyen · Klarna · Mollie · PhonePe · Checkout.com |
267
+ | **AI / LLM** | OpenAI · Anthropic · Gemini · Cohere · Mistral |
268
+ | **Communications** | Twilio · SendGrid · Mailgun · Vonage · MSG91 · Exotel · Gupshup |
269
+ | **KYC / Identity** | HyperVerge · Digio · Karza · IDfy · Setu · Decentro · Perfios |
270
+ | **Tools & Infra** | GitHub · HubSpot · Supabase · Auth0 · Apollo |
271
+ | **Logistics** | Shiprocket · Delhivery |
272
+ | **Other** | MapMyIndia · Cleartax |
180
273
 
181
274
  ---
182
275
 
183
- ## Links
276
+ ## Contributing
277
+
278
+ New adapter: `npx meridian generate --provider name --openapi ./spec.json` → implement TODOs → `npm test`.
184
279
 
185
- - [GitHub](https://github.com/Raghaverma/Meridianjs)
186
- - [Changelog](CHANGELOG.md)
187
- - [Roadmap](ROADMAP.md)
188
- - [License: MIT](LICENSE.md)
280
+ [Changelog](CHANGELOG.md) · [License: MIT](LICENSE.md) · [npm](https://www.npmjs.com/package/meridianjs)
@@ -0,0 +1,44 @@
1
+ import type { ErrorContext, Metric, ObservabilityAdapter, RequestContext, ResponseContext } from "../core/types.js";
2
+ export interface ProviderStats {
3
+ requests: number;
4
+ errors: number;
5
+ errorRate: string;
6
+ successRate: string;
7
+ avgLatency: number;
8
+ p95Latency: number;
9
+ }
10
+ export interface HealthEntry {
11
+ status: "healthy" | "degraded" | "down";
12
+ successRate: string;
13
+ avgLatency: number;
14
+ }
15
+ export interface CostEntry {
16
+ requests: number;
17
+ costPerRequest: number;
18
+ estimatedSpend: number;
19
+ }
20
+ export interface CostReport {
21
+ providers: Record<string, CostEntry>;
22
+ total: {
23
+ requests: number;
24
+ estimatedSpend: number;
25
+ };
26
+ since: string;
27
+ currency: string;
28
+ }
29
+ export declare class AnalyticsCollector implements ObservabilityAdapter {
30
+ private buckets;
31
+ private startedAt;
32
+ logRequest(_ctx: RequestContext): void;
33
+ logResponse(ctx: ResponseContext): void;
34
+ logError(ctx: ErrorContext): void;
35
+ logWarning(): void;
36
+ recordMetric(_metric: Metric): void;
37
+ private record;
38
+ get(): Record<string, ProviderStats>;
39
+ getHealth(): Record<string, HealthEntry>;
40
+ private computeStats;
41
+ getCost(costs: Record<string, number>, currency?: string): CostReport;
42
+ reset(): void;
43
+ }
44
+ //# sourceMappingURL=collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../src/analytics/collector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,MAAM,EACN,oBAAoB,EACpB,cAAc,EACd,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACrC,KAAK,EAAE;QACL,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAQD,qBAAa,kBAAmB,YAAW,oBAAoB;IAC7D,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAA4B;IAE7C,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAEtC,WAAW,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAIvC,QAAQ,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI;IAIjC,UAAU,IAAI,IAAI;IAElB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAEnC,OAAO,CAAC,MAAM;IAWd,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;IAQpC,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC;IAcxC,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,SAAQ,GAAG,UAAU;IAqBpE,KAAK,IAAI,IAAI;CAId"}
@@ -0,0 +1,82 @@
1
+ export class AnalyticsCollector {
2
+ buckets = new Map();
3
+ startedAt = new Date().toISOString();
4
+ logRequest(_ctx) { }
5
+ logResponse(ctx) {
6
+ this.record(ctx.provider, true, ctx.duration);
7
+ }
8
+ logError(ctx) {
9
+ this.record(ctx.provider, false, ctx.duration);
10
+ }
11
+ logWarning() { }
12
+ recordMetric(_metric) { }
13
+ record(provider, success, latency) {
14
+ if (!this.buckets.has(provider)) {
15
+ this.buckets.set(provider, { requests: 0, errors: 0, latencies: [] });
16
+ }
17
+ const b = this.buckets.get(provider);
18
+ b.requests++;
19
+ if (!success)
20
+ b.errors++;
21
+ b.latencies.push(latency);
22
+ if (b.latencies.length > 1000)
23
+ b.latencies.shift();
24
+ }
25
+ get() {
26
+ const out = {};
27
+ for (const [provider, b] of this.buckets) {
28
+ out[provider] = this.computeStats(b);
29
+ }
30
+ return out;
31
+ }
32
+ getHealth() {
33
+ const out = {};
34
+ for (const [provider, b] of this.buckets) {
35
+ const stats = this.computeStats(b);
36
+ const successPct = Number.parseFloat(stats.successRate);
37
+ out[provider] = {
38
+ status: successPct >= 99 ? "healthy" : successPct >= 95 ? "degraded" : "down",
39
+ successRate: stats.successRate,
40
+ avgLatency: stats.avgLatency,
41
+ };
42
+ }
43
+ return out;
44
+ }
45
+ computeStats(b) {
46
+ const sorted = [...b.latencies].sort((a, c) => a - c);
47
+ const avg = sorted.length > 0 ? sorted.reduce((a, c) => a + c, 0) / sorted.length : 0;
48
+ const p95 = sorted.length > 0 ? (sorted[Math.floor(sorted.length * 0.95)] ?? 0) : 0;
49
+ const errorRate = b.requests > 0 ? (b.errors / b.requests) * 100 : 0;
50
+ return {
51
+ requests: b.requests,
52
+ errors: b.errors,
53
+ errorRate: `${errorRate.toFixed(1)}%`,
54
+ successRate: `${(100 - errorRate).toFixed(1)}%`,
55
+ avgLatency: Math.round(avg),
56
+ p95Latency: Math.round(p95),
57
+ };
58
+ }
59
+ getCost(costs, currency = "USD") {
60
+ const providers = {};
61
+ let totalRequests = 0;
62
+ let totalSpend = 0;
63
+ for (const [provider, b] of this.buckets) {
64
+ const costPerRequest = costs[provider] ?? 0;
65
+ const estimatedSpend = b.requests * costPerRequest;
66
+ providers[provider] = { requests: b.requests, costPerRequest, estimatedSpend };
67
+ totalRequests += b.requests;
68
+ totalSpend += estimatedSpend;
69
+ }
70
+ return {
71
+ providers,
72
+ total: { requests: totalRequests, estimatedSpend: totalSpend },
73
+ since: this.startedAt,
74
+ currency,
75
+ };
76
+ }
77
+ reset() {
78
+ this.buckets.clear();
79
+ this.startedAt = new Date().toISOString();
80
+ }
81
+ }
82
+ //# sourceMappingURL=collector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector.js","sourceRoot":"","sources":["../../src/analytics/collector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { AnalyticsCollector } from "./collector.js";
2
+ export type { ProviderStats, HealthEntry } from "./collector.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { AnalyticsCollector } from "./collector.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { PROVIDER_CAPABILITIES } from "./registry.js";
2
+ export type { ProviderInfo } from "./registry.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capabilities/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { PROVIDER_CAPABILITIES } from "./registry.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/capabilities/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare const PROVIDER_CAPABILITIES: Record<string, string[]>;
2
+ export interface ProviderInfo {
3
+ name: string;
4
+ capabilities: string[];
5
+ }
6
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/capabilities/registry.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAqE1D,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB"}