@hypercerts-org/sdk-core 0.5.0-beta.0 → 0.7.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -8
- package/dist/index.cjs +93 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +64 -1
- package/dist/index.mjs +93 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -5
- package/.turbo/turbo-build.log +0 -40
- package/.turbo/turbo-test.log +0 -119
- package/CHANGELOG.md +0 -62
- package/eslint.config.mjs +0 -22
- package/rollup.config.js +0 -75
- package/src/auth/OAuthClient.ts +0 -497
- package/src/core/SDK.ts +0 -410
- package/src/core/config.ts +0 -243
- package/src/core/errors.ts +0 -257
- package/src/core/interfaces.ts +0 -324
- package/src/core/types.ts +0 -282
- package/src/errors.ts +0 -57
- package/src/index.ts +0 -107
- package/src/lexicons.ts +0 -64
- package/src/repository/BlobOperationsImpl.ts +0 -199
- package/src/repository/CollaboratorOperationsImpl.ts +0 -442
- package/src/repository/HypercertOperationsImpl.ts +0 -1146
- package/src/repository/LexiconRegistry.ts +0 -332
- package/src/repository/OrganizationOperationsImpl.ts +0 -282
- package/src/repository/ProfileOperationsImpl.ts +0 -281
- package/src/repository/RecordOperationsImpl.ts +0 -340
- package/src/repository/Repository.ts +0 -482
- package/src/repository/interfaces.ts +0 -909
- package/src/repository/types.ts +0 -111
- package/src/services/hypercerts/types.ts +0 -87
- package/src/storage/InMemorySessionStore.ts +0 -127
- package/src/storage/InMemoryStateStore.ts +0 -146
- package/src/storage.ts +0 -63
- package/src/testing/index.ts +0 -67
- package/src/testing/mocks.ts +0 -142
- package/src/testing/stores.ts +0 -285
- package/src/testing.ts +0 -64
- package/src/types.ts +0 -86
- package/tests/auth/OAuthClient.test.ts +0 -164
- package/tests/core/SDK.test.ts +0 -176
- package/tests/core/errors.test.ts +0 -81
- package/tests/repository/BlobOperationsImpl.test.ts +0 -155
- package/tests/repository/CollaboratorOperationsImpl.test.ts +0 -438
- package/tests/repository/HypercertOperationsImpl.test.ts +0 -652
- package/tests/repository/LexiconRegistry.test.ts +0 -192
- package/tests/repository/OrganizationOperationsImpl.test.ts +0 -240
- package/tests/repository/ProfileOperationsImpl.test.ts +0 -254
- package/tests/repository/RecordOperationsImpl.test.ts +0 -375
- package/tests/repository/Repository.test.ts +0 -149
- package/tests/utils/fixtures.ts +0 -117
- package/tests/utils/mocks.ts +0 -109
- package/tests/utils/repository-fixtures.ts +0 -78
- package/tsconfig.json +0 -11
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -30
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import { RecordOperationsImpl } from "../../src/repository/RecordOperationsImpl.js";
|
|
3
|
-
import { LexiconRegistry } from "../../src/repository/LexiconRegistry.js";
|
|
4
|
-
import { NetworkError, ValidationError } from "../../src/core/errors.js";
|
|
5
|
-
|
|
6
|
-
describe("RecordOperationsImpl", () => {
|
|
7
|
-
let mockAgent: any;
|
|
8
|
-
let lexiconRegistry: LexiconRegistry;
|
|
9
|
-
let recordOps: RecordOperationsImpl;
|
|
10
|
-
const repoDid = "did:plc:testdid123";
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
mockAgent = {
|
|
14
|
-
com: {
|
|
15
|
-
atproto: {
|
|
16
|
-
repo: {
|
|
17
|
-
createRecord: vi.fn(),
|
|
18
|
-
putRecord: vi.fn(),
|
|
19
|
-
getRecord: vi.fn(),
|
|
20
|
-
listRecords: vi.fn(),
|
|
21
|
-
deleteRecord: vi.fn(),
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
lexiconRegistry = new LexiconRegistry();
|
|
28
|
-
recordOps = new RecordOperationsImpl(mockAgent, repoDid, lexiconRegistry);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe("create", () => {
|
|
32
|
-
it("should create a record successfully", async () => {
|
|
33
|
-
mockAgent.com.atproto.repo.createRecord.mockResolvedValue({
|
|
34
|
-
success: true,
|
|
35
|
-
data: { uri: "at://did:plc:test/collection/rkey", cid: "bafyrei123" },
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const result = await recordOps.create({
|
|
39
|
-
collection: "app.bsky.feed.post",
|
|
40
|
-
record: { text: "Hello world" },
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
expect(result.uri).toBe("at://did:plc:test/collection/rkey");
|
|
44
|
-
expect(result.cid).toBe("bafyrei123");
|
|
45
|
-
expect(mockAgent.com.atproto.repo.createRecord).toHaveBeenCalledWith({
|
|
46
|
-
repo: repoDid,
|
|
47
|
-
collection: "app.bsky.feed.post",
|
|
48
|
-
record: { text: "Hello world" },
|
|
49
|
-
rkey: undefined,
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("should create a record with custom rkey", async () => {
|
|
54
|
-
mockAgent.com.atproto.repo.createRecord.mockResolvedValue({
|
|
55
|
-
success: true,
|
|
56
|
-
data: { uri: "at://did:plc:test/collection/custom-rkey", cid: "bafyrei123" },
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
await recordOps.create({
|
|
60
|
-
collection: "app.bsky.feed.post",
|
|
61
|
-
record: { text: "Hello" },
|
|
62
|
-
rkey: "custom-rkey",
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
expect(mockAgent.com.atproto.repo.createRecord).toHaveBeenCalledWith(
|
|
66
|
-
expect.objectContaining({ rkey: "custom-rkey" }),
|
|
67
|
-
);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("should throw NetworkError when API returns success: false", async () => {
|
|
71
|
-
mockAgent.com.atproto.repo.createRecord.mockResolvedValue({
|
|
72
|
-
success: false,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
await expect(
|
|
76
|
-
recordOps.create({
|
|
77
|
-
collection: "app.bsky.feed.post",
|
|
78
|
-
record: { text: "Hello" },
|
|
79
|
-
}),
|
|
80
|
-
).rejects.toThrow(NetworkError);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("should throw NetworkError when API throws", async () => {
|
|
84
|
-
mockAgent.com.atproto.repo.createRecord.mockRejectedValue(new Error("Network failure"));
|
|
85
|
-
|
|
86
|
-
await expect(
|
|
87
|
-
recordOps.create({
|
|
88
|
-
collection: "app.bsky.feed.post",
|
|
89
|
-
record: { text: "Hello" },
|
|
90
|
-
}),
|
|
91
|
-
).rejects.toThrow(NetworkError);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("should validate record against lexicon and throw ValidationError", async () => {
|
|
95
|
-
// Register a lexicon that requires specific fields
|
|
96
|
-
lexiconRegistry.register({
|
|
97
|
-
lexicon: 1,
|
|
98
|
-
id: "test.validated.record",
|
|
99
|
-
defs: {
|
|
100
|
-
main: {
|
|
101
|
-
type: "record",
|
|
102
|
-
record: {
|
|
103
|
-
type: "object",
|
|
104
|
-
required: ["requiredField"],
|
|
105
|
-
properties: {
|
|
106
|
-
requiredField: { type: "string" },
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
await expect(
|
|
114
|
-
recordOps.create({
|
|
115
|
-
collection: "test.validated.record",
|
|
116
|
-
record: { wrongField: "value" }, // Missing requiredField
|
|
117
|
-
}),
|
|
118
|
-
).rejects.toThrow(ValidationError);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe("update", () => {
|
|
123
|
-
it("should update a record successfully", async () => {
|
|
124
|
-
mockAgent.com.atproto.repo.putRecord.mockResolvedValue({
|
|
125
|
-
success: true,
|
|
126
|
-
data: { uri: "at://did:plc:test/collection/rkey", cid: "bafyrei456" },
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
const result = await recordOps.update({
|
|
130
|
-
collection: "app.bsky.feed.post",
|
|
131
|
-
rkey: "test-rkey",
|
|
132
|
-
record: { text: "Updated text" },
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
expect(result.uri).toBe("at://did:plc:test/collection/rkey");
|
|
136
|
-
expect(result.cid).toBe("bafyrei456");
|
|
137
|
-
expect(mockAgent.com.atproto.repo.putRecord).toHaveBeenCalledWith({
|
|
138
|
-
repo: repoDid,
|
|
139
|
-
collection: "app.bsky.feed.post",
|
|
140
|
-
rkey: "test-rkey",
|
|
141
|
-
record: { text: "Updated text" },
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it("should throw NetworkError when API returns success: false", async () => {
|
|
146
|
-
mockAgent.com.atproto.repo.putRecord.mockResolvedValue({
|
|
147
|
-
success: false,
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
await expect(
|
|
151
|
-
recordOps.update({
|
|
152
|
-
collection: "app.bsky.feed.post",
|
|
153
|
-
rkey: "test-rkey",
|
|
154
|
-
record: { text: "Hello" },
|
|
155
|
-
}),
|
|
156
|
-
).rejects.toThrow(NetworkError);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("should throw NetworkError when API throws", async () => {
|
|
160
|
-
mockAgent.com.atproto.repo.putRecord.mockRejectedValue(new Error("Network failure"));
|
|
161
|
-
|
|
162
|
-
await expect(
|
|
163
|
-
recordOps.update({
|
|
164
|
-
collection: "app.bsky.feed.post",
|
|
165
|
-
rkey: "test-rkey",
|
|
166
|
-
record: { text: "Hello" },
|
|
167
|
-
}),
|
|
168
|
-
).rejects.toThrow(NetworkError);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
describe("get", () => {
|
|
173
|
-
it("should get a record successfully", async () => {
|
|
174
|
-
mockAgent.com.atproto.repo.getRecord.mockResolvedValue({
|
|
175
|
-
success: true,
|
|
176
|
-
data: {
|
|
177
|
-
uri: "at://did:plc:test/collection/rkey",
|
|
178
|
-
cid: "bafyrei123",
|
|
179
|
-
value: { text: "Hello world" },
|
|
180
|
-
},
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const result = await recordOps.get({
|
|
184
|
-
collection: "app.bsky.feed.post",
|
|
185
|
-
rkey: "test-rkey",
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
expect(result.uri).toBe("at://did:plc:test/collection/rkey");
|
|
189
|
-
expect(result.cid).toBe("bafyrei123");
|
|
190
|
-
expect(result.value).toEqual({ text: "Hello world" });
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it("should handle missing cid", async () => {
|
|
194
|
-
mockAgent.com.atproto.repo.getRecord.mockResolvedValue({
|
|
195
|
-
success: true,
|
|
196
|
-
data: {
|
|
197
|
-
uri: "at://did:plc:test/collection/rkey",
|
|
198
|
-
cid: undefined,
|
|
199
|
-
value: { text: "Hello" },
|
|
200
|
-
},
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
const result = await recordOps.get({
|
|
204
|
-
collection: "app.bsky.feed.post",
|
|
205
|
-
rkey: "test-rkey",
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
expect(result.cid).toBe("");
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it("should throw NetworkError when API returns success: false", async () => {
|
|
212
|
-
mockAgent.com.atproto.repo.getRecord.mockResolvedValue({
|
|
213
|
-
success: false,
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
await expect(
|
|
217
|
-
recordOps.get({
|
|
218
|
-
collection: "app.bsky.feed.post",
|
|
219
|
-
rkey: "test-rkey",
|
|
220
|
-
}),
|
|
221
|
-
).rejects.toThrow(NetworkError);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it("should throw NetworkError when API throws", async () => {
|
|
225
|
-
mockAgent.com.atproto.repo.getRecord.mockRejectedValue(new Error("Not found"));
|
|
226
|
-
|
|
227
|
-
await expect(
|
|
228
|
-
recordOps.get({
|
|
229
|
-
collection: "app.bsky.feed.post",
|
|
230
|
-
rkey: "test-rkey",
|
|
231
|
-
}),
|
|
232
|
-
).rejects.toThrow(NetworkError);
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
describe("list", () => {
|
|
237
|
-
it("should list records successfully", async () => {
|
|
238
|
-
mockAgent.com.atproto.repo.listRecords.mockResolvedValue({
|
|
239
|
-
success: true,
|
|
240
|
-
data: {
|
|
241
|
-
records: [
|
|
242
|
-
{ uri: "at://did:plc:test/collection/1", cid: "bafyrei1", value: { text: "First" } },
|
|
243
|
-
{ uri: "at://did:plc:test/collection/2", cid: "bafyrei2", value: { text: "Second" } },
|
|
244
|
-
],
|
|
245
|
-
cursor: "next-cursor",
|
|
246
|
-
},
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
const result = await recordOps.list({
|
|
250
|
-
collection: "app.bsky.feed.post",
|
|
251
|
-
limit: 10,
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
expect(result.records).toHaveLength(2);
|
|
255
|
-
expect(result.cursor).toBe("next-cursor");
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it("should handle empty results", async () => {
|
|
259
|
-
mockAgent.com.atproto.repo.listRecords.mockResolvedValue({
|
|
260
|
-
success: true,
|
|
261
|
-
data: {
|
|
262
|
-
records: [],
|
|
263
|
-
cursor: undefined,
|
|
264
|
-
},
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
const result = await recordOps.list({
|
|
268
|
-
collection: "app.bsky.feed.post",
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
expect(result.records).toHaveLength(0);
|
|
272
|
-
expect(result.cursor).toBeUndefined();
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it("should handle null records", async () => {
|
|
276
|
-
mockAgent.com.atproto.repo.listRecords.mockResolvedValue({
|
|
277
|
-
success: true,
|
|
278
|
-
data: {
|
|
279
|
-
records: null,
|
|
280
|
-
cursor: null,
|
|
281
|
-
},
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
const result = await recordOps.list({
|
|
285
|
-
collection: "app.bsky.feed.post",
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
expect(result.records).toHaveLength(0);
|
|
289
|
-
expect(result.cursor).toBeUndefined();
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
it("should pass pagination parameters", async () => {
|
|
293
|
-
mockAgent.com.atproto.repo.listRecords.mockResolvedValue({
|
|
294
|
-
success: true,
|
|
295
|
-
data: { records: [], cursor: undefined },
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
await recordOps.list({
|
|
299
|
-
collection: "app.bsky.feed.post",
|
|
300
|
-
limit: 50,
|
|
301
|
-
cursor: "prev-cursor",
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
expect(mockAgent.com.atproto.repo.listRecords).toHaveBeenCalledWith({
|
|
305
|
-
repo: repoDid,
|
|
306
|
-
collection: "app.bsky.feed.post",
|
|
307
|
-
limit: 50,
|
|
308
|
-
cursor: "prev-cursor",
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it("should throw NetworkError when API returns success: false", async () => {
|
|
313
|
-
mockAgent.com.atproto.repo.listRecords.mockResolvedValue({
|
|
314
|
-
success: false,
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
await expect(
|
|
318
|
-
recordOps.list({ collection: "app.bsky.feed.post" }),
|
|
319
|
-
).rejects.toThrow(NetworkError);
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
it("should throw NetworkError when API throws", async () => {
|
|
323
|
-
mockAgent.com.atproto.repo.listRecords.mockRejectedValue(new Error("Network failure"));
|
|
324
|
-
|
|
325
|
-
await expect(
|
|
326
|
-
recordOps.list({ collection: "app.bsky.feed.post" }),
|
|
327
|
-
).rejects.toThrow(NetworkError);
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
describe("delete", () => {
|
|
332
|
-
it("should delete a record successfully", async () => {
|
|
333
|
-
mockAgent.com.atproto.repo.deleteRecord.mockResolvedValue({
|
|
334
|
-
success: true,
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
await expect(
|
|
338
|
-
recordOps.delete({
|
|
339
|
-
collection: "app.bsky.feed.post",
|
|
340
|
-
rkey: "test-rkey",
|
|
341
|
-
}),
|
|
342
|
-
).resolves.toBeUndefined();
|
|
343
|
-
|
|
344
|
-
expect(mockAgent.com.atproto.repo.deleteRecord).toHaveBeenCalledWith({
|
|
345
|
-
repo: repoDid,
|
|
346
|
-
collection: "app.bsky.feed.post",
|
|
347
|
-
rkey: "test-rkey",
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it("should throw NetworkError when API returns success: false", async () => {
|
|
352
|
-
mockAgent.com.atproto.repo.deleteRecord.mockResolvedValue({
|
|
353
|
-
success: false,
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
await expect(
|
|
357
|
-
recordOps.delete({
|
|
358
|
-
collection: "app.bsky.feed.post",
|
|
359
|
-
rkey: "test-rkey",
|
|
360
|
-
}),
|
|
361
|
-
).rejects.toThrow(NetworkError);
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
it("should throw NetworkError when API throws", async () => {
|
|
365
|
-
mockAgent.com.atproto.repo.deleteRecord.mockRejectedValue(new Error("Network failure"));
|
|
366
|
-
|
|
367
|
-
await expect(
|
|
368
|
-
recordOps.delete({
|
|
369
|
-
collection: "app.bsky.feed.post",
|
|
370
|
-
rkey: "test-rkey",
|
|
371
|
-
}),
|
|
372
|
-
).rejects.toThrow(NetworkError);
|
|
373
|
-
});
|
|
374
|
-
});
|
|
375
|
-
});
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
import { SDSRequiredError } from "../../src/core/errors.js";
|
|
3
|
-
import { LexiconRegistry } from "../../src/repository/LexiconRegistry.js";
|
|
4
|
-
import { Repository } from "../../src/repository/Repository.js";
|
|
5
|
-
import { createMockSession } from "../utils/repository-fixtures.js";
|
|
6
|
-
|
|
7
|
-
describe("Repository", () => {
|
|
8
|
-
let mockSession: ReturnType<typeof createMockSession>;
|
|
9
|
-
let lexiconRegistry: LexiconRegistry;
|
|
10
|
-
let repository: Repository;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
mockSession = createMockSession();
|
|
14
|
-
lexiconRegistry = new LexiconRegistry();
|
|
15
|
-
repository = new Repository(
|
|
16
|
-
mockSession,
|
|
17
|
-
"https://pds.example.com",
|
|
18
|
-
mockSession.did,
|
|
19
|
-
lexiconRegistry,
|
|
20
|
-
false, // Not SDS
|
|
21
|
-
);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe("constructor", () => {
|
|
25
|
-
it("should create a repository with correct properties", () => {
|
|
26
|
-
expect(repository.did).toBe(mockSession.did);
|
|
27
|
-
expect(repository.isSDS).toBe(false);
|
|
28
|
-
expect(repository.getServerUrl()).toBe("https://pds.example.com");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("should create an SDS repository", () => {
|
|
32
|
-
const sdsRepo = new Repository(
|
|
33
|
-
mockSession,
|
|
34
|
-
"https://sds.example.com",
|
|
35
|
-
mockSession.did,
|
|
36
|
-
lexiconRegistry,
|
|
37
|
-
true, // SDS
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
expect(sdsRepo.isSDS).toBe(true);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe("repo()", () => {
|
|
45
|
-
it("should create a new repository for a different DID", () => {
|
|
46
|
-
const otherDid = "did:plc:otherdid123456789";
|
|
47
|
-
const otherRepo = repository.repo(otherDid);
|
|
48
|
-
|
|
49
|
-
expect(otherRepo.did).toBe(otherDid);
|
|
50
|
-
expect(otherRepo.getServerUrl()).toBe(repository.getServerUrl());
|
|
51
|
-
expect(otherRepo.isSDS).toBe(repository.isSDS);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe("records", () => {
|
|
56
|
-
it("should return a RecordOperations instance", () => {
|
|
57
|
-
const records = repository.records;
|
|
58
|
-
expect(records).toBeDefined();
|
|
59
|
-
expect(typeof records.create).toBe("function");
|
|
60
|
-
expect(typeof records.update).toBe("function");
|
|
61
|
-
expect(typeof records.get).toBe("function");
|
|
62
|
-
expect(typeof records.list).toBe("function");
|
|
63
|
-
expect(typeof records.delete).toBe("function");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("should return the same instance on subsequent calls", () => {
|
|
67
|
-
const records1 = repository.records;
|
|
68
|
-
const records2 = repository.records;
|
|
69
|
-
expect(records1).toBe(records2);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
describe("blobs", () => {
|
|
74
|
-
it("should return a BlobOperations instance", () => {
|
|
75
|
-
const blobs = repository.blobs;
|
|
76
|
-
expect(blobs).toBeDefined();
|
|
77
|
-
expect(typeof blobs.upload).toBe("function");
|
|
78
|
-
expect(typeof blobs.get).toBe("function");
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
describe("profile", () => {
|
|
83
|
-
it("should return a ProfileOperations instance", () => {
|
|
84
|
-
const profile = repository.profile;
|
|
85
|
-
expect(profile).toBeDefined();
|
|
86
|
-
expect(typeof profile.get).toBe("function");
|
|
87
|
-
expect(typeof profile.update).toBe("function");
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
describe("hypercerts", () => {
|
|
92
|
-
it("should return a HypercertOperations instance", () => {
|
|
93
|
-
const hypercerts = repository.hypercerts;
|
|
94
|
-
expect(hypercerts).toBeDefined();
|
|
95
|
-
expect(typeof hypercerts.create).toBe("function");
|
|
96
|
-
expect(typeof hypercerts.update).toBe("function");
|
|
97
|
-
expect(typeof hypercerts.get).toBe("function");
|
|
98
|
-
expect(typeof hypercerts.list).toBe("function");
|
|
99
|
-
expect(typeof hypercerts.delete).toBe("function");
|
|
100
|
-
expect(typeof hypercerts.attachLocation).toBe("function");
|
|
101
|
-
expect(typeof hypercerts.addEvidence).toBe("function");
|
|
102
|
-
expect(typeof hypercerts.addContribution).toBe("function");
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
describe("collaborators (SDS only)", () => {
|
|
107
|
-
it("should throw SDSRequiredError on PDS repository", () => {
|
|
108
|
-
expect(() => repository.collaborators).toThrow(SDSRequiredError);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("should return CollaboratorOperations on SDS repository", () => {
|
|
112
|
-
const sdsRepo = new Repository(
|
|
113
|
-
mockSession,
|
|
114
|
-
"https://sds.example.com",
|
|
115
|
-
mockSession.did,
|
|
116
|
-
lexiconRegistry,
|
|
117
|
-
true,
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
const collaborators = sdsRepo.collaborators;
|
|
121
|
-
expect(collaborators).toBeDefined();
|
|
122
|
-
expect(typeof collaborators.grant).toBe("function");
|
|
123
|
-
expect(typeof collaborators.revoke).toBe("function");
|
|
124
|
-
expect(typeof collaborators.list).toBe("function");
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
describe("organizations (SDS only)", () => {
|
|
129
|
-
it("should throw SDSRequiredError on PDS repository", () => {
|
|
130
|
-
expect(() => repository.organizations).toThrow(SDSRequiredError);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("should return OrganizationOperations on SDS repository", () => {
|
|
134
|
-
const sdsRepo = new Repository(
|
|
135
|
-
mockSession,
|
|
136
|
-
"https://sds.example.com",
|
|
137
|
-
mockSession.did,
|
|
138
|
-
lexiconRegistry,
|
|
139
|
-
true,
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
const organizations = sdsRepo.organizations;
|
|
143
|
-
expect(organizations).toBeDefined();
|
|
144
|
-
expect(typeof organizations.create).toBe("function");
|
|
145
|
-
expect(typeof organizations.get).toBe("function");
|
|
146
|
-
expect(typeof organizations.list).toBe("function");
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
});
|
package/tests/utils/fixtures.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { generateKeyPair, exportJWK } from "jose";
|
|
2
|
-
import { randomUUID } from "crypto";
|
|
3
|
-
import type { ATProtoSDKConfig } from "../../src/core/config.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Generate a valid test JWK private key using jose
|
|
7
|
-
* Creates a real ES256 keypair for testing
|
|
8
|
-
*/
|
|
9
|
-
let cachedTestJWK: string | null = null;
|
|
10
|
-
|
|
11
|
-
export async function createTestJWK(): Promise<string> {
|
|
12
|
-
if (cachedTestJWK) {
|
|
13
|
-
return cachedTestJWK;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// Generate ES256 keypair
|
|
17
|
-
const { privateKey } = await generateKeyPair("ES256", {
|
|
18
|
-
extractable: true,
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// Generate a unique key ID
|
|
22
|
-
const kid = randomUUID();
|
|
23
|
-
|
|
24
|
-
// Export private key to JWK format
|
|
25
|
-
const privateJWK = await exportJWK(privateKey);
|
|
26
|
-
privateJWK.kid = kid;
|
|
27
|
-
privateJWK.alg = "ES256";
|
|
28
|
-
// Use key_ops instead of 'use' (deprecated)
|
|
29
|
-
privateJWK.key_ops = ["sign"];
|
|
30
|
-
|
|
31
|
-
// Create keyset
|
|
32
|
-
const privateKeyset = {
|
|
33
|
-
keys: [privateJWK],
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
cachedTestJWK = JSON.stringify(privateKeyset);
|
|
37
|
-
return cachedTestJWK;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Synchronous version that uses a pre-generated JWK
|
|
42
|
-
* For tests that don't need async setup
|
|
43
|
-
*/
|
|
44
|
-
export function createTestJWKSync(): string {
|
|
45
|
-
// Return a minimal valid structure - tests will need to handle async JWK generation
|
|
46
|
-
// For most tests, we can use a cached version
|
|
47
|
-
if (cachedTestJWK) {
|
|
48
|
-
return cachedTestJWK;
|
|
49
|
-
}
|
|
50
|
-
// Fallback - will fail in actual OAuth client but allows SDK construction tests
|
|
51
|
-
return JSON.stringify({
|
|
52
|
-
keys: [
|
|
53
|
-
{
|
|
54
|
-
kid: "test-key-1",
|
|
55
|
-
kty: "EC",
|
|
56
|
-
crv: "P-256",
|
|
57
|
-
x: "test-x",
|
|
58
|
-
y: "test-y",
|
|
59
|
-
d: "test-d",
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Create a minimal valid SDK configuration for testing
|
|
67
|
-
* Note: Storage is now optional - will use in-memory defaults if not provided
|
|
68
|
-
*/
|
|
69
|
-
export function createTestConfig(overrides?: Partial<ATProtoSDKConfig>): ATProtoSDKConfig {
|
|
70
|
-
return {
|
|
71
|
-
oauth: {
|
|
72
|
-
clientId: "https://example.com/atproto-client-metadata.json",
|
|
73
|
-
redirectUri: "https://example.com/api/auth/atproto/callback",
|
|
74
|
-
scope: "atproto",
|
|
75
|
-
jwksUri: "https://example.com/jwks.json",
|
|
76
|
-
jwkPrivate: createTestJWKSync(),
|
|
77
|
-
...overrides?.oauth,
|
|
78
|
-
},
|
|
79
|
-
servers: {
|
|
80
|
-
pds: "https://bsky.social",
|
|
81
|
-
...overrides?.servers,
|
|
82
|
-
},
|
|
83
|
-
storage: overrides?.storage,
|
|
84
|
-
cache: overrides?.cache,
|
|
85
|
-
logger: overrides?.logger,
|
|
86
|
-
fetch: overrides?.fetch,
|
|
87
|
-
timeouts: overrides?.timeouts,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Create a test config with a real JWK (async)
|
|
93
|
-
* Use this for tests that need actual OAuth client functionality
|
|
94
|
-
*/
|
|
95
|
-
export async function createTestConfigAsync(overrides?: Partial<ATProtoSDKConfig>): Promise<ATProtoSDKConfig> {
|
|
96
|
-
const jwkPrivate = await createTestJWK();
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
oauth: {
|
|
100
|
-
clientId: "https://example.com/atproto-client-metadata.json",
|
|
101
|
-
redirectUri: "https://example.com/api/auth/atproto/callback",
|
|
102
|
-
scope: "atproto",
|
|
103
|
-
jwksUri: "https://example.com/jwks.json",
|
|
104
|
-
jwkPrivate,
|
|
105
|
-
...overrides?.oauth,
|
|
106
|
-
},
|
|
107
|
-
servers: {
|
|
108
|
-
pds: "https://bsky.social",
|
|
109
|
-
...overrides?.servers,
|
|
110
|
-
},
|
|
111
|
-
storage: overrides?.storage,
|
|
112
|
-
cache: overrides?.cache,
|
|
113
|
-
logger: overrides?.logger,
|
|
114
|
-
fetch: overrides?.fetch,
|
|
115
|
-
timeouts: overrides?.timeouts,
|
|
116
|
-
};
|
|
117
|
-
}
|