@monigo/sdk 0.1.2

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/Readme.md ADDED
@@ -0,0 +1,712 @@
1
+ # @monigo/sdk
2
+
3
+ The official JavaScript/TypeScript client library for the [Monigo](https://monigo.co) API. Zero external dependencies — works natively in Node.js (≥ 18), browsers, Cloudflare Workers, Bun, and Deno.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @monigo/sdk
9
+ ```
10
+
11
+ Or with other package managers:
12
+
13
+ ```bash
14
+ yarn add @monigo/sdk
15
+ pnpm add @monigo/sdk
16
+ bun add @monigo/sdk
17
+ ```
18
+
19
+ Requires Node.js 18 or later (for native `fetch`). For Node.js 16, see [Polyfilling fetch](#polyfilling-fetch).
20
+
21
+ ## Quick start
22
+
23
+ ```ts
24
+ import { MonigoClient, Aggregation, BillingPeriod, PricingModel } from '@monigo/sdk'
25
+
26
+ const client = new MonigoClient({ apiKey: process.env.MONIGO_API_KEY! })
27
+
28
+ // Create a customer
29
+ const customer = await client.customers.create({
30
+ external_id: 'user_abc123',
31
+ name: 'Acme Corp',
32
+ email: 'billing@acme.com',
33
+ })
34
+
35
+ // Ingest a usage event
36
+ await client.events.ingest({
37
+ events: [
38
+ {
39
+ event_name: 'api_call',
40
+ customer_id: customer.id,
41
+ idempotency_key: 'evt_20260201_001',
42
+ timestamp: new Date(),
43
+ properties: { endpoint: '/v1/data', region: 'us-east-1' },
44
+ },
45
+ ],
46
+ })
47
+
48
+ console.log(`Customer ${customer.id} created and event ingested`)
49
+ ```
50
+
51
+ > **Tip:** Use a test-mode API key (`mk_test_...`) during development. Test events are isolated from live data and won't trigger real charges.
52
+
53
+ ## Client configuration
54
+
55
+ ### Constructor options
56
+
57
+ ```ts
58
+ const client = new MonigoClient({
59
+ apiKey: 'mk_live_...', // required — your Monigo API key
60
+ baseURL: 'https://api.monigo.co', // optional — defaults to production
61
+ timeout: 30_000, // optional — request timeout in ms (default: none)
62
+ fetch: customFetch, // optional — custom fetch implementation
63
+ })
64
+ ```
65
+
66
+ ### Custom base URL
67
+
68
+ For self-hosted deployments or a local development server:
69
+
70
+ ```ts
71
+ const client = new MonigoClient({
72
+ apiKey: process.env.MONIGO_API_KEY!,
73
+ baseURL: 'http://localhost:8000',
74
+ })
75
+ ```
76
+
77
+ ### Polyfilling fetch
78
+
79
+ Node.js 16 does not include a built-in `fetch`. Pass a polyfill via the `fetch` option:
80
+
81
+ ```ts
82
+ import fetch from 'node-fetch'
83
+
84
+ const client = new MonigoClient({
85
+ apiKey: process.env.MONIGO_API_KEY!,
86
+ fetch: fetch as unknown as typeof globalThis.fetch,
87
+ })
88
+ ```
89
+
90
+ ### Using in tests
91
+
92
+ The `fetch` option makes the client fully testable without a network:
93
+
94
+ ```ts
95
+ const client = new MonigoClient({
96
+ apiKey: 'test_key',
97
+ fetch: (_url, _init) =>
98
+ Promise.resolve(new Response(JSON.stringify({ customer: mockCustomer }))),
99
+ })
100
+ ```
101
+
102
+ ## Authentication
103
+
104
+ Every request is authenticated with a Bearer token derived from your API key:
105
+
106
+ ```
107
+ Authorization: Bearer mk_live_...
108
+ ```
109
+
110
+ API keys are scoped to an organisation and carry one of two prefixes:
111
+
112
+ | Prefix | Mode | Usage |
113
+ |--------|------|-------|
114
+ | `mk_live_` | Live | Production traffic — charges and payouts are real |
115
+ | `mk_test_` | Test | Development / CI — events, customers, and invoices are sandboxed |
116
+
117
+ ## Error handling
118
+
119
+ All methods return a `Promise`. On a 4xx or 5xx response the SDK throws a `MonigoAPIError` with a typed status code, message, and optional field-level details map.
120
+
121
+ ```ts
122
+ import { MonigoAPIError } from '@monigo/sdk'
123
+
124
+ try {
125
+ const customer = await client.customers.get('cust_missing')
126
+ } catch (err) {
127
+ if (err instanceof MonigoAPIError) {
128
+ console.error(`API error ${err.statusCode}: ${err.message}`)
129
+ // { statusCode: 404, message: 'customer not found' }
130
+ }
131
+ }
132
+ ```
133
+
134
+ ### Static type-narrowing guards
135
+
136
+ Instead of catching and casting, use the static guards on `MonigoAPIError`:
137
+
138
+ ```ts
139
+ try {
140
+ await client.subscriptions.create({ customer_id: 'c', plan_id: 'p' })
141
+ } catch (err) {
142
+ if (MonigoAPIError.isConflict(err)) {
143
+ // customer already has an active subscription of this plan type
144
+ } else if (MonigoAPIError.isNotFound(err)) {
145
+ // customer or plan not found
146
+ } else if (MonigoAPIError.isRateLimited(err)) {
147
+ // back off and retry
148
+ } else if (err) {
149
+ throw err
150
+ }
151
+ }
152
+ ```
153
+
154
+ | Guard | HTTP Status | When it fires |
155
+ |-------|-------------|---------------|
156
+ | `MonigoAPIError.isNotFound(err)` | 404 | Resource does not exist |
157
+ | `MonigoAPIError.isUnauthorized(err)` | 401 | Invalid or missing API key |
158
+ | `MonigoAPIError.isForbidden(err)` | 403 | API key lacks required scope |
159
+ | `MonigoAPIError.isConflict(err)` | 409 | Duplicate resource (e.g. second active subscription of same plan type) |
160
+ | `MonigoAPIError.isRateLimited(err)` | 429 | Request rate limit exceeded |
161
+ | `MonigoAPIError.isQuotaExceeded(err)` | 402 | Organisation event quota exhausted |
162
+ | `MonigoAPIError.isServerError(err)` | 5xx | Unexpected server-side error |
163
+
164
+ ### Instance flags
165
+
166
+ `MonigoAPIError` also exposes boolean instance properties:
167
+
168
+ ```ts
169
+ catch (err) {
170
+ if (err instanceof MonigoAPIError && err.isNotFound) {
171
+ // same as MonigoAPIError.isNotFound(err)
172
+ }
173
+ }
174
+ ```
175
+
176
+ ## Events
177
+
178
+ The `client.events` service handles usage event ingestion and event replay.
179
+
180
+ ### Ingest events
181
+
182
+ ```ts
183
+ const response = await client.events.ingest({
184
+ events: [
185
+ {
186
+ event_name: 'api_call',
187
+ customer_id: 'cust_abc',
188
+ idempotency_key: 'evt_20260201_001',
189
+ timestamp: new Date(),
190
+ properties: {
191
+ endpoint: '/v1/predict',
192
+ region: 'us-east-1',
193
+ tokens: 1500,
194
+ },
195
+ },
196
+ ],
197
+ })
198
+
199
+ console.log('ingested:', response.ingested)
200
+ console.log('duplicates:', response.duplicates)
201
+ ```
202
+
203
+ The server deduplicates events by `idempotency_key`. Sending the same key twice is safe — the second call appears in `response.duplicates` and won't be counted again. A single `ingest` call can contain up to **1,000 events**.
204
+
205
+ > **Tip:** Always supply a stable `idempotency_key` (e.g. a UUID or `${customer_id}_${event_type}_${timestamp}`) so retries after a network error don't double-count events.
206
+
207
+ ### Replay events
208
+
209
+ Replay reprocesses all events in a time window through the metering pipeline. Use this to correct rollups after an outage or metric definition change.
210
+
211
+ ```ts
212
+ let job = await client.events.startReplay({
213
+ from: new Date('2026-01-01'),
214
+ to: new Date('2026-01-31'),
215
+ event_name: 'api_call', // omit to replay all event names
216
+ })
217
+
218
+ // Poll until complete
219
+ while (job.status === 'pending' || job.status === 'processing') {
220
+ await new Promise(r => setTimeout(r, 5_000))
221
+ job = await client.events.getReplay(job.id)
222
+ console.log(`replayed ${job.events_replayed} / ${job.events_total}`)
223
+ }
224
+ ```
225
+
226
+ ## Customers
227
+
228
+ ```ts
229
+ // Create
230
+ const customer = await client.customers.create({
231
+ external_id: 'user_123',
232
+ name: 'Acme Corp',
233
+ email: 'billing@acme.com',
234
+ })
235
+
236
+ // List
237
+ const { customers, count } = await client.customers.list()
238
+ console.log(`${count} customers`)
239
+
240
+ // Get by Monigo ID
241
+ const customer = await client.customers.get('cust_abc')
242
+
243
+ // Update
244
+ const updated = await client.customers.update('cust_abc', {
245
+ name: 'Acme Corp Ltd',
246
+ email: 'newbilling@acme.com',
247
+ })
248
+
249
+ // Delete
250
+ await client.customers.delete('cust_abc')
251
+ ```
252
+
253
+ > **Tip:** Set `external_id` to your own system's user ID. This lets you look up a customer by your existing identifier without storing Monigo's UUID separately.
254
+
255
+ ## Metrics
256
+
257
+ A metric defines what gets counted (e.g. "API calls", "GB stored") and how raw event values are aggregated.
258
+
259
+ ```ts
260
+ import { Aggregation } from '@monigo/sdk'
261
+
262
+ // Count events
263
+ const metric = await client.metrics.create({
264
+ name: 'API Calls',
265
+ event_name: 'api_call',
266
+ aggregation: Aggregation.Count,
267
+ })
268
+
269
+ // Sum a numeric property (e.g. tokens used)
270
+ const metric = await client.metrics.create({
271
+ name: 'Tokens Used',
272
+ event_name: 'completion',
273
+ aggregation: Aggregation.Sum,
274
+ aggregation_property: 'tokens',
275
+ })
276
+
277
+ // List / get / update / delete
278
+ const { metrics } = await client.metrics.list()
279
+ const metric = await client.metrics.get('metric_abc')
280
+ const updated = await client.metrics.update('metric_abc', { name: 'Renamed' })
281
+ await client.metrics.delete('metric_abc')
282
+ ```
283
+
284
+ ### Aggregation constants
285
+
286
+ | Constant | Value | Description |
287
+ |----------|-------|-------------|
288
+ | `Aggregation.Count` | `count` | Count the number of matching events |
289
+ | `Aggregation.Sum` | `sum` | Sum a numeric property across events |
290
+ | `Aggregation.Max` | `max` | Maximum value of a property |
291
+ | `Aggregation.Min` | `minimum` | Minimum value of a property |
292
+ | `Aggregation.Average` | `average` | Average value of a property |
293
+ | `Aggregation.Unique` | `unique` | Count distinct values of a property |
294
+
295
+ ## Plans
296
+
297
+ A plan combines billing period, currency, and one or more prices. Each price links a metric to a pricing model.
298
+
299
+ ```ts
300
+ import { BillingPeriod, PricingModel, PlanType } from '@monigo/sdk'
301
+
302
+ // Flat rate: ₦50 per API call
303
+ const plan = await client.plans.create({
304
+ name: 'Starter',
305
+ currency: 'NGN',
306
+ billing_period: BillingPeriod.Monthly,
307
+ prices: [
308
+ {
309
+ metric_id: 'metric_api_calls',
310
+ model: PricingModel.Flat,
311
+ unit_price: '50.000000',
312
+ },
313
+ ],
314
+ })
315
+
316
+ // Tiered pricing
317
+ const plan = await client.plans.create({
318
+ name: 'Growth',
319
+ currency: 'NGN',
320
+ billing_period: BillingPeriod.Monthly,
321
+ prices: [
322
+ {
323
+ metric_id: 'metric_api_calls',
324
+ model: PricingModel.Tiered,
325
+ tiers: [
326
+ { up_to: 10_000, unit_amount: '5.00' },
327
+ { up_to: 100_000, unit_amount: '3.00' },
328
+ { up_to: null, unit_amount: '1.50' }, // null = infinity
329
+ ],
330
+ },
331
+ ],
332
+ })
333
+
334
+ // List / get / update / delete
335
+ const { plans } = await client.plans.list()
336
+ const plan = await client.plans.get('plan_abc')
337
+ const updated = await client.plans.update('plan_abc', { name: 'Renamed' })
338
+ await client.plans.delete('plan_abc')
339
+ ```
340
+
341
+ ### Pricing model constants
342
+
343
+ | Constant | Value | Description |
344
+ |----------|-------|-------------|
345
+ | `PricingModel.Flat` | `flat` | Fixed price per unit |
346
+ | `PricingModel.Tiered` | `tiered` | Graduated tiers — each unit priced at its tier |
347
+ | `PricingModel.Volume` | `volume` | Whole volume priced at one tier |
348
+ | `PricingModel.Package` | `package` | Price per block of N units |
349
+ | `PricingModel.Overage` | `overage` | Base allowance + per-unit above threshold |
350
+ | `PricingModel.WeightedTiered` | `weighted_tiered` | Average price across graduated tiers |
351
+
352
+ ### Plan type and billing period constants
353
+
354
+ | Constant | Value | Meaning |
355
+ |----------|-------|---------|
356
+ | `PlanType.Collection` | `collection` | Charge customers (default) |
357
+ | `PlanType.Payout` | `payout` | Pay out to vendors |
358
+ | `BillingPeriod.Daily` | `daily` | Billed every day |
359
+ | `BillingPeriod.Weekly` | `weekly` | Billed every week |
360
+ | `BillingPeriod.Monthly` | `monthly` | Billed every month |
361
+ | `BillingPeriod.Quarterly` | `quarterly` | Billed every quarter |
362
+ | `BillingPeriod.Annually` | `annually` | Billed every year |
363
+
364
+ ## Subscriptions
365
+
366
+ A subscription links a customer to a plan and tracks the current billing period.
367
+
368
+ ```ts
369
+ import { SubscriptionStatus } from '@monigo/sdk'
370
+
371
+ // Create
372
+ const sub = await client.subscriptions.create({
373
+ customer_id: 'cust_abc',
374
+ plan_id: 'plan_starter',
375
+ })
376
+
377
+ // List with filters
378
+ const { subscriptions } = await client.subscriptions.list({
379
+ customer_id: 'cust_abc',
380
+ status: SubscriptionStatus.Active,
381
+ })
382
+
383
+ // Get
384
+ const sub = await client.subscriptions.get('sub_abc')
385
+
386
+ // Pause / resume
387
+ await client.subscriptions.updateStatus('sub_abc', SubscriptionStatus.Paused)
388
+ await client.subscriptions.updateStatus('sub_abc', SubscriptionStatus.Active)
389
+
390
+ // Cancel (soft-delete)
391
+ await client.subscriptions.delete('sub_abc')
392
+ ```
393
+
394
+ | Status constant | Value | Meaning |
395
+ |-----------------|-------|---------|
396
+ | `SubscriptionStatus.Active` | `active` | Billing is running |
397
+ | `SubscriptionStatus.Paused` | `paused` | Billing paused; events still accepted |
398
+ | `SubscriptionStatus.Canceled` | `canceled` | Subscription ended |
399
+
400
+ > **Warning:** A customer can hold at most one active subscription per plan type. A second active **collection** or **payout** subscription returns a 409 — catch it with `MonigoAPIError.isConflict(err)`.
401
+
402
+ ## Payout accounts
403
+
404
+ Payout accounts are bank or mobile-money accounts that a customer can receive payouts to. All methods require a `customerId` as the first argument.
405
+
406
+ ```ts
407
+ import { PayoutMethod } from '@monigo/sdk'
408
+
409
+ // Bank transfer
410
+ const account = await client.payoutAccounts.create('cust_abc', {
411
+ account_name: 'John Driver',
412
+ payout_method: PayoutMethod.BankTransfer,
413
+ bank_name: 'First Bank Nigeria',
414
+ bank_code: '011',
415
+ account_number: '3001234567',
416
+ currency: 'NGN',
417
+ is_default: true,
418
+ })
419
+
420
+ // Mobile money
421
+ const account = await client.payoutAccounts.create('cust_abc', {
422
+ account_name: 'John Driver',
423
+ payout_method: PayoutMethod.MobileMoney,
424
+ mobile_money_number: '+2348012345678',
425
+ currency: 'NGN',
426
+ })
427
+
428
+ // List all accounts for a customer
429
+ const { payout_accounts } = await client.payoutAccounts.list('cust_abc')
430
+
431
+ // Get / update / delete
432
+ const acct = await client.payoutAccounts.get('cust_abc', 'acct_xyz')
433
+ await client.payoutAccounts.update('cust_abc', 'acct_xyz', { account_name: 'Jane Driver' })
434
+ await client.payoutAccounts.delete('cust_abc', 'acct_xyz')
435
+ ```
436
+
437
+ | Method constant | Value |
438
+ |-----------------|-------|
439
+ | `PayoutMethod.BankTransfer` | `bank_transfer` |
440
+ | `PayoutMethod.MobileMoney` | `mobile_money` |
441
+
442
+ ## Invoices
443
+
444
+ Invoices are generated from subscriptions and contain line items derived from the customer's usage in the billing period.
445
+
446
+ ```ts
447
+ import { InvoiceStatus } from '@monigo/sdk'
448
+
449
+ // Generate a draft invoice
450
+ const invoice = await client.invoices.generate('sub_abc')
451
+ console.log(`Draft invoice ${invoice.id} — total: ${invoice.total} ${invoice.currency}`)
452
+
453
+ for (const item of invoice.line_items ?? []) {
454
+ console.log(` ${item.description}: qty ${item.quantity} × ${item.unit_price} = ${item.amount}`)
455
+ }
456
+
457
+ // List with filters
458
+ const { invoices } = await client.invoices.list({
459
+ customer_id: 'cust_abc',
460
+ status: InvoiceStatus.Draft,
461
+ })
462
+
463
+ // Get / finalize / void
464
+ const invoice = await client.invoices.get('inv_abc')
465
+ const finalized = await client.invoices.finalize('inv_abc')
466
+ const voided = await client.invoices.void('inv_abc')
467
+ ```
468
+
469
+ | Status constant | Value | Meaning |
470
+ |-----------------|-------|---------|
471
+ | `InvoiceStatus.Draft` | `draft` | Not yet finalized; amounts may change |
472
+ | `InvoiceStatus.Finalized` | `finalized` | Locked and ready for payment |
473
+ | `InvoiceStatus.Paid` | `paid` | Payment collected |
474
+ | `InvoiceStatus.Void` | `void` | Cancelled |
475
+
476
+ > **Note:** All monetary amounts (`subtotal`, `total`, `unit_price`) are returned as decimal strings (e.g. `"1500.00"`) to preserve precision across currencies.
477
+
478
+ ## Usage
479
+
480
+ Query aggregated usage rollups to see how much a customer has consumed in a period.
481
+
482
+ ```ts
483
+ // All usage for the current period
484
+ const result = await client.usage.query()
485
+
486
+ // Filter by customer and metric
487
+ const result = await client.usage.query({
488
+ customer_id: 'cust_abc',
489
+ metric_id: 'metric_api_calls',
490
+ })
491
+
492
+ // Custom time window — accepts Date objects or ISO strings
493
+ const result = await client.usage.query({
494
+ customer_id: 'cust_abc',
495
+ from: new Date('2026-01-01'),
496
+ to: new Date('2026-01-31'),
497
+ })
498
+
499
+ for (const rollup of result.rollups) {
500
+ console.log(
501
+ `customer ${rollup.customer_id} — ${rollup.aggregation}: ${rollup.value}`,
502
+ `(period: ${rollup.period_start} → ${rollup.period_end})`
503
+ )
504
+ }
505
+ ```
506
+
507
+ ## Example programs
508
+
509
+ The SDK ships five runnable example programs under `examples/`:
510
+
511
+ | Program | What it demonstrates |
512
+ |---------|----------------------|
513
+ | `examples/quickstart` | End-to-end: customer → metric → plan → subscription → ingest events |
514
+ | `examples/metering` | High-volume batch ingest with idempotency and rate-limit handling |
515
+ | `examples/billing` | Generate, inspect, and finalize an invoice |
516
+ | `examples/payouts` | Register payout accounts and trigger event replay |
517
+ | `examples/usage-report` | Query rollups and print a terminal usage table |
518
+
519
+ Run any example by setting `MONIGO_API_KEY` and executing from the `examples/` directory:
520
+
521
+ ```bash
522
+ cd js-sdk/examples
523
+ npm install
524
+ MONIGO_API_KEY=mk_test_... npm run quickstart
525
+ ```
526
+
527
+ ## TypeScript exports
528
+
529
+ All types and constants are exported from the root:
530
+
531
+ ```ts
532
+ import {
533
+ MonigoClient,
534
+ MonigoAPIError,
535
+
536
+ // Constants
537
+ Aggregation,
538
+ PricingModel,
539
+ PlanType,
540
+ BillingPeriod,
541
+ SubscriptionStatus,
542
+ InvoiceStatus,
543
+ PayoutMethod,
544
+
545
+ // Types
546
+ type Customer,
547
+ type CreateCustomerRequest,
548
+ type UpdateCustomerRequest,
549
+ type ListCustomersResponse,
550
+ type Metric,
551
+ type CreateMetricRequest,
552
+ type Plan,
553
+ type CreatePlanRequest,
554
+ type CreatePriceRequest,
555
+ type PriceTier,
556
+ type Subscription,
557
+ type CreateSubscriptionRequest,
558
+ type ListSubscriptionsParams,
559
+ type PayoutAccount,
560
+ type CreatePayoutAccountRequest,
561
+ type Invoice,
562
+ type InvoiceLineItem,
563
+ type ListInvoicesParams,
564
+ type IngestEvent,
565
+ type IngestRequest,
566
+ type IngestResponse,
567
+ type EventReplayJob,
568
+ type UsageRollup,
569
+ type UsageQueryParams,
570
+ type UsageQueryResult,
571
+ } from '@monigo/sdk'
572
+ ```
573
+
574
+ ## Module format
575
+
576
+ The package ships dual-format bundles to work everywhere:
577
+
578
+ | Format | File | Used by |
579
+ |--------|------|---------|
580
+ | ESM | `dist/monigo.js` | Node.js ≥ 12 (with `"type":"module"`), bundlers, browsers |
581
+ | CJS | `dist/monigo.cjs` | Node.js CommonJS (`require()`) |
582
+ | Types | `dist/index.d.ts` | TypeScript consumers (both formats) |
583
+
584
+ The `exports` map in `package.json` routes each environment automatically — no manual import paths needed.
585
+
586
+ ## Browser / CDN usage
587
+
588
+ ```html
589
+ <script type="module">
590
+ import { MonigoClient } from 'https://esm.sh/@monigo/sdk'
591
+
592
+ const client = new MonigoClient({ apiKey: 'mk_test_...' })
593
+ const { customers } = await client.customers.list()
594
+ console.log(customers)
595
+ </script>
596
+ ```
597
+
598
+ > **Warning:** Never embed a live API key in frontend code. For browser usage, proxy requests through your own backend or use a restricted publishable key if supported.
599
+
600
+ ## Development
601
+
602
+ ### Prerequisites
603
+
604
+ - Node.js ≥ 18
605
+ - npm
606
+
607
+ ### Setup
608
+
609
+ ```bash
610
+ cd js-sdk
611
+ npm install
612
+ ```
613
+
614
+ ### Build
615
+
616
+ ```bash
617
+ npm run build
618
+ ```
619
+
620
+ Produces `dist/monigo.js` (ESM), `dist/monigo.cjs` (CJS), and `dist/index.d.ts`.
621
+
622
+ ### Run tests
623
+
624
+ ```bash
625
+ npm run test:run # run all 90 tests once
626
+ npm run test # watch mode
627
+ npm run test:coverage # with V8 coverage report
628
+ ```
629
+
630
+ ### Type check
631
+
632
+ ```bash
633
+ npm run typecheck
634
+ ```
635
+
636
+ ## Publishing
637
+
638
+ The release workflow builds, tests, bumps the version, tags the commit, and publishes to npm in one command:
639
+
640
+ ```bash
641
+ # Patch release (1.0.0 → 1.0.1) — bug fixes
642
+ npm run release:patch
643
+
644
+ # Minor release (1.0.0 → 1.1.0) — new features, backwards-compatible
645
+ npm run release:minor
646
+
647
+ # Major release (1.0.0 → 2.0.0) — breaking changes
648
+ npm run release:major
649
+ ```
650
+
651
+ Each command runs:
652
+ 1. `npm run build` — compile TypeScript and bundle
653
+ 2. `npm run test:run` — run the full test suite
654
+ 3. `npm version <patch|minor|major>` — bump `package.json` and create a git tag
655
+ 4. `git push --follow-tags` — push the commit and tag to the remote
656
+ 5. `npm publish --access public` — publish to the npm registry
657
+
658
+ ### CI/CD
659
+
660
+ Add this GitHub Actions workflow to publish automatically on a tag push:
661
+
662
+ ```yaml
663
+ # .github/workflows/publish.yml
664
+ name: Publish to npm
665
+
666
+ on:
667
+ push:
668
+ tags:
669
+ - 'js-sdk/v*'
670
+
671
+ jobs:
672
+ publish:
673
+ runs-on: ubuntu-latest
674
+ steps:
675
+ - uses: actions/checkout@v4
676
+ - uses: actions/setup-node@v4
677
+ with:
678
+ node-version: '22'
679
+ registry-url: 'https://registry.npmjs.org'
680
+ - run: npm ci
681
+ working-directory: js-sdk
682
+ - run: npm run build
683
+ working-directory: js-sdk
684
+ - run: npm run test:run
685
+ working-directory: js-sdk
686
+ - run: npm publish --access public
687
+ working-directory: js-sdk
688
+ env:
689
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
690
+ ```
691
+
692
+ ## Versioning
693
+
694
+ This SDK follows [Semantic Versioning](https://semver.org/):
695
+
696
+ - **Patch** (`x.y.Z`) — bug fixes, internal improvements, no API changes
697
+ - **Minor** (`x.Y.0`) — new methods or options, fully backwards-compatible
698
+ - **Major** (`X.0.0`) — breaking changes to the public API surface
699
+
700
+ ## Changelog
701
+
702
+ ### 1.0.0 (2026-02-22)
703
+
704
+ - Initial release
705
+ - Full coverage of all 9 Monigo API resource groups: Events, Customers, Metrics, Plans, Subscriptions, Payout Accounts, Invoices, Usage
706
+ - Dual ESM + CJS build via Vite library mode
707
+ - 90 unit tests with zero external dependencies
708
+ - TypeScript types and constant objects for all enum values
709
+
710
+ ## License
711
+
712
+ MIT