@hypercerts-org/sdk-core 0.4.0-beta.0 → 0.6.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.
Files changed (62) hide show
  1. package/README.md +459 -79
  2. package/dist/index.cjs +128 -47
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.ts +28 -9
  5. package/dist/index.mjs +128 -47
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/types.cjs +3 -2
  8. package/dist/types.cjs.map +1 -1
  9. package/dist/types.d.ts +28 -9
  10. package/dist/types.mjs +3 -2
  11. package/dist/types.mjs.map +1 -1
  12. package/package.json +9 -5
  13. package/.turbo/turbo-build.log +0 -328
  14. package/.turbo/turbo-test.log +0 -118
  15. package/CHANGELOG.md +0 -22
  16. package/eslint.config.mjs +0 -22
  17. package/rollup.config.js +0 -75
  18. package/src/auth/OAuthClient.ts +0 -497
  19. package/src/core/SDK.ts +0 -410
  20. package/src/core/config.ts +0 -243
  21. package/src/core/errors.ts +0 -257
  22. package/src/core/interfaces.ts +0 -324
  23. package/src/core/types.ts +0 -281
  24. package/src/errors.ts +0 -57
  25. package/src/index.ts +0 -107
  26. package/src/lexicons.ts +0 -64
  27. package/src/repository/BlobOperationsImpl.ts +0 -199
  28. package/src/repository/CollaboratorOperationsImpl.ts +0 -396
  29. package/src/repository/HypercertOperationsImpl.ts +0 -1146
  30. package/src/repository/LexiconRegistry.ts +0 -332
  31. package/src/repository/OrganizationOperationsImpl.ts +0 -234
  32. package/src/repository/ProfileOperationsImpl.ts +0 -281
  33. package/src/repository/RecordOperationsImpl.ts +0 -340
  34. package/src/repository/Repository.ts +0 -482
  35. package/src/repository/interfaces.ts +0 -897
  36. package/src/repository/types.ts +0 -111
  37. package/src/services/hypercerts/types.ts +0 -87
  38. package/src/storage/InMemorySessionStore.ts +0 -127
  39. package/src/storage/InMemoryStateStore.ts +0 -146
  40. package/src/storage.ts +0 -63
  41. package/src/testing/index.ts +0 -67
  42. package/src/testing/mocks.ts +0 -142
  43. package/src/testing/stores.ts +0 -285
  44. package/src/testing.ts +0 -64
  45. package/src/types.ts +0 -86
  46. package/tests/auth/OAuthClient.test.ts +0 -164
  47. package/tests/core/SDK.test.ts +0 -176
  48. package/tests/core/errors.test.ts +0 -81
  49. package/tests/repository/BlobOperationsImpl.test.ts +0 -154
  50. package/tests/repository/CollaboratorOperationsImpl.test.ts +0 -438
  51. package/tests/repository/HypercertOperationsImpl.test.ts +0 -652
  52. package/tests/repository/LexiconRegistry.test.ts +0 -192
  53. package/tests/repository/OrganizationOperationsImpl.test.ts +0 -242
  54. package/tests/repository/ProfileOperationsImpl.test.ts +0 -254
  55. package/tests/repository/RecordOperationsImpl.test.ts +0 -375
  56. package/tests/repository/Repository.test.ts +0 -149
  57. package/tests/utils/fixtures.ts +0 -117
  58. package/tests/utils/mocks.ts +0 -109
  59. package/tests/utils/repository-fixtures.ts +0 -78
  60. package/tsconfig.json +0 -11
  61. package/tsconfig.tsbuildinfo +0 -1
  62. package/vitest.config.ts +0 -30
@@ -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
- }
@@ -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
- }