@ophelio/sdk 0.1.0

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,158 @@
1
+ # @ophelio/sdk
2
+
3
+ Official JavaScript / TypeScript SDK for the [Ophel.io](https://ophel.io)
4
+ membership & entitlement API. Works on Node 20+, Cloudflare Workers, Deno and
5
+ Bun — zero runtime dependencies, ESM + CJS, fully typed from the platform's
6
+ OpenAPI spec.
7
+
8
+ > **Status:** pre-1.0. Minor versions may contain breaking changes (most
9
+ > notably, list-method return shapes will change when the API gains
10
+ > pagination).
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ npm install @ophelio/sdk
16
+ ```
17
+
18
+ ## Quickstart
19
+
20
+ ```ts
21
+ import { Ophelio } from '@ophelio/sdk'
22
+
23
+ const ophelio = new Ophelio({ apiKey: process.env.OPHELIO_API_KEY })
24
+
25
+ // Gate admission check
26
+ const result = await ophelio.admit.check({ card: 'M-123456' })
27
+ if (result.admitted) {
28
+ console.log(result.pricing_group) // e.g. 'member_adult'
29
+ }
30
+
31
+ // Offline gate cache
32
+ const { members, generated_at } = await ophelio.members.sync()
33
+ // later, incremental:
34
+ await ophelio.members.sync({ since: generated_at })
35
+ ```
36
+
37
+ ## Authentication
38
+
39
+ Pass your project API key (`o_live_…`); the project is implied by the key:
40
+
41
+ ```ts
42
+ new Ophelio({ apiKey: 'o_live_…' })
43
+ ```
44
+
45
+ For first-party / server-side session use, pass auth headers instead and
46
+ optionally your own `fetch`:
47
+
48
+ ```ts
49
+ new Ophelio({
50
+ baseUrl: 'https://api.your-deployment.example',
51
+ headers: { cookie: cookieHeader },
52
+ fetch: event.fetch, // e.g. SvelteKit
53
+ })
54
+ ```
55
+
56
+ ## Errors
57
+
58
+ Non-2xx responses throw a typed subclass of `OphelioError` mapped from the
59
+ API's AIP-193 envelope:
60
+
61
+ ```ts
62
+ import { NotFoundError, OphelioError } from '@ophelio/sdk'
63
+
64
+ try {
65
+ await ophelio.memberships.get(id)
66
+ } catch (error) {
67
+ if (error instanceof NotFoundError) {
68
+ // error.status === 404, error.code === 'NOT_FOUND', error.details — raw ErrorInfo
69
+ } else if (error instanceof OphelioError) {
70
+ // UnauthenticatedError, PermissionDeniedError, AlreadyExistsError,
71
+ // ResourceExhaustedError, OphelioTimeoutError, OphelioConnectionError, …
72
+ }
73
+ }
74
+ ```
75
+
76
+ ## Retries & idempotency
77
+
78
+ GETs are retried automatically on network errors / 429 / 502 / 503 / 504
79
+ (exponential backoff with jitter, `Retry-After` honoured; configure with
80
+ `maxRetries`, default 2). Plain mutations are **never** retried.
81
+
82
+ `entitlements.redeem` and `entitlements.recordUsage` are idempotency-keyed:
83
+ the SDK generates a key per call and reuses it across its own retries, so
84
+ they're safely retryable. If you retry at the application level (offline
85
+ gate queues), pass your own key:
86
+
87
+ ```ts
88
+ await ophelio.entitlements.redeem(
89
+ { member_id, entitlement_code: 'guest_pass' },
90
+ { idempotencyKey: `gate-7-${visitId}` },
91
+ )
92
+ ```
93
+
94
+ ## Webhooks
95
+
96
+ Verify deliveries with the `X-Ophelio-Signature` header and your
97
+ subscription secret. Always pass the **raw** request body:
98
+
99
+ ```ts
100
+ import { Ophelio, WebhookSignatureVerificationError } from '@ophelio/sdk'
101
+
102
+ const event = await Ophelio.webhooks.constructEvent(
103
+ rawBody,
104
+ request.headers.get('X-Ophelio-Signature'),
105
+ process.env.OPHELIO_WEBHOOK_SECRET,
106
+ ) // throws WebhookSignatureVerificationError on mismatch
107
+
108
+ switch (event.type) {
109
+ case 'membership.payment_failed':
110
+ // event.data — see the event reference at https://docs.ophel.io
111
+ break
112
+ }
113
+ ```
114
+
115
+ Unknown event types still verify and parse, so new platform events don't
116
+ break older SDK versions.
117
+
118
+ ## API surface
119
+
120
+ Everything reachable with an API key is covered:
121
+
122
+ | Namespace | Methods |
123
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
124
+ | `admit` | `check({ card })` |
125
+ | `members` | `sync({ since? })`, `get(id)`, `listEntitlements(id, { type? })`, `reissueCard(id, { note? })` |
126
+ | `memberships` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `cancel(id, ...)`, `uncancel(id)`, `renew(id, ...)`, `upgrade(id, ...)`, `downgrade(id, ...)`, `listMembers(id)`, `listScheduledChanges(id, { status? })`, `cancelScheduledChange(id, changeId)`, `listTransactions(id)`, `listTermTransactions(id, termId)`, `createTransaction(id, ...)` |
127
+ | `customers` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)`, `listMemberships(id)` |
128
+ | `entitlements` | `redeem(...)`, `recordUsage(...)`, `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
129
+ | `plans` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)`, `listBillingOptions(id)`, `createBillingOption(id, ...)`, `listEntitlements(id)`, `createEntitlement(id, ...)`, `listPlanLinks(id)`, `createPlanLink(id, ...)`, `listPricingGroupMappings(id)`, `createPricingGroupMapping(id, ...)` |
130
+ | `billingOptions` | `list()`, `get(id)`, `update(id, ...)`, `delete(id)`, `listSalesChannels(id)`, `createSalesChannelLink(id, ...)` |
131
+ | `memberRoles` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
132
+ | `pricingGroups` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
133
+ | `salesChannels` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
134
+ | `planEntitlements` | `get(id)`, `update(id, ...)`, `delete(id)` |
135
+ | `planLinks` | `delete(id)` |
136
+ | `planPricingGroupMappings` | `delete(id)` |
137
+ | `billingOptionSalesChannels` | `delete(id)` |
138
+ | `webhookSubscriptions` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)`, `listDeliveries(id)` |
139
+
140
+ Every method accepts a trailing `options` argument: `{ signal?, headers? }`
141
+ (plus `idempotencyKey?` on `redeem` / `recordUsage`).
142
+
143
+ Endpoints requiring a user session (API key management, organisation and
144
+ project admin) are intentionally not in the SDK — API keys cannot call them.
145
+
146
+ ## Development (monorepo)
147
+
148
+ Types are generated from `packages/ophelio-api/openapi.json`:
149
+
150
+ ```sh
151
+ npm run generate:sdk # repo root: regenerate spec + SDK types
152
+ npm test -w packages/ophelio-sdk
153
+ ```
154
+
155
+ `src/generated/schema.d.ts` is a build artifact (git-ignored), regenerated
156
+ from the committed spec on every `build`, `typecheck`, and `test`. The
157
+ committed source of truth is `packages/ophelio-api/openapi.json`; CI fails
158
+ if it drifts from the API controllers.