@atproto/lex-client 0.0.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 (111) hide show
  1. package/dist/agent.d.ts +33 -0
  2. package/dist/agent.d.ts.map +1 -0
  3. package/dist/agent.js +21 -0
  4. package/dist/agent.js.map +1 -0
  5. package/dist/client.d.ts +456 -0
  6. package/dist/client.d.ts.map +1 -0
  7. package/dist/client.js +236 -0
  8. package/dist/client.js.map +1 -0
  9. package/dist/error.d.ts +70 -0
  10. package/dist/error.d.ts.map +1 -0
  11. package/dist/error.js +98 -0
  12. package/dist/error.js.map +1 -0
  13. package/dist/index.d.ts +7 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +10 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/lexicons/com/atproto/repo/createRecord.d.ts +3 -0
  18. package/dist/lexicons/com/atproto/repo/createRecord.d.ts.map +1 -0
  19. package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts +100 -0
  20. package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts.map +1 -0
  21. package/dist/lexicons/com/atproto/repo/createRecord.defs.js +42 -0
  22. package/dist/lexicons/com/atproto/repo/createRecord.defs.js.map +1 -0
  23. package/dist/lexicons/com/atproto/repo/createRecord.js +10 -0
  24. package/dist/lexicons/com/atproto/repo/createRecord.js.map +1 -0
  25. package/dist/lexicons/com/atproto/repo/defs.d.ts +3 -0
  26. package/dist/lexicons/com/atproto/repo/defs.d.ts.map +1 -0
  27. package/dist/lexicons/com/atproto/repo/defs.defs.d.ts +12 -0
  28. package/dist/lexicons/com/atproto/repo/defs.defs.d.ts.map +1 -0
  29. package/dist/lexicons/com/atproto/repo/defs.defs.js +16 -0
  30. package/dist/lexicons/com/atproto/repo/defs.defs.js.map +1 -0
  31. package/dist/lexicons/com/atproto/repo/defs.js +10 -0
  32. package/dist/lexicons/com/atproto/repo/defs.js.map +1 -0
  33. package/dist/lexicons/com/atproto/repo/deleteRecord.d.ts +3 -0
  34. package/dist/lexicons/com/atproto/repo/deleteRecord.d.ts.map +1 -0
  35. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts +70 -0
  36. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts.map +1 -0
  37. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js +33 -0
  38. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js.map +1 -0
  39. package/dist/lexicons/com/atproto/repo/deleteRecord.js +10 -0
  40. package/dist/lexicons/com/atproto/repo/deleteRecord.js.map +1 -0
  41. package/dist/lexicons/com/atproto/repo/getRecord.d.ts +3 -0
  42. package/dist/lexicons/com/atproto/repo/getRecord.d.ts.map +1 -0
  43. package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts +82 -0
  44. package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts.map +1 -0
  45. package/dist/lexicons/com/atproto/repo/getRecord.defs.js +30 -0
  46. package/dist/lexicons/com/atproto/repo/getRecord.defs.js.map +1 -0
  47. package/dist/lexicons/com/atproto/repo/getRecord.js +10 -0
  48. package/dist/lexicons/com/atproto/repo/getRecord.js.map +1 -0
  49. package/dist/lexicons/com/atproto/repo/listRecords.d.ts +3 -0
  50. package/dist/lexicons/com/atproto/repo/listRecords.d.ts.map +1 -0
  51. package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts +75 -0
  52. package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts.map +1 -0
  53. package/dist/lexicons/com/atproto/repo/listRecords.defs.js +42 -0
  54. package/dist/lexicons/com/atproto/repo/listRecords.defs.js.map +1 -0
  55. package/dist/lexicons/com/atproto/repo/listRecords.js +10 -0
  56. package/dist/lexicons/com/atproto/repo/listRecords.js.map +1 -0
  57. package/dist/lexicons/com/atproto/repo/putRecord.d.ts +3 -0
  58. package/dist/lexicons/com/atproto/repo/putRecord.d.ts.map +1 -0
  59. package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts +110 -0
  60. package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts.map +1 -0
  61. package/dist/lexicons/com/atproto/repo/putRecord.defs.js +46 -0
  62. package/dist/lexicons/com/atproto/repo/putRecord.defs.js.map +1 -0
  63. package/dist/lexicons/com/atproto/repo/putRecord.js +10 -0
  64. package/dist/lexicons/com/atproto/repo/putRecord.js.map +1 -0
  65. package/dist/lexicons/com/atproto/repo/uploadBlob.d.ts +3 -0
  66. package/dist/lexicons/com/atproto/repo/uploadBlob.d.ts.map +1 -0
  67. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts +25 -0
  68. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts.map +1 -0
  69. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js +22 -0
  70. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js.map +1 -0
  71. package/dist/lexicons/com/atproto/repo/uploadBlob.js +10 -0
  72. package/dist/lexicons/com/atproto/repo/uploadBlob.js.map +1 -0
  73. package/dist/lexicons/com/atproto/repo.d.ts +8 -0
  74. package/dist/lexicons/com/atproto/repo.d.ts.map +1 -0
  75. package/dist/lexicons/com/atproto/repo.js +15 -0
  76. package/dist/lexicons/com/atproto/repo.js.map +1 -0
  77. package/dist/lexicons/com/atproto.d.ts +2 -0
  78. package/dist/lexicons/com/atproto.d.ts.map +1 -0
  79. package/dist/lexicons/com/atproto.js +9 -0
  80. package/dist/lexicons/com/atproto.js.map +1 -0
  81. package/dist/lexicons/com.d.ts +2 -0
  82. package/dist/lexicons/com.d.ts.map +1 -0
  83. package/dist/lexicons/com.js +9 -0
  84. package/dist/lexicons/com.js.map +1 -0
  85. package/dist/response.d.ts +21 -0
  86. package/dist/response.d.ts.map +1 -0
  87. package/dist/response.js +31 -0
  88. package/dist/response.js.map +1 -0
  89. package/dist/types.d.ts +17 -0
  90. package/dist/types.d.ts.map +1 -0
  91. package/dist/types.js +7 -0
  92. package/dist/types.js.map +1 -0
  93. package/dist/xrpc.d.ts +37 -0
  94. package/dist/xrpc.d.ts.map +1 -0
  95. package/dist/xrpc.js +185 -0
  96. package/dist/xrpc.js.map +1 -0
  97. package/jest.config.js +5 -0
  98. package/package.json +46 -0
  99. package/scripts/lex-build.mjs +40 -0
  100. package/src/agent.ts +63 -0
  101. package/src/client.ts +513 -0
  102. package/src/error.ts +154 -0
  103. package/src/index.ts +6 -0
  104. package/src/response.ts +42 -0
  105. package/src/types.ts +21 -0
  106. package/src/xrpc.ts +335 -0
  107. package/tests/client.test.ts +370 -0
  108. package/tsconfig.build.json +12 -0
  109. package/tsconfig.build.tsbuildinfo +1 -0
  110. package/tsconfig.json +7 -0
  111. package/tsconfig.tests.json +12 -0
