@atproto/lex-data 0.1.2 → 0.1.4
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/CHANGELOG.md +14 -0
- package/dist/blob.d.ts +2 -2
- package/dist/blob.d.ts.map +1 -1
- package/dist/blob.js +1 -1
- package/dist/blob.js.map +1 -1
- package/dist/lex-equals.d.ts +1 -1
- package/dist/lex-equals.d.ts.map +1 -1
- package/dist/lex-equals.js.map +1 -1
- package/dist/lex.d.ts +1 -1
- package/dist/lex.d.ts.map +1 -1
- package/dist/lex.js.map +1 -1
- package/dist/lib/nodejs-buffer.js.map +1 -1
- package/dist/uint8array-from-base64.d.ts +1 -1
- package/dist/uint8array-from-base64.d.ts.map +1 -1
- package/dist/uint8array-from-base64.js.map +1 -1
- package/dist/uint8array-to-base64.d.ts +1 -1
- package/dist/uint8array-to-base64.d.ts.map +1 -1
- package/dist/uint8array-to-base64.js.map +1 -1
- package/dist/uint8array.d.ts +1 -1
- package/dist/uint8array.d.ts.map +1 -1
- package/dist/uint8array.js.map +1 -1
- package/dist/utf8-from-base64.d.ts +1 -1
- package/dist/utf8-from-base64.d.ts.map +1 -1
- package/dist/utf8-from-base64.js.map +1 -1
- package/dist/utf8-to-base64.d.ts +1 -1
- package/dist/utf8-to-base64.d.ts.map +1 -1
- package/dist/utf8-to-base64.js.map +1 -1
- package/dist/utf8.d.ts +1 -1
- package/dist/utf8.d.ts.map +1 -1
- package/dist/utf8.js.map +1 -1
- package/package.json +4 -8
- package/src/blob.test.ts +0 -405
- package/src/blob.ts +0 -478
- package/src/cid-implementation.test.ts +0 -129
- package/src/cid.test.ts +0 -350
- package/src/cid.ts +0 -603
- package/src/core-js.d.ts +0 -2
- package/src/index.ts +0 -8
- package/src/lex-equals.test.ts +0 -183
- package/src/lex-equals.ts +0 -123
- package/src/lex-error.test.ts +0 -54
- package/src/lex-error.ts +0 -83
- package/src/lex.test.ts +0 -279
- package/src/lex.ts +0 -253
- package/src/lib/nodejs-buffer.ts +0 -46
- package/src/lib/util.test.ts +0 -49
- package/src/lib/util.ts +0 -7
- package/src/object.test.ts +0 -80
- package/src/object.ts +0 -83
- package/src/uint8array-base64.ts +0 -2
- package/src/uint8array-concat.test.ts +0 -197
- package/src/uint8array-concat.ts +0 -25
- package/src/uint8array-from-base64.test.ts +0 -130
- package/src/uint8array-from-base64.ts +0 -98
- package/src/uint8array-to-base64.test.ts +0 -170
- package/src/uint8array-to-base64.ts +0 -55
- package/src/uint8array.test.ts +0 -503
- package/src/uint8array.ts +0 -197
- package/src/utf8-from-base64.test.ts +0 -39
- package/src/utf8-from-base64.ts +0 -23
- package/src/utf8-from-bytes.test.ts +0 -43
- package/src/utf8-from-bytes.ts +0 -21
- package/src/utf8-grapheme-len.test.ts +0 -38
- package/src/utf8-grapheme-len.ts +0 -21
- package/src/utf8-len.test.ts +0 -21
- package/src/utf8-len.ts +0 -51
- package/src/utf8-to-base64.test.ts +0 -35
- package/src/utf8-to-base64.ts +0 -22
- package/src/utf8.ts +0 -128
- package/tsconfig.build.json +0 -12
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -8
package/src/blob.ts
DELETED
|
@@ -1,478 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CheckCidOptions,
|
|
3
|
-
Cid,
|
|
4
|
-
RawCid,
|
|
5
|
-
ifCid,
|
|
6
|
-
parseCid,
|
|
7
|
-
validateCidString,
|
|
8
|
-
} from './cid.js'
|
|
9
|
-
import { LexValue } from './lex.js'
|
|
10
|
-
import { isPlainObject, isPlainProto } from './object.js'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Options to use with {@link ifCid}, {@link validateCidString}, and related CID
|
|
14
|
-
* validation functions when validating CIDs in BlobRefs, in strict mode. This
|
|
15
|
-
* ensures that the CID is a {@link RawCid} (CID v1, raw multicodec, sha256
|
|
16
|
-
* multihash), which is the expected format for blob references in the AT
|
|
17
|
-
* Protocol data model.
|
|
18
|
-
*/
|
|
19
|
-
const STRICT_CID_CHECK_OPTIONS: CheckCidOptions = { flavor: 'raw' }
|
|
20
|
-
|
|
21
|
-
// Number.isSafeInteger is actually safe to use with non-number values, so we
|
|
22
|
-
// can use it as a type guard.
|
|
23
|
-
const isSafeInteger = Number.isSafeInteger as (v: unknown) => v is number
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Reference to binary data (like images, videos, etc.) in the AT Protocol data
|
|
27
|
-
* model.
|
|
28
|
-
*
|
|
29
|
-
* This type represents a reference to a blob of binary data, identified by its
|
|
30
|
-
* content hash (CID) and accompanied by metadata such as MIME type and size.
|
|
31
|
-
*
|
|
32
|
-
* The {@link BlobRef} type is a union of the current {@link TypedBlobRef}
|
|
33
|
-
* format and the legacy {@link LegacyBlobRef} format.
|
|
34
|
-
*/
|
|
35
|
-
export type BlobRef<Ref extends Cid = Cid> = TypedBlobRef<Ref> | LegacyBlobRef
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Options for validating a {@link BlobRef}.
|
|
39
|
-
*/
|
|
40
|
-
export type BlobRefCheckOptions = {
|
|
41
|
-
/**
|
|
42
|
-
* If `false`, skips strict CID validation of {@link BlobRef.ref}, allowing
|
|
43
|
-
* any valid CID. Otherwise, validates that the CID is v1, uses the raw
|
|
44
|
-
* multicodec, and has a sha256 multihash.
|
|
45
|
-
*
|
|
46
|
-
* @default true
|
|
47
|
-
*/
|
|
48
|
-
strict?: boolean
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Type guard to check if a value is a valid {@link BlobRef}, which can be
|
|
53
|
-
* either a {@link TypedBlobRef} or a {@link LegacyBlobRef}. By default, strict
|
|
54
|
-
* CID validation is applied to ensure that the CID in the blob reference is in
|
|
55
|
-
* the expected format for the AT Protocol, but this can be relaxed with the
|
|
56
|
-
* `strict: false` option.
|
|
57
|
-
*/
|
|
58
|
-
export function isBlobRef(input: unknown): input is BlobRef<RawCid>
|
|
59
|
-
export function isBlobRef<TOptions extends BlobRefCheckOptions>(
|
|
60
|
-
input: unknown,
|
|
61
|
-
options: TOptions,
|
|
62
|
-
): input is LegacyBlobRef | InferTypedBlobRef<TOptions>
|
|
63
|
-
export function isBlobRef(
|
|
64
|
-
input: unknown,
|
|
65
|
-
options?: BlobRefCheckOptions,
|
|
66
|
-
): input is BlobRef<RawCid>
|
|
67
|
-
export function isBlobRef(
|
|
68
|
-
input: unknown,
|
|
69
|
-
options?: BlobRefCheckOptions,
|
|
70
|
-
): input is BlobRef {
|
|
71
|
-
return (input as any)?.$type === 'blob'
|
|
72
|
-
? isTypedBlobRef(input, options)
|
|
73
|
-
: isLegacyBlobRef(input, options)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Extracts the MIME type from a {@link BlobRef}.
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* ```ts
|
|
81
|
-
* const mimeType = getBlobMime(blobRef)
|
|
82
|
-
* console.log(mimeType) // e.g., 'image/jpeg'
|
|
83
|
-
* ```
|
|
84
|
-
*/
|
|
85
|
-
export function getBlobMime(blob: BlobRef): string
|
|
86
|
-
export function getBlobMime(blob?: BlobRef): string | undefined
|
|
87
|
-
export function getBlobMime(blob?: BlobRef): string | undefined {
|
|
88
|
-
return blob?.mimeType
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Extracts the size (in bytes) from a {@link TypedBlobRef}. For
|
|
93
|
-
* {@link LegacyBlobRef}, size information is not available, so this function
|
|
94
|
-
* returns `undefined` for legacy refs.
|
|
95
|
-
*
|
|
96
|
-
* @note The size property, in blob refs, cannot be 100% trusted since the PDS
|
|
97
|
-
* might not have a local copy of the blob (to check the size against) and might
|
|
98
|
-
* just be passing through the blob ref from the client without validating it.
|
|
99
|
-
* So, while this function can be useful for getting size information when
|
|
100
|
-
* available, it should not be solely relied upon for critical functionality
|
|
101
|
-
* without additional validation.
|
|
102
|
-
*
|
|
103
|
-
* @example
|
|
104
|
-
* ```ts
|
|
105
|
-
* const size = getBlobSize(blobRef)
|
|
106
|
-
* if (size !== undefined) {
|
|
107
|
-
* console.log(`Blob size: ${size} bytes`)
|
|
108
|
-
* } else {
|
|
109
|
-
* console.log('Size information not available for legacy blob ref')
|
|
110
|
-
* }
|
|
111
|
-
* ```
|
|
112
|
-
*/
|
|
113
|
-
export function getBlobSize(blob: BlobRef): number | undefined {
|
|
114
|
-
if ('$type' in blob && blob.size >= 0) return blob.size
|
|
115
|
-
// LegacyBlobRef doesn't have size information
|
|
116
|
-
return undefined
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Extracts the {@link Cid} from a {@link BlobRef}.
|
|
121
|
-
*
|
|
122
|
-
* @throws If the input input is a {@link LegacyBlobRef} with an invalid CID string
|
|
123
|
-
* @example
|
|
124
|
-
* ```ts
|
|
125
|
-
* const cid = getBlobCid(blobRef)
|
|
126
|
-
* console.log(cid.bytes)
|
|
127
|
-
* ```
|
|
128
|
-
*/
|
|
129
|
-
export function getBlobCid(blob: BlobRef): Cid
|
|
130
|
-
export function getBlobCid(blob?: BlobRef): Cid | undefined
|
|
131
|
-
export function getBlobCid(blob?: BlobRef): Cid | undefined {
|
|
132
|
-
if (!blob) return undefined
|
|
133
|
-
return '$type' in blob ? blob.ref : parseCid(blob.cid)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Extracts the CID string from a {@link BlobRef}.
|
|
138
|
-
*
|
|
139
|
-
* This is similar to `getBlobCid(blob).toString()` but is more optimized since
|
|
140
|
-
* the CID string is already available in the legacy format and we can avoid
|
|
141
|
-
* parsing it into a CID object just to convert it back to a string.
|
|
142
|
-
*
|
|
143
|
-
* @example
|
|
144
|
-
* ```ts
|
|
145
|
-
* const cidString = getBlobCidString(blobRef)
|
|
146
|
-
* console.log(cidString)
|
|
147
|
-
* ```
|
|
148
|
-
*/
|
|
149
|
-
export function getBlobCidString(blob: BlobRef): string
|
|
150
|
-
export function getBlobCidString(blob?: BlobRef): string | undefined
|
|
151
|
-
export function getBlobCidString(blob?: BlobRef): string | undefined {
|
|
152
|
-
if (!blob) return undefined
|
|
153
|
-
return '$type' in blob ? blob.ref.toString() : blob.cid
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Reference to binary data (like images, videos, etc.) in the AT Protocol data model.
|
|
158
|
-
*
|
|
159
|
-
* A {@link TypedBlobRef} is a {@link LexMap} with a specific structure that
|
|
160
|
-
* identifies binary content by its content hash (CID), along with metadata
|
|
161
|
-
* about the content type and size.
|
|
162
|
-
*
|
|
163
|
-
* @typeParam Ref - The type of CID reference, defaults to any {@link Cid}
|
|
164
|
-
*
|
|
165
|
-
* @example
|
|
166
|
-
* ```typescript
|
|
167
|
-
* import type { TypedBlobRef } from '@atproto/lex-data'
|
|
168
|
-
*
|
|
169
|
-
* const imageRef: TypedBlobRef = {
|
|
170
|
-
* $type: 'blob',
|
|
171
|
-
* mimeType: 'image/jpeg',
|
|
172
|
-
* ref: cid, // CID of the blob content
|
|
173
|
-
* size: 12345
|
|
174
|
-
* }
|
|
175
|
-
* ```
|
|
176
|
-
*
|
|
177
|
-
* @see {@link isTypedBlobRef} to check if a value is a valid {@link TypedBlobRef}
|
|
178
|
-
* @see {@link LegacyBlobRef} for the older blob reference format
|
|
179
|
-
*/
|
|
180
|
-
export type TypedBlobRef<Ref extends Cid = Cid> = {
|
|
181
|
-
$type: 'blob'
|
|
182
|
-
mimeType: string
|
|
183
|
-
ref: Ref
|
|
184
|
-
size: number
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Infers the BlobRef type based on the check options.
|
|
189
|
-
*
|
|
190
|
-
* @typeParam TOptions - The options used for checking
|
|
191
|
-
*/
|
|
192
|
-
export type InferTypedBlobRef<TOptions extends BlobRefCheckOptions> =
|
|
193
|
-
TOptions extends { strict: false }
|
|
194
|
-
? TypedBlobRef
|
|
195
|
-
: { strict: boolean } extends TOptions
|
|
196
|
-
? TypedBlobRef
|
|
197
|
-
: TypedBlobRef<RawCid>
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Type guard to check if a value is a valid {@link BlobRef}.
|
|
201
|
-
*
|
|
202
|
-
* Validates the structure of the input including:
|
|
203
|
-
* - `$type` must be `'blob'`
|
|
204
|
-
* - `mimeType` must be a valid MIME type string (containing '/')
|
|
205
|
-
* - `size` must be a non-negative safe integer
|
|
206
|
-
* - `ref` must be a valid CID (strict validation by default)
|
|
207
|
-
*
|
|
208
|
-
* @param input - The value to check
|
|
209
|
-
* @param options - Optional validation options
|
|
210
|
-
* @returns `true` if the input is a valid BlobRef
|
|
211
|
-
*
|
|
212
|
-
* @example
|
|
213
|
-
* ```typescript
|
|
214
|
-
* import { isTypedBlobRef } from '@atproto/lex-data'
|
|
215
|
-
*
|
|
216
|
-
* if (isTypedBlobRef(data)) {
|
|
217
|
-
* console.log(data.mimeType) // e.g., 'image/jpeg'
|
|
218
|
-
* console.log(data.size) // e.g., 12345
|
|
219
|
-
* }
|
|
220
|
-
*
|
|
221
|
-
* // Allow any valid CID (not just raw CIDs)
|
|
222
|
-
* if (isTypedBlobRef(data, { strict: false })) {
|
|
223
|
-
* // ...
|
|
224
|
-
* }
|
|
225
|
-
* ```
|
|
226
|
-
*/
|
|
227
|
-
export function isTypedBlobRef(input: unknown): input is TypedBlobRef<RawCid>
|
|
228
|
-
export function isTypedBlobRef<TOptions extends BlobRefCheckOptions>(
|
|
229
|
-
input: unknown,
|
|
230
|
-
options: TOptions,
|
|
231
|
-
): input is InferTypedBlobRef<TOptions>
|
|
232
|
-
export function isTypedBlobRef(
|
|
233
|
-
input: unknown,
|
|
234
|
-
options?: BlobRefCheckOptions,
|
|
235
|
-
): input is TypedBlobRef<RawCid>
|
|
236
|
-
export function isTypedBlobRef(
|
|
237
|
-
input: unknown,
|
|
238
|
-
options?: BlobRefCheckOptions,
|
|
239
|
-
): input is TypedBlobRef {
|
|
240
|
-
if (!isPlainObject(input)) {
|
|
241
|
-
return false
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (input?.$type !== 'blob') {
|
|
245
|
-
return false
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const { mimeType, size, ref } = input
|
|
249
|
-
// @NOTE Very basic mime validation
|
|
250
|
-
if (typeof mimeType !== 'string' || !mimeType.includes('/')) {
|
|
251
|
-
return false
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (size === -1 && options?.strict === false) {
|
|
255
|
-
// In non-strict mode, allow size to be -1 to accommodate legacy blob refs
|
|
256
|
-
// that don't include size information.
|
|
257
|
-
} else if (!isSafeInteger(size) || size < 0) {
|
|
258
|
-
return false
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (typeof ref !== 'object' || ref === null) {
|
|
262
|
-
return false
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
for (const key in input) {
|
|
266
|
-
if (
|
|
267
|
-
key !== '$type' &&
|
|
268
|
-
key !== 'mimeType' &&
|
|
269
|
-
key !== 'ref' &&
|
|
270
|
-
key !== 'size'
|
|
271
|
-
) {
|
|
272
|
-
return false
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const cid = ifCid(
|
|
277
|
-
ref,
|
|
278
|
-
// Strict unless explicitly disabled
|
|
279
|
-
options?.strict === false ? undefined : STRICT_CID_CHECK_OPTIONS,
|
|
280
|
-
)
|
|
281
|
-
if (!cid) {
|
|
282
|
-
return false
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return true
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Legacy format for blob references used in older AT Protocol data.
|
|
290
|
-
*
|
|
291
|
-
* This is the older format that stores the CID as a string rather than
|
|
292
|
-
* as a structured CID object. New code should use {@link BlobRef} instead.
|
|
293
|
-
*
|
|
294
|
-
* @example
|
|
295
|
-
* ```typescript
|
|
296
|
-
* import type { LegacyBlobRef } from '@atproto/lex-data'
|
|
297
|
-
*
|
|
298
|
-
* const legacyRef: LegacyBlobRef = {
|
|
299
|
-
* cid: 'bafyreib...',
|
|
300
|
-
* mimeType: 'image/jpeg'
|
|
301
|
-
* }
|
|
302
|
-
* ```
|
|
303
|
-
*
|
|
304
|
-
* @see {@link isLegacyBlobRef} to check if a value is a LegacyBlobRef
|
|
305
|
-
* @see {@link BlobRef} for the current blob reference format
|
|
306
|
-
* @deprecated Use {@link BlobRef} for new code
|
|
307
|
-
*/
|
|
308
|
-
export type LegacyBlobRef = {
|
|
309
|
-
cid: string
|
|
310
|
-
mimeType: string
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Type guard to check if a value is a valid {@link LegacyBlobRef}.
|
|
315
|
-
*
|
|
316
|
-
* Validates the structure of the input:
|
|
317
|
-
* - `cid` must be a valid CID string
|
|
318
|
-
* - `mimeType` must be a non-empty string
|
|
319
|
-
* - No additional properties allowed
|
|
320
|
-
*
|
|
321
|
-
* @example
|
|
322
|
-
* ```typescript
|
|
323
|
-
* import { isLegacyBlobRef } from '@atproto/lex-data'
|
|
324
|
-
*
|
|
325
|
-
* if (isLegacyBlobRef(data)) {
|
|
326
|
-
* console.log(data.cid) // CID as string
|
|
327
|
-
* console.log(data.mimeType) // e.g., 'image/jpeg'
|
|
328
|
-
* }
|
|
329
|
-
* ```
|
|
330
|
-
*
|
|
331
|
-
* @see {@link isTypedBlobRef} for checking the current blob reference format
|
|
332
|
-
*/
|
|
333
|
-
export function isLegacyBlobRef(
|
|
334
|
-
input: unknown,
|
|
335
|
-
options?: BlobRefCheckOptions,
|
|
336
|
-
): input is LegacyBlobRef {
|
|
337
|
-
if (!isPlainObject(input)) {
|
|
338
|
-
return false
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const { cid, mimeType } = input
|
|
342
|
-
if (typeof cid !== 'string') {
|
|
343
|
-
return false
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if (typeof mimeType !== 'string' || mimeType.length === 0) {
|
|
347
|
-
return false
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
for (const key in input) {
|
|
351
|
-
if (key !== 'cid' && key !== 'mimeType') {
|
|
352
|
-
return false
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
if (
|
|
357
|
-
!validateCidString(
|
|
358
|
-
cid,
|
|
359
|
-
options?.strict === false ? undefined : STRICT_CID_CHECK_OPTIONS,
|
|
360
|
-
)
|
|
361
|
-
) {
|
|
362
|
-
return false
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return true
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Options for enumerating blob references within a {@link LexValue}.
|
|
370
|
-
*/
|
|
371
|
-
export type EnumBlobRefsOptions = BlobRefCheckOptions & {
|
|
372
|
-
/**
|
|
373
|
-
* If `true`, also yields {@link LegacyBlobRef} objects in addition to
|
|
374
|
-
* {@link BlobRef} objects.
|
|
375
|
-
*
|
|
376
|
-
* @default false
|
|
377
|
-
*/
|
|
378
|
-
allowLegacy?: boolean
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Infers the yielded type of {@link enumBlobRefs} based on options.
|
|
383
|
-
*
|
|
384
|
-
* @typeParam TOptions - The options used for enumeration
|
|
385
|
-
*/
|
|
386
|
-
export type InferEnumBlobRefs<TOptions extends EnumBlobRefsOptions> =
|
|
387
|
-
TOptions extends { allowLegacy: true }
|
|
388
|
-
? InferTypedBlobRef<TOptions> | LegacyBlobRef
|
|
389
|
-
: { allowLegacy: boolean } extends TOptions
|
|
390
|
-
? InferTypedBlobRef<TOptions> | LegacyBlobRef
|
|
391
|
-
: InferTypedBlobRef<TOptions>
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Generator that enumerates all {@link BlobRef}s (and, optionally,
|
|
395
|
-
* {@link LegacyBlobRef}s) found within a {@link LexValue}.
|
|
396
|
-
*
|
|
397
|
-
* Performs a deep traversal of the input value, yielding any blob references
|
|
398
|
-
* found. This is useful for extracting all media references from a record.
|
|
399
|
-
*
|
|
400
|
-
* @param input - The LexValue to search for blob references
|
|
401
|
-
* @param options - Optional configuration for the enumeration
|
|
402
|
-
* @yields Each blob reference found in the input
|
|
403
|
-
*
|
|
404
|
-
* @example
|
|
405
|
-
* ```typescript
|
|
406
|
-
* import { enumBlobRefs } from '@atproto/lex-data'
|
|
407
|
-
*
|
|
408
|
-
* const record = {
|
|
409
|
-
* text: 'Hello',
|
|
410
|
-
* images: [
|
|
411
|
-
* { $type: 'blob', mimeType: 'image/jpeg', ref: cid1, size: 1000 },
|
|
412
|
-
* { $type: 'blob', mimeType: 'image/png', ref: cid2, size: 2000 }
|
|
413
|
-
* ]
|
|
414
|
-
* }
|
|
415
|
-
*
|
|
416
|
-
* for (const blobRef of enumBlobRefs(record)) {
|
|
417
|
-
* console.log(blobRef.mimeType, blobRef.size)
|
|
418
|
-
* }
|
|
419
|
-
*
|
|
420
|
-
* // Include legacy blob references
|
|
421
|
-
* for (const ref of enumBlobRefs(record, { allowLegacy: true, strict: false })) {
|
|
422
|
-
* // ref may be BlobRef or LegacyBlobRef, with relaxed CID validation
|
|
423
|
-
* }
|
|
424
|
-
* ```
|
|
425
|
-
*/
|
|
426
|
-
export function enumBlobRefs(
|
|
427
|
-
input: LexValue,
|
|
428
|
-
): Generator<BlobRef<RawCid>, void, unknown>
|
|
429
|
-
export function enumBlobRefs<TOptions extends EnumBlobRefsOptions>(
|
|
430
|
-
input: LexValue,
|
|
431
|
-
options: TOptions,
|
|
432
|
-
): Generator<InferEnumBlobRefs<TOptions>, void, unknown>
|
|
433
|
-
export function enumBlobRefs(
|
|
434
|
-
input: LexValue,
|
|
435
|
-
options?: EnumBlobRefsOptions,
|
|
436
|
-
): Generator<BlobRef, void, unknown>
|
|
437
|
-
export function* enumBlobRefs(
|
|
438
|
-
input: LexValue,
|
|
439
|
-
options?: EnumBlobRefsOptions,
|
|
440
|
-
): Generator<BlobRef, void, unknown> {
|
|
441
|
-
// LegacyBlobRef not included by default
|
|
442
|
-
const includeLegacy = options?.allowLegacy === true
|
|
443
|
-
|
|
444
|
-
// Using a stack to avoid recursion depth issues.
|
|
445
|
-
const stack: LexValue[] = [input]
|
|
446
|
-
|
|
447
|
-
// Since we are using a stack, we could end-up in an infinite loop with cyclic
|
|
448
|
-
// structures. Cyclic structures are not valid LexValues and should, thus,
|
|
449
|
-
// never occur, but let's be safe.
|
|
450
|
-
const visited = new Set<object>()
|
|
451
|
-
|
|
452
|
-
do {
|
|
453
|
-
const value = stack.pop()!
|
|
454
|
-
|
|
455
|
-
if (value != null && typeof value === 'object') {
|
|
456
|
-
if (Array.isArray(value)) {
|
|
457
|
-
if (visited.has(value)) continue
|
|
458
|
-
visited.add(value)
|
|
459
|
-
stack.push(...value)
|
|
460
|
-
} else if (isPlainProto(value)) {
|
|
461
|
-
if (visited.has(value)) continue
|
|
462
|
-
visited.add(value)
|
|
463
|
-
if (isTypedBlobRef(value, options)) {
|
|
464
|
-
yield value
|
|
465
|
-
} else if (includeLegacy && isLegacyBlobRef(value, options)) {
|
|
466
|
-
yield value
|
|
467
|
-
} else {
|
|
468
|
-
for (const v of Object.values(value)) {
|
|
469
|
-
if (v != null) stack.push(v)
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
} while (stack.length > 0)
|
|
475
|
-
|
|
476
|
-
// Optimization: ease GC's work
|
|
477
|
-
visited.clear()
|
|
478
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { base32 } from 'multiformats/bases/base32'
|
|
2
|
-
import { CID } from 'multiformats/cid'
|
|
3
|
-
import { create as createDigest } from 'multiformats/hashes/digest'
|
|
4
|
-
import { assert, describe, expect, it } from 'vitest'
|
|
5
|
-
import { Cid } from './cid.js'
|
|
6
|
-
import { ui8Equals } from './uint8array.js'
|
|
7
|
-
|
|
8
|
-
export class BytesCid implements Cid {
|
|
9
|
-
constructor(readonly bytes: Uint8Array) {
|
|
10
|
-
if (this.bytes.length < 4) {
|
|
11
|
-
throw new Error('CID bytes are too short')
|
|
12
|
-
}
|
|
13
|
-
if (this.bytes[0] > 1) {
|
|
14
|
-
throw new Error('Unsupported CID version')
|
|
15
|
-
}
|
|
16
|
-
if (this.bytes.length !== 4 + this.bytes[3]) {
|
|
17
|
-
throw new Error('CID bytes length mismatch')
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
get version() {
|
|
22
|
-
return this.bytes[0] as 0 | 1
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
get code() {
|
|
26
|
-
return this.bytes[1]
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
get multihash() {
|
|
30
|
-
const code = this.bytes[2]
|
|
31
|
-
const digest = this.bytes.subarray(4)
|
|
32
|
-
return { code, digest }
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
equals(other: Cid): boolean {
|
|
36
|
-
return ui8Equals(this.bytes, other.bytes)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
toString(): string {
|
|
40
|
-
return base32.encode(this.bytes)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
describe(BytesCid, () => {
|
|
45
|
-
it('creates a BytesCid from valid bytes', () => {
|
|
46
|
-
const bytes = new Uint8Array([1, 0x55, 0x12, 3, 1, 2, 3])
|
|
47
|
-
const cid = new BytesCid(bytes)
|
|
48
|
-
|
|
49
|
-
assert(cid.version === 1)
|
|
50
|
-
assert(cid.code === 0x55)
|
|
51
|
-
assert(cid.multihash.code === 0x12)
|
|
52
|
-
assert(ui8Equals(cid.multihash.digest, new Uint8Array([1, 2, 3])))
|
|
53
|
-
assert(ui8Equals(cid.bytes, bytes))
|
|
54
|
-
assert(typeof cid.toString === 'function')
|
|
55
|
-
assert(typeof cid.equals === 'function')
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
it('throws an error for invalid CID bytes', () => {
|
|
59
|
-
expect(
|
|
60
|
-
() => new BytesCid(new Uint8Array([2, 0x55, 0x12, 3, 1, 2, 3])),
|
|
61
|
-
).toThrow('Unsupported CID version')
|
|
62
|
-
expect(() => new BytesCid(new Uint8Array([1, 0x55, 0x12]))).toThrow(
|
|
63
|
-
'CID bytes are too short',
|
|
64
|
-
)
|
|
65
|
-
expect(
|
|
66
|
-
() => new BytesCid(new Uint8Array([1, 0x55, 0x12, 4, 1, 2, 3])),
|
|
67
|
-
).toThrow('CID bytes length mismatch')
|
|
68
|
-
})
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* A minimal custom implementation of the `Cid` interface for testing purposes.
|
|
73
|
-
*/
|
|
74
|
-
export function createCustomCid<
|
|
75
|
-
TVersion extends 0 | 1,
|
|
76
|
-
TCodec extends number,
|
|
77
|
-
THashCode extends number,
|
|
78
|
-
>(
|
|
79
|
-
version: TVersion,
|
|
80
|
-
code: TCodec,
|
|
81
|
-
hashCode: THashCode,
|
|
82
|
-
digest: Uint8Array,
|
|
83
|
-
): Cid<TVersion, TCodec, THashCode> {
|
|
84
|
-
return {
|
|
85
|
-
version,
|
|
86
|
-
code,
|
|
87
|
-
multihash: { code: hashCode, digest },
|
|
88
|
-
bytes: new Uint8Array([version, code, hashCode, digest.length, ...digest]),
|
|
89
|
-
toString,
|
|
90
|
-
equals,
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function equals(this: Cid, other: Cid): boolean {
|
|
95
|
-
return (
|
|
96
|
-
this.version === other.version &&
|
|
97
|
-
this.code === other.code &&
|
|
98
|
-
this.multihash.code === other.multihash.code &&
|
|
99
|
-
ui8Equals(this.multihash.digest, other.multihash.digest)
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function toString(this: Cid): string {
|
|
104
|
-
return CID.create(
|
|
105
|
-
this.version,
|
|
106
|
-
this.code,
|
|
107
|
-
createDigest(this.multihash.code, this.multihash.digest),
|
|
108
|
-
).toString()
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
describe(createCustomCid, () => {
|
|
112
|
-
it('creates a CID with the specified properties', () => {
|
|
113
|
-
const digest = new Uint8Array([1, 2, 3, 4, 5])
|
|
114
|
-
const customCid = createCustomCid(1, 0x55, 0x12, digest)
|
|
115
|
-
|
|
116
|
-
assert(customCid.version === 1)
|
|
117
|
-
assert(customCid.code === 0x55)
|
|
118
|
-
assert(customCid.multihash.code === 0x12)
|
|
119
|
-
assert(ui8Equals(customCid.multihash.digest, digest))
|
|
120
|
-
assert(
|
|
121
|
-
ui8Equals(
|
|
122
|
-
customCid.bytes,
|
|
123
|
-
new Uint8Array([1, 0x55, 0x12, 5, 1, 2, 3, 4, 5]),
|
|
124
|
-
),
|
|
125
|
-
)
|
|
126
|
-
assert(typeof customCid.toString === 'function')
|
|
127
|
-
assert(typeof customCid.equals === 'function')
|
|
128
|
-
})
|
|
129
|
-
})
|