@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/storage.mjs
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory implementation of the SessionStore interface.
|
|
3
|
+
*
|
|
4
|
+
* This store keeps OAuth sessions in memory using a Map. It's intended
|
|
5
|
+
* for development, testing, and simple use cases where session persistence
|
|
6
|
+
* across restarts is not required.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* **Warning**: This implementation is **not suitable for production** because:
|
|
10
|
+
* - Sessions are lost when the process restarts
|
|
11
|
+
* - Sessions cannot be shared across multiple server instances
|
|
12
|
+
* - No automatic cleanup of expired sessions
|
|
13
|
+
*
|
|
14
|
+
* For production, implement {@link SessionStore} with a persistent backend:
|
|
15
|
+
* - **Redis**: Good for distributed systems, supports TTL
|
|
16
|
+
* - **PostgreSQL/MySQL**: Good for existing database infrastructure
|
|
17
|
+
* - **MongoDB**: Good for document-based storage
|
|
18
|
+
*
|
|
19
|
+
* @example Basic usage
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { InMemorySessionStore } from "@hypercerts-org/sdk/storage";
|
|
22
|
+
*
|
|
23
|
+
* const sessionStore = new InMemorySessionStore();
|
|
24
|
+
*
|
|
25
|
+
* const sdk = new ATProtoSDK({
|
|
26
|
+
* oauth: { ... },
|
|
27
|
+
* storage: {
|
|
28
|
+
* sessionStore, // Will warn in logs for production
|
|
29
|
+
* },
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @example Testing usage
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const sessionStore = new InMemorySessionStore();
|
|
36
|
+
*
|
|
37
|
+
* // After tests, clean up
|
|
38
|
+
* sessionStore.clear();
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @see {@link SessionStore} for the interface definition
|
|
42
|
+
* @see {@link InMemoryStateStore} for the corresponding state store
|
|
43
|
+
*/
|
|
44
|
+
class InMemorySessionStore {
|
|
45
|
+
constructor() {
|
|
46
|
+
/**
|
|
47
|
+
* Internal storage for sessions, keyed by DID.
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
this.sessions = new Map();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Retrieves a session by DID.
|
|
54
|
+
*
|
|
55
|
+
* @param did - The user's Decentralized Identifier
|
|
56
|
+
* @returns Promise resolving to the session, or `undefined` if not found
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const session = await sessionStore.get("did:plc:abc123");
|
|
61
|
+
* if (session) {
|
|
62
|
+
* console.log("Session found");
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
async get(did) {
|
|
67
|
+
return this.sessions.get(did);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Stores or updates a session.
|
|
71
|
+
*
|
|
72
|
+
* @param did - The user's DID to use as the key
|
|
73
|
+
* @param session - The session data to store
|
|
74
|
+
*
|
|
75
|
+
* @remarks
|
|
76
|
+
* If a session already exists for the DID, it is overwritten.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* await sessionStore.set("did:plc:abc123", sessionData);
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
async set(did, session) {
|
|
84
|
+
this.sessions.set(did, session);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Deletes a session by DID.
|
|
88
|
+
*
|
|
89
|
+
* @param did - The DID of the session to delete
|
|
90
|
+
*
|
|
91
|
+
* @remarks
|
|
92
|
+
* If no session exists for the DID, this is a no-op.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* await sessionStore.del("did:plc:abc123");
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
async del(did) {
|
|
100
|
+
this.sessions.delete(did);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Clears all stored sessions.
|
|
104
|
+
*
|
|
105
|
+
* This is primarily useful for testing to ensure a clean state
|
|
106
|
+
* between test runs.
|
|
107
|
+
*
|
|
108
|
+
* @remarks
|
|
109
|
+
* This method is synchronous (not async) for convenience in test cleanup.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* // In test teardown
|
|
114
|
+
* afterEach(() => {
|
|
115
|
+
* sessionStore.clear();
|
|
116
|
+
* });
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
clear() {
|
|
120
|
+
this.sessions.clear();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* In-memory implementation of the StateStore interface.
|
|
126
|
+
*
|
|
127
|
+
* This store keeps OAuth state parameters in memory using a Map. State is
|
|
128
|
+
* used during the OAuth authorization flow for CSRF protection and PKCE.
|
|
129
|
+
*
|
|
130
|
+
* @remarks
|
|
131
|
+
* **Warning**: This implementation is **not suitable for production** because:
|
|
132
|
+
* - State is lost when the process restarts (breaking in-progress OAuth flows)
|
|
133
|
+
* - State cannot be shared across multiple server instances
|
|
134
|
+
* - No automatic cleanup of expired state (memory leak potential)
|
|
135
|
+
*
|
|
136
|
+
* For production, implement {@link StateStore} with a persistent backend
|
|
137
|
+
* that supports TTL (time-to-live):
|
|
138
|
+
* - **Redis**: Ideal choice with built-in TTL support
|
|
139
|
+
* - **Database with cleanup job**: PostgreSQL/MySQL with periodic cleanup
|
|
140
|
+
*
|
|
141
|
+
* **State Lifecycle**:
|
|
142
|
+
* 1. Created when user starts OAuth flow (`authorize()`)
|
|
143
|
+
* 2. Retrieved and validated during callback
|
|
144
|
+
* 3. Deleted after successful or failed callback
|
|
145
|
+
* 4. Should expire after ~15 minutes if callback never happens
|
|
146
|
+
*
|
|
147
|
+
* @example Basic usage
|
|
148
|
+
* ```typescript
|
|
149
|
+
* import { InMemoryStateStore } from "@hypercerts-org/sdk/storage";
|
|
150
|
+
*
|
|
151
|
+
* const stateStore = new InMemoryStateStore();
|
|
152
|
+
*
|
|
153
|
+
* const sdk = new ATProtoSDK({
|
|
154
|
+
* oauth: { ... },
|
|
155
|
+
* storage: {
|
|
156
|
+
* stateStore, // Will warn in logs for production
|
|
157
|
+
* },
|
|
158
|
+
* });
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* @example Testing usage
|
|
162
|
+
* ```typescript
|
|
163
|
+
* const stateStore = new InMemoryStateStore();
|
|
164
|
+
*
|
|
165
|
+
* // After tests, clean up
|
|
166
|
+
* stateStore.clear();
|
|
167
|
+
* ```
|
|
168
|
+
*
|
|
169
|
+
* @see {@link StateStore} for the interface definition
|
|
170
|
+
* @see {@link InMemorySessionStore} for the corresponding session store
|
|
171
|
+
*/
|
|
172
|
+
class InMemoryStateStore {
|
|
173
|
+
constructor() {
|
|
174
|
+
/**
|
|
175
|
+
* Internal storage for OAuth state, keyed by state string.
|
|
176
|
+
* @internal
|
|
177
|
+
*/
|
|
178
|
+
this.states = new Map();
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Retrieves OAuth state by key.
|
|
182
|
+
*
|
|
183
|
+
* @param key - The state key (random string from authorization URL)
|
|
184
|
+
* @returns Promise resolving to the state, or `undefined` if not found
|
|
185
|
+
*
|
|
186
|
+
* @remarks
|
|
187
|
+
* The key is a cryptographically random string generated during
|
|
188
|
+
* the authorization request. It's included in the callback URL
|
|
189
|
+
* and used to retrieve the associated PKCE verifier and other data.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```typescript
|
|
193
|
+
* // During OAuth callback
|
|
194
|
+
* const state = await stateStore.get(params.get("state")!);
|
|
195
|
+
* if (!state) {
|
|
196
|
+
* throw new Error("Invalid or expired state");
|
|
197
|
+
* }
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
async get(key) {
|
|
201
|
+
return this.states.get(key);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Stores OAuth state temporarily.
|
|
205
|
+
*
|
|
206
|
+
* @param key - The state key to use for storage
|
|
207
|
+
* @param state - The OAuth state data (includes PKCE verifier, etc.)
|
|
208
|
+
*
|
|
209
|
+
* @remarks
|
|
210
|
+
* In production implementations, state should be stored with a TTL
|
|
211
|
+
* of approximately 10-15 minutes to prevent stale state accumulation.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```typescript
|
|
215
|
+
* // Called internally by OAuthClient during authorize()
|
|
216
|
+
* await stateStore.set(stateKey, {
|
|
217
|
+
* // PKCE code verifier, redirect URI, etc.
|
|
218
|
+
* });
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
async set(key, state) {
|
|
222
|
+
this.states.set(key, state);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Deletes OAuth state by key.
|
|
226
|
+
*
|
|
227
|
+
* @param key - The state key to delete
|
|
228
|
+
*
|
|
229
|
+
* @remarks
|
|
230
|
+
* Called after the OAuth callback is processed (whether successful or not)
|
|
231
|
+
* to clean up the temporary state.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```typescript
|
|
235
|
+
* // After processing callback
|
|
236
|
+
* await stateStore.del(stateKey);
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
async del(key) {
|
|
240
|
+
this.states.delete(key);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Clears all stored state.
|
|
244
|
+
*
|
|
245
|
+
* This is primarily useful for testing to ensure a clean state
|
|
246
|
+
* between test runs.
|
|
247
|
+
*
|
|
248
|
+
* @remarks
|
|
249
|
+
* This method is synchronous (not async) for convenience in test cleanup.
|
|
250
|
+
* In production, be careful using this as it will invalidate all
|
|
251
|
+
* in-progress OAuth flows.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* // In test teardown
|
|
256
|
+
* afterEach(() => {
|
|
257
|
+
* stateStore.clear();
|
|
258
|
+
* });
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
clear() {
|
|
262
|
+
this.states.clear();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export { InMemorySessionStore, InMemoryStateStore };
|
|
267
|
+
//# sourceMappingURL=storage.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.mjs","sources":["../src/storage/InMemorySessionStore.ts","../src/storage/InMemoryStateStore.ts"],"sourcesContent":["import type { SessionStore } from \"../core/interfaces.js\";\nimport type { NodeSavedSession } from \"@atproto/oauth-client-node\";\n\n/**\n * In-memory implementation of the SessionStore interface.\n *\n * This store keeps OAuth sessions in memory using a Map. It's intended\n * for development, testing, and simple use cases where session persistence\n * across restarts is not required.\n *\n * @remarks\n * **Warning**: This implementation is **not suitable for production** because:\n * - Sessions are lost when the process restarts\n * - Sessions cannot be shared across multiple server instances\n * - No automatic cleanup of expired sessions\n *\n * For production, implement {@link SessionStore} with a persistent backend:\n * - **Redis**: Good for distributed systems, supports TTL\n * - **PostgreSQL/MySQL**: Good for existing database infrastructure\n * - **MongoDB**: Good for document-based storage\n *\n * @example Basic usage\n * ```typescript\n * import { InMemorySessionStore } from \"@hypercerts-org/sdk/storage\";\n *\n * const sessionStore = new InMemorySessionStore();\n *\n * const sdk = new ATProtoSDK({\n * oauth: { ... },\n * storage: {\n * sessionStore, // Will warn in logs for production\n * },\n * });\n * ```\n *\n * @example Testing usage\n * ```typescript\n * const sessionStore = new InMemorySessionStore();\n *\n * // After tests, clean up\n * sessionStore.clear();\n * ```\n *\n * @see {@link SessionStore} for the interface definition\n * @see {@link InMemoryStateStore} for the corresponding state store\n */\nexport class InMemorySessionStore implements SessionStore {\n /**\n * Internal storage for sessions, keyed by DID.\n * @internal\n */\n private sessions = new Map<string, NodeSavedSession>();\n\n /**\n * Retrieves a session by DID.\n *\n * @param did - The user's Decentralized Identifier\n * @returns Promise resolving to the session, or `undefined` if not found\n *\n * @example\n * ```typescript\n * const session = await sessionStore.get(\"did:plc:abc123\");\n * if (session) {\n * console.log(\"Session found\");\n * }\n * ```\n */\n async get(did: string): Promise<NodeSavedSession | undefined> {\n return this.sessions.get(did);\n }\n\n /**\n * Stores or updates a session.\n *\n * @param did - The user's DID to use as the key\n * @param session - The session data to store\n *\n * @remarks\n * If a session already exists for the DID, it is overwritten.\n *\n * @example\n * ```typescript\n * await sessionStore.set(\"did:plc:abc123\", sessionData);\n * ```\n */\n async set(did: string, session: NodeSavedSession): Promise<void> {\n this.sessions.set(did, session);\n }\n\n /**\n * Deletes a session by DID.\n *\n * @param did - The DID of the session to delete\n *\n * @remarks\n * If no session exists for the DID, this is a no-op.\n *\n * @example\n * ```typescript\n * await sessionStore.del(\"did:plc:abc123\");\n * ```\n */\n async del(did: string): Promise<void> {\n this.sessions.delete(did);\n }\n\n /**\n * Clears all stored sessions.\n *\n * This is primarily useful for testing to ensure a clean state\n * between test runs.\n *\n * @remarks\n * This method is synchronous (not async) for convenience in test cleanup.\n *\n * @example\n * ```typescript\n * // In test teardown\n * afterEach(() => {\n * sessionStore.clear();\n * });\n * ```\n */\n clear(): void {\n this.sessions.clear();\n }\n}\n","import type { StateStore } from \"../core/interfaces.js\";\nimport type { NodeSavedState } from \"@atproto/oauth-client-node\";\n\n/**\n * In-memory implementation of the StateStore interface.\n *\n * This store keeps OAuth state parameters in memory using a Map. State is\n * used during the OAuth authorization flow for CSRF protection and PKCE.\n *\n * @remarks\n * **Warning**: This implementation is **not suitable for production** because:\n * - State is lost when the process restarts (breaking in-progress OAuth flows)\n * - State cannot be shared across multiple server instances\n * - No automatic cleanup of expired state (memory leak potential)\n *\n * For production, implement {@link StateStore} with a persistent backend\n * that supports TTL (time-to-live):\n * - **Redis**: Ideal choice with built-in TTL support\n * - **Database with cleanup job**: PostgreSQL/MySQL with periodic cleanup\n *\n * **State Lifecycle**:\n * 1. Created when user starts OAuth flow (`authorize()`)\n * 2. Retrieved and validated during callback\n * 3. Deleted after successful or failed callback\n * 4. Should expire after ~15 minutes if callback never happens\n *\n * @example Basic usage\n * ```typescript\n * import { InMemoryStateStore } from \"@hypercerts-org/sdk/storage\";\n *\n * const stateStore = new InMemoryStateStore();\n *\n * const sdk = new ATProtoSDK({\n * oauth: { ... },\n * storage: {\n * stateStore, // Will warn in logs for production\n * },\n * });\n * ```\n *\n * @example Testing usage\n * ```typescript\n * const stateStore = new InMemoryStateStore();\n *\n * // After tests, clean up\n * stateStore.clear();\n * ```\n *\n * @see {@link StateStore} for the interface definition\n * @see {@link InMemorySessionStore} for the corresponding session store\n */\nexport class InMemoryStateStore implements StateStore {\n /**\n * Internal storage for OAuth state, keyed by state string.\n * @internal\n */\n private states = new Map<string, NodeSavedState>();\n\n /**\n * Retrieves OAuth state by key.\n *\n * @param key - The state key (random string from authorization URL)\n * @returns Promise resolving to the state, or `undefined` if not found\n *\n * @remarks\n * The key is a cryptographically random string generated during\n * the authorization request. It's included in the callback URL\n * and used to retrieve the associated PKCE verifier and other data.\n *\n * @example\n * ```typescript\n * // During OAuth callback\n * const state = await stateStore.get(params.get(\"state\")!);\n * if (!state) {\n * throw new Error(\"Invalid or expired state\");\n * }\n * ```\n */\n async get(key: string): Promise<NodeSavedState | undefined> {\n return this.states.get(key);\n }\n\n /**\n * Stores OAuth state temporarily.\n *\n * @param key - The state key to use for storage\n * @param state - The OAuth state data (includes PKCE verifier, etc.)\n *\n * @remarks\n * In production implementations, state should be stored with a TTL\n * of approximately 10-15 minutes to prevent stale state accumulation.\n *\n * @example\n * ```typescript\n * // Called internally by OAuthClient during authorize()\n * await stateStore.set(stateKey, {\n * // PKCE code verifier, redirect URI, etc.\n * });\n * ```\n */\n async set(key: string, state: NodeSavedState): Promise<void> {\n this.states.set(key, state);\n }\n\n /**\n * Deletes OAuth state by key.\n *\n * @param key - The state key to delete\n *\n * @remarks\n * Called after the OAuth callback is processed (whether successful or not)\n * to clean up the temporary state.\n *\n * @example\n * ```typescript\n * // After processing callback\n * await stateStore.del(stateKey);\n * ```\n */\n async del(key: string): Promise<void> {\n this.states.delete(key);\n }\n\n /**\n * Clears all stored state.\n *\n * This is primarily useful for testing to ensure a clean state\n * between test runs.\n *\n * @remarks\n * This method is synchronous (not async) for convenience in test cleanup.\n * In production, be careful using this as it will invalidate all\n * in-progress OAuth flows.\n *\n * @example\n * ```typescript\n * // In test teardown\n * afterEach(() => {\n * stateStore.clear();\n * });\n * ```\n */\n clear(): void {\n this.states.clear();\n }\n}\n"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CG;MACU,oBAAoB,CAAA;AAAjC,IAAA,WAAA,GAAA;AACE;;;AAGG;AACK,QAAA,IAAA,CAAA,QAAQ,GAAG,IAAI,GAAG,EAA4B;IA2ExD;AAzEE;;;;;;;;;;;;;AAaG;IACH,MAAM,GAAG,CAAC,GAAW,EAAA;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;IAC/B;AAEA;;;;;;;;;;;;;AAaG;AACH,IAAA,MAAM,GAAG,CAAC,GAAW,EAAE,OAAyB,EAAA;QAC9C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC;IACjC;AAEA;;;;;;;;;;;;AAYG;IACH,MAAM,GAAG,CAAC,GAAW,EAAA;AACnB,QAAA,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC;IAC3B;AAEA;;;;;;;;;;;;;;;;AAgBG;IACH,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE;IACvB;AACD;;AC3HD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CG;MACU,kBAAkB,CAAA;AAA/B,IAAA,WAAA,GAAA;AACE;;;AAGG;AACK,QAAA,IAAA,CAAA,MAAM,GAAG,IAAI,GAAG,EAA0B;IAyFpD;AAvFE;;;;;;;;;;;;;;;;;;;AAmBG;IACH,MAAM,GAAG,CAAC,GAAW,EAAA;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;IAC7B;AAEA;;;;;;;;;;;;;;;;;AAiBG;AACH,IAAA,MAAM,GAAG,CAAC,GAAW,EAAE,KAAqB,EAAA;QAC1C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC;IAC7B;AAEA;;;;;;;;;;;;;;AAcG;IACH,MAAM,GAAG,CAAC,GAAW,EAAA;AACnB,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;IACzB;AAEA;;;;;;;;;;;;;;;;;;AAkBG;IACH,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;IACrB;AACD;;;;"}
|
package/dist/testing.cjs
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mock factories for testing.
|
|
5
|
+
*
|
|
6
|
+
* This module provides factory functions to create mock objects
|
|
7
|
+
* for testing SDK functionality without real AT Protocol connections.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Creates a mock OAuth session for testing.
|
|
13
|
+
*
|
|
14
|
+
* The mock session includes all required properties and a mock
|
|
15
|
+
* `fetchHandler` that returns empty successful responses by default.
|
|
16
|
+
*
|
|
17
|
+
* @param overrides - Partial session object to override default values
|
|
18
|
+
* @returns A mock Session object suitable for testing
|
|
19
|
+
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* The mock session is cast to `Session` type for compatibility.
|
|
22
|
+
* In real usage, sessions come from the OAuth flow and contain
|
|
23
|
+
* actual tokens and a real fetch handler.
|
|
24
|
+
*
|
|
25
|
+
* **Default Values**:
|
|
26
|
+
* - `did`: `"did:plc:test123"`
|
|
27
|
+
* - `handle`: `"test.bsky.social"`
|
|
28
|
+
* - `fetchHandler`: Returns `Response` with `{}` body
|
|
29
|
+
*
|
|
30
|
+
* @example Basic mock session
|
|
31
|
+
* ```typescript
|
|
32
|
+
* import { createMockSession } from "@hypercerts-org/sdk/testing";
|
|
33
|
+
*
|
|
34
|
+
* const session = createMockSession();
|
|
35
|
+
* const repo = sdk.repository(session);
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @example With custom DID
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const session = createMockSession({
|
|
41
|
+
* did: "did:plc:custom-test-user",
|
|
42
|
+
* handle: "custom.bsky.social",
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @example With custom fetch handler
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const session = createMockSession({
|
|
49
|
+
* fetchHandler: async (url, init) => {
|
|
50
|
+
* // Custom response logic
|
|
51
|
+
* return new Response(JSON.stringify({ success: true }));
|
|
52
|
+
* },
|
|
53
|
+
* });
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
function createMockSession(overrides = {}) {
|
|
57
|
+
const mockSession = {
|
|
58
|
+
did: "did:plc:test123",
|
|
59
|
+
sub: "did:plc:test123",
|
|
60
|
+
handle: "test.bsky.social",
|
|
61
|
+
accessJwt: "mock-access-jwt",
|
|
62
|
+
refreshJwt: "mock-refresh-jwt",
|
|
63
|
+
active: true,
|
|
64
|
+
fetchHandler: async (_input, _init) => {
|
|
65
|
+
return new Response(JSON.stringify({}), {
|
|
66
|
+
status: 200,
|
|
67
|
+
headers: { "Content-Type": "application/json" },
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
...overrides,
|
|
71
|
+
};
|
|
72
|
+
return mockSession;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Creates a mock SDK configuration for testing.
|
|
76
|
+
*
|
|
77
|
+
* The configuration includes all required OAuth settings with
|
|
78
|
+
* placeholder values suitable for testing (not real credentials).
|
|
79
|
+
*
|
|
80
|
+
* @param overrides - Partial configuration to override default values
|
|
81
|
+
* @returns A complete ATProtoSDKConfig suitable for testing
|
|
82
|
+
*
|
|
83
|
+
* @remarks
|
|
84
|
+
* The default configuration uses example.com domains and a minimal
|
|
85
|
+
* JWK structure. This is sufficient for unit tests but won't work
|
|
86
|
+
* for integration tests that require real OAuth flows.
|
|
87
|
+
*
|
|
88
|
+
* **Default Values**:
|
|
89
|
+
* - `clientId`: `"https://test.example.com/client-metadata.json"`
|
|
90
|
+
* - `pds`: `"https://bsky.social"`
|
|
91
|
+
* - `sds`: `"https://sds.example.com"`
|
|
92
|
+
*
|
|
93
|
+
* @example Basic test config
|
|
94
|
+
* ```typescript
|
|
95
|
+
* import { createTestConfig } from "@hypercerts-org/sdk/testing";
|
|
96
|
+
*
|
|
97
|
+
* const config = createTestConfig();
|
|
98
|
+
* const sdk = new ATProtoSDK(config);
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* @example With custom PDS
|
|
102
|
+
* ```typescript
|
|
103
|
+
* const config = createTestConfig({
|
|
104
|
+
* servers: {
|
|
105
|
+
* pds: "https://custom-pds.example.com",
|
|
106
|
+
* },
|
|
107
|
+
* });
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* @example With logger for debugging tests
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const config = createTestConfig({
|
|
113
|
+
* logger: console,
|
|
114
|
+
* });
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
function createTestConfig(overrides = {}) {
|
|
118
|
+
return {
|
|
119
|
+
oauth: {
|
|
120
|
+
clientId: "https://test.example.com/client-metadata.json",
|
|
121
|
+
redirectUri: "https://test.example.com/callback",
|
|
122
|
+
scope: "atproto transition:generic",
|
|
123
|
+
jwksUri: "https://test.example.com/jwks.json",
|
|
124
|
+
jwkPrivate: JSON.stringify({
|
|
125
|
+
kty: "EC",
|
|
126
|
+
crv: "P-256",
|
|
127
|
+
x: "test",
|
|
128
|
+
y: "test",
|
|
129
|
+
d: "test",
|
|
130
|
+
}),
|
|
131
|
+
},
|
|
132
|
+
servers: {
|
|
133
|
+
pds: "https://bsky.social",
|
|
134
|
+
sds: "https://sds.example.com",
|
|
135
|
+
},
|
|
136
|
+
...overrides,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Mock storage implementations for testing.
|
|
142
|
+
*
|
|
143
|
+
* This module provides mock implementations of SessionStore and StateStore
|
|
144
|
+
* that track all operations for verification in tests.
|
|
145
|
+
*
|
|
146
|
+
* @packageDocumentation
|
|
147
|
+
*/
|
|
148
|
+
/**
|
|
149
|
+
* Mock session store that tracks all operations.
|
|
150
|
+
*
|
|
151
|
+
* This implementation stores sessions in memory and records all
|
|
152
|
+
* method calls for verification in tests.
|
|
153
|
+
*
|
|
154
|
+
* @remarks
|
|
155
|
+
* Use this in tests to:
|
|
156
|
+
* - Verify that sessions are being stored correctly
|
|
157
|
+
* - Check what DIDs have been accessed
|
|
158
|
+
* - Assert on the number and order of operations
|
|
159
|
+
* - Pre-populate sessions for testing restore flows
|
|
160
|
+
*
|
|
161
|
+
* @example Basic usage
|
|
162
|
+
* ```typescript
|
|
163
|
+
* import { MockSessionStore } from "@hypercerts-org/sdk/testing";
|
|
164
|
+
*
|
|
165
|
+
* const sessionStore = new MockSessionStore();
|
|
166
|
+
* const sdk = new ATProtoSDK({
|
|
167
|
+
* ...config,
|
|
168
|
+
* storage: { sessionStore },
|
|
169
|
+
* });
|
|
170
|
+
*
|
|
171
|
+
* // After some operations...
|
|
172
|
+
* expect(sessionStore.setCalls).toHaveLength(1);
|
|
173
|
+
* expect(sessionStore.getCalls).toContain("did:plc:test123");
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* @example Pre-populating for tests
|
|
177
|
+
* ```typescript
|
|
178
|
+
* const sessionStore = new MockSessionStore();
|
|
179
|
+
*
|
|
180
|
+
* // Pre-populate a session
|
|
181
|
+
* await sessionStore.set("did:plc:existing", mockSessionData);
|
|
182
|
+
*
|
|
183
|
+
* // Reset tracking (keeps the data)
|
|
184
|
+
* sessionStore.getCalls = [];
|
|
185
|
+
* sessionStore.setCalls = [];
|
|
186
|
+
*
|
|
187
|
+
* // Now test restore behavior
|
|
188
|
+
* const session = await sdk.restoreSession("did:plc:existing");
|
|
189
|
+
* expect(sessionStore.getCalls).toContain("did:plc:existing");
|
|
190
|
+
* ```
|
|
191
|
+
*
|
|
192
|
+
* @example Asserting on operations
|
|
193
|
+
* ```typescript
|
|
194
|
+
* const sessionStore = new MockSessionStore();
|
|
195
|
+
*
|
|
196
|
+
* // ... perform operations ...
|
|
197
|
+
*
|
|
198
|
+
* // Verify session was stored for correct DID
|
|
199
|
+
* expect(sessionStore.setCalls[0].did).toBe("did:plc:expected");
|
|
200
|
+
*
|
|
201
|
+
* // Verify session was deleted on logout
|
|
202
|
+
* expect(sessionStore.delCalls).toContain("did:plc:logged-out");
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
class MockSessionStore {
|
|
206
|
+
constructor() {
|
|
207
|
+
/**
|
|
208
|
+
* Internal storage for sessions.
|
|
209
|
+
* @internal
|
|
210
|
+
*/
|
|
211
|
+
this.store = new Map();
|
|
212
|
+
/**
|
|
213
|
+
* Record of all `get()` calls made to this store.
|
|
214
|
+
*
|
|
215
|
+
* Each entry is the DID that was requested.
|
|
216
|
+
*/
|
|
217
|
+
this.getCalls = [];
|
|
218
|
+
/**
|
|
219
|
+
* Record of all `set()` calls made to this store.
|
|
220
|
+
*
|
|
221
|
+
* Each entry contains the DID and session that was stored.
|
|
222
|
+
*/
|
|
223
|
+
this.setCalls = [];
|
|
224
|
+
/**
|
|
225
|
+
* Record of all `del()` calls made to this store.
|
|
226
|
+
*
|
|
227
|
+
* Each entry is the DID that was deleted.
|
|
228
|
+
*/
|
|
229
|
+
this.delCalls = [];
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Retrieves a session by DID.
|
|
233
|
+
*
|
|
234
|
+
* Records the call in `getCalls`.
|
|
235
|
+
*
|
|
236
|
+
* @param did - The DID to look up
|
|
237
|
+
* @returns The stored session or undefined
|
|
238
|
+
*/
|
|
239
|
+
async get(did) {
|
|
240
|
+
this.getCalls.push(did);
|
|
241
|
+
return this.store.get(did);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Stores a session.
|
|
245
|
+
*
|
|
246
|
+
* Records the call in `setCalls`.
|
|
247
|
+
*
|
|
248
|
+
* @param did - The DID to store under
|
|
249
|
+
* @param session - The session data to store
|
|
250
|
+
*/
|
|
251
|
+
async set(did, session) {
|
|
252
|
+
this.setCalls.push({ did, session });
|
|
253
|
+
this.store.set(did, session);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Deletes a session.
|
|
257
|
+
*
|
|
258
|
+
* Records the call in `delCalls`.
|
|
259
|
+
*
|
|
260
|
+
* @param did - The DID to delete
|
|
261
|
+
*/
|
|
262
|
+
async del(did) {
|
|
263
|
+
this.delCalls.push(did);
|
|
264
|
+
this.store.delete(did);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Resets the store to initial state.
|
|
268
|
+
*
|
|
269
|
+
* Clears all stored sessions and all recorded calls.
|
|
270
|
+
* Call this in `beforeEach` or `afterEach` to ensure test isolation.
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```typescript
|
|
274
|
+
* beforeEach(() => {
|
|
275
|
+
* sessionStore.reset();
|
|
276
|
+
* });
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
reset() {
|
|
280
|
+
this.store.clear();
|
|
281
|
+
this.getCalls = [];
|
|
282
|
+
this.setCalls = [];
|
|
283
|
+
this.delCalls = [];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Mock state store that tracks all operations.
|
|
288
|
+
*
|
|
289
|
+
* This implementation stores OAuth state in memory and records all
|
|
290
|
+
* method calls for verification in tests.
|
|
291
|
+
*
|
|
292
|
+
* @remarks
|
|
293
|
+
* Use this in tests to:
|
|
294
|
+
* - Verify OAuth state is being created during authorization
|
|
295
|
+
* - Check that state is retrieved during callback
|
|
296
|
+
* - Assert that state is cleaned up after use
|
|
297
|
+
* - Test error handling for missing/invalid state
|
|
298
|
+
*
|
|
299
|
+
* @example Basic usage
|
|
300
|
+
* ```typescript
|
|
301
|
+
* import { MockStateStore } from "@hypercerts-org/sdk/testing";
|
|
302
|
+
*
|
|
303
|
+
* const stateStore = new MockStateStore();
|
|
304
|
+
* const sdk = new ATProtoSDK({
|
|
305
|
+
* ...config,
|
|
306
|
+
* storage: { stateStore },
|
|
307
|
+
* });
|
|
308
|
+
*
|
|
309
|
+
* // After authorize()
|
|
310
|
+
* expect(stateStore.setCalls).toHaveLength(1);
|
|
311
|
+
*
|
|
312
|
+
* // After callback()
|
|
313
|
+
* expect(stateStore.getCalls).toHaveLength(1);
|
|
314
|
+
* expect(stateStore.delCalls).toHaveLength(1);
|
|
315
|
+
* ```
|
|
316
|
+
*
|
|
317
|
+
* @example Testing invalid state
|
|
318
|
+
* ```typescript
|
|
319
|
+
* const stateStore = new MockStateStore();
|
|
320
|
+
* // Don't pre-populate - state will be missing
|
|
321
|
+
*
|
|
322
|
+
* // This should fail because state doesn't exist
|
|
323
|
+
* await expect(sdk.callback(params)).rejects.toThrow();
|
|
324
|
+
*
|
|
325
|
+
* // Verify the lookup was attempted
|
|
326
|
+
* expect(stateStore.getCalls).toContain(stateKey);
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
class MockStateStore {
|
|
330
|
+
constructor() {
|
|
331
|
+
/**
|
|
332
|
+
* Internal storage for OAuth state.
|
|
333
|
+
* @internal
|
|
334
|
+
*/
|
|
335
|
+
this.store = new Map();
|
|
336
|
+
/**
|
|
337
|
+
* Record of all `get()` calls made to this store.
|
|
338
|
+
*
|
|
339
|
+
* Each entry is the state key that was requested.
|
|
340
|
+
*/
|
|
341
|
+
this.getCalls = [];
|
|
342
|
+
/**
|
|
343
|
+
* Record of all `set()` calls made to this store.
|
|
344
|
+
*
|
|
345
|
+
* Each entry contains the key and state that was stored.
|
|
346
|
+
*/
|
|
347
|
+
this.setCalls = [];
|
|
348
|
+
/**
|
|
349
|
+
* Record of all `del()` calls made to this store.
|
|
350
|
+
*
|
|
351
|
+
* Each entry is the state key that was deleted.
|
|
352
|
+
*/
|
|
353
|
+
this.delCalls = [];
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Retrieves OAuth state by key.
|
|
357
|
+
*
|
|
358
|
+
* Records the call in `getCalls`.
|
|
359
|
+
*
|
|
360
|
+
* @param key - The state key to look up
|
|
361
|
+
* @returns The stored state or undefined
|
|
362
|
+
*/
|
|
363
|
+
async get(key) {
|
|
364
|
+
this.getCalls.push(key);
|
|
365
|
+
return this.store.get(key);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Stores OAuth state.
|
|
369
|
+
*
|
|
370
|
+
* Records the call in `setCalls`.
|
|
371
|
+
*
|
|
372
|
+
* @param key - The state key to store under
|
|
373
|
+
* @param state - The OAuth state data to store
|
|
374
|
+
*/
|
|
375
|
+
async set(key, state) {
|
|
376
|
+
this.setCalls.push({ key, state });
|
|
377
|
+
this.store.set(key, state);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Deletes OAuth state.
|
|
381
|
+
*
|
|
382
|
+
* Records the call in `delCalls`.
|
|
383
|
+
*
|
|
384
|
+
* @param key - The state key to delete
|
|
385
|
+
*/
|
|
386
|
+
async del(key) {
|
|
387
|
+
this.delCalls.push(key);
|
|
388
|
+
this.store.delete(key);
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Resets the store to initial state.
|
|
392
|
+
*
|
|
393
|
+
* Clears all stored state and all recorded calls.
|
|
394
|
+
* Call this in `beforeEach` or `afterEach` to ensure test isolation.
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* ```typescript
|
|
398
|
+
* beforeEach(() => {
|
|
399
|
+
* stateStore.reset();
|
|
400
|
+
* });
|
|
401
|
+
* ```
|
|
402
|
+
*/
|
|
403
|
+
reset() {
|
|
404
|
+
this.store.clear();
|
|
405
|
+
this.getCalls = [];
|
|
406
|
+
this.setCalls = [];
|
|
407
|
+
this.delCalls = [];
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
exports.MockSessionStore = MockSessionStore;
|
|
412
|
+
exports.MockStateStore = MockStateStore;
|
|
413
|
+
exports.createMockSession = createMockSession;
|
|
414
|
+
exports.createTestConfig = createTestConfig;
|
|
415
|
+
//# sourceMappingURL=testing.cjs.map
|