@hypercerts-org/sdk-core 0.2.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/.turbo/turbo-build.log +328 -0
- package/.turbo/turbo-test.log +118 -0
- package/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +100 -0
- package/dist/errors.cjs +260 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +233 -0
- package/dist/errors.mjs +253 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/index.cjs +4531 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +3430 -0
- package/dist/index.mjs +4448 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lexicons.cjs +420 -0
- package/dist/lexicons.cjs.map +1 -0
- package/dist/lexicons.d.ts +227 -0
- package/dist/lexicons.mjs +410 -0
- package/dist/lexicons.mjs.map +1 -0
- package/dist/storage.cjs +270 -0
- package/dist/storage.cjs.map +1 -0
- package/dist/storage.d.ts +474 -0
- package/dist/storage.mjs +267 -0
- package/dist/storage.mjs.map +1 -0
- package/dist/testing.cjs +415 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.ts +928 -0
- package/dist/testing.mjs +410 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/types.cjs +220 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.ts +2118 -0
- package/dist/types.mjs +212 -0
- package/dist/types.mjs.map +1 -0
- package/eslint.config.mjs +22 -0
- package/package.json +90 -0
- package/rollup.config.js +75 -0
- package/src/auth/OAuthClient.ts +497 -0
- package/src/core/SDK.ts +410 -0
- package/src/core/config.ts +243 -0
- package/src/core/errors.ts +257 -0
- package/src/core/interfaces.ts +324 -0
- package/src/core/types.ts +281 -0
- package/src/errors.ts +57 -0
- package/src/index.ts +107 -0
- package/src/lexicons.ts +64 -0
- package/src/repository/BlobOperationsImpl.ts +199 -0
- package/src/repository/CollaboratorOperationsImpl.ts +288 -0
- package/src/repository/HypercertOperationsImpl.ts +1146 -0
- package/src/repository/LexiconRegistry.ts +332 -0
- package/src/repository/OrganizationOperationsImpl.ts +234 -0
- package/src/repository/ProfileOperationsImpl.ts +281 -0
- package/src/repository/RecordOperationsImpl.ts +340 -0
- package/src/repository/Repository.ts +482 -0
- package/src/repository/interfaces.ts +868 -0
- package/src/repository/types.ts +111 -0
- package/src/services/hypercerts/types.ts +87 -0
- package/src/storage/InMemorySessionStore.ts +127 -0
- package/src/storage/InMemoryStateStore.ts +146 -0
- package/src/storage.ts +63 -0
- package/src/testing/index.ts +67 -0
- package/src/testing/mocks.ts +142 -0
- package/src/testing/stores.ts +285 -0
- package/src/testing.ts +64 -0
- package/src/types.ts +86 -0
- package/tests/auth/OAuthClient.test.ts +164 -0
- package/tests/core/SDK.test.ts +176 -0
- package/tests/core/errors.test.ts +81 -0
- package/tests/repository/BlobOperationsImpl.test.ts +154 -0
- package/tests/repository/CollaboratorOperationsImpl.test.ts +323 -0
- package/tests/repository/HypercertOperationsImpl.test.ts +652 -0
- package/tests/repository/LexiconRegistry.test.ts +192 -0
- package/tests/repository/OrganizationOperationsImpl.test.ts +242 -0
- package/tests/repository/ProfileOperationsImpl.test.ts +254 -0
- package/tests/repository/RecordOperationsImpl.test.ts +375 -0
- package/tests/repository/Repository.test.ts +149 -0
- package/tests/utils/fixtures.ts +117 -0
- package/tests/utils/mocks.ts +109 -0
- package/tests/utils/repository-fixtures.ts +78 -0
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +30 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3430 @@
|
|
|
1
|
+
import { Agent } from '@atproto/api';
|
|
2
|
+
import { LexiconDoc } from '@atproto/lexicon';
|
|
3
|
+
import { NodeSavedSession, NodeSavedState, OAuthSession } from '@atproto/oauth-client-node';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { EventEmitter } from 'eventemitter3';
|
|
6
|
+
import { OrgHypercertsClaim, OrgHypercertsCollection, ComAtprotoRepoStrongRef, OrgHypercertsClaimRights, OrgHypercertsClaimContribution, OrgHypercertsClaimMeasurement, OrgHypercertsClaimEvaluation, AppCertifiedLocation } from '@hypercerts-org/lexicon';
|
|
7
|
+
export { AppCertifiedDefs, AppCertifiedLocation, ComAtprotoRepoStrongRef, HYPERCERT_COLLECTIONS, HYPERCERT_LEXICONS, OrgHypercertsClaim, OrgHypercertsClaimContribution, OrgHypercertsClaimEvaluation, OrgHypercertsClaimEvidence, OrgHypercertsClaimMeasurement, OrgHypercertsClaimRights, OrgHypercertsCollection, ids, lexicons, schemaDict, schemas, validate } from '@hypercerts-org/lexicon';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Result of validating a record against a lexicon schema.
|
|
11
|
+
*/
|
|
12
|
+
interface ValidationResult {
|
|
13
|
+
/**
|
|
14
|
+
* Whether the record is valid according to the lexicon schema.
|
|
15
|
+
*/
|
|
16
|
+
valid: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Error message if validation failed.
|
|
19
|
+
*
|
|
20
|
+
* Only present when `valid` is `false`.
|
|
21
|
+
*/
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Registry for managing and validating AT Protocol lexicon schemas.
|
|
26
|
+
*
|
|
27
|
+
* Lexicons are schema definitions that describe the structure of records
|
|
28
|
+
* in the AT Protocol. This registry allows you to:
|
|
29
|
+
*
|
|
30
|
+
* - Register custom lexicons for your application's record types
|
|
31
|
+
* - Validate records against their lexicon schemas
|
|
32
|
+
* - Extend the AT Protocol Agent with custom lexicon support
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* The SDK automatically registers hypercert lexicons when creating a Repository.
|
|
36
|
+
* You only need to use this class directly if you're working with custom
|
|
37
|
+
* record types.
|
|
38
|
+
*
|
|
39
|
+
* **Lexicon IDs** follow the NSID (Namespaced Identifier) format:
|
|
40
|
+
* `{authority}.{name}` (e.g., `org.hypercerts.hypercert`)
|
|
41
|
+
*
|
|
42
|
+
* @example Registering custom lexicons
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const registry = sdk.getLexiconRegistry();
|
|
45
|
+
*
|
|
46
|
+
* // Register a single lexicon
|
|
47
|
+
* registry.register({
|
|
48
|
+
* lexicon: 1,
|
|
49
|
+
* id: "org.example.myRecord",
|
|
50
|
+
* defs: {
|
|
51
|
+
* main: {
|
|
52
|
+
* type: "record",
|
|
53
|
+
* key: "tid",
|
|
54
|
+
* record: {
|
|
55
|
+
* type: "object",
|
|
56
|
+
* required: ["title", "createdAt"],
|
|
57
|
+
* properties: {
|
|
58
|
+
* title: { type: "string" },
|
|
59
|
+
* description: { type: "string" },
|
|
60
|
+
* createdAt: { type: "string", format: "datetime" },
|
|
61
|
+
* },
|
|
62
|
+
* },
|
|
63
|
+
* },
|
|
64
|
+
* },
|
|
65
|
+
* });
|
|
66
|
+
*
|
|
67
|
+
* // Register multiple lexicons at once
|
|
68
|
+
* registry.registerMany([lexicon1, lexicon2, lexicon3]);
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @example Validating records
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const result = registry.validate("org.example.myRecord", {
|
|
74
|
+
* title: "Test",
|
|
75
|
+
* createdAt: new Date().toISOString(),
|
|
76
|
+
* });
|
|
77
|
+
*
|
|
78
|
+
* if (!result.valid) {
|
|
79
|
+
* console.error(`Validation failed: ${result.error}`);
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* @see https://atproto.com/specs/lexicon for the Lexicon specification
|
|
84
|
+
*/
|
|
85
|
+
declare class LexiconRegistry {
|
|
86
|
+
/** Map of lexicon ID to lexicon document */
|
|
87
|
+
private lexicons;
|
|
88
|
+
/** Lexicons collection for validation */
|
|
89
|
+
private lexiconsCollection;
|
|
90
|
+
/**
|
|
91
|
+
* Creates a new LexiconRegistry.
|
|
92
|
+
*
|
|
93
|
+
* The registry starts empty. Use {@link register} or {@link registerMany}
|
|
94
|
+
* to add lexicons.
|
|
95
|
+
*/
|
|
96
|
+
constructor();
|
|
97
|
+
/**
|
|
98
|
+
* Registers a single lexicon schema.
|
|
99
|
+
*
|
|
100
|
+
* @param lexicon - The lexicon document to register
|
|
101
|
+
* @throws {@link ValidationError} if the lexicon doesn't have an `id` field
|
|
102
|
+
*
|
|
103
|
+
* @remarks
|
|
104
|
+
* If a lexicon with the same ID is already registered, it will be
|
|
105
|
+
* replaced with the new definition. This is useful for testing but
|
|
106
|
+
* should generally be avoided in production.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* registry.register({
|
|
111
|
+
* lexicon: 1,
|
|
112
|
+
* id: "org.example.post",
|
|
113
|
+
* defs: {
|
|
114
|
+
* main: {
|
|
115
|
+
* type: "record",
|
|
116
|
+
* key: "tid",
|
|
117
|
+
* record: {
|
|
118
|
+
* type: "object",
|
|
119
|
+
* required: ["text", "createdAt"],
|
|
120
|
+
* properties: {
|
|
121
|
+
* text: { type: "string", maxLength: 300 },
|
|
122
|
+
* createdAt: { type: "string", format: "datetime" },
|
|
123
|
+
* },
|
|
124
|
+
* },
|
|
125
|
+
* },
|
|
126
|
+
* },
|
|
127
|
+
* });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
register(lexicon: LexiconDoc): void;
|
|
131
|
+
/**
|
|
132
|
+
* Registers multiple lexicons at once.
|
|
133
|
+
*
|
|
134
|
+
* @param lexicons - Array of lexicon documents to register
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* import { HYPERCERT_LEXICONS } from "@hypercerts-org/sdk/lexicons";
|
|
139
|
+
*
|
|
140
|
+
* registry.registerMany(HYPERCERT_LEXICONS);
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
registerMany(lexicons: LexiconDoc[]): void;
|
|
144
|
+
/**
|
|
145
|
+
* Gets a lexicon document by ID.
|
|
146
|
+
*
|
|
147
|
+
* @param id - The lexicon NSID (e.g., "org.hypercerts.hypercert")
|
|
148
|
+
* @returns The lexicon document, or `undefined` if not registered
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* const lexicon = registry.get("org.hypercerts.hypercert");
|
|
153
|
+
* if (lexicon) {
|
|
154
|
+
* console.log(`Found lexicon: ${lexicon.id}`);
|
|
155
|
+
* }
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
get(id: string): LexiconDoc | undefined;
|
|
159
|
+
/**
|
|
160
|
+
* Validates a record against a collection's lexicon schema.
|
|
161
|
+
*
|
|
162
|
+
* @param collection - The collection NSID (same as lexicon ID)
|
|
163
|
+
* @param record - The record data to validate
|
|
164
|
+
* @returns Validation result with `valid` boolean and optional `error` message
|
|
165
|
+
*
|
|
166
|
+
* @remarks
|
|
167
|
+
* - If no lexicon is registered for the collection, validation passes
|
|
168
|
+
* (we can't validate against unknown schemas)
|
|
169
|
+
* - Validation checks required fields and type constraints defined
|
|
170
|
+
* in the lexicon schema
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* const result = registry.validate("org.hypercerts.hypercert", {
|
|
175
|
+
* title: "My Hypercert",
|
|
176
|
+
* description: "Description...",
|
|
177
|
+
* // ... other fields
|
|
178
|
+
* });
|
|
179
|
+
*
|
|
180
|
+
* if (!result.valid) {
|
|
181
|
+
* throw new Error(`Invalid record: ${result.error}`);
|
|
182
|
+
* }
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
validate(collection: string, record: unknown): ValidationResult;
|
|
186
|
+
/**
|
|
187
|
+
* Adds all registered lexicons to an AT Protocol Agent instance.
|
|
188
|
+
*
|
|
189
|
+
* This allows the Agent to understand custom lexicon types when making
|
|
190
|
+
* API requests.
|
|
191
|
+
*
|
|
192
|
+
* @param agent - The Agent instance to extend
|
|
193
|
+
*
|
|
194
|
+
* @remarks
|
|
195
|
+
* This is called automatically when creating a Repository. You typically
|
|
196
|
+
* don't need to call this directly unless you're using the Agent
|
|
197
|
+
* independently.
|
|
198
|
+
*
|
|
199
|
+
* @internal
|
|
200
|
+
*/
|
|
201
|
+
addToAgent(agent: Agent): void;
|
|
202
|
+
/**
|
|
203
|
+
* Gets all registered lexicon IDs.
|
|
204
|
+
*
|
|
205
|
+
* @returns Array of lexicon NSIDs
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```typescript
|
|
209
|
+
* const ids = registry.getRegisteredIds();
|
|
210
|
+
* console.log(`Registered lexicons: ${ids.join(", ")}`);
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
getRegisteredIds(): string[];
|
|
214
|
+
/**
|
|
215
|
+
* Checks if a lexicon is registered.
|
|
216
|
+
*
|
|
217
|
+
* @param id - The lexicon NSID to check
|
|
218
|
+
* @returns `true` if the lexicon is registered
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```typescript
|
|
222
|
+
* if (registry.has("org.hypercerts.hypercert")) {
|
|
223
|
+
* // Hypercert lexicon is available
|
|
224
|
+
* }
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
has(id: string): boolean;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Storage interface for persisting OAuth sessions.
|
|
232
|
+
*
|
|
233
|
+
* Implement this interface to provide persistent storage for user sessions.
|
|
234
|
+
* Sessions contain sensitive data including access tokens, refresh tokens,
|
|
235
|
+
* and DPoP key pairs.
|
|
236
|
+
*
|
|
237
|
+
* The SDK provides {@link InMemorySessionStore} for development/testing,
|
|
238
|
+
* but **production applications should implement persistent storage**
|
|
239
|
+
* (e.g., Redis, PostgreSQL, MongoDB).
|
|
240
|
+
*
|
|
241
|
+
* @remarks
|
|
242
|
+
* - Sessions are keyed by the user's DID (Decentralized Identifier)
|
|
243
|
+
* - The `NodeSavedSession` type comes from `@atproto/oauth-client-node`
|
|
244
|
+
* - Sessions may be large (~2-4KB) due to embedded cryptographic keys
|
|
245
|
+
* - Consider encrypting sessions at rest for additional security
|
|
246
|
+
*
|
|
247
|
+
* @example Redis implementation
|
|
248
|
+
* ```typescript
|
|
249
|
+
* import { Redis } from "ioredis";
|
|
250
|
+
* import type { SessionStore } from "@hypercerts-org/sdk";
|
|
251
|
+
* import type { NodeSavedSession } from "@atproto/oauth-client-node";
|
|
252
|
+
*
|
|
253
|
+
* class RedisSessionStore implements SessionStore {
|
|
254
|
+
* constructor(private redis: Redis, private prefix = "session:") {}
|
|
255
|
+
*
|
|
256
|
+
* async get(did: string): Promise<NodeSavedSession | undefined> {
|
|
257
|
+
* const data = await this.redis.get(this.prefix + did);
|
|
258
|
+
* return data ? JSON.parse(data) : undefined;
|
|
259
|
+
* }
|
|
260
|
+
*
|
|
261
|
+
* async set(did: string, session: NodeSavedSession): Promise<void> {
|
|
262
|
+
* // Set with 30-day expiry (sessions can be refreshed)
|
|
263
|
+
* await this.redis.setex(
|
|
264
|
+
* this.prefix + did,
|
|
265
|
+
* 30 * 24 * 60 * 60,
|
|
266
|
+
* JSON.stringify(session)
|
|
267
|
+
* );
|
|
268
|
+
* }
|
|
269
|
+
*
|
|
270
|
+
* async del(did: string): Promise<void> {
|
|
271
|
+
* await this.redis.del(this.prefix + did);
|
|
272
|
+
* }
|
|
273
|
+
* }
|
|
274
|
+
* ```
|
|
275
|
+
*
|
|
276
|
+
* @see {@link InMemorySessionStore} for the default in-memory implementation
|
|
277
|
+
*/
|
|
278
|
+
interface SessionStore {
|
|
279
|
+
/**
|
|
280
|
+
* Retrieves a session by DID.
|
|
281
|
+
*
|
|
282
|
+
* @param did - The user's Decentralized Identifier (e.g., "did:plc:abc123...")
|
|
283
|
+
* @returns The stored session, or `undefined` if not found
|
|
284
|
+
*/
|
|
285
|
+
get(did: string): Promise<NodeSavedSession | undefined>;
|
|
286
|
+
/**
|
|
287
|
+
* Stores or updates a session.
|
|
288
|
+
*
|
|
289
|
+
* This is called after successful authentication and whenever tokens are refreshed.
|
|
290
|
+
*
|
|
291
|
+
* @param did - The user's DID to use as the storage key
|
|
292
|
+
* @param session - The session data to store (contains tokens, DPoP keys, etc.)
|
|
293
|
+
*/
|
|
294
|
+
set(did: string, session: NodeSavedSession): Promise<void>;
|
|
295
|
+
/**
|
|
296
|
+
* Deletes a session.
|
|
297
|
+
*
|
|
298
|
+
* Called when a user logs out or when a session is revoked.
|
|
299
|
+
*
|
|
300
|
+
* @param did - The user's DID
|
|
301
|
+
*/
|
|
302
|
+
del(did: string): Promise<void>;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Storage interface for OAuth state during the authorization flow.
|
|
306
|
+
*
|
|
307
|
+
* Implement this interface to provide temporary storage for OAuth state
|
|
308
|
+
* parameters. State is used for CSRF protection and PKCE (Proof Key for
|
|
309
|
+
* Code Exchange) during the authorization flow.
|
|
310
|
+
*
|
|
311
|
+
* @remarks
|
|
312
|
+
* - State is short-lived (typically 10-15 minutes)
|
|
313
|
+
* - Keys are random state strings generated by the OAuth client
|
|
314
|
+
* - The `NodeSavedState` type comes from `@atproto/oauth-client-node`
|
|
315
|
+
* - State should be automatically cleaned up after expiry
|
|
316
|
+
*
|
|
317
|
+
* @example Redis implementation with automatic expiry
|
|
318
|
+
* ```typescript
|
|
319
|
+
* import { Redis } from "ioredis";
|
|
320
|
+
* import type { StateStore } from "@hypercerts-org/sdk";
|
|
321
|
+
* import type { NodeSavedState } from "@atproto/oauth-client-node";
|
|
322
|
+
*
|
|
323
|
+
* class RedisStateStore implements StateStore {
|
|
324
|
+
* constructor(
|
|
325
|
+
* private redis: Redis,
|
|
326
|
+
* private prefix = "oauth-state:",
|
|
327
|
+
* private ttlSeconds = 900 // 15 minutes
|
|
328
|
+
* ) {}
|
|
329
|
+
*
|
|
330
|
+
* async get(key: string): Promise<NodeSavedState | undefined> {
|
|
331
|
+
* const data = await this.redis.get(this.prefix + key);
|
|
332
|
+
* return data ? JSON.parse(data) : undefined;
|
|
333
|
+
* }
|
|
334
|
+
*
|
|
335
|
+
* async set(key: string, state: NodeSavedState): Promise<void> {
|
|
336
|
+
* await this.redis.setex(
|
|
337
|
+
* this.prefix + key,
|
|
338
|
+
* this.ttlSeconds,
|
|
339
|
+
* JSON.stringify(state)
|
|
340
|
+
* );
|
|
341
|
+
* }
|
|
342
|
+
*
|
|
343
|
+
* async del(key: string): Promise<void> {
|
|
344
|
+
* await this.redis.del(this.prefix + key);
|
|
345
|
+
* }
|
|
346
|
+
* }
|
|
347
|
+
* ```
|
|
348
|
+
*
|
|
349
|
+
* @see {@link InMemoryStateStore} for the default in-memory implementation
|
|
350
|
+
*/
|
|
351
|
+
interface StateStore {
|
|
352
|
+
/**
|
|
353
|
+
* Retrieves OAuth state by key.
|
|
354
|
+
*
|
|
355
|
+
* @param key - The state key (random string from authorization URL)
|
|
356
|
+
* @returns The stored state, or `undefined` if not found or expired
|
|
357
|
+
*/
|
|
358
|
+
get(key: string): Promise<NodeSavedState | undefined>;
|
|
359
|
+
/**
|
|
360
|
+
* Stores OAuth state temporarily.
|
|
361
|
+
*
|
|
362
|
+
* Called when starting the authorization flow. The state should be
|
|
363
|
+
* stored with a short TTL (10-15 minutes recommended).
|
|
364
|
+
*
|
|
365
|
+
* @param key - The state key to use for storage
|
|
366
|
+
* @param state - The OAuth state data (includes PKCE verifier, etc.)
|
|
367
|
+
*/
|
|
368
|
+
set(key: string, state: NodeSavedState): Promise<void>;
|
|
369
|
+
/**
|
|
370
|
+
* Deletes OAuth state.
|
|
371
|
+
*
|
|
372
|
+
* Called after the state has been used (successful or failed callback).
|
|
373
|
+
*
|
|
374
|
+
* @param key - The state key to delete
|
|
375
|
+
*/
|
|
376
|
+
del(key: string): Promise<void>;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Generic cache interface for profiles, metadata, and other data.
|
|
380
|
+
*
|
|
381
|
+
* Implement this interface to provide caching for frequently accessed data.
|
|
382
|
+
* Caching can significantly reduce API calls and improve performance.
|
|
383
|
+
*
|
|
384
|
+
* @remarks
|
|
385
|
+
* The SDK does not provide a default cache implementation - you must
|
|
386
|
+
* implement this interface if you want caching. Consider using:
|
|
387
|
+
* - In-memory cache (e.g., `lru-cache`) for single-instance applications
|
|
388
|
+
* - Redis for distributed applications
|
|
389
|
+
* - Database-backed cache for persistence
|
|
390
|
+
*
|
|
391
|
+
* @example LRU cache implementation
|
|
392
|
+
* ```typescript
|
|
393
|
+
* import { LRUCache } from "lru-cache";
|
|
394
|
+
* import type { CacheInterface } from "@hypercerts-org/sdk";
|
|
395
|
+
*
|
|
396
|
+
* class LRUCacheAdapter implements CacheInterface {
|
|
397
|
+
* private cache = new LRUCache<string, unknown>({
|
|
398
|
+
* max: 1000,
|
|
399
|
+
* ttl: 5 * 60 * 1000, // 5 minutes default
|
|
400
|
+
* });
|
|
401
|
+
*
|
|
402
|
+
* async get<T>(key: string): Promise<T | undefined> {
|
|
403
|
+
* return this.cache.get(key) as T | undefined;
|
|
404
|
+
* }
|
|
405
|
+
*
|
|
406
|
+
* async set<T>(key: string, value: T, ttlSeconds?: number): Promise<void> {
|
|
407
|
+
* this.cache.set(key, value, {
|
|
408
|
+
* ttl: ttlSeconds ? ttlSeconds * 1000 : undefined,
|
|
409
|
+
* });
|
|
410
|
+
* }
|
|
411
|
+
*
|
|
412
|
+
* async del(key: string): Promise<void> {
|
|
413
|
+
* this.cache.delete(key);
|
|
414
|
+
* }
|
|
415
|
+
*
|
|
416
|
+
* async clear(): Promise<void> {
|
|
417
|
+
* this.cache.clear();
|
|
418
|
+
* }
|
|
419
|
+
* }
|
|
420
|
+
* ```
|
|
421
|
+
*/
|
|
422
|
+
interface CacheInterface {
|
|
423
|
+
/**
|
|
424
|
+
* Gets a cached value by key.
|
|
425
|
+
*
|
|
426
|
+
* @typeParam T - The expected type of the cached value
|
|
427
|
+
* @param key - The cache key
|
|
428
|
+
* @returns The cached value, or `undefined` if not found or expired
|
|
429
|
+
*/
|
|
430
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
431
|
+
/**
|
|
432
|
+
* Sets a cached value with optional TTL (time-to-live).
|
|
433
|
+
*
|
|
434
|
+
* @typeParam T - The type of the value being cached
|
|
435
|
+
* @param key - The cache key
|
|
436
|
+
* @param value - The value to cache
|
|
437
|
+
* @param ttlSeconds - Optional time-to-live in seconds. If not provided,
|
|
438
|
+
* the cache implementation should use its default TTL.
|
|
439
|
+
*/
|
|
440
|
+
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
|
441
|
+
/**
|
|
442
|
+
* Deletes a cached value.
|
|
443
|
+
*
|
|
444
|
+
* @param key - The cache key to delete
|
|
445
|
+
*/
|
|
446
|
+
del(key: string): Promise<void>;
|
|
447
|
+
/**
|
|
448
|
+
* Clears all cached values.
|
|
449
|
+
*
|
|
450
|
+
* Use with caution in production as this affects all cached data.
|
|
451
|
+
*/
|
|
452
|
+
clear(): Promise<void>;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Logger interface for debugging and observability.
|
|
456
|
+
*
|
|
457
|
+
* Implement this interface to receive log messages from the SDK.
|
|
458
|
+
* The interface is compatible with `console` and most popular
|
|
459
|
+
* logging libraries (Pino, Winston, Bunyan, etc.).
|
|
460
|
+
*
|
|
461
|
+
* @remarks
|
|
462
|
+
* Log levels follow standard conventions:
|
|
463
|
+
* - `debug`: Detailed information for debugging (tokens, request details)
|
|
464
|
+
* - `info`: General operational messages (initialization, successful auth)
|
|
465
|
+
* - `warn`: Potentially problematic situations (deprecated features, retries)
|
|
466
|
+
* - `error`: Errors that don't crash the application (failed requests, validation)
|
|
467
|
+
*
|
|
468
|
+
* @example Using console
|
|
469
|
+
* ```typescript
|
|
470
|
+
* const sdk = new ATProtoSDK({
|
|
471
|
+
* // ...
|
|
472
|
+
* logger: console,
|
|
473
|
+
* });
|
|
474
|
+
* ```
|
|
475
|
+
*
|
|
476
|
+
* @example Using Pino
|
|
477
|
+
* ```typescript
|
|
478
|
+
* import pino from "pino";
|
|
479
|
+
*
|
|
480
|
+
* const logger = pino({ level: "debug" });
|
|
481
|
+
* const sdk = new ATProtoSDK({
|
|
482
|
+
* // ...
|
|
483
|
+
* logger: logger,
|
|
484
|
+
* });
|
|
485
|
+
* ```
|
|
486
|
+
*
|
|
487
|
+
* @example Custom logger with context
|
|
488
|
+
* ```typescript
|
|
489
|
+
* const logger: LoggerInterface = {
|
|
490
|
+
* debug: (msg, ...args) => console.debug(`[SDK] ${msg}`, ...args),
|
|
491
|
+
* info: (msg, ...args) => console.info(`[SDK] ${msg}`, ...args),
|
|
492
|
+
* warn: (msg, ...args) => console.warn(`[SDK] ${msg}`, ...args),
|
|
493
|
+
* error: (msg, ...args) => console.error(`[SDK] ${msg}`, ...args),
|
|
494
|
+
* };
|
|
495
|
+
* ```
|
|
496
|
+
*/
|
|
497
|
+
interface LoggerInterface {
|
|
498
|
+
/**
|
|
499
|
+
* Logs debug-level messages.
|
|
500
|
+
*
|
|
501
|
+
* Used for detailed diagnostic information. May include sensitive
|
|
502
|
+
* data like request URLs and headers (but not tokens).
|
|
503
|
+
*
|
|
504
|
+
* @param message - The log message
|
|
505
|
+
* @param args - Additional arguments (objects, error details, etc.)
|
|
506
|
+
*/
|
|
507
|
+
debug(message: string, ...args: unknown[]): void;
|
|
508
|
+
/**
|
|
509
|
+
* Logs info-level messages.
|
|
510
|
+
*
|
|
511
|
+
* Used for general operational information like successful
|
|
512
|
+
* initialization, authentication events, etc.
|
|
513
|
+
*
|
|
514
|
+
* @param message - The log message
|
|
515
|
+
* @param args - Additional arguments
|
|
516
|
+
*/
|
|
517
|
+
info(message: string, ...args: unknown[]): void;
|
|
518
|
+
/**
|
|
519
|
+
* Logs warning-level messages.
|
|
520
|
+
*
|
|
521
|
+
* Used for potentially problematic situations that don't prevent
|
|
522
|
+
* operation but may indicate issues.
|
|
523
|
+
*
|
|
524
|
+
* @param message - The log message
|
|
525
|
+
* @param args - Additional arguments
|
|
526
|
+
*/
|
|
527
|
+
warn(message: string, ...args: unknown[]): void;
|
|
528
|
+
/**
|
|
529
|
+
* Logs error-level messages.
|
|
530
|
+
*
|
|
531
|
+
* Used for error conditions that should be investigated.
|
|
532
|
+
* The SDK continues to operate after logging errors.
|
|
533
|
+
*
|
|
534
|
+
* @param message - The log message
|
|
535
|
+
* @param args - Additional arguments (typically includes the error object)
|
|
536
|
+
*/
|
|
537
|
+
error(message: string, ...args: unknown[]): void;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Decentralized Identifier (DID) - a unique, persistent identifier for AT Protocol users.
|
|
542
|
+
*
|
|
543
|
+
* DIDs are the canonical identifier for users in the AT Protocol ecosystem.
|
|
544
|
+
* Unlike handles which can change, DIDs remain constant for the lifetime of an account.
|
|
545
|
+
*
|
|
546
|
+
* @remarks
|
|
547
|
+
* AT Protocol supports multiple DID methods:
|
|
548
|
+
* - `did:plc:` - PLC (Public Ledger of Credentials) DIDs, most common for Bluesky users
|
|
549
|
+
* - `did:web:` - Web DIDs, resolved via HTTPS
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* ```typescript
|
|
553
|
+
* const did: DID = "did:plc:ewvi7nxzyoun6zhxrhs64oiz";
|
|
554
|
+
* const webDid: DID = "did:web:example.com";
|
|
555
|
+
* ```
|
|
556
|
+
*
|
|
557
|
+
* @see https://atproto.com/specs/did for DID specification
|
|
558
|
+
*/
|
|
559
|
+
type DID = string;
|
|
560
|
+
/**
|
|
561
|
+
* OAuth session with DPoP (Demonstrating Proof of Possession) support.
|
|
562
|
+
*
|
|
563
|
+
* This type represents an authenticated user session. It wraps the
|
|
564
|
+
* `@atproto/oauth-client-node` OAuthSession and contains:
|
|
565
|
+
* - Access token for API requests
|
|
566
|
+
* - Refresh token for obtaining new access tokens
|
|
567
|
+
* - DPoP key pair for proof-of-possession
|
|
568
|
+
* - User's DID and other identity information
|
|
569
|
+
*
|
|
570
|
+
* @remarks
|
|
571
|
+
* Sessions are managed by the SDK and automatically refresh when tokens expire.
|
|
572
|
+
* Store the user's DID to restore sessions later with {@link ATProtoSDK.restoreSession}.
|
|
573
|
+
*
|
|
574
|
+
* Key properties from OAuthSession:
|
|
575
|
+
* - `did` or `sub`: The user's DID
|
|
576
|
+
* - `handle`: The user's handle (e.g., "user.bsky.social")
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* ```typescript
|
|
580
|
+
* const session = await sdk.callback(params);
|
|
581
|
+
*
|
|
582
|
+
* // Access user identity
|
|
583
|
+
* console.log(`Logged in as: ${session.did}`);
|
|
584
|
+
*
|
|
585
|
+
* // Use session for repository operations
|
|
586
|
+
* const repo = sdk.repository(session);
|
|
587
|
+
* ```
|
|
588
|
+
*
|
|
589
|
+
* @see https://atproto.com/specs/oauth for OAuth specification
|
|
590
|
+
*/
|
|
591
|
+
type Session = OAuthSession;
|
|
592
|
+
/**
|
|
593
|
+
* Zod schema for collaborator permissions in SDS repositories.
|
|
594
|
+
*
|
|
595
|
+
* Defines the granular permissions a collaborator can have on a shared repository.
|
|
596
|
+
* Permissions follow a hierarchical model where higher-level permissions
|
|
597
|
+
* typically imply lower-level ones.
|
|
598
|
+
*/
|
|
599
|
+
declare const CollaboratorPermissionsSchema: z.ZodObject<{
|
|
600
|
+
/**
|
|
601
|
+
* Can read/view records in the repository.
|
|
602
|
+
* This is the most basic permission level.
|
|
603
|
+
*/
|
|
604
|
+
read: z.ZodBoolean;
|
|
605
|
+
/**
|
|
606
|
+
* Can create new records in the repository.
|
|
607
|
+
* Typically implies `read` permission.
|
|
608
|
+
*/
|
|
609
|
+
create: z.ZodBoolean;
|
|
610
|
+
/**
|
|
611
|
+
* Can modify existing records in the repository.
|
|
612
|
+
* Typically implies `read` and `create` permissions.
|
|
613
|
+
*/
|
|
614
|
+
update: z.ZodBoolean;
|
|
615
|
+
/**
|
|
616
|
+
* Can delete records from the repository.
|
|
617
|
+
* Typically implies `read`, `create`, and `update` permissions.
|
|
618
|
+
*/
|
|
619
|
+
delete: z.ZodBoolean;
|
|
620
|
+
/**
|
|
621
|
+
* Can manage collaborators and their permissions.
|
|
622
|
+
* Administrative permission that allows inviting/removing collaborators.
|
|
623
|
+
*/
|
|
624
|
+
admin: z.ZodBoolean;
|
|
625
|
+
/**
|
|
626
|
+
* Full ownership of the repository.
|
|
627
|
+
* Owners have all permissions and cannot be removed by other admins.
|
|
628
|
+
* There must always be at least one owner.
|
|
629
|
+
*/
|
|
630
|
+
owner: z.ZodBoolean;
|
|
631
|
+
}, "strip", z.ZodTypeAny, {
|
|
632
|
+
read: boolean;
|
|
633
|
+
create: boolean;
|
|
634
|
+
update: boolean;
|
|
635
|
+
delete: boolean;
|
|
636
|
+
admin: boolean;
|
|
637
|
+
owner: boolean;
|
|
638
|
+
}, {
|
|
639
|
+
read: boolean;
|
|
640
|
+
create: boolean;
|
|
641
|
+
update: boolean;
|
|
642
|
+
delete: boolean;
|
|
643
|
+
admin: boolean;
|
|
644
|
+
owner: boolean;
|
|
645
|
+
}>;
|
|
646
|
+
/**
|
|
647
|
+
* Collaborator permissions for SDS (Shared Data Server) repositories.
|
|
648
|
+
*
|
|
649
|
+
* These permissions control what actions a collaborator can perform
|
|
650
|
+
* on records within a shared repository.
|
|
651
|
+
*
|
|
652
|
+
* @example
|
|
653
|
+
* ```typescript
|
|
654
|
+
* // Read-only collaborator
|
|
655
|
+
* const readOnlyPerms: CollaboratorPermissions = {
|
|
656
|
+
* read: true,
|
|
657
|
+
* create: false,
|
|
658
|
+
* update: false,
|
|
659
|
+
* delete: false,
|
|
660
|
+
* admin: false,
|
|
661
|
+
* owner: false,
|
|
662
|
+
* };
|
|
663
|
+
*
|
|
664
|
+
* // Editor collaborator
|
|
665
|
+
* const editorPerms: CollaboratorPermissions = {
|
|
666
|
+
* read: true,
|
|
667
|
+
* create: true,
|
|
668
|
+
* update: true,
|
|
669
|
+
* delete: false,
|
|
670
|
+
* admin: false,
|
|
671
|
+
* owner: false,
|
|
672
|
+
* };
|
|
673
|
+
*
|
|
674
|
+
* // Admin collaborator
|
|
675
|
+
* const adminPerms: CollaboratorPermissions = {
|
|
676
|
+
* read: true,
|
|
677
|
+
* create: true,
|
|
678
|
+
* update: true,
|
|
679
|
+
* delete: true,
|
|
680
|
+
* admin: true,
|
|
681
|
+
* owner: false,
|
|
682
|
+
* };
|
|
683
|
+
* ```
|
|
684
|
+
*/
|
|
685
|
+
type CollaboratorPermissions = z.infer<typeof CollaboratorPermissionsSchema>;
|
|
686
|
+
/**
|
|
687
|
+
* Zod schema for SDS organization data.
|
|
688
|
+
*
|
|
689
|
+
* Organizations are top-level entities in SDS that can own repositories
|
|
690
|
+
* and have multiple collaborators with different permission levels.
|
|
691
|
+
*/
|
|
692
|
+
declare const OrganizationSchema: z.ZodObject<{
|
|
693
|
+
/**
|
|
694
|
+
* The organization's DID - unique identifier.
|
|
695
|
+
* Format: "did:plc:..." or "did:web:..."
|
|
696
|
+
*/
|
|
697
|
+
did: z.ZodString;
|
|
698
|
+
/**
|
|
699
|
+
* The organization's handle - human-readable identifier.
|
|
700
|
+
* Format: "orgname.sds.hypercerts.org" or similar
|
|
701
|
+
*/
|
|
702
|
+
handle: z.ZodString;
|
|
703
|
+
/**
|
|
704
|
+
* Display name for the organization.
|
|
705
|
+
*/
|
|
706
|
+
name: z.ZodString;
|
|
707
|
+
/**
|
|
708
|
+
* Optional description of the organization's purpose.
|
|
709
|
+
*/
|
|
710
|
+
description: z.ZodOptional<z.ZodString>;
|
|
711
|
+
/**
|
|
712
|
+
* ISO 8601 timestamp when the organization was created.
|
|
713
|
+
* Format: "2024-01-15T10:30:00.000Z"
|
|
714
|
+
*/
|
|
715
|
+
createdAt: z.ZodString;
|
|
716
|
+
/**
|
|
717
|
+
* The current user's permissions within this organization.
|
|
718
|
+
*/
|
|
719
|
+
permissions: z.ZodObject<{
|
|
720
|
+
/**
|
|
721
|
+
* Can read/view records in the repository.
|
|
722
|
+
* This is the most basic permission level.
|
|
723
|
+
*/
|
|
724
|
+
read: z.ZodBoolean;
|
|
725
|
+
/**
|
|
726
|
+
* Can create new records in the repository.
|
|
727
|
+
* Typically implies `read` permission.
|
|
728
|
+
*/
|
|
729
|
+
create: z.ZodBoolean;
|
|
730
|
+
/**
|
|
731
|
+
* Can modify existing records in the repository.
|
|
732
|
+
* Typically implies `read` and `create` permissions.
|
|
733
|
+
*/
|
|
734
|
+
update: z.ZodBoolean;
|
|
735
|
+
/**
|
|
736
|
+
* Can delete records from the repository.
|
|
737
|
+
* Typically implies `read`, `create`, and `update` permissions.
|
|
738
|
+
*/
|
|
739
|
+
delete: z.ZodBoolean;
|
|
740
|
+
/**
|
|
741
|
+
* Can manage collaborators and their permissions.
|
|
742
|
+
* Administrative permission that allows inviting/removing collaborators.
|
|
743
|
+
*/
|
|
744
|
+
admin: z.ZodBoolean;
|
|
745
|
+
/**
|
|
746
|
+
* Full ownership of the repository.
|
|
747
|
+
* Owners have all permissions and cannot be removed by other admins.
|
|
748
|
+
* There must always be at least one owner.
|
|
749
|
+
*/
|
|
750
|
+
owner: z.ZodBoolean;
|
|
751
|
+
}, "strip", z.ZodTypeAny, {
|
|
752
|
+
read: boolean;
|
|
753
|
+
create: boolean;
|
|
754
|
+
update: boolean;
|
|
755
|
+
delete: boolean;
|
|
756
|
+
admin: boolean;
|
|
757
|
+
owner: boolean;
|
|
758
|
+
}, {
|
|
759
|
+
read: boolean;
|
|
760
|
+
create: boolean;
|
|
761
|
+
update: boolean;
|
|
762
|
+
delete: boolean;
|
|
763
|
+
admin: boolean;
|
|
764
|
+
owner: boolean;
|
|
765
|
+
}>;
|
|
766
|
+
/**
|
|
767
|
+
* How the current user relates to this organization.
|
|
768
|
+
* - `"owner"`: User created or owns the organization
|
|
769
|
+
* - `"collaborator"`: User was invited to collaborate
|
|
770
|
+
*/
|
|
771
|
+
accessType: z.ZodEnum<["owner", "collaborator"]>;
|
|
772
|
+
}, "strip", z.ZodTypeAny, {
|
|
773
|
+
did: string;
|
|
774
|
+
handle: string;
|
|
775
|
+
name: string;
|
|
776
|
+
createdAt: string;
|
|
777
|
+
permissions: {
|
|
778
|
+
read: boolean;
|
|
779
|
+
create: boolean;
|
|
780
|
+
update: boolean;
|
|
781
|
+
delete: boolean;
|
|
782
|
+
admin: boolean;
|
|
783
|
+
owner: boolean;
|
|
784
|
+
};
|
|
785
|
+
accessType: "owner" | "collaborator";
|
|
786
|
+
description?: string | undefined;
|
|
787
|
+
}, {
|
|
788
|
+
did: string;
|
|
789
|
+
handle: string;
|
|
790
|
+
name: string;
|
|
791
|
+
createdAt: string;
|
|
792
|
+
permissions: {
|
|
793
|
+
read: boolean;
|
|
794
|
+
create: boolean;
|
|
795
|
+
update: boolean;
|
|
796
|
+
delete: boolean;
|
|
797
|
+
admin: boolean;
|
|
798
|
+
owner: boolean;
|
|
799
|
+
};
|
|
800
|
+
accessType: "owner" | "collaborator";
|
|
801
|
+
description?: string | undefined;
|
|
802
|
+
}>;
|
|
803
|
+
/**
|
|
804
|
+
* SDS Organization entity.
|
|
805
|
+
*
|
|
806
|
+
* Represents an organization on a Shared Data Server. Organizations
|
|
807
|
+
* provide a way to group repositories and manage access for teams.
|
|
808
|
+
*
|
|
809
|
+
* @example
|
|
810
|
+
* ```typescript
|
|
811
|
+
* const org: Organization = {
|
|
812
|
+
* did: "did:plc:org123abc",
|
|
813
|
+
* handle: "my-team.sds.hypercerts.org",
|
|
814
|
+
* name: "My Team",
|
|
815
|
+
* description: "A team working on impact certificates",
|
|
816
|
+
* createdAt: "2024-01-15T10:30:00.000Z",
|
|
817
|
+
* permissions: {
|
|
818
|
+
* read: true,
|
|
819
|
+
* create: true,
|
|
820
|
+
* update: true,
|
|
821
|
+
* delete: true,
|
|
822
|
+
* admin: true,
|
|
823
|
+
* owner: true,
|
|
824
|
+
* },
|
|
825
|
+
* accessType: "owner",
|
|
826
|
+
* };
|
|
827
|
+
* ```
|
|
828
|
+
*/
|
|
829
|
+
type Organization = z.infer<typeof OrganizationSchema>;
|
|
830
|
+
/**
|
|
831
|
+
* Zod schema for collaborator data.
|
|
832
|
+
*
|
|
833
|
+
* Represents a user who has been granted access to a shared repository
|
|
834
|
+
* or organization with specific permissions.
|
|
835
|
+
*/
|
|
836
|
+
declare const CollaboratorSchema: z.ZodObject<{
|
|
837
|
+
/**
|
|
838
|
+
* The collaborator's DID - their unique identifier.
|
|
839
|
+
* Format: "did:plc:..." or "did:web:..."
|
|
840
|
+
*/
|
|
841
|
+
userDid: z.ZodString;
|
|
842
|
+
/**
|
|
843
|
+
* The permissions granted to this collaborator.
|
|
844
|
+
*/
|
|
845
|
+
permissions: z.ZodObject<{
|
|
846
|
+
/**
|
|
847
|
+
* Can read/view records in the repository.
|
|
848
|
+
* This is the most basic permission level.
|
|
849
|
+
*/
|
|
850
|
+
read: z.ZodBoolean;
|
|
851
|
+
/**
|
|
852
|
+
* Can create new records in the repository.
|
|
853
|
+
* Typically implies `read` permission.
|
|
854
|
+
*/
|
|
855
|
+
create: z.ZodBoolean;
|
|
856
|
+
/**
|
|
857
|
+
* Can modify existing records in the repository.
|
|
858
|
+
* Typically implies `read` and `create` permissions.
|
|
859
|
+
*/
|
|
860
|
+
update: z.ZodBoolean;
|
|
861
|
+
/**
|
|
862
|
+
* Can delete records from the repository.
|
|
863
|
+
* Typically implies `read`, `create`, and `update` permissions.
|
|
864
|
+
*/
|
|
865
|
+
delete: z.ZodBoolean;
|
|
866
|
+
/**
|
|
867
|
+
* Can manage collaborators and their permissions.
|
|
868
|
+
* Administrative permission that allows inviting/removing collaborators.
|
|
869
|
+
*/
|
|
870
|
+
admin: z.ZodBoolean;
|
|
871
|
+
/**
|
|
872
|
+
* Full ownership of the repository.
|
|
873
|
+
* Owners have all permissions and cannot be removed by other admins.
|
|
874
|
+
* There must always be at least one owner.
|
|
875
|
+
*/
|
|
876
|
+
owner: z.ZodBoolean;
|
|
877
|
+
}, "strip", z.ZodTypeAny, {
|
|
878
|
+
read: boolean;
|
|
879
|
+
create: boolean;
|
|
880
|
+
update: boolean;
|
|
881
|
+
delete: boolean;
|
|
882
|
+
admin: boolean;
|
|
883
|
+
owner: boolean;
|
|
884
|
+
}, {
|
|
885
|
+
read: boolean;
|
|
886
|
+
create: boolean;
|
|
887
|
+
update: boolean;
|
|
888
|
+
delete: boolean;
|
|
889
|
+
admin: boolean;
|
|
890
|
+
owner: boolean;
|
|
891
|
+
}>;
|
|
892
|
+
/**
|
|
893
|
+
* DID of the user who granted these permissions.
|
|
894
|
+
* Useful for audit trails.
|
|
895
|
+
*/
|
|
896
|
+
grantedBy: z.ZodString;
|
|
897
|
+
/**
|
|
898
|
+
* ISO 8601 timestamp when permissions were granted.
|
|
899
|
+
* Format: "2024-01-15T10:30:00.000Z"
|
|
900
|
+
*/
|
|
901
|
+
grantedAt: z.ZodString;
|
|
902
|
+
/**
|
|
903
|
+
* ISO 8601 timestamp when permissions were revoked, if applicable.
|
|
904
|
+
* Undefined if the collaborator is still active.
|
|
905
|
+
*/
|
|
906
|
+
revokedAt: z.ZodOptional<z.ZodString>;
|
|
907
|
+
}, "strip", z.ZodTypeAny, {
|
|
908
|
+
permissions: {
|
|
909
|
+
read: boolean;
|
|
910
|
+
create: boolean;
|
|
911
|
+
update: boolean;
|
|
912
|
+
delete: boolean;
|
|
913
|
+
admin: boolean;
|
|
914
|
+
owner: boolean;
|
|
915
|
+
};
|
|
916
|
+
userDid: string;
|
|
917
|
+
grantedBy: string;
|
|
918
|
+
grantedAt: string;
|
|
919
|
+
revokedAt?: string | undefined;
|
|
920
|
+
}, {
|
|
921
|
+
permissions: {
|
|
922
|
+
read: boolean;
|
|
923
|
+
create: boolean;
|
|
924
|
+
update: boolean;
|
|
925
|
+
delete: boolean;
|
|
926
|
+
admin: boolean;
|
|
927
|
+
owner: boolean;
|
|
928
|
+
};
|
|
929
|
+
userDid: string;
|
|
930
|
+
grantedBy: string;
|
|
931
|
+
grantedAt: string;
|
|
932
|
+
revokedAt?: string | undefined;
|
|
933
|
+
}>;
|
|
934
|
+
/**
|
|
935
|
+
* Collaborator information for SDS repositories.
|
|
936
|
+
*
|
|
937
|
+
* Represents a user who has been granted access to collaborate on
|
|
938
|
+
* a shared repository or organization.
|
|
939
|
+
*
|
|
940
|
+
* @example
|
|
941
|
+
* ```typescript
|
|
942
|
+
* const collaborator: Collaborator = {
|
|
943
|
+
* userDid: "did:plc:user456def",
|
|
944
|
+
* permissions: {
|
|
945
|
+
* read: true,
|
|
946
|
+
* create: true,
|
|
947
|
+
* update: true,
|
|
948
|
+
* delete: false,
|
|
949
|
+
* admin: false,
|
|
950
|
+
* owner: false,
|
|
951
|
+
* },
|
|
952
|
+
* grantedBy: "did:plc:owner123abc",
|
|
953
|
+
* grantedAt: "2024-02-01T14:00:00.000Z",
|
|
954
|
+
* };
|
|
955
|
+
* ```
|
|
956
|
+
*/
|
|
957
|
+
type Collaborator = z.infer<typeof CollaboratorSchema>;
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Repository types - Shared types for repository operations
|
|
961
|
+
* @packageDocumentation
|
|
962
|
+
*/
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* Options for creating a repository instance
|
|
966
|
+
*/
|
|
967
|
+
interface RepositoryOptions {
|
|
968
|
+
/** Use "sds" for configured SDS server, or provide a custom URL */
|
|
969
|
+
server?: "pds" | "sds" | string;
|
|
970
|
+
/** Custom server URL (overrides server option) */
|
|
971
|
+
serverUrl?: string;
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Result of a create operation
|
|
975
|
+
*/
|
|
976
|
+
interface CreateResult {
|
|
977
|
+
uri: string;
|
|
978
|
+
cid: string;
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Result of an update operation
|
|
982
|
+
*/
|
|
983
|
+
interface UpdateResult {
|
|
984
|
+
uri: string;
|
|
985
|
+
cid: string;
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Paginated list result
|
|
989
|
+
*/
|
|
990
|
+
interface PaginatedList<T> {
|
|
991
|
+
records: T[];
|
|
992
|
+
cursor?: string;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* List parameters
|
|
996
|
+
*/
|
|
997
|
+
interface ListParams {
|
|
998
|
+
limit?: number;
|
|
999
|
+
cursor?: string;
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Role for repository access
|
|
1003
|
+
*/
|
|
1004
|
+
type RepositoryRole = "viewer" | "editor" | "admin" | "owner";
|
|
1005
|
+
/**
|
|
1006
|
+
* Repository access grant
|
|
1007
|
+
*/
|
|
1008
|
+
interface RepositoryAccessGrant {
|
|
1009
|
+
userDid: string;
|
|
1010
|
+
role: RepositoryRole;
|
|
1011
|
+
permissions: CollaboratorPermissions;
|
|
1012
|
+
grantedBy: string;
|
|
1013
|
+
grantedAt: string;
|
|
1014
|
+
revokedAt?: string;
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Organization info
|
|
1018
|
+
*/
|
|
1019
|
+
interface OrganizationInfo {
|
|
1020
|
+
did: string;
|
|
1021
|
+
handle: string;
|
|
1022
|
+
name: string;
|
|
1023
|
+
description?: string;
|
|
1024
|
+
createdAt: string;
|
|
1025
|
+
accessType: "owner" | "collaborator";
|
|
1026
|
+
permissions: CollaboratorPermissions;
|
|
1027
|
+
collaboratorCount?: number;
|
|
1028
|
+
profile?: {
|
|
1029
|
+
displayName?: string;
|
|
1030
|
+
avatar?: string;
|
|
1031
|
+
banner?: string;
|
|
1032
|
+
website?: string;
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Progress step for long-running operations
|
|
1037
|
+
*/
|
|
1038
|
+
interface ProgressStep {
|
|
1039
|
+
name: string;
|
|
1040
|
+
status: "start" | "success" | "error";
|
|
1041
|
+
data?: unknown;
|
|
1042
|
+
error?: Error;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* TypeScript types for hypercert lexicon schemas.
|
|
1047
|
+
*
|
|
1048
|
+
* Re-exports generated types and validation from `@hypercerts-org/lexicon`.
|
|
1049
|
+
*
|
|
1050
|
+
* @packageDocumentation
|
|
1051
|
+
*/
|
|
1052
|
+
|
|
1053
|
+
type StrongRef = ComAtprotoRepoStrongRef.Main;
|
|
1054
|
+
type HypercertClaim = OrgHypercertsClaim.Main;
|
|
1055
|
+
type HypercertRights = OrgHypercertsClaimRights.Main;
|
|
1056
|
+
type HypercertContribution = OrgHypercertsClaimContribution.Main;
|
|
1057
|
+
type HypercertMeasurement = OrgHypercertsClaimMeasurement.Main;
|
|
1058
|
+
type HypercertEvaluation = OrgHypercertsClaimEvaluation.Main;
|
|
1059
|
+
type HypercertCollection = OrgHypercertsCollection.Main;
|
|
1060
|
+
type HypercertCollectionClaimItem = OrgHypercertsCollection.ClaimItem;
|
|
1061
|
+
type HypercertLocation = AppCertifiedLocation.Main;
|
|
1062
|
+
/** Blob reference for uploaded files (images, GeoJSON, etc.) */
|
|
1063
|
+
interface BlobRef {
|
|
1064
|
+
$type: "blob";
|
|
1065
|
+
ref: {
|
|
1066
|
+
$link: string;
|
|
1067
|
+
};
|
|
1068
|
+
mimeType: string;
|
|
1069
|
+
size: number;
|
|
1070
|
+
}
|
|
1071
|
+
/** Evidence item for operations */
|
|
1072
|
+
interface HypercertEvidence {
|
|
1073
|
+
uri: string;
|
|
1074
|
+
title?: string;
|
|
1075
|
+
description?: string;
|
|
1076
|
+
}
|
|
1077
|
+
/** Image input for operations */
|
|
1078
|
+
type HypercertImage = {
|
|
1079
|
+
type: "uri";
|
|
1080
|
+
uri: string;
|
|
1081
|
+
} | {
|
|
1082
|
+
type: "blob";
|
|
1083
|
+
blob: Blob;
|
|
1084
|
+
};
|
|
1085
|
+
/** Hypercert with AT Protocol metadata */
|
|
1086
|
+
interface HypercertWithMetadata {
|
|
1087
|
+
uri: string;
|
|
1088
|
+
cid: string;
|
|
1089
|
+
record: HypercertClaim;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* Repository interfaces - Operation contracts for repository functionality.
|
|
1094
|
+
*
|
|
1095
|
+
* This module defines the interfaces for all repository operations,
|
|
1096
|
+
* providing clear contracts for record management, blob handling,
|
|
1097
|
+
* profile management, and domain-specific operations.
|
|
1098
|
+
*
|
|
1099
|
+
* @packageDocumentation
|
|
1100
|
+
*/
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* Parameters for creating a new hypercert.
|
|
1104
|
+
*
|
|
1105
|
+
* This interface defines all the data needed to create a hypercert
|
|
1106
|
+
* along with optional related records (location, contributions, evidence).
|
|
1107
|
+
*
|
|
1108
|
+
* @example Minimal hypercert
|
|
1109
|
+
* ```typescript
|
|
1110
|
+
* const params: CreateHypercertParams = {
|
|
1111
|
+
* title: "Community Garden Project",
|
|
1112
|
+
* description: "Established a 1-acre community garden serving 50 families",
|
|
1113
|
+
* workScope: "Food Security",
|
|
1114
|
+
* workTimeframeFrom: "2024-01-01",
|
|
1115
|
+
* workTimeframeTo: "2024-06-30",
|
|
1116
|
+
* rights: {
|
|
1117
|
+
* name: "Attribution",
|
|
1118
|
+
* type: "license",
|
|
1119
|
+
* description: "CC-BY-4.0",
|
|
1120
|
+
* },
|
|
1121
|
+
* };
|
|
1122
|
+
* ```
|
|
1123
|
+
*
|
|
1124
|
+
* @example Full hypercert with all options
|
|
1125
|
+
* ```typescript
|
|
1126
|
+
* const params: CreateHypercertParams = {
|
|
1127
|
+
* title: "Reforestation Initiative",
|
|
1128
|
+
* description: "Planted 10,000 trees in deforested areas",
|
|
1129
|
+
* shortDescription: "10K trees planted",
|
|
1130
|
+
* workScope: "Environmental Restoration",
|
|
1131
|
+
* workTimeframeFrom: "2024-01-01",
|
|
1132
|
+
* workTimeframeTo: "2024-12-31",
|
|
1133
|
+
* rights: {
|
|
1134
|
+
* name: "Open Impact",
|
|
1135
|
+
* type: "impact-rights",
|
|
1136
|
+
* description: "Transferable impact rights",
|
|
1137
|
+
* },
|
|
1138
|
+
* image: coverImageBlob,
|
|
1139
|
+
* location: {
|
|
1140
|
+
* value: "Amazon Rainforest, Brazil",
|
|
1141
|
+
* name: "Amazon Basin",
|
|
1142
|
+
* description: "Southern Amazon region",
|
|
1143
|
+
* },
|
|
1144
|
+
* contributions: [
|
|
1145
|
+
* {
|
|
1146
|
+
* contributors: ["did:plc:lead-org"],
|
|
1147
|
+
* role: "coordinator",
|
|
1148
|
+
* description: "Project coordination and funding",
|
|
1149
|
+
* },
|
|
1150
|
+
* {
|
|
1151
|
+
* contributors: ["did:plc:local-partner"],
|
|
1152
|
+
* role: "implementer",
|
|
1153
|
+
* description: "On-ground planting and monitoring",
|
|
1154
|
+
* },
|
|
1155
|
+
* ],
|
|
1156
|
+
* evidence: [
|
|
1157
|
+
* {
|
|
1158
|
+
* uri: "https://example.com/satellite-data",
|
|
1159
|
+
* description: "Satellite imagery showing reforestation progress",
|
|
1160
|
+
* },
|
|
1161
|
+
* ],
|
|
1162
|
+
* onProgress: (step) => console.log(`${step.name}: ${step.status}`),
|
|
1163
|
+
* };
|
|
1164
|
+
* ```
|
|
1165
|
+
*/
|
|
1166
|
+
interface CreateHypercertParams {
|
|
1167
|
+
/**
|
|
1168
|
+
* Title of the hypercert.
|
|
1169
|
+
*
|
|
1170
|
+
* Should be concise but descriptive of the impact claim.
|
|
1171
|
+
*/
|
|
1172
|
+
title: string;
|
|
1173
|
+
/**
|
|
1174
|
+
* Detailed description of the impact.
|
|
1175
|
+
*
|
|
1176
|
+
* Can include methodology, outcomes, and other relevant details.
|
|
1177
|
+
* Supports markdown formatting.
|
|
1178
|
+
*/
|
|
1179
|
+
description: string;
|
|
1180
|
+
/**
|
|
1181
|
+
* Scope of work or impact area.
|
|
1182
|
+
*
|
|
1183
|
+
* @example "Climate Action", "Education", "Healthcare"
|
|
1184
|
+
*/
|
|
1185
|
+
workScope: string;
|
|
1186
|
+
/**
|
|
1187
|
+
* Start date of the work period.
|
|
1188
|
+
*
|
|
1189
|
+
* ISO 8601 date format (YYYY-MM-DD).
|
|
1190
|
+
*/
|
|
1191
|
+
workTimeframeFrom: string;
|
|
1192
|
+
/**
|
|
1193
|
+
* End date of the work period.
|
|
1194
|
+
*
|
|
1195
|
+
* ISO 8601 date format (YYYY-MM-DD).
|
|
1196
|
+
*/
|
|
1197
|
+
workTimeframeTo: string;
|
|
1198
|
+
/**
|
|
1199
|
+
* Rights associated with the hypercert.
|
|
1200
|
+
*/
|
|
1201
|
+
rights: {
|
|
1202
|
+
/**
|
|
1203
|
+
* Name of the rights.
|
|
1204
|
+
*
|
|
1205
|
+
* @example "Attribution", "Impact Rights", "Public Domain"
|
|
1206
|
+
*/
|
|
1207
|
+
name: string;
|
|
1208
|
+
/**
|
|
1209
|
+
* Type of rights.
|
|
1210
|
+
*
|
|
1211
|
+
* @example "license", "impact-rights", "ownership"
|
|
1212
|
+
*/
|
|
1213
|
+
type: string;
|
|
1214
|
+
/**
|
|
1215
|
+
* Description of the rights terms.
|
|
1216
|
+
*
|
|
1217
|
+
* @example "CC-BY-4.0", "Transferable impact rights"
|
|
1218
|
+
*/
|
|
1219
|
+
description: string;
|
|
1220
|
+
};
|
|
1221
|
+
/**
|
|
1222
|
+
* Optional short description for display in lists/cards.
|
|
1223
|
+
*
|
|
1224
|
+
* Should be under 200 characters.
|
|
1225
|
+
*/
|
|
1226
|
+
shortDescription?: string;
|
|
1227
|
+
/**
|
|
1228
|
+
* Optional cover image for the hypercert.
|
|
1229
|
+
*
|
|
1230
|
+
* Recommended size: 1200x630 pixels (social media preview ratio).
|
|
1231
|
+
*/
|
|
1232
|
+
image?: Blob;
|
|
1233
|
+
/**
|
|
1234
|
+
* Optional geographic location of the impact.
|
|
1235
|
+
*/
|
|
1236
|
+
location?: {
|
|
1237
|
+
/**
|
|
1238
|
+
* Location value/address.
|
|
1239
|
+
*
|
|
1240
|
+
* @example "San Francisco, CA, USA"
|
|
1241
|
+
*/
|
|
1242
|
+
value: string;
|
|
1243
|
+
/**
|
|
1244
|
+
* Human-readable location name.
|
|
1245
|
+
*
|
|
1246
|
+
* @example "SF Bay Area"
|
|
1247
|
+
*/
|
|
1248
|
+
name?: string;
|
|
1249
|
+
/**
|
|
1250
|
+
* Description of the location scope.
|
|
1251
|
+
*/
|
|
1252
|
+
description?: string;
|
|
1253
|
+
/**
|
|
1254
|
+
* Spatial Reference System identifier.
|
|
1255
|
+
*
|
|
1256
|
+
* @example "EPSG:4326" for WGS84
|
|
1257
|
+
*/
|
|
1258
|
+
srs?: string;
|
|
1259
|
+
/**
|
|
1260
|
+
* GeoJSON file as a Blob for precise boundaries.
|
|
1261
|
+
*/
|
|
1262
|
+
geojson?: Blob;
|
|
1263
|
+
};
|
|
1264
|
+
/**
|
|
1265
|
+
* Optional list of contributions to the impact.
|
|
1266
|
+
*
|
|
1267
|
+
* Use this to credit multiple contributors with different roles.
|
|
1268
|
+
*/
|
|
1269
|
+
contributions?: Array<{
|
|
1270
|
+
/**
|
|
1271
|
+
* DIDs of the contributors.
|
|
1272
|
+
*/
|
|
1273
|
+
contributors: string[];
|
|
1274
|
+
/**
|
|
1275
|
+
* Role in the contribution.
|
|
1276
|
+
*
|
|
1277
|
+
* @example "coordinator", "implementer", "funder", "volunteer"
|
|
1278
|
+
*/
|
|
1279
|
+
role: string;
|
|
1280
|
+
/**
|
|
1281
|
+
* Description of the contribution.
|
|
1282
|
+
*/
|
|
1283
|
+
description?: string;
|
|
1284
|
+
}>;
|
|
1285
|
+
/**
|
|
1286
|
+
* Optional evidence supporting the impact claim.
|
|
1287
|
+
*/
|
|
1288
|
+
evidence?: HypercertEvidence[];
|
|
1289
|
+
/**
|
|
1290
|
+
* Optional callback for progress updates during creation.
|
|
1291
|
+
*
|
|
1292
|
+
* Called for each step of the creation process (image upload,
|
|
1293
|
+
* record creation, attachment linking, etc.).
|
|
1294
|
+
*/
|
|
1295
|
+
onProgress?: (step: ProgressStep) => void;
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Result of creating a hypercert.
|
|
1299
|
+
*
|
|
1300
|
+
* Contains URIs and CIDs for all created records.
|
|
1301
|
+
*/
|
|
1302
|
+
interface CreateHypercertResult {
|
|
1303
|
+
/**
|
|
1304
|
+
* AT-URI of the main hypercert record.
|
|
1305
|
+
*/
|
|
1306
|
+
hypercertUri: string;
|
|
1307
|
+
/**
|
|
1308
|
+
* AT-URI of the associated rights record.
|
|
1309
|
+
*/
|
|
1310
|
+
rightsUri: string;
|
|
1311
|
+
/**
|
|
1312
|
+
* CID of the hypercert record.
|
|
1313
|
+
*/
|
|
1314
|
+
hypercertCid: string;
|
|
1315
|
+
/**
|
|
1316
|
+
* CID of the rights record.
|
|
1317
|
+
*/
|
|
1318
|
+
rightsCid: string;
|
|
1319
|
+
/**
|
|
1320
|
+
* AT-URI of the location record, if location was provided.
|
|
1321
|
+
*/
|
|
1322
|
+
locationUri?: string;
|
|
1323
|
+
/**
|
|
1324
|
+
* AT-URIs of contribution records, if contributions were provided.
|
|
1325
|
+
*/
|
|
1326
|
+
contributionUris?: string[];
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Low-level record operations for AT Protocol CRUD.
|
|
1330
|
+
*
|
|
1331
|
+
* Use this interface for direct access to AT Protocol records when
|
|
1332
|
+
* the high-level APIs don't meet your needs.
|
|
1333
|
+
*
|
|
1334
|
+
* @example
|
|
1335
|
+
* ```typescript
|
|
1336
|
+
* // Create a record
|
|
1337
|
+
* const { uri, cid } = await repo.records.create({
|
|
1338
|
+
* collection: "org.example.mytype",
|
|
1339
|
+
* record: { foo: "bar", createdAt: new Date().toISOString() },
|
|
1340
|
+
* });
|
|
1341
|
+
*
|
|
1342
|
+
* // Get a record
|
|
1343
|
+
* const { value } = await repo.records.get({
|
|
1344
|
+
* collection: "org.example.mytype",
|
|
1345
|
+
* rkey: "abc123",
|
|
1346
|
+
* });
|
|
1347
|
+
*
|
|
1348
|
+
* // Update a record
|
|
1349
|
+
* await repo.records.update({
|
|
1350
|
+
* collection: "org.example.mytype",
|
|
1351
|
+
* rkey: "abc123",
|
|
1352
|
+
* record: { ...value, foo: "updated" },
|
|
1353
|
+
* });
|
|
1354
|
+
*
|
|
1355
|
+
* // List records
|
|
1356
|
+
* const { records, cursor } = await repo.records.list({
|
|
1357
|
+
* collection: "org.example.mytype",
|
|
1358
|
+
* limit: 50,
|
|
1359
|
+
* });
|
|
1360
|
+
*
|
|
1361
|
+
* // Delete a record
|
|
1362
|
+
* await repo.records.delete({
|
|
1363
|
+
* collection: "org.example.mytype",
|
|
1364
|
+
* rkey: "abc123",
|
|
1365
|
+
* });
|
|
1366
|
+
* ```
|
|
1367
|
+
*/
|
|
1368
|
+
interface RecordOperations {
|
|
1369
|
+
/**
|
|
1370
|
+
* Creates a new record in a collection.
|
|
1371
|
+
*
|
|
1372
|
+
* @param params - Creation parameters
|
|
1373
|
+
* @param params.collection - NSID of the collection (e.g., "org.hypercerts.hypercert")
|
|
1374
|
+
* @param params.record - Record data (must conform to collection's lexicon)
|
|
1375
|
+
* @param params.rkey - Optional record key. Auto-generated if not provided.
|
|
1376
|
+
* @returns Promise resolving to the created record's URI and CID
|
|
1377
|
+
*/
|
|
1378
|
+
create(params: {
|
|
1379
|
+
collection: string;
|
|
1380
|
+
record: unknown;
|
|
1381
|
+
rkey?: string;
|
|
1382
|
+
}): Promise<CreateResult>;
|
|
1383
|
+
/**
|
|
1384
|
+
* Updates an existing record.
|
|
1385
|
+
*
|
|
1386
|
+
* @param params - Update parameters
|
|
1387
|
+
* @param params.collection - NSID of the collection
|
|
1388
|
+
* @param params.rkey - Record key to update
|
|
1389
|
+
* @param params.record - New record data (replaces entire record)
|
|
1390
|
+
* @returns Promise resolving to the updated record's URI and CID
|
|
1391
|
+
*/
|
|
1392
|
+
update(params: {
|
|
1393
|
+
collection: string;
|
|
1394
|
+
rkey: string;
|
|
1395
|
+
record: unknown;
|
|
1396
|
+
}): Promise<UpdateResult>;
|
|
1397
|
+
/**
|
|
1398
|
+
* Gets a single record by collection and key.
|
|
1399
|
+
*
|
|
1400
|
+
* @param params - Get parameters
|
|
1401
|
+
* @param params.collection - NSID of the collection
|
|
1402
|
+
* @param params.rkey - Record key
|
|
1403
|
+
* @returns Promise resolving to the record's URI, CID, and value
|
|
1404
|
+
*/
|
|
1405
|
+
get(params: {
|
|
1406
|
+
collection: string;
|
|
1407
|
+
rkey: string;
|
|
1408
|
+
}): Promise<{
|
|
1409
|
+
uri: string;
|
|
1410
|
+
cid: string;
|
|
1411
|
+
value: unknown;
|
|
1412
|
+
}>;
|
|
1413
|
+
/**
|
|
1414
|
+
* Lists records in a collection with pagination.
|
|
1415
|
+
*
|
|
1416
|
+
* @param params - List parameters
|
|
1417
|
+
* @param params.collection - NSID of the collection
|
|
1418
|
+
* @param params.limit - Maximum records to return (default varies by server)
|
|
1419
|
+
* @param params.cursor - Pagination cursor from previous response
|
|
1420
|
+
* @returns Promise resolving to paginated list of records
|
|
1421
|
+
*/
|
|
1422
|
+
list(params: {
|
|
1423
|
+
collection: string;
|
|
1424
|
+
limit?: number;
|
|
1425
|
+
cursor?: string;
|
|
1426
|
+
}): Promise<PaginatedList<{
|
|
1427
|
+
uri: string;
|
|
1428
|
+
cid: string;
|
|
1429
|
+
value: unknown;
|
|
1430
|
+
}>>;
|
|
1431
|
+
/**
|
|
1432
|
+
* Deletes a record.
|
|
1433
|
+
*
|
|
1434
|
+
* @param params - Delete parameters
|
|
1435
|
+
* @param params.collection - NSID of the collection
|
|
1436
|
+
* @param params.rkey - Record key to delete
|
|
1437
|
+
*/
|
|
1438
|
+
delete(params: {
|
|
1439
|
+
collection: string;
|
|
1440
|
+
rkey: string;
|
|
1441
|
+
}): Promise<void>;
|
|
1442
|
+
}
|
|
1443
|
+
/**
|
|
1444
|
+
* Blob operations for binary data handling.
|
|
1445
|
+
*
|
|
1446
|
+
* Blobs are used for images, files, and other binary content.
|
|
1447
|
+
* They are referenced from records using blob references.
|
|
1448
|
+
*
|
|
1449
|
+
* @example
|
|
1450
|
+
* ```typescript
|
|
1451
|
+
* // Upload an image
|
|
1452
|
+
* const imageBlob = new Blob([imageData], { type: "image/jpeg" });
|
|
1453
|
+
* const { ref, mimeType, size } = await repo.blobs.upload(imageBlob);
|
|
1454
|
+
*
|
|
1455
|
+
* // Use the ref in a record
|
|
1456
|
+
* await repo.records.create({
|
|
1457
|
+
* collection: "org.example.post",
|
|
1458
|
+
* record: {
|
|
1459
|
+
* text: "Hello!",
|
|
1460
|
+
* image: ref, // { $link: "bafyrei..." }
|
|
1461
|
+
* },
|
|
1462
|
+
* });
|
|
1463
|
+
*
|
|
1464
|
+
* // Retrieve a blob
|
|
1465
|
+
* const { data, mimeType } = await repo.blobs.get(ref.$link);
|
|
1466
|
+
* ```
|
|
1467
|
+
*/
|
|
1468
|
+
interface BlobOperations {
|
|
1469
|
+
/**
|
|
1470
|
+
* Uploads a blob to the server.
|
|
1471
|
+
*
|
|
1472
|
+
* @param blob - The blob to upload
|
|
1473
|
+
* @returns Promise resolving to blob reference and metadata
|
|
1474
|
+
*/
|
|
1475
|
+
upload(blob: Blob): Promise<{
|
|
1476
|
+
/**
|
|
1477
|
+
* Blob reference to use in records.
|
|
1478
|
+
*
|
|
1479
|
+
* Contains `$link` property with the CID.
|
|
1480
|
+
*/
|
|
1481
|
+
ref: {
|
|
1482
|
+
$link: string;
|
|
1483
|
+
};
|
|
1484
|
+
/**
|
|
1485
|
+
* MIME type of the uploaded blob.
|
|
1486
|
+
*/
|
|
1487
|
+
mimeType: string;
|
|
1488
|
+
/**
|
|
1489
|
+
* Size of the blob in bytes.
|
|
1490
|
+
*/
|
|
1491
|
+
size: number;
|
|
1492
|
+
}>;
|
|
1493
|
+
/**
|
|
1494
|
+
* Retrieves a blob by its CID.
|
|
1495
|
+
*
|
|
1496
|
+
* @param cid - Content Identifier of the blob
|
|
1497
|
+
* @returns Promise resolving to blob data and MIME type
|
|
1498
|
+
*/
|
|
1499
|
+
get(cid: string): Promise<{
|
|
1500
|
+
/**
|
|
1501
|
+
* Raw blob data as Uint8Array.
|
|
1502
|
+
*/
|
|
1503
|
+
data: Uint8Array;
|
|
1504
|
+
/**
|
|
1505
|
+
* MIME type of the blob.
|
|
1506
|
+
*/
|
|
1507
|
+
mimeType: string;
|
|
1508
|
+
}>;
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Profile operations for user profile management.
|
|
1512
|
+
*
|
|
1513
|
+
* @example
|
|
1514
|
+
* ```typescript
|
|
1515
|
+
* // Get profile
|
|
1516
|
+
* const profile = await repo.profile.get();
|
|
1517
|
+
* console.log(profile.displayName);
|
|
1518
|
+
*
|
|
1519
|
+
* // Update profile
|
|
1520
|
+
* await repo.profile.update({
|
|
1521
|
+
* displayName: "New Name",
|
|
1522
|
+
* description: "Updated bio",
|
|
1523
|
+
* });
|
|
1524
|
+
*
|
|
1525
|
+
* // Update avatar
|
|
1526
|
+
* await repo.profile.update({
|
|
1527
|
+
* avatar: new Blob([avatarData], { type: "image/png" }),
|
|
1528
|
+
* });
|
|
1529
|
+
*
|
|
1530
|
+
* // Clear a field by passing null
|
|
1531
|
+
* await repo.profile.update({
|
|
1532
|
+
* website: null, // Removes website
|
|
1533
|
+
* });
|
|
1534
|
+
* ```
|
|
1535
|
+
*/
|
|
1536
|
+
interface ProfileOperations {
|
|
1537
|
+
/**
|
|
1538
|
+
* Gets the repository's profile.
|
|
1539
|
+
*
|
|
1540
|
+
* @returns Promise resolving to profile data
|
|
1541
|
+
*/
|
|
1542
|
+
get(): Promise<{
|
|
1543
|
+
/**
|
|
1544
|
+
* User's handle (e.g., "alice.bsky.social").
|
|
1545
|
+
*/
|
|
1546
|
+
handle: string;
|
|
1547
|
+
/**
|
|
1548
|
+
* Display name.
|
|
1549
|
+
*/
|
|
1550
|
+
displayName?: string;
|
|
1551
|
+
/**
|
|
1552
|
+
* Profile description/bio.
|
|
1553
|
+
*/
|
|
1554
|
+
description?: string;
|
|
1555
|
+
/**
|
|
1556
|
+
* Avatar image URL or blob reference.
|
|
1557
|
+
*/
|
|
1558
|
+
avatar?: string;
|
|
1559
|
+
/**
|
|
1560
|
+
* Banner image URL or blob reference.
|
|
1561
|
+
*/
|
|
1562
|
+
banner?: string;
|
|
1563
|
+
/**
|
|
1564
|
+
* Website URL.
|
|
1565
|
+
*/
|
|
1566
|
+
website?: string;
|
|
1567
|
+
}>;
|
|
1568
|
+
/**
|
|
1569
|
+
* Updates the repository's profile.
|
|
1570
|
+
*
|
|
1571
|
+
* Pass `null` to clear a field. Omitted fields are unchanged.
|
|
1572
|
+
*
|
|
1573
|
+
* @param params - Fields to update
|
|
1574
|
+
* @returns Promise resolving to update result
|
|
1575
|
+
*/
|
|
1576
|
+
update(params: {
|
|
1577
|
+
displayName?: string | null;
|
|
1578
|
+
description?: string | null;
|
|
1579
|
+
avatar?: Blob | null;
|
|
1580
|
+
banner?: Blob | null;
|
|
1581
|
+
website?: string | null;
|
|
1582
|
+
}): Promise<UpdateResult>;
|
|
1583
|
+
}
|
|
1584
|
+
/**
|
|
1585
|
+
* Events emitted by hypercert operations.
|
|
1586
|
+
*
|
|
1587
|
+
* Use these to track progress of complex operations or react
|
|
1588
|
+
* to record changes.
|
|
1589
|
+
*/
|
|
1590
|
+
interface HypercertEvents {
|
|
1591
|
+
/**
|
|
1592
|
+
* Emitted when a hypercert record is created.
|
|
1593
|
+
*/
|
|
1594
|
+
recordCreated: {
|
|
1595
|
+
uri: string;
|
|
1596
|
+
cid: string;
|
|
1597
|
+
};
|
|
1598
|
+
/**
|
|
1599
|
+
* Emitted when a hypercert record is updated.
|
|
1600
|
+
*/
|
|
1601
|
+
recordUpdated: {
|
|
1602
|
+
uri: string;
|
|
1603
|
+
cid: string;
|
|
1604
|
+
};
|
|
1605
|
+
/**
|
|
1606
|
+
* Emitted when a rights record is created.
|
|
1607
|
+
*/
|
|
1608
|
+
rightsCreated: {
|
|
1609
|
+
uri: string;
|
|
1610
|
+
cid: string;
|
|
1611
|
+
};
|
|
1612
|
+
/**
|
|
1613
|
+
* Emitted when a location is attached to a hypercert.
|
|
1614
|
+
*/
|
|
1615
|
+
locationAttached: {
|
|
1616
|
+
uri: string;
|
|
1617
|
+
cid: string;
|
|
1618
|
+
hypercertUri: string;
|
|
1619
|
+
};
|
|
1620
|
+
/**
|
|
1621
|
+
* Emitted when a contribution record is created.
|
|
1622
|
+
*/
|
|
1623
|
+
contributionCreated: {
|
|
1624
|
+
uri: string;
|
|
1625
|
+
cid: string;
|
|
1626
|
+
};
|
|
1627
|
+
/**
|
|
1628
|
+
* Emitted when evidence is added to a hypercert.
|
|
1629
|
+
*/
|
|
1630
|
+
evidenceAdded: {
|
|
1631
|
+
uri: string;
|
|
1632
|
+
cid: string;
|
|
1633
|
+
};
|
|
1634
|
+
/**
|
|
1635
|
+
* Emitted when a collection is created.
|
|
1636
|
+
*/
|
|
1637
|
+
collectionCreated: {
|
|
1638
|
+
uri: string;
|
|
1639
|
+
cid: string;
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1643
|
+
* High-level hypercert operations.
|
|
1644
|
+
*
|
|
1645
|
+
* Provides a convenient API for creating and managing hypercerts
|
|
1646
|
+
* with automatic handling of related records (rights, locations,
|
|
1647
|
+
* contributions, etc.).
|
|
1648
|
+
*
|
|
1649
|
+
* Extends EventEmitter to provide progress events.
|
|
1650
|
+
*
|
|
1651
|
+
* @example Basic usage
|
|
1652
|
+
* ```typescript
|
|
1653
|
+
* // Create a hypercert
|
|
1654
|
+
* const result = await repo.hypercerts.create({
|
|
1655
|
+
* title: "Impact Project",
|
|
1656
|
+
* description: "Description...",
|
|
1657
|
+
* workScope: "Climate",
|
|
1658
|
+
* workTimeframeFrom: "2024-01-01",
|
|
1659
|
+
* workTimeframeTo: "2024-12-31",
|
|
1660
|
+
* rights: { name: "CC-BY", type: "license", description: "..." },
|
|
1661
|
+
* });
|
|
1662
|
+
*
|
|
1663
|
+
* // List hypercerts
|
|
1664
|
+
* const { records } = await repo.hypercerts.list({ limit: 20 });
|
|
1665
|
+
*
|
|
1666
|
+
* // Get a specific hypercert
|
|
1667
|
+
* const hypercert = await repo.hypercerts.get(result.hypercertUri);
|
|
1668
|
+
* ```
|
|
1669
|
+
*
|
|
1670
|
+
* @example With events
|
|
1671
|
+
* ```typescript
|
|
1672
|
+
* repo.hypercerts.on("recordCreated", ({ uri }) => {
|
|
1673
|
+
* console.log(`Created: ${uri}`);
|
|
1674
|
+
* });
|
|
1675
|
+
* ```
|
|
1676
|
+
*/
|
|
1677
|
+
interface HypercertOperations extends EventEmitter<HypercertEvents> {
|
|
1678
|
+
/**
|
|
1679
|
+
* Creates a new hypercert with all related records.
|
|
1680
|
+
*
|
|
1681
|
+
* @param params - Creation parameters
|
|
1682
|
+
* @returns Promise resolving to URIs and CIDs of created records
|
|
1683
|
+
*/
|
|
1684
|
+
create(params: CreateHypercertParams): Promise<CreateHypercertResult>;
|
|
1685
|
+
/**
|
|
1686
|
+
* Updates an existing hypercert.
|
|
1687
|
+
*
|
|
1688
|
+
* @param params - Update parameters
|
|
1689
|
+
* @param params.uri - AT-URI of the hypercert to update
|
|
1690
|
+
* @param params.updates - Fields to update
|
|
1691
|
+
* @param params.image - New image, or `null` to remove
|
|
1692
|
+
* @returns Promise resolving to update result
|
|
1693
|
+
*/
|
|
1694
|
+
update(params: {
|
|
1695
|
+
uri: string;
|
|
1696
|
+
updates: Partial<Omit<HypercertClaim, "$type" | "createdAt" | "rights">>;
|
|
1697
|
+
image?: Blob | null;
|
|
1698
|
+
}): Promise<UpdateResult>;
|
|
1699
|
+
/**
|
|
1700
|
+
* Gets a hypercert by URI.
|
|
1701
|
+
*
|
|
1702
|
+
* @param uri - AT-URI of the hypercert
|
|
1703
|
+
* @returns Promise resolving to hypercert data
|
|
1704
|
+
*/
|
|
1705
|
+
get(uri: string): Promise<{
|
|
1706
|
+
uri: string;
|
|
1707
|
+
cid: string;
|
|
1708
|
+
record: HypercertClaim;
|
|
1709
|
+
}>;
|
|
1710
|
+
/**
|
|
1711
|
+
* Lists hypercerts with pagination.
|
|
1712
|
+
*
|
|
1713
|
+
* @param params - Optional pagination parameters
|
|
1714
|
+
* @returns Promise resolving to paginated list
|
|
1715
|
+
*/
|
|
1716
|
+
list(params?: ListParams): Promise<PaginatedList<{
|
|
1717
|
+
uri: string;
|
|
1718
|
+
cid: string;
|
|
1719
|
+
record: HypercertClaim;
|
|
1720
|
+
}>>;
|
|
1721
|
+
/**
|
|
1722
|
+
* Deletes a hypercert.
|
|
1723
|
+
*
|
|
1724
|
+
* Note: This does not automatically delete related records
|
|
1725
|
+
* (rights, locations, contributions).
|
|
1726
|
+
*
|
|
1727
|
+
* @param uri - AT-URI of the hypercert to delete
|
|
1728
|
+
*/
|
|
1729
|
+
delete(uri: string): Promise<void>;
|
|
1730
|
+
/**
|
|
1731
|
+
* Attaches a location to an existing hypercert.
|
|
1732
|
+
*
|
|
1733
|
+
* @param uri - AT-URI of the hypercert
|
|
1734
|
+
* @param location - Location data
|
|
1735
|
+
* @returns Promise resolving to location record result
|
|
1736
|
+
*/
|
|
1737
|
+
attachLocation(uri: string, location: {
|
|
1738
|
+
value: string;
|
|
1739
|
+
name?: string;
|
|
1740
|
+
description?: string;
|
|
1741
|
+
srs?: string;
|
|
1742
|
+
geojson?: Blob;
|
|
1743
|
+
}): Promise<CreateResult>;
|
|
1744
|
+
/**
|
|
1745
|
+
* Adds evidence to an existing hypercert.
|
|
1746
|
+
*
|
|
1747
|
+
* @param uri - AT-URI of the hypercert
|
|
1748
|
+
* @param evidence - Array of evidence items to add
|
|
1749
|
+
* @returns Promise resolving to update result
|
|
1750
|
+
*/
|
|
1751
|
+
addEvidence(uri: string, evidence: HypercertEvidence[]): Promise<UpdateResult>;
|
|
1752
|
+
/**
|
|
1753
|
+
* Creates a contribution record.
|
|
1754
|
+
*
|
|
1755
|
+
* @param params - Contribution parameters
|
|
1756
|
+
* @returns Promise resolving to contribution record result
|
|
1757
|
+
*/
|
|
1758
|
+
addContribution(params: {
|
|
1759
|
+
hypercertUri?: string;
|
|
1760
|
+
contributors: string[];
|
|
1761
|
+
role: string;
|
|
1762
|
+
description?: string;
|
|
1763
|
+
}): Promise<CreateResult>;
|
|
1764
|
+
/**
|
|
1765
|
+
* Creates a measurement record for a hypercert.
|
|
1766
|
+
*
|
|
1767
|
+
* @param params - Measurement parameters
|
|
1768
|
+
* @returns Promise resolving to measurement record result
|
|
1769
|
+
*/
|
|
1770
|
+
addMeasurement(params: {
|
|
1771
|
+
hypercertUri: string;
|
|
1772
|
+
measurers: string[];
|
|
1773
|
+
metric: string;
|
|
1774
|
+
value: string;
|
|
1775
|
+
methodUri?: string;
|
|
1776
|
+
evidenceUris?: string[];
|
|
1777
|
+
}): Promise<CreateResult>;
|
|
1778
|
+
/**
|
|
1779
|
+
* Creates an evaluation record.
|
|
1780
|
+
*
|
|
1781
|
+
* @param params - Evaluation parameters
|
|
1782
|
+
* @returns Promise resolving to evaluation record result
|
|
1783
|
+
*/
|
|
1784
|
+
addEvaluation(params: {
|
|
1785
|
+
subjectUri: string;
|
|
1786
|
+
evaluators: string[];
|
|
1787
|
+
summary: string;
|
|
1788
|
+
}): Promise<CreateResult>;
|
|
1789
|
+
/**
|
|
1790
|
+
* Creates a collection of hypercerts.
|
|
1791
|
+
*
|
|
1792
|
+
* @param params - Collection parameters
|
|
1793
|
+
* @returns Promise resolving to collection record result
|
|
1794
|
+
*/
|
|
1795
|
+
createCollection(params: {
|
|
1796
|
+
title: string;
|
|
1797
|
+
claims: Array<{
|
|
1798
|
+
uri: string;
|
|
1799
|
+
cid: string;
|
|
1800
|
+
weight: string;
|
|
1801
|
+
}>;
|
|
1802
|
+
shortDescription?: string;
|
|
1803
|
+
coverPhoto?: Blob;
|
|
1804
|
+
}): Promise<CreateResult>;
|
|
1805
|
+
/**
|
|
1806
|
+
* Gets a collection by URI.
|
|
1807
|
+
*
|
|
1808
|
+
* @param uri - AT-URI of the collection
|
|
1809
|
+
* @returns Promise resolving to collection data
|
|
1810
|
+
*/
|
|
1811
|
+
getCollection(uri: string): Promise<{
|
|
1812
|
+
uri: string;
|
|
1813
|
+
cid: string;
|
|
1814
|
+
record: HypercertCollection;
|
|
1815
|
+
}>;
|
|
1816
|
+
/**
|
|
1817
|
+
* Lists collections with pagination.
|
|
1818
|
+
*
|
|
1819
|
+
* @param params - Optional pagination parameters
|
|
1820
|
+
* @returns Promise resolving to paginated list
|
|
1821
|
+
*/
|
|
1822
|
+
listCollections(params?: ListParams): Promise<PaginatedList<{
|
|
1823
|
+
uri: string;
|
|
1824
|
+
cid: string;
|
|
1825
|
+
record: HypercertCollection;
|
|
1826
|
+
}>>;
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Collaborator operations for SDS access control.
|
|
1830
|
+
*
|
|
1831
|
+
* **SDS Only**: These operations are only available on Shared Data Servers.
|
|
1832
|
+
*
|
|
1833
|
+
* @example
|
|
1834
|
+
* ```typescript
|
|
1835
|
+
* // Grant access
|
|
1836
|
+
* await repo.collaborators.grant({
|
|
1837
|
+
* userDid: "did:plc:new-user",
|
|
1838
|
+
* role: "editor",
|
|
1839
|
+
* });
|
|
1840
|
+
*
|
|
1841
|
+
* // List collaborators
|
|
1842
|
+
* const collaborators = await repo.collaborators.list();
|
|
1843
|
+
*
|
|
1844
|
+
* // Check access
|
|
1845
|
+
* const hasAccess = await repo.collaborators.hasAccess("did:plc:someone");
|
|
1846
|
+
* const role = await repo.collaborators.getRole("did:plc:someone");
|
|
1847
|
+
*
|
|
1848
|
+
* // Revoke access
|
|
1849
|
+
* await repo.collaborators.revoke({ userDid: "did:plc:former-user" });
|
|
1850
|
+
* ```
|
|
1851
|
+
*/
|
|
1852
|
+
interface CollaboratorOperations {
|
|
1853
|
+
/**
|
|
1854
|
+
* Grants repository access to a user.
|
|
1855
|
+
*
|
|
1856
|
+
* @param params - Grant parameters
|
|
1857
|
+
* @param params.userDid - DID of the user to grant access to
|
|
1858
|
+
* @param params.role - Role to assign
|
|
1859
|
+
*/
|
|
1860
|
+
grant(params: {
|
|
1861
|
+
userDid: string;
|
|
1862
|
+
role: RepositoryRole;
|
|
1863
|
+
}): Promise<void>;
|
|
1864
|
+
/**
|
|
1865
|
+
* Revokes repository access from a user.
|
|
1866
|
+
*
|
|
1867
|
+
* @param params - Revoke parameters
|
|
1868
|
+
* @param params.userDid - DID of the user to revoke access from
|
|
1869
|
+
*/
|
|
1870
|
+
revoke(params: {
|
|
1871
|
+
userDid: string;
|
|
1872
|
+
}): Promise<void>;
|
|
1873
|
+
/**
|
|
1874
|
+
* Lists all collaborators on the repository.
|
|
1875
|
+
*
|
|
1876
|
+
* @returns Promise resolving to array of access grants
|
|
1877
|
+
*/
|
|
1878
|
+
list(): Promise<RepositoryAccessGrant[]>;
|
|
1879
|
+
/**
|
|
1880
|
+
* Checks if a user has any access to the repository.
|
|
1881
|
+
*
|
|
1882
|
+
* @param userDid - DID to check
|
|
1883
|
+
* @returns Promise resolving to boolean
|
|
1884
|
+
*/
|
|
1885
|
+
hasAccess(userDid: string): Promise<boolean>;
|
|
1886
|
+
/**
|
|
1887
|
+
* Gets the role assigned to a user.
|
|
1888
|
+
*
|
|
1889
|
+
* @param userDid - DID to check
|
|
1890
|
+
* @returns Promise resolving to role, or `null` if no access
|
|
1891
|
+
*/
|
|
1892
|
+
getRole(userDid: string): Promise<RepositoryRole | null>;
|
|
1893
|
+
}
|
|
1894
|
+
/**
|
|
1895
|
+
* Organization operations for SDS organization management.
|
|
1896
|
+
*
|
|
1897
|
+
* **SDS Only**: These operations are only available on Shared Data Servers.
|
|
1898
|
+
*
|
|
1899
|
+
* @example
|
|
1900
|
+
* ```typescript
|
|
1901
|
+
* // Create an organization
|
|
1902
|
+
* const org = await repo.organizations.create({
|
|
1903
|
+
* name: "My Team",
|
|
1904
|
+
* description: "A team for impact projects",
|
|
1905
|
+
* });
|
|
1906
|
+
*
|
|
1907
|
+
* // List organizations
|
|
1908
|
+
* const orgs = await repo.organizations.list();
|
|
1909
|
+
*
|
|
1910
|
+
* // Get organization details
|
|
1911
|
+
* const orgInfo = await repo.organizations.get(org.did);
|
|
1912
|
+
* ```
|
|
1913
|
+
*/
|
|
1914
|
+
interface OrganizationOperations {
|
|
1915
|
+
/**
|
|
1916
|
+
* Creates a new organization.
|
|
1917
|
+
*
|
|
1918
|
+
* @param params - Organization parameters
|
|
1919
|
+
* @param params.name - Organization name
|
|
1920
|
+
* @param params.description - Optional description
|
|
1921
|
+
* @param params.handle - Optional custom handle
|
|
1922
|
+
* @returns Promise resolving to organization info
|
|
1923
|
+
*/
|
|
1924
|
+
create(params: {
|
|
1925
|
+
name: string;
|
|
1926
|
+
description?: string;
|
|
1927
|
+
handle?: string;
|
|
1928
|
+
}): Promise<OrganizationInfo>;
|
|
1929
|
+
/**
|
|
1930
|
+
* Gets an organization by DID.
|
|
1931
|
+
*
|
|
1932
|
+
* @param did - Organization DID
|
|
1933
|
+
* @returns Promise resolving to organization info, or `null` if not found
|
|
1934
|
+
*/
|
|
1935
|
+
get(did: string): Promise<OrganizationInfo | null>;
|
|
1936
|
+
/**
|
|
1937
|
+
* Lists organizations the current user has access to.
|
|
1938
|
+
*
|
|
1939
|
+
* @returns Promise resolving to array of organization info
|
|
1940
|
+
*/
|
|
1941
|
+
list(): Promise<OrganizationInfo[]>;
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
/**
|
|
1945
|
+
* Repository - Unified fluent API for ATProto repository operations.
|
|
1946
|
+
*
|
|
1947
|
+
* This module provides the main interface for interacting with AT Protocol
|
|
1948
|
+
* data servers (PDS and SDS) through a consistent, fluent API.
|
|
1949
|
+
*
|
|
1950
|
+
* @packageDocumentation
|
|
1951
|
+
*/
|
|
1952
|
+
|
|
1953
|
+
/**
|
|
1954
|
+
* Repository provides a fluent API for AT Protocol data operations.
|
|
1955
|
+
*
|
|
1956
|
+
* This class is the primary interface for working with data in the AT Protocol
|
|
1957
|
+
* ecosystem. It provides organized access to:
|
|
1958
|
+
*
|
|
1959
|
+
* - **Records**: Low-level CRUD operations for any AT Protocol record type
|
|
1960
|
+
* - **Blobs**: Binary data upload and retrieval (images, files)
|
|
1961
|
+
* - **Profile**: User profile management
|
|
1962
|
+
* - **Hypercerts**: High-level hypercert creation and management
|
|
1963
|
+
* - **Collaborators**: Access control for shared repositories (SDS only)
|
|
1964
|
+
* - **Organizations**: Organization management (SDS only)
|
|
1965
|
+
*
|
|
1966
|
+
* @remarks
|
|
1967
|
+
* The Repository uses lazy initialization for operation handlers - they are
|
|
1968
|
+
* created only when first accessed. This improves performance when you only
|
|
1969
|
+
* need a subset of operations.
|
|
1970
|
+
*
|
|
1971
|
+
* **PDS vs SDS:**
|
|
1972
|
+
* - **PDS (Personal Data Server)**: User's own data storage. All operations
|
|
1973
|
+
* except collaborators and organizations are available.
|
|
1974
|
+
* - **SDS (Shared Data Server)**: Collaborative data storage with access
|
|
1975
|
+
* control. All operations including collaborators and organizations.
|
|
1976
|
+
*
|
|
1977
|
+
* @example Basic usage
|
|
1978
|
+
* ```typescript
|
|
1979
|
+
* // Get a repository from the SDK
|
|
1980
|
+
* const repo = sdk.repository(session);
|
|
1981
|
+
*
|
|
1982
|
+
* // Access user profile
|
|
1983
|
+
* const profile = await repo.profile.get();
|
|
1984
|
+
*
|
|
1985
|
+
* // Create a hypercert
|
|
1986
|
+
* const result = await repo.hypercerts.create({
|
|
1987
|
+
* title: "My Impact",
|
|
1988
|
+
* description: "Description of the impact",
|
|
1989
|
+
* workScope: "Climate Action",
|
|
1990
|
+
* workTimeframeFrom: "2024-01-01",
|
|
1991
|
+
* workTimeframeTo: "2024-12-31",
|
|
1992
|
+
* rights: {
|
|
1993
|
+
* name: "Attribution",
|
|
1994
|
+
* type: "license",
|
|
1995
|
+
* description: "CC-BY-4.0",
|
|
1996
|
+
* },
|
|
1997
|
+
* });
|
|
1998
|
+
* ```
|
|
1999
|
+
*
|
|
2000
|
+
* @example Working with a different user's repository
|
|
2001
|
+
* ```typescript
|
|
2002
|
+
* // Get the current user's repo
|
|
2003
|
+
* const myRepo = sdk.repository(session);
|
|
2004
|
+
*
|
|
2005
|
+
* // Get another user's repo (read-only for most operations)
|
|
2006
|
+
* const otherRepo = myRepo.repo("did:plc:other-user-did");
|
|
2007
|
+
* const theirProfile = await otherRepo.profile.get();
|
|
2008
|
+
* ```
|
|
2009
|
+
*
|
|
2010
|
+
* @example SDS operations
|
|
2011
|
+
* ```typescript
|
|
2012
|
+
* // Get SDS repository for collaborator features
|
|
2013
|
+
* const sdsRepo = sdk.repository(session, { server: "sds" });
|
|
2014
|
+
*
|
|
2015
|
+
* // Manage collaborators
|
|
2016
|
+
* await sdsRepo.collaborators.grant({
|
|
2017
|
+
* userDid: "did:plc:collaborator",
|
|
2018
|
+
* role: "editor",
|
|
2019
|
+
* });
|
|
2020
|
+
*
|
|
2021
|
+
* // List organizations
|
|
2022
|
+
* const orgs = await sdsRepo.organizations.list();
|
|
2023
|
+
* ```
|
|
2024
|
+
*
|
|
2025
|
+
* @see {@link ATProtoSDK.repository} for creating Repository instances
|
|
2026
|
+
*/
|
|
2027
|
+
declare class Repository {
|
|
2028
|
+
private session;
|
|
2029
|
+
private serverUrl;
|
|
2030
|
+
private repoDid;
|
|
2031
|
+
private lexiconRegistry;
|
|
2032
|
+
private logger?;
|
|
2033
|
+
private agent;
|
|
2034
|
+
private _isSDS;
|
|
2035
|
+
private _records?;
|
|
2036
|
+
private _blobs?;
|
|
2037
|
+
private _profile?;
|
|
2038
|
+
private _hypercerts?;
|
|
2039
|
+
private _collaborators?;
|
|
2040
|
+
private _organizations?;
|
|
2041
|
+
/**
|
|
2042
|
+
* Creates a new Repository instance.
|
|
2043
|
+
*
|
|
2044
|
+
* @param session - Authenticated OAuth session
|
|
2045
|
+
* @param serverUrl - Base URL of the AT Protocol server
|
|
2046
|
+
* @param repoDid - DID of the repository to operate on
|
|
2047
|
+
* @param lexiconRegistry - Registry for lexicon validation
|
|
2048
|
+
* @param isSDS - Whether this is a Shared Data Server
|
|
2049
|
+
* @param logger - Optional logger for debugging
|
|
2050
|
+
*
|
|
2051
|
+
* @remarks
|
|
2052
|
+
* This constructor is typically not called directly. Use
|
|
2053
|
+
* {@link ATProtoSDK.repository} to create Repository instances.
|
|
2054
|
+
*
|
|
2055
|
+
* @internal
|
|
2056
|
+
*/
|
|
2057
|
+
constructor(session: Session, serverUrl: string, repoDid: string, lexiconRegistry: LexiconRegistry, isSDS: boolean, logger?: LoggerInterface);
|
|
2058
|
+
/**
|
|
2059
|
+
* The DID (Decentralized Identifier) of this repository.
|
|
2060
|
+
*
|
|
2061
|
+
* This is the user or organization that owns the repository data.
|
|
2062
|
+
*
|
|
2063
|
+
* @example
|
|
2064
|
+
* ```typescript
|
|
2065
|
+
* console.log(`Working with repo: ${repo.did}`);
|
|
2066
|
+
* // Output: Working with repo: did:plc:abc123xyz...
|
|
2067
|
+
* ```
|
|
2068
|
+
*/
|
|
2069
|
+
get did(): string;
|
|
2070
|
+
/**
|
|
2071
|
+
* Whether this repository is on a Shared Data Server (SDS).
|
|
2072
|
+
*
|
|
2073
|
+
* SDS servers support additional features like collaborators and
|
|
2074
|
+
* organizations. Attempting to use these features on a PDS will
|
|
2075
|
+
* throw {@link SDSRequiredError}.
|
|
2076
|
+
*
|
|
2077
|
+
* @example
|
|
2078
|
+
* ```typescript
|
|
2079
|
+
* if (repo.isSDS) {
|
|
2080
|
+
* const collaborators = await repo.collaborators.list();
|
|
2081
|
+
* }
|
|
2082
|
+
* ```
|
|
2083
|
+
*/
|
|
2084
|
+
get isSDS(): boolean;
|
|
2085
|
+
/**
|
|
2086
|
+
* Gets the server URL this repository connects to.
|
|
2087
|
+
*
|
|
2088
|
+
* @returns The base URL of the AT Protocol server
|
|
2089
|
+
*
|
|
2090
|
+
* @example
|
|
2091
|
+
* ```typescript
|
|
2092
|
+
* console.log(repo.getServerUrl());
|
|
2093
|
+
* // Output: https://bsky.social or https://sds.hypercerts.org
|
|
2094
|
+
* ```
|
|
2095
|
+
*/
|
|
2096
|
+
getServerUrl(): string;
|
|
2097
|
+
/**
|
|
2098
|
+
* Creates a Repository instance for a different DID on the same server.
|
|
2099
|
+
*
|
|
2100
|
+
* This allows you to read data from other users' repositories while
|
|
2101
|
+
* maintaining your authenticated session.
|
|
2102
|
+
*
|
|
2103
|
+
* @param did - The DID of the repository to access
|
|
2104
|
+
* @returns A new Repository instance for the specified DID
|
|
2105
|
+
*
|
|
2106
|
+
* @remarks
|
|
2107
|
+
* Write operations on another user's repository will typically fail
|
|
2108
|
+
* unless you have been granted collaborator access (SDS only).
|
|
2109
|
+
*
|
|
2110
|
+
* @example
|
|
2111
|
+
* ```typescript
|
|
2112
|
+
* // Read another user's profile
|
|
2113
|
+
* const otherRepo = repo.repo("did:plc:other-user");
|
|
2114
|
+
* const profile = await otherRepo.profile.get();
|
|
2115
|
+
*
|
|
2116
|
+
* // List their public hypercerts
|
|
2117
|
+
* const hypercerts = await otherRepo.hypercerts.list();
|
|
2118
|
+
* ```
|
|
2119
|
+
*/
|
|
2120
|
+
repo(did: string): Repository;
|
|
2121
|
+
/**
|
|
2122
|
+
* Low-level record operations for CRUD on any AT Protocol record type.
|
|
2123
|
+
*
|
|
2124
|
+
* Use this for direct access to AT Protocol records when the high-level
|
|
2125
|
+
* APIs don't meet your needs.
|
|
2126
|
+
*
|
|
2127
|
+
* @returns {@link RecordOperations} interface for record CRUD
|
|
2128
|
+
*
|
|
2129
|
+
* @example
|
|
2130
|
+
* ```typescript
|
|
2131
|
+
* // Create a custom record
|
|
2132
|
+
* const result = await repo.records.create({
|
|
2133
|
+
* collection: "org.example.myRecord",
|
|
2134
|
+
* record: { foo: "bar" },
|
|
2135
|
+
* });
|
|
2136
|
+
*
|
|
2137
|
+
* // List records in a collection
|
|
2138
|
+
* const list = await repo.records.list({
|
|
2139
|
+
* collection: "org.example.myRecord",
|
|
2140
|
+
* limit: 50,
|
|
2141
|
+
* });
|
|
2142
|
+
*
|
|
2143
|
+
* // Get a specific record
|
|
2144
|
+
* const record = await repo.records.get({
|
|
2145
|
+
* collection: "org.example.myRecord",
|
|
2146
|
+
* rkey: "abc123",
|
|
2147
|
+
* });
|
|
2148
|
+
* ```
|
|
2149
|
+
*/
|
|
2150
|
+
get records(): RecordOperations;
|
|
2151
|
+
/**
|
|
2152
|
+
* Blob operations for uploading and retrieving binary data.
|
|
2153
|
+
*
|
|
2154
|
+
* Blobs are used for images, files, and other binary content associated
|
|
2155
|
+
* with records.
|
|
2156
|
+
*
|
|
2157
|
+
* @returns {@link BlobOperations} interface for blob management
|
|
2158
|
+
*
|
|
2159
|
+
* @example
|
|
2160
|
+
* ```typescript
|
|
2161
|
+
* // Upload an image
|
|
2162
|
+
* const imageBlob = new Blob([imageData], { type: "image/png" });
|
|
2163
|
+
* const uploadResult = await repo.blobs.upload(imageBlob);
|
|
2164
|
+
*
|
|
2165
|
+
* // The ref can be used in records
|
|
2166
|
+
* console.log(uploadResult.ref.$link); // CID of the blob
|
|
2167
|
+
*
|
|
2168
|
+
* // Retrieve a blob by CID
|
|
2169
|
+
* const { data, mimeType } = await repo.blobs.get(cid);
|
|
2170
|
+
* ```
|
|
2171
|
+
*/
|
|
2172
|
+
get blobs(): BlobOperations;
|
|
2173
|
+
/**
|
|
2174
|
+
* Profile operations for managing user profiles.
|
|
2175
|
+
*
|
|
2176
|
+
* @returns {@link ProfileOperations} interface for profile management
|
|
2177
|
+
*
|
|
2178
|
+
* @example
|
|
2179
|
+
* ```typescript
|
|
2180
|
+
* // Get current profile
|
|
2181
|
+
* const profile = await repo.profile.get();
|
|
2182
|
+
* console.log(profile.displayName);
|
|
2183
|
+
*
|
|
2184
|
+
* // Update profile
|
|
2185
|
+
* await repo.profile.update({
|
|
2186
|
+
* displayName: "New Name",
|
|
2187
|
+
* description: "Updated bio",
|
|
2188
|
+
* avatar: avatarBlob, // Optional: update avatar image
|
|
2189
|
+
* });
|
|
2190
|
+
* ```
|
|
2191
|
+
*/
|
|
2192
|
+
get profile(): ProfileOperations;
|
|
2193
|
+
/**
|
|
2194
|
+
* High-level hypercert operations.
|
|
2195
|
+
*
|
|
2196
|
+
* Provides a convenient API for creating and managing hypercerts,
|
|
2197
|
+
* including related records like locations, contributions, and evidence.
|
|
2198
|
+
*
|
|
2199
|
+
* @returns {@link HypercertOperations} interface with EventEmitter capabilities
|
|
2200
|
+
*
|
|
2201
|
+
* @example Creating a hypercert
|
|
2202
|
+
* ```typescript
|
|
2203
|
+
* const result = await repo.hypercerts.create({
|
|
2204
|
+
* title: "Climate Action Project",
|
|
2205
|
+
* description: "Reduced carbon emissions by 1000 tons",
|
|
2206
|
+
* workScope: "Climate Action",
|
|
2207
|
+
* workTimeframeFrom: "2024-01-01",
|
|
2208
|
+
* workTimeframeTo: "2024-06-30",
|
|
2209
|
+
* rights: {
|
|
2210
|
+
* name: "Public Domain",
|
|
2211
|
+
* type: "license",
|
|
2212
|
+
* description: "CC0 - No Rights Reserved",
|
|
2213
|
+
* },
|
|
2214
|
+
* image: imageBlob, // Optional cover image
|
|
2215
|
+
* location: {
|
|
2216
|
+
* value: "San Francisco, CA",
|
|
2217
|
+
* name: "SF Bay Area",
|
|
2218
|
+
* },
|
|
2219
|
+
* });
|
|
2220
|
+
* console.log(`Created: ${result.hypercertUri}`);
|
|
2221
|
+
* ```
|
|
2222
|
+
*
|
|
2223
|
+
* @example Listening to events
|
|
2224
|
+
* ```typescript
|
|
2225
|
+
* repo.hypercerts.on("recordCreated", ({ uri, cid }) => {
|
|
2226
|
+
* console.log(`Record created: ${uri}`);
|
|
2227
|
+
* });
|
|
2228
|
+
* ```
|
|
2229
|
+
*/
|
|
2230
|
+
get hypercerts(): HypercertOperations;
|
|
2231
|
+
/**
|
|
2232
|
+
* Collaborator operations for managing repository access.
|
|
2233
|
+
*
|
|
2234
|
+
* **SDS Only**: This property throws {@link SDSRequiredError} if accessed
|
|
2235
|
+
* on a PDS repository.
|
|
2236
|
+
*
|
|
2237
|
+
* @returns {@link CollaboratorOperations} interface for access control
|
|
2238
|
+
* @throws {@link SDSRequiredError} if not connected to an SDS server
|
|
2239
|
+
*
|
|
2240
|
+
* @example
|
|
2241
|
+
* ```typescript
|
|
2242
|
+
* // Ensure we're on SDS
|
|
2243
|
+
* const sdsRepo = sdk.repository(session, { server: "sds" });
|
|
2244
|
+
*
|
|
2245
|
+
* // Grant editor access
|
|
2246
|
+
* await sdsRepo.collaborators.grant({
|
|
2247
|
+
* userDid: "did:plc:new-collaborator",
|
|
2248
|
+
* role: "editor",
|
|
2249
|
+
* });
|
|
2250
|
+
*
|
|
2251
|
+
* // List all collaborators
|
|
2252
|
+
* const collaborators = await sdsRepo.collaborators.list();
|
|
2253
|
+
*
|
|
2254
|
+
* // Check access
|
|
2255
|
+
* const hasAccess = await sdsRepo.collaborators.hasAccess("did:plc:someone");
|
|
2256
|
+
*
|
|
2257
|
+
* // Revoke access
|
|
2258
|
+
* await sdsRepo.collaborators.revoke({ userDid: "did:plc:former-collaborator" });
|
|
2259
|
+
* ```
|
|
2260
|
+
*/
|
|
2261
|
+
get collaborators(): CollaboratorOperations;
|
|
2262
|
+
/**
|
|
2263
|
+
* Organization operations for creating and managing organizations.
|
|
2264
|
+
*
|
|
2265
|
+
* **SDS Only**: This property throws {@link SDSRequiredError} if accessed
|
|
2266
|
+
* on a PDS repository.
|
|
2267
|
+
*
|
|
2268
|
+
* @returns {@link OrganizationOperations} interface for organization management
|
|
2269
|
+
* @throws {@link SDSRequiredError} if not connected to an SDS server
|
|
2270
|
+
*
|
|
2271
|
+
* @example
|
|
2272
|
+
* ```typescript
|
|
2273
|
+
* // Ensure we're on SDS
|
|
2274
|
+
* const sdsRepo = sdk.repository(session, { server: "sds" });
|
|
2275
|
+
*
|
|
2276
|
+
* // Create an organization
|
|
2277
|
+
* const org = await sdsRepo.organizations.create({
|
|
2278
|
+
* name: "My Organization",
|
|
2279
|
+
* description: "A team working on impact certificates",
|
|
2280
|
+
* handle: "my-org", // Optional custom handle
|
|
2281
|
+
* });
|
|
2282
|
+
*
|
|
2283
|
+
* // List organizations you have access to
|
|
2284
|
+
* const orgs = await sdsRepo.organizations.list();
|
|
2285
|
+
*
|
|
2286
|
+
* // Get specific organization
|
|
2287
|
+
* const orgInfo = await sdsRepo.organizations.get(org.did);
|
|
2288
|
+
* ```
|
|
2289
|
+
*/
|
|
2290
|
+
get organizations(): OrganizationOperations;
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
/**
|
|
2294
|
+
* Zod schema for OAuth configuration validation.
|
|
2295
|
+
*
|
|
2296
|
+
* @remarks
|
|
2297
|
+
* All URLs must be valid and use HTTPS in production. The `jwkPrivate` field
|
|
2298
|
+
* should contain the private key in JWK (JSON Web Key) format as a string.
|
|
2299
|
+
*/
|
|
2300
|
+
declare const OAuthConfigSchema: z.ZodObject<{
|
|
2301
|
+
/**
|
|
2302
|
+
* URL to the OAuth client metadata JSON document.
|
|
2303
|
+
* This document describes your application to the authorization server.
|
|
2304
|
+
*
|
|
2305
|
+
* @see https://atproto.com/specs/oauth#client-metadata
|
|
2306
|
+
*/
|
|
2307
|
+
clientId: z.ZodString;
|
|
2308
|
+
/**
|
|
2309
|
+
* URL where users are redirected after authentication.
|
|
2310
|
+
* Must match one of the redirect URIs in your client metadata.
|
|
2311
|
+
*/
|
|
2312
|
+
redirectUri: z.ZodString;
|
|
2313
|
+
/**
|
|
2314
|
+
* OAuth scopes to request, space-separated.
|
|
2315
|
+
* Common scopes: "atproto", "transition:generic"
|
|
2316
|
+
*/
|
|
2317
|
+
scope: z.ZodString;
|
|
2318
|
+
/**
|
|
2319
|
+
* URL to your public JWKS (JSON Web Key Set) endpoint.
|
|
2320
|
+
* Used by the authorization server to verify your client's signatures.
|
|
2321
|
+
*/
|
|
2322
|
+
jwksUri: z.ZodString;
|
|
2323
|
+
/**
|
|
2324
|
+
* Private JWK (JSON Web Key) as a JSON string.
|
|
2325
|
+
* Used for signing DPoP proofs and client assertions.
|
|
2326
|
+
*
|
|
2327
|
+
* @remarks
|
|
2328
|
+
* This should be kept secret and never exposed to clients.
|
|
2329
|
+
* Typically loaded from environment variables or a secrets manager.
|
|
2330
|
+
*/
|
|
2331
|
+
jwkPrivate: z.ZodString;
|
|
2332
|
+
}, "strip", z.ZodTypeAny, {
|
|
2333
|
+
clientId: string;
|
|
2334
|
+
redirectUri: string;
|
|
2335
|
+
scope: string;
|
|
2336
|
+
jwksUri: string;
|
|
2337
|
+
jwkPrivate: string;
|
|
2338
|
+
}, {
|
|
2339
|
+
clientId: string;
|
|
2340
|
+
redirectUri: string;
|
|
2341
|
+
scope: string;
|
|
2342
|
+
jwksUri: string;
|
|
2343
|
+
jwkPrivate: string;
|
|
2344
|
+
}>;
|
|
2345
|
+
/**
|
|
2346
|
+
* Zod schema for server URL configuration.
|
|
2347
|
+
*
|
|
2348
|
+
* @remarks
|
|
2349
|
+
* At least one server (PDS or SDS) should be configured for the SDK to be useful.
|
|
2350
|
+
*/
|
|
2351
|
+
declare const ServerConfigSchema: z.ZodObject<{
|
|
2352
|
+
/**
|
|
2353
|
+
* Personal Data Server URL - the user's own AT Protocol server.
|
|
2354
|
+
* This is the primary server for user data operations.
|
|
2355
|
+
*
|
|
2356
|
+
* @example "https://bsky.social"
|
|
2357
|
+
*/
|
|
2358
|
+
pds: z.ZodOptional<z.ZodString>;
|
|
2359
|
+
/**
|
|
2360
|
+
* Shared Data Server URL - for collaborative data storage.
|
|
2361
|
+
* Required for collaborator and organization operations.
|
|
2362
|
+
*
|
|
2363
|
+
* @example "https://sds.hypercerts.org"
|
|
2364
|
+
*/
|
|
2365
|
+
sds: z.ZodOptional<z.ZodString>;
|
|
2366
|
+
}, "strip", z.ZodTypeAny, {
|
|
2367
|
+
pds?: string | undefined;
|
|
2368
|
+
sds?: string | undefined;
|
|
2369
|
+
}, {
|
|
2370
|
+
pds?: string | undefined;
|
|
2371
|
+
sds?: string | undefined;
|
|
2372
|
+
}>;
|
|
2373
|
+
/**
|
|
2374
|
+
* Zod schema for timeout configuration.
|
|
2375
|
+
*
|
|
2376
|
+
* @remarks
|
|
2377
|
+
* All timeout values are in milliseconds.
|
|
2378
|
+
*/
|
|
2379
|
+
declare const TimeoutConfigSchema: z.ZodObject<{
|
|
2380
|
+
/**
|
|
2381
|
+
* Timeout for fetching PDS metadata during identity resolution.
|
|
2382
|
+
* @default 5000 (5 seconds, set by OAuthClient)
|
|
2383
|
+
*/
|
|
2384
|
+
pdsMetadata: z.ZodOptional<z.ZodNumber>;
|
|
2385
|
+
/**
|
|
2386
|
+
* Timeout for general API requests to PDS/SDS.
|
|
2387
|
+
* @default 30000 (30 seconds)
|
|
2388
|
+
*/
|
|
2389
|
+
apiRequests: z.ZodOptional<z.ZodNumber>;
|
|
2390
|
+
}, "strip", z.ZodTypeAny, {
|
|
2391
|
+
pdsMetadata?: number | undefined;
|
|
2392
|
+
apiRequests?: number | undefined;
|
|
2393
|
+
}, {
|
|
2394
|
+
pdsMetadata?: number | undefined;
|
|
2395
|
+
apiRequests?: number | undefined;
|
|
2396
|
+
}>;
|
|
2397
|
+
/**
|
|
2398
|
+
* Zod schema for SDK configuration validation.
|
|
2399
|
+
*
|
|
2400
|
+
* @remarks
|
|
2401
|
+
* This schema validates only the primitive/serializable parts of the configuration.
|
|
2402
|
+
* Storage interfaces ({@link SessionStore}, {@link StateStore}) cannot be validated
|
|
2403
|
+
* with Zod as they are runtime objects.
|
|
2404
|
+
*/
|
|
2405
|
+
declare const ATProtoSDKConfigSchema: z.ZodObject<{
|
|
2406
|
+
oauth: z.ZodObject<{
|
|
2407
|
+
/**
|
|
2408
|
+
* URL to the OAuth client metadata JSON document.
|
|
2409
|
+
* This document describes your application to the authorization server.
|
|
2410
|
+
*
|
|
2411
|
+
* @see https://atproto.com/specs/oauth#client-metadata
|
|
2412
|
+
*/
|
|
2413
|
+
clientId: z.ZodString;
|
|
2414
|
+
/**
|
|
2415
|
+
* URL where users are redirected after authentication.
|
|
2416
|
+
* Must match one of the redirect URIs in your client metadata.
|
|
2417
|
+
*/
|
|
2418
|
+
redirectUri: z.ZodString;
|
|
2419
|
+
/**
|
|
2420
|
+
* OAuth scopes to request, space-separated.
|
|
2421
|
+
* Common scopes: "atproto", "transition:generic"
|
|
2422
|
+
*/
|
|
2423
|
+
scope: z.ZodString;
|
|
2424
|
+
/**
|
|
2425
|
+
* URL to your public JWKS (JSON Web Key Set) endpoint.
|
|
2426
|
+
* Used by the authorization server to verify your client's signatures.
|
|
2427
|
+
*/
|
|
2428
|
+
jwksUri: z.ZodString;
|
|
2429
|
+
/**
|
|
2430
|
+
* Private JWK (JSON Web Key) as a JSON string.
|
|
2431
|
+
* Used for signing DPoP proofs and client assertions.
|
|
2432
|
+
*
|
|
2433
|
+
* @remarks
|
|
2434
|
+
* This should be kept secret and never exposed to clients.
|
|
2435
|
+
* Typically loaded from environment variables or a secrets manager.
|
|
2436
|
+
*/
|
|
2437
|
+
jwkPrivate: z.ZodString;
|
|
2438
|
+
}, "strip", z.ZodTypeAny, {
|
|
2439
|
+
clientId: string;
|
|
2440
|
+
redirectUri: string;
|
|
2441
|
+
scope: string;
|
|
2442
|
+
jwksUri: string;
|
|
2443
|
+
jwkPrivate: string;
|
|
2444
|
+
}, {
|
|
2445
|
+
clientId: string;
|
|
2446
|
+
redirectUri: string;
|
|
2447
|
+
scope: string;
|
|
2448
|
+
jwksUri: string;
|
|
2449
|
+
jwkPrivate: string;
|
|
2450
|
+
}>;
|
|
2451
|
+
servers: z.ZodOptional<z.ZodObject<{
|
|
2452
|
+
/**
|
|
2453
|
+
* Personal Data Server URL - the user's own AT Protocol server.
|
|
2454
|
+
* This is the primary server for user data operations.
|
|
2455
|
+
*
|
|
2456
|
+
* @example "https://bsky.social"
|
|
2457
|
+
*/
|
|
2458
|
+
pds: z.ZodOptional<z.ZodString>;
|
|
2459
|
+
/**
|
|
2460
|
+
* Shared Data Server URL - for collaborative data storage.
|
|
2461
|
+
* Required for collaborator and organization operations.
|
|
2462
|
+
*
|
|
2463
|
+
* @example "https://sds.hypercerts.org"
|
|
2464
|
+
*/
|
|
2465
|
+
sds: z.ZodOptional<z.ZodString>;
|
|
2466
|
+
}, "strip", z.ZodTypeAny, {
|
|
2467
|
+
pds?: string | undefined;
|
|
2468
|
+
sds?: string | undefined;
|
|
2469
|
+
}, {
|
|
2470
|
+
pds?: string | undefined;
|
|
2471
|
+
sds?: string | undefined;
|
|
2472
|
+
}>>;
|
|
2473
|
+
timeouts: z.ZodOptional<z.ZodObject<{
|
|
2474
|
+
/**
|
|
2475
|
+
* Timeout for fetching PDS metadata during identity resolution.
|
|
2476
|
+
* @default 5000 (5 seconds, set by OAuthClient)
|
|
2477
|
+
*/
|
|
2478
|
+
pdsMetadata: z.ZodOptional<z.ZodNumber>;
|
|
2479
|
+
/**
|
|
2480
|
+
* Timeout for general API requests to PDS/SDS.
|
|
2481
|
+
* @default 30000 (30 seconds)
|
|
2482
|
+
*/
|
|
2483
|
+
apiRequests: z.ZodOptional<z.ZodNumber>;
|
|
2484
|
+
}, "strip", z.ZodTypeAny, {
|
|
2485
|
+
pdsMetadata?: number | undefined;
|
|
2486
|
+
apiRequests?: number | undefined;
|
|
2487
|
+
}, {
|
|
2488
|
+
pdsMetadata?: number | undefined;
|
|
2489
|
+
apiRequests?: number | undefined;
|
|
2490
|
+
}>>;
|
|
2491
|
+
}, "strip", z.ZodTypeAny, {
|
|
2492
|
+
oauth: {
|
|
2493
|
+
clientId: string;
|
|
2494
|
+
redirectUri: string;
|
|
2495
|
+
scope: string;
|
|
2496
|
+
jwksUri: string;
|
|
2497
|
+
jwkPrivate: string;
|
|
2498
|
+
};
|
|
2499
|
+
servers?: {
|
|
2500
|
+
pds?: string | undefined;
|
|
2501
|
+
sds?: string | undefined;
|
|
2502
|
+
} | undefined;
|
|
2503
|
+
timeouts?: {
|
|
2504
|
+
pdsMetadata?: number | undefined;
|
|
2505
|
+
apiRequests?: number | undefined;
|
|
2506
|
+
} | undefined;
|
|
2507
|
+
}, {
|
|
2508
|
+
oauth: {
|
|
2509
|
+
clientId: string;
|
|
2510
|
+
redirectUri: string;
|
|
2511
|
+
scope: string;
|
|
2512
|
+
jwksUri: string;
|
|
2513
|
+
jwkPrivate: string;
|
|
2514
|
+
};
|
|
2515
|
+
servers?: {
|
|
2516
|
+
pds?: string | undefined;
|
|
2517
|
+
sds?: string | undefined;
|
|
2518
|
+
} | undefined;
|
|
2519
|
+
timeouts?: {
|
|
2520
|
+
pdsMetadata?: number | undefined;
|
|
2521
|
+
apiRequests?: number | undefined;
|
|
2522
|
+
} | undefined;
|
|
2523
|
+
}>;
|
|
2524
|
+
/**
|
|
2525
|
+
* Configuration options for the ATProto SDK.
|
|
2526
|
+
*
|
|
2527
|
+
* This interface defines all configuration needed to initialize the SDK,
|
|
2528
|
+
* including OAuth credentials, server endpoints, and optional customizations.
|
|
2529
|
+
*
|
|
2530
|
+
* @example Minimal configuration
|
|
2531
|
+
* ```typescript
|
|
2532
|
+
* const config: ATProtoSDKConfig = {
|
|
2533
|
+
* oauth: {
|
|
2534
|
+
* clientId: "https://my-app.com/client-metadata.json",
|
|
2535
|
+
* redirectUri: "https://my-app.com/callback",
|
|
2536
|
+
* scope: "atproto transition:generic",
|
|
2537
|
+
* jwksUri: "https://my-app.com/.well-known/jwks.json",
|
|
2538
|
+
* jwkPrivate: process.env.JWK_PRIVATE_KEY!,
|
|
2539
|
+
* },
|
|
2540
|
+
* servers: {
|
|
2541
|
+
* pds: "https://bsky.social",
|
|
2542
|
+
* },
|
|
2543
|
+
* };
|
|
2544
|
+
* ```
|
|
2545
|
+
*
|
|
2546
|
+
* @example Full configuration with custom storage
|
|
2547
|
+
* ```typescript
|
|
2548
|
+
* const config: ATProtoSDKConfig = {
|
|
2549
|
+
* oauth: { ... },
|
|
2550
|
+
* servers: {
|
|
2551
|
+
* pds: "https://bsky.social",
|
|
2552
|
+
* sds: "https://sds.hypercerts.org",
|
|
2553
|
+
* },
|
|
2554
|
+
* storage: {
|
|
2555
|
+
* sessionStore: new RedisSessionStore(redisClient),
|
|
2556
|
+
* stateStore: new RedisStateStore(redisClient),
|
|
2557
|
+
* },
|
|
2558
|
+
* timeouts: {
|
|
2559
|
+
* pdsMetadata: 5000,
|
|
2560
|
+
* apiRequests: 30000,
|
|
2561
|
+
* },
|
|
2562
|
+
* logger: console,
|
|
2563
|
+
* };
|
|
2564
|
+
* ```
|
|
2565
|
+
*/
|
|
2566
|
+
interface ATProtoSDKConfig {
|
|
2567
|
+
/**
|
|
2568
|
+
* OAuth 2.0 configuration for authentication.
|
|
2569
|
+
*
|
|
2570
|
+
* Required fields for the OAuth flow with DPoP (Demonstrating Proof of Possession).
|
|
2571
|
+
* Your application must host the client metadata and JWKS endpoints.
|
|
2572
|
+
*
|
|
2573
|
+
* @see https://atproto.com/specs/oauth for AT Protocol OAuth specification
|
|
2574
|
+
*/
|
|
2575
|
+
oauth: z.infer<typeof OAuthConfigSchema>;
|
|
2576
|
+
/**
|
|
2577
|
+
* Server URLs for PDS and SDS connections.
|
|
2578
|
+
*
|
|
2579
|
+
* - **PDS**: Personal Data Server - user's own data storage
|
|
2580
|
+
* - **SDS**: Shared Data Server - collaborative storage with access control
|
|
2581
|
+
*/
|
|
2582
|
+
servers?: z.infer<typeof ServerConfigSchema>;
|
|
2583
|
+
/**
|
|
2584
|
+
* Storage adapters for persisting OAuth sessions and state.
|
|
2585
|
+
*
|
|
2586
|
+
* If not provided, in-memory implementations are used automatically.
|
|
2587
|
+
* **Warning**: In-memory storage is lost on process restart - use persistent
|
|
2588
|
+
* storage (Redis, database, etc.) in production.
|
|
2589
|
+
*
|
|
2590
|
+
* @example
|
|
2591
|
+
* ```typescript
|
|
2592
|
+
* storage: {
|
|
2593
|
+
* sessionStore: new RedisSessionStore(redis),
|
|
2594
|
+
* stateStore: new RedisStateStore(redis),
|
|
2595
|
+
* }
|
|
2596
|
+
* ```
|
|
2597
|
+
*/
|
|
2598
|
+
storage?: {
|
|
2599
|
+
/**
|
|
2600
|
+
* Persistent storage for OAuth sessions.
|
|
2601
|
+
* Sessions contain access tokens, refresh tokens, and DPoP keys.
|
|
2602
|
+
*/
|
|
2603
|
+
sessionStore?: SessionStore;
|
|
2604
|
+
/**
|
|
2605
|
+
* Temporary storage for OAuth state during the authorization flow.
|
|
2606
|
+
* State is short-lived and used for PKCE and CSRF protection.
|
|
2607
|
+
*/
|
|
2608
|
+
stateStore?: StateStore;
|
|
2609
|
+
};
|
|
2610
|
+
/**
|
|
2611
|
+
* Custom fetch implementation for HTTP requests.
|
|
2612
|
+
*
|
|
2613
|
+
* Use this to add custom headers, logging, or to use a different HTTP client.
|
|
2614
|
+
* Must be compatible with the standard Fetch API.
|
|
2615
|
+
*
|
|
2616
|
+
* @example
|
|
2617
|
+
* ```typescript
|
|
2618
|
+
* fetch: async (url, init) => {
|
|
2619
|
+
* console.log(`Fetching: ${url}`);
|
|
2620
|
+
* return globalThis.fetch(url, init);
|
|
2621
|
+
* }
|
|
2622
|
+
* ```
|
|
2623
|
+
*/
|
|
2624
|
+
fetch?: typeof fetch;
|
|
2625
|
+
/**
|
|
2626
|
+
* Timeout configuration for network requests.
|
|
2627
|
+
* Values are in milliseconds.
|
|
2628
|
+
*/
|
|
2629
|
+
timeouts?: z.infer<typeof TimeoutConfigSchema>;
|
|
2630
|
+
/**
|
|
2631
|
+
* Cache for profiles, metadata, and other frequently accessed data.
|
|
2632
|
+
*
|
|
2633
|
+
* Implementing caching can significantly reduce API calls and improve performance.
|
|
2634
|
+
* The SDK does not provide a default cache - you must implement {@link CacheInterface}.
|
|
2635
|
+
*/
|
|
2636
|
+
cache?: CacheInterface;
|
|
2637
|
+
/**
|
|
2638
|
+
* Logger for debugging and observability.
|
|
2639
|
+
*
|
|
2640
|
+
* The logger receives debug, info, warn, and error messages from the SDK.
|
|
2641
|
+
* Compatible with `console` or any logger implementing {@link LoggerInterface}.
|
|
2642
|
+
*
|
|
2643
|
+
* @example
|
|
2644
|
+
* ```typescript
|
|
2645
|
+
* logger: console
|
|
2646
|
+
* // or
|
|
2647
|
+
* logger: pino()
|
|
2648
|
+
* // or
|
|
2649
|
+
* logger: winston.createLogger({ ... })
|
|
2650
|
+
* ```
|
|
2651
|
+
*/
|
|
2652
|
+
logger?: LoggerInterface;
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
/**
|
|
2656
|
+
* Options for the OAuth authorization flow.
|
|
2657
|
+
*/
|
|
2658
|
+
interface AuthorizeOptions {
|
|
2659
|
+
/**
|
|
2660
|
+
* OAuth scope string to request specific permissions.
|
|
2661
|
+
* Overrides the default scope configured in {@link ATProtoSDKConfig.oauth.scope}.
|
|
2662
|
+
*
|
|
2663
|
+
* @example
|
|
2664
|
+
* ```typescript
|
|
2665
|
+
* // Request read-only access
|
|
2666
|
+
* await sdk.authorize("user.bsky.social", { scope: "atproto" });
|
|
2667
|
+
*
|
|
2668
|
+
* // Request full access (default typically includes transition:generic)
|
|
2669
|
+
* await sdk.authorize("user.bsky.social", { scope: "atproto transition:generic" });
|
|
2670
|
+
* ```
|
|
2671
|
+
*/
|
|
2672
|
+
scope?: string;
|
|
2673
|
+
}
|
|
2674
|
+
/**
|
|
2675
|
+
* Main ATProto SDK class providing OAuth authentication and repository access.
|
|
2676
|
+
*
|
|
2677
|
+
* This is the primary entry point for interacting with AT Protocol servers.
|
|
2678
|
+
* It handles the OAuth 2.0 flow with DPoP (Demonstrating Proof of Possession)
|
|
2679
|
+
* and provides access to repository operations for managing records, blobs,
|
|
2680
|
+
* and profiles.
|
|
2681
|
+
*
|
|
2682
|
+
* @example Basic usage with OAuth flow
|
|
2683
|
+
* ```typescript
|
|
2684
|
+
* import { ATProtoSDK, InMemorySessionStore, InMemoryStateStore } from "@hypercerts-org/sdk";
|
|
2685
|
+
*
|
|
2686
|
+
* const sdk = new ATProtoSDK({
|
|
2687
|
+
* oauth: {
|
|
2688
|
+
* clientId: "https://my-app.com/client-metadata.json",
|
|
2689
|
+
* redirectUri: "https://my-app.com/callback",
|
|
2690
|
+
* scope: "atproto transition:generic",
|
|
2691
|
+
* jwksUri: "https://my-app.com/.well-known/jwks.json",
|
|
2692
|
+
* jwkPrivate: process.env.JWK_PRIVATE_KEY!,
|
|
2693
|
+
* },
|
|
2694
|
+
* servers: {
|
|
2695
|
+
* pds: "https://bsky.social",
|
|
2696
|
+
* sds: "https://sds.hypercerts.org",
|
|
2697
|
+
* },
|
|
2698
|
+
* });
|
|
2699
|
+
*
|
|
2700
|
+
* // Start OAuth flow - redirect user to this URL
|
|
2701
|
+
* const authUrl = await sdk.authorize("user.bsky.social");
|
|
2702
|
+
*
|
|
2703
|
+
* // After user returns, handle the callback
|
|
2704
|
+
* const session = await sdk.callback(new URLSearchParams(window.location.search));
|
|
2705
|
+
*
|
|
2706
|
+
* // Get a repository to work with data
|
|
2707
|
+
* const repo = sdk.repository(session);
|
|
2708
|
+
* ```
|
|
2709
|
+
*
|
|
2710
|
+
* @example Restoring an existing session
|
|
2711
|
+
* ```typescript
|
|
2712
|
+
* // Restore a previous session by DID
|
|
2713
|
+
* const session = await sdk.restoreSession("did:plc:abc123...");
|
|
2714
|
+
* if (session) {
|
|
2715
|
+
* const repo = sdk.repository(session);
|
|
2716
|
+
* // Continue working with the restored session
|
|
2717
|
+
* }
|
|
2718
|
+
* ```
|
|
2719
|
+
*
|
|
2720
|
+
* @see {@link ATProtoSDKConfig} for configuration options
|
|
2721
|
+
* @see {@link Repository} for data operations
|
|
2722
|
+
* @see {@link OAuthClient} for OAuth implementation details
|
|
2723
|
+
*/
|
|
2724
|
+
declare class ATProtoSDK {
|
|
2725
|
+
private oauthClient;
|
|
2726
|
+
private config;
|
|
2727
|
+
private logger?;
|
|
2728
|
+
private lexiconRegistry;
|
|
2729
|
+
/**
|
|
2730
|
+
* Creates a new ATProto SDK instance.
|
|
2731
|
+
*
|
|
2732
|
+
* @param config - SDK configuration including OAuth credentials, server URLs, and optional storage adapters
|
|
2733
|
+
* @throws {@link ValidationError} if the configuration is invalid (e.g., malformed URLs, missing required fields)
|
|
2734
|
+
*
|
|
2735
|
+
* @remarks
|
|
2736
|
+
* If no storage adapters are provided, in-memory implementations are used.
|
|
2737
|
+
* These are suitable for development and testing but **not recommended for production**
|
|
2738
|
+
* as sessions will be lost on restart.
|
|
2739
|
+
*
|
|
2740
|
+
* @example
|
|
2741
|
+
* ```typescript
|
|
2742
|
+
* // Minimal configuration (uses in-memory storage)
|
|
2743
|
+
* const sdk = new ATProtoSDK({
|
|
2744
|
+
* oauth: {
|
|
2745
|
+
* clientId: "https://my-app.com/client-metadata.json",
|
|
2746
|
+
* redirectUri: "https://my-app.com/callback",
|
|
2747
|
+
* scope: "atproto",
|
|
2748
|
+
* jwksUri: "https://my-app.com/.well-known/jwks.json",
|
|
2749
|
+
* jwkPrivate: privateKeyJwk,
|
|
2750
|
+
* },
|
|
2751
|
+
* servers: { pds: "https://bsky.social" },
|
|
2752
|
+
* });
|
|
2753
|
+
* ```
|
|
2754
|
+
*/
|
|
2755
|
+
constructor(config: ATProtoSDKConfig);
|
|
2756
|
+
/**
|
|
2757
|
+
* Initiates the OAuth authorization flow.
|
|
2758
|
+
*
|
|
2759
|
+
* This method starts the OAuth 2.0 authorization flow by resolving the user's
|
|
2760
|
+
* identity and generating an authorization URL. The user should be redirected
|
|
2761
|
+
* to this URL to authenticate.
|
|
2762
|
+
*
|
|
2763
|
+
* @param identifier - The user's ATProto identifier. Can be:
|
|
2764
|
+
* - A handle (e.g., `"user.bsky.social"`)
|
|
2765
|
+
* - A DID (e.g., `"did:plc:abc123..."`)
|
|
2766
|
+
* - A PDS URL (e.g., `"https://bsky.social"`)
|
|
2767
|
+
* @param options - Optional authorization settings
|
|
2768
|
+
* @returns A Promise resolving to the authorization URL to redirect the user to
|
|
2769
|
+
* @throws {@link ValidationError} if the identifier is empty or invalid
|
|
2770
|
+
* @throws {@link NetworkError} if the identity cannot be resolved
|
|
2771
|
+
*
|
|
2772
|
+
* @example
|
|
2773
|
+
* ```typescript
|
|
2774
|
+
* // Using a handle
|
|
2775
|
+
* const authUrl = await sdk.authorize("alice.bsky.social");
|
|
2776
|
+
*
|
|
2777
|
+
* // Using a DID directly
|
|
2778
|
+
* const authUrl = await sdk.authorize("did:plc:abc123xyz");
|
|
2779
|
+
*
|
|
2780
|
+
* // With custom scope
|
|
2781
|
+
* const authUrl = await sdk.authorize("alice.bsky.social", {
|
|
2782
|
+
* scope: "atproto transition:generic"
|
|
2783
|
+
* });
|
|
2784
|
+
*
|
|
2785
|
+
* // Redirect user to authUrl
|
|
2786
|
+
* window.location.href = authUrl;
|
|
2787
|
+
* ```
|
|
2788
|
+
*/
|
|
2789
|
+
authorize(identifier: string, options?: AuthorizeOptions): Promise<string>;
|
|
2790
|
+
/**
|
|
2791
|
+
* Handles the OAuth callback and exchanges the authorization code for tokens.
|
|
2792
|
+
*
|
|
2793
|
+
* Call this method when the user is redirected back to your application
|
|
2794
|
+
* after authenticating. It validates the OAuth state, exchanges the
|
|
2795
|
+
* authorization code for access/refresh tokens, and creates a session.
|
|
2796
|
+
*
|
|
2797
|
+
* @param params - URL search parameters from the callback URL
|
|
2798
|
+
* @returns A Promise resolving to the authenticated OAuth session
|
|
2799
|
+
* @throws {@link AuthenticationError} if the callback parameters are invalid or the code exchange fails
|
|
2800
|
+
* @throws {@link ValidationError} if required parameters are missing
|
|
2801
|
+
*
|
|
2802
|
+
* @example
|
|
2803
|
+
* ```typescript
|
|
2804
|
+
* // In your callback route handler
|
|
2805
|
+
* const params = new URLSearchParams(window.location.search);
|
|
2806
|
+
* // params contains: code, state, iss (issuer)
|
|
2807
|
+
*
|
|
2808
|
+
* const session = await sdk.callback(params);
|
|
2809
|
+
* console.log(`Authenticated as ${session.did}`);
|
|
2810
|
+
*
|
|
2811
|
+
* // Store the DID to restore the session later
|
|
2812
|
+
* localStorage.setItem("userDid", session.did);
|
|
2813
|
+
* ```
|
|
2814
|
+
*/
|
|
2815
|
+
callback(params: URLSearchParams): Promise<Session>;
|
|
2816
|
+
/**
|
|
2817
|
+
* Restores an existing OAuth session by DID.
|
|
2818
|
+
*
|
|
2819
|
+
* Use this method to restore a previously authenticated session, typically
|
|
2820
|
+
* on application startup. The method retrieves the stored session and
|
|
2821
|
+
* automatically refreshes expired tokens if needed.
|
|
2822
|
+
*
|
|
2823
|
+
* @param did - The user's Decentralized Identifier (DID), e.g., `"did:plc:abc123..."`
|
|
2824
|
+
* @returns A Promise resolving to the restored session, or `null` if no session exists
|
|
2825
|
+
* @throws {@link ValidationError} if the DID is empty
|
|
2826
|
+
* @throws {@link SessionExpiredError} if the session cannot be refreshed
|
|
2827
|
+
*
|
|
2828
|
+
* @example
|
|
2829
|
+
* ```typescript
|
|
2830
|
+
* // On application startup
|
|
2831
|
+
* const savedDid = localStorage.getItem("userDid");
|
|
2832
|
+
* if (savedDid) {
|
|
2833
|
+
* const session = await sdk.restoreSession(savedDid);
|
|
2834
|
+
* if (session) {
|
|
2835
|
+
* // User is still authenticated
|
|
2836
|
+
* const repo = sdk.repository(session);
|
|
2837
|
+
* } else {
|
|
2838
|
+
* // Session not found, user needs to re-authenticate
|
|
2839
|
+
* const authUrl = await sdk.authorize(savedDid);
|
|
2840
|
+
* }
|
|
2841
|
+
* }
|
|
2842
|
+
* ```
|
|
2843
|
+
*/
|
|
2844
|
+
restoreSession(did: string): Promise<Session | null>;
|
|
2845
|
+
/**
|
|
2846
|
+
* Revokes an OAuth session, logging the user out.
|
|
2847
|
+
*
|
|
2848
|
+
* This method invalidates the session's tokens and removes it from storage.
|
|
2849
|
+
* After revocation, the session can no longer be used or restored.
|
|
2850
|
+
*
|
|
2851
|
+
* @param did - The user's DID to revoke the session for
|
|
2852
|
+
* @throws {@link ValidationError} if the DID is empty
|
|
2853
|
+
*
|
|
2854
|
+
* @example
|
|
2855
|
+
* ```typescript
|
|
2856
|
+
* // Log out the user
|
|
2857
|
+
* await sdk.revokeSession(session.did);
|
|
2858
|
+
* localStorage.removeItem("userDid");
|
|
2859
|
+
* ```
|
|
2860
|
+
*/
|
|
2861
|
+
revokeSession(did: string): Promise<void>;
|
|
2862
|
+
/**
|
|
2863
|
+
* Creates a repository instance for data operations.
|
|
2864
|
+
*
|
|
2865
|
+
* The repository provides a fluent API for working with AT Protocol data
|
|
2866
|
+
* including records, blobs, profiles, and domain-specific operations like
|
|
2867
|
+
* hypercerts and collaborators.
|
|
2868
|
+
*
|
|
2869
|
+
* @param session - An authenticated OAuth session
|
|
2870
|
+
* @param options - Repository configuration options
|
|
2871
|
+
* @returns A {@link Repository} instance configured for the specified server
|
|
2872
|
+
* @throws {@link ValidationError} if the session is invalid or server URL is not configured
|
|
2873
|
+
*
|
|
2874
|
+
* @remarks
|
|
2875
|
+
* - **PDS (Personal Data Server)**: User's own data storage, default for most operations
|
|
2876
|
+
* - **SDS (Shared Data Server)**: Shared data storage with collaborator support
|
|
2877
|
+
*
|
|
2878
|
+
* @example Using default PDS
|
|
2879
|
+
* ```typescript
|
|
2880
|
+
* const repo = sdk.repository(session);
|
|
2881
|
+
* const profile = await repo.profile.get();
|
|
2882
|
+
* ```
|
|
2883
|
+
*
|
|
2884
|
+
* @example Using configured SDS
|
|
2885
|
+
* ```typescript
|
|
2886
|
+
* const sdsRepo = sdk.repository(session, { server: "sds" });
|
|
2887
|
+
* const collaborators = await sdsRepo.collaborators.list();
|
|
2888
|
+
* ```
|
|
2889
|
+
*
|
|
2890
|
+
* @example Using custom server URL
|
|
2891
|
+
* ```typescript
|
|
2892
|
+
* const customRepo = sdk.repository(session, {
|
|
2893
|
+
* serverUrl: "https://custom.atproto.server"
|
|
2894
|
+
* });
|
|
2895
|
+
* ```
|
|
2896
|
+
*/
|
|
2897
|
+
repository(session: Session, options?: RepositoryOptions): Repository;
|
|
2898
|
+
/**
|
|
2899
|
+
* Gets the lexicon registry for schema validation.
|
|
2900
|
+
*
|
|
2901
|
+
* The lexicon registry manages AT Protocol lexicon schemas used for
|
|
2902
|
+
* validating record data. You can register custom lexicons to extend
|
|
2903
|
+
* the SDK's capabilities.
|
|
2904
|
+
*
|
|
2905
|
+
* @returns The {@link LexiconRegistry} instance
|
|
2906
|
+
*
|
|
2907
|
+
* @example
|
|
2908
|
+
* ```typescript
|
|
2909
|
+
* const registry = sdk.getLexiconRegistry();
|
|
2910
|
+
*
|
|
2911
|
+
* // Register custom lexicons
|
|
2912
|
+
* registry.register(myCustomLexicons);
|
|
2913
|
+
*
|
|
2914
|
+
* // Check if a lexicon is registered
|
|
2915
|
+
* const hasLexicon = registry.has("org.example.myRecord");
|
|
2916
|
+
* ```
|
|
2917
|
+
*/
|
|
2918
|
+
getLexiconRegistry(): LexiconRegistry;
|
|
2919
|
+
/**
|
|
2920
|
+
* The configured PDS (Personal Data Server) URL.
|
|
2921
|
+
*
|
|
2922
|
+
* @returns The PDS URL if configured, otherwise `undefined`
|
|
2923
|
+
*/
|
|
2924
|
+
get pdsUrl(): string | undefined;
|
|
2925
|
+
/**
|
|
2926
|
+
* The configured SDS (Shared Data Server) URL.
|
|
2927
|
+
*
|
|
2928
|
+
* @returns The SDS URL if configured, otherwise `undefined`
|
|
2929
|
+
*/
|
|
2930
|
+
get sdsUrl(): string | undefined;
|
|
2931
|
+
}
|
|
2932
|
+
/**
|
|
2933
|
+
* Factory function to create an ATProto SDK instance.
|
|
2934
|
+
*
|
|
2935
|
+
* This is a convenience function equivalent to `new ATProtoSDK(config)`.
|
|
2936
|
+
*
|
|
2937
|
+
* @param config - SDK configuration
|
|
2938
|
+
* @returns A new {@link ATProtoSDK} instance
|
|
2939
|
+
*
|
|
2940
|
+
* @example
|
|
2941
|
+
* ```typescript
|
|
2942
|
+
* import { createATProtoSDK } from "@hypercerts-org/sdk";
|
|
2943
|
+
*
|
|
2944
|
+
* const sdk = createATProtoSDK({
|
|
2945
|
+
* oauth: { ... },
|
|
2946
|
+
* servers: { pds: "https://bsky.social" },
|
|
2947
|
+
* });
|
|
2948
|
+
* ```
|
|
2949
|
+
*/
|
|
2950
|
+
declare function createATProtoSDK(config: ATProtoSDKConfig): ATProtoSDK;
|
|
2951
|
+
|
|
2952
|
+
/**
|
|
2953
|
+
* Base error class for all SDK errors.
|
|
2954
|
+
*
|
|
2955
|
+
* All errors thrown by the Hypercerts SDK extend this class, making it easy
|
|
2956
|
+
* to catch and handle SDK-specific errors.
|
|
2957
|
+
*
|
|
2958
|
+
* @example Catching all SDK errors
|
|
2959
|
+
* ```typescript
|
|
2960
|
+
* try {
|
|
2961
|
+
* await sdk.authorize("user.bsky.social");
|
|
2962
|
+
* } catch (error) {
|
|
2963
|
+
* if (error instanceof ATProtoSDKError) {
|
|
2964
|
+
* console.error(`SDK Error [${error.code}]: ${error.message}`);
|
|
2965
|
+
* console.error(`HTTP Status: ${error.status}`);
|
|
2966
|
+
* }
|
|
2967
|
+
* }
|
|
2968
|
+
* ```
|
|
2969
|
+
*
|
|
2970
|
+
* @example Checking error codes
|
|
2971
|
+
* ```typescript
|
|
2972
|
+
* try {
|
|
2973
|
+
* await repo.records.get(collection, rkey);
|
|
2974
|
+
* } catch (error) {
|
|
2975
|
+
* if (error instanceof ATProtoSDKError) {
|
|
2976
|
+
* switch (error.code) {
|
|
2977
|
+
* case "AUTHENTICATION_ERROR":
|
|
2978
|
+
* // Redirect to login
|
|
2979
|
+
* break;
|
|
2980
|
+
* case "VALIDATION_ERROR":
|
|
2981
|
+
* // Show form errors
|
|
2982
|
+
* break;
|
|
2983
|
+
* case "NETWORK_ERROR":
|
|
2984
|
+
* // Retry or show offline message
|
|
2985
|
+
* break;
|
|
2986
|
+
* }
|
|
2987
|
+
* }
|
|
2988
|
+
* }
|
|
2989
|
+
* ```
|
|
2990
|
+
*/
|
|
2991
|
+
declare class ATProtoSDKError extends Error {
|
|
2992
|
+
code: string;
|
|
2993
|
+
status?: number | undefined;
|
|
2994
|
+
cause?: unknown | undefined;
|
|
2995
|
+
/**
|
|
2996
|
+
* Creates a new SDK error.
|
|
2997
|
+
*
|
|
2998
|
+
* @param message - Human-readable error description
|
|
2999
|
+
* @param code - Machine-readable error code for programmatic handling
|
|
3000
|
+
* @param status - HTTP status code associated with this error type
|
|
3001
|
+
* @param cause - The underlying error that caused this error, if any
|
|
3002
|
+
*/
|
|
3003
|
+
constructor(message: string, code: string, status?: number | undefined, cause?: unknown | undefined);
|
|
3004
|
+
}
|
|
3005
|
+
/**
|
|
3006
|
+
* Error thrown when authentication fails.
|
|
3007
|
+
*
|
|
3008
|
+
* This error indicates problems with the OAuth flow, invalid credentials,
|
|
3009
|
+
* or failed token exchanges. Common causes:
|
|
3010
|
+
* - Invalid authorization code
|
|
3011
|
+
* - Expired or invalid state parameter
|
|
3012
|
+
* - Revoked or invalid tokens
|
|
3013
|
+
* - User denied authorization
|
|
3014
|
+
*
|
|
3015
|
+
* @example
|
|
3016
|
+
* ```typescript
|
|
3017
|
+
* try {
|
|
3018
|
+
* const session = await sdk.callback(params);
|
|
3019
|
+
* } catch (error) {
|
|
3020
|
+
* if (error instanceof AuthenticationError) {
|
|
3021
|
+
* // Clear any stored state and redirect to login
|
|
3022
|
+
* console.error("Authentication failed:", error.message);
|
|
3023
|
+
* }
|
|
3024
|
+
* }
|
|
3025
|
+
* ```
|
|
3026
|
+
*/
|
|
3027
|
+
declare class AuthenticationError extends ATProtoSDKError {
|
|
3028
|
+
/**
|
|
3029
|
+
* Creates an authentication error.
|
|
3030
|
+
*
|
|
3031
|
+
* @param message - Description of what went wrong during authentication
|
|
3032
|
+
* @param cause - The underlying error (e.g., from the OAuth client)
|
|
3033
|
+
*/
|
|
3034
|
+
constructor(message: string, cause?: unknown);
|
|
3035
|
+
}
|
|
3036
|
+
/**
|
|
3037
|
+
* Error thrown when a session has expired and cannot be refreshed.
|
|
3038
|
+
*
|
|
3039
|
+
* This typically occurs when:
|
|
3040
|
+
* - The refresh token has expired (usually after extended inactivity)
|
|
3041
|
+
* - The user has revoked access to your application
|
|
3042
|
+
* - The PDS has invalidated all sessions for the user
|
|
3043
|
+
*
|
|
3044
|
+
* When this error occurs, the user must re-authenticate.
|
|
3045
|
+
*
|
|
3046
|
+
* @example
|
|
3047
|
+
* ```typescript
|
|
3048
|
+
* try {
|
|
3049
|
+
* const session = await sdk.restoreSession(did);
|
|
3050
|
+
* } catch (error) {
|
|
3051
|
+
* if (error instanceof SessionExpiredError) {
|
|
3052
|
+
* // Clear stored session and prompt user to log in again
|
|
3053
|
+
* localStorage.removeItem("userDid");
|
|
3054
|
+
* window.location.href = "/login";
|
|
3055
|
+
* }
|
|
3056
|
+
* }
|
|
3057
|
+
* ```
|
|
3058
|
+
*/
|
|
3059
|
+
declare class SessionExpiredError extends ATProtoSDKError {
|
|
3060
|
+
/**
|
|
3061
|
+
* Creates a session expired error.
|
|
3062
|
+
*
|
|
3063
|
+
* @param message - Description of why the session expired
|
|
3064
|
+
* @param cause - The underlying error from the token refresh attempt
|
|
3065
|
+
*/
|
|
3066
|
+
constructor(message?: string, cause?: unknown);
|
|
3067
|
+
}
|
|
3068
|
+
/**
|
|
3069
|
+
* Error thrown when input validation fails.
|
|
3070
|
+
*
|
|
3071
|
+
* This error indicates that provided data doesn't meet the required format
|
|
3072
|
+
* or constraints. Common causes:
|
|
3073
|
+
* - Missing required fields
|
|
3074
|
+
* - Invalid URL formats
|
|
3075
|
+
* - Invalid DID format
|
|
3076
|
+
* - Schema validation failures for records
|
|
3077
|
+
* - Invalid configuration values
|
|
3078
|
+
*
|
|
3079
|
+
* @example
|
|
3080
|
+
* ```typescript
|
|
3081
|
+
* try {
|
|
3082
|
+
* await sdk.authorize(""); // Empty identifier
|
|
3083
|
+
* } catch (error) {
|
|
3084
|
+
* if (error instanceof ValidationError) {
|
|
3085
|
+
* console.error("Invalid input:", error.message);
|
|
3086
|
+
* // Show validation error to user
|
|
3087
|
+
* }
|
|
3088
|
+
* }
|
|
3089
|
+
* ```
|
|
3090
|
+
*
|
|
3091
|
+
* @example With Zod validation cause
|
|
3092
|
+
* ```typescript
|
|
3093
|
+
* try {
|
|
3094
|
+
* await repo.records.create(collection, record);
|
|
3095
|
+
* } catch (error) {
|
|
3096
|
+
* if (error instanceof ValidationError && error.cause) {
|
|
3097
|
+
* // error.cause may be a ZodError with detailed field errors
|
|
3098
|
+
* const zodError = error.cause as ZodError;
|
|
3099
|
+
* zodError.errors.forEach(e => {
|
|
3100
|
+
* console.error(`Field ${e.path.join(".")}: ${e.message}`);
|
|
3101
|
+
* });
|
|
3102
|
+
* }
|
|
3103
|
+
* }
|
|
3104
|
+
* ```
|
|
3105
|
+
*/
|
|
3106
|
+
declare class ValidationError extends ATProtoSDKError {
|
|
3107
|
+
/**
|
|
3108
|
+
* Creates a validation error.
|
|
3109
|
+
*
|
|
3110
|
+
* @param message - Description of what validation failed
|
|
3111
|
+
* @param cause - The underlying validation error (e.g., ZodError)
|
|
3112
|
+
*/
|
|
3113
|
+
constructor(message: string, cause?: unknown);
|
|
3114
|
+
}
|
|
3115
|
+
/**
|
|
3116
|
+
* Error thrown when a network request fails.
|
|
3117
|
+
*
|
|
3118
|
+
* This error indicates connectivity issues or server unavailability.
|
|
3119
|
+
* Common causes:
|
|
3120
|
+
* - No internet connection
|
|
3121
|
+
* - DNS resolution failure
|
|
3122
|
+
* - Server timeout
|
|
3123
|
+
* - Server returned 5xx error
|
|
3124
|
+
* - TLS/SSL errors
|
|
3125
|
+
*
|
|
3126
|
+
* These errors are typically transient and may succeed on retry.
|
|
3127
|
+
*
|
|
3128
|
+
* @example
|
|
3129
|
+
* ```typescript
|
|
3130
|
+
* try {
|
|
3131
|
+
* await repo.records.list(collection);
|
|
3132
|
+
* } catch (error) {
|
|
3133
|
+
* if (error instanceof NetworkError) {
|
|
3134
|
+
* // Implement retry logic or show offline indicator
|
|
3135
|
+
* console.error("Network error:", error.message);
|
|
3136
|
+
* await retryWithBackoff(() => repo.records.list(collection));
|
|
3137
|
+
* }
|
|
3138
|
+
* }
|
|
3139
|
+
* ```
|
|
3140
|
+
*/
|
|
3141
|
+
declare class NetworkError extends ATProtoSDKError {
|
|
3142
|
+
/**
|
|
3143
|
+
* Creates a network error.
|
|
3144
|
+
*
|
|
3145
|
+
* @param message - Description of the network failure
|
|
3146
|
+
* @param cause - The underlying error (e.g., fetch error, timeout)
|
|
3147
|
+
*/
|
|
3148
|
+
constructor(message: string, cause?: unknown);
|
|
3149
|
+
}
|
|
3150
|
+
/**
|
|
3151
|
+
* Error thrown when an SDS-only operation is attempted on a PDS.
|
|
3152
|
+
*
|
|
3153
|
+
* Certain operations are only available on Shared Data Servers (SDS),
|
|
3154
|
+
* such as collaborator management and organization operations.
|
|
3155
|
+
* This error is thrown when these operations are attempted on a
|
|
3156
|
+
* Personal Data Server (PDS).
|
|
3157
|
+
*
|
|
3158
|
+
* @example
|
|
3159
|
+
* ```typescript
|
|
3160
|
+
* const pdsRepo = sdk.repository(session); // Default is PDS
|
|
3161
|
+
*
|
|
3162
|
+
* try {
|
|
3163
|
+
* // This will throw SDSRequiredError
|
|
3164
|
+
* await pdsRepo.collaborators.list();
|
|
3165
|
+
* } catch (error) {
|
|
3166
|
+
* if (error instanceof SDSRequiredError) {
|
|
3167
|
+
* // Switch to SDS for this operation
|
|
3168
|
+
* const sdsRepo = sdk.repository(session, { server: "sds" });
|
|
3169
|
+
* const collaborators = await sdsRepo.collaborators.list();
|
|
3170
|
+
* }
|
|
3171
|
+
* }
|
|
3172
|
+
* ```
|
|
3173
|
+
*/
|
|
3174
|
+
declare class SDSRequiredError extends ATProtoSDKError {
|
|
3175
|
+
/**
|
|
3176
|
+
* Creates an SDS required error.
|
|
3177
|
+
*
|
|
3178
|
+
* @param message - Description of which operation requires SDS
|
|
3179
|
+
* @param cause - Any underlying error
|
|
3180
|
+
*/
|
|
3181
|
+
constructor(message?: string, cause?: unknown);
|
|
3182
|
+
}
|
|
3183
|
+
|
|
3184
|
+
/**
|
|
3185
|
+
* In-memory implementation of the SessionStore interface.
|
|
3186
|
+
*
|
|
3187
|
+
* This store keeps OAuth sessions in memory using a Map. It's intended
|
|
3188
|
+
* for development, testing, and simple use cases where session persistence
|
|
3189
|
+
* across restarts is not required.
|
|
3190
|
+
*
|
|
3191
|
+
* @remarks
|
|
3192
|
+
* **Warning**: This implementation is **not suitable for production** because:
|
|
3193
|
+
* - Sessions are lost when the process restarts
|
|
3194
|
+
* - Sessions cannot be shared across multiple server instances
|
|
3195
|
+
* - No automatic cleanup of expired sessions
|
|
3196
|
+
*
|
|
3197
|
+
* For production, implement {@link SessionStore} with a persistent backend:
|
|
3198
|
+
* - **Redis**: Good for distributed systems, supports TTL
|
|
3199
|
+
* - **PostgreSQL/MySQL**: Good for existing database infrastructure
|
|
3200
|
+
* - **MongoDB**: Good for document-based storage
|
|
3201
|
+
*
|
|
3202
|
+
* @example Basic usage
|
|
3203
|
+
* ```typescript
|
|
3204
|
+
* import { InMemorySessionStore } from "@hypercerts-org/sdk/storage";
|
|
3205
|
+
*
|
|
3206
|
+
* const sessionStore = new InMemorySessionStore();
|
|
3207
|
+
*
|
|
3208
|
+
* const sdk = new ATProtoSDK({
|
|
3209
|
+
* oauth: { ... },
|
|
3210
|
+
* storage: {
|
|
3211
|
+
* sessionStore, // Will warn in logs for production
|
|
3212
|
+
* },
|
|
3213
|
+
* });
|
|
3214
|
+
* ```
|
|
3215
|
+
*
|
|
3216
|
+
* @example Testing usage
|
|
3217
|
+
* ```typescript
|
|
3218
|
+
* const sessionStore = new InMemorySessionStore();
|
|
3219
|
+
*
|
|
3220
|
+
* // After tests, clean up
|
|
3221
|
+
* sessionStore.clear();
|
|
3222
|
+
* ```
|
|
3223
|
+
*
|
|
3224
|
+
* @see {@link SessionStore} for the interface definition
|
|
3225
|
+
* @see {@link InMemoryStateStore} for the corresponding state store
|
|
3226
|
+
*/
|
|
3227
|
+
declare class InMemorySessionStore implements SessionStore {
|
|
3228
|
+
/**
|
|
3229
|
+
* Internal storage for sessions, keyed by DID.
|
|
3230
|
+
* @internal
|
|
3231
|
+
*/
|
|
3232
|
+
private sessions;
|
|
3233
|
+
/**
|
|
3234
|
+
* Retrieves a session by DID.
|
|
3235
|
+
*
|
|
3236
|
+
* @param did - The user's Decentralized Identifier
|
|
3237
|
+
* @returns Promise resolving to the session, or `undefined` if not found
|
|
3238
|
+
*
|
|
3239
|
+
* @example
|
|
3240
|
+
* ```typescript
|
|
3241
|
+
* const session = await sessionStore.get("did:plc:abc123");
|
|
3242
|
+
* if (session) {
|
|
3243
|
+
* console.log("Session found");
|
|
3244
|
+
* }
|
|
3245
|
+
* ```
|
|
3246
|
+
*/
|
|
3247
|
+
get(did: string): Promise<NodeSavedSession | undefined>;
|
|
3248
|
+
/**
|
|
3249
|
+
* Stores or updates a session.
|
|
3250
|
+
*
|
|
3251
|
+
* @param did - The user's DID to use as the key
|
|
3252
|
+
* @param session - The session data to store
|
|
3253
|
+
*
|
|
3254
|
+
* @remarks
|
|
3255
|
+
* If a session already exists for the DID, it is overwritten.
|
|
3256
|
+
*
|
|
3257
|
+
* @example
|
|
3258
|
+
* ```typescript
|
|
3259
|
+
* await sessionStore.set("did:plc:abc123", sessionData);
|
|
3260
|
+
* ```
|
|
3261
|
+
*/
|
|
3262
|
+
set(did: string, session: NodeSavedSession): Promise<void>;
|
|
3263
|
+
/**
|
|
3264
|
+
* Deletes a session by DID.
|
|
3265
|
+
*
|
|
3266
|
+
* @param did - The DID of the session to delete
|
|
3267
|
+
*
|
|
3268
|
+
* @remarks
|
|
3269
|
+
* If no session exists for the DID, this is a no-op.
|
|
3270
|
+
*
|
|
3271
|
+
* @example
|
|
3272
|
+
* ```typescript
|
|
3273
|
+
* await sessionStore.del("did:plc:abc123");
|
|
3274
|
+
* ```
|
|
3275
|
+
*/
|
|
3276
|
+
del(did: string): Promise<void>;
|
|
3277
|
+
/**
|
|
3278
|
+
* Clears all stored sessions.
|
|
3279
|
+
*
|
|
3280
|
+
* This is primarily useful for testing to ensure a clean state
|
|
3281
|
+
* between test runs.
|
|
3282
|
+
*
|
|
3283
|
+
* @remarks
|
|
3284
|
+
* This method is synchronous (not async) for convenience in test cleanup.
|
|
3285
|
+
*
|
|
3286
|
+
* @example
|
|
3287
|
+
* ```typescript
|
|
3288
|
+
* // In test teardown
|
|
3289
|
+
* afterEach(() => {
|
|
3290
|
+
* sessionStore.clear();
|
|
3291
|
+
* });
|
|
3292
|
+
* ```
|
|
3293
|
+
*/
|
|
3294
|
+
clear(): void;
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
/**
|
|
3298
|
+
* In-memory implementation of the StateStore interface.
|
|
3299
|
+
*
|
|
3300
|
+
* This store keeps OAuth state parameters in memory using a Map. State is
|
|
3301
|
+
* used during the OAuth authorization flow for CSRF protection and PKCE.
|
|
3302
|
+
*
|
|
3303
|
+
* @remarks
|
|
3304
|
+
* **Warning**: This implementation is **not suitable for production** because:
|
|
3305
|
+
* - State is lost when the process restarts (breaking in-progress OAuth flows)
|
|
3306
|
+
* - State cannot be shared across multiple server instances
|
|
3307
|
+
* - No automatic cleanup of expired state (memory leak potential)
|
|
3308
|
+
*
|
|
3309
|
+
* For production, implement {@link StateStore} with a persistent backend
|
|
3310
|
+
* that supports TTL (time-to-live):
|
|
3311
|
+
* - **Redis**: Ideal choice with built-in TTL support
|
|
3312
|
+
* - **Database with cleanup job**: PostgreSQL/MySQL with periodic cleanup
|
|
3313
|
+
*
|
|
3314
|
+
* **State Lifecycle**:
|
|
3315
|
+
* 1. Created when user starts OAuth flow (`authorize()`)
|
|
3316
|
+
* 2. Retrieved and validated during callback
|
|
3317
|
+
* 3. Deleted after successful or failed callback
|
|
3318
|
+
* 4. Should expire after ~15 minutes if callback never happens
|
|
3319
|
+
*
|
|
3320
|
+
* @example Basic usage
|
|
3321
|
+
* ```typescript
|
|
3322
|
+
* import { InMemoryStateStore } from "@hypercerts-org/sdk/storage";
|
|
3323
|
+
*
|
|
3324
|
+
* const stateStore = new InMemoryStateStore();
|
|
3325
|
+
*
|
|
3326
|
+
* const sdk = new ATProtoSDK({
|
|
3327
|
+
* oauth: { ... },
|
|
3328
|
+
* storage: {
|
|
3329
|
+
* stateStore, // Will warn in logs for production
|
|
3330
|
+
* },
|
|
3331
|
+
* });
|
|
3332
|
+
* ```
|
|
3333
|
+
*
|
|
3334
|
+
* @example Testing usage
|
|
3335
|
+
* ```typescript
|
|
3336
|
+
* const stateStore = new InMemoryStateStore();
|
|
3337
|
+
*
|
|
3338
|
+
* // After tests, clean up
|
|
3339
|
+
* stateStore.clear();
|
|
3340
|
+
* ```
|
|
3341
|
+
*
|
|
3342
|
+
* @see {@link StateStore} for the interface definition
|
|
3343
|
+
* @see {@link InMemorySessionStore} for the corresponding session store
|
|
3344
|
+
*/
|
|
3345
|
+
declare class InMemoryStateStore implements StateStore {
|
|
3346
|
+
/**
|
|
3347
|
+
* Internal storage for OAuth state, keyed by state string.
|
|
3348
|
+
* @internal
|
|
3349
|
+
*/
|
|
3350
|
+
private states;
|
|
3351
|
+
/**
|
|
3352
|
+
* Retrieves OAuth state by key.
|
|
3353
|
+
*
|
|
3354
|
+
* @param key - The state key (random string from authorization URL)
|
|
3355
|
+
* @returns Promise resolving to the state, or `undefined` if not found
|
|
3356
|
+
*
|
|
3357
|
+
* @remarks
|
|
3358
|
+
* The key is a cryptographically random string generated during
|
|
3359
|
+
* the authorization request. It's included in the callback URL
|
|
3360
|
+
* and used to retrieve the associated PKCE verifier and other data.
|
|
3361
|
+
*
|
|
3362
|
+
* @example
|
|
3363
|
+
* ```typescript
|
|
3364
|
+
* // During OAuth callback
|
|
3365
|
+
* const state = await stateStore.get(params.get("state")!);
|
|
3366
|
+
* if (!state) {
|
|
3367
|
+
* throw new Error("Invalid or expired state");
|
|
3368
|
+
* }
|
|
3369
|
+
* ```
|
|
3370
|
+
*/
|
|
3371
|
+
get(key: string): Promise<NodeSavedState | undefined>;
|
|
3372
|
+
/**
|
|
3373
|
+
* Stores OAuth state temporarily.
|
|
3374
|
+
*
|
|
3375
|
+
* @param key - The state key to use for storage
|
|
3376
|
+
* @param state - The OAuth state data (includes PKCE verifier, etc.)
|
|
3377
|
+
*
|
|
3378
|
+
* @remarks
|
|
3379
|
+
* In production implementations, state should be stored with a TTL
|
|
3380
|
+
* of approximately 10-15 minutes to prevent stale state accumulation.
|
|
3381
|
+
*
|
|
3382
|
+
* @example
|
|
3383
|
+
* ```typescript
|
|
3384
|
+
* // Called internally by OAuthClient during authorize()
|
|
3385
|
+
* await stateStore.set(stateKey, {
|
|
3386
|
+
* // PKCE code verifier, redirect URI, etc.
|
|
3387
|
+
* });
|
|
3388
|
+
* ```
|
|
3389
|
+
*/
|
|
3390
|
+
set(key: string, state: NodeSavedState): Promise<void>;
|
|
3391
|
+
/**
|
|
3392
|
+
* Deletes OAuth state by key.
|
|
3393
|
+
*
|
|
3394
|
+
* @param key - The state key to delete
|
|
3395
|
+
*
|
|
3396
|
+
* @remarks
|
|
3397
|
+
* Called after the OAuth callback is processed (whether successful or not)
|
|
3398
|
+
* to clean up the temporary state.
|
|
3399
|
+
*
|
|
3400
|
+
* @example
|
|
3401
|
+
* ```typescript
|
|
3402
|
+
* // After processing callback
|
|
3403
|
+
* await stateStore.del(stateKey);
|
|
3404
|
+
* ```
|
|
3405
|
+
*/
|
|
3406
|
+
del(key: string): Promise<void>;
|
|
3407
|
+
/**
|
|
3408
|
+
* Clears all stored state.
|
|
3409
|
+
*
|
|
3410
|
+
* This is primarily useful for testing to ensure a clean state
|
|
3411
|
+
* between test runs.
|
|
3412
|
+
*
|
|
3413
|
+
* @remarks
|
|
3414
|
+
* This method is synchronous (not async) for convenience in test cleanup.
|
|
3415
|
+
* In production, be careful using this as it will invalidate all
|
|
3416
|
+
* in-progress OAuth flows.
|
|
3417
|
+
*
|
|
3418
|
+
* @example
|
|
3419
|
+
* ```typescript
|
|
3420
|
+
* // In test teardown
|
|
3421
|
+
* afterEach(() => {
|
|
3422
|
+
* stateStore.clear();
|
|
3423
|
+
* });
|
|
3424
|
+
* ```
|
|
3425
|
+
*/
|
|
3426
|
+
clear(): void;
|
|
3427
|
+
}
|
|
3428
|
+
|
|
3429
|
+
export { ATProtoSDK, ATProtoSDKConfigSchema, ATProtoSDKError, AuthenticationError, CollaboratorPermissionsSchema, CollaboratorSchema, InMemorySessionStore, InMemoryStateStore, LexiconRegistry, NetworkError, OAuthConfigSchema, OrganizationSchema, Repository, SDSRequiredError, ServerConfigSchema, SessionExpiredError, TimeoutConfigSchema, ValidationError, createATProtoSDK };
|
|
3430
|
+
export type { ATProtoSDKConfig, AuthorizeOptions, BlobOperations, BlobRef, CacheInterface, Collaborator, CollaboratorOperations, CollaboratorPermissions, CreateHypercertParams, CreateHypercertResult, CreateResult, DID, HypercertClaim, HypercertCollection, HypercertCollectionClaimItem, HypercertContribution, HypercertEvaluation, HypercertEvents, HypercertEvidence, HypercertImage, HypercertLocation, HypercertMeasurement, HypercertOperations, HypercertRights, HypercertWithMetadata, ListParams, LoggerInterface, Organization, OrganizationInfo, OrganizationOperations, PaginatedList, ProfileOperations, ProgressStep, RecordOperations, RepositoryAccessGrant, RepositoryOptions, RepositoryRole, Session, SessionStore, StateStore, StrongRef, UpdateResult, ValidationResult };
|