@casfa/client 0.0.1

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/dist/index.js ADDED
@@ -0,0 +1,994 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/api/index.ts
8
+ var api_exports = {};
9
+ __export(api_exports, {
10
+ approveAuthRequest: () => approveAuthRequest,
11
+ commitDepot: () => commitDepot,
12
+ createAuthRequest: () => createAuthRequest,
13
+ createDepot: () => createDepot,
14
+ createTicket: () => createTicket,
15
+ createToken: () => createToken,
16
+ delegateToken: () => delegateToken,
17
+ deleteDepot: () => deleteDepot,
18
+ exchangeCode: () => exchangeCode,
19
+ fetchServiceInfo: () => fetchServiceInfo,
20
+ getAuthRequest: () => getAuthRequest,
21
+ getDepot: () => getDepot,
22
+ getMe: () => getMe,
23
+ getNode: () => getNode,
24
+ getNodeMetadata: () => getNodeMetadata,
25
+ getOAuthConfig: () => getOAuthConfig,
26
+ getTicket: () => getTicket,
27
+ getToken: () => getToken,
28
+ healthCheck: () => healthCheck,
29
+ listDepots: () => listDepots,
30
+ listTickets: () => listTickets,
31
+ listTokens: () => listTokens,
32
+ login: () => login,
33
+ pollAuthRequest: () => pollAuthRequest,
34
+ prepareNodes: () => prepareNodes,
35
+ putNode: () => putNode,
36
+ refresh: () => refresh,
37
+ rejectAuthRequest: () => rejectAuthRequest,
38
+ revokeToken: () => revokeToken,
39
+ submitTicket: () => submitTicket,
40
+ tokenResponseToStoredUserToken: () => tokenResponseToStoredUserToken,
41
+ updateDepot: () => updateDepot
42
+ });
43
+
44
+ // src/utils/http.ts
45
+ var statusToErrorCode = (status) => {
46
+ switch (status) {
47
+ case 401:
48
+ return "UNAUTHORIZED";
49
+ case 403:
50
+ return "FORBIDDEN";
51
+ case 404:
52
+ return "NOT_FOUND";
53
+ case 409:
54
+ return "CONFLICT";
55
+ case 400:
56
+ case 422:
57
+ return "VALIDATION_ERROR";
58
+ case 429:
59
+ return "RATE_LIMITED";
60
+ default:
61
+ return "UNKNOWN";
62
+ }
63
+ };
64
+ var createErrorFromResponse = async (response) => {
65
+ const code = statusToErrorCode(response.status);
66
+ let message = response.statusText;
67
+ let details;
68
+ try {
69
+ const body = await response.json();
70
+ if (typeof body.message === "string") {
71
+ message = body.message;
72
+ }
73
+ if (typeof body.error === "string") {
74
+ message = body.error;
75
+ }
76
+ details = body;
77
+ } catch {
78
+ }
79
+ return { code, message, status: response.status, details };
80
+ };
81
+ var createNetworkError = (err) => ({
82
+ code: "NETWORK_ERROR",
83
+ message: err instanceof Error ? err.message : "Network request failed",
84
+ details: err
85
+ });
86
+ var fetchApi = async (url, options = {}) => {
87
+ const { method = "GET", headers = {}, body, responseType = "json" } = options;
88
+ const requestHeaders = { ...headers };
89
+ if (body !== void 0 && !requestHeaders["Content-Type"]) {
90
+ requestHeaders["Content-Type"] = "application/json";
91
+ }
92
+ try {
93
+ const response = await fetch(url, {
94
+ method,
95
+ headers: requestHeaders,
96
+ body: body !== void 0 ? JSON.stringify(body) : void 0
97
+ });
98
+ if (!response.ok) {
99
+ const error = await createErrorFromResponse(response);
100
+ return { ok: false, error };
101
+ }
102
+ let data;
103
+ switch (responseType) {
104
+ case "json":
105
+ data = await response.json();
106
+ break;
107
+ case "blob":
108
+ data = await response.blob();
109
+ break;
110
+ case "text":
111
+ data = await response.text();
112
+ break;
113
+ case "none":
114
+ data = void 0;
115
+ break;
116
+ }
117
+ return { ok: true, data, status: response.status };
118
+ } catch (err) {
119
+ return { ok: false, error: createNetworkError(err) };
120
+ }
121
+ };
122
+ var fetchWithAuth = async (url, authHeader, options = {}) => {
123
+ const headers = { ...options.headers };
124
+ if (authHeader) {
125
+ headers.Authorization = authHeader;
126
+ }
127
+ return fetchApi(url, { ...options, headers });
128
+ };
129
+
130
+ // src/api/depots.ts
131
+ var createDepot = async (baseUrl, realm, accessTokenBase64, params) => {
132
+ return fetchWithAuth(
133
+ `${baseUrl}/api/realm/${encodeURIComponent(realm)}/depots`,
134
+ `Bearer ${accessTokenBase64}`,
135
+ {
136
+ method: "POST",
137
+ body: params
138
+ }
139
+ );
140
+ };
141
+ var listDepots = async (baseUrl, realm, accessTokenBase64, params) => {
142
+ const query = new URLSearchParams();
143
+ if (params?.limit) query.set("limit", String(params.limit));
144
+ if (params?.cursor) query.set("cursor", params.cursor);
145
+ const queryString = query.toString();
146
+ const url = `${baseUrl}/api/realm/${encodeURIComponent(realm)}/depots${queryString ? `?${queryString}` : ""}`;
147
+ return fetchWithAuth(url, `Bearer ${accessTokenBase64}`);
148
+ };
149
+ var getDepot = async (baseUrl, realm, accessTokenBase64, depotId) => {
150
+ return fetchWithAuth(
151
+ `${baseUrl}/api/realm/${encodeURIComponent(realm)}/depots/${encodeURIComponent(depotId)}`,
152
+ `Bearer ${accessTokenBase64}`
153
+ );
154
+ };
155
+ var updateDepot = async (baseUrl, realm, accessTokenBase64, depotId, params) => {
156
+ return fetchWithAuth(
157
+ `${baseUrl}/api/realm/${encodeURIComponent(realm)}/depots/${encodeURIComponent(depotId)}`,
158
+ `Bearer ${accessTokenBase64}`,
159
+ {
160
+ method: "PATCH",
161
+ body: params
162
+ }
163
+ );
164
+ };
165
+ var deleteDepot = async (baseUrl, realm, accessTokenBase64, depotId) => {
166
+ return fetchWithAuth(
167
+ `${baseUrl}/api/realm/${encodeURIComponent(realm)}/depots/${encodeURIComponent(depotId)}`,
168
+ `Bearer ${accessTokenBase64}`,
169
+ {
170
+ method: "DELETE",
171
+ responseType: "none"
172
+ }
173
+ );
174
+ };
175
+ var commitDepot = async (baseUrl, realm, accessTokenBase64, depotId, params) => {
176
+ return fetchWithAuth(
177
+ `${baseUrl}/api/realm/${encodeURIComponent(realm)}/depots/${encodeURIComponent(depotId)}/commit`,
178
+ `Bearer ${accessTokenBase64}`,
179
+ {
180
+ method: "POST",
181
+ body: params
182
+ }
183
+ );
184
+ };
185
+
186
+ // src/api/info.ts
187
+ var fetchServiceInfo = async (baseUrl) => {
188
+ return fetchApi(`${baseUrl}/api/info`);
189
+ };
190
+ var healthCheck = async (baseUrl) => {
191
+ return fetchApi(`${baseUrl}/api/health`);
192
+ };
193
+
194
+ // src/api/nodes.ts
195
+ var getNode = async (baseUrl, realm, accessTokenBase64, nodeKey, indexPath) => {
196
+ const url = `${baseUrl}/api/realm/${encodeURIComponent(realm)}/nodes/${encodeURIComponent(nodeKey)}`;
197
+ try {
198
+ const response = await fetch(url, {
199
+ headers: {
200
+ Authorization: `Bearer ${accessTokenBase64}`,
201
+ "X-CAS-Index-Path": indexPath
202
+ }
203
+ });
204
+ if (!response.ok) {
205
+ const error = await response.json().catch(() => ({ message: response.statusText }));
206
+ return {
207
+ ok: false,
208
+ error: {
209
+ code: String(response.status),
210
+ message: error.message ?? response.statusText,
211
+ status: response.status
212
+ }
213
+ };
214
+ }
215
+ const data = new Uint8Array(await response.arrayBuffer());
216
+ return { ok: true, data, status: response.status };
217
+ } catch (err) {
218
+ return {
219
+ ok: false,
220
+ error: {
221
+ code: "NETWORK_ERROR",
222
+ message: err instanceof Error ? err.message : "Network error"
223
+ }
224
+ };
225
+ }
226
+ };
227
+ var getNodeMetadata = async (baseUrl, realm, accessTokenBase64, nodeKey, indexPath) => {
228
+ return fetchWithAuth(
229
+ `${baseUrl}/api/realm/${encodeURIComponent(realm)}/nodes/${encodeURIComponent(nodeKey)}/metadata`,
230
+ `Bearer ${accessTokenBase64}`,
231
+ {
232
+ headers: {
233
+ "X-CAS-Index-Path": indexPath
234
+ }
235
+ }
236
+ );
237
+ };
238
+ var prepareNodes = async (baseUrl, realm, accessTokenBase64, params) => {
239
+ return fetchWithAuth(
240
+ `${baseUrl}/api/realm/${encodeURIComponent(realm)}/nodes/prepare`,
241
+ `Bearer ${accessTokenBase64}`,
242
+ {
243
+ method: "POST",
244
+ body: params
245
+ }
246
+ );
247
+ };
248
+ var putNode = async (baseUrl, realm, accessTokenBase64, nodeKey, content) => {
249
+ const url = `${baseUrl}/api/realm/${encodeURIComponent(realm)}/nodes/${encodeURIComponent(nodeKey)}`;
250
+ try {
251
+ const response = await fetch(url, {
252
+ method: "PUT",
253
+ headers: {
254
+ Authorization: `Bearer ${accessTokenBase64}`,
255
+ "Content-Type": "application/octet-stream"
256
+ },
257
+ body: content
258
+ });
259
+ if (!response.ok) {
260
+ const error = await response.json().catch(() => ({ message: response.statusText }));
261
+ return {
262
+ ok: false,
263
+ error: {
264
+ code: String(response.status),
265
+ message: error.message ?? response.statusText,
266
+ status: response.status
267
+ }
268
+ };
269
+ }
270
+ const data = await response.json();
271
+ return { ok: true, data, status: response.status };
272
+ } catch (err) {
273
+ return {
274
+ ok: false,
275
+ error: {
276
+ code: "NETWORK_ERROR",
277
+ message: err instanceof Error ? err.message : "Network error"
278
+ }
279
+ };
280
+ }
281
+ };
282
+
283
+ // src/api/oauth.ts
284
+ var getOAuthConfig = async (baseUrl) => {
285
+ return fetchApi(`${baseUrl}/api/oauth/config`);
286
+ };
287
+ var exchangeCode = async (baseUrl, params) => {
288
+ return fetchApi(`${baseUrl}/api/oauth/token`, {
289
+ method: "POST",
290
+ body: params
291
+ });
292
+ };
293
+ var login = async (baseUrl, params) => {
294
+ return fetchApi(`${baseUrl}/api/oauth/login`, {
295
+ method: "POST",
296
+ body: params
297
+ });
298
+ };
299
+ var refresh = async (baseUrl, params) => {
300
+ return fetchApi(`${baseUrl}/api/oauth/refresh`, {
301
+ method: "POST",
302
+ body: params
303
+ });
304
+ };
305
+ var getMe = async (baseUrl, userAccessToken) => {
306
+ return fetchWithAuth(`${baseUrl}/api/oauth/me`, `Bearer ${userAccessToken}`);
307
+ };
308
+ var tokenResponseToStoredUserToken = (response, userId) => ({
309
+ accessToken: response.accessToken,
310
+ refreshToken: response.refreshToken,
311
+ userId,
312
+ expiresAt: Date.now() + response.expiresIn * 1e3
313
+ });
314
+
315
+ // src/api/requests.ts
316
+ var createAuthRequest = async (baseUrl, params) => {
317
+ return fetchApi(`${baseUrl}/api/tokens/requests`, {
318
+ method: "POST",
319
+ body: params
320
+ });
321
+ };
322
+ var pollAuthRequest = async (baseUrl, requestId) => {
323
+ return fetchApi(
324
+ `${baseUrl}/api/tokens/requests/${encodeURIComponent(requestId)}/poll`
325
+ );
326
+ };
327
+ var getAuthRequest = async (baseUrl, userAccessToken, requestId) => {
328
+ return fetchWithAuth(
329
+ `${baseUrl}/api/tokens/requests/${encodeURIComponent(requestId)}`,
330
+ `Bearer ${userAccessToken}`
331
+ );
332
+ };
333
+ var approveAuthRequest = async (baseUrl, userAccessToken, requestId, params) => {
334
+ return fetchWithAuth(
335
+ `${baseUrl}/api/tokens/requests/${encodeURIComponent(requestId)}/approve`,
336
+ `Bearer ${userAccessToken}`,
337
+ {
338
+ method: "POST",
339
+ body: params ?? {}
340
+ }
341
+ );
342
+ };
343
+ var rejectAuthRequest = async (baseUrl, userAccessToken, requestId, params) => {
344
+ return fetchWithAuth(
345
+ `${baseUrl}/api/tokens/requests/${encodeURIComponent(requestId)}/reject`,
346
+ `Bearer ${userAccessToken}`,
347
+ {
348
+ method: "POST",
349
+ body: params ?? {}
350
+ }
351
+ );
352
+ };
353
+
354
+ // src/api/tickets.ts
355
+ var createTicket = async (baseUrl, realm, accessTokenBase64, params) => {
356
+ return fetchWithAuth(
357
+ `${baseUrl}/api/realm/${encodeURIComponent(realm)}/tickets`,
358
+ `Bearer ${accessTokenBase64}`,
359
+ {
360
+ method: "POST",
361
+ body: params
362
+ }
363
+ );
364
+ };
365
+ var listTickets = async (baseUrl, realm, accessTokenBase64, params) => {
366
+ const query = new URLSearchParams();
367
+ if (params?.limit) query.set("limit", String(params.limit));
368
+ if (params?.cursor) query.set("cursor", params.cursor);
369
+ if (params?.status) query.set("status", params.status);
370
+ const queryString = query.toString();
371
+ const url = `${baseUrl}/api/realm/${encodeURIComponent(realm)}/tickets${queryString ? `?${queryString}` : ""}`;
372
+ return fetchWithAuth(url, `Bearer ${accessTokenBase64}`);
373
+ };
374
+ var getTicket = async (baseUrl, realm, accessTokenBase64, ticketId) => {
375
+ return fetchWithAuth(
376
+ `${baseUrl}/api/realm/${encodeURIComponent(realm)}/tickets/${encodeURIComponent(ticketId)}`,
377
+ `Bearer ${accessTokenBase64}`
378
+ );
379
+ };
380
+ var submitTicket = async (baseUrl, realm, accessTokenBase64, ticketId, params) => {
381
+ return fetchWithAuth(
382
+ `${baseUrl}/api/realm/${encodeURIComponent(realm)}/tickets/${encodeURIComponent(ticketId)}/submit`,
383
+ `Bearer ${accessTokenBase64}`,
384
+ {
385
+ method: "POST",
386
+ body: params
387
+ }
388
+ );
389
+ };
390
+
391
+ // src/api/tokens.ts
392
+ var createToken = async (baseUrl, userAccessToken, params) => {
393
+ return fetchWithAuth(`${baseUrl}/api/tokens`, `Bearer ${userAccessToken}`, {
394
+ method: "POST",
395
+ body: params
396
+ });
397
+ };
398
+ var listTokens = async (baseUrl, userAccessToken, params) => {
399
+ const query = new URLSearchParams();
400
+ if (params?.limit) query.set("limit", String(params.limit));
401
+ if (params?.cursor) query.set("cursor", params.cursor);
402
+ if (params?.type) query.set("type", params.type);
403
+ const queryString = query.toString();
404
+ const url = `${baseUrl}/api/tokens${queryString ? `?${queryString}` : ""}`;
405
+ return fetchWithAuth(url, `Bearer ${userAccessToken}`);
406
+ };
407
+ var getToken = async (baseUrl, userAccessToken, tokenId) => {
408
+ return fetchWithAuth(
409
+ `${baseUrl}/api/tokens/${encodeURIComponent(tokenId)}`,
410
+ `Bearer ${userAccessToken}`
411
+ );
412
+ };
413
+ var revokeToken = async (baseUrl, userAccessToken, tokenId) => {
414
+ return fetchWithAuth(
415
+ `${baseUrl}/api/tokens/${encodeURIComponent(tokenId)}/revoke`,
416
+ `Bearer ${userAccessToken}`,
417
+ { method: "POST" }
418
+ );
419
+ };
420
+ var delegateToken = async (baseUrl, delegateTokenBase64, params) => {
421
+ return fetchWithAuth(
422
+ `${baseUrl}/api/tokens/delegate`,
423
+ `Bearer ${delegateTokenBase64}`,
424
+ {
425
+ method: "POST",
426
+ body: params
427
+ }
428
+ );
429
+ };
430
+
431
+ // src/store/token-checks.ts
432
+ var DEFAULT_EXPIRY_BUFFER_MS = 6e4;
433
+ var isTokenValid = (token, bufferMs = DEFAULT_EXPIRY_BUFFER_MS) => {
434
+ if (!token) return false;
435
+ return Date.now() + bufferMs < token.expiresAt;
436
+ };
437
+ var isTokenExpiringSoon = (token, windowMs = 5 * 6e4) => {
438
+ if (!token) return false;
439
+ return Date.now() + windowMs >= token.expiresAt;
440
+ };
441
+ var isUserTokenValid = (userToken, bufferMs) => {
442
+ return isTokenValid(userToken, bufferMs);
443
+ };
444
+ var isDelegateTokenValid = (delegateToken3, bufferMs) => {
445
+ return isTokenValid(delegateToken3, bufferMs);
446
+ };
447
+ var isAccessTokenValid = (accessToken, bufferMs) => {
448
+ return isTokenValid(accessToken, bufferMs);
449
+ };
450
+ var getMaxIssuerId = (state) => {
451
+ if (state.user && isUserTokenValid(state.user)) {
452
+ return state.user.userId;
453
+ }
454
+ if (state.delegate && isDelegateTokenValid(state.delegate)) {
455
+ return state.delegate.tokenId;
456
+ }
457
+ return null;
458
+ };
459
+ var isAccessTokenFromMaxIssuer = (state) => {
460
+ const accessToken = state.access;
461
+ if (!accessToken || !isAccessTokenValid(accessToken)) {
462
+ return false;
463
+ }
464
+ const maxIssuerId = getMaxIssuerId(state);
465
+ if (!maxIssuerId) {
466
+ return true;
467
+ }
468
+ return accessToken.issuerId === maxIssuerId;
469
+ };
470
+ var isDelegateTokenFromCurrentUser = (state) => {
471
+ const delegateToken3 = state.delegate;
472
+ const userToken = state.user;
473
+ if (!delegateToken3 || !isDelegateTokenValid(delegateToken3)) {
474
+ return false;
475
+ }
476
+ if (!userToken || !isUserTokenValid(userToken)) {
477
+ return true;
478
+ }
479
+ return delegateToken3.issuerId === userToken.userId;
480
+ };
481
+ var shouldReissueAccessToken = (state) => {
482
+ if (!isAccessTokenValid(state.access)) {
483
+ return true;
484
+ }
485
+ if (!isAccessTokenFromMaxIssuer(state)) {
486
+ return true;
487
+ }
488
+ return false;
489
+ };
490
+ var shouldReissueDelegateToken = (state) => {
491
+ if (!isDelegateTokenValid(state.delegate)) {
492
+ return true;
493
+ }
494
+ if (state.user && !isDelegateTokenFromCurrentUser(state)) {
495
+ return true;
496
+ }
497
+ return false;
498
+ };
499
+
500
+ // src/store/jwt-refresh.ts
501
+ var callRefreshApi = async (baseUrl, refreshToken) => {
502
+ try {
503
+ const response = await fetch(`${baseUrl}/api/oauth/refresh`, {
504
+ method: "POST",
505
+ headers: { "Content-Type": "application/json" },
506
+ body: JSON.stringify({ refreshToken })
507
+ });
508
+ if (!response.ok) {
509
+ return null;
510
+ }
511
+ return await response.json();
512
+ } catch {
513
+ return null;
514
+ }
515
+ };
516
+ var createRefreshManager = (config) => {
517
+ const { store, baseUrl, onAuthRequired } = config;
518
+ let refreshPromise = null;
519
+ let refreshTimer = null;
520
+ const doRefresh = async () => {
521
+ const state = store.getState();
522
+ const userToken = state.user;
523
+ if (!userToken?.refreshToken) {
524
+ onAuthRequired?.();
525
+ return null;
526
+ }
527
+ const result = await callRefreshApi(baseUrl, userToken.refreshToken);
528
+ if (!result) {
529
+ store.setUser(null);
530
+ onAuthRequired?.();
531
+ return null;
532
+ }
533
+ const newUserToken = {
534
+ accessToken: result.accessToken,
535
+ refreshToken: result.refreshToken ?? userToken.refreshToken,
536
+ userId: userToken.userId,
537
+ expiresAt: Date.now() + result.expiresIn * 1e3
538
+ };
539
+ store.setUser(newUserToken);
540
+ return newUserToken;
541
+ };
542
+ const ensureValidUserToken = async () => {
543
+ const state = store.getState();
544
+ const userToken = state.user;
545
+ if (isUserTokenValid(userToken)) {
546
+ return userToken;
547
+ }
548
+ if (!userToken) {
549
+ return null;
550
+ }
551
+ if (!refreshPromise) {
552
+ refreshPromise = doRefresh().finally(() => {
553
+ refreshPromise = null;
554
+ });
555
+ }
556
+ return refreshPromise;
557
+ };
558
+ const scheduleProactiveRefresh = () => {
559
+ cancelScheduledRefresh();
560
+ const state = store.getState();
561
+ const userToken = state.user;
562
+ if (!userToken) return;
563
+ const refreshTime = userToken.expiresAt - Date.now() - 5 * 6e4;
564
+ if (refreshTime <= 0) {
565
+ ensureValidUserToken();
566
+ return;
567
+ }
568
+ refreshTimer = setTimeout(() => {
569
+ ensureValidUserToken().then((newToken) => {
570
+ if (newToken) {
571
+ scheduleProactiveRefresh();
572
+ }
573
+ });
574
+ }, refreshTime);
575
+ };
576
+ const cancelScheduledRefresh = () => {
577
+ if (refreshTimer) {
578
+ clearTimeout(refreshTimer);
579
+ refreshTimer = null;
580
+ }
581
+ };
582
+ return {
583
+ ensureValidUserToken,
584
+ scheduleProactiveRefresh,
585
+ cancelScheduledRefresh
586
+ };
587
+ };
588
+
589
+ // src/store/token-selector.ts
590
+ var issueTokenWithUserJwt = async (baseUrl, userAccessToken, request) => {
591
+ try {
592
+ const response = await fetch(`${baseUrl}/api/tokens`, {
593
+ method: "POST",
594
+ headers: {
595
+ "Content-Type": "application/json",
596
+ Authorization: `Bearer ${userAccessToken}`
597
+ },
598
+ body: JSON.stringify(request)
599
+ });
600
+ if (!response.ok) {
601
+ console.error("[TokenSelector] Failed to issue token with User JWT:", response.status);
602
+ return null;
603
+ }
604
+ return await response.json();
605
+ } catch (err) {
606
+ console.error("[TokenSelector] Error issuing token with User JWT:", err);
607
+ return null;
608
+ }
609
+ };
610
+ var delegateToken2 = async (baseUrl, delegateTokenBase64, request) => {
611
+ try {
612
+ const response = await fetch(`${baseUrl}/api/tokens/delegate`, {
613
+ method: "POST",
614
+ headers: {
615
+ "Content-Type": "application/json",
616
+ Authorization: `Bearer ${delegateTokenBase64}`
617
+ },
618
+ body: JSON.stringify(request)
619
+ });
620
+ if (!response.ok) {
621
+ console.error("[TokenSelector] Failed to delegate token:", response.status);
622
+ return null;
623
+ }
624
+ return await response.json();
625
+ } catch (err) {
626
+ console.error("[TokenSelector] Error delegating token:", err);
627
+ return null;
628
+ }
629
+ };
630
+ var createTokenSelector = (config) => {
631
+ const { store, baseUrl, realm, serverInfo, defaultTokenTtl } = config;
632
+ const getTokenTtl = (type) => {
633
+ if (defaultTokenTtl) return defaultTokenTtl;
634
+ if (serverInfo?.limits) {
635
+ if (type === "delegate" && serverInfo.limits.maxDelegateTokenTtl) {
636
+ return serverInfo.limits.maxDelegateTokenTtl;
637
+ }
638
+ if (type === "access" && serverInfo.limits.maxAccessTokenTtl) {
639
+ return serverInfo.limits.maxAccessTokenTtl;
640
+ }
641
+ }
642
+ return type === "access" ? 3600 : 30 * 24 * 3600;
643
+ };
644
+ const ensureAccessToken = async () => {
645
+ const state = store.getState();
646
+ if (!shouldReissueAccessToken(state)) {
647
+ return state.access;
648
+ }
649
+ const userToken = state.user;
650
+ const delegateToken_ = state.delegate;
651
+ if (isUserTokenValid(userToken)) {
652
+ const result = await issueTokenWithUserJwt(baseUrl, userToken.accessToken, {
653
+ realm,
654
+ name: "auto-issued-access",
655
+ type: "access",
656
+ expiresIn: getTokenTtl("access"),
657
+ canUpload: true,
658
+ canManageDepot: true
659
+ });
660
+ if (result) {
661
+ const newToken = {
662
+ tokenId: result.tokenId,
663
+ tokenBase64: result.tokenBase64,
664
+ type: "access",
665
+ issuerId: result.issuerId,
666
+ expiresAt: result.expiresAt,
667
+ canUpload: result.canUpload,
668
+ canManageDepot: result.canManageDepot
669
+ };
670
+ store.setAccess(newToken);
671
+ return newToken;
672
+ }
673
+ }
674
+ if (isDelegateTokenValid(delegateToken_)) {
675
+ const result = await delegateToken2(baseUrl, delegateToken_.tokenBase64, {
676
+ name: "auto-issued-access",
677
+ type: "access",
678
+ expiresIn: getTokenTtl("access"),
679
+ canUpload: delegateToken_.canUpload,
680
+ canManageDepot: delegateToken_.canManageDepot
681
+ });
682
+ if (result) {
683
+ const newToken = {
684
+ tokenId: result.tokenId,
685
+ tokenBase64: result.tokenBase64,
686
+ type: "access",
687
+ issuerId: result.issuerId,
688
+ expiresAt: result.expiresAt,
689
+ canUpload: result.canUpload,
690
+ canManageDepot: result.canManageDepot
691
+ };
692
+ store.setAccess(newToken);
693
+ return newToken;
694
+ }
695
+ }
696
+ return null;
697
+ };
698
+ const ensureDelegateToken = async () => {
699
+ const state = store.getState();
700
+ if (isDelegateTokenValid(state.delegate)) {
701
+ return state.delegate;
702
+ }
703
+ const userToken = state.user;
704
+ if (!isUserTokenValid(userToken)) {
705
+ return null;
706
+ }
707
+ const result = await issueTokenWithUserJwt(baseUrl, userToken.accessToken, {
708
+ realm,
709
+ name: "auto-issued-delegate",
710
+ type: "delegate",
711
+ expiresIn: getTokenTtl("delegate"),
712
+ canUpload: true,
713
+ canManageDepot: true
714
+ });
715
+ if (result) {
716
+ const newToken = {
717
+ tokenId: result.tokenId,
718
+ tokenBase64: result.tokenBase64,
719
+ type: "delegate",
720
+ issuerId: result.issuerId,
721
+ expiresAt: result.expiresAt,
722
+ canUpload: result.canUpload,
723
+ canManageDepot: result.canManageDepot
724
+ };
725
+ store.setDelegate(newToken);
726
+ return newToken;
727
+ }
728
+ return null;
729
+ };
730
+ return {
731
+ ensureAccessToken,
732
+ ensureDelegateToken
733
+ };
734
+ };
735
+
736
+ // src/types/tokens.ts
737
+ var emptyTokenState = () => ({
738
+ user: null,
739
+ delegate: null,
740
+ access: null
741
+ });
742
+
743
+ // src/store/token-store.ts
744
+ var createTokenStore = (config = {}) => {
745
+ const { storage, onTokenChange } = config;
746
+ let state = emptyTokenState();
747
+ const notifyAndPersist = () => {
748
+ onTokenChange?.(state);
749
+ storage?.save(state).catch((err) => {
750
+ console.error("[TokenStore] Failed to persist state:", err);
751
+ });
752
+ };
753
+ return {
754
+ getState: () => ({ ...state }),
755
+ setUser: (token) => {
756
+ state = { ...state, user: token };
757
+ notifyAndPersist();
758
+ },
759
+ setDelegate: (token) => {
760
+ state = { ...state, delegate: token };
761
+ notifyAndPersist();
762
+ },
763
+ setAccess: (token) => {
764
+ state = { ...state, access: token };
765
+ notifyAndPersist();
766
+ },
767
+ clear: () => {
768
+ state = emptyTokenState();
769
+ notifyAndPersist();
770
+ storage?.clear().catch((err) => {
771
+ console.error("[TokenStore] Failed to clear storage:", err);
772
+ });
773
+ },
774
+ initialize: async () => {
775
+ if (!storage) return;
776
+ try {
777
+ const loaded = await storage.load();
778
+ if (loaded) {
779
+ state = loaded;
780
+ }
781
+ } catch (err) {
782
+ console.error("[TokenStore] Failed to load from storage:", err);
783
+ }
784
+ }
785
+ };
786
+ };
787
+
788
+ // src/client/helpers.ts
789
+ var ERRORS = {
790
+ USER_REQUIRED: { code: "UNAUTHORIZED", message: "User login required" },
791
+ DELEGATE_REQUIRED: { code: "FORBIDDEN", message: "Delegate token required" },
792
+ ACCESS_REQUIRED: { code: "FORBIDDEN", message: "Access token required" }
793
+ };
794
+ var withToken = (getToken2, error) => {
795
+ return (fn) => getToken2().then(
796
+ (token) => token ? fn(token) : Promise.resolve({ ok: false, error })
797
+ );
798
+ };
799
+ var withUserToken = (getToken2) => withToken(getToken2, ERRORS.USER_REQUIRED);
800
+ var withDelegateToken = (getToken2) => withToken(getToken2, ERRORS.DELEGATE_REQUIRED);
801
+ var withAccessToken = (getToken2) => withToken(getToken2, ERRORS.ACCESS_REQUIRED);
802
+
803
+ // src/client/depots.ts
804
+ var createDepotMethods = ({ baseUrl, realm, tokenSelector }) => {
805
+ const requireAccess = withAccessToken(() => tokenSelector.ensureAccessToken());
806
+ return {
807
+ create: (params) => requireAccess((t) => createDepot(baseUrl, realm, t.tokenBase64, params)),
808
+ list: (params) => requireAccess((t) => listDepots(baseUrl, realm, t.tokenBase64, params)),
809
+ get: (depotId) => requireAccess((t) => getDepot(baseUrl, realm, t.tokenBase64, depotId)),
810
+ update: (depotId, params) => requireAccess((t) => updateDepot(baseUrl, realm, t.tokenBase64, depotId, params)),
811
+ delete: (depotId) => requireAccess((t) => deleteDepot(baseUrl, realm, t.tokenBase64, depotId)),
812
+ commit: (depotId, params) => requireAccess((t) => commitDepot(baseUrl, realm, t.tokenBase64, depotId, params))
813
+ };
814
+ };
815
+
816
+ // src/client/nodes.ts
817
+ var createNodeMethods = ({ baseUrl, realm, tokenSelector }) => {
818
+ const requireAccess = withAccessToken(() => tokenSelector.ensureAccessToken());
819
+ return {
820
+ get: (nodeKey, indexPath) => requireAccess((t) => getNode(baseUrl, realm, t.tokenBase64, nodeKey, indexPath)),
821
+ getMetadata: (nodeKey, indexPath) => requireAccess((t) => getNodeMetadata(baseUrl, realm, t.tokenBase64, nodeKey, indexPath)),
822
+ prepare: (params) => requireAccess((t) => prepareNodes(baseUrl, realm, t.tokenBase64, params)),
823
+ put: (nodeKey, content) => requireAccess((t) => putNode(baseUrl, realm, t.tokenBase64, nodeKey, content))
824
+ };
825
+ };
826
+
827
+ // src/client/oauth.ts
828
+ var createOAuthMethods = ({
829
+ baseUrl,
830
+ store,
831
+ refreshManager
832
+ }) => ({
833
+ getConfig: () => getOAuthConfig(baseUrl),
834
+ login: async (email, password) => {
835
+ const result = await login(baseUrl, { email, password });
836
+ if (!result.ok) return result;
837
+ const meResult = await getMe(baseUrl, result.data.accessToken);
838
+ if (!meResult.ok) return meResult;
839
+ store.setUser(tokenResponseToStoredUserToken(result.data, meResult.data.userId));
840
+ refreshManager.scheduleProactiveRefresh();
841
+ return meResult;
842
+ },
843
+ exchangeCode: async (code, redirectUri, codeVerifier) => {
844
+ const result = await exchangeCode(baseUrl, {
845
+ code,
846
+ redirect_uri: redirectUri,
847
+ code_verifier: codeVerifier
848
+ });
849
+ if (!result.ok) return result;
850
+ const meResult = await getMe(baseUrl, result.data.accessToken);
851
+ if (!meResult.ok) return meResult;
852
+ store.setUser(tokenResponseToStoredUserToken(result.data, meResult.data.userId));
853
+ refreshManager.scheduleProactiveRefresh();
854
+ return meResult;
855
+ },
856
+ getMe: async () => {
857
+ const user = await refreshManager.ensureValidUserToken();
858
+ if (!user) {
859
+ return { ok: false, error: ERRORS.USER_REQUIRED };
860
+ }
861
+ return getMe(baseUrl, user.accessToken);
862
+ }
863
+ });
864
+
865
+ // src/client/tickets.ts
866
+ var createTicketMethods = ({
867
+ baseUrl,
868
+ realm,
869
+ tokenSelector
870
+ }) => {
871
+ const requireAccess = withAccessToken(() => tokenSelector.ensureAccessToken());
872
+ return {
873
+ create: (params) => requireAccess((access) => createTicket(baseUrl, realm, access.tokenBase64, params)),
874
+ list: (params) => requireAccess((access) => listTickets(baseUrl, realm, access.tokenBase64, params)),
875
+ get: (ticketId) => requireAccess((access) => getTicket(baseUrl, realm, access.tokenBase64, ticketId)),
876
+ submit: (ticketId, params) => requireAccess(
877
+ (access) => submitTicket(baseUrl, realm, access.tokenBase64, ticketId, params)
878
+ )
879
+ };
880
+ };
881
+
882
+ // src/client/tokens.ts
883
+ var createTokenMethods = ({
884
+ baseUrl,
885
+ realm,
886
+ store,
887
+ refreshManager,
888
+ tokenSelector
889
+ }) => {
890
+ const requireUser = withUserToken(() => refreshManager.ensureValidUserToken());
891
+ const requireDelegate = withDelegateToken(() => tokenSelector.ensureDelegateToken());
892
+ return {
893
+ create: (params) => requireUser(async (user) => {
894
+ const result = await createToken(baseUrl, user.accessToken, params);
895
+ if (!result.ok) return result;
896
+ const newToken = toStoredToken(result.data);
897
+ if (params.realm === realm) {
898
+ if (params.type === "delegate") {
899
+ store.setDelegate(newToken);
900
+ } else {
901
+ store.setAccess(newToken);
902
+ }
903
+ }
904
+ return { ok: true, data: newToken, status: result.status };
905
+ }),
906
+ list: (params) => requireUser((user) => listTokens(baseUrl, user.accessToken, params)),
907
+ revoke: (tokenId) => requireUser(async (user) => {
908
+ const result = await revokeToken(baseUrl, user.accessToken, tokenId);
909
+ if (!result.ok) return { ok: false, error: result.error };
910
+ const state = store.getState();
911
+ if (state.delegate?.tokenId === tokenId) store.setDelegate(null);
912
+ if (state.access?.tokenId === tokenId) store.setAccess(null);
913
+ return { ok: true, data: void 0, status: result.status };
914
+ }),
915
+ delegate: (params) => requireDelegate(async (delegate) => {
916
+ const result = await delegateToken(baseUrl, delegate.tokenBase64, params);
917
+ if (!result.ok) return result;
918
+ return { ok: true, data: toStoredToken(result.data), status: result.status };
919
+ })
920
+ };
921
+ };
922
+ var toStoredToken = (response) => ({
923
+ tokenId: response.tokenId,
924
+ tokenBase64: response.tokenBase64,
925
+ type: response.type ?? "delegate",
926
+ issuerId: response.issuerId ?? "",
927
+ expiresAt: response.expiresAt,
928
+ canUpload: response.canUpload ?? false,
929
+ canManageDepot: response.canManageDepot ?? false
930
+ });
931
+
932
+ // src/client/index.ts
933
+ var createClient = async (config) => {
934
+ const { baseUrl, realm, tokenStorage, onTokenChange, onAuthRequired, defaultTokenTtl } = config;
935
+ const store = createTokenStore({
936
+ storage: tokenStorage,
937
+ onTokenChange,
938
+ onAuthRequired
939
+ });
940
+ await store.initialize();
941
+ const refreshManager = createRefreshManager({
942
+ store,
943
+ baseUrl,
944
+ onAuthRequired
945
+ });
946
+ let serverInfo = null;
947
+ const infoResult = await fetchServiceInfo(baseUrl);
948
+ if (infoResult.ok) {
949
+ serverInfo = infoResult.data;
950
+ }
951
+ const tokenSelector = createTokenSelector({
952
+ store,
953
+ baseUrl,
954
+ realm,
955
+ serverInfo,
956
+ defaultTokenTtl
957
+ });
958
+ const deps = { baseUrl, realm, store, refreshManager, tokenSelector };
959
+ return {
960
+ getState: () => store.getState(),
961
+ getServerInfo: () => serverInfo,
962
+ setDelegateToken: (token) => store.setDelegate(token),
963
+ setAccessToken: (token) => store.setAccess(token),
964
+ logout: () => {
965
+ refreshManager.cancelScheduledRefresh();
966
+ store.clear();
967
+ },
968
+ oauth: createOAuthMethods(deps),
969
+ tokens: createTokenMethods(deps),
970
+ tickets: createTicketMethods(deps),
971
+ depots: createDepotMethods(deps),
972
+ nodes: createNodeMethods(deps)
973
+ };
974
+ };
975
+ export {
976
+ DEFAULT_EXPIRY_BUFFER_MS,
977
+ api_exports as api,
978
+ createClient,
979
+ createRefreshManager,
980
+ createTokenSelector,
981
+ createTokenStore,
982
+ emptyTokenState,
983
+ getMaxIssuerId,
984
+ isAccessTokenFromMaxIssuer,
985
+ isAccessTokenValid,
986
+ isDelegateTokenFromCurrentUser,
987
+ isDelegateTokenValid,
988
+ isTokenExpiringSoon,
989
+ isTokenValid,
990
+ isUserTokenValid,
991
+ shouldReissueAccessToken,
992
+ shouldReissueDelegateToken
993
+ };
994
+ //# sourceMappingURL=index.js.map