@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.
Files changed (83) hide show
  1. package/.turbo/turbo-build.log +328 -0
  2. package/.turbo/turbo-test.log +118 -0
  3. package/CHANGELOG.md +16 -0
  4. package/LICENSE +21 -0
  5. package/README.md +100 -0
  6. package/dist/errors.cjs +260 -0
  7. package/dist/errors.cjs.map +1 -0
  8. package/dist/errors.d.ts +233 -0
  9. package/dist/errors.mjs +253 -0
  10. package/dist/errors.mjs.map +1 -0
  11. package/dist/index.cjs +4531 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.ts +3430 -0
  14. package/dist/index.mjs +4448 -0
  15. package/dist/index.mjs.map +1 -0
  16. package/dist/lexicons.cjs +420 -0
  17. package/dist/lexicons.cjs.map +1 -0
  18. package/dist/lexicons.d.ts +227 -0
  19. package/dist/lexicons.mjs +410 -0
  20. package/dist/lexicons.mjs.map +1 -0
  21. package/dist/storage.cjs +270 -0
  22. package/dist/storage.cjs.map +1 -0
  23. package/dist/storage.d.ts +474 -0
  24. package/dist/storage.mjs +267 -0
  25. package/dist/storage.mjs.map +1 -0
  26. package/dist/testing.cjs +415 -0
  27. package/dist/testing.cjs.map +1 -0
  28. package/dist/testing.d.ts +928 -0
  29. package/dist/testing.mjs +410 -0
  30. package/dist/testing.mjs.map +1 -0
  31. package/dist/types.cjs +220 -0
  32. package/dist/types.cjs.map +1 -0
  33. package/dist/types.d.ts +2118 -0
  34. package/dist/types.mjs +212 -0
  35. package/dist/types.mjs.map +1 -0
  36. package/eslint.config.mjs +22 -0
  37. package/package.json +90 -0
  38. package/rollup.config.js +75 -0
  39. package/src/auth/OAuthClient.ts +497 -0
  40. package/src/core/SDK.ts +410 -0
  41. package/src/core/config.ts +243 -0
  42. package/src/core/errors.ts +257 -0
  43. package/src/core/interfaces.ts +324 -0
  44. package/src/core/types.ts +281 -0
  45. package/src/errors.ts +57 -0
  46. package/src/index.ts +107 -0
  47. package/src/lexicons.ts +64 -0
  48. package/src/repository/BlobOperationsImpl.ts +199 -0
  49. package/src/repository/CollaboratorOperationsImpl.ts +288 -0
  50. package/src/repository/HypercertOperationsImpl.ts +1146 -0
  51. package/src/repository/LexiconRegistry.ts +332 -0
  52. package/src/repository/OrganizationOperationsImpl.ts +234 -0
  53. package/src/repository/ProfileOperationsImpl.ts +281 -0
  54. package/src/repository/RecordOperationsImpl.ts +340 -0
  55. package/src/repository/Repository.ts +482 -0
  56. package/src/repository/interfaces.ts +868 -0
  57. package/src/repository/types.ts +111 -0
  58. package/src/services/hypercerts/types.ts +87 -0
  59. package/src/storage/InMemorySessionStore.ts +127 -0
  60. package/src/storage/InMemoryStateStore.ts +146 -0
  61. package/src/storage.ts +63 -0
  62. package/src/testing/index.ts +67 -0
  63. package/src/testing/mocks.ts +142 -0
  64. package/src/testing/stores.ts +285 -0
  65. package/src/testing.ts +64 -0
  66. package/src/types.ts +86 -0
  67. package/tests/auth/OAuthClient.test.ts +164 -0
  68. package/tests/core/SDK.test.ts +176 -0
  69. package/tests/core/errors.test.ts +81 -0
  70. package/tests/repository/BlobOperationsImpl.test.ts +154 -0
  71. package/tests/repository/CollaboratorOperationsImpl.test.ts +323 -0
  72. package/tests/repository/HypercertOperationsImpl.test.ts +652 -0
  73. package/tests/repository/LexiconRegistry.test.ts +192 -0
  74. package/tests/repository/OrganizationOperationsImpl.test.ts +242 -0
  75. package/tests/repository/ProfileOperationsImpl.test.ts +254 -0
  76. package/tests/repository/RecordOperationsImpl.test.ts +375 -0
  77. package/tests/repository/Repository.test.ts +149 -0
  78. package/tests/utils/fixtures.ts +117 -0
  79. package/tests/utils/mocks.ts +109 -0
  80. package/tests/utils/repository-fixtures.ts +78 -0
  81. package/tsconfig.json +11 -0
  82. package/tsconfig.tsbuildinfo +1 -0
  83. package/vitest.config.ts +30 -0
