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.
- package/CHANGELOG.md +97 -150
- package/README.md +214 -122
- package/dist/analytics/collector.d.ts +44 -0
- package/dist/analytics/collector.d.ts.map +1 -0
- package/dist/analytics/collector.js +82 -0
- package/dist/analytics/collector.js.map +1 -0
- package/dist/analytics/index.d.ts +3 -0
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/index.js +2 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/capabilities/index.d.ts +3 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +2 -0
- package/dist/capabilities/index.js.map +1 -0
- package/dist/capabilities/registry.d.ts +6 -0
- package/dist/capabilities/registry.d.ts.map +1 -0
- package/dist/capabilities/registry.js +61 -0
- package/dist/capabilities/registry.js.map +1 -0
- package/dist/core/pipeline.d.ts +3 -1
- package/dist/core/pipeline.d.ts.map +1 -1
- package/dist/core/pipeline.js +42 -0
- package/dist/core/pipeline.js.map +1 -1
- package/dist/core/types.d.ts +42 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/debug/index.d.ts +3 -0
- package/dist/debug/index.d.ts.map +1 -0
- package/dist/debug/index.js +2 -0
- package/dist/debug/index.js.map +1 -0
- package/dist/debug/recorder.d.ts +32 -0
- package/dist/debug/recorder.d.ts.map +1 -0
- package/dist/debug/recorder.js +76 -0
- package/dist/debug/recorder.js.map +1 -0
- package/dist/generator/cli.d.ts +20 -0
- package/dist/generator/cli.d.ts.map +1 -0
- package/dist/generator/cli.js +56 -0
- package/dist/generator/cli.js.map +1 -0
- package/dist/generator/index.d.ts +9 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +49 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/openapi.d.ts +13 -0
- package/dist/generator/openapi.d.ts.map +1 -0
- package/dist/generator/openapi.js +49 -0
- package/dist/generator/openapi.js.map +1 -0
- package/dist/generator/templates.d.ts +16 -0
- package/dist/generator/templates.d.ts.map +1 -0
- package/dist/generator/templates.js +292 -0
- package/dist/generator/templates.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +101 -0
- package/dist/index.js.map +1 -1
- package/dist/policies/builtin.d.ts +40 -0
- package/dist/policies/builtin.d.ts.map +1 -0
- package/dist/policies/builtin.js +186 -0
- package/dist/policies/builtin.js.map +1 -0
- package/dist/policies/index.d.ts +2 -0
- package/dist/policies/index.d.ts.map +1 -0
- package/dist/policies/index.js +2 -0
- package/dist/policies/index.js.map +1 -0
- package/dist/public.d.ts +17 -0
- package/dist/public.d.ts.map +1 -1
- package/dist/public.js +15 -0
- package/dist/public.js.map +1 -1
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/monitor.d.ts +23 -0
- package/dist/schema/monitor.d.ts.map +1 -0
- package/dist/schema/monitor.js +70 -0
- package/dist/schema/monitor.js.map +1 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +2 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/service-client.d.ts +41 -0
- package/dist/services/service-client.d.ts.map +1 -0
- package/dist/services/service-client.js +243 -0
- package/dist/services/service-client.js.map +1 -0
- package/dist/transactions/index.d.ts +3 -0
- package/dist/transactions/index.d.ts.map +1 -0
- package/dist/transactions/index.js +2 -0
- package/dist/transactions/index.js.map +1 -0
- package/dist/transactions/saga.d.ts +22 -0
- package/dist/transactions/saga.d.ts.map +1 -0
- package/dist/transactions/saga.js +58 -0
- package/dist/transactions/saga.js.map +1 -0
- package/dist/validation/drift-detector.d.ts.map +1 -1
- package/dist/validation/drift-detector.js +17 -8
- package/dist/validation/drift-detector.js.map +1 -1
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -2,28 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Meridian
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Integration Reliability SDK**
|
|
6
6
|
|
|
7
|
-
[](#provider-status-matrix)
|
|
7
|
+
[](https://www.npmjs.com/package/meridianjs)
|
|
8
|
+
[](CHANGELOG.md)
|
|
9
|
+
[](https://vitest.dev)
|
|
10
|
+
[](#providers)
|
|
11
|
+
[](LICENSE.md)
|
|
13
12
|
|
|
14
|
-
|
|
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
|
-
|
|
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.
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
82
|
+
## What Meridian Does
|
|
60
83
|
|
|
61
|
-
|
|
|
62
|
-
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
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
|
|
96
|
+
## Provider Failover
|
|
77
97
|
|
|
78
|
-
|
|
98
|
+
Your app calls `"llm"`. It never touches `"openai"` or `"anthropic"` directly.
|
|
79
99
|
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
##
|
|
138
|
+
## Observability
|
|
130
139
|
|
|
131
|
-
Every
|
|
140
|
+
Every response includes a trace. No configuration needed.
|
|
132
141
|
|
|
133
142
|
```typescript
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
##
|
|
159
|
+
## Policy Engine
|
|
160
|
+
|
|
161
|
+
Runs before every request. No network round-trip on block.
|
|
149
162
|
|
|
150
163
|
```typescript
|
|
151
|
-
import {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
228
|
+
---
|
|
178
229
|
|
|
179
|
-
|
|
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
|
-
##
|
|
276
|
+
## Contributing
|
|
277
|
+
|
|
278
|
+
New adapter: `npx meridian generate --provider name --openapi ./spec.json` → implement TODOs → `npm test`.
|
|
184
279
|
|
|
185
|
-
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|