@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/README.md +201 -0
- package/README.zh-CN.md +201 -0
- package/dist/api/index.d.ts +3 -0
- package/dist/api/index.js +421 -0
- package/dist/api/index.js.map +1 -0
- package/dist/index-cPO-6GxE.d.ts +338 -0
- package/dist/index.d.ts +298 -0
- package/dist/index.js +994 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +144 -0
- package/dist/types/index.js +10 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/index.ts","../src/utils/http.ts","../src/api/depots.ts","../src/api/info.ts","../src/api/nodes.ts","../src/api/oauth.ts","../src/api/requests.ts","../src/api/tickets.ts","../src/api/tokens.ts","../src/store/token-checks.ts","../src/store/jwt-refresh.ts","../src/store/token-selector.ts","../src/types/tokens.ts","../src/store/token-store.ts","../src/client/helpers.ts","../src/client/depots.ts","../src/client/nodes.ts","../src/client/oauth.ts","../src/client/tickets.ts","../src/client/tokens.ts","../src/client/index.ts"],"sourcesContent":["/**\n * API module exports for @casfa/client\n */\n\n// Depot API\nexport {\n type CommitDepotResponse,\n commitDepot,\n createDepot,\n deleteDepot,\n getDepot,\n type ListDepotsResponse,\n listDepots,\n updateDepot,\n} from \"./depots.ts\";\n// Info API\nexport { fetchServiceInfo, healthCheck } from \"./info.ts\";\n// Node API\nexport { getNode, getNodeMetadata, type NodeUploadResult, prepareNodes, putNode } from \"./nodes.ts\";\n// OAuth API\nexport {\n type CognitoConfig,\n exchangeCode,\n getMe,\n getOAuthConfig,\n login,\n refresh,\n type TokenResponse,\n tokenResponseToStoredUserToken,\n type UserInfo,\n} from \"./oauth.ts\";\n// Client Authorization Request API\nexport {\n approveAuthRequest,\n createAuthRequest,\n getAuthRequest,\n pollAuthRequest,\n rejectAuthRequest,\n} from \"./requests.ts\";\n// Ticket API\nexport {\n createTicket,\n getTicket,\n type ListTicketsResponse,\n listTickets,\n type SubmitTicketResponse,\n submitTicket,\n} from \"./tickets.ts\";\n// Token management API\nexport {\n createToken,\n type DelegateTokenParams,\n delegateToken,\n getToken,\n type ListTokensParams,\n type ListTokensResponse,\n listTokens,\n revokeToken,\n} from \"./tokens.ts\";\n","/**\n * Fetch utilities for the stateful client.\n */\n\nimport type { ClientError, FetchResult } from \"../types/client.ts\";\n\n// ============================================================================\n// Error Handling\n// ============================================================================\n\n/**\n * Map HTTP status to error code.\n */\nexport const statusToErrorCode = (status: number): string => {\n switch (status) {\n case 401:\n return \"UNAUTHORIZED\";\n case 403:\n return \"FORBIDDEN\";\n case 404:\n return \"NOT_FOUND\";\n case 409:\n return \"CONFLICT\";\n case 400:\n case 422:\n return \"VALIDATION_ERROR\";\n case 429:\n return \"RATE_LIMITED\";\n default:\n return \"UNKNOWN\";\n }\n};\n\n/**\n * Create error from HTTP response.\n */\nexport const createErrorFromResponse = async (response: Response): Promise<ClientError> => {\n const code = statusToErrorCode(response.status);\n let message = response.statusText;\n let details: unknown;\n\n try {\n const body = (await response.json()) as Record<string, unknown>;\n if (typeof body.message === \"string\") {\n message = body.message;\n }\n if (typeof body.error === \"string\") {\n message = body.error;\n }\n details = body;\n } catch {\n // Response body is not JSON, use status text\n }\n\n return { code, message, status: response.status, details };\n};\n\n/**\n * Create a network error.\n */\nexport const createNetworkError = (err: unknown): ClientError => ({\n code: \"NETWORK_ERROR\",\n message: err instanceof Error ? err.message : \"Network request failed\",\n details: err,\n});\n\n// ============================================================================\n// Fetch Function\n// ============================================================================\n\nexport type FetchOptions = {\n method?: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n headers?: Record<string, string>;\n body?: unknown;\n /** Expected response type */\n responseType?: \"json\" | \"blob\" | \"text\" | \"none\";\n};\n\n/**\n * Make a fetch request with error handling.\n */\nexport const fetchApi = async <T>(\n url: string,\n options: FetchOptions = {}\n): Promise<FetchResult<T>> => {\n const { method = \"GET\", headers = {}, body, responseType = \"json\" } = options;\n\n const requestHeaders: Record<string, string> = { ...headers };\n\n // Add content-type for JSON body\n if (body !== undefined && !requestHeaders[\"Content-Type\"]) {\n requestHeaders[\"Content-Type\"] = \"application/json\";\n }\n\n try {\n const response = await fetch(url, {\n method,\n headers: requestHeaders,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const error = await createErrorFromResponse(response);\n return { ok: false, error };\n }\n\n // Parse response based on type\n let data: T;\n switch (responseType) {\n case \"json\":\n data = (await response.json()) as T;\n break;\n case \"blob\":\n data = (await response.blob()) as T;\n break;\n case \"text\":\n data = (await response.text()) as T;\n break;\n case \"none\":\n data = undefined as T;\n break;\n }\n\n return { ok: true, data, status: response.status };\n } catch (err) {\n return { ok: false, error: createNetworkError(err) };\n }\n};\n\n/**\n * Make an authenticated fetch request.\n */\nexport const fetchWithAuth = async <T>(\n url: string,\n authHeader: string | null,\n options: FetchOptions = {}\n): Promise<FetchResult<T>> => {\n const headers = { ...options.headers };\n\n if (authHeader) {\n headers.Authorization = authHeader;\n }\n\n return fetchApi<T>(url, { ...options, headers });\n};\n","/**\n * Depot API functions.\n *\n * Token Requirement:\n * - All depot operations require Access Token with canManageDepot permission.\n */\n\nimport type {\n CreateDepot,\n CreateDepotResponse,\n DepotCommit,\n DepotDetail,\n DepotListItem,\n ListDepotsQuery,\n UpdateDepot,\n} from \"@casfa/protocol\";\nimport type { FetchResult } from \"../types/client.ts\";\nimport { fetchWithAuth } from \"../utils/http.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ListDepotsResponse = {\n depots: DepotListItem[];\n nextCursor?: string;\n};\n\nexport type CommitDepotResponse = {\n depotId: string;\n root: string;\n updatedAt: number;\n};\n\n// ============================================================================\n// Access Token APIs\n// ============================================================================\n\n/**\n * Create a new depot.\n * Requires Access Token with canManageDepot.\n */\nexport const createDepot = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n params: CreateDepot\n): Promise<FetchResult<CreateDepotResponse>> => {\n return fetchWithAuth<CreateDepotResponse>(\n `${baseUrl}/api/realm/${encodeURIComponent(realm)}/depots`,\n `Bearer ${accessTokenBase64}`,\n {\n method: \"POST\",\n body: params,\n }\n );\n};\n\n/**\n * List depots.\n * Requires Access Token.\n */\nexport const listDepots = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n params?: ListDepotsQuery\n): Promise<FetchResult<ListDepotsResponse>> => {\n const query = new URLSearchParams();\n if (params?.limit) query.set(\"limit\", String(params.limit));\n if (params?.cursor) query.set(\"cursor\", params.cursor);\n\n const queryString = query.toString();\n const url = `${baseUrl}/api/realm/${encodeURIComponent(realm)}/depots${queryString ? `?${queryString}` : \"\"}`;\n\n return fetchWithAuth<ListDepotsResponse>(url, `Bearer ${accessTokenBase64}`);\n};\n\n/**\n * Get depot details.\n * Requires Access Token.\n */\nexport const getDepot = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n depotId: string\n): Promise<FetchResult<DepotDetail>> => {\n return fetchWithAuth<DepotDetail>(\n `${baseUrl}/api/realm/${encodeURIComponent(realm)}/depots/${encodeURIComponent(depotId)}`,\n `Bearer ${accessTokenBase64}`\n );\n};\n\n/**\n * Update depot metadata.\n * Requires Access Token with canManageDepot.\n */\nexport const updateDepot = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n depotId: string,\n params: UpdateDepot\n): Promise<FetchResult<DepotDetail>> => {\n return fetchWithAuth<DepotDetail>(\n `${baseUrl}/api/realm/${encodeURIComponent(realm)}/depots/${encodeURIComponent(depotId)}`,\n `Bearer ${accessTokenBase64}`,\n {\n method: \"PATCH\",\n body: params,\n }\n );\n};\n\n/**\n * Delete a depot.\n * Requires Access Token with canManageDepot.\n */\nexport const deleteDepot = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n depotId: string\n): Promise<FetchResult<void>> => {\n return fetchWithAuth<void>(\n `${baseUrl}/api/realm/${encodeURIComponent(realm)}/depots/${encodeURIComponent(depotId)}`,\n `Bearer ${accessTokenBase64}`,\n {\n method: \"DELETE\",\n responseType: \"none\",\n }\n );\n};\n\n/**\n * Commit new root to depot.\n * Requires Access Token with canManageDepot.\n */\nexport const commitDepot = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n depotId: string,\n params: DepotCommit\n): Promise<FetchResult<CommitDepotResponse>> => {\n return fetchWithAuth<CommitDepotResponse>(\n `${baseUrl}/api/realm/${encodeURIComponent(realm)}/depots/${encodeURIComponent(depotId)}/commit`,\n `Bearer ${accessTokenBase64}`,\n {\n method: \"POST\",\n body: params,\n }\n );\n};\n","/**\n * Service info API.\n */\n\nimport type { ServiceInfo } from \"@casfa/protocol\";\nimport type { FetchResult } from \"../types/client.ts\";\nimport { fetchApi } from \"../utils/http.ts\";\n\n/**\n * Fetch service info from /api/info.\n */\nexport const fetchServiceInfo = async (baseUrl: string): Promise<FetchResult<ServiceInfo>> => {\n return fetchApi<ServiceInfo>(`${baseUrl}/api/info`);\n};\n\n/**\n * Health check.\n */\nexport const healthCheck = async (baseUrl: string): Promise<FetchResult<{ status: string }>> => {\n return fetchApi<{ status: string }>(`${baseUrl}/api/health`);\n};\n","/**\n * Node API functions.\n *\n * Token Requirement:\n * - GET /nodes/:nodeKey: Access Token (with X-CAS-Index-Path header)\n * - POST /nodes/prepare: Access Token with canUpload\n * - PUT /nodes/:nodeKey: Access Token with canUpload\n */\n\nimport type { NodeMetadata, PrepareNodes, PrepareNodesResponse } from \"@casfa/protocol\";\nimport type { FetchResult } from \"../types/client.ts\";\nimport { fetchWithAuth } from \"../utils/http.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type NodeUploadResult = {\n nodeKey: string;\n status: \"created\" | \"exists\";\n};\n\n// ============================================================================\n// Access Token APIs\n// ============================================================================\n\n/**\n * Get node content.\n * Requires Access Token with scope covering the index path.\n *\n * @param indexPath - The CAS index path for scope verification (e.g., \"depot:MAIN:0:1\")\n */\nexport const getNode = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n nodeKey: string,\n indexPath: string\n): Promise<FetchResult<Uint8Array>> => {\n const url = `${baseUrl}/api/realm/${encodeURIComponent(realm)}/nodes/${encodeURIComponent(nodeKey)}`;\n\n try {\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${accessTokenBase64}`,\n \"X-CAS-Index-Path\": indexPath,\n },\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: response.statusText }));\n return {\n ok: false,\n error: {\n code: String(response.status),\n message: (error as { message?: string }).message ?? response.statusText,\n status: response.status,\n },\n };\n }\n\n const data = new Uint8Array(await response.arrayBuffer());\n return { ok: true, data, status: response.status };\n } catch (err) {\n return {\n ok: false,\n error: {\n code: \"NETWORK_ERROR\",\n message: err instanceof Error ? err.message : \"Network error\",\n },\n };\n }\n};\n\n/**\n * Get node metadata.\n * Requires Access Token with scope covering the index path.\n */\nexport const getNodeMetadata = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n nodeKey: string,\n indexPath: string\n): Promise<FetchResult<NodeMetadata>> => {\n return fetchWithAuth<NodeMetadata>(\n `${baseUrl}/api/realm/${encodeURIComponent(realm)}/nodes/${encodeURIComponent(nodeKey)}/metadata`,\n `Bearer ${accessTokenBase64}`,\n {\n headers: {\n \"X-CAS-Index-Path\": indexPath,\n },\n }\n );\n};\n\n/**\n * Prepare nodes for upload.\n * Returns which nodes need to be uploaded vs already exist.\n * Requires Access Token with canUpload.\n */\nexport const prepareNodes = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n params: PrepareNodes\n): Promise<FetchResult<PrepareNodesResponse>> => {\n return fetchWithAuth<PrepareNodesResponse>(\n `${baseUrl}/api/realm/${encodeURIComponent(realm)}/nodes/prepare`,\n `Bearer ${accessTokenBase64}`,\n {\n method: \"POST\",\n body: params,\n }\n );\n};\n\n/**\n * Upload a node.\n * Requires Access Token with canUpload.\n */\nexport const putNode = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n nodeKey: string,\n content: Uint8Array\n): Promise<FetchResult<NodeUploadResult>> => {\n const url = `${baseUrl}/api/realm/${encodeURIComponent(realm)}/nodes/${encodeURIComponent(nodeKey)}`;\n\n try {\n const response = await fetch(url, {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${accessTokenBase64}`,\n \"Content-Type\": \"application/octet-stream\",\n },\n body: content,\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: response.statusText }));\n return {\n ok: false,\n error: {\n code: String(response.status),\n message: (error as { message?: string }).message ?? response.statusText,\n status: response.status,\n },\n };\n }\n\n const data = (await response.json()) as NodeUploadResult;\n return { ok: true, data, status: response.status };\n } catch (err) {\n return {\n ok: false,\n error: {\n code: \"NETWORK_ERROR\",\n message: err instanceof Error ? err.message : \"Network error\",\n },\n };\n }\n};\n","/**\n * OAuth API functions.\n */\n\nimport type { Login, Refresh, TokenExchange } from \"@casfa/protocol\";\nimport type { FetchResult } from \"../types/client.ts\";\nimport type { StoredUserToken } from \"../types/tokens.ts\";\nimport { fetchApi, fetchWithAuth } from \"../utils/http.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type CognitoConfig = {\n region: string;\n userPoolId: string;\n clientId: string;\n domain: string;\n};\n\nexport type TokenResponse = {\n accessToken: string;\n refreshToken: string;\n expiresIn: number;\n idToken?: string;\n};\n\nexport type UserInfo = {\n userId: string;\n email: string;\n role: string;\n};\n\n// ============================================================================\n// Public OAuth API\n// ============================================================================\n\n/**\n * Get Cognito configuration.\n */\nexport const getOAuthConfig = async (baseUrl: string): Promise<FetchResult<CognitoConfig>> => {\n return fetchApi<CognitoConfig>(`${baseUrl}/api/oauth/config`);\n};\n\n/**\n * Exchange authorization code for tokens.\n */\nexport const exchangeCode = async (\n baseUrl: string,\n params: TokenExchange\n): Promise<FetchResult<TokenResponse>> => {\n return fetchApi<TokenResponse>(`${baseUrl}/api/oauth/token`, {\n method: \"POST\",\n body: params,\n });\n};\n\n/**\n * Login with email and password.\n */\nexport const login = async (\n baseUrl: string,\n params: Login\n): Promise<FetchResult<TokenResponse>> => {\n return fetchApi<TokenResponse>(`${baseUrl}/api/oauth/login`, {\n method: \"POST\",\n body: params,\n });\n};\n\n/**\n * Refresh access token.\n */\nexport const refresh = async (\n baseUrl: string,\n params: Refresh\n): Promise<FetchResult<TokenResponse>> => {\n return fetchApi<TokenResponse>(`${baseUrl}/api/oauth/refresh`, {\n method: \"POST\",\n body: params,\n });\n};\n\n// ============================================================================\n// Authenticated OAuth API\n// ============================================================================\n\n/**\n * Get current user info.\n * Requires User JWT.\n */\nexport const getMe = async (\n baseUrl: string,\n userAccessToken: string\n): Promise<FetchResult<UserInfo>> => {\n return fetchWithAuth<UserInfo>(`${baseUrl}/api/oauth/me`, `Bearer ${userAccessToken}`);\n};\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Convert token response to stored user token.\n */\nexport const tokenResponseToStoredUserToken = (\n response: TokenResponse,\n userId: string\n): StoredUserToken => ({\n accessToken: response.accessToken,\n refreshToken: response.refreshToken,\n userId,\n expiresAt: Date.now() + response.expiresIn * 1000,\n});\n","/**\n * Client Authorization Request API functions.\n *\n * For CLI/desktop apps to request tokens through user approval.\n */\n\nimport type {\n ApproveRequest,\n ApproveRequestResponse,\n CreateAuthRequest,\n CreateAuthRequestResponse,\n DenyRequest,\n DenyRequestResponse,\n PollRequestResponse,\n} from \"@casfa/protocol\";\nimport type { FetchResult } from \"../types/client.ts\";\nimport { fetchApi, fetchWithAuth } from \"../utils/http.ts\";\n\n// ============================================================================\n// Public APIs (No auth required)\n// ============================================================================\n\n/**\n * Create an authorization request.\n * No auth required - called by CLI/desktop clients.\n */\nexport const createAuthRequest = async (\n baseUrl: string,\n params: CreateAuthRequest\n): Promise<FetchResult<CreateAuthRequestResponse>> => {\n return fetchApi<CreateAuthRequestResponse>(`${baseUrl}/api/tokens/requests`, {\n method: \"POST\",\n body: params,\n });\n};\n\n/**\n * Poll authorization request status.\n * No auth required - called by CLI/desktop clients.\n */\nexport const pollAuthRequest = async (\n baseUrl: string,\n requestId: string\n): Promise<FetchResult<PollRequestResponse>> => {\n return fetchApi<PollRequestResponse>(\n `${baseUrl}/api/tokens/requests/${encodeURIComponent(requestId)}/poll`\n );\n};\n\n// ============================================================================\n// User JWT APIs (For approving/rejecting requests)\n// ============================================================================\n\n/**\n * Get authorization request details.\n * Requires User JWT.\n */\nexport const getAuthRequest = async (\n baseUrl: string,\n userAccessToken: string,\n requestId: string\n): Promise<FetchResult<PollRequestResponse>> => {\n return fetchWithAuth<PollRequestResponse>(\n `${baseUrl}/api/tokens/requests/${encodeURIComponent(requestId)}`,\n `Bearer ${userAccessToken}`\n );\n};\n\n/**\n * Approve an authorization request.\n * Requires User JWT.\n */\nexport const approveAuthRequest = async (\n baseUrl: string,\n userAccessToken: string,\n requestId: string,\n params?: ApproveRequest\n): Promise<FetchResult<ApproveRequestResponse>> => {\n return fetchWithAuth<ApproveRequestResponse>(\n `${baseUrl}/api/tokens/requests/${encodeURIComponent(requestId)}/approve`,\n `Bearer ${userAccessToken}`,\n {\n method: \"POST\",\n body: params ?? {},\n }\n );\n};\n\n/**\n * Reject an authorization request.\n * Requires User JWT.\n */\nexport const rejectAuthRequest = async (\n baseUrl: string,\n userAccessToken: string,\n requestId: string,\n params?: DenyRequest\n): Promise<FetchResult<DenyRequestResponse>> => {\n return fetchWithAuth<DenyRequestResponse>(\n `${baseUrl}/api/tokens/requests/${encodeURIComponent(requestId)}/reject`,\n `Bearer ${userAccessToken}`,\n {\n method: \"POST\",\n body: params ?? {},\n }\n );\n};\n","/**\n * Ticket API functions.\n *\n * Token Requirement:\n * - POST /api/realm/{realmId}/tickets: Access Token (create ticket, bind pre-issued token)\n * - GET /api/realm/{realmId}/tickets: Access Token (list)\n * - GET /api/realm/{realmId}/tickets/:ticketId: Access Token (get detail)\n * - POST /api/realm/{realmId}/tickets/:ticketId/submit: Access Token (submit)\n *\n * Design Principle: All Realm data operations use Access Token.\n * Delegate Token is only for issuing tokens.\n *\n * Two-step Ticket creation flow:\n * 1. Issue Access Token using Delegate Token (POST /api/tokens/delegate)\n * 2. Create Ticket and bind the token (POST /api/realm/{realmId}/tickets)\n */\n\nimport type {\n CreateTicket,\n CreateTicketResponse,\n ListTicketsQuery,\n TicketDetail,\n TicketListItem,\n TicketSubmit,\n} from \"@casfa/protocol\";\nimport type { FetchResult } from \"../types/client.ts\";\nimport { fetchWithAuth } from \"../utils/http.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ListTicketsResponse = {\n tickets: TicketListItem[];\n nextCursor?: string;\n};\n\nexport type SubmitTicketResponse = {\n ticketId: string;\n status: \"submitted\";\n root: string;\n submittedAt: number;\n};\n\n// ============================================================================\n// Access Token APIs\n// ============================================================================\n\n/**\n * Create a new ticket and bind a pre-issued Access Token.\n * Requires Access Token.\n *\n * @param accessTokenBase64 - Caller's Access Token (for authentication)\n * @param params.accessTokenId - Pre-issued Access Token ID to bind (for Tool use)\n */\nexport const createTicket = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n params: CreateTicket\n): Promise<FetchResult<CreateTicketResponse>> => {\n return fetchWithAuth<CreateTicketResponse>(\n `${baseUrl}/api/realm/${encodeURIComponent(realm)}/tickets`,\n `Bearer ${accessTokenBase64}`,\n {\n method: \"POST\",\n body: params,\n }\n );\n};\n\n/**\n * List tickets.\n * Requires Access Token.\n */\nexport const listTickets = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n params?: ListTicketsQuery\n): Promise<FetchResult<ListTicketsResponse>> => {\n const query = new URLSearchParams();\n if (params?.limit) query.set(\"limit\", String(params.limit));\n if (params?.cursor) query.set(\"cursor\", params.cursor);\n if (params?.status) query.set(\"status\", params.status);\n\n const queryString = query.toString();\n const url = `${baseUrl}/api/realm/${encodeURIComponent(realm)}/tickets${queryString ? `?${queryString}` : \"\"}`;\n\n return fetchWithAuth<ListTicketsResponse>(url, `Bearer ${accessTokenBase64}`);\n};\n\n/**\n * Get ticket details.\n * Requires Access Token.\n */\nexport const getTicket = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n ticketId: string\n): Promise<FetchResult<TicketDetail>> => {\n return fetchWithAuth<TicketDetail>(\n `${baseUrl}/api/realm/${encodeURIComponent(realm)}/tickets/${encodeURIComponent(ticketId)}`,\n `Bearer ${accessTokenBase64}`\n );\n};\n\n/**\n * Submit a ticket.\n * Requires Access Token.\n */\nexport const submitTicket = async (\n baseUrl: string,\n realm: string,\n accessTokenBase64: string,\n ticketId: string,\n params: TicketSubmit\n): Promise<FetchResult<SubmitTicketResponse>> => {\n return fetchWithAuth<SubmitTicketResponse>(\n `${baseUrl}/api/realm/${encodeURIComponent(realm)}/tickets/${encodeURIComponent(ticketId)}/submit`,\n `Bearer ${accessTokenBase64}`,\n {\n method: \"POST\",\n body: params,\n }\n );\n};\n","/**\n * Token management API functions.\n *\n * Token Requirement:\n * - POST /api/tokens: User JWT\n * - GET /api/tokens: User JWT\n * - GET /api/tokens/:tokenId: User JWT\n * - POST /api/tokens/:tokenId/revoke: User JWT\n * - POST /api/tokens/delegate: Delegate Token\n */\n\nimport type {\n CreateToken,\n CreateTokenResponse,\n RevokeTokenResponse,\n TokenDetail,\n TokenListItem,\n} from \"@casfa/protocol\";\nimport type { FetchResult } from \"../types/client.ts\";\nimport { fetchWithAuth } from \"../utils/http.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ListTokensParams = {\n limit?: number;\n cursor?: string;\n type?: \"delegate\" | \"access\";\n};\n\nexport type ListTokensResponse = {\n tokens: TokenListItem[];\n nextCursor?: string;\n};\n\n// ============================================================================\n// User JWT APIs\n// ============================================================================\n\n/**\n * Create a new Delegate Token.\n * Requires User JWT.\n */\nexport const createToken = async (\n baseUrl: string,\n userAccessToken: string,\n params: CreateToken\n): Promise<FetchResult<CreateTokenResponse>> => {\n return fetchWithAuth<CreateTokenResponse>(`${baseUrl}/api/tokens`, `Bearer ${userAccessToken}`, {\n method: \"POST\",\n body: params,\n });\n};\n\n/**\n * List Delegate Tokens.\n * Requires User JWT.\n */\nexport const listTokens = async (\n baseUrl: string,\n userAccessToken: string,\n params?: ListTokensParams\n): Promise<FetchResult<ListTokensResponse>> => {\n const query = new URLSearchParams();\n if (params?.limit) query.set(\"limit\", String(params.limit));\n if (params?.cursor) query.set(\"cursor\", params.cursor);\n if (params?.type) query.set(\"type\", params.type);\n\n const queryString = query.toString();\n const url = `${baseUrl}/api/tokens${queryString ? `?${queryString}` : \"\"}`;\n\n return fetchWithAuth<ListTokensResponse>(url, `Bearer ${userAccessToken}`);\n};\n\n/**\n * Get token details.\n * Requires User JWT.\n */\nexport const getToken = async (\n baseUrl: string,\n userAccessToken: string,\n tokenId: string\n): Promise<FetchResult<TokenDetail>> => {\n return fetchWithAuth<TokenDetail>(\n `${baseUrl}/api/tokens/${encodeURIComponent(tokenId)}`,\n `Bearer ${userAccessToken}`\n );\n};\n\n/**\n * Revoke a token.\n * Requires User JWT.\n */\nexport const revokeToken = async (\n baseUrl: string,\n userAccessToken: string,\n tokenId: string\n): Promise<FetchResult<RevokeTokenResponse>> => {\n return fetchWithAuth<RevokeTokenResponse>(\n `${baseUrl}/api/tokens/${encodeURIComponent(tokenId)}/revoke`,\n `Bearer ${userAccessToken}`,\n { method: \"POST\" }\n );\n};\n\n// ============================================================================\n// Delegate Token APIs\n// ============================================================================\n\nexport type DelegateTokenParams = {\n name: string;\n type: \"delegate\" | \"access\";\n expiresIn?: number;\n canUpload?: boolean;\n canManageDepot?: boolean;\n scope?: string[];\n};\n\n/**\n * Delegate (re-issue) a token using existing Delegate Token.\n * Requires Delegate Token.\n */\nexport const delegateToken = async (\n baseUrl: string,\n delegateTokenBase64: string,\n params: DelegateTokenParams\n): Promise<FetchResult<CreateTokenResponse>> => {\n return fetchWithAuth<CreateTokenResponse>(\n `${baseUrl}/api/tokens/delegate`,\n `Bearer ${delegateTokenBase64}`,\n {\n method: \"POST\",\n body: params,\n }\n );\n};\n","/**\n * Token validity and issuer consistency checks.\n */\n\nimport type {\n StoredAccessToken,\n StoredDelegateToken,\n StoredUserToken,\n TokenState,\n} from \"../types/tokens.ts\";\n\n// ============================================================================\n// Validity Checks\n// ============================================================================\n\n/**\n * Default buffer time before expiration (60 seconds).\n */\nexport const DEFAULT_EXPIRY_BUFFER_MS = 60_000;\n\n/**\n * Check if a token is valid (not expired with buffer).\n */\nexport const isTokenValid = (\n token: { expiresAt: number } | null,\n bufferMs: number = DEFAULT_EXPIRY_BUFFER_MS\n): boolean => {\n if (!token) return false;\n return Date.now() + bufferMs < token.expiresAt;\n};\n\n/**\n * Check if token is expiring soon and should be refreshed proactively.\n * Used for JWT refresh scheduling.\n */\nexport const isTokenExpiringSoon = (\n token: { expiresAt: number } | null,\n windowMs: number = 5 * 60_000 // 5 minutes\n): boolean => {\n if (!token) return false;\n return Date.now() + windowMs >= token.expiresAt;\n};\n\n/**\n * Check if user JWT is valid.\n */\nexport const isUserTokenValid = (userToken: StoredUserToken | null, bufferMs?: number): boolean => {\n return isTokenValid(userToken, bufferMs);\n};\n\n/**\n * Check if delegate token is valid.\n */\nexport const isDelegateTokenValid = (\n delegateToken: StoredDelegateToken | null,\n bufferMs?: number\n): boolean => {\n return isTokenValid(delegateToken, bufferMs);\n};\n\n/**\n * Check if access token is valid.\n */\nexport const isAccessTokenValid = (\n accessToken: StoredAccessToken | null,\n bufferMs?: number\n): boolean => {\n return isTokenValid(accessToken, bufferMs);\n};\n\n// ============================================================================\n// Issuer Consistency Checks\n// ============================================================================\n\n/**\n * Get the current max issuer ID.\n * Priority: User JWT (userId) > Delegate Token (tokenId)\n */\nexport const getMaxIssuerId = (state: TokenState): string | null => {\n if (state.user && isUserTokenValid(state.user)) {\n return state.user.userId;\n }\n if (state.delegate && isDelegateTokenValid(state.delegate)) {\n return state.delegate.tokenId;\n }\n return null;\n};\n\n/**\n * Check if access token is issued by the current max issuer.\n * If not, the access token should be re-issued.\n */\nexport const isAccessTokenFromMaxIssuer = (state: TokenState): boolean => {\n const accessToken = state.access;\n if (!accessToken || !isAccessTokenValid(accessToken)) {\n return false;\n }\n\n const maxIssuerId = getMaxIssuerId(state);\n if (!maxIssuerId) {\n // No valid issuer, access token is orphaned but still usable\n return true;\n }\n\n return accessToken.issuerId === maxIssuerId;\n};\n\n/**\n * Check if delegate token is issued by current user.\n * If user JWT exists but delegate token is from a different user,\n * we may want to re-issue the delegate token.\n */\nexport const isDelegateTokenFromCurrentUser = (state: TokenState): boolean => {\n const delegateToken = state.delegate;\n const userToken = state.user;\n\n if (!delegateToken || !isDelegateTokenValid(delegateToken)) {\n return false;\n }\n\n if (!userToken || !isUserTokenValid(userToken)) {\n // No user token, delegate token is the top-level authority\n return true;\n }\n\n return delegateToken.issuerId === userToken.userId;\n};\n\n/**\n * Determine if access token needs re-issue.\n * Returns true if:\n * - Access token is invalid/expired\n * - Access token's issuer is not the current max issuer\n */\nexport const shouldReissueAccessToken = (state: TokenState): boolean => {\n if (!isAccessTokenValid(state.access)) {\n return true;\n }\n if (!isAccessTokenFromMaxIssuer(state)) {\n return true;\n }\n return false;\n};\n\n/**\n * Determine if delegate token needs re-issue.\n * Returns true if:\n * - Delegate token is invalid/expired\n * - User token exists but delegate token is not from current user\n */\nexport const shouldReissueDelegateToken = (state: TokenState): boolean => {\n if (!isDelegateTokenValid(state.delegate)) {\n return true;\n }\n if (state.user && !isDelegateTokenFromCurrentUser(state)) {\n return true;\n }\n return false;\n};\n","/**\n * JWT refresh management with promise deduplication.\n */\n\nimport type { OnAuthRequiredCallback } from \"../types/client.ts\";\nimport type { StoredUserToken } from \"../types/tokens.ts\";\nimport { isUserTokenValid } from \"./token-checks.ts\";\nimport type { TokenStore } from \"./token-store.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type RefreshManager = {\n /**\n * Ensure user token is valid, refreshing if needed.\n * Returns the valid user token or null if refresh failed.\n */\n ensureValidUserToken: () => Promise<StoredUserToken | null>;\n\n /**\n * Schedule proactive refresh before expiration.\n */\n scheduleProactiveRefresh: () => void;\n\n /**\n * Cancel any scheduled refresh.\n */\n cancelScheduledRefresh: () => void;\n};\n\nexport type RefreshManagerConfig = {\n store: TokenStore;\n baseUrl: string;\n onAuthRequired?: OnAuthRequiredCallback;\n};\n\n// ============================================================================\n// Refresh API Call\n// ============================================================================\n\ntype RefreshResponse = {\n accessToken: string;\n refreshToken?: string;\n expiresIn: number;\n};\n\nconst callRefreshApi = async (\n baseUrl: string,\n refreshToken: string\n): Promise<RefreshResponse | null> => {\n try {\n const response = await fetch(`${baseUrl}/api/oauth/refresh`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken }),\n });\n\n if (!response.ok) {\n return null;\n }\n\n return (await response.json()) as RefreshResponse;\n } catch {\n return null;\n }\n};\n\n// ============================================================================\n// Refresh Manager Factory\n// ============================================================================\n\n/**\n * Create a refresh manager for JWT token refresh.\n */\nexport const createRefreshManager = (config: RefreshManagerConfig): RefreshManager => {\n const { store, baseUrl, onAuthRequired } = config;\n\n // Promise deduplication: only one refresh in flight at a time\n let refreshPromise: Promise<StoredUserToken | null> | null = null;\n\n // Scheduled refresh timer\n let refreshTimer: ReturnType<typeof setTimeout> | null = null;\n\n const doRefresh = async (): Promise<StoredUserToken | null> => {\n const state = store.getState();\n const userToken = state.user;\n\n if (!userToken?.refreshToken) {\n onAuthRequired?.();\n return null;\n }\n\n const result = await callRefreshApi(baseUrl, userToken.refreshToken);\n\n if (!result) {\n // Refresh failed, require re-auth\n store.setUser(null);\n onAuthRequired?.();\n return null;\n }\n\n const newUserToken: StoredUserToken = {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken ?? userToken.refreshToken,\n userId: userToken.userId,\n expiresAt: Date.now() + result.expiresIn * 1000,\n };\n\n store.setUser(newUserToken);\n return newUserToken;\n };\n\n const ensureValidUserToken = async (): Promise<StoredUserToken | null> => {\n const state = store.getState();\n const userToken = state.user;\n\n // Check if current token is valid\n if (isUserTokenValid(userToken)) {\n return userToken;\n }\n\n // No user token at all\n if (!userToken) {\n return null;\n }\n\n // Need to refresh - use deduplication\n if (!refreshPromise) {\n refreshPromise = doRefresh().finally(() => {\n refreshPromise = null;\n });\n }\n\n return refreshPromise;\n };\n\n const scheduleProactiveRefresh = () => {\n cancelScheduledRefresh();\n\n const state = store.getState();\n const userToken = state.user;\n\n if (!userToken) return;\n\n // Schedule refresh 5 minutes before expiration\n const refreshTime = userToken.expiresAt - Date.now() - 5 * 60_000;\n\n if (refreshTime <= 0) {\n // Already expiring soon, refresh immediately\n ensureValidUserToken();\n return;\n }\n\n refreshTimer = setTimeout(() => {\n ensureValidUserToken().then((newToken) => {\n if (newToken) {\n // Schedule next refresh\n scheduleProactiveRefresh();\n }\n });\n }, refreshTime);\n };\n\n const cancelScheduledRefresh = () => {\n if (refreshTimer) {\n clearTimeout(refreshTimer);\n refreshTimer = null;\n }\n };\n\n return {\n ensureValidUserToken,\n scheduleProactiveRefresh,\n cancelScheduledRefresh,\n };\n};\n","/**\n * Token selector and auto-issuer.\n *\n * Implements the \"maximum authority\" principle:\n * - When issuing tokens, prefer User JWT (depth=0) over Delegate Token\n * - Check issuer consistency before using existing tokens\n * - Re-issue tokens when issuer is not maximized\n */\n\nimport type { ServiceInfo } from \"@casfa/protocol\";\nimport type { StoredAccessToken, StoredDelegateToken } from \"../types/tokens.ts\";\nimport {\n isDelegateTokenValid,\n isUserTokenValid,\n shouldReissueAccessToken,\n} from \"./token-checks.ts\";\nimport type { TokenStore } from \"./token-store.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type TokenSelectorConfig = {\n store: TokenStore;\n baseUrl: string;\n realm: string;\n serverInfo: ServiceInfo | null;\n defaultTokenTtl?: number;\n};\n\nexport type TokenSelector = {\n /**\n * Get or issue an Access Token.\n * - If valid Access Token exists and is from max issuer, return it\n * - Otherwise, issue a new one using User JWT or Delegate Token\n */\n ensureAccessToken: () => Promise<StoredAccessToken | null>;\n\n /**\n * Get or issue a Delegate Token.\n * - If valid Delegate Token exists, return it\n * - If User JWT exists, issue a new one\n */\n ensureDelegateToken: () => Promise<StoredDelegateToken | null>;\n};\n\n// ============================================================================\n// API Calls for Token Issuance\n// ============================================================================\n\ntype CreateTokenRequest = {\n realm: string;\n name: string;\n type: \"delegate\" | \"access\";\n expiresIn?: number;\n canUpload?: boolean;\n canManageDepot?: boolean;\n scope?: string[];\n};\n\ntype CreateTokenResponse = {\n tokenId: string;\n tokenBase64: string;\n type: \"delegate\" | \"access\";\n issuerId: string;\n expiresAt: number;\n canUpload: boolean;\n canManageDepot: boolean;\n};\n\n/**\n * Issue a token using User JWT.\n */\nconst issueTokenWithUserJwt = async (\n baseUrl: string,\n userAccessToken: string,\n request: CreateTokenRequest\n): Promise<CreateTokenResponse | null> => {\n try {\n const response = await fetch(`${baseUrl}/api/tokens`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${userAccessToken}`,\n },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n console.error(\"[TokenSelector] Failed to issue token with User JWT:\", response.status);\n return null;\n }\n\n return (await response.json()) as CreateTokenResponse;\n } catch (err) {\n console.error(\"[TokenSelector] Error issuing token with User JWT:\", err);\n return null;\n }\n};\n\n/**\n * Delegate a token using existing Delegate Token.\n */\nconst delegateToken = async (\n baseUrl: string,\n delegateTokenBase64: string,\n request: Omit<CreateTokenRequest, \"realm\">\n): Promise<CreateTokenResponse | null> => {\n try {\n const response = await fetch(`${baseUrl}/api/tokens/delegate`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${delegateTokenBase64}`,\n },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n console.error(\"[TokenSelector] Failed to delegate token:\", response.status);\n return null;\n }\n\n return (await response.json()) as CreateTokenResponse;\n } catch (err) {\n console.error(\"[TokenSelector] Error delegating token:\", err);\n return null;\n }\n};\n\n// ============================================================================\n// Token Selector Factory\n// ============================================================================\n\n/**\n * Create a token selector instance.\n */\nexport const createTokenSelector = (config: TokenSelectorConfig): TokenSelector => {\n const { store, baseUrl, realm, serverInfo, defaultTokenTtl } = config;\n\n /**\n * Get token TTL in seconds.\n * Priority: defaultTokenTtl > server max > fallback\n */\n const getTokenTtl = (type: \"delegate\" | \"access\"): number => {\n if (defaultTokenTtl) return defaultTokenTtl;\n\n if (serverInfo?.limits) {\n if (type === \"delegate\" && serverInfo.limits.maxDelegateTokenTtl) {\n return serverInfo.limits.maxDelegateTokenTtl;\n }\n if (type === \"access\" && serverInfo.limits.maxAccessTokenTtl) {\n return serverInfo.limits.maxAccessTokenTtl;\n }\n }\n\n // Fallback: 1 hour for access, 30 days for delegate\n return type === \"access\" ? 3600 : 30 * 24 * 3600;\n };\n\n const ensureAccessToken = async (): Promise<StoredAccessToken | null> => {\n const state = store.getState();\n\n // Check if existing access token is valid and from max issuer\n if (!shouldReissueAccessToken(state)) {\n return state.access;\n }\n\n // Need to issue a new access token\n // Priority: User JWT > Delegate Token\n const userToken = state.user;\n const delegateToken_ = state.delegate;\n\n if (isUserTokenValid(userToken)) {\n // Issue with User JWT (max authority)\n const result = await issueTokenWithUserJwt(baseUrl, userToken!.accessToken, {\n realm,\n name: \"auto-issued-access\",\n type: \"access\",\n expiresIn: getTokenTtl(\"access\"),\n canUpload: true,\n canManageDepot: true,\n });\n\n if (result) {\n const newToken: StoredAccessToken = {\n tokenId: result.tokenId,\n tokenBase64: result.tokenBase64,\n type: \"access\",\n issuerId: result.issuerId,\n expiresAt: result.expiresAt,\n canUpload: result.canUpload,\n canManageDepot: result.canManageDepot,\n };\n store.setAccess(newToken);\n return newToken;\n }\n }\n\n if (isDelegateTokenValid(delegateToken_)) {\n // Issue with Delegate Token (fallback)\n const result = await delegateToken(baseUrl, delegateToken_!.tokenBase64, {\n name: \"auto-issued-access\",\n type: \"access\",\n expiresIn: getTokenTtl(\"access\"),\n canUpload: delegateToken_!.canUpload,\n canManageDepot: delegateToken_!.canManageDepot,\n });\n\n if (result) {\n const newToken: StoredAccessToken = {\n tokenId: result.tokenId,\n tokenBase64: result.tokenBase64,\n type: \"access\",\n issuerId: result.issuerId,\n expiresAt: result.expiresAt,\n canUpload: result.canUpload,\n canManageDepot: result.canManageDepot,\n };\n store.setAccess(newToken);\n return newToken;\n }\n }\n\n // No way to issue access token\n return null;\n };\n\n const ensureDelegateToken = async (): Promise<StoredDelegateToken | null> => {\n const state = store.getState();\n\n // Check if existing delegate token is valid\n if (isDelegateTokenValid(state.delegate)) {\n return state.delegate;\n }\n\n // Need to issue a new delegate token\n // Only User JWT can issue delegate tokens\n const userToken = state.user;\n\n if (!isUserTokenValid(userToken)) {\n return null;\n }\n\n const result = await issueTokenWithUserJwt(baseUrl, userToken!.accessToken, {\n realm,\n name: \"auto-issued-delegate\",\n type: \"delegate\",\n expiresIn: getTokenTtl(\"delegate\"),\n canUpload: true,\n canManageDepot: true,\n });\n\n if (result) {\n const newToken: StoredDelegateToken = {\n tokenId: result.tokenId,\n tokenBase64: result.tokenBase64,\n type: \"delegate\",\n issuerId: result.issuerId,\n expiresAt: result.expiresAt,\n canUpload: result.canUpload,\n canManageDepot: result.canManageDepot,\n };\n store.setDelegate(newToken);\n return newToken;\n }\n\n return null;\n };\n\n return {\n ensureAccessToken,\n ensureDelegateToken,\n };\n};\n","/**\n * Token types for the stateful client.\n *\n * Three-tier token hierarchy:\n * - User JWT: OAuth login token, highest authority\n * - Delegate Token: Re-delegation token, can issue child tokens\n * - Access Token: Data access token, used for CAS operations\n */\n\n// ============================================================================\n// Stored Token Types\n// ============================================================================\n\n/**\n * User JWT token with refresh capability.\n */\nexport type StoredUserToken = {\n /** JWT access token */\n accessToken: string;\n /** Refresh token for token renewal */\n refreshToken: string;\n /** User ID (usr_xxx format) */\n userId: string;\n /** Token expiration time (epoch ms) */\n expiresAt: number;\n};\n\n/**\n * Delegate Token (re-delegation token).\n */\nexport type StoredDelegateToken = {\n /** Token ID (dlt1_xxx format) */\n tokenId: string;\n /** Token binary as Base64 */\n tokenBase64: string;\n /** Token type: always \"delegate\" */\n type: \"delegate\";\n /** Issuer ID (usr_xxx or dlt1_xxx) */\n issuerId: string;\n /** Token expiration time (epoch ms) */\n expiresAt: number;\n /** Whether the token can upload nodes */\n canUpload: boolean;\n /** Whether the token can manage depots */\n canManageDepot: boolean;\n};\n\n/**\n * Access Token (data access token).\n */\nexport type StoredAccessToken = {\n /** Token ID (dlt1_xxx format) */\n tokenId: string;\n /** Token binary as Base64 */\n tokenBase64: string;\n /** Token type: always \"access\" */\n type: \"access\";\n /** Issuer ID (usr_xxx or dlt1_xxx) */\n issuerId: string;\n /** Token expiration time (epoch ms) */\n expiresAt: number;\n /** Whether the token can upload nodes */\n canUpload: boolean;\n /** Whether the token can manage depots */\n canManageDepot: boolean;\n};\n\n/**\n * Complete token state held by the client.\n */\nexport type TokenState = {\n /** User JWT (optional) */\n user: StoredUserToken | null;\n /** Delegate Token (optional) */\n delegate: StoredDelegateToken | null;\n /** Access Token (optional) */\n access: StoredAccessToken | null;\n};\n\n/**\n * Empty token state.\n */\nexport const emptyTokenState = (): TokenState => ({\n user: null,\n delegate: null,\n access: null,\n});\n\n// ============================================================================\n// Token Requirement Types\n// ============================================================================\n\n/**\n * Token requirement for API calls.\n */\nexport type TokenRequirement = \"none\" | \"user\" | \"delegate\" | \"access\";\n\n/**\n * Auth header format.\n */\nexport type AuthHeader = {\n Authorization: string;\n};\n\n/**\n * Get issuer ID from current state (for signing new tokens).\n * Priority: User JWT > Delegate Token\n */\nexport const getMaxIssuerId = (state: TokenState): string | null => {\n if (state.user) {\n return state.user.userId;\n }\n if (state.delegate) {\n return state.delegate.tokenId;\n }\n return null;\n};\n\n/**\n * Check if Access Token was issued by the current max issuer.\n */\nexport const isAccessTokenFromMaxIssuer = (state: TokenState): boolean => {\n if (!state.access) return false;\n\n const maxIssuerId = getMaxIssuerId(state);\n if (!maxIssuerId) return false;\n\n return state.access.issuerId === maxIssuerId;\n};\n\n/**\n * Check if Delegate Token was issued by current user.\n */\nexport const isDelegateTokenFromCurrentUser = (state: TokenState): boolean => {\n if (!state.delegate || !state.user) return false;\n return state.delegate.issuerId === state.user.userId;\n};\n","/**\n * Token store - manages the three-tier token state.\n *\n * Provides a closure-based store for token state management with\n * automatic persistence and change notifications.\n */\n\nimport type {\n OnAuthRequiredCallback,\n OnTokenChangeCallback,\n TokenStorageProvider,\n} from \"../types/client.ts\";\nimport type {\n StoredAccessToken,\n StoredDelegateToken,\n StoredUserToken,\n TokenState,\n} from \"../types/tokens.ts\";\nimport { emptyTokenState } from \"../types/tokens.ts\";\n\n// ============================================================================\n// Store Types\n// ============================================================================\n\nexport type TokenStore = {\n /** Get current token state (immutable snapshot) */\n getState: () => TokenState;\n\n /** Set user JWT token */\n setUser: (token: StoredUserToken | null) => void;\n\n /** Set delegate token */\n setDelegate: (token: StoredDelegateToken | null) => void;\n\n /** Set access token */\n setAccess: (token: StoredAccessToken | null) => void;\n\n /** Clear all tokens */\n clear: () => void;\n\n /** Initialize from storage provider */\n initialize: () => Promise<void>;\n};\n\nexport type TokenStoreConfig = {\n storage?: TokenStorageProvider;\n onTokenChange?: OnTokenChangeCallback;\n onAuthRequired?: OnAuthRequiredCallback;\n};\n\n// ============================================================================\n// Store Factory\n// ============================================================================\n\n/**\n * Create a token store instance.\n */\nexport const createTokenStore = (config: TokenStoreConfig = {}): TokenStore => {\n const { storage, onTokenChange } = config;\n\n // Internal mutable state\n let state: TokenState = emptyTokenState();\n\n // Notify change and persist\n const notifyAndPersist = () => {\n onTokenChange?.(state);\n storage?.save(state).catch((err) => {\n console.error(\"[TokenStore] Failed to persist state:\", err);\n });\n };\n\n return {\n getState: () => ({ ...state }),\n\n setUser: (token) => {\n state = { ...state, user: token };\n notifyAndPersist();\n },\n\n setDelegate: (token) => {\n state = { ...state, delegate: token };\n notifyAndPersist();\n },\n\n setAccess: (token) => {\n state = { ...state, access: token };\n notifyAndPersist();\n },\n\n clear: () => {\n state = emptyTokenState();\n notifyAndPersist();\n storage?.clear().catch((err) => {\n console.error(\"[TokenStore] Failed to clear storage:\", err);\n });\n },\n\n initialize: async () => {\n if (!storage) return;\n\n try {\n const loaded = await storage.load();\n if (loaded) {\n state = loaded;\n // Don't notify on initial load to avoid side effects\n }\n } catch (err) {\n console.error(\"[TokenStore] Failed to load from storage:\", err);\n }\n },\n };\n};\n","/**\n * Client helper functions for reducing boilerplate.\n */\n\nimport type { FetchResult } from \"../types/client.ts\";\nimport type { StoredAccessToken, StoredDelegateToken, StoredUserToken } from \"../types/tokens.ts\";\n\n// ============================================================================\n// Error Constants\n// ============================================================================\n\nexport const ERRORS = {\n USER_REQUIRED: { code: \"UNAUTHORIZED\", message: \"User login required\" },\n DELEGATE_REQUIRED: { code: \"FORBIDDEN\", message: \"Delegate token required\" },\n ACCESS_REQUIRED: { code: \"FORBIDDEN\", message: \"Access token required\" },\n} as const;\n\n// ============================================================================\n// Token Guards\n// ============================================================================\n\nexport type TokenGetter<T> = () => Promise<T | null>;\n\n/**\n * Higher-order function for token-required operations.\n * Reduces repetitive null checks and error returns.\n */\nexport const withToken = <T>(\n getToken: TokenGetter<T>,\n error: { code: string; message: string }\n) => {\n return <R>(fn: (token: T) => Promise<FetchResult<R>>): Promise<FetchResult<R>> =>\n getToken().then((token) =>\n token ? fn(token) : Promise.resolve({ ok: false as const, error })\n );\n};\n\nexport const withUserToken = (getToken: TokenGetter<StoredUserToken>) =>\n withToken(getToken, ERRORS.USER_REQUIRED);\n\nexport const withDelegateToken = (getToken: TokenGetter<StoredDelegateToken>) =>\n withToken(getToken, ERRORS.DELEGATE_REQUIRED);\n\nexport const withAccessToken = (getToken: TokenGetter<StoredAccessToken>) =>\n withToken(getToken, ERRORS.ACCESS_REQUIRED);\n","/**\n * Depot methods for the stateful client.\n */\n\nimport type {\n CreateDepot,\n CreateDepotResponse,\n DepotCommit,\n DepotDetail,\n ListDepotsQuery,\n UpdateDepot,\n} from \"@casfa/protocol\";\nimport * as api from \"../api/index.ts\";\nimport type { TokenSelector } from \"../store/token-selector.ts\";\nimport type { FetchResult } from \"../types/client.ts\";\nimport { withAccessToken } from \"./helpers.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type DepotMethods = {\n /** Create a new depot */\n create: (params: CreateDepot) => Promise<FetchResult<CreateDepotResponse>>;\n /** List depots */\n list: (params?: ListDepotsQuery) => Promise<FetchResult<api.ListDepotsResponse>>;\n /** Get depot details */\n get: (depotId: string) => Promise<FetchResult<DepotDetail>>;\n /** Update depot */\n update: (depotId: string, params: UpdateDepot) => Promise<FetchResult<DepotDetail>>;\n /** Delete depot */\n delete: (depotId: string) => Promise<FetchResult<void>>;\n /** Commit new root */\n commit: (depotId: string, params: DepotCommit) => Promise<FetchResult<api.CommitDepotResponse>>;\n};\n\nexport type DepotDeps = {\n baseUrl: string;\n realm: string;\n tokenSelector: TokenSelector;\n};\n\n// ============================================================================\n// Factory\n// ============================================================================\n\nexport const createDepotMethods = ({ baseUrl, realm, tokenSelector }: DepotDeps): DepotMethods => {\n const requireAccess = withAccessToken(() => tokenSelector.ensureAccessToken());\n\n return {\n create: (params) =>\n requireAccess((t) => api.createDepot(baseUrl, realm, t.tokenBase64, params)),\n\n list: (params) => requireAccess((t) => api.listDepots(baseUrl, realm, t.tokenBase64, params)),\n\n get: (depotId) => requireAccess((t) => api.getDepot(baseUrl, realm, t.tokenBase64, depotId)),\n\n update: (depotId, params) =>\n requireAccess((t) => api.updateDepot(baseUrl, realm, t.tokenBase64, depotId, params)),\n\n delete: (depotId) =>\n requireAccess((t) => api.deleteDepot(baseUrl, realm, t.tokenBase64, depotId)),\n\n commit: (depotId, params) =>\n requireAccess((t) => api.commitDepot(baseUrl, realm, t.tokenBase64, depotId, params)),\n };\n};\n","/**\n * Node methods for the stateful client.\n */\n\nimport type { NodeMetadata, PrepareNodes, PrepareNodesResponse } from \"@casfa/protocol\";\nimport * as api from \"../api/index.ts\";\nimport type { TokenSelector } from \"../store/token-selector.ts\";\nimport type { FetchResult } from \"../types/client.ts\";\nimport { withAccessToken } from \"./helpers.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type NodeMethods = {\n /** Get node content */\n get: (nodeKey: string, indexPath: string) => Promise<FetchResult<Uint8Array>>;\n /** Get node metadata */\n getMetadata: (nodeKey: string, indexPath: string) => Promise<FetchResult<NodeMetadata>>;\n /** Prepare nodes for upload */\n prepare: (params: PrepareNodes) => Promise<FetchResult<PrepareNodesResponse>>;\n /** Upload a node */\n put: (nodeKey: string, content: Uint8Array) => Promise<FetchResult<api.NodeUploadResult>>;\n};\n\nexport type NodeDeps = {\n baseUrl: string;\n realm: string;\n tokenSelector: TokenSelector;\n};\n\n// ============================================================================\n// Factory\n// ============================================================================\n\nexport const createNodeMethods = ({ baseUrl, realm, tokenSelector }: NodeDeps): NodeMethods => {\n const requireAccess = withAccessToken(() => tokenSelector.ensureAccessToken());\n\n return {\n get: (nodeKey, indexPath) =>\n requireAccess((t) => api.getNode(baseUrl, realm, t.tokenBase64, nodeKey, indexPath)),\n\n getMetadata: (nodeKey, indexPath) =>\n requireAccess((t) => api.getNodeMetadata(baseUrl, realm, t.tokenBase64, nodeKey, indexPath)),\n\n prepare: (params) =>\n requireAccess((t) => api.prepareNodes(baseUrl, realm, t.tokenBase64, params)),\n\n put: (nodeKey, content) =>\n requireAccess((t) => api.putNode(baseUrl, realm, t.tokenBase64, nodeKey, content)),\n };\n};\n","/**\n * OAuth methods for the stateful client.\n */\n\nimport * as api from \"../api/index.ts\";\nimport type { RefreshManager } from \"../store/jwt-refresh.ts\";\nimport type { TokenStore } from \"../store/token-store.ts\";\nimport type { FetchResult } from \"../types/client.ts\";\nimport { ERRORS } from \"./helpers.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type OAuthMethods = {\n /** Get Cognito configuration */\n getConfig: () => Promise<FetchResult<api.CognitoConfig>>;\n /** Login with email and password */\n login: (email: string, password: string) => Promise<FetchResult<api.UserInfo>>;\n /** Exchange authorization code for tokens */\n exchangeCode: (\n code: string,\n redirectUri: string,\n codeVerifier?: string\n ) => Promise<FetchResult<api.UserInfo>>;\n /** Get current user info */\n getMe: () => Promise<FetchResult<api.UserInfo>>;\n};\n\nexport type OAuthDeps = {\n baseUrl: string;\n store: TokenStore;\n refreshManager: RefreshManager;\n};\n\n// ============================================================================\n// Factory\n// ============================================================================\n\nexport const createOAuthMethods = ({\n baseUrl,\n store,\n refreshManager,\n}: OAuthDeps): OAuthMethods => ({\n getConfig: () => api.getOAuthConfig(baseUrl),\n\n login: async (email, password) => {\n const result = await api.login(baseUrl, { email, password });\n if (!result.ok) return result;\n\n const meResult = await api.getMe(baseUrl, result.data.accessToken);\n if (!meResult.ok) return meResult;\n\n store.setUser(api.tokenResponseToStoredUserToken(result.data, meResult.data.userId));\n refreshManager.scheduleProactiveRefresh();\n return meResult;\n },\n\n exchangeCode: async (code, redirectUri, codeVerifier) => {\n const result = await api.exchangeCode(baseUrl, {\n code,\n redirect_uri: redirectUri,\n code_verifier: codeVerifier,\n });\n if (!result.ok) return result;\n\n const meResult = await api.getMe(baseUrl, result.data.accessToken);\n if (!meResult.ok) return meResult;\n\n store.setUser(api.tokenResponseToStoredUserToken(result.data, meResult.data.userId));\n refreshManager.scheduleProactiveRefresh();\n return meResult;\n },\n\n getMe: async () => {\n const user = await refreshManager.ensureValidUserToken();\n if (!user) {\n return { ok: false, error: ERRORS.USER_REQUIRED };\n }\n return api.getMe(baseUrl, user.accessToken);\n },\n});\n","/**\n * Ticket methods for the stateful client.\n *\n * Design Principle: All Realm data operations use Access Token.\n * Delegate Token is only for issuing tokens.\n *\n * Two-step Ticket creation flow:\n * 1. Issue Access Token using tokens.delegate() (requires Delegate Token)\n * 2. Create Ticket using tickets.create() (requires Access Token)\n */\n\nimport type {\n CreateTicket,\n CreateTicketResponse,\n ListTicketsQuery,\n TicketDetail,\n TicketSubmit,\n} from \"@casfa/protocol\";\nimport * as api from \"../api/index.ts\";\nimport type { TokenSelector } from \"../store/token-selector.ts\";\nimport type { FetchResult } from \"../types/client.ts\";\nimport { withAccessToken } from \"./helpers.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type TicketMethods = {\n /**\n * Create a new ticket and bind a pre-issued Access Token.\n * Requires Access Token.\n *\n * Note: The Access Token to bind must be issued first using tokens.delegate().\n *\n * @param params.accessTokenId - Pre-issued Access Token ID to bind (for Tool use)\n */\n create: (params: CreateTicket) => Promise<FetchResult<CreateTicketResponse>>;\n /** List tickets */\n list: (params?: ListTicketsQuery) => Promise<FetchResult<api.ListTicketsResponse>>;\n /** Get ticket details */\n get: (ticketId: string) => Promise<FetchResult<TicketDetail>>;\n /** Submit ticket */\n submit: (\n ticketId: string,\n params: TicketSubmit\n ) => Promise<FetchResult<api.SubmitTicketResponse>>;\n};\n\nexport type TicketDeps = {\n baseUrl: string;\n realm: string;\n tokenSelector: TokenSelector;\n};\n\n// ============================================================================\n// Factory\n// ============================================================================\n\nexport const createTicketMethods = ({\n baseUrl,\n realm,\n tokenSelector,\n}: TicketDeps): TicketMethods => {\n const requireAccess = withAccessToken(() => tokenSelector.ensureAccessToken());\n\n return {\n create: (params) =>\n requireAccess((access) => api.createTicket(baseUrl, realm, access.tokenBase64, params)),\n\n list: (params) =>\n requireAccess((access) => api.listTickets(baseUrl, realm, access.tokenBase64, params)),\n\n get: (ticketId) =>\n requireAccess((access) => api.getTicket(baseUrl, realm, access.tokenBase64, ticketId)),\n\n submit: (ticketId, params) =>\n requireAccess((access) =>\n api.submitTicket(baseUrl, realm, access.tokenBase64, ticketId, params)\n ),\n };\n};\n","/**\n * Token management methods for the stateful client.\n */\n\nimport type { CreateToken, CreateTokenResponse } from \"@casfa/protocol\";\nimport * as api from \"../api/index.ts\";\nimport type { RefreshManager } from \"../store/jwt-refresh.ts\";\nimport type { TokenSelector } from \"../store/token-selector.ts\";\nimport type { TokenStore } from \"../store/token-store.ts\";\nimport type { FetchResult } from \"../types/client.ts\";\nimport type { StoredAccessToken, StoredDelegateToken } from \"../types/tokens.ts\";\nimport { withDelegateToken, withUserToken } from \"./helpers.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type TokenMethods = {\n /** Create a new token (User JWT required) */\n create: (params: CreateToken) => Promise<FetchResult<StoredDelegateToken | StoredAccessToken>>;\n /** List tokens (User JWT required) */\n list: (params?: api.ListTokensParams) => Promise<FetchResult<api.ListTokensResponse>>;\n /** Revoke a token (User JWT required) */\n revoke: (tokenId: string) => Promise<FetchResult<void>>;\n /** Delegate a token using current Delegate Token */\n delegate: (\n params: api.DelegateTokenParams\n ) => Promise<FetchResult<StoredDelegateToken | StoredAccessToken>>;\n};\n\nexport type TokenDeps = {\n baseUrl: string;\n realm: string;\n store: TokenStore;\n refreshManager: RefreshManager;\n tokenSelector: TokenSelector;\n};\n\n// ============================================================================\n// Factory\n// ============================================================================\n\nexport const createTokenMethods = ({\n baseUrl,\n realm,\n store,\n refreshManager,\n tokenSelector,\n}: TokenDeps): TokenMethods => {\n const requireUser = withUserToken(() => refreshManager.ensureValidUserToken());\n const requireDelegate = withDelegateToken(() => tokenSelector.ensureDelegateToken());\n\n return {\n create: (params) =>\n requireUser(async (user) => {\n const result = await api.createToken(baseUrl, user.accessToken, params);\n if (!result.ok) return result;\n\n const newToken = toStoredToken(result.data);\n\n // Auto-store if for current realm\n if (params.realm === realm) {\n if (params.type === \"delegate\") {\n store.setDelegate(newToken as StoredDelegateToken);\n } else {\n store.setAccess(newToken as StoredAccessToken);\n }\n }\n\n return { ok: true, data: newToken, status: result.status };\n }),\n\n list: (params) => requireUser((user) => api.listTokens(baseUrl, user.accessToken, params)),\n\n revoke: (tokenId) =>\n requireUser(async (user) => {\n const result = await api.revokeToken(baseUrl, user.accessToken, tokenId);\n if (!result.ok) return { ok: false, error: result.error };\n\n // Clear local token if it matches\n const state = store.getState();\n if (state.delegate?.tokenId === tokenId) store.setDelegate(null);\n if (state.access?.tokenId === tokenId) store.setAccess(null);\n\n return { ok: true, data: undefined, status: result.status };\n }),\n\n delegate: (params) =>\n requireDelegate(async (delegate) => {\n const result = await api.delegateToken(baseUrl, delegate.tokenBase64, params);\n if (!result.ok) return result;\n return { ok: true, data: toStoredToken(result.data), status: result.status };\n }),\n };\n};\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nconst toStoredToken = (response: CreateTokenResponse): StoredDelegateToken | StoredAccessToken => ({\n tokenId: response.tokenId,\n tokenBase64: response.tokenBase64,\n type: (response as { type?: \"delegate\" | \"access\" }).type ?? \"delegate\",\n issuerId: (response as { issuerId?: string }).issuerId ?? \"\",\n expiresAt: response.expiresAt,\n canUpload: (response as { canUpload?: boolean }).canUpload ?? false,\n canManageDepot: (response as { canManageDepot?: boolean }).canManageDepot ?? false,\n});\n","/**\n * Stateful CASFA Client\n *\n * A closure-based client that manages three-tier token hierarchy:\n * - User JWT: OAuth login token, highest authority\n * - Delegate Token: Re-delegation token, can issue child tokens\n * - Access Token: Data access token, used for CAS operations\n */\n\nimport type { ServiceInfo } from \"@casfa/protocol\";\nimport * as api from \"../api/index.ts\";\nimport { createRefreshManager } from \"../store/jwt-refresh.ts\";\nimport { createTokenSelector } from \"../store/token-selector.ts\";\nimport { createTokenStore } from \"../store/token-store.ts\";\nimport type {\n ClientConfig,\n OnAuthRequiredCallback,\n OnTokenChangeCallback,\n TokenStorageProvider,\n} from \"../types/client.ts\";\nimport type { StoredAccessToken, StoredDelegateToken, TokenState } from \"../types/tokens.ts\";\nimport { createDepotMethods, type DepotMethods } from \"./depots.ts\";\nimport { createNodeMethods, type NodeMethods } from \"./nodes.ts\";\nimport { createOAuthMethods, type OAuthMethods } from \"./oauth.ts\";\nimport { createTicketMethods, type TicketMethods } from \"./tickets.ts\";\nimport { createTokenMethods, type TokenMethods } from \"./tokens.ts\";\n\n// ============================================================================\n// Re-exports\n// ============================================================================\n\nexport type { DepotMethods, NodeMethods, OAuthMethods, TicketMethods, TokenMethods };\n\nexport type { ClientConfig, OnAuthRequiredCallback, OnTokenChangeCallback, TokenStorageProvider };\n\n// ============================================================================\n// Client Type\n// ============================================================================\n\n/**\n * The stateful CASFA client.\n */\nexport type CasfaClient = {\n /** Get current token state */\n getState: () => TokenState;\n /** Get server info */\n getServerInfo: () => ServiceInfo | null;\n\n /** Set delegate token (e.g., from external source) */\n setDelegateToken: (token: StoredDelegateToken) => void;\n /** Set access token (e.g., from external source) */\n setAccessToken: (token: StoredAccessToken) => void;\n /** Clear all tokens and logout */\n logout: () => void;\n\n /** OAuth methods */\n oauth: OAuthMethods;\n /** Token management methods */\n tokens: TokenMethods;\n /** Ticket methods */\n tickets: TicketMethods;\n /** Depot methods */\n depots: DepotMethods;\n /** Node methods */\n nodes: NodeMethods;\n};\n\n// ============================================================================\n// Client Factory\n// ============================================================================\n\n/**\n * Create a stateful CASFA client.\n */\nexport const createClient = async (config: ClientConfig): Promise<CasfaClient> => {\n const { baseUrl, realm, tokenStorage, onTokenChange, onAuthRequired, defaultTokenTtl } = config;\n\n // Initialize token store\n const store = createTokenStore({\n storage: tokenStorage,\n onTokenChange,\n onAuthRequired,\n });\n await store.initialize();\n\n // Initialize refresh manager\n const refreshManager = createRefreshManager({\n store,\n baseUrl,\n onAuthRequired,\n });\n\n // Fetch server info\n let serverInfo: ServiceInfo | null = null;\n const infoResult = await api.fetchServiceInfo(baseUrl);\n if (infoResult.ok) {\n serverInfo = infoResult.data;\n }\n\n // Initialize token selector\n const tokenSelector = createTokenSelector({\n store,\n baseUrl,\n realm,\n serverInfo,\n defaultTokenTtl,\n });\n\n // Shared dependencies\n const deps = { baseUrl, realm, store, refreshManager, tokenSelector };\n\n // Build client\n return {\n getState: () => store.getState(),\n getServerInfo: () => serverInfo,\n\n setDelegateToken: (token) => store.setDelegate(token),\n setAccessToken: (token) => store.setAccess(token),\n\n logout: () => {\n refreshManager.cancelScheduledRefresh();\n store.clear();\n },\n\n oauth: createOAuthMethods(deps),\n tokens: createTokenMethods(deps),\n tickets: createTicketMethods(deps),\n depots: createDepotMethods(deps),\n nodes: createNodeMethods(deps),\n };\n};\n"],"mappings":"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","names":["delegateToken","delegateToken","getToken"]}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token types for the stateful client.
|
|
3
|
+
*
|
|
4
|
+
* Three-tier token hierarchy:
|
|
5
|
+
* - User JWT: OAuth login token, highest authority
|
|
6
|
+
* - Delegate Token: Re-delegation token, can issue child tokens
|
|
7
|
+
* - Access Token: Data access token, used for CAS operations
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* User JWT token with refresh capability.
|
|
11
|
+
*/
|
|
12
|
+
type StoredUserToken = {
|
|
13
|
+
/** JWT access token */
|
|
14
|
+
accessToken: string;
|
|
15
|
+
/** Refresh token for token renewal */
|
|
16
|
+
refreshToken: string;
|
|
17
|
+
/** User ID (usr_xxx format) */
|
|
18
|
+
userId: string;
|
|
19
|
+
/** Token expiration time (epoch ms) */
|
|
20
|
+
expiresAt: number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Delegate Token (re-delegation token).
|
|
24
|
+
*/
|
|
25
|
+
type StoredDelegateToken = {
|
|
26
|
+
/** Token ID (dlt1_xxx format) */
|
|
27
|
+
tokenId: string;
|
|
28
|
+
/** Token binary as Base64 */
|
|
29
|
+
tokenBase64: string;
|
|
30
|
+
/** Token type: always "delegate" */
|
|
31
|
+
type: "delegate";
|
|
32
|
+
/** Issuer ID (usr_xxx or dlt1_xxx) */
|
|
33
|
+
issuerId: string;
|
|
34
|
+
/** Token expiration time (epoch ms) */
|
|
35
|
+
expiresAt: number;
|
|
36
|
+
/** Whether the token can upload nodes */
|
|
37
|
+
canUpload: boolean;
|
|
38
|
+
/** Whether the token can manage depots */
|
|
39
|
+
canManageDepot: boolean;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Access Token (data access token).
|
|
43
|
+
*/
|
|
44
|
+
type StoredAccessToken = {
|
|
45
|
+
/** Token ID (dlt1_xxx format) */
|
|
46
|
+
tokenId: string;
|
|
47
|
+
/** Token binary as Base64 */
|
|
48
|
+
tokenBase64: string;
|
|
49
|
+
/** Token type: always "access" */
|
|
50
|
+
type: "access";
|
|
51
|
+
/** Issuer ID (usr_xxx or dlt1_xxx) */
|
|
52
|
+
issuerId: string;
|
|
53
|
+
/** Token expiration time (epoch ms) */
|
|
54
|
+
expiresAt: number;
|
|
55
|
+
/** Whether the token can upload nodes */
|
|
56
|
+
canUpload: boolean;
|
|
57
|
+
/** Whether the token can manage depots */
|
|
58
|
+
canManageDepot: boolean;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Complete token state held by the client.
|
|
62
|
+
*/
|
|
63
|
+
type TokenState = {
|
|
64
|
+
/** User JWT (optional) */
|
|
65
|
+
user: StoredUserToken | null;
|
|
66
|
+
/** Delegate Token (optional) */
|
|
67
|
+
delegate: StoredDelegateToken | null;
|
|
68
|
+
/** Access Token (optional) */
|
|
69
|
+
access: StoredAccessToken | null;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Empty token state.
|
|
73
|
+
*/
|
|
74
|
+
declare const emptyTokenState: () => TokenState;
|
|
75
|
+
/**
|
|
76
|
+
* Token requirement for API calls.
|
|
77
|
+
*/
|
|
78
|
+
type TokenRequirement = "none" | "user" | "delegate" | "access";
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Client configuration and callback types.
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Token storage provider for persistence.
|
|
86
|
+
* Allows users to persist tokens to localStorage, file, etc.
|
|
87
|
+
*/
|
|
88
|
+
type TokenStorageProvider = {
|
|
89
|
+
/** Load persisted token state */
|
|
90
|
+
load: () => Promise<TokenState | null>;
|
|
91
|
+
/** Save token state */
|
|
92
|
+
save: (state: TokenState) => Promise<void>;
|
|
93
|
+
/** Clear all persisted tokens */
|
|
94
|
+
clear: () => Promise<void>;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Callback when token state changes.
|
|
98
|
+
* Called after any token is added, removed, or refreshed.
|
|
99
|
+
*/
|
|
100
|
+
type OnTokenChangeCallback = (state: TokenState) => void;
|
|
101
|
+
/**
|
|
102
|
+
* Callback when authentication is required.
|
|
103
|
+
* Called when token refresh fails and user needs to re-login.
|
|
104
|
+
*/
|
|
105
|
+
type OnAuthRequiredCallback = () => void;
|
|
106
|
+
/**
|
|
107
|
+
* Configuration for creating a CASFA client.
|
|
108
|
+
*/
|
|
109
|
+
type ClientConfig = {
|
|
110
|
+
/** Base URL of the CASFA server (e.g., "https://api.casfa.app") */
|
|
111
|
+
baseUrl: string;
|
|
112
|
+
/** Realm ID this client operates on (e.g., "usr_abc123") */
|
|
113
|
+
realm: string;
|
|
114
|
+
/** Optional token storage provider for persistence */
|
|
115
|
+
tokenStorage?: TokenStorageProvider;
|
|
116
|
+
/** Default TTL for auto-issued tokens (seconds). If not set, uses server max. */
|
|
117
|
+
defaultTokenTtl?: number;
|
|
118
|
+
/** Callback when token state changes */
|
|
119
|
+
onTokenChange?: OnTokenChangeCallback;
|
|
120
|
+
/** Callback when auth is required (refresh failed) */
|
|
121
|
+
onAuthRequired?: OnAuthRequiredCallback;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Fetch result type.
|
|
125
|
+
*/
|
|
126
|
+
type FetchResult<T> = {
|
|
127
|
+
ok: true;
|
|
128
|
+
data: T;
|
|
129
|
+
status: number;
|
|
130
|
+
} | {
|
|
131
|
+
ok: false;
|
|
132
|
+
error: ClientError;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Client error type.
|
|
136
|
+
*/
|
|
137
|
+
type ClientError = {
|
|
138
|
+
code: string;
|
|
139
|
+
message: string;
|
|
140
|
+
status?: number;
|
|
141
|
+
details?: unknown;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export { type ClientConfig, type ClientError, type FetchResult, type OnAuthRequiredCallback, type OnTokenChangeCallback, type StoredAccessToken, type StoredDelegateToken, type StoredUserToken, type TokenRequirement, type TokenState, type TokenStorageProvider, emptyTokenState };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/types/tokens.ts"],"sourcesContent":["/**\n * Token types for the stateful client.\n *\n * Three-tier token hierarchy:\n * - User JWT: OAuth login token, highest authority\n * - Delegate Token: Re-delegation token, can issue child tokens\n * - Access Token: Data access token, used for CAS operations\n */\n\n// ============================================================================\n// Stored Token Types\n// ============================================================================\n\n/**\n * User JWT token with refresh capability.\n */\nexport type StoredUserToken = {\n /** JWT access token */\n accessToken: string;\n /** Refresh token for token renewal */\n refreshToken: string;\n /** User ID (usr_xxx format) */\n userId: string;\n /** Token expiration time (epoch ms) */\n expiresAt: number;\n};\n\n/**\n * Delegate Token (re-delegation token).\n */\nexport type StoredDelegateToken = {\n /** Token ID (dlt1_xxx format) */\n tokenId: string;\n /** Token binary as Base64 */\n tokenBase64: string;\n /** Token type: always \"delegate\" */\n type: \"delegate\";\n /** Issuer ID (usr_xxx or dlt1_xxx) */\n issuerId: string;\n /** Token expiration time (epoch ms) */\n expiresAt: number;\n /** Whether the token can upload nodes */\n canUpload: boolean;\n /** Whether the token can manage depots */\n canManageDepot: boolean;\n};\n\n/**\n * Access Token (data access token).\n */\nexport type StoredAccessToken = {\n /** Token ID (dlt1_xxx format) */\n tokenId: string;\n /** Token binary as Base64 */\n tokenBase64: string;\n /** Token type: always \"access\" */\n type: \"access\";\n /** Issuer ID (usr_xxx or dlt1_xxx) */\n issuerId: string;\n /** Token expiration time (epoch ms) */\n expiresAt: number;\n /** Whether the token can upload nodes */\n canUpload: boolean;\n /** Whether the token can manage depots */\n canManageDepot: boolean;\n};\n\n/**\n * Complete token state held by the client.\n */\nexport type TokenState = {\n /** User JWT (optional) */\n user: StoredUserToken | null;\n /** Delegate Token (optional) */\n delegate: StoredDelegateToken | null;\n /** Access Token (optional) */\n access: StoredAccessToken | null;\n};\n\n/**\n * Empty token state.\n */\nexport const emptyTokenState = (): TokenState => ({\n user: null,\n delegate: null,\n access: null,\n});\n\n// ============================================================================\n// Token Requirement Types\n// ============================================================================\n\n/**\n * Token requirement for API calls.\n */\nexport type TokenRequirement = \"none\" | \"user\" | \"delegate\" | \"access\";\n\n/**\n * Auth header format.\n */\nexport type AuthHeader = {\n Authorization: string;\n};\n\n/**\n * Get issuer ID from current state (for signing new tokens).\n * Priority: User JWT > Delegate Token\n */\nexport const getMaxIssuerId = (state: TokenState): string | null => {\n if (state.user) {\n return state.user.userId;\n }\n if (state.delegate) {\n return state.delegate.tokenId;\n }\n return null;\n};\n\n/**\n * Check if Access Token was issued by the current max issuer.\n */\nexport const isAccessTokenFromMaxIssuer = (state: TokenState): boolean => {\n if (!state.access) return false;\n\n const maxIssuerId = getMaxIssuerId(state);\n if (!maxIssuerId) return false;\n\n return state.access.issuerId === maxIssuerId;\n};\n\n/**\n * Check if Delegate Token was issued by current user.\n */\nexport const isDelegateTokenFromCurrentUser = (state: TokenState): boolean => {\n if (!state.delegate || !state.user) return false;\n return state.delegate.issuerId === state.user.userId;\n};\n"],"mappings":";AAkFO,IAAM,kBAAkB,OAAmB;AAAA,EAChD,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AACV;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@casfa/client",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CASFA client library with unified authorization strategies",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./types": {
|
|
14
|
+
"types": "./dist/types/index.d.ts",
|
|
15
|
+
"import": "./dist/types/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./api": {
|
|
18
|
+
"types": "./dist/api/index.d.ts",
|
|
19
|
+
"import": "./dist/api/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"dev": "tsup --watch",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"lint": "biome check .",
|
|
30
|
+
"lint:fix": "biome check --write .",
|
|
31
|
+
"check": "tsc --noEmit && biome check .",
|
|
32
|
+
"prepublishOnly": "bun run build"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@casfa/cas-uri": "workspace:*",
|
|
36
|
+
"@casfa/client-auth-crypto": "workspace:*",
|
|
37
|
+
"@casfa/delegate-token": "workspace:*",
|
|
38
|
+
"@casfa/protocol": "workspace:*"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@casfa/storage-core": "workspace:*"
|
|
42
|
+
},
|
|
43
|
+
"peerDependenciesMeta": {
|
|
44
|
+
"@casfa/storage-core": {
|
|
45
|
+
"optional": true
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"typescript": "^5.3.0"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"license": "MIT"
|
|
55
|
+
}
|