@@ -0,0 +1,270 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * In-memory implementation of the SessionStore interface.
5
+ *
6
+ * This store keeps OAuth sessions in memory using a Map. It's intended
7
+ * for development, testing, and simple use cases where session persistence
8
+ * across restarts is not required.
9
+ *
10
+ * @remarks
11
+ * **Warning**: This implementation is **not suitable for production** because:
12
+ * - Sessions are lost when the process restarts
13
+ * - Sessions cannot be shared across multiple server instances
14
+ * - No automatic cleanup of expired sessions
15
+ *
16
+ * For production, implement {@link SessionStore} with a persistent backend:
17
+ * - **Redis**: Good for distributed systems, supports TTL
18
+ * - **PostgreSQL/MySQL**: Good for existing database infrastructure
19
+ * - **MongoDB**: Good for document-based storage
20
+ *
21
+ * @example Basic usage
22
+ * ```typescript
23
+ * import { InMemorySessionStore } from "@hypercerts-org/sdk/storage";
24
+ *
25
+ * const sessionStore = new InMemorySessionStore();
26
+ *
27
+ * const sdk = new ATProtoSDK({
28
+ * oauth: { ... },
29
+ * storage: {
30
+ * sessionStore, // Will warn in logs for production
31
+ * },
32
+ * });
33
+ * ```
34
+ *
35
+ * @example Testing usage
36
+ * ```typescript
37
+ * const sessionStore = new InMemorySessionStore();
38
+ *
39
+ * // After tests, clean up
40
+ * sessionStore.clear();
41
+ * ```
42
+ *
43
+ * @see {@link SessionStore} for the interface definition
44
+ * @see {@link InMemoryStateStore} for the corresponding state store
45
+ */
46
+ class InMemorySessionStore {
47
+ constructor() {
48
+ /**
49
+ * Internal storage for sessions, keyed by DID.
50
+ * @internal
51
+ */
52
+ this.sessions = new Map();
53
+ }
54
+ /**
55
+ * Retrieves a session by DID.
56
+ *
57
+ * @param did - The user's Decentralized Identifier
58
+ * @returns Promise resolving to the session, or `undefined` if not found
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const session = await sessionStore.get("did:plc:abc123");
63
+ * if (session) {
64
+ * console.log("Session found");
65
+ * }
66
+ * ```
67
+ */
68
+ async get(did) {
69
+ return this.sessions.get(did);
70
+ }
71
+ /**
72
+ * Stores or updates a session.
73
+ *
74
+ * @param did - The user's DID to use as the key
75
+ * @param session - The session data to store
76
+ *
77
+ * @remarks
78
+ * If a session already exists for the DID, it is overwritten.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * await sessionStore.set("did:plc:abc123", sessionData);
83
+ * ```
84
+ */
85
+ async set(did, session) {
86
+ this.sessions.set(did, session);
87
+ }
88
+ /**
89
+ * Deletes a session by DID.
90
+ *
91
+ * @param did - The DID of the session to delete
92
+ *
93
+ * @remarks
94
+ * If no session exists for the DID, this is a no-op.
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * await sessionStore.del("did:plc:abc123");
99
+ * ```
100
+ */
101
+ async del(did) {
102
+ this.sessions.delete(did);
103
+ }
104
+ /**
105
+ * Clears all stored sessions.
106
+ *
107
+ * This is primarily useful for testing to ensure a clean state
108
+ * between test runs.
109
+ *
110
+ * @remarks
111
+ * This method is synchronous (not async) for convenience in test cleanup.
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * // In test teardown
116
+ * afterEach(() => {
117
+ * sessionStore.clear();
118
+ * });
119
+ * ```
120
+ */
121
+ clear() {
122
+ this.sessions.clear();
123
+ }
124
+ }
125
+
126
+ /**
127
+ * In-memory implementation of the StateStore interface.
128
+ *
129
+ * This store keeps OAuth state parameters in memory using a Map. State is
130
+ * used during the OAuth authorization flow for CSRF protection and PKCE.
131
+ *
132
+ * @remarks
133
+ * **Warning**: This implementation is **not suitable for production** because:
134
+ * - State is lost when the process restarts (breaking in-progress OAuth flows)
135
+ * - State cannot be shared across multiple server instances
136
+ * - No automatic cleanup of expired state (memory leak potential)
137
+ *
138
+ * For production, implement {@link StateStore} with a persistent backend
139
+ * that supports TTL (time-to-live):
140
+ * - **Redis**: Ideal choice with built-in TTL support
141
+ * - **Database with cleanup job**: PostgreSQL/MySQL with periodic cleanup
142
+ *
143
+ * **State Lifecycle**:
144
+ * 1. Created when user starts OAuth flow (`authorize()`)
145
+ * 2. Retrieved and validated during callback
146
+ * 3. Deleted after successful or failed callback
147
+ * 4. Should expire after ~15 minutes if callback never happens
148
+ *
149
+ * @example Basic usage
150
+ * ```typescript
151
+ * import { InMemoryStateStore } from "@hypercerts-org/sdk/storage";
152
+ *
153
+ * const stateStore = new InMemoryStateStore();
154
+ *
155
+ * const sdk = new ATProtoSDK({
156
+ * oauth: { ... },
157
+ * storage: {
158
+ * stateStore, // Will warn in logs for production
159
+ * },
160
+ * });
161
+ * ```
162
+ *
163
+ * @example Testing usage
164
+ * ```typescript
165
+ * const stateStore = new InMemoryStateStore();
166
+ *
167
+ * // After tests, clean up
168
+ * stateStore.clear();
169
+ * ```
170
+ *
171
+ * @see {@link StateStore} for the interface definition
172
+ * @see {@link InMemorySessionStore} for the corresponding session store
173
+ */
174
+ class InMemoryStateStore {
175
+ constructor() {
176
+ /**
177
+ * Internal storage for OAuth state, keyed by state string.
178
+ * @internal
179
+ */
180
+ this.states = new Map();
181
+ }
182
+ /**
183
+ * Retrieves OAuth state by key.
184
+ *
185
+ * @param key - The state key (random string from authorization URL)
186
+ * @returns Promise resolving to the state, or `undefined` if not found
187
+ *
188
+ * @remarks
189
+ * The key is a cryptographically random string generated during
190
+ * the authorization request. It's included in the callback URL
191
+ * and used to retrieve the associated PKCE verifier and other data.
192
+ *
193
+ * @example
194
+ * ```typescript
195
+ * // During OAuth callback
196
+ * const state = await stateStore.get(params.get("state")!);
197
+ * if (!state) {
198
+ * throw new Error("Invalid or expired state");
199
+ * }
200
+ * ```
201
+ */
202
+ async get(key) {
203
+ return this.states.get(key);
204
+ }
205
+ /**
206
+ * Stores OAuth state temporarily.
207
+ *
208
+ * @param key - The state key to use for storage
209
+ * @param state - The OAuth state data (includes PKCE verifier, etc.)
210
+ *
211
+ * @remarks
212
+ * In production implementations, state should be stored with a TTL
213
+ * of approximately 10-15 minutes to prevent stale state accumulation.
214
+ *
215
+ * @example
216
+ * ```typescript
217
+ * // Called internally by OAuthClient during authorize()
218
+ * await stateStore.set(stateKey, {
219
+ * // PKCE code verifier, redirect URI, etc.
220
+ * });
221
+ * ```
222
+ */
223
+ async set(key, state) {
224
+ this.states.set(key, state);
225
+ }
226
+ /**
227
+ * Deletes OAuth state by key.
228
+ *
229
+ * @param key - The state key to delete
230
+ *
231
+ * @remarks
232
+ * Called after the OAuth callback is processed (whether successful or not)
233
+ * to clean up the temporary state.
234
+ *
235
+ * @example
236
+ * ```typescript
237
+ * // After processing callback
238
+ * await stateStore.del(stateKey);
239
+ * ```
240
+ */
241
+ async del(key) {
242
+ this.states.delete(key);
243
+ }
244
+ /**
245
+ * Clears all stored state.
246
+ *
247
+ * This is primarily useful for testing to ensure a clean state
248
+ * between test runs.
249
+ *
250
+ * @remarks
251
+ * This method is synchronous (not async) for convenience in test cleanup.
252
+ * In production, be careful using this as it will invalidate all
253
+ * in-progress OAuth flows.
254
+ *
255
+ * @example
256
+ * ```typescript
257
+ * // In test teardown
258
+ * afterEach(() => {
259
+ * stateStore.clear();
260
+ * });
261
+ * ```
262
+ */
263
+ clear() {
264
+ this.states.clear();
265
+ }
266
+ }
267
+
268
+ exports.InMemorySessionStore = InMemorySessionStore;
269
+ exports.InMemoryStateStore = InMemoryStateStore;
270
+ //# sourceMappingURL=storage.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.cjs","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;;;;;"}