@hypercerts-org/sdk-core 0.5.0-beta.0 → 0.7.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -8
- package/dist/index.cjs +93 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +64 -1
- package/dist/index.mjs +93 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -5
- package/.turbo/turbo-build.log +0 -40
- package/.turbo/turbo-test.log +0 -119
- package/CHANGELOG.md +0 -62
- package/eslint.config.mjs +0 -22
- package/rollup.config.js +0 -75
- package/src/auth/OAuthClient.ts +0 -497
- package/src/core/SDK.ts +0 -410
- package/src/core/config.ts +0 -243
- package/src/core/errors.ts +0 -257
- package/src/core/interfaces.ts +0 -324
- package/src/core/types.ts +0 -282
- package/src/errors.ts +0 -57
- package/src/index.ts +0 -107
- package/src/lexicons.ts +0 -64
- package/src/repository/BlobOperationsImpl.ts +0 -199
- package/src/repository/CollaboratorOperationsImpl.ts +0 -442
- package/src/repository/HypercertOperationsImpl.ts +0 -1146
- package/src/repository/LexiconRegistry.ts +0 -332
- package/src/repository/OrganizationOperationsImpl.ts +0 -282
- package/src/repository/ProfileOperationsImpl.ts +0 -281
- package/src/repository/RecordOperationsImpl.ts +0 -340
- package/src/repository/Repository.ts +0 -482
- package/src/repository/interfaces.ts +0 -909
- package/src/repository/types.ts +0 -111
- package/src/services/hypercerts/types.ts +0 -87
- package/src/storage/InMemorySessionStore.ts +0 -127
- package/src/storage/InMemoryStateStore.ts +0 -146
- package/src/storage.ts +0 -63
- package/src/testing/index.ts +0 -67
- package/src/testing/mocks.ts +0 -142
- package/src/testing/stores.ts +0 -285
- package/src/testing.ts +0 -64
- package/src/types.ts +0 -86
- package/tests/auth/OAuthClient.test.ts +0 -164
- package/tests/core/SDK.test.ts +0 -176
- package/tests/core/errors.test.ts +0 -81
- package/tests/repository/BlobOperationsImpl.test.ts +0 -155
- package/tests/repository/CollaboratorOperationsImpl.test.ts +0 -438
- package/tests/repository/HypercertOperationsImpl.test.ts +0 -652
- package/tests/repository/LexiconRegistry.test.ts +0 -192
- package/tests/repository/OrganizationOperationsImpl.test.ts +0 -240
- package/tests/repository/ProfileOperationsImpl.test.ts +0 -254
- package/tests/repository/RecordOperationsImpl.test.ts +0 -375
- package/tests/repository/Repository.test.ts +0 -149
- package/tests/utils/fixtures.ts +0 -117
- package/tests/utils/mocks.ts +0 -109
- package/tests/utils/repository-fixtures.ts +0 -78
- package/tsconfig.json +0 -11
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -30
package/src/core/errors.ts
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base error class for all SDK errors.
|
|
3
|
-
*
|
|
4
|
-
* All errors thrown by the Hypercerts SDK extend this class, making it easy
|
|
5
|
-
* to catch and handle SDK-specific errors.
|
|
6
|
-
*
|
|
7
|
-
* @example Catching all SDK errors
|
|
8
|
-
* ```typescript
|
|
9
|
-
* try {
|
|
10
|
-
* await sdk.authorize("user.bsky.social");
|
|
11
|
-
* } catch (error) {
|
|
12
|
-
* if (error instanceof ATProtoSDKError) {
|
|
13
|
-
* console.error(`SDK Error [${error.code}]: ${error.message}`);
|
|
14
|
-
* console.error(`HTTP Status: ${error.status}`);
|
|
15
|
-
* }
|
|
16
|
-
* }
|
|
17
|
-
* ```
|
|
18
|
-
*
|
|
19
|
-
* @example Checking error codes
|
|
20
|
-
* ```typescript
|
|
21
|
-
* try {
|
|
22
|
-
* await repo.records.get(collection, rkey);
|
|
23
|
-
* } catch (error) {
|
|
24
|
-
* if (error instanceof ATProtoSDKError) {
|
|
25
|
-
* switch (error.code) {
|
|
26
|
-
* case "AUTHENTICATION_ERROR":
|
|
27
|
-
* // Redirect to login
|
|
28
|
-
* break;
|
|
29
|
-
* case "VALIDATION_ERROR":
|
|
30
|
-
* // Show form errors
|
|
31
|
-
* break;
|
|
32
|
-
* case "NETWORK_ERROR":
|
|
33
|
-
* // Retry or show offline message
|
|
34
|
-
* break;
|
|
35
|
-
* }
|
|
36
|
-
* }
|
|
37
|
-
* }
|
|
38
|
-
* ```
|
|
39
|
-
*/
|
|
40
|
-
export class ATProtoSDKError extends Error {
|
|
41
|
-
/**
|
|
42
|
-
* Creates a new SDK error.
|
|
43
|
-
*
|
|
44
|
-
* @param message - Human-readable error description
|
|
45
|
-
* @param code - Machine-readable error code for programmatic handling
|
|
46
|
-
* @param status - HTTP status code associated with this error type
|
|
47
|
-
* @param cause - The underlying error that caused this error, if any
|
|
48
|
-
*/
|
|
49
|
-
constructor(
|
|
50
|
-
message: string,
|
|
51
|
-
public code: string,
|
|
52
|
-
public status?: number,
|
|
53
|
-
public cause?: unknown,
|
|
54
|
-
) {
|
|
55
|
-
super(message);
|
|
56
|
-
this.name = "ATProtoSDKError";
|
|
57
|
-
Error.captureStackTrace?.(this, this.constructor);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Error thrown when authentication fails.
|
|
63
|
-
*
|
|
64
|
-
* This error indicates problems with the OAuth flow, invalid credentials,
|
|
65
|
-
* or failed token exchanges. Common causes:
|
|
66
|
-
* - Invalid authorization code
|
|
67
|
-
* - Expired or invalid state parameter
|
|
68
|
-
* - Revoked or invalid tokens
|
|
69
|
-
* - User denied authorization
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* ```typescript
|
|
73
|
-
* try {
|
|
74
|
-
* const session = await sdk.callback(params);
|
|
75
|
-
* } catch (error) {
|
|
76
|
-
* if (error instanceof AuthenticationError) {
|
|
77
|
-
* // Clear any stored state and redirect to login
|
|
78
|
-
* console.error("Authentication failed:", error.message);
|
|
79
|
-
* }
|
|
80
|
-
* }
|
|
81
|
-
* ```
|
|
82
|
-
*/
|
|
83
|
-
export class AuthenticationError extends ATProtoSDKError {
|
|
84
|
-
/**
|
|
85
|
-
* Creates an authentication error.
|
|
86
|
-
*
|
|
87
|
-
* @param message - Description of what went wrong during authentication
|
|
88
|
-
* @param cause - The underlying error (e.g., from the OAuth client)
|
|
89
|
-
*/
|
|
90
|
-
constructor(message: string, cause?: unknown) {
|
|
91
|
-
super(message, "AUTHENTICATION_ERROR", 401, cause);
|
|
92
|
-
this.name = "AuthenticationError";
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Error thrown when a session has expired and cannot be refreshed.
|
|
98
|
-
*
|
|
99
|
-
* This typically occurs when:
|
|
100
|
-
* - The refresh token has expired (usually after extended inactivity)
|
|
101
|
-
* - The user has revoked access to your application
|
|
102
|
-
* - The PDS has invalidated all sessions for the user
|
|
103
|
-
*
|
|
104
|
-
* When this error occurs, the user must re-authenticate.
|
|
105
|
-
*
|
|
106
|
-
* @example
|
|
107
|
-
* ```typescript
|
|
108
|
-
* try {
|
|
109
|
-
* const session = await sdk.restoreSession(did);
|
|
110
|
-
* } catch (error) {
|
|
111
|
-
* if (error instanceof SessionExpiredError) {
|
|
112
|
-
* // Clear stored session and prompt user to log in again
|
|
113
|
-
* localStorage.removeItem("userDid");
|
|
114
|
-
* window.location.href = "/login";
|
|
115
|
-
* }
|
|
116
|
-
* }
|
|
117
|
-
* ```
|
|
118
|
-
*/
|
|
119
|
-
export class SessionExpiredError extends ATProtoSDKError {
|
|
120
|
-
/**
|
|
121
|
-
* Creates a session expired error.
|
|
122
|
-
*
|
|
123
|
-
* @param message - Description of why the session expired
|
|
124
|
-
* @param cause - The underlying error from the token refresh attempt
|
|
125
|
-
*/
|
|
126
|
-
constructor(message: string = "Session expired", cause?: unknown) {
|
|
127
|
-
super(message, "SESSION_EXPIRED", 401, cause);
|
|
128
|
-
this.name = "SessionExpiredError";
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Error thrown when input validation fails.
|
|
134
|
-
*
|
|
135
|
-
* This error indicates that provided data doesn't meet the required format
|
|
136
|
-
* or constraints. Common causes:
|
|
137
|
-
* - Missing required fields
|
|
138
|
-
* - Invalid URL formats
|
|
139
|
-
* - Invalid DID format
|
|
140
|
-
* - Schema validation failures for records
|
|
141
|
-
* - Invalid configuration values
|
|
142
|
-
*
|
|
143
|
-
* @example
|
|
144
|
-
* ```typescript
|
|
145
|
-
* try {
|
|
146
|
-
* await sdk.authorize(""); // Empty identifier
|
|
147
|
-
* } catch (error) {
|
|
148
|
-
* if (error instanceof ValidationError) {
|
|
149
|
-
* console.error("Invalid input:", error.message);
|
|
150
|
-
* // Show validation error to user
|
|
151
|
-
* }
|
|
152
|
-
* }
|
|
153
|
-
* ```
|
|
154
|
-
*
|
|
155
|
-
* @example With Zod validation cause
|
|
156
|
-
* ```typescript
|
|
157
|
-
* try {
|
|
158
|
-
* await repo.records.create(collection, record);
|
|
159
|
-
* } catch (error) {
|
|
160
|
-
* if (error instanceof ValidationError && error.cause) {
|
|
161
|
-
* // error.cause may be a ZodError with detailed field errors
|
|
162
|
-
* const zodError = error.cause as ZodError;
|
|
163
|
-
* zodError.errors.forEach(e => {
|
|
164
|
-
* console.error(`Field ${e.path.join(".")}: ${e.message}`);
|
|
165
|
-
* });
|
|
166
|
-
* }
|
|
167
|
-
* }
|
|
168
|
-
* ```
|
|
169
|
-
*/
|
|
170
|
-
export class ValidationError extends ATProtoSDKError {
|
|
171
|
-
/**
|
|
172
|
-
* Creates a validation error.
|
|
173
|
-
*
|
|
174
|
-
* @param message - Description of what validation failed
|
|
175
|
-
* @param cause - The underlying validation error (e.g., ZodError)
|
|
176
|
-
*/
|
|
177
|
-
constructor(message: string, cause?: unknown) {
|
|
178
|
-
super(message, "VALIDATION_ERROR", 400, cause);
|
|
179
|
-
this.name = "ValidationError";
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Error thrown when a network request fails.
|
|
185
|
-
*
|
|
186
|
-
* This error indicates connectivity issues or server unavailability.
|
|
187
|
-
* Common causes:
|
|
188
|
-
* - No internet connection
|
|
189
|
-
* - DNS resolution failure
|
|
190
|
-
* - Server timeout
|
|
191
|
-
* - Server returned 5xx error
|
|
192
|
-
* - TLS/SSL errors
|
|
193
|
-
*
|
|
194
|
-
* These errors are typically transient and may succeed on retry.
|
|
195
|
-
*
|
|
196
|
-
* @example
|
|
197
|
-
* ```typescript
|
|
198
|
-
* try {
|
|
199
|
-
* await repo.records.list(collection);
|
|
200
|
-
* } catch (error) {
|
|
201
|
-
* if (error instanceof NetworkError) {
|
|
202
|
-
* // Implement retry logic or show offline indicator
|
|
203
|
-
* console.error("Network error:", error.message);
|
|
204
|
-
* await retryWithBackoff(() => repo.records.list(collection));
|
|
205
|
-
* }
|
|
206
|
-
* }
|
|
207
|
-
* ```
|
|
208
|
-
*/
|
|
209
|
-
export class NetworkError extends ATProtoSDKError {
|
|
210
|
-
/**
|
|
211
|
-
* Creates a network error.
|
|
212
|
-
*
|
|
213
|
-
* @param message - Description of the network failure
|
|
214
|
-
* @param cause - The underlying error (e.g., fetch error, timeout)
|
|
215
|
-
*/
|
|
216
|
-
constructor(message: string, cause?: unknown) {
|
|
217
|
-
super(message, "NETWORK_ERROR", 503, cause);
|
|
218
|
-
this.name = "NetworkError";
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Error thrown when an SDS-only operation is attempted on a PDS.
|
|
224
|
-
*
|
|
225
|
-
* Certain operations are only available on Shared Data Servers (SDS),
|
|
226
|
-
* such as collaborator management and organization operations.
|
|
227
|
-
* This error is thrown when these operations are attempted on a
|
|
228
|
-
* Personal Data Server (PDS).
|
|
229
|
-
*
|
|
230
|
-
* @example
|
|
231
|
-
* ```typescript
|
|
232
|
-
* const pdsRepo = sdk.repository(session); // Default is PDS
|
|
233
|
-
*
|
|
234
|
-
* try {
|
|
235
|
-
* // This will throw SDSRequiredError
|
|
236
|
-
* await pdsRepo.collaborators.list();
|
|
237
|
-
* } catch (error) {
|
|
238
|
-
* if (error instanceof SDSRequiredError) {
|
|
239
|
-
* // Switch to SDS for this operation
|
|
240
|
-
* const sdsRepo = sdk.repository(session, { server: "sds" });
|
|
241
|
-
* const collaborators = await sdsRepo.collaborators.list();
|
|
242
|
-
* }
|
|
243
|
-
* }
|
|
244
|
-
* ```
|
|
245
|
-
*/
|
|
246
|
-
export class SDSRequiredError extends ATProtoSDKError {
|
|
247
|
-
/**
|
|
248
|
-
* Creates an SDS required error.
|
|
249
|
-
*
|
|
250
|
-
* @param message - Description of which operation requires SDS
|
|
251
|
-
* @param cause - Any underlying error
|
|
252
|
-
*/
|
|
253
|
-
constructor(message: string = "This operation requires a Shared Data Server (SDS)", cause?: unknown) {
|
|
254
|
-
super(message, "SDS_REQUIRED", 400, cause);
|
|
255
|
-
this.name = "SDSRequiredError";
|
|
256
|
-
}
|
|
257
|
-
}
|
package/src/core/interfaces.ts
DELETED
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
import type { NodeSavedSession, NodeSavedState } from "@atproto/oauth-client-node";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Storage interface for persisting OAuth sessions.
|
|
5
|
-
*
|
|
6
|
-
* Implement this interface to provide persistent storage for user sessions.
|
|
7
|
-
* Sessions contain sensitive data including access tokens, refresh tokens,
|
|
8
|
-
* and DPoP key pairs.
|
|
9
|
-
*
|
|
10
|
-
* The SDK provides {@link InMemorySessionStore} for development/testing,
|
|
11
|
-
* but **production applications should implement persistent storage**
|
|
12
|
-
* (e.g., Redis, PostgreSQL, MongoDB).
|
|
13
|
-
*
|
|
14
|
-
* @remarks
|
|
15
|
-
* - Sessions are keyed by the user's DID (Decentralized Identifier)
|
|
16
|
-
* - The `NodeSavedSession` type comes from `@atproto/oauth-client-node`
|
|
17
|
-
* - Sessions may be large (~2-4KB) due to embedded cryptographic keys
|
|
18
|
-
* - Consider encrypting sessions at rest for additional security
|
|
19
|
-
*
|
|
20
|
-
* @example Redis implementation
|
|
21
|
-
* ```typescript
|
|
22
|
-
* import { Redis } from "ioredis";
|
|
23
|
-
* import type { SessionStore } from "@hypercerts-org/sdk";
|
|
24
|
-
* import type { NodeSavedSession } from "@atproto/oauth-client-node";
|
|
25
|
-
*
|
|
26
|
-
* class RedisSessionStore implements SessionStore {
|
|
27
|
-
* constructor(private redis: Redis, private prefix = "session:") {}
|
|
28
|
-
*
|
|
29
|
-
* async get(did: string): Promise<NodeSavedSession | undefined> {
|
|
30
|
-
* const data = await this.redis.get(this.prefix + did);
|
|
31
|
-
* return data ? JSON.parse(data) : undefined;
|
|
32
|
-
* }
|
|
33
|
-
*
|
|
34
|
-
* async set(did: string, session: NodeSavedSession): Promise<void> {
|
|
35
|
-
* // Set with 30-day expiry (sessions can be refreshed)
|
|
36
|
-
* await this.redis.setex(
|
|
37
|
-
* this.prefix + did,
|
|
38
|
-
* 30 * 24 * 60 * 60,
|
|
39
|
-
* JSON.stringify(session)
|
|
40
|
-
* );
|
|
41
|
-
* }
|
|
42
|
-
*
|
|
43
|
-
* async del(did: string): Promise<void> {
|
|
44
|
-
* await this.redis.del(this.prefix + did);
|
|
45
|
-
* }
|
|
46
|
-
* }
|
|
47
|
-
* ```
|
|
48
|
-
*
|
|
49
|
-
* @see {@link InMemorySessionStore} for the default in-memory implementation
|
|
50
|
-
*/
|
|
51
|
-
export interface SessionStore {
|
|
52
|
-
/**
|
|
53
|
-
* Retrieves a session by DID.
|
|
54
|
-
*
|
|
55
|
-
* @param did - The user's Decentralized Identifier (e.g., "did:plc:abc123...")
|
|
56
|
-
* @returns The stored session, or `undefined` if not found
|
|
57
|
-
*/
|
|
58
|
-
get(did: string): Promise<NodeSavedSession | undefined>;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Stores or updates a session.
|
|
62
|
-
*
|
|
63
|
-
* This is called after successful authentication and whenever tokens are refreshed.
|
|
64
|
-
*
|
|
65
|
-
* @param did - The user's DID to use as the storage key
|
|
66
|
-
* @param session - The session data to store (contains tokens, DPoP keys, etc.)
|
|
67
|
-
*/
|
|
68
|
-
set(did: string, session: NodeSavedSession): Promise<void>;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Deletes a session.
|
|
72
|
-
*
|
|
73
|
-
* Called when a user logs out or when a session is revoked.
|
|
74
|
-
*
|
|
75
|
-
* @param did - The user's DID
|
|
76
|
-
*/
|
|
77
|
-
del(did: string): Promise<void>;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Storage interface for OAuth state during the authorization flow.
|
|
82
|
-
*
|
|
83
|
-
* Implement this interface to provide temporary storage for OAuth state
|
|
84
|
-
* parameters. State is used for CSRF protection and PKCE (Proof Key for
|
|
85
|
-
* Code Exchange) during the authorization flow.
|
|
86
|
-
*
|
|
87
|
-
* @remarks
|
|
88
|
-
* - State is short-lived (typically 10-15 minutes)
|
|
89
|
-
* - Keys are random state strings generated by the OAuth client
|
|
90
|
-
* - The `NodeSavedState` type comes from `@atproto/oauth-client-node`
|
|
91
|
-
* - State should be automatically cleaned up after expiry
|
|
92
|
-
*
|
|
93
|
-
* @example Redis implementation with automatic expiry
|
|
94
|
-
* ```typescript
|
|
95
|
-
* import { Redis } from "ioredis";
|
|
96
|
-
* import type { StateStore } from "@hypercerts-org/sdk";
|
|
97
|
-
* import type { NodeSavedState } from "@atproto/oauth-client-node";
|
|
98
|
-
*
|
|
99
|
-
* class RedisStateStore implements StateStore {
|
|
100
|
-
* constructor(
|
|
101
|
-
* private redis: Redis,
|
|
102
|
-
* private prefix = "oauth-state:",
|
|
103
|
-
* private ttlSeconds = 900 // 15 minutes
|
|
104
|
-
* ) {}
|
|
105
|
-
*
|
|
106
|
-
* async get(key: string): Promise<NodeSavedState | undefined> {
|
|
107
|
-
* const data = await this.redis.get(this.prefix + key);
|
|
108
|
-
* return data ? JSON.parse(data) : undefined;
|
|
109
|
-
* }
|
|
110
|
-
*
|
|
111
|
-
* async set(key: string, state: NodeSavedState): Promise<void> {
|
|
112
|
-
* await this.redis.setex(
|
|
113
|
-
* this.prefix + key,
|
|
114
|
-
* this.ttlSeconds,
|
|
115
|
-
* JSON.stringify(state)
|
|
116
|
-
* );
|
|
117
|
-
* }
|
|
118
|
-
*
|
|
119
|
-
* async del(key: string): Promise<void> {
|
|
120
|
-
* await this.redis.del(this.prefix + key);
|
|
121
|
-
* }
|
|
122
|
-
* }
|
|
123
|
-
* ```
|
|
124
|
-
*
|
|
125
|
-
* @see {@link InMemoryStateStore} for the default in-memory implementation
|
|
126
|
-
*/
|
|
127
|
-
export interface StateStore {
|
|
128
|
-
/**
|
|
129
|
-
* Retrieves OAuth state by key.
|
|
130
|
-
*
|
|
131
|
-
* @param key - The state key (random string from authorization URL)
|
|
132
|
-
* @returns The stored state, or `undefined` if not found or expired
|
|
133
|
-
*/
|
|
134
|
-
get(key: string): Promise<NodeSavedState | undefined>;
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Stores OAuth state temporarily.
|
|
138
|
-
*
|
|
139
|
-
* Called when starting the authorization flow. The state should be
|
|
140
|
-
* stored with a short TTL (10-15 minutes recommended).
|
|
141
|
-
*
|
|
142
|
-
* @param key - The state key to use for storage
|
|
143
|
-
* @param state - The OAuth state data (includes PKCE verifier, etc.)
|
|
144
|
-
*/
|
|
145
|
-
set(key: string, state: NodeSavedState): Promise<void>;
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Deletes OAuth state.
|
|
149
|
-
*
|
|
150
|
-
* Called after the state has been used (successful or failed callback).
|
|
151
|
-
*
|
|
152
|
-
* @param key - The state key to delete
|
|
153
|
-
*/
|
|
154
|
-
del(key: string): Promise<void>;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Generic cache interface for profiles, metadata, and other data.
|
|
159
|
-
*
|
|
160
|
-
* Implement this interface to provide caching for frequently accessed data.
|
|
161
|
-
* Caching can significantly reduce API calls and improve performance.
|
|
162
|
-
*
|
|
163
|
-
* @remarks
|
|
164
|
-
* The SDK does not provide a default cache implementation - you must
|
|
165
|
-
* implement this interface if you want caching. Consider using:
|
|
166
|
-
* - In-memory cache (e.g., `lru-cache`) for single-instance applications
|
|
167
|
-
* - Redis for distributed applications
|
|
168
|
-
* - Database-backed cache for persistence
|
|
169
|
-
*
|
|
170
|
-
* @example LRU cache implementation
|
|
171
|
-
* ```typescript
|
|
172
|
-
* import { LRUCache } from "lru-cache";
|
|
173
|
-
* import type { CacheInterface } from "@hypercerts-org/sdk";
|
|
174
|
-
*
|
|
175
|
-
* class LRUCacheAdapter implements CacheInterface {
|
|
176
|
-
* private cache = new LRUCache<string, unknown>({
|
|
177
|
-
* max: 1000,
|
|
178
|
-
* ttl: 5 * 60 * 1000, // 5 minutes default
|
|
179
|
-
* });
|
|
180
|
-
*
|
|
181
|
-
* async get<T>(key: string): Promise<T | undefined> {
|
|
182
|
-
* return this.cache.get(key) as T | undefined;
|
|
183
|
-
* }
|
|
184
|
-
*
|
|
185
|
-
* async set<T>(key: string, value: T, ttlSeconds?: number): Promise<void> {
|
|
186
|
-
* this.cache.set(key, value, {
|
|
187
|
-
* ttl: ttlSeconds ? ttlSeconds * 1000 : undefined,
|
|
188
|
-
* });
|
|
189
|
-
* }
|
|
190
|
-
*
|
|
191
|
-
* async del(key: string): Promise<void> {
|
|
192
|
-
* this.cache.delete(key);
|
|
193
|
-
* }
|
|
194
|
-
*
|
|
195
|
-
* async clear(): Promise<void> {
|
|
196
|
-
* this.cache.clear();
|
|
197
|
-
* }
|
|
198
|
-
* }
|
|
199
|
-
* ```
|
|
200
|
-
*/
|
|
201
|
-
export interface CacheInterface {
|
|
202
|
-
/**
|
|
203
|
-
* Gets a cached value by key.
|
|
204
|
-
*
|
|
205
|
-
* @typeParam T - The expected type of the cached value
|
|
206
|
-
* @param key - The cache key
|
|
207
|
-
* @returns The cached value, or `undefined` if not found or expired
|
|
208
|
-
*/
|
|
209
|
-
get<T>(key: string): Promise<T | undefined>;
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Sets a cached value with optional TTL (time-to-live).
|
|
213
|
-
*
|
|
214
|
-
* @typeParam T - The type of the value being cached
|
|
215
|
-
* @param key - The cache key
|
|
216
|
-
* @param value - The value to cache
|
|
217
|
-
* @param ttlSeconds - Optional time-to-live in seconds. If not provided,
|
|
218
|
-
* the cache implementation should use its default TTL.
|
|
219
|
-
*/
|
|
220
|
-
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Deletes a cached value.
|
|
224
|
-
*
|
|
225
|
-
* @param key - The cache key to delete
|
|
226
|
-
*/
|
|
227
|
-
del(key: string): Promise<void>;
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Clears all cached values.
|
|
231
|
-
*
|
|
232
|
-
* Use with caution in production as this affects all cached data.
|
|
233
|
-
*/
|
|
234
|
-
clear(): Promise<void>;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Logger interface for debugging and observability.
|
|
239
|
-
*
|
|
240
|
-
* Implement this interface to receive log messages from the SDK.
|
|
241
|
-
* The interface is compatible with `console` and most popular
|
|
242
|
-
* logging libraries (Pino, Winston, Bunyan, etc.).
|
|
243
|
-
*
|
|
244
|
-
* @remarks
|
|
245
|
-
* Log levels follow standard conventions:
|
|
246
|
-
* - `debug`: Detailed information for debugging (tokens, request details)
|
|
247
|
-
* - `info`: General operational messages (initialization, successful auth)
|
|
248
|
-
* - `warn`: Potentially problematic situations (deprecated features, retries)
|
|
249
|
-
* - `error`: Errors that don't crash the application (failed requests, validation)
|
|
250
|
-
*
|
|
251
|
-
* @example Using console
|
|
252
|
-
* ```typescript
|
|
253
|
-
* const sdk = new ATProtoSDK({
|
|
254
|
-
* // ...
|
|
255
|
-
* logger: console,
|
|
256
|
-
* });
|
|
257
|
-
* ```
|
|
258
|
-
*
|
|
259
|
-
* @example Using Pino
|
|
260
|
-
* ```typescript
|
|
261
|
-
* import pino from "pino";
|
|
262
|
-
*
|
|
263
|
-
* const logger = pino({ level: "debug" });
|
|
264
|
-
* const sdk = new ATProtoSDK({
|
|
265
|
-
* // ...
|
|
266
|
-
* logger: logger,
|
|
267
|
-
* });
|
|
268
|
-
* ```
|
|
269
|
-
*
|
|
270
|
-
* @example Custom logger with context
|
|
271
|
-
* ```typescript
|
|
272
|
-
* const logger: LoggerInterface = {
|
|
273
|
-
* debug: (msg, ...args) => console.debug(`[SDK] ${msg}`, ...args),
|
|
274
|
-
* info: (msg, ...args) => console.info(`[SDK] ${msg}`, ...args),
|
|
275
|
-
* warn: (msg, ...args) => console.warn(`[SDK] ${msg}`, ...args),
|
|
276
|
-
* error: (msg, ...args) => console.error(`[SDK] ${msg}`, ...args),
|
|
277
|
-
* };
|
|
278
|
-
* ```
|
|
279
|
-
*/
|
|
280
|
-
export interface LoggerInterface {
|
|
281
|
-
/**
|
|
282
|
-
* Logs debug-level messages.
|
|
283
|
-
*
|
|
284
|
-
* Used for detailed diagnostic information. May include sensitive
|
|
285
|
-
* data like request URLs and headers (but not tokens).
|
|
286
|
-
*
|
|
287
|
-
* @param message - The log message
|
|
288
|
-
* @param args - Additional arguments (objects, error details, etc.)
|
|
289
|
-
*/
|
|
290
|
-
debug(message: string, ...args: unknown[]): void;
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Logs info-level messages.
|
|
294
|
-
*
|
|
295
|
-
* Used for general operational information like successful
|
|
296
|
-
* initialization, authentication events, etc.
|
|
297
|
-
*
|
|
298
|
-
* @param message - The log message
|
|
299
|
-
* @param args - Additional arguments
|
|
300
|
-
*/
|
|
301
|
-
info(message: string, ...args: unknown[]): void;
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Logs warning-level messages.
|
|
305
|
-
*
|
|
306
|
-
* Used for potentially problematic situations that don't prevent
|
|
307
|
-
* operation but may indicate issues.
|
|
308
|
-
*
|
|
309
|
-
* @param message - The log message
|
|
310
|
-
* @param args - Additional arguments
|
|
311
|
-
*/
|
|
312
|
-
warn(message: string, ...args: unknown[]): void;
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Logs error-level messages.
|
|
316
|
-
*
|
|
317
|
-
* Used for error conditions that should be investigated.
|
|
318
|
-
* The SDK continues to operate after logging errors.
|
|
319
|
-
*
|
|
320
|
-
* @param message - The log message
|
|
321
|
-
* @param args - Additional arguments (typically includes the error object)
|
|
322
|
-
*/
|
|
323
|
-
error(message: string, ...args: unknown[]): void;
|
|
324
|
-
}
|