@ledewire/browser 0.3.0 → 0.5.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/llms.txt ADDED
@@ -0,0 +1,290 @@
1
+ # @ledewire/browser
2
+
3
+ > LedeWire Browser SDK — buyer authentication, checkout, wallet, and purchases for web apps.
4
+ > Works as a CDN script tag (no build step) or as an ESM npm package.
5
+ > npm install @ledewire/browser
6
+ > Source: https://github.com/ledewire/ledewire-js-sdk
7
+ > API docs: https://ledewire.github.io/ledewire-js-sdk/api/
8
+
9
+ ## CDN (no install required)
10
+
11
+ ```html
12
+ <!-- Pin to a major version in production -->
13
+ <script src="https://cdn.jsdelivr.net/npm/@ledewire/browser@0/dist/ledewire.min.js"></script>
14
+ <script>
15
+ const lw = Ledewire.init({ apiKey: 'your_api_key' })
16
+ </script>
17
+ ```
18
+
19
+ The global is `Ledewire`. All methods return Promises — use async/await or `.then()`.
20
+
21
+ ## npm / ESM install
22
+
23
+ ```bash
24
+ npm install @ledewire/browser
25
+ ```
26
+
27
+ ```ts
28
+ import { init, localStorageAdapter } from '@ledewire/browser'
29
+ ```
30
+
31
+ ## init() configuration
32
+
33
+ ```ts
34
+ import { init } from '@ledewire/browser'
35
+ import type { BrowserClientConfig } from '@ledewire/browser'
36
+
37
+ const lw = init({
38
+ apiKey: string, // required — your LedeWire platform API key
39
+
40
+ baseUrl?: string, // defaults to 'https://api.ledewire.com'
41
+
42
+ // Token persistence — defaults to MemoryTokenStorage (cleared on page unload)
43
+ storage?: TokenStorage,
44
+
45
+ onTokenRefreshed?: (tokens: StoredTokens) => void,
46
+ onAuthExpired?: () => void, // called when refresh fails — show login UI
47
+ })
48
+ ```
49
+
50
+ Token refresh is automatic — never call a refresh method manually.
51
+
52
+ ## localStorageAdapter
53
+
54
+ Persist sessions across page reloads using `localStorage`:
55
+
56
+ ```ts
57
+ import { init, localStorageAdapter } from '@ledewire/browser'
58
+
59
+ const lw = init({
60
+ apiKey: 'your_api_key',
61
+ storage: localStorageAdapter(), // persists under 'lw_tokens' key
62
+ onAuthExpired: () => showLoginModal(),
63
+ })
64
+ ```
65
+
66
+ Use `MemoryTokenStorage` (default) for highest security — tokens are never written
67
+ to disk and are cleared when the page unloads.
68
+
69
+ ## Field naming convention
70
+
71
+ All API response types use `snake_case` field names (`access_token`, `price_cents`,
72
+ `content_uri`), matching the wire format exactly.
73
+
74
+ SDK-owned types use `camelCase` where they do not map 1:1 to a raw API response
75
+ field: `StoredTokens.accessToken`, `StoredTokens.expiresAt: number` (a converted
76
+ Unix ms timestamp, not the raw `expires_at: string`).
77
+
78
+ Do not attempt to remap snake_case fields — the SDK has no `transformKeys` option.
79
+
80
+ ## Complete method reference
81
+
82
+ ### lw.config
83
+
84
+ ```ts
85
+ lw.config.getPublic() → Promise<PublicConfigResponse>
86
+ // { google_client_id: string, ... }
87
+ // Safe to call before authentication — use to get the Google Client ID for sign-in UI.
88
+ ```
89
+
90
+ ### lw.auth (buyer and seller auth)
91
+
92
+ ```ts
93
+ lw.auth.signup(body) → Promise<AuthenticationResponse>
94
+ lw.auth.loginWithEmail({ email, password }) → Promise<AuthenticationResponse>
95
+ lw.auth.loginWithGoogle({ id_token }) → Promise<AuthenticationResponse>
96
+ lw.auth.loginWithApiKey({ key, secret? }) → Promise<AuthenticationResponse>
97
+ // key-only → view permission (read-only, sufficient for lw.seller.content.*)
98
+ // key + secret → full permission (read/write)
99
+ lw.auth.logout() → Promise<void>
100
+ lw.auth.requestPasswordReset({ email }) → Promise<AuthPasswordResetResponse>
101
+ lw.auth.resetPassword({ email, reset_code, password }) → Promise<AuthPasswordResetResponse>
102
+ ```
103
+
104
+ Tokens are stored automatically after every successful login.
105
+
106
+ ### lw.checkout
107
+
108
+ ```ts
109
+ lw.checkout.state(contentId: string) → Promise<CheckoutStateResponse>
110
+ ```
111
+
112
+ Use this to determine what the current visitor needs to do before accessing content:
113
+
114
+ ```ts
115
+ const { checkout_state } = await lw.checkout.state('article-id')
116
+
117
+ switch (checkout_state.next_required_action) {
118
+ case 'authenticate':
119
+ // Show login/signup UI
120
+ break
121
+ case 'fund_wallet':
122
+ // Show wallet funding UI (checkout_state.has_sufficient_funds === false)
123
+ break
124
+ case 'purchase':
125
+ // Show purchase confirmation UI
126
+ break
127
+ case 'view_content':
128
+ // User has access — fetch and render the content
129
+ break
130
+ }
131
+ ```
132
+
133
+ ### lw.wallet
134
+
135
+ ```ts
136
+ lw.wallet.balance() → Promise<WalletBalanceResponse>
137
+ // { balance_cents: number }
138
+
139
+ lw.wallet.transactions(params?: PaginationParams) → Promise<{ data: WalletTransactionItem[], pagination: PaginationMeta }>
140
+
141
+ lw.wallet.createPaymentSession({ amount_cents: number }) → Promise<WalletPaymentSessionResponse>
142
+ // { payment_url: string, session_id: string, ... } — redirect user to payment_url
143
+
144
+ lw.wallet.getPaymentStatus(sessionId: string) → Promise<WalletPaymentStatusResponse>
145
+ // Poll after return from payment redirect to confirm funding
146
+ ```
147
+
148
+ ### lw.purchases
149
+
150
+ ```ts
151
+ lw.purchases.create({ content_id: string }) → Promise<PurchaseResponse>
152
+ lw.purchases.list(params?: PaginationParams) → Promise<{ data: PurchaseResponse[], pagination: PaginationMeta }>
153
+ lw.purchases.get(purchaseId: string) → Promise<PurchaseResponse>
154
+ ```
155
+
156
+ ### lw.content
157
+
158
+ ```ts
159
+ lw.content.getWithAccess(contentId: string, options?) → Promise<ContentWithAccessResponse>
160
+ // Returns content payload + access info. Requires a valid buyer session.
161
+ // content_type: 'markdown' | 'external_ref'
162
+ // content_body: base64-encoded string (use atob() to decode markdown)
163
+ // content_uri: URL for external_ref content (Vimeo, YouTube, PDF, etc.)
164
+ ```
165
+
166
+ ### lw.seller.content (requires API key login)
167
+
168
+ Read-only operations scoped to the authenticated seller's store.
169
+ Store identity is derived server-side from the token — no `store_id` needed.
170
+
171
+ ```ts
172
+ // 1. Obtain a view token (key-only is sufficient)
173
+ await lw.auth.loginWithApiKey({ key: 'your_api_key' })
174
+
175
+ // 2. List all store content
176
+ lw.seller.content.list() → Promise<ContentResponse[]>
177
+
178
+ // 3. Search by title, URI, or metadata (AND logic — at least one field required)
179
+ lw.seller.content.search({ title?: string, uri?: string, metadata?: Record<string, unknown> })
180
+ → Promise<ContentResponse[]>
181
+
182
+ // 4. Fetch a single item
183
+ lw.seller.content.get(contentId: string) → Promise<ContentResponse>
184
+ ```
185
+
186
+ ## Full checkout flow example
187
+
188
+ ```ts
189
+ const lw = Ledewire.init({ apiKey: 'your_api_key' })
190
+
191
+ const { checkout_state } = await lw.checkout.state('article-123')
192
+
193
+ switch (checkout_state.next_required_action) {
194
+ case 'authenticate':
195
+ await lw.auth.loginWithEmail({ email, password })
196
+ break
197
+
198
+ case 'fund_wallet':
199
+ const session = await lw.wallet.createPaymentSession({ amount_cents: 500 })
200
+ window.location.href = session.payment_url // Stripe checkout
201
+ break
202
+
203
+ case 'purchase':
204
+ await lw.purchases.create({ content_id: 'article-123' })
205
+ break
206
+
207
+ case 'view_content':
208
+ const { content_type, content_body, content_uri } =
209
+ await lw.content.getWithAccess('article-123')
210
+ if (content_type === 'markdown') {
211
+ renderMarkdown(atob(content_body)) // base64-decode then render
212
+ } else {
213
+ window.location.href = content_uri // redirect to gated video/PDF
214
+ }
215
+ break
216
+ }
217
+ ```
218
+
219
+ ## Google OAuth sign-in setup
220
+
221
+ ```ts
222
+ // Fetch the platform's Google Client ID before the user signs in:
223
+ const { google_client_id } = await lw.config.getPublic()
224
+ google.accounts.id.initialize({
225
+ client_id: google_client_id,
226
+ callback: async ({ credential }) => {
227
+ await lw.auth.loginWithGoogle({ id_token: credential })
228
+ },
229
+ })
230
+ google.accounts.id.renderButton(document.getElementById('signin-btn'), { theme: 'outline' })
231
+ ```
232
+
233
+ ## Key types
234
+
235
+ ```ts
236
+ interface CheckoutStateResponse {
237
+ content_id: string
238
+ content_title: string
239
+ price_cents: number
240
+ checkout_state: {
241
+ is_authenticated: boolean
242
+ has_sufficient_funds: boolean | null // null when not authenticated
243
+ has_purchased: boolean
244
+ next_required_action: 'authenticate' | 'fund_wallet' | 'purchase' | 'view_content'
245
+ }
246
+ }
247
+
248
+ interface StoredTokens {
249
+ accessToken: string
250
+ refreshToken: string
251
+ expiresAt: number // Unix timestamp (ms)
252
+ }
253
+
254
+ interface TokenStorage {
255
+ getTokens(): Promise<StoredTokens | null>
256
+ setTokens(tokens: StoredTokens): Promise<void>
257
+ clearTokens(): Promise<void>
258
+ }
259
+ ```
260
+
261
+ ## Error classes
262
+
263
+ All errors extend `LedewireError`. Import named subclasses for actionable handling:
264
+
265
+ ```ts
266
+ import { LedewireError, AuthError, ForbiddenError, NotFoundError, PurchaseError } from '@ledewire/browser'
267
+
268
+ class LedewireError extends Error {
269
+ statusCode: number // HTTP status code
270
+ code: number | undefined // machine-readable code from API body
271
+ message: string // human-readable description from API body
272
+ }
273
+
274
+ class AuthError extends LedewireError {} // 401 — bad credentials / session expired
275
+ class ForbiddenError extends LedewireError {} // 403 — authenticated but access denied
276
+ class NotFoundError extends LedewireError {} // 404 — resource not found
277
+ class PurchaseError extends LedewireError {} // 409/422 — purchase validation failure
278
+ ```
279
+
280
+ ```ts
281
+ try {
282
+ await lw.purchases.create({ content_id: 'article-123' })
283
+ } catch (err) {
284
+ if (err instanceof AuthError) {
285
+ showLoginModal()
286
+ } else if (err instanceof PurchaseError) {
287
+ showError('Purchase failed: ' + err.message)
288
+ }
289
+ }
290
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ledewire/browser",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "LedeWire SDK for browsers — embed a paywall with a single script tag",
5
5
  "type": "module",
6
6
  "exports": {
@@ -15,7 +15,8 @@
15
15
  "types": "./dist/index.d.ts",
16
16
  "files": [
17
17
  "dist",
18
- "README.md"
18
+ "README.md",
19
+ "llms.txt"
19
20
  ],
20
21
  "keywords": [
21
22
  "ledewire",