@ledewire/browser 0.2.2 → 0.3.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 +12 -0
- package/dist/index.d.ts +120 -7
- package/dist/index.js +53 -28
- package/dist/index.js.map +1 -1
- package/dist/ledewire.min.js +1 -1
- package/dist/ledewire.min.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,12 +42,24 @@ const lw = init({
|
|
|
42
42
|
|
|
43
43
|
| Namespace | Description |
|
|
44
44
|
| -------------- | ------------------------------------------------ |
|
|
45
|
+
| `lw.config` | Platform public config (no auth required) |
|
|
45
46
|
| `lw.auth` | Buyer signup, email/password login, Google OAuth |
|
|
46
47
|
| `lw.wallet` | Wallet balance, fund wallet via payment session |
|
|
47
48
|
| `lw.purchases` | List and create content purchases |
|
|
48
49
|
| `lw.content` | Fetch content with buyer access info |
|
|
49
50
|
| `lw.checkout` | Checkout state — what action is required next |
|
|
50
51
|
|
|
52
|
+
## Example: Fetch Google OAuth Client ID Before Sign-In
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const lw = Ledewire.init({ apiKey: 'your_api_key' })
|
|
56
|
+
|
|
57
|
+
// No login needed — safe to call on page load
|
|
58
|
+
const { google_client_id } = await lw.config.getPublic()
|
|
59
|
+
google.accounts.id.initialize({ client_id: google_client_id, callback: handleCredential })
|
|
60
|
+
google.accounts.id.renderButton(document.getElementById('signin-btn'), { theme: 'outline' })
|
|
61
|
+
```
|
|
62
|
+
|
|
51
63
|
## Example: Full Checkout Flow
|
|
52
64
|
|
|
53
65
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -109,6 +109,8 @@ declare class BrowserClient {
|
|
|
109
109
|
readonly _http: HttpClient;
|
|
110
110
|
readonly _tokenManager: TokenManager;
|
|
111
111
|
readonly _config: BrowserClientConfig;
|
|
112
|
+
/** Platform-level public configuration (no auth required) */
|
|
113
|
+
readonly config: BrowserConfigNamespace;
|
|
112
114
|
/** Buyer authentication: email/password signup/login, Google, password reset */
|
|
113
115
|
readonly auth: BrowserAuthNamespace;
|
|
114
116
|
/** Checkout state machine: determines next action for a piece of content */
|
|
@@ -160,6 +162,31 @@ export declare interface BrowserClientConfig {
|
|
|
160
162
|
onAuthExpired?: () => void;
|
|
161
163
|
}
|
|
162
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Platform-level configuration for browser clients.
|
|
167
|
+
*
|
|
168
|
+
* Obtain via `client.config` — do not construct directly.
|
|
169
|
+
*/
|
|
170
|
+
declare class BrowserConfigNamespace {
|
|
171
|
+
protected readonly http: HttpClient;
|
|
172
|
+
/* Excluded from this release type: __constructor */
|
|
173
|
+
/**
|
|
174
|
+
* Returns platform-level public configuration.
|
|
175
|
+
* No authentication required — safe to call before the user has signed in.
|
|
176
|
+
*
|
|
177
|
+
* Use `google_client_id` to initialise the Google Identity Services library
|
|
178
|
+
* on the storefront login page without requiring a prior authenticated call.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* const lw = init({ apiKey: 'your_api_key' })
|
|
183
|
+
* const { google_client_id } = await lw.config.getPublic()
|
|
184
|
+
* google.accounts.id.initialize({ client_id: google_client_id, callback })
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
getPublic(): Promise<PublicConfigResponse>;
|
|
188
|
+
}
|
|
189
|
+
|
|
163
190
|
/**
|
|
164
191
|
* Buyer content namespace — fetch content with real-time access information.
|
|
165
192
|
*
|
|
@@ -315,6 +342,11 @@ export declare type CheckoutStateResponse = components['schemas']['CheckoutState
|
|
|
315
342
|
|
|
316
343
|
declare interface components {
|
|
317
344
|
schemas: {
|
|
345
|
+
/** @description Platform-level public configuration. No authentication required. */
|
|
346
|
+
PublicConfigResponse: {
|
|
347
|
+
/** @description Google OAuth client ID for initialising the Google Identity Services library. */
|
|
348
|
+
google_client_id: string;
|
|
349
|
+
};
|
|
318
350
|
ContentAccessInfo: {
|
|
319
351
|
user_id: string | null;
|
|
320
352
|
has_purchased: boolean;
|
|
@@ -335,7 +367,7 @@ declare interface components {
|
|
|
335
367
|
*/
|
|
336
368
|
expires_at: string;
|
|
337
369
|
};
|
|
338
|
-
/** @description Token response for merchant authentication.
|
|
370
|
+
/** @description Token response for merchant authentication. Includes stores the user has access to so the client can prompt for store selection. */
|
|
339
371
|
MerchantAuthenticationResponse: {
|
|
340
372
|
/** @enum {string} */
|
|
341
373
|
token_type: 'Bearer';
|
|
@@ -343,6 +375,18 @@ declare interface components {
|
|
|
343
375
|
refresh_token: string;
|
|
344
376
|
/** Format: date-time */
|
|
345
377
|
expires_at: string;
|
|
378
|
+
/** @description Stores the authenticated user can manage, for store-selection on the client. */
|
|
379
|
+
stores: components['schemas']['MerchantLoginStore'][];
|
|
380
|
+
};
|
|
381
|
+
/** @description A store the authenticated merchant user has access to. */
|
|
382
|
+
MerchantLoginStore: {
|
|
383
|
+
id: string;
|
|
384
|
+
name: string;
|
|
385
|
+
/**
|
|
386
|
+
* @description Derived role. `owner` if the user owns this store; `author` if they have author permissions only.
|
|
387
|
+
* @enum {string}
|
|
388
|
+
*/
|
|
389
|
+
role: 'owner' | 'author';
|
|
346
390
|
};
|
|
347
391
|
JWTClaims: {
|
|
348
392
|
/** @description User or Store identifier */
|
|
@@ -414,8 +458,11 @@ declare interface components {
|
|
|
414
458
|
store_id: string;
|
|
415
459
|
store_name: string;
|
|
416
460
|
store_key: string;
|
|
417
|
-
/**
|
|
418
|
-
|
|
461
|
+
/**
|
|
462
|
+
* @description Derived role. `owner` if the user owns this store; `author` if they have author permissions only.
|
|
463
|
+
* @enum {string}
|
|
464
|
+
*/
|
|
465
|
+
role: 'owner' | 'author';
|
|
419
466
|
/** @description Whether the authenticated user has author permissions for this store. */
|
|
420
467
|
is_author: boolean;
|
|
421
468
|
logo: string | null;
|
|
@@ -425,12 +472,14 @@ declare interface components {
|
|
|
425
472
|
user_id: string | null;
|
|
426
473
|
store_id: string;
|
|
427
474
|
/**
|
|
428
|
-
* @description
|
|
475
|
+
* @description Ownership flag expressed as a string. `owner` when `is_owner` is true; `null` for non-owner members (authors or uninvited users).
|
|
429
476
|
* @enum {string|null}
|
|
430
477
|
*/
|
|
431
478
|
role: 'owner' | null;
|
|
432
479
|
/** @description Whether this user can create and manage their own content. */
|
|
433
480
|
is_author: boolean;
|
|
481
|
+
/** @description Per-author fee override in basis points (1/100th of a percent). Overrides the store's `default_author_fee_bps` when set. `null` means the store default applies. */
|
|
482
|
+
author_fee_bps: number | null;
|
|
434
483
|
/** Format: date-time */
|
|
435
484
|
invited_at: string | null;
|
|
436
485
|
/** Format: date-time */
|
|
@@ -445,6 +494,8 @@ declare interface components {
|
|
|
445
494
|
* @default true
|
|
446
495
|
*/
|
|
447
496
|
is_author: boolean;
|
|
497
|
+
/** @description Per-author fee override in basis points (0–10000). Overrides the store's `default_author_fee_bps`. `null` or omitted means the store default applies. */
|
|
498
|
+
author_fee_bps?: number | null;
|
|
448
499
|
};
|
|
449
500
|
MerchantInviteResponse: {
|
|
450
501
|
invitation_token: string;
|
|
@@ -452,6 +503,8 @@ declare interface components {
|
|
|
452
503
|
/** @enum {string|null} */
|
|
453
504
|
role: 'owner' | null;
|
|
454
505
|
is_author: boolean;
|
|
506
|
+
/** @description Per-author fee override set at invitation time. */
|
|
507
|
+
author_fee_bps?: number | null;
|
|
455
508
|
/** Format: date-time */
|
|
456
509
|
expires_at: string;
|
|
457
510
|
};
|
|
@@ -541,7 +594,7 @@ declare interface components {
|
|
|
541
594
|
*/
|
|
542
595
|
content_body?: string;
|
|
543
596
|
/**
|
|
544
|
-
* @description URI of the
|
|
597
|
+
* @description URI of the resource. Required for `external_ref` content; optional for `markdown` content.
|
|
545
598
|
* @example https://vimeo.com/123456789
|
|
546
599
|
*/
|
|
547
600
|
content_uri?: string;
|
|
@@ -773,7 +826,7 @@ declare interface components {
|
|
|
773
826
|
*/
|
|
774
827
|
started_at?: string;
|
|
775
828
|
};
|
|
776
|
-
/** @description Create request body for content. Required fields vary by `content_type`: `markdown` requires `content_body`; `external_ref` requires `content_uri`. */
|
|
829
|
+
/** @description Create request body for content. Required fields vary by `content_type`: `markdown` requires `content_body`; `external_ref` requires `content_uri`. Both types accept an optional `content_uri` link. */
|
|
777
830
|
Content: {
|
|
778
831
|
/**
|
|
779
832
|
* @description The type of content being created.
|
|
@@ -788,7 +841,7 @@ declare interface components {
|
|
|
788
841
|
*/
|
|
789
842
|
content_body?: string;
|
|
790
843
|
/**
|
|
791
|
-
* @description URI of the
|
|
844
|
+
* @description URI of the resource. Required when `content_type` is `external_ref`; optional for `markdown` content.
|
|
792
845
|
* @example https://vimeo.com/123456789
|
|
793
846
|
*/
|
|
794
847
|
content_uri?: string;
|
|
@@ -880,6 +933,59 @@ declare interface components {
|
|
|
880
933
|
[key: string]: unknown;
|
|
881
934
|
};
|
|
882
935
|
};
|
|
936
|
+
/** @description Pagination metadata returned on all paginated list endpoints. */
|
|
937
|
+
PaginationMeta: {
|
|
938
|
+
/**
|
|
939
|
+
* @description Total number of records matching the query.
|
|
940
|
+
* @example 84
|
|
941
|
+
*/
|
|
942
|
+
total: number;
|
|
943
|
+
/**
|
|
944
|
+
* @description Maximum number of records returned per page.
|
|
945
|
+
* @example 25
|
|
946
|
+
*/
|
|
947
|
+
per_page: number;
|
|
948
|
+
/**
|
|
949
|
+
* @description The current page number (1-based).
|
|
950
|
+
* @example 2
|
|
951
|
+
*/
|
|
952
|
+
current_page: number;
|
|
953
|
+
/**
|
|
954
|
+
* @description Total number of pages.
|
|
955
|
+
* @example 4
|
|
956
|
+
*/
|
|
957
|
+
total_pages: number;
|
|
958
|
+
/**
|
|
959
|
+
* @description Next page number, or null if on the last page.
|
|
960
|
+
* @example 3
|
|
961
|
+
*/
|
|
962
|
+
next_page?: number | null;
|
|
963
|
+
/**
|
|
964
|
+
* @description Previous page number, or null if on the first page.
|
|
965
|
+
* @example 1
|
|
966
|
+
*/
|
|
967
|
+
prev_page?: number | null;
|
|
968
|
+
};
|
|
969
|
+
/** @description Paginated list of content items. */
|
|
970
|
+
PaginatedContentList: {
|
|
971
|
+
data: components['schemas']['ContentListItem'][];
|
|
972
|
+
pagination: components['schemas']['PaginationMeta'];
|
|
973
|
+
};
|
|
974
|
+
/** @description Paginated list of content sales statistics. */
|
|
975
|
+
PaginatedSalesList: {
|
|
976
|
+
data: components['schemas']['SalesStatisticsItem'][];
|
|
977
|
+
pagination: components['schemas']['PaginationMeta'];
|
|
978
|
+
};
|
|
979
|
+
/** @description Paginated list of anonymised buyer statistics. */
|
|
980
|
+
PaginatedBuyersList: {
|
|
981
|
+
data: components['schemas']['BuyerStatisticsItem'][];
|
|
982
|
+
pagination: components['schemas']['PaginationMeta'];
|
|
983
|
+
};
|
|
984
|
+
/** @description Paginated list of store members. */
|
|
985
|
+
PaginatedUsersList: {
|
|
986
|
+
data: components['schemas']['MerchantUser'][];
|
|
987
|
+
pagination: components['schemas']['PaginationMeta'];
|
|
988
|
+
};
|
|
883
989
|
/** @description Lightweight content representation returned by list and search endpoints. Omits body fields to keep list payloads small. Use the detail endpoint (`GET /v1/merchant/:store_id/content/:id`) to retrieve the full body or URI. */
|
|
884
990
|
ContentListItem: {
|
|
885
991
|
id: string;
|
|
@@ -1215,6 +1321,13 @@ export declare class NotFoundError extends LedewireError {
|
|
|
1215
1321
|
*/
|
|
1216
1322
|
export declare function parseExpiresAt(expiresAt: string): number;
|
|
1217
1323
|
|
|
1324
|
+
/**
|
|
1325
|
+
* Platform-level public configuration returned by `GET /v1/config/public`.
|
|
1326
|
+
* No authentication required. Use this to get the Google OAuth client ID
|
|
1327
|
+
* before the user has signed in.
|
|
1328
|
+
*/
|
|
1329
|
+
export declare type PublicConfigResponse = components['schemas']['PublicConfigResponse'];
|
|
1330
|
+
|
|
1218
1331
|
/** Request body for creating a purchase. */
|
|
1219
1332
|
export declare type PurchaseCreateRequest = components['schemas']['PurchaseCreateRequest'];
|
|
1220
1333
|
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var p = Object.defineProperty;
|
|
2
2
|
var w = (s, e, t) => e in s ? p(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t;
|
|
3
3
|
var n = (s, e, t) => w(s, typeof e != "symbol" ? e + "" : e, t);
|
|
4
|
-
class
|
|
4
|
+
class c extends Error {
|
|
5
5
|
constructor(t, r, o) {
|
|
6
6
|
super(t);
|
|
7
7
|
/** HTTP status code returned by the API (e.g. 400, 401, 404, 422). */
|
|
@@ -11,33 +11,33 @@ class h extends Error {
|
|
|
11
11
|
this.name = "LedewireError", this.statusCode = r, this.code = o, Object.setPrototypeOf(this, new.target.prototype);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
-
class u extends
|
|
14
|
+
class u extends c {
|
|
15
15
|
constructor(e, t) {
|
|
16
16
|
super(e, 401, t), this.name = "AuthError";
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
class d extends
|
|
19
|
+
class d extends c {
|
|
20
20
|
constructor(e, t) {
|
|
21
21
|
super(e, 403, t), this.name = "ForbiddenError";
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
class k extends
|
|
24
|
+
class k extends c {
|
|
25
25
|
constructor(e, t) {
|
|
26
26
|
super(e, 404, t), this.name = "NotFoundError";
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
class
|
|
29
|
+
class N extends c {
|
|
30
30
|
constructor(e, t, r) {
|
|
31
31
|
super(e, t, r), this.name = "PurchaseError";
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
const
|
|
34
|
+
const g = "https://api.ledewire.com";
|
|
35
35
|
class f {
|
|
36
36
|
constructor(e = {}) {
|
|
37
37
|
n(this, "baseUrl");
|
|
38
38
|
n(this, "getAccessToken");
|
|
39
39
|
n(this, "onUnauthorized");
|
|
40
|
-
this.baseUrl = (e.baseUrl ??
|
|
40
|
+
this.baseUrl = (e.baseUrl ?? g).replace(/\/$/, ""), this.getAccessToken = e.getAccessToken ?? (() => null), this.onUnauthorized = e.onUnauthorized ?? (() => null);
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
43
|
* GET request with optional query parameters.
|
|
@@ -86,11 +86,11 @@ class f {
|
|
|
86
86
|
return !t || Object.keys(t).length === 0 ? r : `${r}?${new URLSearchParams(t).toString()}`;
|
|
87
87
|
}
|
|
88
88
|
async request(e, t, r, o = !1) {
|
|
89
|
-
const
|
|
89
|
+
const h = await this.getAccessToken(), i = {
|
|
90
90
|
"Content-Type": "application/json",
|
|
91
91
|
Accept: "application/json"
|
|
92
92
|
};
|
|
93
|
-
|
|
93
|
+
h && (i.Authorization = `Bearer ${h}`);
|
|
94
94
|
const a = await fetch(t, {
|
|
95
95
|
method: e,
|
|
96
96
|
headers: i,
|
|
@@ -119,11 +119,11 @@ class f {
|
|
|
119
119
|
case 404:
|
|
120
120
|
throw new k(r, o);
|
|
121
121
|
default:
|
|
122
|
-
throw new
|
|
122
|
+
throw new c(r, e.status, o);
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
const
|
|
126
|
+
const T = 6e4;
|
|
127
127
|
class y {
|
|
128
128
|
constructor(e) {
|
|
129
129
|
n(this, "storage");
|
|
@@ -139,7 +139,7 @@ class y {
|
|
|
139
139
|
*/
|
|
140
140
|
async getAccessToken() {
|
|
141
141
|
const e = await this.storage.getTokens();
|
|
142
|
-
return e ? e.expiresAt - Date.now() <
|
|
142
|
+
return e ? e.expiresAt - Date.now() < T ? (await this.performRefresh(e.refreshToken))?.accessToken ?? null : e.accessToken : null;
|
|
143
143
|
}
|
|
144
144
|
/**
|
|
145
145
|
* Called by `HttpClient` when a 401 is received.
|
|
@@ -171,7 +171,7 @@ class y {
|
|
|
171
171
|
}), this.refreshPromise);
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
|
-
class
|
|
174
|
+
class m {
|
|
175
175
|
constructor() {
|
|
176
176
|
n(this, "tokens", null);
|
|
177
177
|
}
|
|
@@ -188,7 +188,7 @@ class E {
|
|
|
188
188
|
function l(s) {
|
|
189
189
|
return new Date(s).getTime();
|
|
190
190
|
}
|
|
191
|
-
class
|
|
191
|
+
class E {
|
|
192
192
|
/** @internal */
|
|
193
193
|
constructor(e, t) {
|
|
194
194
|
this.http = e, this.tokenManager = t;
|
|
@@ -235,6 +235,29 @@ class m {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
class A {
|
|
238
|
+
/** @internal */
|
|
239
|
+
constructor(e) {
|
|
240
|
+
this.http = e;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Returns platform-level public configuration.
|
|
244
|
+
* No authentication required — safe to call before the user has signed in.
|
|
245
|
+
*
|
|
246
|
+
* Use `google_client_id` to initialise the Google Identity Services library
|
|
247
|
+
* on the storefront login page without requiring a prior authenticated call.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* const lw = init({ apiKey: 'your_api_key' })
|
|
252
|
+
* const { google_client_id } = await lw.config.getPublic()
|
|
253
|
+
* google.accounts.id.initialize({ client_id: google_client_id, callback })
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
async getPublic() {
|
|
257
|
+
return this.http.get("/v1/config/public");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
class x {
|
|
238
261
|
constructor(e) {
|
|
239
262
|
this.http = e;
|
|
240
263
|
}
|
|
@@ -254,7 +277,7 @@ class A {
|
|
|
254
277
|
);
|
|
255
278
|
}
|
|
256
279
|
}
|
|
257
|
-
class
|
|
280
|
+
class U {
|
|
258
281
|
constructor(e) {
|
|
259
282
|
this.http = e;
|
|
260
283
|
}
|
|
@@ -285,7 +308,7 @@ class x {
|
|
|
285
308
|
return this.http.get(`/v1/purchases/${e}`);
|
|
286
309
|
}
|
|
287
310
|
}
|
|
288
|
-
class
|
|
311
|
+
class b {
|
|
289
312
|
constructor(e) {
|
|
290
313
|
this.http = e;
|
|
291
314
|
}
|
|
@@ -324,7 +347,7 @@ class U {
|
|
|
324
347
|
return this.http.get(`/v1/wallet/payment-status/${e}`);
|
|
325
348
|
}
|
|
326
349
|
}
|
|
327
|
-
class
|
|
350
|
+
class v {
|
|
328
351
|
constructor(e) {
|
|
329
352
|
this.http = e;
|
|
330
353
|
}
|
|
@@ -344,13 +367,13 @@ class S {
|
|
|
344
367
|
}
|
|
345
368
|
}
|
|
346
369
|
function O(s) {
|
|
347
|
-
const e = s.baseUrl ?? "https://api.ledewire.com", t = s.storage ?? new
|
|
370
|
+
const e = s.baseUrl ?? "https://api.ledewire.com", t = s.storage ?? new m(), r = new y({
|
|
348
371
|
storage: t,
|
|
349
|
-
refreshFn: async (
|
|
372
|
+
refreshFn: async (h) => {
|
|
350
373
|
const i = await fetch(`${e}/v1/auth/token/refresh`, {
|
|
351
374
|
method: "POST",
|
|
352
375
|
headers: { "Content-Type": "application/json" },
|
|
353
|
-
body: JSON.stringify({ refresh_token:
|
|
376
|
+
body: JSON.stringify({ refresh_token: h })
|
|
354
377
|
});
|
|
355
378
|
if (!i.ok) throw new Error("Token refresh failed");
|
|
356
379
|
const a = await i.json();
|
|
@@ -366,11 +389,13 @@ function O(s) {
|
|
|
366
389
|
getAccessToken: () => r.getAccessToken(),
|
|
367
390
|
onUnauthorized: () => r.handleUnauthorized()
|
|
368
391
|
});
|
|
369
|
-
return new
|
|
392
|
+
return new S(o, r, s);
|
|
370
393
|
}
|
|
371
|
-
class
|
|
394
|
+
class S {
|
|
372
395
|
/** @internal */
|
|
373
396
|
constructor(e, t, r) {
|
|
397
|
+
/** Platform-level public configuration (no auth required) */
|
|
398
|
+
n(this, "config");
|
|
374
399
|
/** Buyer authentication: email/password signup/login, Google, password reset */
|
|
375
400
|
n(this, "auth");
|
|
376
401
|
/** Checkout state machine: determines next action for a piece of content */
|
|
@@ -381,11 +406,11 @@ class b {
|
|
|
381
406
|
n(this, "purchases");
|
|
382
407
|
/** Public content with per-user access information */
|
|
383
408
|
n(this, "content");
|
|
384
|
-
this._http = e, this._tokenManager = t, this._config = r, this.auth = new
|
|
409
|
+
this._http = e, this._tokenManager = t, this._config = r, this.config = new A(e), this.auth = new E(e, t), this.checkout = new v(e), this.wallet = new b(e), this.purchases = new U(e), this.content = new x(e);
|
|
385
410
|
}
|
|
386
411
|
}
|
|
387
|
-
const
|
|
388
|
-
function _(s =
|
|
412
|
+
const P = "lw:tokens";
|
|
413
|
+
function _(s = P) {
|
|
389
414
|
return {
|
|
390
415
|
getTokens() {
|
|
391
416
|
const e = localStorage.getItem(s);
|
|
@@ -407,10 +432,10 @@ function _(s = v) {
|
|
|
407
432
|
export {
|
|
408
433
|
u as AuthError,
|
|
409
434
|
d as ForbiddenError,
|
|
410
|
-
|
|
411
|
-
|
|
435
|
+
c as LedewireError,
|
|
436
|
+
m as MemoryTokenStorage,
|
|
412
437
|
k as NotFoundError,
|
|
413
|
-
|
|
438
|
+
N as PurchaseError,
|
|
414
439
|
O as init,
|
|
415
440
|
_ as localStorageAdapter,
|
|
416
441
|
l as parseExpiresAt
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../core/src/errors.ts","../../core/src/http-client.ts","../../core/src/token-manager.ts","../src/resources/auth.ts","../src/resources/content.ts","../src/resources/purchases.ts","../src/resources/wallet.ts","../src/resources/checkout.ts","../src/client.ts","../src/local-storage-adapter.ts"],"sourcesContent":["/**\n * Base error class for all LedeWire SDK errors.\n * All errors thrown by the SDK are instances of this class,\n * making it safe to use `err instanceof LedewireError` as a type guard.\n *\n * @example\n * ```ts\n * try {\n * await client.purchases.create({ contentId: '...' })\n * } catch (err) {\n * if (err instanceof LedewireError) {\n * console.error(err.statusCode, err.message)\n * }\n * }\n * ```\n */\nexport class LedewireError extends Error {\n /** HTTP status code returned by the API (e.g. 400, 401, 404, 422). */\n public readonly statusCode: number\n\n /** Machine-readable error code from the API error body, if present. */\n public readonly code: number | undefined\n\n constructor(message: string, statusCode: number, code?: number) {\n super(message)\n this.name = 'LedewireError'\n this.statusCode = statusCode\n this.code = code\n // Restore prototype chain (required for extending built-in Error in TS)\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\n/**\n * Thrown when the request lacks valid authentication credentials,\n * or when a token refresh fails and re-authentication is required.\n */\nexport class AuthError extends LedewireError {\n constructor(message: string, code?: number) {\n super(message, 401, code)\n this.name = 'AuthError'\n }\n}\n\n/**\n * Thrown when the authenticated user does not have permission\n * to perform the requested operation.\n */\nexport class ForbiddenError extends LedewireError {\n constructor(message: string, code?: number) {\n super(message, 403, code)\n this.name = 'ForbiddenError'\n }\n}\n\n/**\n * Thrown when the requested resource does not exist.\n */\nexport class NotFoundError extends LedewireError {\n constructor(message: string, code?: number) {\n super(message, 404, code)\n this.name = 'NotFoundError'\n }\n}\n\n/**\n * Thrown when the purchase cannot be completed due to a validation\n * failure, such as a price mismatch or a duplicate purchase.\n */\nexport class PurchaseError extends LedewireError {\n constructor(message: string, statusCode: number, code?: number) {\n super(message, statusCode, code)\n this.name = 'PurchaseError'\n }\n}\n","import type { components } from './api.gen.js'\nimport { AuthError, ForbiddenError, LedewireError, NotFoundError } from './errors.js'\n\n/** @internal */\ntype ApiErrorBody = components['schemas']['ErrorResponse']\n\n/**\n * Configuration options for `HttpClient`.\n */\nexport interface HttpClientConfig {\n /** API base URL. Defaults to the production API. */\n baseUrl?: string\n /**\n * Returns the current access token (or null) before each request.\n * If null, requests are sent without an Authorization header.\n */\n getAccessToken?: () => string | null | Promise<string | null>\n /**\n * Called when a 401 is received on the first attempt.\n * Should refresh and return the new access token, or null to propagate\n * an `AuthError` to the caller.\n */\n onUnauthorized?: () => string | null | Promise<string | null>\n}\n\nconst DEFAULT_BASE_URL = 'https://api.ledewire.com'\n\n/**\n * Core HTTP client used by all SDK packages.\n *\n * Features:\n * - Injects `Authorization: Bearer <token>` headers automatically\n * - Maps HTTP error responses to typed `LedewireError` subclasses\n * - On receiving a 401, calls `onUnauthorized` once and retries the request\n *\n * This is an internal class - consumers should use the\n * package-level client factories (`init` / `createClient`) instead.\n */\nexport class HttpClient {\n private readonly baseUrl: string\n private readonly getAccessToken: NonNullable<HttpClientConfig['getAccessToken']>\n private readonly onUnauthorized: NonNullable<HttpClientConfig['onUnauthorized']>\n\n constructor(config: HttpClientConfig = {}) {\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '')\n this.getAccessToken = config.getAccessToken ?? (() => null)\n this.onUnauthorized = config.onUnauthorized ?? (() => null)\n }\n\n /**\n * GET request with optional query parameters.\n * @param path - API path (e.g. `/v1/wallet/balance`)\n * @param params - Query string parameters\n */\n async get<T>(path: string, params?: Record<string, string>): Promise<T> {\n return this.request<T>('GET', this.buildUrl(path, params))\n }\n\n /**\n * POST request.\n * @param path - API path\n * @param body - Request body (JSON-serialized)\n */\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('POST', this.buildUrl(path), body)\n }\n\n /**\n * PUT request.\n * @param path - API path\n * @param body - Request body (JSON-serialized)\n */\n async put<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PUT', this.buildUrl(path), body)\n }\n\n /**\n * PATCH request.\n * @param path - API path\n * @param body - Partial update body (JSON-serialized)\n */\n async patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PATCH', this.buildUrl(path), body)\n }\n\n /**\n * DELETE request.\n * @param path - API path\n */\n async delete<T = void>(path: string): Promise<T> {\n return this.request<T>('DELETE', this.buildUrl(path))\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private buildUrl(path: string, params?: Record<string, string>): string {\n const url = `${this.baseUrl}${path}`\n if (!params || Object.keys(params).length === 0) return url\n return `${url}?${new URLSearchParams(params).toString()}`\n }\n\n private async request<T>(\n method: string,\n url: string,\n body?: unknown,\n isRetry = false,\n ): Promise<T> {\n const token = await this.getAccessToken()\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n }\n if (token) headers['Authorization'] = `Bearer ${token}`\n\n const response = await fetch(url, {\n method,\n headers,\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n if (response.status === 401 && !isRetry) {\n const newToken = await this.onUnauthorized()\n if (newToken) {\n return this.request<T>(method, url, body, true)\n }\n throw new AuthError('Session expired. Please re-authenticate.')\n }\n\n if (!response.ok) {\n await this.throwApiError(response)\n }\n\n if (response.status === 204) {\n return undefined as T\n }\n\n return response.json() as Promise<T>\n }\n\n private async throwApiError(response: Response): Promise<never> {\n let body: ApiErrorBody | undefined\n try {\n body = (await response.json()) as ApiErrorBody\n } catch {\n // Non-JSON body - fall through to use statusText\n }\n\n const message = body?.error.message ?? response.statusText\n const code = body?.error.code\n\n switch (response.status) {\n case 401:\n throw new AuthError(message, code)\n case 403:\n throw new ForbiddenError(message, code)\n case 404:\n throw new NotFoundError(message, code)\n default:\n throw new LedewireError(message, response.status, code)\n }\n }\n}\n","import type { StoredTokens, TokenStorage } from './types.js'\nimport { AuthError } from './errors.js'\n\n/** How many milliseconds before expiry to proactively refresh the token. */\nconst REFRESH_THRESHOLD_MS = 60_000 // 60 seconds\n\n/**\n * Options for constructing a `TokenManager`.\n */\nexport interface TokenManagerOptions {\n storage: TokenStorage\n /**\n * Exchanges a refresh token for a new token pair.\n * Implemented by each package's client factory using the\n * `/v1/auth/token/refresh` endpoint.\n */\n refreshFn: (refreshToken: string) => Promise<StoredTokens>\n /**\n * Called after a successful token refresh.\n * Use in server environments to persist the new tokens to a database or cache.\n *\n * @example\n * ```ts\n * onTokenRefreshed: async (tokens) => {\n * await redis.set('session:tokens', JSON.stringify(tokens))\n * }\n * ```\n */\n onTokenRefreshed?: (tokens: StoredTokens) => void | Promise<void>\n /**\n * Called when a refresh attempt fails and the user must re-authenticate.\n * Typically used to redirect to login or emit a UI event.\n *\n * @example\n * ```ts\n * onAuthExpired: () => { window.location.href = '/login' }\n * ```\n */\n onAuthExpired?: () => void | Promise<void>\n}\n\n/**\n * Manages the JWT token lifecycle transparently.\n *\n * - Proactively refreshes the access token when it expires within 60 seconds\n * - Handles reactive refresh on 401 responses from the HTTP client\n * - Deduplicates concurrent refresh calls (only one in-flight refresh at a time)\n * - Fires `onAuthExpired` when refresh fails so the UI can prompt re-login\n *\n * This is an internal class - use the package-level client factories instead.\n */\nexport class TokenManager {\n private readonly storage: TokenStorage\n private readonly refreshFn: TokenManagerOptions['refreshFn']\n private readonly onTokenRefreshed: TokenManagerOptions['onTokenRefreshed']\n private readonly onAuthExpired: TokenManagerOptions['onAuthExpired']\n private refreshPromise: Promise<StoredTokens | null> | null = null\n\n constructor(options: TokenManagerOptions) {\n this.storage = options.storage\n this.refreshFn = options.refreshFn\n this.onTokenRefreshed = options.onTokenRefreshed\n this.onAuthExpired = options.onAuthExpired\n }\n\n /**\n * Returns a valid access token, refreshing proactively if needed.\n * Returns `null` if no tokens are stored (user not authenticated).\n */\n async getAccessToken(): Promise<string | null> {\n const stored = await this.storage.getTokens()\n if (!stored) return null\n\n const expiresInMs = stored.expiresAt - Date.now()\n if (expiresInMs < REFRESH_THRESHOLD_MS) {\n const refreshed = await this.performRefresh(stored.refreshToken)\n return refreshed?.accessToken ?? null\n }\n\n return stored.accessToken\n }\n\n /**\n * Called by `HttpClient` when a 401 is received.\n * Attempts one refresh; returns the new access token or null.\n */\n async handleUnauthorized(): Promise<string | null> {\n const stored = await this.storage.getTokens()\n if (!stored) {\n await this.onAuthExpired?.()\n return null\n }\n const refreshed = await this.performRefresh(stored.refreshToken)\n if (!refreshed) await this.onAuthExpired?.()\n return refreshed?.accessToken ?? null\n }\n\n /** Store new tokens after a successful login or signup. */\n async setTokens(tokens: StoredTokens): Promise<void> {\n await this.storage.setTokens(tokens)\n }\n\n /** Clear all stored tokens (call on logout). */\n async clearTokens(): Promise<void> {\n await this.storage.clearTokens()\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private performRefresh(refreshToken: string): Promise<StoredTokens | null> {\n if (this.refreshPromise) return this.refreshPromise\n\n this.refreshPromise = this.refreshFn(refreshToken)\n .then(async (tokens) => {\n await this.storage.setTokens(tokens)\n await this.onTokenRefreshed?.(tokens)\n return tokens\n })\n .catch((_err: unknown) => {\n void this.storage.clearTokens()\n throw new AuthError('Token refresh failed. Please re-authenticate.')\n })\n .finally(() => {\n this.refreshPromise = null\n })\n\n return this.refreshPromise\n }\n}\n\n// ---------------------------------------------------------------------------\n// Built-in storage adapters\n// ---------------------------------------------------------------------------\n\n/**\n * In-memory token storage (default for both packages).\n * Tokens are cleared when the page unloads or the process exits.\n * This is the most secure default for browser environments.\n */\nexport class MemoryTokenStorage implements TokenStorage {\n private tokens: StoredTokens | null = null\n\n getTokens(): StoredTokens | null {\n return this.tokens\n }\n\n setTokens(tokens: StoredTokens): void {\n this.tokens = tokens\n }\n\n clearTokens(): void {\n this.tokens = null\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Converts an `expires_at` ISO 8601 string from an API auth response\n * into a Unix timestamp (milliseconds) for use in `StoredTokens.expiresAt`.\n *\n * @param expiresAt - ISO 8601 datetime string, e.g. `\"2026-01-01T12:00:00Z\"`\n * @returns Unix timestamp in milliseconds\n */\nexport function parseExpiresAt(expiresAt: string): number {\n return new Date(expiresAt).getTime()\n}\n","/**\n * Buyer authentication namespace for browser environments.\n *\n * Tokens are stored automatically in the `TokenManager` after every\n * successful call — callers never need to manage tokens directly.\n *\n * @module\n */\nimport { parseExpiresAt } from '@ledewire/core'\nimport type { HttpClient, TokenManager } from '@ledewire/core'\nimport type {\n AuthLoginEmailRequest,\n AuthLoginOAuthRequest,\n AuthSignupRequest,\n AuthenticationResponse,\n} from '@ledewire/core'\n\n/**\n * Buyer authentication: signup, email/password login, Google OAuth.\n *\n * Obtain via `lw.auth` — do not construct directly.\n *\n * @example\n * ```ts\n * const lw = Ledewire.init({ apiKey: 'your_api_key' })\n * await lw.auth.loginWithEmail({ email: 'u@example.com', password: 'pw' })\n * ```\n */\nexport class BrowserAuthNamespace {\n /** @internal */\n constructor(\n private readonly http: HttpClient,\n private readonly tokenManager: TokenManager,\n ) {}\n\n /**\n * Register a new buyer account with email and password.\n * Tokens are stored automatically after successful signup.\n *\n * @param body - Signup credentials and display name.\n * @returns The authentication token response.\n */\n async signup(body: AuthSignupRequest): Promise<AuthenticationResponse> {\n const res = await this.http.post<AuthenticationResponse>('/v1/auth/signup', body)\n await this.storeTokens(res)\n return res\n }\n\n /**\n * Log in with email and password.\n * Tokens are stored automatically after successful login.\n *\n * @param body - Email and password credentials.\n * @returns The authentication token response.\n */\n async loginWithEmail(body: AuthLoginEmailRequest): Promise<AuthenticationResponse> {\n const res = await this.http.post<AuthenticationResponse>('/v1/auth/login/email', body)\n await this.storeTokens(res)\n return res\n }\n\n /**\n * Log in with a Google ID token obtained from the Google OAuth flow.\n * Tokens are stored automatically after successful login.\n *\n * @param body - The Google ID token.\n * @returns The authentication token response.\n */\n async loginWithGoogle(body: AuthLoginOAuthRequest): Promise<AuthenticationResponse> {\n const res = await this.http.post<AuthenticationResponse>('/v1/auth/login/google', body)\n await this.storeTokens(res)\n return res\n }\n\n private async storeTokens(res: AuthenticationResponse): Promise<void> {\n await this.tokenManager.setTokens({\n accessToken: res.access_token,\n refreshToken: res.refresh_token,\n expiresAt: parseExpiresAt(res.expires_at),\n })\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type { ContentWithAccessResponse } from '@ledewire/core'\n\n/**\n * Buyer content namespace — fetch content with real-time access information.\n *\n * Obtain via `lw.content` — do not construct directly.\n *\n * @example\n * ```ts\n * const { content, access } = await lw.content.getWithAccess('content-id')\n * if (access.has_access) showFullContent(content)\n * ```\n */\nexport class BrowserContentNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns a content item together with access information for the\n * authenticated buyer (or a generic access response when unauthenticated).\n *\n * @param id - The content ID.\n * @param userId - Optional user ID to check a specific user's access status.\n * @returns The content item with access information.\n */\n async getWithAccess(id: string, userId?: string): Promise<ContentWithAccessResponse> {\n const params: Record<string, string> = {}\n if (userId !== undefined) {\n params['user_id'] = userId\n }\n return this.http.get<ContentWithAccessResponse>(\n `/v1/content/${id}/with-access`,\n Object.keys(params).length > 0 ? params : undefined,\n )\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type { PurchaseCreateRequest, PurchaseResponse } from '@ledewire/core'\n\n/**\n * Buyer purchases namespace — create and retrieve content purchases.\n *\n * Obtain via `lw.purchases` — do not construct directly.\n *\n * @example\n * ```ts\n * await lw.purchases.create({ content_id: 'content-id' })\n * const all = await lw.purchases.list()\n * ```\n */\nexport class BrowserPurchasesNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Completes a content purchase using the buyer's wallet balance.\n *\n * @param body - The content ID and expected price in cents.\n * @returns The completed purchase record.\n */\n async create(body: PurchaseCreateRequest): Promise<PurchaseResponse> {\n return this.http.post<PurchaseResponse>('/v1/purchases', body)\n }\n\n /**\n * Returns all purchases made by the authenticated buyer.\n *\n * @returns A list of purchase records, newest first.\n */\n async list(): Promise<PurchaseResponse[]> {\n return this.http.get<PurchaseResponse[]>('/v1/purchases')\n }\n\n /**\n * Returns a single purchase by ID.\n *\n * @param id - The purchase ID.\n * @returns The purchase record.\n */\n async get(id: string): Promise<PurchaseResponse> {\n return this.http.get<PurchaseResponse>(`/v1/purchases/${id}`)\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type {\n WalletBalanceResponse,\n WalletPaymentSessionRequest,\n WalletPaymentSessionResponse,\n WalletPaymentStatusResponse,\n WalletTransactionItem,\n} from '@ledewire/core'\n\n/**\n * Buyer wallet namespace — balance, transactions, and payment session management.\n *\n * Obtain via `lw.wallet` — do not construct directly.\n *\n * @example\n * ```ts\n * const { balance_cents } = await lw.wallet.balance()\n * const session = await lw.wallet.createPaymentSession({ amount_cents: 500 })\n * ```\n */\nexport class BrowserWalletNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns the authenticated buyer's current wallet balance.\n *\n * @returns The current wallet balance in cents.\n */\n async balance(): Promise<WalletBalanceResponse> {\n return this.http.get<WalletBalanceResponse>('/v1/wallet/balance')\n }\n\n /**\n * Returns the authenticated buyer's wallet transaction history, newest first.\n *\n * @returns A list of completed wallet transaction entries.\n */\n async transactions(): Promise<WalletTransactionItem[]> {\n return this.http.get<WalletTransactionItem[]>('/v1/wallet/transactions')\n }\n\n /**\n * Creates a payment session for funding the buyer's wallet.\n *\n * @param body - The amount and currency to fund.\n * @returns Payment session details for use with the payment provider widget.\n */\n async createPaymentSession(\n body: WalletPaymentSessionRequest,\n ): Promise<WalletPaymentSessionResponse> {\n return this.http.post<WalletPaymentSessionResponse>('/v1/wallet/payment-session', body)\n }\n\n /**\n * Polls the status of a wallet funding payment session.\n *\n * @param sessionId - The session ID returned by `createPaymentSession`.\n * @returns The current payment status.\n */\n async getPaymentStatus(sessionId: string): Promise<WalletPaymentStatusResponse> {\n return this.http.get<WalletPaymentStatusResponse>(`/v1/wallet/payment-status/${sessionId}`)\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type { CheckoutStateResponse } from '@ledewire/core'\n\n/**\n * Checkout namespace — buyer checkout state for a specific content item.\n *\n * Obtain via `lw.checkout` — do not construct directly.\n *\n * @example\n * ```ts\n * const state = await lw.checkout.state('content-id')\n * // state.checkout_state.next_required_action:\n * // 'authenticate' | 'fund_wallet' | 'purchase' | 'view_content'\n * ```\n */\nexport class CheckoutNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns the authenticated buyer's checkout readiness for a given piece of\n * content. Indicates whether the buyer needs to authenticate, fund their\n * wallet, or purchase before accessing the content.\n *\n * Works without authentication — returns anonymous checkout state when the\n * buyer is not logged in.\n *\n * @param contentId - The content item ID.\n * @returns The checkout state, including what action is required next.\n */\n async state(contentId: string): Promise<CheckoutStateResponse> {\n return this.http.get<CheckoutStateResponse>(`/v1/checkout/state/${contentId}`)\n }\n}\n","import { HttpClient, MemoryTokenStorage, TokenManager, parseExpiresAt } from '@ledewire/core'\nimport type { TokenStorage } from '@ledewire/core'\nimport { BrowserAuthNamespace } from './resources/auth.js'\nimport { BrowserContentNamespace } from './resources/content.js'\nimport { BrowserPurchasesNamespace } from './resources/purchases.js'\nimport { BrowserWalletNamespace } from './resources/wallet.js'\nimport { CheckoutNamespace } from './resources/checkout.js'\n\n/**\n * Configuration options for the LedeWire browser client.\n */\nexport interface BrowserClientConfig {\n /**\n * LedeWire API key that identifies the store.\n * Obtained from the LedeWire merchant dashboard.\n */\n apiKey: string\n /**\n * Override the API base URL.\n * Defaults to `https://api.ledewire.com`.\n */\n baseUrl?: string\n /**\n * Token storage adapter.\n * Defaults to {@link MemoryTokenStorage} (in-memory, most secure).\n *\n * To persist sessions across page reloads, use the built-in\n * `localStorageAdapter`:\n * ```ts\n * import { init, localStorageAdapter } from '@ledewire/browser'\n * const lw = init({ apiKey: '...', storage: localStorageAdapter() })\n * ```\n */\n storage?: TokenStorage\n /**\n * Called when the user's session expires and cannot be refreshed.\n * Use this to show a re-authentication prompt.\n *\n * @example\n * ```ts\n * onAuthExpired: () => showLoginModal()\n * ```\n */\n onAuthExpired?: () => void\n}\n\n/**\n * Initialises a LedeWire browser client.\n * This is the primary entry point for the browser SDK.\n *\n * @example CDN usage:\n * ```html\n * <script src=\"https://cdn.jsdelivr.net/npm/@ledewire/browser@1/dist/ledewire.min.js\"></script>\n * <script>\n * const lw = Ledewire.init({ apiKey: 'your_api_key' })\n * const state = await lw.checkout.state('content-id')\n * </script>\n * ```\n *\n * @example npm usage:\n * ```ts\n * import { init } from '@ledewire/browser'\n * const lw = init({ apiKey: 'your_api_key' })\n * ```\n */\nexport function init(config: BrowserClientConfig): BrowserClient {\n const baseUrl = config.baseUrl ?? 'https://api.ledewire.com'\n const storage = config.storage ?? new MemoryTokenStorage()\n\n const tokenManager = new TokenManager({\n storage,\n refreshFn: async (refreshToken) => {\n const res = await fetch(`${baseUrl}/v1/auth/token/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refresh_token: refreshToken }),\n })\n if (!res.ok) throw new Error('Token refresh failed')\n const data = (await res.json()) as {\n access_token: string\n refresh_token: string\n expires_at: string\n }\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: parseExpiresAt(data.expires_at),\n }\n },\n ...(config.onAuthExpired !== undefined && { onAuthExpired: config.onAuthExpired }),\n })\n\n const http = new HttpClient({\n baseUrl,\n getAccessToken: () => tokenManager.getAccessToken(),\n onUnauthorized: () => tokenManager.handleUnauthorized(),\n })\n\n return new BrowserClient(http, tokenManager, config)\n}\n\n/**\n * The LedeWire browser client.\n * Access buyer flows through the namespaced properties.\n *\n * @remarks\n * Instantiate with {@link init} rather than constructing directly.\n */\nexport class BrowserClient {\n /** Buyer authentication: email/password signup/login, Google, password reset */\n readonly auth: BrowserAuthNamespace\n\n /** Checkout state machine: determines next action for a piece of content */\n readonly checkout: CheckoutNamespace\n\n /** Wallet: balance, fund via payment session, transaction history */\n readonly wallet: BrowserWalletNamespace\n\n /** Content purchases for the authenticated buyer */\n readonly purchases: BrowserPurchasesNamespace\n\n /** Public content with per-user access information */\n readonly content: BrowserContentNamespace\n\n /** @internal */\n constructor(\n public readonly _http: HttpClient,\n public readonly _tokenManager: TokenManager,\n public readonly _config: BrowserClientConfig,\n ) {\n this.auth = new BrowserAuthNamespace(_http, _tokenManager)\n this.checkout = new CheckoutNamespace(_http)\n this.wallet = new BrowserWalletNamespace(_http)\n this.purchases = new BrowserPurchasesNamespace(_http)\n this.content = new BrowserContentNamespace(_http)\n }\n}\n","import type { TokenStorage, StoredTokens } from '@ledewire/core'\n\nconst DEFAULT_STORAGE_KEY = 'lw:tokens'\n\n/**\n * A {@link TokenStorage} adapter that persists tokens in `localStorage`.\n *\n * Tokens survive page reloads and browser restarts, which is convenient\n * for sites where users expect persistent sessions.\n *\n * **Security note:** `localStorage` is accessible to any JavaScript on the\n * same origin. Only use this adapter on sites you fully control and trust.\n * The default in-memory storage is safer for high-security use cases.\n *\n * @param key - The `localStorage` key used to store tokens.\n * Defaults to `'lw:tokens'`. Override this if you have multiple\n * LedeWire integrations on the same origin.\n *\n * @example\n * ```ts\n * import { init, localStorageAdapter } from '@ledewire/browser'\n *\n * const lw = init({\n * apiKey: 'your_api_key',\n * storage: localStorageAdapter(),\n * })\n * ```\n */\nexport function localStorageAdapter(key = DEFAULT_STORAGE_KEY): TokenStorage {\n return {\n getTokens(): StoredTokens | null {\n const raw = localStorage.getItem(key)\n if (!raw) return null\n try {\n return JSON.parse(raw) as StoredTokens\n } catch {\n // Corrupted value - treat as empty\n return null\n }\n },\n setTokens(tokens: StoredTokens): void {\n localStorage.setItem(key, JSON.stringify(tokens))\n },\n clearTokens(): void {\n localStorage.removeItem(key)\n },\n }\n}\n"],"names":["LedewireError","message","statusCode","code","__publicField","AuthError","ForbiddenError","NotFoundError","PurchaseError","DEFAULT_BASE_URL","HttpClient","config","path","params","body","url","method","isRetry","token","headers","response","REFRESH_THRESHOLD_MS","TokenManager","options","stored","refreshed","tokens","refreshToken","_err","MemoryTokenStorage","parseExpiresAt","expiresAt","BrowserAuthNamespace","http","tokenManager","res","BrowserContentNamespace","id","userId","BrowserPurchasesNamespace","BrowserWalletNamespace","sessionId","CheckoutNamespace","contentId","init","baseUrl","storage","data","BrowserClient","_http","_tokenManager","_config","DEFAULT_STORAGE_KEY","localStorageAdapter","key","raw"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../core/src/errors.ts","../../core/src/http-client.ts","../../core/src/token-manager.ts","../src/resources/auth.ts","../src/resources/config.ts","../src/resources/content.ts","../src/resources/purchases.ts","../src/resources/wallet.ts","../src/resources/checkout.ts","../src/client.ts","../src/local-storage-adapter.ts"],"sourcesContent":["/**\n * Base error class for all LedeWire SDK errors.\n * All errors thrown by the SDK are instances of this class,\n * making it safe to use `err instanceof LedewireError` as a type guard.\n *\n * @example\n * ```ts\n * try {\n * await client.purchases.create({ contentId: '...' })\n * } catch (err) {\n * if (err instanceof LedewireError) {\n * console.error(err.statusCode, err.message)\n * }\n * }\n * ```\n */\nexport class LedewireError extends Error {\n /** HTTP status code returned by the API (e.g. 400, 401, 404, 422). */\n public readonly statusCode: number\n\n /** Machine-readable error code from the API error body, if present. */\n public readonly code: number | undefined\n\n constructor(message: string, statusCode: number, code?: number) {\n super(message)\n this.name = 'LedewireError'\n this.statusCode = statusCode\n this.code = code\n // Restore prototype chain (required for extending built-in Error in TS)\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\n/**\n * Thrown when the request lacks valid authentication credentials,\n * or when a token refresh fails and re-authentication is required.\n */\nexport class AuthError extends LedewireError {\n constructor(message: string, code?: number) {\n super(message, 401, code)\n this.name = 'AuthError'\n }\n}\n\n/**\n * Thrown when the authenticated user does not have permission\n * to perform the requested operation.\n */\nexport class ForbiddenError extends LedewireError {\n constructor(message: string, code?: number) {\n super(message, 403, code)\n this.name = 'ForbiddenError'\n }\n}\n\n/**\n * Thrown when the requested resource does not exist.\n */\nexport class NotFoundError extends LedewireError {\n constructor(message: string, code?: number) {\n super(message, 404, code)\n this.name = 'NotFoundError'\n }\n}\n\n/**\n * Thrown when the purchase cannot be completed due to a validation\n * failure, such as a price mismatch or a duplicate purchase.\n */\nexport class PurchaseError extends LedewireError {\n constructor(message: string, statusCode: number, code?: number) {\n super(message, statusCode, code)\n this.name = 'PurchaseError'\n }\n}\n","import type { components } from './api.gen.js'\nimport { AuthError, ForbiddenError, LedewireError, NotFoundError } from './errors.js'\n\n/** @internal */\ntype ApiErrorBody = components['schemas']['ErrorResponse']\n\n/**\n * Configuration options for `HttpClient`.\n */\nexport interface HttpClientConfig {\n /** API base URL. Defaults to the production API. */\n baseUrl?: string\n /**\n * Returns the current access token (or null) before each request.\n * If null, requests are sent without an Authorization header.\n */\n getAccessToken?: () => string | null | Promise<string | null>\n /**\n * Called when a 401 is received on the first attempt.\n * Should refresh and return the new access token, or null to propagate\n * an `AuthError` to the caller.\n */\n onUnauthorized?: () => string | null | Promise<string | null>\n}\n\nconst DEFAULT_BASE_URL = 'https://api.ledewire.com'\n\n/**\n * Core HTTP client used by all SDK packages.\n *\n * Features:\n * - Injects `Authorization: Bearer <token>` headers automatically\n * - Maps HTTP error responses to typed `LedewireError` subclasses\n * - On receiving a 401, calls `onUnauthorized` once and retries the request\n *\n * This is an internal class - consumers should use the\n * package-level client factories (`init` / `createClient`) instead.\n */\nexport class HttpClient {\n private readonly baseUrl: string\n private readonly getAccessToken: NonNullable<HttpClientConfig['getAccessToken']>\n private readonly onUnauthorized: NonNullable<HttpClientConfig['onUnauthorized']>\n\n constructor(config: HttpClientConfig = {}) {\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '')\n this.getAccessToken = config.getAccessToken ?? (() => null)\n this.onUnauthorized = config.onUnauthorized ?? (() => null)\n }\n\n /**\n * GET request with optional query parameters.\n * @param path - API path (e.g. `/v1/wallet/balance`)\n * @param params - Query string parameters\n */\n async get<T>(path: string, params?: Record<string, string>): Promise<T> {\n return this.request<T>('GET', this.buildUrl(path, params))\n }\n\n /**\n * POST request.\n * @param path - API path\n * @param body - Request body (JSON-serialized)\n */\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('POST', this.buildUrl(path), body)\n }\n\n /**\n * PUT request.\n * @param path - API path\n * @param body - Request body (JSON-serialized)\n */\n async put<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PUT', this.buildUrl(path), body)\n }\n\n /**\n * PATCH request.\n * @param path - API path\n * @param body - Partial update body (JSON-serialized)\n */\n async patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PATCH', this.buildUrl(path), body)\n }\n\n /**\n * DELETE request.\n * @param path - API path\n */\n async delete<T = void>(path: string): Promise<T> {\n return this.request<T>('DELETE', this.buildUrl(path))\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private buildUrl(path: string, params?: Record<string, string>): string {\n const url = `${this.baseUrl}${path}`\n if (!params || Object.keys(params).length === 0) return url\n return `${url}?${new URLSearchParams(params).toString()}`\n }\n\n private async request<T>(\n method: string,\n url: string,\n body?: unknown,\n isRetry = false,\n ): Promise<T> {\n const token = await this.getAccessToken()\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n }\n if (token) headers['Authorization'] = `Bearer ${token}`\n\n const response = await fetch(url, {\n method,\n headers,\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n if (response.status === 401 && !isRetry) {\n const newToken = await this.onUnauthorized()\n if (newToken) {\n return this.request<T>(method, url, body, true)\n }\n throw new AuthError('Session expired. Please re-authenticate.')\n }\n\n if (!response.ok) {\n await this.throwApiError(response)\n }\n\n if (response.status === 204) {\n return undefined as T\n }\n\n return response.json() as Promise<T>\n }\n\n private async throwApiError(response: Response): Promise<never> {\n let body: ApiErrorBody | undefined\n try {\n body = (await response.json()) as ApiErrorBody\n } catch {\n // Non-JSON body - fall through to use statusText\n }\n\n const message = body?.error.message ?? response.statusText\n const code = body?.error.code\n\n switch (response.status) {\n case 401:\n throw new AuthError(message, code)\n case 403:\n throw new ForbiddenError(message, code)\n case 404:\n throw new NotFoundError(message, code)\n default:\n throw new LedewireError(message, response.status, code)\n }\n }\n}\n","import type { StoredTokens, TokenStorage } from './types.js'\nimport { AuthError } from './errors.js'\n\n/** How many milliseconds before expiry to proactively refresh the token. */\nconst REFRESH_THRESHOLD_MS = 60_000 // 60 seconds\n\n/**\n * Options for constructing a `TokenManager`.\n */\nexport interface TokenManagerOptions {\n storage: TokenStorage\n /**\n * Exchanges a refresh token for a new token pair.\n * Implemented by each package's client factory using the\n * `/v1/auth/token/refresh` endpoint.\n */\n refreshFn: (refreshToken: string) => Promise<StoredTokens>\n /**\n * Called after a successful token refresh.\n * Use in server environments to persist the new tokens to a database or cache.\n *\n * @example\n * ```ts\n * onTokenRefreshed: async (tokens) => {\n * await redis.set('session:tokens', JSON.stringify(tokens))\n * }\n * ```\n */\n onTokenRefreshed?: (tokens: StoredTokens) => void | Promise<void>\n /**\n * Called when a refresh attempt fails and the user must re-authenticate.\n * Typically used to redirect to login or emit a UI event.\n *\n * @example\n * ```ts\n * onAuthExpired: () => { window.location.href = '/login' }\n * ```\n */\n onAuthExpired?: () => void | Promise<void>\n}\n\n/**\n * Manages the JWT token lifecycle transparently.\n *\n * - Proactively refreshes the access token when it expires within 60 seconds\n * - Handles reactive refresh on 401 responses from the HTTP client\n * - Deduplicates concurrent refresh calls (only one in-flight refresh at a time)\n * - Fires `onAuthExpired` when refresh fails so the UI can prompt re-login\n *\n * This is an internal class - use the package-level client factories instead.\n */\nexport class TokenManager {\n private readonly storage: TokenStorage\n private readonly refreshFn: TokenManagerOptions['refreshFn']\n private readonly onTokenRefreshed: TokenManagerOptions['onTokenRefreshed']\n private readonly onAuthExpired: TokenManagerOptions['onAuthExpired']\n private refreshPromise: Promise<StoredTokens | null> | null = null\n\n constructor(options: TokenManagerOptions) {\n this.storage = options.storage\n this.refreshFn = options.refreshFn\n this.onTokenRefreshed = options.onTokenRefreshed\n this.onAuthExpired = options.onAuthExpired\n }\n\n /**\n * Returns a valid access token, refreshing proactively if needed.\n * Returns `null` if no tokens are stored (user not authenticated).\n */\n async getAccessToken(): Promise<string | null> {\n const stored = await this.storage.getTokens()\n if (!stored) return null\n\n const expiresInMs = stored.expiresAt - Date.now()\n if (expiresInMs < REFRESH_THRESHOLD_MS) {\n const refreshed = await this.performRefresh(stored.refreshToken)\n return refreshed?.accessToken ?? null\n }\n\n return stored.accessToken\n }\n\n /**\n * Called by `HttpClient` when a 401 is received.\n * Attempts one refresh; returns the new access token or null.\n */\n async handleUnauthorized(): Promise<string | null> {\n const stored = await this.storage.getTokens()\n if (!stored) {\n await this.onAuthExpired?.()\n return null\n }\n const refreshed = await this.performRefresh(stored.refreshToken)\n if (!refreshed) await this.onAuthExpired?.()\n return refreshed?.accessToken ?? null\n }\n\n /** Store new tokens after a successful login or signup. */\n async setTokens(tokens: StoredTokens): Promise<void> {\n await this.storage.setTokens(tokens)\n }\n\n /** Clear all stored tokens (call on logout). */\n async clearTokens(): Promise<void> {\n await this.storage.clearTokens()\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private performRefresh(refreshToken: string): Promise<StoredTokens | null> {\n if (this.refreshPromise) return this.refreshPromise\n\n this.refreshPromise = this.refreshFn(refreshToken)\n .then(async (tokens) => {\n await this.storage.setTokens(tokens)\n await this.onTokenRefreshed?.(tokens)\n return tokens\n })\n .catch((_err: unknown) => {\n void this.storage.clearTokens()\n throw new AuthError('Token refresh failed. Please re-authenticate.')\n })\n .finally(() => {\n this.refreshPromise = null\n })\n\n return this.refreshPromise\n }\n}\n\n// ---------------------------------------------------------------------------\n// Built-in storage adapters\n// ---------------------------------------------------------------------------\n\n/**\n * In-memory token storage (default for both packages).\n * Tokens are cleared when the page unloads or the process exits.\n * This is the most secure default for browser environments.\n */\nexport class MemoryTokenStorage implements TokenStorage {\n private tokens: StoredTokens | null = null\n\n getTokens(): StoredTokens | null {\n return this.tokens\n }\n\n setTokens(tokens: StoredTokens): void {\n this.tokens = tokens\n }\n\n clearTokens(): void {\n this.tokens = null\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Converts an `expires_at` ISO 8601 string from an API auth response\n * into a Unix timestamp (milliseconds) for use in `StoredTokens.expiresAt`.\n *\n * @param expiresAt - ISO 8601 datetime string, e.g. `\"2026-01-01T12:00:00Z\"`\n * @returns Unix timestamp in milliseconds\n */\nexport function parseExpiresAt(expiresAt: string): number {\n return new Date(expiresAt).getTime()\n}\n","/**\n * Buyer authentication namespace for browser environments.\n *\n * Tokens are stored automatically in the `TokenManager` after every\n * successful call — callers never need to manage tokens directly.\n *\n * @module\n */\nimport { parseExpiresAt } from '@ledewire/core'\nimport type { HttpClient, TokenManager } from '@ledewire/core'\nimport type {\n AuthLoginEmailRequest,\n AuthLoginOAuthRequest,\n AuthSignupRequest,\n AuthenticationResponse,\n} from '@ledewire/core'\n\n/**\n * Buyer authentication: signup, email/password login, Google OAuth.\n *\n * Obtain via `lw.auth` — do not construct directly.\n *\n * @example\n * ```ts\n * const lw = Ledewire.init({ apiKey: 'your_api_key' })\n * await lw.auth.loginWithEmail({ email: 'u@example.com', password: 'pw' })\n * ```\n */\nexport class BrowserAuthNamespace {\n /** @internal */\n constructor(\n private readonly http: HttpClient,\n private readonly tokenManager: TokenManager,\n ) {}\n\n /**\n * Register a new buyer account with email and password.\n * Tokens are stored automatically after successful signup.\n *\n * @param body - Signup credentials and display name.\n * @returns The authentication token response.\n */\n async signup(body: AuthSignupRequest): Promise<AuthenticationResponse> {\n const res = await this.http.post<AuthenticationResponse>('/v1/auth/signup', body)\n await this.storeTokens(res)\n return res\n }\n\n /**\n * Log in with email and password.\n * Tokens are stored automatically after successful login.\n *\n * @param body - Email and password credentials.\n * @returns The authentication token response.\n */\n async loginWithEmail(body: AuthLoginEmailRequest): Promise<AuthenticationResponse> {\n const res = await this.http.post<AuthenticationResponse>('/v1/auth/login/email', body)\n await this.storeTokens(res)\n return res\n }\n\n /**\n * Log in with a Google ID token obtained from the Google OAuth flow.\n * Tokens are stored automatically after successful login.\n *\n * @param body - The Google ID token.\n * @returns The authentication token response.\n */\n async loginWithGoogle(body: AuthLoginOAuthRequest): Promise<AuthenticationResponse> {\n const res = await this.http.post<AuthenticationResponse>('/v1/auth/login/google', body)\n await this.storeTokens(res)\n return res\n }\n\n private async storeTokens(res: AuthenticationResponse): Promise<void> {\n await this.tokenManager.setTokens({\n accessToken: res.access_token,\n refreshToken: res.refresh_token,\n expiresAt: parseExpiresAt(res.expires_at),\n })\n }\n}\n","/**\n * Platform configuration namespace (browser).\n *\n * Provides access to public (unauthenticated) platform-level configuration.\n *\n * @module\n */\nimport type { HttpClient } from '@ledewire/core'\nimport type { PublicConfigResponse } from '@ledewire/core'\n\n/**\n * Platform-level configuration for browser clients.\n *\n * Obtain via `client.config` — do not construct directly.\n */\nexport class BrowserConfigNamespace {\n /** @internal */\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns platform-level public configuration.\n * No authentication required — safe to call before the user has signed in.\n *\n * Use `google_client_id` to initialise the Google Identity Services library\n * on the storefront login page without requiring a prior authenticated call.\n *\n * @example\n * ```ts\n * const lw = init({ apiKey: 'your_api_key' })\n * const { google_client_id } = await lw.config.getPublic()\n * google.accounts.id.initialize({ client_id: google_client_id, callback })\n * ```\n */\n async getPublic(): Promise<PublicConfigResponse> {\n return this.http.get<PublicConfigResponse>('/v1/config/public')\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type { ContentWithAccessResponse } from '@ledewire/core'\n\n/**\n * Buyer content namespace — fetch content with real-time access information.\n *\n * Obtain via `lw.content` — do not construct directly.\n *\n * @example\n * ```ts\n * const { content, access } = await lw.content.getWithAccess('content-id')\n * if (access.has_access) showFullContent(content)\n * ```\n */\nexport class BrowserContentNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns a content item together with access information for the\n * authenticated buyer (or a generic access response when unauthenticated).\n *\n * @param id - The content ID.\n * @param userId - Optional user ID to check a specific user's access status.\n * @returns The content item with access information.\n */\n async getWithAccess(id: string, userId?: string): Promise<ContentWithAccessResponse> {\n const params: Record<string, string> = {}\n if (userId !== undefined) {\n params['user_id'] = userId\n }\n return this.http.get<ContentWithAccessResponse>(\n `/v1/content/${id}/with-access`,\n Object.keys(params).length > 0 ? params : undefined,\n )\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type { PurchaseCreateRequest, PurchaseResponse } from '@ledewire/core'\n\n/**\n * Buyer purchases namespace — create and retrieve content purchases.\n *\n * Obtain via `lw.purchases` — do not construct directly.\n *\n * @example\n * ```ts\n * await lw.purchases.create({ content_id: 'content-id' })\n * const all = await lw.purchases.list()\n * ```\n */\nexport class BrowserPurchasesNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Completes a content purchase using the buyer's wallet balance.\n *\n * @param body - The content ID and expected price in cents.\n * @returns The completed purchase record.\n */\n async create(body: PurchaseCreateRequest): Promise<PurchaseResponse> {\n return this.http.post<PurchaseResponse>('/v1/purchases', body)\n }\n\n /**\n * Returns all purchases made by the authenticated buyer.\n *\n * @returns A list of purchase records, newest first.\n */\n async list(): Promise<PurchaseResponse[]> {\n return this.http.get<PurchaseResponse[]>('/v1/purchases')\n }\n\n /**\n * Returns a single purchase by ID.\n *\n * @param id - The purchase ID.\n * @returns The purchase record.\n */\n async get(id: string): Promise<PurchaseResponse> {\n return this.http.get<PurchaseResponse>(`/v1/purchases/${id}`)\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type {\n WalletBalanceResponse,\n WalletPaymentSessionRequest,\n WalletPaymentSessionResponse,\n WalletPaymentStatusResponse,\n WalletTransactionItem,\n} from '@ledewire/core'\n\n/**\n * Buyer wallet namespace — balance, transactions, and payment session management.\n *\n * Obtain via `lw.wallet` — do not construct directly.\n *\n * @example\n * ```ts\n * const { balance_cents } = await lw.wallet.balance()\n * const session = await lw.wallet.createPaymentSession({ amount_cents: 500 })\n * ```\n */\nexport class BrowserWalletNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns the authenticated buyer's current wallet balance.\n *\n * @returns The current wallet balance in cents.\n */\n async balance(): Promise<WalletBalanceResponse> {\n return this.http.get<WalletBalanceResponse>('/v1/wallet/balance')\n }\n\n /**\n * Returns the authenticated buyer's wallet transaction history, newest first.\n *\n * @returns A list of completed wallet transaction entries.\n */\n async transactions(): Promise<WalletTransactionItem[]> {\n return this.http.get<WalletTransactionItem[]>('/v1/wallet/transactions')\n }\n\n /**\n * Creates a payment session for funding the buyer's wallet.\n *\n * @param body - The amount and currency to fund.\n * @returns Payment session details for use with the payment provider widget.\n */\n async createPaymentSession(\n body: WalletPaymentSessionRequest,\n ): Promise<WalletPaymentSessionResponse> {\n return this.http.post<WalletPaymentSessionResponse>('/v1/wallet/payment-session', body)\n }\n\n /**\n * Polls the status of a wallet funding payment session.\n *\n * @param sessionId - The session ID returned by `createPaymentSession`.\n * @returns The current payment status.\n */\n async getPaymentStatus(sessionId: string): Promise<WalletPaymentStatusResponse> {\n return this.http.get<WalletPaymentStatusResponse>(`/v1/wallet/payment-status/${sessionId}`)\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type { CheckoutStateResponse } from '@ledewire/core'\n\n/**\n * Checkout namespace — buyer checkout state for a specific content item.\n *\n * Obtain via `lw.checkout` — do not construct directly.\n *\n * @example\n * ```ts\n * const state = await lw.checkout.state('content-id')\n * // state.checkout_state.next_required_action:\n * // 'authenticate' | 'fund_wallet' | 'purchase' | 'view_content'\n * ```\n */\nexport class CheckoutNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns the authenticated buyer's checkout readiness for a given piece of\n * content. Indicates whether the buyer needs to authenticate, fund their\n * wallet, or purchase before accessing the content.\n *\n * Works without authentication — returns anonymous checkout state when the\n * buyer is not logged in.\n *\n * @param contentId - The content item ID.\n * @returns The checkout state, including what action is required next.\n */\n async state(contentId: string): Promise<CheckoutStateResponse> {\n return this.http.get<CheckoutStateResponse>(`/v1/checkout/state/${contentId}`)\n }\n}\n","import { HttpClient, MemoryTokenStorage, TokenManager, parseExpiresAt } from '@ledewire/core'\nimport type { TokenStorage } from '@ledewire/core'\nimport { BrowserAuthNamespace } from './resources/auth.js'\nimport { BrowserConfigNamespace } from './resources/config.js'\nimport { BrowserContentNamespace } from './resources/content.js'\nimport { BrowserPurchasesNamespace } from './resources/purchases.js'\nimport { BrowserWalletNamespace } from './resources/wallet.js'\nimport { CheckoutNamespace } from './resources/checkout.js'\n\n/**\n * Configuration options for the LedeWire browser client.\n */\nexport interface BrowserClientConfig {\n /**\n * LedeWire API key that identifies the store.\n * Obtained from the LedeWire merchant dashboard.\n */\n apiKey: string\n /**\n * Override the API base URL.\n * Defaults to `https://api.ledewire.com`.\n */\n baseUrl?: string\n /**\n * Token storage adapter.\n * Defaults to {@link MemoryTokenStorage} (in-memory, most secure).\n *\n * To persist sessions across page reloads, use the built-in\n * `localStorageAdapter`:\n * ```ts\n * import { init, localStorageAdapter } from '@ledewire/browser'\n * const lw = init({ apiKey: '...', storage: localStorageAdapter() })\n * ```\n */\n storage?: TokenStorage\n /**\n * Called when the user's session expires and cannot be refreshed.\n * Use this to show a re-authentication prompt.\n *\n * @example\n * ```ts\n * onAuthExpired: () => showLoginModal()\n * ```\n */\n onAuthExpired?: () => void\n}\n\n/**\n * Initialises a LedeWire browser client.\n * This is the primary entry point for the browser SDK.\n *\n * @example CDN usage:\n * ```html\n * <script src=\"https://cdn.jsdelivr.net/npm/@ledewire/browser@1/dist/ledewire.min.js\"></script>\n * <script>\n * const lw = Ledewire.init({ apiKey: 'your_api_key' })\n * const state = await lw.checkout.state('content-id')\n * </script>\n * ```\n *\n * @example npm usage:\n * ```ts\n * import { init } from '@ledewire/browser'\n * const lw = init({ apiKey: 'your_api_key' })\n * ```\n */\nexport function init(config: BrowserClientConfig): BrowserClient {\n const baseUrl = config.baseUrl ?? 'https://api.ledewire.com'\n const storage = config.storage ?? new MemoryTokenStorage()\n\n const tokenManager = new TokenManager({\n storage,\n refreshFn: async (refreshToken) => {\n const res = await fetch(`${baseUrl}/v1/auth/token/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refresh_token: refreshToken }),\n })\n if (!res.ok) throw new Error('Token refresh failed')\n const data = (await res.json()) as {\n access_token: string\n refresh_token: string\n expires_at: string\n }\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: parseExpiresAt(data.expires_at),\n }\n },\n ...(config.onAuthExpired !== undefined && { onAuthExpired: config.onAuthExpired }),\n })\n\n const http = new HttpClient({\n baseUrl,\n getAccessToken: () => tokenManager.getAccessToken(),\n onUnauthorized: () => tokenManager.handleUnauthorized(),\n })\n\n return new BrowserClient(http, tokenManager, config)\n}\n\n/**\n * The LedeWire browser client.\n * Access buyer flows through the namespaced properties.\n *\n * @remarks\n * Instantiate with {@link init} rather than constructing directly.\n */\nexport class BrowserClient {\n /** Platform-level public configuration (no auth required) */\n readonly config: BrowserConfigNamespace\n\n /** Buyer authentication: email/password signup/login, Google, password reset */\n readonly auth: BrowserAuthNamespace\n\n /** Checkout state machine: determines next action for a piece of content */\n readonly checkout: CheckoutNamespace\n\n /** Wallet: balance, fund via payment session, transaction history */\n readonly wallet: BrowserWalletNamespace\n\n /** Content purchases for the authenticated buyer */\n readonly purchases: BrowserPurchasesNamespace\n\n /** Public content with per-user access information */\n readonly content: BrowserContentNamespace\n\n /** @internal */\n constructor(\n public readonly _http: HttpClient,\n public readonly _tokenManager: TokenManager,\n public readonly _config: BrowserClientConfig,\n ) {\n this.config = new BrowserConfigNamespace(_http)\n this.auth = new BrowserAuthNamespace(_http, _tokenManager)\n this.checkout = new CheckoutNamespace(_http)\n this.wallet = new BrowserWalletNamespace(_http)\n this.purchases = new BrowserPurchasesNamespace(_http)\n this.content = new BrowserContentNamespace(_http)\n }\n}\n","import type { TokenStorage, StoredTokens } from '@ledewire/core'\n\nconst DEFAULT_STORAGE_KEY = 'lw:tokens'\n\n/**\n * A {@link TokenStorage} adapter that persists tokens in `localStorage`.\n *\n * Tokens survive page reloads and browser restarts, which is convenient\n * for sites where users expect persistent sessions.\n *\n * **Security note:** `localStorage` is accessible to any JavaScript on the\n * same origin. Only use this adapter on sites you fully control and trust.\n * The default in-memory storage is safer for high-security use cases.\n *\n * @param key - The `localStorage` key used to store tokens.\n * Defaults to `'lw:tokens'`. Override this if you have multiple\n * LedeWire integrations on the same origin.\n *\n * @example\n * ```ts\n * import { init, localStorageAdapter } from '@ledewire/browser'\n *\n * const lw = init({\n * apiKey: 'your_api_key',\n * storage: localStorageAdapter(),\n * })\n * ```\n */\nexport function localStorageAdapter(key = DEFAULT_STORAGE_KEY): TokenStorage {\n return {\n getTokens(): StoredTokens | null {\n const raw = localStorage.getItem(key)\n if (!raw) return null\n try {\n return JSON.parse(raw) as StoredTokens\n } catch {\n // Corrupted value - treat as empty\n return null\n }\n },\n setTokens(tokens: StoredTokens): void {\n localStorage.setItem(key, JSON.stringify(tokens))\n },\n clearTokens(): void {\n localStorage.removeItem(key)\n },\n }\n}\n"],"names":["LedewireError","message","statusCode","code","__publicField","AuthError","ForbiddenError","NotFoundError","PurchaseError","DEFAULT_BASE_URL","HttpClient","config","path","params","body","url","method","isRetry","token","headers","response","REFRESH_THRESHOLD_MS","TokenManager","options","stored","refreshed","tokens","refreshToken","_err","MemoryTokenStorage","parseExpiresAt","expiresAt","BrowserAuthNamespace","http","tokenManager","res","BrowserConfigNamespace","BrowserContentNamespace","id","userId","BrowserPurchasesNamespace","BrowserWalletNamespace","sessionId","CheckoutNamespace","contentId","init","baseUrl","storage","data","BrowserClient","_http","_tokenManager","_config","DEFAULT_STORAGE_KEY","localStorageAdapter","key","raw"],"mappings":"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"}
|
package/dist/ledewire.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var Ledewire=(function(
|
|
1
|
+
var Ledewire=(function(o){"use strict";var O=Object.defineProperty;var R=(o,a,c)=>a in o?O(o,a,{enumerable:!0,configurable:!0,writable:!0,value:c}):o[a]=c;var n=(o,a,c)=>R(o,typeof a!="symbol"?a+"":a,c);class a extends Error{constructor(t,r,i){super(t);n(this,"statusCode");n(this,"code");this.name="LedewireError",this.statusCode=r,this.code=i,Object.setPrototypeOf(this,new.target.prototype)}}class c extends a{constructor(e,t){super(e,401,t),this.name="AuthError"}}class d extends a{constructor(e,t){super(e,403,t),this.name="ForbiddenError"}}class p extends a{constructor(e,t){super(e,404,t),this.name="NotFoundError"}}class k extends a{constructor(e,t,r){super(e,t,r),this.name="PurchaseError"}}const f="https://api.ledewire.com";class T{constructor(e={}){n(this,"baseUrl");n(this,"getAccessToken");n(this,"onUnauthorized");this.baseUrl=(e.baseUrl??f).replace(/\/$/,""),this.getAccessToken=e.getAccessToken??(()=>null),this.onUnauthorized=e.onUnauthorized??(()=>null)}async get(e,t){return this.request("GET",this.buildUrl(e,t))}async post(e,t){return this.request("POST",this.buildUrl(e),t)}async put(e,t){return this.request("PUT",this.buildUrl(e),t)}async patch(e,t){return this.request("PATCH",this.buildUrl(e),t)}async delete(e){return this.request("DELETE",this.buildUrl(e))}buildUrl(e,t){const r=`${this.baseUrl}${e}`;return!t||Object.keys(t).length===0?r:`${r}?${new URLSearchParams(t).toString()}`}async request(e,t,r,i=!1){const l=await this.getAccessToken(),u={"Content-Type":"application/json",Accept:"application/json"};l&&(u.Authorization=`Bearer ${l}`);const h=await fetch(t,{method:e,headers:u,...r!==void 0&&{body:JSON.stringify(r)}});if(h.status===401&&!i){if(await this.onUnauthorized())return this.request(e,t,r,!0);throw new c("Session expired. Please re-authenticate.")}if(h.ok||await this.throwApiError(h),h.status!==204)return h.json()}async throwApiError(e){let t;try{t=await e.json()}catch{}const r=t?.error.message??e.statusText,i=t?.error.code;switch(e.status){case 401:throw new c(r,i);case 403:throw new d(r,i);case 404:throw new p(r,i);default:throw new a(r,e.status,i)}}}const y=6e4;class E{constructor(e){n(this,"storage");n(this,"refreshFn");n(this,"onTokenRefreshed");n(this,"onAuthExpired");n(this,"refreshPromise",null);this.storage=e.storage,this.refreshFn=e.refreshFn,this.onTokenRefreshed=e.onTokenRefreshed,this.onAuthExpired=e.onAuthExpired}async getAccessToken(){const e=await this.storage.getTokens();return e?e.expiresAt-Date.now()<y?(await this.performRefresh(e.refreshToken))?.accessToken??null:e.accessToken:null}async handleUnauthorized(){const e=await this.storage.getTokens();if(!e)return await this.onAuthExpired?.(),null;const t=await this.performRefresh(e.refreshToken);return t||await this.onAuthExpired?.(),t?.accessToken??null}async setTokens(e){await this.storage.setTokens(e)}async clearTokens(){await this.storage.clearTokens()}performRefresh(e){return this.refreshPromise?this.refreshPromise:(this.refreshPromise=this.refreshFn(e).then(async t=>(await this.storage.setTokens(t),await this.onTokenRefreshed?.(t),t)).catch(t=>{throw this.storage.clearTokens(),new c("Token refresh failed. Please re-authenticate.")}).finally(()=>{this.refreshPromise=null}),this.refreshPromise)}}class g{constructor(){n(this,"tokens",null)}getTokens(){return this.tokens}setTokens(e){this.tokens=e}clearTokens(){this.tokens=null}}function w(s){return new Date(s).getTime()}class m{constructor(e,t){this.http=e,this.tokenManager=t}async signup(e){const t=await this.http.post("/v1/auth/signup",e);return await this.storeTokens(t),t}async loginWithEmail(e){const t=await this.http.post("/v1/auth/login/email",e);return await this.storeTokens(t),t}async loginWithGoogle(e){const t=await this.http.post("/v1/auth/login/google",e);return await this.storeTokens(t),t}async storeTokens(e){await this.tokenManager.setTokens({accessToken:e.access_token,refreshToken:e.refresh_token,expiresAt:w(e.expires_at)})}}class A{constructor(e){this.http=e}async getPublic(){return this.http.get("/v1/config/public")}}class b{constructor(e){this.http=e}async getWithAccess(e,t){const r={};return t!==void 0&&(r.user_id=t),this.http.get(`/v1/content/${e}/with-access`,Object.keys(r).length>0?r:void 0)}}class S{constructor(e){this.http=e}async create(e){return this.http.post("/v1/purchases",e)}async list(){return this.http.get("/v1/purchases")}async get(e){return this.http.get(`/v1/purchases/${e}`)}}class U{constructor(e){this.http=e}async balance(){return this.http.get("/v1/wallet/balance")}async transactions(){return this.http.get("/v1/wallet/transactions")}async createPaymentSession(e){return this.http.post("/v1/wallet/payment-session",e)}async getPaymentStatus(e){return this.http.get(`/v1/wallet/payment-status/${e}`)}}class v{constructor(e){this.http=e}async state(e){return this.http.get(`/v1/checkout/state/${e}`)}}function P(s){const e=s.baseUrl??"https://api.ledewire.com",t=s.storage??new g,r=new E({storage:t,refreshFn:async l=>{const u=await fetch(`${e}/v1/auth/token/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:l})});if(!u.ok)throw new Error("Token refresh failed");const h=await u.json();return{accessToken:h.access_token,refreshToken:h.refresh_token,expiresAt:w(h.expires_at)}},...s.onAuthExpired!==void 0&&{onAuthExpired:s.onAuthExpired}}),i=new T({baseUrl:e,getAccessToken:()=>r.getAccessToken(),onUnauthorized:()=>r.handleUnauthorized()});return new x(i,r,s)}class x{constructor(e,t,r){n(this,"config");n(this,"auth");n(this,"checkout");n(this,"wallet");n(this,"purchases");n(this,"content");this._http=e,this._tokenManager=t,this._config=r,this.config=new A(e),this.auth=new m(e,t),this.checkout=new v(e),this.wallet=new U(e),this.purchases=new S(e),this.content=new b(e)}}const F="lw:tokens";function N(s=F){return{getTokens(){const e=localStorage.getItem(s);if(!e)return null;try{return JSON.parse(e)}catch{return null}},setTokens(e){localStorage.setItem(s,JSON.stringify(e))},clearTokens(){localStorage.removeItem(s)}}}return o.AuthError=c,o.ForbiddenError=d,o.LedewireError=a,o.MemoryTokenStorage=g,o.NotFoundError=p,o.PurchaseError=k,o.init=P,o.localStorageAdapter=N,o.parseExpiresAt=w,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}),o})({});
|
|
2
2
|
//# sourceMappingURL=ledewire.min.js.map
|
package/dist/ledewire.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ledewire.min.js","sources":["../../core/src/errors.ts","../../core/src/http-client.ts","../../core/src/token-manager.ts","../src/resources/auth.ts","../src/resources/content.ts","../src/resources/purchases.ts","../src/resources/wallet.ts","../src/resources/checkout.ts","../src/client.ts","../src/local-storage-adapter.ts"],"sourcesContent":["/**\n * Base error class for all LedeWire SDK errors.\n * All errors thrown by the SDK are instances of this class,\n * making it safe to use `err instanceof LedewireError` as a type guard.\n *\n * @example\n * ```ts\n * try {\n * await client.purchases.create({ contentId: '...' })\n * } catch (err) {\n * if (err instanceof LedewireError) {\n * console.error(err.statusCode, err.message)\n * }\n * }\n * ```\n */\nexport class LedewireError extends Error {\n /** HTTP status code returned by the API (e.g. 400, 401, 404, 422). */\n public readonly statusCode: number\n\n /** Machine-readable error code from the API error body, if present. */\n public readonly code: number | undefined\n\n constructor(message: string, statusCode: number, code?: number) {\n super(message)\n this.name = 'LedewireError'\n this.statusCode = statusCode\n this.code = code\n // Restore prototype chain (required for extending built-in Error in TS)\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\n/**\n * Thrown when the request lacks valid authentication credentials,\n * or when a token refresh fails and re-authentication is required.\n */\nexport class AuthError extends LedewireError {\n constructor(message: string, code?: number) {\n super(message, 401, code)\n this.name = 'AuthError'\n }\n}\n\n/**\n * Thrown when the authenticated user does not have permission\n * to perform the requested operation.\n */\nexport class ForbiddenError extends LedewireError {\n constructor(message: string, code?: number) {\n super(message, 403, code)\n this.name = 'ForbiddenError'\n }\n}\n\n/**\n * Thrown when the requested resource does not exist.\n */\nexport class NotFoundError extends LedewireError {\n constructor(message: string, code?: number) {\n super(message, 404, code)\n this.name = 'NotFoundError'\n }\n}\n\n/**\n * Thrown when the purchase cannot be completed due to a validation\n * failure, such as a price mismatch or a duplicate purchase.\n */\nexport class PurchaseError extends LedewireError {\n constructor(message: string, statusCode: number, code?: number) {\n super(message, statusCode, code)\n this.name = 'PurchaseError'\n }\n}\n","import type { components } from './api.gen.js'\nimport { AuthError, ForbiddenError, LedewireError, NotFoundError } from './errors.js'\n\n/** @internal */\ntype ApiErrorBody = components['schemas']['ErrorResponse']\n\n/**\n * Configuration options for `HttpClient`.\n */\nexport interface HttpClientConfig {\n /** API base URL. Defaults to the production API. */\n baseUrl?: string\n /**\n * Returns the current access token (or null) before each request.\n * If null, requests are sent without an Authorization header.\n */\n getAccessToken?: () => string | null | Promise<string | null>\n /**\n * Called when a 401 is received on the first attempt.\n * Should refresh and return the new access token, or null to propagate\n * an `AuthError` to the caller.\n */\n onUnauthorized?: () => string | null | Promise<string | null>\n}\n\nconst DEFAULT_BASE_URL = 'https://api.ledewire.com'\n\n/**\n * Core HTTP client used by all SDK packages.\n *\n * Features:\n * - Injects `Authorization: Bearer <token>` headers automatically\n * - Maps HTTP error responses to typed `LedewireError` subclasses\n * - On receiving a 401, calls `onUnauthorized` once and retries the request\n *\n * This is an internal class - consumers should use the\n * package-level client factories (`init` / `createClient`) instead.\n */\nexport class HttpClient {\n private readonly baseUrl: string\n private readonly getAccessToken: NonNullable<HttpClientConfig['getAccessToken']>\n private readonly onUnauthorized: NonNullable<HttpClientConfig['onUnauthorized']>\n\n constructor(config: HttpClientConfig = {}) {\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '')\n this.getAccessToken = config.getAccessToken ?? (() => null)\n this.onUnauthorized = config.onUnauthorized ?? (() => null)\n }\n\n /**\n * GET request with optional query parameters.\n * @param path - API path (e.g. `/v1/wallet/balance`)\n * @param params - Query string parameters\n */\n async get<T>(path: string, params?: Record<string, string>): Promise<T> {\n return this.request<T>('GET', this.buildUrl(path, params))\n }\n\n /**\n * POST request.\n * @param path - API path\n * @param body - Request body (JSON-serialized)\n */\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('POST', this.buildUrl(path), body)\n }\n\n /**\n * PUT request.\n * @param path - API path\n * @param body - Request body (JSON-serialized)\n */\n async put<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PUT', this.buildUrl(path), body)\n }\n\n /**\n * PATCH request.\n * @param path - API path\n * @param body - Partial update body (JSON-serialized)\n */\n async patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PATCH', this.buildUrl(path), body)\n }\n\n /**\n * DELETE request.\n * @param path - API path\n */\n async delete<T = void>(path: string): Promise<T> {\n return this.request<T>('DELETE', this.buildUrl(path))\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private buildUrl(path: string, params?: Record<string, string>): string {\n const url = `${this.baseUrl}${path}`\n if (!params || Object.keys(params).length === 0) return url\n return `${url}?${new URLSearchParams(params).toString()}`\n }\n\n private async request<T>(\n method: string,\n url: string,\n body?: unknown,\n isRetry = false,\n ): Promise<T> {\n const token = await this.getAccessToken()\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n }\n if (token) headers['Authorization'] = `Bearer ${token}`\n\n const response = await fetch(url, {\n method,\n headers,\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n if (response.status === 401 && !isRetry) {\n const newToken = await this.onUnauthorized()\n if (newToken) {\n return this.request<T>(method, url, body, true)\n }\n throw new AuthError('Session expired. Please re-authenticate.')\n }\n\n if (!response.ok) {\n await this.throwApiError(response)\n }\n\n if (response.status === 204) {\n return undefined as T\n }\n\n return response.json() as Promise<T>\n }\n\n private async throwApiError(response: Response): Promise<never> {\n let body: ApiErrorBody | undefined\n try {\n body = (await response.json()) as ApiErrorBody\n } catch {\n // Non-JSON body - fall through to use statusText\n }\n\n const message = body?.error.message ?? response.statusText\n const code = body?.error.code\n\n switch (response.status) {\n case 401:\n throw new AuthError(message, code)\n case 403:\n throw new ForbiddenError(message, code)\n case 404:\n throw new NotFoundError(message, code)\n default:\n throw new LedewireError(message, response.status, code)\n }\n }\n}\n","import type { StoredTokens, TokenStorage } from './types.js'\nimport { AuthError } from './errors.js'\n\n/** How many milliseconds before expiry to proactively refresh the token. */\nconst REFRESH_THRESHOLD_MS = 60_000 // 60 seconds\n\n/**\n * Options for constructing a `TokenManager`.\n */\nexport interface TokenManagerOptions {\n storage: TokenStorage\n /**\n * Exchanges a refresh token for a new token pair.\n * Implemented by each package's client factory using the\n * `/v1/auth/token/refresh` endpoint.\n */\n refreshFn: (refreshToken: string) => Promise<StoredTokens>\n /**\n * Called after a successful token refresh.\n * Use in server environments to persist the new tokens to a database or cache.\n *\n * @example\n * ```ts\n * onTokenRefreshed: async (tokens) => {\n * await redis.set('session:tokens', JSON.stringify(tokens))\n * }\n * ```\n */\n onTokenRefreshed?: (tokens: StoredTokens) => void | Promise<void>\n /**\n * Called when a refresh attempt fails and the user must re-authenticate.\n * Typically used to redirect to login or emit a UI event.\n *\n * @example\n * ```ts\n * onAuthExpired: () => { window.location.href = '/login' }\n * ```\n */\n onAuthExpired?: () => void | Promise<void>\n}\n\n/**\n * Manages the JWT token lifecycle transparently.\n *\n * - Proactively refreshes the access token when it expires within 60 seconds\n * - Handles reactive refresh on 401 responses from the HTTP client\n * - Deduplicates concurrent refresh calls (only one in-flight refresh at a time)\n * - Fires `onAuthExpired` when refresh fails so the UI can prompt re-login\n *\n * This is an internal class - use the package-level client factories instead.\n */\nexport class TokenManager {\n private readonly storage: TokenStorage\n private readonly refreshFn: TokenManagerOptions['refreshFn']\n private readonly onTokenRefreshed: TokenManagerOptions['onTokenRefreshed']\n private readonly onAuthExpired: TokenManagerOptions['onAuthExpired']\n private refreshPromise: Promise<StoredTokens | null> | null = null\n\n constructor(options: TokenManagerOptions) {\n this.storage = options.storage\n this.refreshFn = options.refreshFn\n this.onTokenRefreshed = options.onTokenRefreshed\n this.onAuthExpired = options.onAuthExpired\n }\n\n /**\n * Returns a valid access token, refreshing proactively if needed.\n * Returns `null` if no tokens are stored (user not authenticated).\n */\n async getAccessToken(): Promise<string | null> {\n const stored = await this.storage.getTokens()\n if (!stored) return null\n\n const expiresInMs = stored.expiresAt - Date.now()\n if (expiresInMs < REFRESH_THRESHOLD_MS) {\n const refreshed = await this.performRefresh(stored.refreshToken)\n return refreshed?.accessToken ?? null\n }\n\n return stored.accessToken\n }\n\n /**\n * Called by `HttpClient` when a 401 is received.\n * Attempts one refresh; returns the new access token or null.\n */\n async handleUnauthorized(): Promise<string | null> {\n const stored = await this.storage.getTokens()\n if (!stored) {\n await this.onAuthExpired?.()\n return null\n }\n const refreshed = await this.performRefresh(stored.refreshToken)\n if (!refreshed) await this.onAuthExpired?.()\n return refreshed?.accessToken ?? null\n }\n\n /** Store new tokens after a successful login or signup. */\n async setTokens(tokens: StoredTokens): Promise<void> {\n await this.storage.setTokens(tokens)\n }\n\n /** Clear all stored tokens (call on logout). */\n async clearTokens(): Promise<void> {\n await this.storage.clearTokens()\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private performRefresh(refreshToken: string): Promise<StoredTokens | null> {\n if (this.refreshPromise) return this.refreshPromise\n\n this.refreshPromise = this.refreshFn(refreshToken)\n .then(async (tokens) => {\n await this.storage.setTokens(tokens)\n await this.onTokenRefreshed?.(tokens)\n return tokens\n })\n .catch((_err: unknown) => {\n void this.storage.clearTokens()\n throw new AuthError('Token refresh failed. Please re-authenticate.')\n })\n .finally(() => {\n this.refreshPromise = null\n })\n\n return this.refreshPromise\n }\n}\n\n// ---------------------------------------------------------------------------\n// Built-in storage adapters\n// ---------------------------------------------------------------------------\n\n/**\n * In-memory token storage (default for both packages).\n * Tokens are cleared when the page unloads or the process exits.\n * This is the most secure default for browser environments.\n */\nexport class MemoryTokenStorage implements TokenStorage {\n private tokens: StoredTokens | null = null\n\n getTokens(): StoredTokens | null {\n return this.tokens\n }\n\n setTokens(tokens: StoredTokens): void {\n this.tokens = tokens\n }\n\n clearTokens(): void {\n this.tokens = null\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Converts an `expires_at` ISO 8601 string from an API auth response\n * into a Unix timestamp (milliseconds) for use in `StoredTokens.expiresAt`.\n *\n * @param expiresAt - ISO 8601 datetime string, e.g. `\"2026-01-01T12:00:00Z\"`\n * @returns Unix timestamp in milliseconds\n */\nexport function parseExpiresAt(expiresAt: string): number {\n return new Date(expiresAt).getTime()\n}\n","/**\n * Buyer authentication namespace for browser environments.\n *\n * Tokens are stored automatically in the `TokenManager` after every\n * successful call — callers never need to manage tokens directly.\n *\n * @module\n */\nimport { parseExpiresAt } from '@ledewire/core'\nimport type { HttpClient, TokenManager } from '@ledewire/core'\nimport type {\n AuthLoginEmailRequest,\n AuthLoginOAuthRequest,\n AuthSignupRequest,\n AuthenticationResponse,\n} from '@ledewire/core'\n\n/**\n * Buyer authentication: signup, email/password login, Google OAuth.\n *\n * Obtain via `lw.auth` — do not construct directly.\n *\n * @example\n * ```ts\n * const lw = Ledewire.init({ apiKey: 'your_api_key' })\n * await lw.auth.loginWithEmail({ email: 'u@example.com', password: 'pw' })\n * ```\n */\nexport class BrowserAuthNamespace {\n /** @internal */\n constructor(\n private readonly http: HttpClient,\n private readonly tokenManager: TokenManager,\n ) {}\n\n /**\n * Register a new buyer account with email and password.\n * Tokens are stored automatically after successful signup.\n *\n * @param body - Signup credentials and display name.\n * @returns The authentication token response.\n */\n async signup(body: AuthSignupRequest): Promise<AuthenticationResponse> {\n const res = await this.http.post<AuthenticationResponse>('/v1/auth/signup', body)\n await this.storeTokens(res)\n return res\n }\n\n /**\n * Log in with email and password.\n * Tokens are stored automatically after successful login.\n *\n * @param body - Email and password credentials.\n * @returns The authentication token response.\n */\n async loginWithEmail(body: AuthLoginEmailRequest): Promise<AuthenticationResponse> {\n const res = await this.http.post<AuthenticationResponse>('/v1/auth/login/email', body)\n await this.storeTokens(res)\n return res\n }\n\n /**\n * Log in with a Google ID token obtained from the Google OAuth flow.\n * Tokens are stored automatically after successful login.\n *\n * @param body - The Google ID token.\n * @returns The authentication token response.\n */\n async loginWithGoogle(body: AuthLoginOAuthRequest): Promise<AuthenticationResponse> {\n const res = await this.http.post<AuthenticationResponse>('/v1/auth/login/google', body)\n await this.storeTokens(res)\n return res\n }\n\n private async storeTokens(res: AuthenticationResponse): Promise<void> {\n await this.tokenManager.setTokens({\n accessToken: res.access_token,\n refreshToken: res.refresh_token,\n expiresAt: parseExpiresAt(res.expires_at),\n })\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type { ContentWithAccessResponse } from '@ledewire/core'\n\n/**\n * Buyer content namespace — fetch content with real-time access information.\n *\n * Obtain via `lw.content` — do not construct directly.\n *\n * @example\n * ```ts\n * const { content, access } = await lw.content.getWithAccess('content-id')\n * if (access.has_access) showFullContent(content)\n * ```\n */\nexport class BrowserContentNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns a content item together with access information for the\n * authenticated buyer (or a generic access response when unauthenticated).\n *\n * @param id - The content ID.\n * @param userId - Optional user ID to check a specific user's access status.\n * @returns The content item with access information.\n */\n async getWithAccess(id: string, userId?: string): Promise<ContentWithAccessResponse> {\n const params: Record<string, string> = {}\n if (userId !== undefined) {\n params['user_id'] = userId\n }\n return this.http.get<ContentWithAccessResponse>(\n `/v1/content/${id}/with-access`,\n Object.keys(params).length > 0 ? params : undefined,\n )\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type { PurchaseCreateRequest, PurchaseResponse } from '@ledewire/core'\n\n/**\n * Buyer purchases namespace — create and retrieve content purchases.\n *\n * Obtain via `lw.purchases` — do not construct directly.\n *\n * @example\n * ```ts\n * await lw.purchases.create({ content_id: 'content-id' })\n * const all = await lw.purchases.list()\n * ```\n */\nexport class BrowserPurchasesNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Completes a content purchase using the buyer's wallet balance.\n *\n * @param body - The content ID and expected price in cents.\n * @returns The completed purchase record.\n */\n async create(body: PurchaseCreateRequest): Promise<PurchaseResponse> {\n return this.http.post<PurchaseResponse>('/v1/purchases', body)\n }\n\n /**\n * Returns all purchases made by the authenticated buyer.\n *\n * @returns A list of purchase records, newest first.\n */\n async list(): Promise<PurchaseResponse[]> {\n return this.http.get<PurchaseResponse[]>('/v1/purchases')\n }\n\n /**\n * Returns a single purchase by ID.\n *\n * @param id - The purchase ID.\n * @returns The purchase record.\n */\n async get(id: string): Promise<PurchaseResponse> {\n return this.http.get<PurchaseResponse>(`/v1/purchases/${id}`)\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type {\n WalletBalanceResponse,\n WalletPaymentSessionRequest,\n WalletPaymentSessionResponse,\n WalletPaymentStatusResponse,\n WalletTransactionItem,\n} from '@ledewire/core'\n\n/**\n * Buyer wallet namespace — balance, transactions, and payment session management.\n *\n * Obtain via `lw.wallet` — do not construct directly.\n *\n * @example\n * ```ts\n * const { balance_cents } = await lw.wallet.balance()\n * const session = await lw.wallet.createPaymentSession({ amount_cents: 500 })\n * ```\n */\nexport class BrowserWalletNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns the authenticated buyer's current wallet balance.\n *\n * @returns The current wallet balance in cents.\n */\n async balance(): Promise<WalletBalanceResponse> {\n return this.http.get<WalletBalanceResponse>('/v1/wallet/balance')\n }\n\n /**\n * Returns the authenticated buyer's wallet transaction history, newest first.\n *\n * @returns A list of completed wallet transaction entries.\n */\n async transactions(): Promise<WalletTransactionItem[]> {\n return this.http.get<WalletTransactionItem[]>('/v1/wallet/transactions')\n }\n\n /**\n * Creates a payment session for funding the buyer's wallet.\n *\n * @param body - The amount and currency to fund.\n * @returns Payment session details for use with the payment provider widget.\n */\n async createPaymentSession(\n body: WalletPaymentSessionRequest,\n ): Promise<WalletPaymentSessionResponse> {\n return this.http.post<WalletPaymentSessionResponse>('/v1/wallet/payment-session', body)\n }\n\n /**\n * Polls the status of a wallet funding payment session.\n *\n * @param sessionId - The session ID returned by `createPaymentSession`.\n * @returns The current payment status.\n */\n async getPaymentStatus(sessionId: string): Promise<WalletPaymentStatusResponse> {\n return this.http.get<WalletPaymentStatusResponse>(`/v1/wallet/payment-status/${sessionId}`)\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type { CheckoutStateResponse } from '@ledewire/core'\n\n/**\n * Checkout namespace — buyer checkout state for a specific content item.\n *\n * Obtain via `lw.checkout` — do not construct directly.\n *\n * @example\n * ```ts\n * const state = await lw.checkout.state('content-id')\n * // state.checkout_state.next_required_action:\n * // 'authenticate' | 'fund_wallet' | 'purchase' | 'view_content'\n * ```\n */\nexport class CheckoutNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns the authenticated buyer's checkout readiness for a given piece of\n * content. Indicates whether the buyer needs to authenticate, fund their\n * wallet, or purchase before accessing the content.\n *\n * Works without authentication — returns anonymous checkout state when the\n * buyer is not logged in.\n *\n * @param contentId - The content item ID.\n * @returns The checkout state, including what action is required next.\n */\n async state(contentId: string): Promise<CheckoutStateResponse> {\n return this.http.get<CheckoutStateResponse>(`/v1/checkout/state/${contentId}`)\n }\n}\n","import { HttpClient, MemoryTokenStorage, TokenManager, parseExpiresAt } from '@ledewire/core'\nimport type { TokenStorage } from '@ledewire/core'\nimport { BrowserAuthNamespace } from './resources/auth.js'\nimport { BrowserContentNamespace } from './resources/content.js'\nimport { BrowserPurchasesNamespace } from './resources/purchases.js'\nimport { BrowserWalletNamespace } from './resources/wallet.js'\nimport { CheckoutNamespace } from './resources/checkout.js'\n\n/**\n * Configuration options for the LedeWire browser client.\n */\nexport interface BrowserClientConfig {\n /**\n * LedeWire API key that identifies the store.\n * Obtained from the LedeWire merchant dashboard.\n */\n apiKey: string\n /**\n * Override the API base URL.\n * Defaults to `https://api.ledewire.com`.\n */\n baseUrl?: string\n /**\n * Token storage adapter.\n * Defaults to {@link MemoryTokenStorage} (in-memory, most secure).\n *\n * To persist sessions across page reloads, use the built-in\n * `localStorageAdapter`:\n * ```ts\n * import { init, localStorageAdapter } from '@ledewire/browser'\n * const lw = init({ apiKey: '...', storage: localStorageAdapter() })\n * ```\n */\n storage?: TokenStorage\n /**\n * Called when the user's session expires and cannot be refreshed.\n * Use this to show a re-authentication prompt.\n *\n * @example\n * ```ts\n * onAuthExpired: () => showLoginModal()\n * ```\n */\n onAuthExpired?: () => void\n}\n\n/**\n * Initialises a LedeWire browser client.\n * This is the primary entry point for the browser SDK.\n *\n * @example CDN usage:\n * ```html\n * <script src=\"https://cdn.jsdelivr.net/npm/@ledewire/browser@1/dist/ledewire.min.js\"></script>\n * <script>\n * const lw = Ledewire.init({ apiKey: 'your_api_key' })\n * const state = await lw.checkout.state('content-id')\n * </script>\n * ```\n *\n * @example npm usage:\n * ```ts\n * import { init } from '@ledewire/browser'\n * const lw = init({ apiKey: 'your_api_key' })\n * ```\n */\nexport function init(config: BrowserClientConfig): BrowserClient {\n const baseUrl = config.baseUrl ?? 'https://api.ledewire.com'\n const storage = config.storage ?? new MemoryTokenStorage()\n\n const tokenManager = new TokenManager({\n storage,\n refreshFn: async (refreshToken) => {\n const res = await fetch(`${baseUrl}/v1/auth/token/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refresh_token: refreshToken }),\n })\n if (!res.ok) throw new Error('Token refresh failed')\n const data = (await res.json()) as {\n access_token: string\n refresh_token: string\n expires_at: string\n }\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: parseExpiresAt(data.expires_at),\n }\n },\n ...(config.onAuthExpired !== undefined && { onAuthExpired: config.onAuthExpired }),\n })\n\n const http = new HttpClient({\n baseUrl,\n getAccessToken: () => tokenManager.getAccessToken(),\n onUnauthorized: () => tokenManager.handleUnauthorized(),\n })\n\n return new BrowserClient(http, tokenManager, config)\n}\n\n/**\n * The LedeWire browser client.\n * Access buyer flows through the namespaced properties.\n *\n * @remarks\n * Instantiate with {@link init} rather than constructing directly.\n */\nexport class BrowserClient {\n /** Buyer authentication: email/password signup/login, Google, password reset */\n readonly auth: BrowserAuthNamespace\n\n /** Checkout state machine: determines next action for a piece of content */\n readonly checkout: CheckoutNamespace\n\n /** Wallet: balance, fund via payment session, transaction history */\n readonly wallet: BrowserWalletNamespace\n\n /** Content purchases for the authenticated buyer */\n readonly purchases: BrowserPurchasesNamespace\n\n /** Public content with per-user access information */\n readonly content: BrowserContentNamespace\n\n /** @internal */\n constructor(\n public readonly _http: HttpClient,\n public readonly _tokenManager: TokenManager,\n public readonly _config: BrowserClientConfig,\n ) {\n this.auth = new BrowserAuthNamespace(_http, _tokenManager)\n this.checkout = new CheckoutNamespace(_http)\n this.wallet = new BrowserWalletNamespace(_http)\n this.purchases = new BrowserPurchasesNamespace(_http)\n this.content = new BrowserContentNamespace(_http)\n }\n}\n","import type { TokenStorage, StoredTokens } from '@ledewire/core'\n\nconst DEFAULT_STORAGE_KEY = 'lw:tokens'\n\n/**\n * A {@link TokenStorage} adapter that persists tokens in `localStorage`.\n *\n * Tokens survive page reloads and browser restarts, which is convenient\n * for sites where users expect persistent sessions.\n *\n * **Security note:** `localStorage` is accessible to any JavaScript on the\n * same origin. Only use this adapter on sites you fully control and trust.\n * The default in-memory storage is safer for high-security use cases.\n *\n * @param key - The `localStorage` key used to store tokens.\n * Defaults to `'lw:tokens'`. Override this if you have multiple\n * LedeWire integrations on the same origin.\n *\n * @example\n * ```ts\n * import { init, localStorageAdapter } from '@ledewire/browser'\n *\n * const lw = init({\n * apiKey: 'your_api_key',\n * storage: localStorageAdapter(),\n * })\n * ```\n */\nexport function localStorageAdapter(key = DEFAULT_STORAGE_KEY): TokenStorage {\n return {\n getTokens(): StoredTokens | null {\n const raw = localStorage.getItem(key)\n if (!raw) return null\n try {\n return JSON.parse(raw) as StoredTokens\n } catch {\n // Corrupted value - treat as empty\n return null\n }\n },\n setTokens(tokens: StoredTokens): void {\n localStorage.setItem(key, JSON.stringify(tokens))\n },\n clearTokens(): void {\n localStorage.removeItem(key)\n },\n }\n}\n"],"names":["LedewireError","message","statusCode","code","__publicField","AuthError","ForbiddenError","NotFoundError","PurchaseError","DEFAULT_BASE_URL","HttpClient","config","path","params","body","url","method","isRetry","token","headers","response","REFRESH_THRESHOLD_MS","TokenManager","options","stored","refreshed","tokens","refreshToken","_err","MemoryTokenStorage","parseExpiresAt","expiresAt","BrowserAuthNamespace","http","tokenManager","res","BrowserContentNamespace","id","userId","BrowserPurchasesNamespace","BrowserWalletNamespace","sessionId","CheckoutNamespace","contentId","init","baseUrl","storage","data","BrowserClient","_http","_tokenManager","_config","DEFAULT_STORAGE_KEY","localStorageAdapter","key","raw"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ledewire.min.js","sources":["../../core/src/errors.ts","../../core/src/http-client.ts","../../core/src/token-manager.ts","../src/resources/auth.ts","../src/resources/config.ts","../src/resources/content.ts","../src/resources/purchases.ts","../src/resources/wallet.ts","../src/resources/checkout.ts","../src/client.ts","../src/local-storage-adapter.ts"],"sourcesContent":["/**\n * Base error class for all LedeWire SDK errors.\n * All errors thrown by the SDK are instances of this class,\n * making it safe to use `err instanceof LedewireError` as a type guard.\n *\n * @example\n * ```ts\n * try {\n * await client.purchases.create({ contentId: '...' })\n * } catch (err) {\n * if (err instanceof LedewireError) {\n * console.error(err.statusCode, err.message)\n * }\n * }\n * ```\n */\nexport class LedewireError extends Error {\n /** HTTP status code returned by the API (e.g. 400, 401, 404, 422). */\n public readonly statusCode: number\n\n /** Machine-readable error code from the API error body, if present. */\n public readonly code: number | undefined\n\n constructor(message: string, statusCode: number, code?: number) {\n super(message)\n this.name = 'LedewireError'\n this.statusCode = statusCode\n this.code = code\n // Restore prototype chain (required for extending built-in Error in TS)\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\n/**\n * Thrown when the request lacks valid authentication credentials,\n * or when a token refresh fails and re-authentication is required.\n */\nexport class AuthError extends LedewireError {\n constructor(message: string, code?: number) {\n super(message, 401, code)\n this.name = 'AuthError'\n }\n}\n\n/**\n * Thrown when the authenticated user does not have permission\n * to perform the requested operation.\n */\nexport class ForbiddenError extends LedewireError {\n constructor(message: string, code?: number) {\n super(message, 403, code)\n this.name = 'ForbiddenError'\n }\n}\n\n/**\n * Thrown when the requested resource does not exist.\n */\nexport class NotFoundError extends LedewireError {\n constructor(message: string, code?: number) {\n super(message, 404, code)\n this.name = 'NotFoundError'\n }\n}\n\n/**\n * Thrown when the purchase cannot be completed due to a validation\n * failure, such as a price mismatch or a duplicate purchase.\n */\nexport class PurchaseError extends LedewireError {\n constructor(message: string, statusCode: number, code?: number) {\n super(message, statusCode, code)\n this.name = 'PurchaseError'\n }\n}\n","import type { components } from './api.gen.js'\nimport { AuthError, ForbiddenError, LedewireError, NotFoundError } from './errors.js'\n\n/** @internal */\ntype ApiErrorBody = components['schemas']['ErrorResponse']\n\n/**\n * Configuration options for `HttpClient`.\n */\nexport interface HttpClientConfig {\n /** API base URL. Defaults to the production API. */\n baseUrl?: string\n /**\n * Returns the current access token (or null) before each request.\n * If null, requests are sent without an Authorization header.\n */\n getAccessToken?: () => string | null | Promise<string | null>\n /**\n * Called when a 401 is received on the first attempt.\n * Should refresh and return the new access token, or null to propagate\n * an `AuthError` to the caller.\n */\n onUnauthorized?: () => string | null | Promise<string | null>\n}\n\nconst DEFAULT_BASE_URL = 'https://api.ledewire.com'\n\n/**\n * Core HTTP client used by all SDK packages.\n *\n * Features:\n * - Injects `Authorization: Bearer <token>` headers automatically\n * - Maps HTTP error responses to typed `LedewireError` subclasses\n * - On receiving a 401, calls `onUnauthorized` once and retries the request\n *\n * This is an internal class - consumers should use the\n * package-level client factories (`init` / `createClient`) instead.\n */\nexport class HttpClient {\n private readonly baseUrl: string\n private readonly getAccessToken: NonNullable<HttpClientConfig['getAccessToken']>\n private readonly onUnauthorized: NonNullable<HttpClientConfig['onUnauthorized']>\n\n constructor(config: HttpClientConfig = {}) {\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '')\n this.getAccessToken = config.getAccessToken ?? (() => null)\n this.onUnauthorized = config.onUnauthorized ?? (() => null)\n }\n\n /**\n * GET request with optional query parameters.\n * @param path - API path (e.g. `/v1/wallet/balance`)\n * @param params - Query string parameters\n */\n async get<T>(path: string, params?: Record<string, string>): Promise<T> {\n return this.request<T>('GET', this.buildUrl(path, params))\n }\n\n /**\n * POST request.\n * @param path - API path\n * @param body - Request body (JSON-serialized)\n */\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('POST', this.buildUrl(path), body)\n }\n\n /**\n * PUT request.\n * @param path - API path\n * @param body - Request body (JSON-serialized)\n */\n async put<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PUT', this.buildUrl(path), body)\n }\n\n /**\n * PATCH request.\n * @param path - API path\n * @param body - Partial update body (JSON-serialized)\n */\n async patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PATCH', this.buildUrl(path), body)\n }\n\n /**\n * DELETE request.\n * @param path - API path\n */\n async delete<T = void>(path: string): Promise<T> {\n return this.request<T>('DELETE', this.buildUrl(path))\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private buildUrl(path: string, params?: Record<string, string>): string {\n const url = `${this.baseUrl}${path}`\n if (!params || Object.keys(params).length === 0) return url\n return `${url}?${new URLSearchParams(params).toString()}`\n }\n\n private async request<T>(\n method: string,\n url: string,\n body?: unknown,\n isRetry = false,\n ): Promise<T> {\n const token = await this.getAccessToken()\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n }\n if (token) headers['Authorization'] = `Bearer ${token}`\n\n const response = await fetch(url, {\n method,\n headers,\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n if (response.status === 401 && !isRetry) {\n const newToken = await this.onUnauthorized()\n if (newToken) {\n return this.request<T>(method, url, body, true)\n }\n throw new AuthError('Session expired. Please re-authenticate.')\n }\n\n if (!response.ok) {\n await this.throwApiError(response)\n }\n\n if (response.status === 204) {\n return undefined as T\n }\n\n return response.json() as Promise<T>\n }\n\n private async throwApiError(response: Response): Promise<never> {\n let body: ApiErrorBody | undefined\n try {\n body = (await response.json()) as ApiErrorBody\n } catch {\n // Non-JSON body - fall through to use statusText\n }\n\n const message = body?.error.message ?? response.statusText\n const code = body?.error.code\n\n switch (response.status) {\n case 401:\n throw new AuthError(message, code)\n case 403:\n throw new ForbiddenError(message, code)\n case 404:\n throw new NotFoundError(message, code)\n default:\n throw new LedewireError(message, response.status, code)\n }\n }\n}\n","import type { StoredTokens, TokenStorage } from './types.js'\nimport { AuthError } from './errors.js'\n\n/** How many milliseconds before expiry to proactively refresh the token. */\nconst REFRESH_THRESHOLD_MS = 60_000 // 60 seconds\n\n/**\n * Options for constructing a `TokenManager`.\n */\nexport interface TokenManagerOptions {\n storage: TokenStorage\n /**\n * Exchanges a refresh token for a new token pair.\n * Implemented by each package's client factory using the\n * `/v1/auth/token/refresh` endpoint.\n */\n refreshFn: (refreshToken: string) => Promise<StoredTokens>\n /**\n * Called after a successful token refresh.\n * Use in server environments to persist the new tokens to a database or cache.\n *\n * @example\n * ```ts\n * onTokenRefreshed: async (tokens) => {\n * await redis.set('session:tokens', JSON.stringify(tokens))\n * }\n * ```\n */\n onTokenRefreshed?: (tokens: StoredTokens) => void | Promise<void>\n /**\n * Called when a refresh attempt fails and the user must re-authenticate.\n * Typically used to redirect to login or emit a UI event.\n *\n * @example\n * ```ts\n * onAuthExpired: () => { window.location.href = '/login' }\n * ```\n */\n onAuthExpired?: () => void | Promise<void>\n}\n\n/**\n * Manages the JWT token lifecycle transparently.\n *\n * - Proactively refreshes the access token when it expires within 60 seconds\n * - Handles reactive refresh on 401 responses from the HTTP client\n * - Deduplicates concurrent refresh calls (only one in-flight refresh at a time)\n * - Fires `onAuthExpired` when refresh fails so the UI can prompt re-login\n *\n * This is an internal class - use the package-level client factories instead.\n */\nexport class TokenManager {\n private readonly storage: TokenStorage\n private readonly refreshFn: TokenManagerOptions['refreshFn']\n private readonly onTokenRefreshed: TokenManagerOptions['onTokenRefreshed']\n private readonly onAuthExpired: TokenManagerOptions['onAuthExpired']\n private refreshPromise: Promise<StoredTokens | null> | null = null\n\n constructor(options: TokenManagerOptions) {\n this.storage = options.storage\n this.refreshFn = options.refreshFn\n this.onTokenRefreshed = options.onTokenRefreshed\n this.onAuthExpired = options.onAuthExpired\n }\n\n /**\n * Returns a valid access token, refreshing proactively if needed.\n * Returns `null` if no tokens are stored (user not authenticated).\n */\n async getAccessToken(): Promise<string | null> {\n const stored = await this.storage.getTokens()\n if (!stored) return null\n\n const expiresInMs = stored.expiresAt - Date.now()\n if (expiresInMs < REFRESH_THRESHOLD_MS) {\n const refreshed = await this.performRefresh(stored.refreshToken)\n return refreshed?.accessToken ?? null\n }\n\n return stored.accessToken\n }\n\n /**\n * Called by `HttpClient` when a 401 is received.\n * Attempts one refresh; returns the new access token or null.\n */\n async handleUnauthorized(): Promise<string | null> {\n const stored = await this.storage.getTokens()\n if (!stored) {\n await this.onAuthExpired?.()\n return null\n }\n const refreshed = await this.performRefresh(stored.refreshToken)\n if (!refreshed) await this.onAuthExpired?.()\n return refreshed?.accessToken ?? null\n }\n\n /** Store new tokens after a successful login or signup. */\n async setTokens(tokens: StoredTokens): Promise<void> {\n await this.storage.setTokens(tokens)\n }\n\n /** Clear all stored tokens (call on logout). */\n async clearTokens(): Promise<void> {\n await this.storage.clearTokens()\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private performRefresh(refreshToken: string): Promise<StoredTokens | null> {\n if (this.refreshPromise) return this.refreshPromise\n\n this.refreshPromise = this.refreshFn(refreshToken)\n .then(async (tokens) => {\n await this.storage.setTokens(tokens)\n await this.onTokenRefreshed?.(tokens)\n return tokens\n })\n .catch((_err: unknown) => {\n void this.storage.clearTokens()\n throw new AuthError('Token refresh failed. Please re-authenticate.')\n })\n .finally(() => {\n this.refreshPromise = null\n })\n\n return this.refreshPromise\n }\n}\n\n// ---------------------------------------------------------------------------\n// Built-in storage adapters\n// ---------------------------------------------------------------------------\n\n/**\n * In-memory token storage (default for both packages).\n * Tokens are cleared when the page unloads or the process exits.\n * This is the most secure default for browser environments.\n */\nexport class MemoryTokenStorage implements TokenStorage {\n private tokens: StoredTokens | null = null\n\n getTokens(): StoredTokens | null {\n return this.tokens\n }\n\n setTokens(tokens: StoredTokens): void {\n this.tokens = tokens\n }\n\n clearTokens(): void {\n this.tokens = null\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Converts an `expires_at` ISO 8601 string from an API auth response\n * into a Unix timestamp (milliseconds) for use in `StoredTokens.expiresAt`.\n *\n * @param expiresAt - ISO 8601 datetime string, e.g. `\"2026-01-01T12:00:00Z\"`\n * @returns Unix timestamp in milliseconds\n */\nexport function parseExpiresAt(expiresAt: string): number {\n return new Date(expiresAt).getTime()\n}\n","/**\n * Buyer authentication namespace for browser environments.\n *\n * Tokens are stored automatically in the `TokenManager` after every\n * successful call — callers never need to manage tokens directly.\n *\n * @module\n */\nimport { parseExpiresAt } from '@ledewire/core'\nimport type { HttpClient, TokenManager } from '@ledewire/core'\nimport type {\n AuthLoginEmailRequest,\n AuthLoginOAuthRequest,\n AuthSignupRequest,\n AuthenticationResponse,\n} from '@ledewire/core'\n\n/**\n * Buyer authentication: signup, email/password login, Google OAuth.\n *\n * Obtain via `lw.auth` — do not construct directly.\n *\n * @example\n * ```ts\n * const lw = Ledewire.init({ apiKey: 'your_api_key' })\n * await lw.auth.loginWithEmail({ email: 'u@example.com', password: 'pw' })\n * ```\n */\nexport class BrowserAuthNamespace {\n /** @internal */\n constructor(\n private readonly http: HttpClient,\n private readonly tokenManager: TokenManager,\n ) {}\n\n /**\n * Register a new buyer account with email and password.\n * Tokens are stored automatically after successful signup.\n *\n * @param body - Signup credentials and display name.\n * @returns The authentication token response.\n */\n async signup(body: AuthSignupRequest): Promise<AuthenticationResponse> {\n const res = await this.http.post<AuthenticationResponse>('/v1/auth/signup', body)\n await this.storeTokens(res)\n return res\n }\n\n /**\n * Log in with email and password.\n * Tokens are stored automatically after successful login.\n *\n * @param body - Email and password credentials.\n * @returns The authentication token response.\n */\n async loginWithEmail(body: AuthLoginEmailRequest): Promise<AuthenticationResponse> {\n const res = await this.http.post<AuthenticationResponse>('/v1/auth/login/email', body)\n await this.storeTokens(res)\n return res\n }\n\n /**\n * Log in with a Google ID token obtained from the Google OAuth flow.\n * Tokens are stored automatically after successful login.\n *\n * @param body - The Google ID token.\n * @returns The authentication token response.\n */\n async loginWithGoogle(body: AuthLoginOAuthRequest): Promise<AuthenticationResponse> {\n const res = await this.http.post<AuthenticationResponse>('/v1/auth/login/google', body)\n await this.storeTokens(res)\n return res\n }\n\n private async storeTokens(res: AuthenticationResponse): Promise<void> {\n await this.tokenManager.setTokens({\n accessToken: res.access_token,\n refreshToken: res.refresh_token,\n expiresAt: parseExpiresAt(res.expires_at),\n })\n }\n}\n","/**\n * Platform configuration namespace (browser).\n *\n * Provides access to public (unauthenticated) platform-level configuration.\n *\n * @module\n */\nimport type { HttpClient } from '@ledewire/core'\nimport type { PublicConfigResponse } from '@ledewire/core'\n\n/**\n * Platform-level configuration for browser clients.\n *\n * Obtain via `client.config` — do not construct directly.\n */\nexport class BrowserConfigNamespace {\n /** @internal */\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns platform-level public configuration.\n * No authentication required — safe to call before the user has signed in.\n *\n * Use `google_client_id` to initialise the Google Identity Services library\n * on the storefront login page without requiring a prior authenticated call.\n *\n * @example\n * ```ts\n * const lw = init({ apiKey: 'your_api_key' })\n * const { google_client_id } = await lw.config.getPublic()\n * google.accounts.id.initialize({ client_id: google_client_id, callback })\n * ```\n */\n async getPublic(): Promise<PublicConfigResponse> {\n return this.http.get<PublicConfigResponse>('/v1/config/public')\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type { ContentWithAccessResponse } from '@ledewire/core'\n\n/**\n * Buyer content namespace — fetch content with real-time access information.\n *\n * Obtain via `lw.content` — do not construct directly.\n *\n * @example\n * ```ts\n * const { content, access } = await lw.content.getWithAccess('content-id')\n * if (access.has_access) showFullContent(content)\n * ```\n */\nexport class BrowserContentNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns a content item together with access information for the\n * authenticated buyer (or a generic access response when unauthenticated).\n *\n * @param id - The content ID.\n * @param userId - Optional user ID to check a specific user's access status.\n * @returns The content item with access information.\n */\n async getWithAccess(id: string, userId?: string): Promise<ContentWithAccessResponse> {\n const params: Record<string, string> = {}\n if (userId !== undefined) {\n params['user_id'] = userId\n }\n return this.http.get<ContentWithAccessResponse>(\n `/v1/content/${id}/with-access`,\n Object.keys(params).length > 0 ? params : undefined,\n )\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type { PurchaseCreateRequest, PurchaseResponse } from '@ledewire/core'\n\n/**\n * Buyer purchases namespace — create and retrieve content purchases.\n *\n * Obtain via `lw.purchases` — do not construct directly.\n *\n * @example\n * ```ts\n * await lw.purchases.create({ content_id: 'content-id' })\n * const all = await lw.purchases.list()\n * ```\n */\nexport class BrowserPurchasesNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Completes a content purchase using the buyer's wallet balance.\n *\n * @param body - The content ID and expected price in cents.\n * @returns The completed purchase record.\n */\n async create(body: PurchaseCreateRequest): Promise<PurchaseResponse> {\n return this.http.post<PurchaseResponse>('/v1/purchases', body)\n }\n\n /**\n * Returns all purchases made by the authenticated buyer.\n *\n * @returns A list of purchase records, newest first.\n */\n async list(): Promise<PurchaseResponse[]> {\n return this.http.get<PurchaseResponse[]>('/v1/purchases')\n }\n\n /**\n * Returns a single purchase by ID.\n *\n * @param id - The purchase ID.\n * @returns The purchase record.\n */\n async get(id: string): Promise<PurchaseResponse> {\n return this.http.get<PurchaseResponse>(`/v1/purchases/${id}`)\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type {\n WalletBalanceResponse,\n WalletPaymentSessionRequest,\n WalletPaymentSessionResponse,\n WalletPaymentStatusResponse,\n WalletTransactionItem,\n} from '@ledewire/core'\n\n/**\n * Buyer wallet namespace — balance, transactions, and payment session management.\n *\n * Obtain via `lw.wallet` — do not construct directly.\n *\n * @example\n * ```ts\n * const { balance_cents } = await lw.wallet.balance()\n * const session = await lw.wallet.createPaymentSession({ amount_cents: 500 })\n * ```\n */\nexport class BrowserWalletNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns the authenticated buyer's current wallet balance.\n *\n * @returns The current wallet balance in cents.\n */\n async balance(): Promise<WalletBalanceResponse> {\n return this.http.get<WalletBalanceResponse>('/v1/wallet/balance')\n }\n\n /**\n * Returns the authenticated buyer's wallet transaction history, newest first.\n *\n * @returns A list of completed wallet transaction entries.\n */\n async transactions(): Promise<WalletTransactionItem[]> {\n return this.http.get<WalletTransactionItem[]>('/v1/wallet/transactions')\n }\n\n /**\n * Creates a payment session for funding the buyer's wallet.\n *\n * @param body - The amount and currency to fund.\n * @returns Payment session details for use with the payment provider widget.\n */\n async createPaymentSession(\n body: WalletPaymentSessionRequest,\n ): Promise<WalletPaymentSessionResponse> {\n return this.http.post<WalletPaymentSessionResponse>('/v1/wallet/payment-session', body)\n }\n\n /**\n * Polls the status of a wallet funding payment session.\n *\n * @param sessionId - The session ID returned by `createPaymentSession`.\n * @returns The current payment status.\n */\n async getPaymentStatus(sessionId: string): Promise<WalletPaymentStatusResponse> {\n return this.http.get<WalletPaymentStatusResponse>(`/v1/wallet/payment-status/${sessionId}`)\n }\n}\n","import type { HttpClient } from '@ledewire/core'\nimport type { CheckoutStateResponse } from '@ledewire/core'\n\n/**\n * Checkout namespace — buyer checkout state for a specific content item.\n *\n * Obtain via `lw.checkout` — do not construct directly.\n *\n * @example\n * ```ts\n * const state = await lw.checkout.state('content-id')\n * // state.checkout_state.next_required_action:\n * // 'authenticate' | 'fund_wallet' | 'purchase' | 'view_content'\n * ```\n */\nexport class CheckoutNamespace {\n constructor(protected readonly http: HttpClient) {}\n\n /**\n * Returns the authenticated buyer's checkout readiness for a given piece of\n * content. Indicates whether the buyer needs to authenticate, fund their\n * wallet, or purchase before accessing the content.\n *\n * Works without authentication — returns anonymous checkout state when the\n * buyer is not logged in.\n *\n * @param contentId - The content item ID.\n * @returns The checkout state, including what action is required next.\n */\n async state(contentId: string): Promise<CheckoutStateResponse> {\n return this.http.get<CheckoutStateResponse>(`/v1/checkout/state/${contentId}`)\n }\n}\n","import { HttpClient, MemoryTokenStorage, TokenManager, parseExpiresAt } from '@ledewire/core'\nimport type { TokenStorage } from '@ledewire/core'\nimport { BrowserAuthNamespace } from './resources/auth.js'\nimport { BrowserConfigNamespace } from './resources/config.js'\nimport { BrowserContentNamespace } from './resources/content.js'\nimport { BrowserPurchasesNamespace } from './resources/purchases.js'\nimport { BrowserWalletNamespace } from './resources/wallet.js'\nimport { CheckoutNamespace } from './resources/checkout.js'\n\n/**\n * Configuration options for the LedeWire browser client.\n */\nexport interface BrowserClientConfig {\n /**\n * LedeWire API key that identifies the store.\n * Obtained from the LedeWire merchant dashboard.\n */\n apiKey: string\n /**\n * Override the API base URL.\n * Defaults to `https://api.ledewire.com`.\n */\n baseUrl?: string\n /**\n * Token storage adapter.\n * Defaults to {@link MemoryTokenStorage} (in-memory, most secure).\n *\n * To persist sessions across page reloads, use the built-in\n * `localStorageAdapter`:\n * ```ts\n * import { init, localStorageAdapter } from '@ledewire/browser'\n * const lw = init({ apiKey: '...', storage: localStorageAdapter() })\n * ```\n */\n storage?: TokenStorage\n /**\n * Called when the user's session expires and cannot be refreshed.\n * Use this to show a re-authentication prompt.\n *\n * @example\n * ```ts\n * onAuthExpired: () => showLoginModal()\n * ```\n */\n onAuthExpired?: () => void\n}\n\n/**\n * Initialises a LedeWire browser client.\n * This is the primary entry point for the browser SDK.\n *\n * @example CDN usage:\n * ```html\n * <script src=\"https://cdn.jsdelivr.net/npm/@ledewire/browser@1/dist/ledewire.min.js\"></script>\n * <script>\n * const lw = Ledewire.init({ apiKey: 'your_api_key' })\n * const state = await lw.checkout.state('content-id')\n * </script>\n * ```\n *\n * @example npm usage:\n * ```ts\n * import { init } from '@ledewire/browser'\n * const lw = init({ apiKey: 'your_api_key' })\n * ```\n */\nexport function init(config: BrowserClientConfig): BrowserClient {\n const baseUrl = config.baseUrl ?? 'https://api.ledewire.com'\n const storage = config.storage ?? new MemoryTokenStorage()\n\n const tokenManager = new TokenManager({\n storage,\n refreshFn: async (refreshToken) => {\n const res = await fetch(`${baseUrl}/v1/auth/token/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refresh_token: refreshToken }),\n })\n if (!res.ok) throw new Error('Token refresh failed')\n const data = (await res.json()) as {\n access_token: string\n refresh_token: string\n expires_at: string\n }\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: parseExpiresAt(data.expires_at),\n }\n },\n ...(config.onAuthExpired !== undefined && { onAuthExpired: config.onAuthExpired }),\n })\n\n const http = new HttpClient({\n baseUrl,\n getAccessToken: () => tokenManager.getAccessToken(),\n onUnauthorized: () => tokenManager.handleUnauthorized(),\n })\n\n return new BrowserClient(http, tokenManager, config)\n}\n\n/**\n * The LedeWire browser client.\n * Access buyer flows through the namespaced properties.\n *\n * @remarks\n * Instantiate with {@link init} rather than constructing directly.\n */\nexport class BrowserClient {\n /** Platform-level public configuration (no auth required) */\n readonly config: BrowserConfigNamespace\n\n /** Buyer authentication: email/password signup/login, Google, password reset */\n readonly auth: BrowserAuthNamespace\n\n /** Checkout state machine: determines next action for a piece of content */\n readonly checkout: CheckoutNamespace\n\n /** Wallet: balance, fund via payment session, transaction history */\n readonly wallet: BrowserWalletNamespace\n\n /** Content purchases for the authenticated buyer */\n readonly purchases: BrowserPurchasesNamespace\n\n /** Public content with per-user access information */\n readonly content: BrowserContentNamespace\n\n /** @internal */\n constructor(\n public readonly _http: HttpClient,\n public readonly _tokenManager: TokenManager,\n public readonly _config: BrowserClientConfig,\n ) {\n this.config = new BrowserConfigNamespace(_http)\n this.auth = new BrowserAuthNamespace(_http, _tokenManager)\n this.checkout = new CheckoutNamespace(_http)\n this.wallet = new BrowserWalletNamespace(_http)\n this.purchases = new BrowserPurchasesNamespace(_http)\n this.content = new BrowserContentNamespace(_http)\n }\n}\n","import type { TokenStorage, StoredTokens } from '@ledewire/core'\n\nconst DEFAULT_STORAGE_KEY = 'lw:tokens'\n\n/**\n * A {@link TokenStorage} adapter that persists tokens in `localStorage`.\n *\n * Tokens survive page reloads and browser restarts, which is convenient\n * for sites where users expect persistent sessions.\n *\n * **Security note:** `localStorage` is accessible to any JavaScript on the\n * same origin. Only use this adapter on sites you fully control and trust.\n * The default in-memory storage is safer for high-security use cases.\n *\n * @param key - The `localStorage` key used to store tokens.\n * Defaults to `'lw:tokens'`. Override this if you have multiple\n * LedeWire integrations on the same origin.\n *\n * @example\n * ```ts\n * import { init, localStorageAdapter } from '@ledewire/browser'\n *\n * const lw = init({\n * apiKey: 'your_api_key',\n * storage: localStorageAdapter(),\n * })\n * ```\n */\nexport function localStorageAdapter(key = DEFAULT_STORAGE_KEY): TokenStorage {\n return {\n getTokens(): StoredTokens | null {\n const raw = localStorage.getItem(key)\n if (!raw) return null\n try {\n return JSON.parse(raw) as StoredTokens\n } catch {\n // Corrupted value - treat as empty\n return null\n }\n },\n setTokens(tokens: StoredTokens): void {\n localStorage.setItem(key, JSON.stringify(tokens))\n },\n clearTokens(): void {\n localStorage.removeItem(key)\n },\n }\n}\n"],"names":["LedewireError","message","statusCode","code","__publicField","AuthError","ForbiddenError","NotFoundError","PurchaseError","DEFAULT_BASE_URL","HttpClient","config","path","params","body","url","method","isRetry","token","headers","response","REFRESH_THRESHOLD_MS","TokenManager","options","stored","refreshed","tokens","refreshToken","_err","MemoryTokenStorage","parseExpiresAt","expiresAt","BrowserAuthNamespace","http","tokenManager","res","BrowserConfigNamespace","BrowserContentNamespace","id","userId","BrowserPurchasesNamespace","BrowserWalletNamespace","sessionId","CheckoutNamespace","contentId","init","baseUrl","storage","data","BrowserClient","_http","_tokenManager","_config","DEFAULT_STORAGE_KEY","localStorageAdapter","key","raw"],"mappings":"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"}
|