@@ -0,0 +1,370 @@
1
+ import { LexValue, cidForLex } from '@atproto/lex-cbor'
2
+ import { lexParse } from '@atproto/lex-json'
3
+ import { Action, Client } from '..'
4
+ import * as app from './lexicons/app.js'
5
+ import * as com from './lexicons/com.js'
6
+
7
+ type Preference = app.bsky.actor.defs.Preferences[number]
8
+
9
+ describe('utils', () => {
10
+ describe('TypedObjectSchema', () => {
11
+ it('overrides $type when building an object', () => {
12
+ const _r = app.bsky.actor.defs.adultContentPref.build({
13
+ $type: 'foo',
14
+ enabled: true,
15
+ })
16
+ expect(_r.$type).toBe('app.bsky.actor.defs#adultContentPref')
17
+ })
18
+ })
19
+ })
20
+
21
+ describe('Client', () => {
22
+ describe('actions', () => {
23
+ it('updatePreferences', async () => {
24
+ const fetchHandler = jest.fn(
25
+ async (url: string, init?: RequestInit): Promise<Response> => {
26
+ if (url === '/xrpc/app.bsky.actor.getPreferences') {
27
+ return new Response(
28
+ JSON.stringify({ preferences: storedPreferences }),
29
+ {
30
+ status: 200,
31
+ headers: { 'Content-Type': 'application/json' },
32
+ },
33
+ )
34
+ } else if (url === '/xrpc/app.bsky.actor.putPreferences') {
35
+ expect(typeof init?.body).toBe('string')
36
+ const { preferences } =
37
+ app.bsky.actor.putPreferences.$input.schema.parse(
38
+ lexParse(init?.body as string),
39
+ )
40
+ storedPreferences = preferences
41
+ return new Response(null, { status: 204 })
42
+ } else {
43
+ return new Response('Not Found', { status: 404 })
44
+ }
45
+ },
46
+ )
47
+
48
+ const client = new Client({ fetchHandler })
49
+
50
+ const updatePreferences: Action<
51
+ (pref: Preference[]) => false | Preference[],
52
+ Preference[]
53
+ > = async function (client, updatePreferences, options) {
54
+ const data = await client.call(app.bsky.actor.getPreferences, options)
55
+
56
+ const preferences = updatePreferences(data.preferences)
57
+ if (preferences === false) return data.preferences
58
+
59
+ options?.signal?.throwIfAborted()
60
+
61
+ await client.call(
62
+ app.bsky.actor.putPreferences,
63
+ { preferences },
64
+ options,
65
+ )
66
+
67
+ return preferences
68
+ }
69
+
70
+ const upsertPreference: Action<Preference, Preference[]> =
71
+ async function (client, pref, options) {
72
+ return updatePreferences(
73
+ client,
74
+ (prefs) => [...prefs.filter((p) => p.$type !== pref.$type), pref],
75
+ options,
76
+ )
77
+ }
78
+
79
+ let storedPreferences: Preference[] = [
80
+ app.bsky.actor.defs.adultContentPref.build({
81
+ enabled: false,
82
+ }),
83
+ app.bsky.actor.defs.contentLabelPref.build({
84
+ label: 'my-label',
85
+ visibility: 'warn',
86
+ }),
87
+ ]
88
+
89
+ expect(fetchHandler).toHaveBeenCalledTimes(0)
90
+ expect(storedPreferences).toEqual([
91
+ {
92
+ $type: 'app.bsky.actor.defs#adultContentPref',
93
+ enabled: false,
94
+ },
95
+ {
96
+ $type: 'app.bsky.actor.defs#contentLabelPref',
97
+ label: 'my-label',
98
+ visibility: 'warn',
99
+ },
100
+ ])
101
+
102
+ // Upsert adult content preference
103
+ await client.call(
104
+ upsertPreference,
105
+ app.bsky.actor.defs.adultContentPref.build({
106
+ enabled: true,
107
+ }),
108
+ )
109
+
110
+ expect(fetchHandler).toHaveBeenCalledTimes(2)
111
+ expect(storedPreferences).toEqual([
112
+ {
113
+ $type: 'app.bsky.actor.defs#contentLabelPref',
114
+ label: 'my-label',
115
+ visibility: 'warn',
116
+ },
117
+ {
118
+ $type: 'app.bsky.actor.defs#adultContentPref',
119
+ enabled: true,
120
+ },
121
+ ])
122
+
123
+ // @ts-expect-error invalid preference value
124
+ await client.call(upsertPreference, {
125
+ $type: 'app.bsky.actor.defs#adultContentPref',
126
+ // enabled: true,
127
+ })
128
+
129
+ expect(fetchHandler).toHaveBeenCalledTimes(4)
130
+ expect(storedPreferences).toEqual([
131
+ {
132
+ $type: 'app.bsky.actor.defs#contentLabelPref',
133
+ label: 'my-label',
134
+ visibility: 'warn',
135
+ },
136
+ {
137
+ $type: 'app.bsky.actor.defs#adultContentPref',
138
+ enabled: false, // "false" default will be enforced when parsing the body
139
+ },
140
+ ])
141
+
142
+ expect(async () => {
143
+ // @ts-expect-error invalid preference value
144
+ await client.call(upsertPreference, {
145
+ $type: 'app.bsky.actor.defs#adultContentPref',
146
+ enabled: 'not-a-boolean',
147
+ })
148
+ }).rejects.toThrow()
149
+ })
150
+ })
151
+
152
+ describe('query', () => {
153
+ it('allows perfoming a GET request and parsing the response', async () => {
154
+ const fetchHandler = jest.fn(
155
+ async (url: string, init?: RequestInit): Promise<Response> => {
156
+ expect(url).toBe('/xrpc/app.bsky.actor.getPreferences')
157
+ expect(init?.method).toBe('GET')
158
+
159
+ const responsePayload = {
160
+ preferences: [
161
+ {
162
+ $type: 'app.bsky.actor.defs#adultContentPref',
163
+ enabled: false,
164
+ },
165
+ {
166
+ $type: 'app.bsky.actor.defs#someOtherPref',
167
+ otherField: 'some value',
168
+ },
169
+ ],
170
+ }
171
+
172
+ return new Response(JSON.stringify(responsePayload), {
173
+ status: 200,
174
+ headers: { 'Content-Type': 'application/json' },
175
+ })
176
+ },
177
+ )
178
+
179
+ const client = new Client({ fetchHandler })
180
+
181
+ const { preferences } = await client.call(app.bsky.actor.getPreferences)
182
+
183
+ expect(preferences).toEqual([
184
+ {
185
+ $type: 'app.bsky.actor.defs#adultContentPref',
186
+ enabled: false,
187
+ },
188
+ {
189
+ $type: 'app.bsky.actor.defs#someOtherPref',
190
+ otherField: 'some value',
191
+ },
192
+ ])
193
+
194
+ expect(fetchHandler).toHaveBeenCalledTimes(1)
195
+
196
+ const adultContentPref = preferences.find((p) =>
197
+ app.bsky.actor.defs.adultContentPref.isTypeOf(p),
198
+ )
199
+
200
+ expect(adultContentPref).toEqual({
201
+ $type: 'app.bsky.actor.defs#adultContentPref',
202
+ enabled: false,
203
+ })
204
+ })
205
+ })
206
+
207
+ describe('records', () => {
208
+ it('allows creating records', async () => {
209
+ let currentTid = 0
210
+ // Only works 8 times
211
+ const nextTid = jest.fn(() => `2222222222${2 + currentTid++}22`)
212
+
213
+ const did = 'did:plc:alice'
214
+ const fetchHandler = jest.fn(
215
+ async (url: string, init?: RequestInit): Promise<Response> => {
216
+ expect(url).toBe('/xrpc/com.atproto.repo.createRecord')
217
+ expect(init?.method).toBe('POST')
218
+ expect(typeof init?.body).toBe('string')
219
+ const payload = com.atproto.repo.createRecord.main.input.schema.parse(
220
+ lexParse(init?.body as string),
221
+ )
222
+
223
+ expect(payload).toMatchObject({
224
+ repo: did,
225
+ collection: expect.any(String),
226
+ record: expect.any(Object),
227
+ })
228
+
229
+ const rkey = payload.rkey || nextTid()
230
+ const cid = await cidForLex(payload.record as LexValue)
231
+
232
+ const responsePayload: com.atproto.repo.createRecord.Output = {
233
+ cid: cid.toString(),
234
+ uri: `at://${payload.repo}/${payload.collection}/${rkey}`,
235
+ }
236
+
237
+ return new Response(JSON.stringify(responsePayload), {
238
+ status: 200,
239
+ headers: { 'Content-Type': 'application/json' },
240
+ })
241
+ },
242
+ )
243
+
244
+ const client = new Client({ fetchHandler, did })
245
+
246
+ await expect(async () => {
247
+ await client.create(
248
+ app.bsky.feed.generator,
249
+ {
250
+ // @ts-expect-error invalid DID
251
+ did: 'not-a-did',
252
+ displayName: 'Alice Generator',
253
+ createdAt: '2024-01-01T00:00:00Z',
254
+ },
255
+ {
256
+ rkey: 'alice-generator',
257
+ validate: true,
258
+ },
259
+ )
260
+ }).rejects.toThrow()
261
+
262
+ // validate performs schema validation before making the request
263
+ expect(fetchHandler).toHaveBeenCalledTimes(0)
264
+
265
+ const newGenerator = await client.create(
266
+ app.bsky.feed.generator,
267
+ {
268
+ did,
269
+ displayName: 'Alice Generator',
270
+ createdAt: '2024-01-01T00:00:00Z',
271
+ },
272
+ {
273
+ rkey: 'alice-generator',
274
+ validate: true,
275
+ },
276
+ )
277
+
278
+ expect(fetchHandler).toHaveBeenCalledTimes(1)
279
+ expect(newGenerator.cid).toBe(
280
+ 'bafyreihx5eurnmsnj6ulfby3icl4ebh6pliwuqaze25z4ejitnt23b4vw4',
281
+ )
282
+
283
+ const aliceGenerator = await client.create(
284
+ // @ts-expect-error an "rkey" option is required for feed generator records
285
+ app.bsky.feed.generator,
286
+ {
287
+ did: 'no-a-did',
288
+ displayName: 'Alice Generator',
289
+ createdAt: new Date().toISOString(),
290
+ },
291
+ )
292
+
293
+ expect(nextTid).toHaveBeenCalledTimes(1)
294
+ expect(aliceGenerator.uri).toBe(
295
+ `at://${did}/app.bsky.feed.generator/${'2'.repeat(13)}`,
296
+ )
297
+
298
+ const newProfile = await client.create(app.bsky.actor.profile, {
299
+ displayName: 'Alice',
300
+ })
301
+
302
+ expect(nextTid).toHaveBeenCalledTimes(1)
303
+ expect(fetchHandler).toHaveBeenCalledTimes(3)
304
+ expect(newProfile.uri).toBe(
305
+ 'at://did:plc:alice/app.bsky.actor.profile/self',
306
+ )
307
+
308
+ const newPost = await client.create(app.bsky.feed.post, {
309
+ text: 'Hello, world!',
310
+ createdAt: new Date().toISOString(),
311
+ })
312
+
313
+ expect(nextTid).toHaveBeenCalledTimes(2)
314
+ expect(fetchHandler).toHaveBeenCalledTimes(4)
315
+ expect(newPost.uri).toBe(`at://${did}/app.bsky.feed.post/2222222222322`)
316
+ })
317
+
318
+ it('allows fetching records', async () => {
319
+ const did = 'did:plc:alice'
320
+ const fetchHandler = jest.fn(
321
+ async (url: string, init?: RequestInit): Promise<Response> => {
322
+ expect(init?.method).toBe('GET')
323
+ const urlObj = new URL(url, 'https://example.com')
324
+ expect(urlObj.pathname).toBe('/xrpc/com.atproto.repo.getRecord')
325
+
326
+ const repo = urlObj.searchParams.get('repo')
327
+ const collection = urlObj.searchParams.get('collection')
328
+ const rkey = urlObj.searchParams.get('rkey')
329
+
330
+ expect(repo).toBe(did)
331
+ expect(collection).toBe(app.bsky.feed.post.$type)
332
+ expect(rkey).toBe('2222222222222')
333
+
334
+ const record = app.bsky.feed.post.$build({
335
+ text: 'This is an old post',
336
+ createdAt: '2024-01-01T00:00:00Z',
337
+ })
338
+
339
+ const cid = await cidForLex(record)
340
+
341
+ const responsePayload: com.atproto.repo.getRecord.Output = {
342
+ cid: cid.toString(),
343
+ uri: `at://${repo!}/${collection!}/${rkey!}` as any,
344
+ value: record,
345
+ }
346
+
347
+ return new Response(JSON.stringify(responsePayload), {
348
+ status: 200,
349
+ headers: { 'Content-Type': 'application/json' },
350
+ })
351
+ },
352
+ )
353
+
354
+ const client = new Client({ fetchHandler, did })
355
+
356
+ const { value: post } = await client.get(app.bsky.feed.post, {
357
+ rkey: '2222222222222',
358
+ })
359
+
360
+ expect(fetchHandler).toHaveBeenCalledTimes(1)
361
+ expect(post).toMatchObject({
362
+ $type: 'app.bsky.feed.post',
363
+ text: 'This is an old post',
364
+ createdAt: '2024-01-01T00:00:00Z',
365
+ })
366
+
367
+ // @TODO: using getRecord method (to check we got the cid)
368
+ })
369
+ })
370
+ })
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": ["../../../tsconfig/isomorphic.json"],
3
+ "include": ["./src"],
4
+ "exclude": ["**/*.test.ts"],
5
+ "compilerOptions": {
6
+ "noImplicitAny": true,
7
+ "importHelpers": true,
8
+ "target": "ES2023",
9
+ "rootDir": "./src",
10
+ "outDir": "./dist"
11
+ }
12
+ }
@@ -0,0 +1 @@
1
+ {"root":["./src/agent.ts","./src/client.ts","./src/error.ts","./src/index.ts","./src/response.ts","./src/types.ts","./src/xrpc.ts","./src/lexicons/com.ts","./src/lexicons/com/atproto.ts","./src/lexicons/com/atproto/repo.ts","./src/lexicons/com/atproto/repo/createrecord.defs.ts","./src/lexicons/com/atproto/repo/createrecord.ts","./src/lexicons/com/atproto/repo/defs.defs.ts","./src/lexicons/com/atproto/repo/defs.ts","./src/lexicons/com/atproto/repo/deleterecord.defs.ts","./src/lexicons/com/atproto/repo/deleterecord.ts","./src/lexicons/com/atproto/repo/getrecord.defs.ts","./src/lexicons/com/atproto/repo/getrecord.ts","./src/lexicons/com/atproto/repo/listrecords.defs.ts","./src/lexicons/com/atproto/repo/listrecords.ts","./src/lexicons/com/atproto/repo/putrecord.defs.ts","./src/lexicons/com/atproto/repo/putrecord.ts","./src/lexicons/com/atproto/repo/uploadblob.defs.ts","./src/lexicons/com/atproto/repo/uploadblob.ts"],"version":"5.8.3"}
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "include": [],
3
+ "references": [
4
+ { "path": "./tsconfig.build.json" },
5
+ { "path": "./tsconfig.tests.json" }
6
+ ]
7
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../../../tsconfig/tests.json",
3
+ "include": ["./tests"],
4
+ "compilerOptions": {
5
+ "noImplicitAny": true,
6
+ "rootDir": "./tests",
7
+ "baseUrl": "./tests",
8
+ "paths": {
9
+ "@atproto/lex-client": ["./dist/index.js"]
10
+ }
11
+ }
12
+ }