@atproto/lex-data 0.0.14 → 0.0.15
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 +118 -39
- package/dist/blob.d.ts.map +1 -1
- package/dist/blob.js +65 -9
- package/dist/blob.js.map +1 -1
- package/dist/lib/nodejs-buffer.d.ts +1 -0
- package/dist/lib/nodejs-buffer.d.ts.map +1 -1
- package/dist/lib/nodejs-buffer.js.map +1 -1
- package/dist/utf8-from-bytes.d.ts +3 -0
- package/dist/utf8-from-bytes.d.ts.map +1 -0
- package/dist/utf8-from-bytes.js +19 -0
- package/dist/utf8-from-bytes.js.map +1 -0
- package/dist/utf8.d.ts +18 -0
- package/dist/utf8.d.ts.map +1 -1
- package/dist/utf8.js +20 -1
- package/dist/utf8.js.map +1 -1
- package/package.json +1 -1
- package/src/blob.test.ts +38 -25
- package/src/blob.ts +190 -52
- package/src/lib/nodejs-buffer.ts +5 -0
- package/src/utf8-from-bytes.test.ts +43 -0
- package/src/utf8-from-bytes.ts +21 -0
- package/src/utf8.ts +20 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @atproto/lex-data
|
|
2
2
|
|
|
3
|
+
## 0.0.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#4828](https://github.com/bluesky-social/atproto/pull/4828) [`c62651d`](https://github.com/bluesky-social/atproto/commit/c62651dd69f1e18bd854b66e499b91fee9eaa856) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Export new utility functions for working with `BlobRef`: `getBlobCid`, `getBlobCidString`, `getBlobMime`, and `getBlobSize`.
|
|
8
|
+
|
|
9
|
+
- [#4835](https://github.com/bluesky-social/atproto/pull/4835) [`f6f100c`](https://github.com/bluesky-social/atproto/commit/f6f100c33700a7ff58a1458109cc7420131feed0) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `utf8FromBytes` that decodes a byte array containing a utf-8 string
|
|
10
|
+
|
|
11
|
+
- [#4828](https://github.com/bluesky-social/atproto/pull/4828) [`c62651d`](https://github.com/bluesky-social/atproto/commit/c62651dd69f1e18bd854b66e499b91fee9eaa856) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Defines `BlobRef` as a union of `TypedBlobRef` and `LegacyBlobRef` to allow for seamless handling of both types of blob references.
|
|
12
|
+
|
|
13
|
+
- [#4828](https://github.com/bluesky-social/atproto/pull/4828) [`c62651d`](https://github.com/bluesky-social/atproto/commit/c62651dd69f1e18bd854b66e499b91fee9eaa856) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Accept legacy blob references in non-strict mode. Legacy blob references (objects with `cid` and `mimeType` properties) are now accepted when `strict: false`, which is the default behavior when `strictResponseProcessing` is disabled on the Client.
|
|
14
|
+
|
|
15
|
+
BREAKING: The `allowLegacy` option has been removed from the blob schema builder, and legacy blobs are now handled automatically based on the strictness mode: in strict mode they are rejected, and in non-strict mode they are accepted. Consumers should stop passing `allowLegacy` and rely on strictness configuration instead. Likewise, CLI consumers should stop using the removed `--allowLegacyBlobs` flag and use the default strict/non-strict behavior.
|
|
16
|
+
|
|
3
17
|
## 0.0.14
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/dist/blob.d.ts
CHANGED
|
@@ -1,19 +1,114 @@
|
|
|
1
1
|
import { Cid, RawCid } from './cid.js';
|
|
2
2
|
import { LexValue } from './lex.js';
|
|
3
|
+
/**
|
|
4
|
+
* Reference to binary data (like images, videos, etc.) in the AT Protocol data
|
|
5
|
+
* model.
|
|
6
|
+
*
|
|
7
|
+
* This type represents a reference to a blob of binary data, identified by its
|
|
8
|
+
* content hash (CID) and accompanied by metadata such as MIME type and size.
|
|
9
|
+
*
|
|
10
|
+
* The {@link BlobRef} type is a union of the current {@link TypedBlobRef}
|
|
11
|
+
* format and the legacy {@link LegacyBlobRef} format.
|
|
12
|
+
*/
|
|
13
|
+
export type BlobRef<Ref extends Cid = Cid> = TypedBlobRef<Ref> | LegacyBlobRef;
|
|
14
|
+
/**
|
|
15
|
+
* Options for validating a {@link BlobRef}.
|
|
16
|
+
*/
|
|
17
|
+
export type BlobRefCheckOptions = {
|
|
18
|
+
/**
|
|
19
|
+
* If `false`, skips strict CID validation of {@link BlobRef.ref}, allowing
|
|
20
|
+
* any valid CID. Otherwise, validates that the CID is v1, uses the raw
|
|
21
|
+
* multicodec, and has a sha256 multihash.
|
|
22
|
+
*
|
|
23
|
+
* @default true
|
|
24
|
+
*/
|
|
25
|
+
strict?: boolean;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Type guard to check if a value is a valid {@link BlobRef}, which can be
|
|
29
|
+
* either a {@link TypedBlobRef} or a {@link LegacyBlobRef}. By default, strict
|
|
30
|
+
* CID validation is applied to ensure that the CID in the blob reference is in
|
|
31
|
+
* the expected format for the AT Protocol, but this can be relaxed with the
|
|
32
|
+
* `strict: false` option.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isBlobRef(input: unknown): input is BlobRef<RawCid>;
|
|
35
|
+
export declare function isBlobRef<TOptions extends BlobRefCheckOptions>(input: unknown, options: TOptions): input is LegacyBlobRef | InferTypedBlobRef<TOptions>;
|
|
36
|
+
export declare function isBlobRef(input: unknown, options?: BlobRefCheckOptions): input is BlobRef<RawCid>;
|
|
37
|
+
/**
|
|
38
|
+
* Extracts the MIME type from a {@link BlobRef}.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const mimeType = getBlobMime(blobRef)
|
|
43
|
+
* console.log(mimeType) // e.g., 'image/jpeg'
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function getBlobMime(blob: BlobRef): string;
|
|
47
|
+
export declare function getBlobMime(blob?: BlobRef): string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Extracts the size (in bytes) from a {@link TypedBlobRef}. For
|
|
50
|
+
* {@link LegacyBlobRef}, size information is not available, so this function
|
|
51
|
+
* returns `undefined` for legacy refs.
|
|
52
|
+
*
|
|
53
|
+
* @note The size property, in blob refs, cannot be 100% trusted since the PDS
|
|
54
|
+
* might not have a local copy of the blob (to check the size against) and might
|
|
55
|
+
* just be passing through the blob ref from the client without validating it.
|
|
56
|
+
* So, while this function can be useful for getting size information when
|
|
57
|
+
* available, it should not be solely relied upon for critical functionality
|
|
58
|
+
* without additional validation.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const size = getBlobSize(blobRef)
|
|
63
|
+
* if (size !== undefined) {
|
|
64
|
+
* console.log(`Blob size: ${size} bytes`)
|
|
65
|
+
* } else {
|
|
66
|
+
* console.log('Size information not available for legacy blob ref')
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function getBlobSize(blob: BlobRef): number | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Extracts the {@link Cid} from a {@link BlobRef}.
|
|
73
|
+
*
|
|
74
|
+
* @throws If the input input is a {@link LegacyBlobRef} with an invalid CID string
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* const cid = getBlobCid(blobRef)
|
|
78
|
+
* console.log(cid.bytes)
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function getBlobCid(blob: BlobRef): Cid;
|
|
82
|
+
export declare function getBlobCid(blob?: BlobRef): Cid | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Extracts the CID string from a {@link BlobRef}.
|
|
85
|
+
*
|
|
86
|
+
* This is similar to `getBlobCid(blob).toString()` but is more optimized since
|
|
87
|
+
* the CID string is already available in the legacy format and we can avoid
|
|
88
|
+
* parsing it into a CID object just to convert it back to a string.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* const cidString = getBlobCidString(blobRef)
|
|
93
|
+
* console.log(cidString)
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export declare function getBlobCidString(blob: BlobRef): string;
|
|
97
|
+
export declare function getBlobCidString(blob?: BlobRef): string | undefined;
|
|
3
98
|
/**
|
|
4
99
|
* Reference to binary data (like images, videos, etc.) in the AT Protocol data model.
|
|
5
100
|
*
|
|
6
|
-
* A
|
|
7
|
-
* content by its content hash (CID), along with metadata
|
|
8
|
-
* and size.
|
|
101
|
+
* A {@link TypedBlobRef} is a {@link LexMap} with a specific structure that
|
|
102
|
+
* identifies binary content by its content hash (CID), along with metadata
|
|
103
|
+
* about the content type and size.
|
|
9
104
|
*
|
|
10
105
|
* @typeParam Ref - The type of CID reference, defaults to any {@link Cid}
|
|
11
106
|
*
|
|
12
107
|
* @example
|
|
13
108
|
* ```typescript
|
|
14
|
-
* import type {
|
|
109
|
+
* import type { TypedBlobRef } from '@atproto/lex-data'
|
|
15
110
|
*
|
|
16
|
-
* const imageRef:
|
|
111
|
+
* const imageRef: TypedBlobRef = {
|
|
17
112
|
* $type: 'blob',
|
|
18
113
|
* mimeType: 'image/jpeg',
|
|
19
114
|
* ref: cid, // CID of the blob content
|
|
@@ -21,38 +116,25 @@ import { LexValue } from './lex.js';
|
|
|
21
116
|
* }
|
|
22
117
|
* ```
|
|
23
118
|
*
|
|
24
|
-
* @see {@link
|
|
119
|
+
* @see {@link isTypedBlobRef} to check if a value is a valid {@link TypedBlobRef}
|
|
25
120
|
* @see {@link LegacyBlobRef} for the older blob reference format
|
|
26
121
|
*/
|
|
27
|
-
export type
|
|
122
|
+
export type TypedBlobRef<Ref extends Cid = Cid> = {
|
|
28
123
|
$type: 'blob';
|
|
29
124
|
mimeType: string;
|
|
30
125
|
ref: Ref;
|
|
31
126
|
size: number;
|
|
32
127
|
};
|
|
33
|
-
/**
|
|
34
|
-
* Options for validating a {@link BlobRef}.
|
|
35
|
-
*/
|
|
36
|
-
export type BlobRefCheckOptions = {
|
|
37
|
-
/**
|
|
38
|
-
* If `false`, skips strict CID validation of {@link BlobRef.ref}, allowing
|
|
39
|
-
* any valid CID. Otherwise, validates that the CID is v1, uses the raw
|
|
40
|
-
* multicodec, and has a sha256 multihash.
|
|
41
|
-
*
|
|
42
|
-
* @default true
|
|
43
|
-
*/
|
|
44
|
-
strict?: boolean;
|
|
45
|
-
};
|
|
46
128
|
/**
|
|
47
129
|
* Infers the BlobRef type based on the check options.
|
|
48
130
|
*
|
|
49
131
|
* @typeParam TOptions - The options used for checking
|
|
50
132
|
*/
|
|
51
|
-
export type
|
|
133
|
+
export type InferTypedBlobRef<TOptions extends BlobRefCheckOptions> = TOptions extends {
|
|
52
134
|
strict: false;
|
|
53
|
-
} ?
|
|
135
|
+
} ? TypedBlobRef : {
|
|
54
136
|
strict: boolean;
|
|
55
|
-
} extends TOptions ?
|
|
137
|
+
} extends TOptions ? TypedBlobRef : TypedBlobRef<RawCid>;
|
|
56
138
|
/**
|
|
57
139
|
* Type guard to check if a value is a valid {@link BlobRef}.
|
|
58
140
|
*
|
|
@@ -68,22 +150,22 @@ export type InferCheckedBlobRef<TOptions extends BlobRefCheckOptions> = TOptions
|
|
|
68
150
|
*
|
|
69
151
|
* @example
|
|
70
152
|
* ```typescript
|
|
71
|
-
* import {
|
|
153
|
+
* import { isTypedBlobRef } from '@atproto/lex-data'
|
|
72
154
|
*
|
|
73
|
-
* if (
|
|
155
|
+
* if (isTypedBlobRef(data)) {
|
|
74
156
|
* console.log(data.mimeType) // e.g., 'image/jpeg'
|
|
75
157
|
* console.log(data.size) // e.g., 12345
|
|
76
158
|
* }
|
|
77
159
|
*
|
|
78
160
|
* // Allow any valid CID (not just raw CIDs)
|
|
79
|
-
* if (
|
|
161
|
+
* if (isTypedBlobRef(data, { strict: false })) {
|
|
80
162
|
* // ...
|
|
81
163
|
* }
|
|
82
164
|
* ```
|
|
83
165
|
*/
|
|
84
|
-
export declare function
|
|
85
|
-
export declare function
|
|
86
|
-
export declare function
|
|
166
|
+
export declare function isTypedBlobRef(input: unknown): input is TypedBlobRef<RawCid>;
|
|
167
|
+
export declare function isTypedBlobRef<TOptions extends BlobRefCheckOptions>(input: unknown, options: TOptions): input is InferTypedBlobRef<TOptions>;
|
|
168
|
+
export declare function isTypedBlobRef(input: unknown, options?: BlobRefCheckOptions): input is TypedBlobRef<RawCid>;
|
|
87
169
|
/**
|
|
88
170
|
* Legacy format for blob references used in older AT Protocol data.
|
|
89
171
|
*
|
|
@@ -116,9 +198,6 @@ export type LegacyBlobRef = {
|
|
|
116
198
|
* - `mimeType` must be a non-empty string
|
|
117
199
|
* - No additional properties allowed
|
|
118
200
|
*
|
|
119
|
-
* @param input - The value to check
|
|
120
|
-
* @returns `true` if the input is a valid LegacyBlobRef
|
|
121
|
-
*
|
|
122
201
|
* @example
|
|
123
202
|
* ```typescript
|
|
124
203
|
* import { isLegacyBlobRef } from '@atproto/lex-data'
|
|
@@ -129,9 +208,9 @@ export type LegacyBlobRef = {
|
|
|
129
208
|
* }
|
|
130
209
|
* ```
|
|
131
210
|
*
|
|
132
|
-
* @see {@link
|
|
211
|
+
* @see {@link isTypedBlobRef} for checking the current blob reference format
|
|
133
212
|
*/
|
|
134
|
-
export declare function isLegacyBlobRef(input: unknown): input is LegacyBlobRef;
|
|
213
|
+
export declare function isLegacyBlobRef(input: unknown, options?: BlobRefCheckOptions): input is LegacyBlobRef;
|
|
135
214
|
/**
|
|
136
215
|
* Options for enumerating blob references within a {@link LexValue}.
|
|
137
216
|
*/
|
|
@@ -151,9 +230,9 @@ export type EnumBlobRefsOptions = BlobRefCheckOptions & {
|
|
|
151
230
|
*/
|
|
152
231
|
export type InferEnumBlobRefs<TOptions extends EnumBlobRefsOptions> = TOptions extends {
|
|
153
232
|
allowLegacy: true;
|
|
154
|
-
} ?
|
|
233
|
+
} ? InferTypedBlobRef<TOptions> | LegacyBlobRef : {
|
|
155
234
|
allowLegacy: boolean;
|
|
156
|
-
} extends TOptions ?
|
|
235
|
+
} extends TOptions ? InferTypedBlobRef<TOptions> | LegacyBlobRef : InferTypedBlobRef<TOptions>;
|
|
157
236
|
/**
|
|
158
237
|
* Generator that enumerates all {@link BlobRef}s (and, optionally,
|
|
159
238
|
* {@link LegacyBlobRef}s) found within a {@link LexValue}.
|
|
@@ -182,12 +261,12 @@ export type InferEnumBlobRefs<TOptions extends EnumBlobRefsOptions> = TOptions e
|
|
|
182
261
|
* }
|
|
183
262
|
*
|
|
184
263
|
* // Include legacy blob references
|
|
185
|
-
* for (const ref of enumBlobRefs(record, { allowLegacy: true })) {
|
|
186
|
-
* // ref may be BlobRef or LegacyBlobRef
|
|
264
|
+
* for (const ref of enumBlobRefs(record, { allowLegacy: true, strict: false })) {
|
|
265
|
+
* // ref may be BlobRef or LegacyBlobRef, with relaxed CID validation
|
|
187
266
|
* }
|
|
188
267
|
* ```
|
|
189
268
|
*/
|
|
190
269
|
export declare function enumBlobRefs(input: LexValue): Generator<BlobRef<RawCid>, void, unknown>;
|
|
191
270
|
export declare function enumBlobRefs<TOptions extends EnumBlobRefsOptions>(input: LexValue, options: TOptions): Generator<InferEnumBlobRefs<TOptions>, void, unknown>;
|
|
192
|
-
export declare function enumBlobRefs(input: LexValue, options?: EnumBlobRefsOptions): Generator<BlobRef
|
|
271
|
+
export declare function enumBlobRefs(input: LexValue, options?: EnumBlobRefsOptions): Generator<BlobRef, void, unknown>;
|
|
193
272
|
//# sourceMappingURL=blob.d.ts.map
|
package/dist/blob.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,GAAG,EACH,MAAM,EAIP,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAgBnC;;;;;;;;;GASG;AACH,MAAM,MAAM,OAAO,CAAC,GAAG,SAAS,GAAG,GAAG,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,aAAa,CAAA;AAE9E;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;AACnE,wBAAgB,SAAS,CAAC,QAAQ,SAAS,mBAAmB,EAC5D,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,QAAQ,GAChB,KAAK,IAAI,aAAa,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAA;AACvD,wBAAgB,SAAS,CACvB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC5B,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;AAU3B;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAA;AAClD,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;AAK/D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI7D;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,GAAG,CAAA;AAC9C,wBAAgB,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,GAAG,GAAG,SAAS,CAAA;AAM3D;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAA;AACvD,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;AAMpE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,YAAY,CAAC,GAAG,SAAS,GAAG,GAAG,GAAG,IAAI;IAChD,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,GAAG,CAAA;IACR,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,CAAC,QAAQ,SAAS,mBAAmB,IAChE,QAAQ,SAAS;IAAE,MAAM,EAAE,KAAK,CAAA;CAAE,GAC9B,YAAY,GACZ;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,SAAS,QAAQ,GAClC,YAAY,GACZ,YAAY,CAAC,MAAM,CAAC,CAAA;AAE5B;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAAC,MAAM,CAAC,CAAA;AAC7E,wBAAgB,cAAc,CAAC,QAAQ,SAAS,mBAAmB,EACjE,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,QAAQ,GAChB,KAAK,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAA;AACvC,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC5B,KAAK,IAAI,YAAY,CAAC,MAAM,CAAC,CAAA;AAqDhC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC5B,KAAK,IAAI,aAAa,CA8BxB;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,GAAG;IACtD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,CAAC,QAAQ,SAAS,mBAAmB,IAChE,QAAQ,SAAS;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GAClC,iBAAiB,CAAC,QAAQ,CAAC,GAAG,aAAa,GAC3C;IAAE,WAAW,EAAE,OAAO,CAAA;CAAE,SAAS,QAAQ,GACvC,iBAAiB,CAAC,QAAQ,CAAC,GAAG,aAAa,GAC3C,iBAAiB,CAAC,QAAQ,CAAC,CAAA;AAEnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,GACd,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;AAC5C,wBAAgB,YAAY,CAAC,QAAQ,SAAS,mBAAmB,EAC/D,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,QAAQ,GAChB,SAAS,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;AACxD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EACf,OAAO,CAAC,EAAE,mBAAmB,GAC5B,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA"}
|
package/dist/blob.js
CHANGED
|
@@ -1,14 +1,73 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isBlobRef = isBlobRef;
|
|
4
|
+
exports.getBlobMime = getBlobMime;
|
|
5
|
+
exports.getBlobSize = getBlobSize;
|
|
6
|
+
exports.getBlobCid = getBlobCid;
|
|
7
|
+
exports.getBlobCidString = getBlobCidString;
|
|
8
|
+
exports.isTypedBlobRef = isTypedBlobRef;
|
|
4
9
|
exports.isLegacyBlobRef = isLegacyBlobRef;
|
|
5
10
|
exports.enumBlobRefs = enumBlobRefs;
|
|
6
11
|
const cid_js_1 = require("./cid.js");
|
|
7
12
|
const object_js_1 = require("./object.js");
|
|
13
|
+
/**
|
|
14
|
+
* Options to use with {@link ifCid}, {@link validateCidString}, and related CID
|
|
15
|
+
* validation functions when validating CIDs in BlobRefs, in strict mode. This
|
|
16
|
+
* ensures that the CID is a {@link RawCid} (CID v1, raw multicodec, sha256
|
|
17
|
+
* multihash), which is the expected format for blob references in the AT
|
|
18
|
+
* Protocol data model.
|
|
19
|
+
*/
|
|
20
|
+
const STRICT_CID_CHECK_OPTIONS = { flavor: 'raw' };
|
|
8
21
|
// Number.isSafeInteger is actually safe to use with non-number values, so we
|
|
9
22
|
// can use it as a type guard.
|
|
10
23
|
const isSafeInteger = Number.isSafeInteger;
|
|
11
24
|
function isBlobRef(input, options) {
|
|
25
|
+
return input?.$type === 'blob'
|
|
26
|
+
? isTypedBlobRef(input, options)
|
|
27
|
+
: isLegacyBlobRef(input, options);
|
|
28
|
+
}
|
|
29
|
+
function getBlobMime(blob) {
|
|
30
|
+
return blob?.mimeType;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extracts the size (in bytes) from a {@link TypedBlobRef}. For
|
|
34
|
+
* {@link LegacyBlobRef}, size information is not available, so this function
|
|
35
|
+
* returns `undefined` for legacy refs.
|
|
36
|
+
*
|
|
37
|
+
* @note The size property, in blob refs, cannot be 100% trusted since the PDS
|
|
38
|
+
* might not have a local copy of the blob (to check the size against) and might
|
|
39
|
+
* just be passing through the blob ref from the client without validating it.
|
|
40
|
+
* So, while this function can be useful for getting size information when
|
|
41
|
+
* available, it should not be solely relied upon for critical functionality
|
|
42
|
+
* without additional validation.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const size = getBlobSize(blobRef)
|
|
47
|
+
* if (size !== undefined) {
|
|
48
|
+
* console.log(`Blob size: ${size} bytes`)
|
|
49
|
+
* } else {
|
|
50
|
+
* console.log('Size information not available for legacy blob ref')
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
function getBlobSize(blob) {
|
|
55
|
+
if ('$type' in blob && blob.size >= 0)
|
|
56
|
+
return blob.size;
|
|
57
|
+
// LegacyBlobRef doesn't have size information
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
function getBlobCid(blob) {
|
|
61
|
+
if (!blob)
|
|
62
|
+
return undefined;
|
|
63
|
+
return '$type' in blob ? blob.ref : (0, cid_js_1.parseCid)(blob.cid);
|
|
64
|
+
}
|
|
65
|
+
function getBlobCidString(blob) {
|
|
66
|
+
if (!blob)
|
|
67
|
+
return undefined;
|
|
68
|
+
return '$type' in blob ? blob.ref.toString() : blob.cid;
|
|
69
|
+
}
|
|
70
|
+
function isTypedBlobRef(input, options) {
|
|
12
71
|
if (!(0, object_js_1.isPlainObject)(input)) {
|
|
13
72
|
return false;
|
|
14
73
|
}
|
|
@@ -40,7 +99,7 @@ function isBlobRef(input, options) {
|
|
|
40
99
|
}
|
|
41
100
|
const cid = (0, cid_js_1.ifCid)(ref,
|
|
42
101
|
// Strict unless explicitly disabled
|
|
43
|
-
options?.strict === false ? undefined :
|
|
102
|
+
options?.strict === false ? undefined : STRICT_CID_CHECK_OPTIONS);
|
|
44
103
|
if (!cid) {
|
|
45
104
|
return false;
|
|
46
105
|
}
|
|
@@ -54,9 +113,6 @@ function isBlobRef(input, options) {
|
|
|
54
113
|
* - `mimeType` must be a non-empty string
|
|
55
114
|
* - No additional properties allowed
|
|
56
115
|
*
|
|
57
|
-
* @param input - The value to check
|
|
58
|
-
* @returns `true` if the input is a valid LegacyBlobRef
|
|
59
|
-
*
|
|
60
116
|
* @example
|
|
61
117
|
* ```typescript
|
|
62
118
|
* import { isLegacyBlobRef } from '@atproto/lex-data'
|
|
@@ -67,9 +123,9 @@ function isBlobRef(input, options) {
|
|
|
67
123
|
* }
|
|
68
124
|
* ```
|
|
69
125
|
*
|
|
70
|
-
* @see {@link
|
|
126
|
+
* @see {@link isTypedBlobRef} for checking the current blob reference format
|
|
71
127
|
*/
|
|
72
|
-
function isLegacyBlobRef(input) {
|
|
128
|
+
function isLegacyBlobRef(input, options) {
|
|
73
129
|
if (!(0, object_js_1.isPlainObject)(input)) {
|
|
74
130
|
return false;
|
|
75
131
|
}
|
|
@@ -85,7 +141,7 @@ function isLegacyBlobRef(input) {
|
|
|
85
141
|
return false;
|
|
86
142
|
}
|
|
87
143
|
}
|
|
88
|
-
if (!(0, cid_js_1.validateCidString)(cid)) {
|
|
144
|
+
if (!(0, cid_js_1.validateCidString)(cid, options?.strict === false ? undefined : STRICT_CID_CHECK_OPTIONS)) {
|
|
89
145
|
return false;
|
|
90
146
|
}
|
|
91
147
|
return true;
|
|
@@ -112,10 +168,10 @@ function* enumBlobRefs(input, options) {
|
|
|
112
168
|
if (visited.has(value))
|
|
113
169
|
continue;
|
|
114
170
|
visited.add(value);
|
|
115
|
-
if (
|
|
171
|
+
if (isTypedBlobRef(value, options)) {
|
|
116
172
|
yield value;
|
|
117
173
|
}
|
|
118
|
-
else if (includeLegacy && isLegacyBlobRef(value)) {
|
|
174
|
+
else if (includeLegacy && isLegacyBlobRef(value, options)) {
|
|
119
175
|
yield value;
|
|
120
176
|
}
|
|
121
177
|
else {
|
package/dist/blob.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blob.js","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"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","sourcesContent":["import { Cid, RawCid, ifCid, validateCidString } from './cid.js'\nimport { LexValue } from './lex.js'\nimport { isPlainObject, isPlainProto } from './object.js'\n\n// Number.isSafeInteger is actually safe to use with non-number values, so we\n// can use it as a type guard.\nconst isSafeInteger = Number.isSafeInteger as (v: unknown) => v is number\n\n/**\n * Reference to binary data (like images, videos, etc.) in the AT Protocol data model.\n *\n * A BlobRef is a {@link LexMap} with a specific structure that identifies binary\n * content by its content hash (CID), along with metadata about the content type\n * and size.\n *\n * @typeParam Ref - The type of CID reference, defaults to any {@link Cid}\n *\n * @example\n * ```typescript\n * import type { BlobRef } from '@atproto/lex-data'\n *\n * const imageRef: BlobRef = {\n * $type: 'blob',\n * mimeType: 'image/jpeg',\n * ref: cid, // CID of the blob content\n * size: 12345\n * }\n * ```\n *\n * @see {@link isBlobRef} to check if a value is a valid BlobRef\n * @see {@link LegacyBlobRef} for the older blob reference format\n */\nexport type BlobRef<Ref extends Cid = Cid> = {\n $type: 'blob'\n mimeType: string\n ref: Ref\n size: number\n}\n\n/**\n * Options for validating a {@link BlobRef}.\n */\nexport type BlobRefCheckOptions = {\n /**\n * If `false`, skips strict CID validation of {@link BlobRef.ref}, allowing\n * any valid CID. Otherwise, validates that the CID is v1, uses the raw\n * multicodec, and has a sha256 multihash.\n *\n * @default true\n */\n strict?: boolean\n}\n\n/**\n * Infers the BlobRef type based on the check options.\n *\n * @typeParam TOptions - The options used for checking\n */\nexport type InferCheckedBlobRef<TOptions extends BlobRefCheckOptions> =\n TOptions extends { strict: false }\n ? BlobRef\n : { strict: boolean } extends TOptions\n ? BlobRef\n : BlobRef<RawCid>\n\n/**\n * Type guard to check if a value is a valid {@link BlobRef}.\n *\n * Validates the structure of the input including:\n * - `$type` must be `'blob'`\n * - `mimeType` must be a valid MIME type string (containing '/')\n * - `size` must be a non-negative safe integer\n * - `ref` must be a valid CID (strict validation by default)\n *\n * @param input - The value to check\n * @param options - Optional validation options\n * @returns `true` if the input is a valid BlobRef\n *\n * @example\n * ```typescript\n * import { isBlobRef } from '@atproto/lex-data'\n *\n * if (isBlobRef(data)) {\n * console.log(data.mimeType) // e.g., 'image/jpeg'\n * console.log(data.size) // e.g., 12345\n * }\n *\n * // Allow any valid CID (not just raw CIDs)\n * if (isBlobRef(data, { strict: false })) {\n * // ...\n * }\n * ```\n */\nexport function isBlobRef(input: unknown): input is BlobRef<RawCid>\nexport function isBlobRef<TOptions extends BlobRefCheckOptions>(\n input: unknown,\n options: TOptions,\n): input is InferCheckedBlobRef<TOptions>\nexport function isBlobRef(\n input: unknown,\n options?: BlobRefCheckOptions,\n): input is BlobRef\nexport function isBlobRef(\n input: unknown,\n options?: BlobRefCheckOptions,\n): input is BlobRef {\n if (!isPlainObject(input)) {\n return false\n }\n\n if (input?.$type !== 'blob') {\n return false\n }\n\n const { mimeType, size, ref } = input\n // @NOTE Very basic mime validation\n if (typeof mimeType !== 'string' || !mimeType.includes('/')) {\n return false\n }\n\n if (size === -1 && options?.strict === false) {\n // In non-strict mode, allow size to be -1 to accommodate legacy blob refs\n // that don't include size information.\n } else if (!isSafeInteger(size) || size < 0) {\n return false\n }\n\n if (typeof ref !== 'object' || ref === null) {\n return false\n }\n\n for (const key in input) {\n if (\n key !== '$type' &&\n key !== 'mimeType' &&\n key !== 'ref' &&\n key !== 'size'\n ) {\n return false\n }\n }\n\n const cid = ifCid(\n ref,\n // Strict unless explicitly disabled\n options?.strict === false ? undefined : { flavor: 'raw' },\n )\n if (!cid) {\n return false\n }\n\n return true\n}\n\n/**\n * Legacy format for blob references used in older AT Protocol data.\n *\n * This is the older format that stores the CID as a string rather than\n * as a structured CID object. New code should use {@link BlobRef} instead.\n *\n * @example\n * ```typescript\n * import type { LegacyBlobRef } from '@atproto/lex-data'\n *\n * const legacyRef: LegacyBlobRef = {\n * cid: 'bafyreib...',\n * mimeType: 'image/jpeg'\n * }\n * ```\n *\n * @see {@link isLegacyBlobRef} to check if a value is a LegacyBlobRef\n * @see {@link BlobRef} for the current blob reference format\n * @deprecated Use {@link BlobRef} for new code\n */\nexport type LegacyBlobRef = {\n cid: string\n mimeType: string\n}\n\n/**\n * Type guard to check if a value is a valid {@link LegacyBlobRef}.\n *\n * Validates the structure of the input:\n * - `cid` must be a valid CID string\n * - `mimeType` must be a non-empty string\n * - No additional properties allowed\n *\n * @param input - The value to check\n * @returns `true` if the input is a valid LegacyBlobRef\n *\n * @example\n * ```typescript\n * import { isLegacyBlobRef } from '@atproto/lex-data'\n *\n * if (isLegacyBlobRef(data)) {\n * console.log(data.cid) // CID as string\n * console.log(data.mimeType) // e.g., 'image/jpeg'\n * }\n * ```\n *\n * @see {@link isBlobRef} for checking the current blob reference format\n */\nexport function isLegacyBlobRef(input: unknown): input is LegacyBlobRef {\n if (!isPlainObject(input)) {\n return false\n }\n\n const { cid, mimeType } = input\n if (typeof cid !== 'string') {\n return false\n }\n\n if (typeof mimeType !== 'string' || mimeType.length === 0) {\n return false\n }\n\n for (const key in input) {\n if (key !== 'cid' && key !== 'mimeType') {\n return false\n }\n }\n\n if (!validateCidString(cid)) {\n return false\n }\n\n return true\n}\n\n/**\n * Options for enumerating blob references within a {@link LexValue}.\n */\nexport type EnumBlobRefsOptions = BlobRefCheckOptions & {\n /**\n * If `true`, also yields {@link LegacyBlobRef} objects in addition to\n * {@link BlobRef} objects.\n *\n * @default false\n */\n allowLegacy?: boolean\n}\n\n/**\n * Infers the yielded type of {@link enumBlobRefs} based on options.\n *\n * @typeParam TOptions - The options used for enumeration\n */\nexport type InferEnumBlobRefs<TOptions extends EnumBlobRefsOptions> =\n TOptions extends { allowLegacy: true }\n ? InferCheckedBlobRef<TOptions> | LegacyBlobRef\n : { allowLegacy: boolean } extends TOptions\n ? InferCheckedBlobRef<TOptions> | LegacyBlobRef\n : InferCheckedBlobRef<TOptions>\n\n/**\n * Generator that enumerates all {@link BlobRef}s (and, optionally,\n * {@link LegacyBlobRef}s) found within a {@link LexValue}.\n *\n * Performs a deep traversal of the input value, yielding any blob references\n * found. This is useful for extracting all media references from a record.\n *\n * @param input - The LexValue to search for blob references\n * @param options - Optional configuration for the enumeration\n * @yields Each blob reference found in the input\n *\n * @example\n * ```typescript\n * import { enumBlobRefs } from '@atproto/lex-data'\n *\n * const record = {\n * text: 'Hello',\n * images: [\n * { $type: 'blob', mimeType: 'image/jpeg', ref: cid1, size: 1000 },\n * { $type: 'blob', mimeType: 'image/png', ref: cid2, size: 2000 }\n * ]\n * }\n *\n * for (const blobRef of enumBlobRefs(record)) {\n * console.log(blobRef.mimeType, blobRef.size)\n * }\n *\n * // Include legacy blob references\n * for (const ref of enumBlobRefs(record, { allowLegacy: true })) {\n * // ref may be BlobRef or LegacyBlobRef\n * }\n * ```\n */\nexport function enumBlobRefs(\n input: LexValue,\n): Generator<BlobRef<RawCid>, void, unknown>\nexport function enumBlobRefs<TOptions extends EnumBlobRefsOptions>(\n input: LexValue,\n options: TOptions,\n): Generator<InferEnumBlobRefs<TOptions>, void, unknown>\nexport function enumBlobRefs(\n input: LexValue,\n options?: EnumBlobRefsOptions,\n): Generator<BlobRef | LegacyBlobRef, void, unknown>\nexport function* enumBlobRefs(\n input: LexValue,\n options?: EnumBlobRefsOptions,\n): Generator<BlobRef | LegacyBlobRef, void, unknown> {\n // LegacyBlobRef not included by default\n const includeLegacy = options?.allowLegacy === true\n\n // Using a stack to avoid recursion depth issues.\n const stack: LexValue[] = [input]\n\n // Since we are using a stack, we could end-up in an infinite loop with cyclic\n // structures. Cyclic structures are not valid LexValues and should, thus,\n // never occur, but let's be safe.\n const visited = new Set<object>()\n\n do {\n const value = stack.pop()!\n\n if (value != null && typeof value === 'object') {\n if (Array.isArray(value)) {\n if (visited.has(value)) continue\n visited.add(value)\n stack.push(...value)\n } else if (isPlainProto(value)) {\n if (visited.has(value)) continue\n visited.add(value)\n if (isBlobRef(value, options)) {\n yield value\n } else if (includeLegacy && isLegacyBlobRef(value)) {\n yield value\n } else {\n for (const v of Object.values(value)) {\n if (v != null) stack.push(v)\n }\n }\n }\n }\n } while (stack.length > 0)\n\n // Optimization: ease GC's work\n visited.clear()\n}\n"]}
|
|
1
|
+
{"version":3,"file":"blob.js","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":";;AAkEA,8BAOC;AAaD,kCAEC;AAwBD,kCAIC;AAcD,gCAGC;AAiBD,4CAGC;AAkFD,wCAkDC;AA+CD,0CAiCC;AAuED,oCAyCC;AA7dD,qCAOiB;AAEjB,2CAAyD;AAEzD;;;;;;GAMG;AACH,MAAM,wBAAwB,GAAoB,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;AAEnE,6EAA6E;AAC7E,8BAA8B;AAC9B,MAAM,aAAa,GAAG,MAAM,CAAC,aAA4C,CAAA;AA4CzE,SAAgB,SAAS,CACvB,KAAc,EACd,OAA6B;IAE7B,OAAQ,KAAa,EAAE,KAAK,KAAK,MAAM;QACrC,CAAC,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC;QAChC,CAAC,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;AACrC,CAAC;AAaD,SAAgB,WAAW,CAAC,IAAc;IACxC,OAAO,IAAI,EAAE,QAAQ,CAAA;AACvB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAgB,WAAW,CAAC,IAAa;IACvC,IAAI,OAAO,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAA;IACvD,8CAA8C;IAC9C,OAAO,SAAS,CAAA;AAClB,CAAC;AAcD,SAAgB,UAAU,CAAC,IAAc;IACvC,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAA;IAC3B,OAAO,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAA,iBAAQ,EAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACxD,CAAC;AAiBD,SAAgB,gBAAgB,CAAC,IAAc;IAC7C,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAA;IAC3B,OAAO,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAA;AACzD,CAAC;AAkFD,SAAgB,cAAc,CAC5B,KAAc,EACd,OAA6B;IAE7B,IAAI,CAAC,IAAA,yBAAa,EAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,KAAK,EAAE,KAAK,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,KAAK,CAAA;IACrC,mCAAmC;IACnC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5D,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,IAAI,KAAK,CAAC,CAAC,IAAI,OAAO,EAAE,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7C,0EAA0E;QAC1E,uCAAuC;IACzC,CAAC;SAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAA;IACd,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IACE,GAAG,KAAK,OAAO;YACf,GAAG,KAAK,UAAU;YAClB,GAAG,KAAK,KAAK;YACb,GAAG,KAAK,MAAM,EACd,CAAC;YACD,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,IAAA,cAAK,EACf,GAAG;IACH,oCAAoC;IACpC,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,wBAAwB,CACjE,CAAA;IACD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,KAAK,CAAA;IACd,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AA2BD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,eAAe,CAC7B,KAAc,EACd,OAA6B;IAE7B,IAAI,CAAC,IAAA,yBAAa,EAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;IAC/B,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAA;IACd,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YACxC,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,IACE,CAAC,IAAA,0BAAiB,EAChB,GAAG,EACH,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,wBAAwB,CACjE,EACD,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAuED,QAAe,CAAC,CAAC,YAAY,CAC3B,KAAe,EACf,OAA6B;IAE7B,wCAAwC;IACxC,MAAM,aAAa,GAAG,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;IAEnD,iDAAiD;IACjD,MAAM,KAAK,GAAe,CAAC,KAAK,CAAC,CAAA;IAEjC,8EAA8E;IAC9E,0EAA0E;IAC1E,kCAAkC;IAClC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;IAEjC,GAAG,CAAC;QACF,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAG,CAAA;QAE1B,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;oBAAE,SAAQ;gBAChC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;gBAClB,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAA;YACtB,CAAC;iBAAM,IAAI,IAAA,wBAAY,EAAC,KAAK,CAAC,EAAE,CAAC;gBAC/B,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;oBAAE,SAAQ;gBAChC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;gBAClB,IAAI,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;oBACnC,MAAM,KAAK,CAAA;gBACb,CAAC;qBAAM,IAAI,aAAa,IAAI,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;oBAC5D,MAAM,KAAK,CAAA;gBACb,CAAC;qBAAM,CAAC;oBACN,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;wBACrC,IAAI,CAAC,IAAI,IAAI;4BAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;oBAC9B,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC,QAAQ,KAAK,CAAC,MAAM,GAAG,CAAC,EAAC;IAE1B,+BAA+B;IAC/B,OAAO,CAAC,KAAK,EAAE,CAAA;AACjB,CAAC","sourcesContent":["import {\n CheckCidOptions,\n Cid,\n RawCid,\n ifCid,\n parseCid,\n validateCidString,\n} from './cid.js'\nimport { LexValue } from './lex.js'\nimport { isPlainObject, isPlainProto } from './object.js'\n\n/**\n * Options to use with {@link ifCid}, {@link validateCidString}, and related CID\n * validation functions when validating CIDs in BlobRefs, in strict mode. This\n * ensures that the CID is a {@link RawCid} (CID v1, raw multicodec, sha256\n * multihash), which is the expected format for blob references in the AT\n * Protocol data model.\n */\nconst STRICT_CID_CHECK_OPTIONS: CheckCidOptions = { flavor: 'raw' }\n\n// Number.isSafeInteger is actually safe to use with non-number values, so we\n// can use it as a type guard.\nconst isSafeInteger = Number.isSafeInteger as (v: unknown) => v is number\n\n/**\n * Reference to binary data (like images, videos, etc.) in the AT Protocol data\n * model.\n *\n * This type represents a reference to a blob of binary data, identified by its\n * content hash (CID) and accompanied by metadata such as MIME type and size.\n *\n * The {@link BlobRef} type is a union of the current {@link TypedBlobRef}\n * format and the legacy {@link LegacyBlobRef} format.\n */\nexport type BlobRef<Ref extends Cid = Cid> = TypedBlobRef<Ref> | LegacyBlobRef\n\n/**\n * Options for validating a {@link BlobRef}.\n */\nexport type BlobRefCheckOptions = {\n /**\n * If `false`, skips strict CID validation of {@link BlobRef.ref}, allowing\n * any valid CID. Otherwise, validates that the CID is v1, uses the raw\n * multicodec, and has a sha256 multihash.\n *\n * @default true\n */\n strict?: boolean\n}\n\n/**\n * Type guard to check if a value is a valid {@link BlobRef}, which can be\n * either a {@link TypedBlobRef} or a {@link LegacyBlobRef}. By default, strict\n * CID validation is applied to ensure that the CID in the blob reference is in\n * the expected format for the AT Protocol, but this can be relaxed with the\n * `strict: false` option.\n */\nexport function isBlobRef(input: unknown): input is BlobRef<RawCid>\nexport function isBlobRef<TOptions extends BlobRefCheckOptions>(\n input: unknown,\n options: TOptions,\n): input is LegacyBlobRef | InferTypedBlobRef<TOptions>\nexport function isBlobRef(\n input: unknown,\n options?: BlobRefCheckOptions,\n): input is BlobRef<RawCid>\nexport function isBlobRef(\n input: unknown,\n options?: BlobRefCheckOptions,\n): input is BlobRef {\n return (input as any)?.$type === 'blob'\n ? isTypedBlobRef(input, options)\n : isLegacyBlobRef(input, options)\n}\n\n/**\n * Extracts the MIME type from a {@link BlobRef}.\n *\n * @example\n * ```ts\n * const mimeType = getBlobMime(blobRef)\n * console.log(mimeType) // e.g., 'image/jpeg'\n * ```\n */\nexport function getBlobMime(blob: BlobRef): string\nexport function getBlobMime(blob?: BlobRef): string | undefined\nexport function getBlobMime(blob?: BlobRef): string | undefined {\n return blob?.mimeType\n}\n\n/**\n * Extracts the size (in bytes) from a {@link TypedBlobRef}. For\n * {@link LegacyBlobRef}, size information is not available, so this function\n * returns `undefined` for legacy refs.\n *\n * @note The size property, in blob refs, cannot be 100% trusted since the PDS\n * might not have a local copy of the blob (to check the size against) and might\n * just be passing through the blob ref from the client without validating it.\n * So, while this function can be useful for getting size information when\n * available, it should not be solely relied upon for critical functionality\n * without additional validation.\n *\n * @example\n * ```ts\n * const size = getBlobSize(blobRef)\n * if (size !== undefined) {\n * console.log(`Blob size: ${size} bytes`)\n * } else {\n * console.log('Size information not available for legacy blob ref')\n * }\n * ```\n */\nexport function getBlobSize(blob: BlobRef): number | undefined {\n if ('$type' in blob && blob.size >= 0) return blob.size\n // LegacyBlobRef doesn't have size information\n return undefined\n}\n\n/**\n * Extracts the {@link Cid} from a {@link BlobRef}.\n *\n * @throws If the input input is a {@link LegacyBlobRef} with an invalid CID string\n * @example\n * ```ts\n * const cid = getBlobCid(blobRef)\n * console.log(cid.bytes)\n * ```\n */\nexport function getBlobCid(blob: BlobRef): Cid\nexport function getBlobCid(blob?: BlobRef): Cid | undefined\nexport function getBlobCid(blob?: BlobRef): Cid | undefined {\n if (!blob) return undefined\n return '$type' in blob ? blob.ref : parseCid(blob.cid)\n}\n\n/**\n * Extracts the CID string from a {@link BlobRef}.\n *\n * This is similar to `getBlobCid(blob).toString()` but is more optimized since\n * the CID string is already available in the legacy format and we can avoid\n * parsing it into a CID object just to convert it back to a string.\n *\n * @example\n * ```ts\n * const cidString = getBlobCidString(blobRef)\n * console.log(cidString)\n * ```\n */\nexport function getBlobCidString(blob: BlobRef): string\nexport function getBlobCidString(blob?: BlobRef): string | undefined\nexport function getBlobCidString(blob?: BlobRef): string | undefined {\n if (!blob) return undefined\n return '$type' in blob ? blob.ref.toString() : blob.cid\n}\n\n/**\n * Reference to binary data (like images, videos, etc.) in the AT Protocol data model.\n *\n * A {@link TypedBlobRef} is a {@link LexMap} with a specific structure that\n * identifies binary content by its content hash (CID), along with metadata\n * about the content type and size.\n *\n * @typeParam Ref - The type of CID reference, defaults to any {@link Cid}\n *\n * @example\n * ```typescript\n * import type { TypedBlobRef } from '@atproto/lex-data'\n *\n * const imageRef: TypedBlobRef = {\n * $type: 'blob',\n * mimeType: 'image/jpeg',\n * ref: cid, // CID of the blob content\n * size: 12345\n * }\n * ```\n *\n * @see {@link isTypedBlobRef} to check if a value is a valid {@link TypedBlobRef}\n * @see {@link LegacyBlobRef} for the older blob reference format\n */\nexport type TypedBlobRef<Ref extends Cid = Cid> = {\n $type: 'blob'\n mimeType: string\n ref: Ref\n size: number\n}\n\n/**\n * Infers the BlobRef type based on the check options.\n *\n * @typeParam TOptions - The options used for checking\n */\nexport type InferTypedBlobRef<TOptions extends BlobRefCheckOptions> =\n TOptions extends { strict: false }\n ? TypedBlobRef\n : { strict: boolean } extends TOptions\n ? TypedBlobRef\n : TypedBlobRef<RawCid>\n\n/**\n * Type guard to check if a value is a valid {@link BlobRef}.\n *\n * Validates the structure of the input including:\n * - `$type` must be `'blob'`\n * - `mimeType` must be a valid MIME type string (containing '/')\n * - `size` must be a non-negative safe integer\n * - `ref` must be a valid CID (strict validation by default)\n *\n * @param input - The value to check\n * @param options - Optional validation options\n * @returns `true` if the input is a valid BlobRef\n *\n * @example\n * ```typescript\n * import { isTypedBlobRef } from '@atproto/lex-data'\n *\n * if (isTypedBlobRef(data)) {\n * console.log(data.mimeType) // e.g., 'image/jpeg'\n * console.log(data.size) // e.g., 12345\n * }\n *\n * // Allow any valid CID (not just raw CIDs)\n * if (isTypedBlobRef(data, { strict: false })) {\n * // ...\n * }\n * ```\n */\nexport function isTypedBlobRef(input: unknown): input is TypedBlobRef<RawCid>\nexport function isTypedBlobRef<TOptions extends BlobRefCheckOptions>(\n input: unknown,\n options: TOptions,\n): input is InferTypedBlobRef<TOptions>\nexport function isTypedBlobRef(\n input: unknown,\n options?: BlobRefCheckOptions,\n): input is TypedBlobRef<RawCid>\nexport function isTypedBlobRef(\n input: unknown,\n options?: BlobRefCheckOptions,\n): input is TypedBlobRef {\n if (!isPlainObject(input)) {\n return false\n }\n\n if (input?.$type !== 'blob') {\n return false\n }\n\n const { mimeType, size, ref } = input\n // @NOTE Very basic mime validation\n if (typeof mimeType !== 'string' || !mimeType.includes('/')) {\n return false\n }\n\n if (size === -1 && options?.strict === false) {\n // In non-strict mode, allow size to be -1 to accommodate legacy blob refs\n // that don't include size information.\n } else if (!isSafeInteger(size) || size < 0) {\n return false\n }\n\n if (typeof ref !== 'object' || ref === null) {\n return false\n }\n\n for (const key in input) {\n if (\n key !== '$type' &&\n key !== 'mimeType' &&\n key !== 'ref' &&\n key !== 'size'\n ) {\n return false\n }\n }\n\n const cid = ifCid(\n ref,\n // Strict unless explicitly disabled\n options?.strict === false ? undefined : STRICT_CID_CHECK_OPTIONS,\n )\n if (!cid) {\n return false\n }\n\n return true\n}\n\n/**\n * Legacy format for blob references used in older AT Protocol data.\n *\n * This is the older format that stores the CID as a string rather than\n * as a structured CID object. New code should use {@link BlobRef} instead.\n *\n * @example\n * ```typescript\n * import type { LegacyBlobRef } from '@atproto/lex-data'\n *\n * const legacyRef: LegacyBlobRef = {\n * cid: 'bafyreib...',\n * mimeType: 'image/jpeg'\n * }\n * ```\n *\n * @see {@link isLegacyBlobRef} to check if a value is a LegacyBlobRef\n * @see {@link BlobRef} for the current blob reference format\n * @deprecated Use {@link BlobRef} for new code\n */\nexport type LegacyBlobRef = {\n cid: string\n mimeType: string\n}\n\n/**\n * Type guard to check if a value is a valid {@link LegacyBlobRef}.\n *\n * Validates the structure of the input:\n * - `cid` must be a valid CID string\n * - `mimeType` must be a non-empty string\n * - No additional properties allowed\n *\n * @example\n * ```typescript\n * import { isLegacyBlobRef } from '@atproto/lex-data'\n *\n * if (isLegacyBlobRef(data)) {\n * console.log(data.cid) // CID as string\n * console.log(data.mimeType) // e.g., 'image/jpeg'\n * }\n * ```\n *\n * @see {@link isTypedBlobRef} for checking the current blob reference format\n */\nexport function isLegacyBlobRef(\n input: unknown,\n options?: BlobRefCheckOptions,\n): input is LegacyBlobRef {\n if (!isPlainObject(input)) {\n return false\n }\n\n const { cid, mimeType } = input\n if (typeof cid !== 'string') {\n return false\n }\n\n if (typeof mimeType !== 'string' || mimeType.length === 0) {\n return false\n }\n\n for (const key in input) {\n if (key !== 'cid' && key !== 'mimeType') {\n return false\n }\n }\n\n if (\n !validateCidString(\n cid,\n options?.strict === false ? undefined : STRICT_CID_CHECK_OPTIONS,\n )\n ) {\n return false\n }\n\n return true\n}\n\n/**\n * Options for enumerating blob references within a {@link LexValue}.\n */\nexport type EnumBlobRefsOptions = BlobRefCheckOptions & {\n /**\n * If `true`, also yields {@link LegacyBlobRef} objects in addition to\n * {@link BlobRef} objects.\n *\n * @default false\n */\n allowLegacy?: boolean\n}\n\n/**\n * Infers the yielded type of {@link enumBlobRefs} based on options.\n *\n * @typeParam TOptions - The options used for enumeration\n */\nexport type InferEnumBlobRefs<TOptions extends EnumBlobRefsOptions> =\n TOptions extends { allowLegacy: true }\n ? InferTypedBlobRef<TOptions> | LegacyBlobRef\n : { allowLegacy: boolean } extends TOptions\n ? InferTypedBlobRef<TOptions> | LegacyBlobRef\n : InferTypedBlobRef<TOptions>\n\n/**\n * Generator that enumerates all {@link BlobRef}s (and, optionally,\n * {@link LegacyBlobRef}s) found within a {@link LexValue}.\n *\n * Performs a deep traversal of the input value, yielding any blob references\n * found. This is useful for extracting all media references from a record.\n *\n * @param input - The LexValue to search for blob references\n * @param options - Optional configuration for the enumeration\n * @yields Each blob reference found in the input\n *\n * @example\n * ```typescript\n * import { enumBlobRefs } from '@atproto/lex-data'\n *\n * const record = {\n * text: 'Hello',\n * images: [\n * { $type: 'blob', mimeType: 'image/jpeg', ref: cid1, size: 1000 },\n * { $type: 'blob', mimeType: 'image/png', ref: cid2, size: 2000 }\n * ]\n * }\n *\n * for (const blobRef of enumBlobRefs(record)) {\n * console.log(blobRef.mimeType, blobRef.size)\n * }\n *\n * // Include legacy blob references\n * for (const ref of enumBlobRefs(record, { allowLegacy: true, strict: false })) {\n * // ref may be BlobRef or LegacyBlobRef, with relaxed CID validation\n * }\n * ```\n */\nexport function enumBlobRefs(\n input: LexValue,\n): Generator<BlobRef<RawCid>, void, unknown>\nexport function enumBlobRefs<TOptions extends EnumBlobRefsOptions>(\n input: LexValue,\n options: TOptions,\n): Generator<InferEnumBlobRefs<TOptions>, void, unknown>\nexport function enumBlobRefs(\n input: LexValue,\n options?: EnumBlobRefsOptions,\n): Generator<BlobRef, void, unknown>\nexport function* enumBlobRefs(\n input: LexValue,\n options?: EnumBlobRefsOptions,\n): Generator<BlobRef, void, unknown> {\n // LegacyBlobRef not included by default\n const includeLegacy = options?.allowLegacy === true\n\n // Using a stack to avoid recursion depth issues.\n const stack: LexValue[] = [input]\n\n // Since we are using a stack, we could end-up in an infinite loop with cyclic\n // structures. Cyclic structures are not valid LexValues and should, thus,\n // never occur, but let's be safe.\n const visited = new Set<object>()\n\n do {\n const value = stack.pop()!\n\n if (value != null && typeof value === 'object') {\n if (Array.isArray(value)) {\n if (visited.has(value)) continue\n visited.add(value)\n stack.push(...value)\n } else if (isPlainProto(value)) {\n if (visited.has(value)) continue\n visited.add(value)\n if (isTypedBlobRef(value, options)) {\n yield value\n } else if (includeLegacy && isLegacyBlobRef(value, options)) {\n yield value\n } else {\n for (const v of Object.values(value)) {\n if (v != null) stack.push(v)\n }\n }\n }\n }\n } while (stack.length > 0)\n\n // Optimization: ease GC's work\n visited.clear()\n}\n"]}
|
|
@@ -7,6 +7,7 @@ interface NodeJSBufferConstructor {
|
|
|
7
7
|
new (input: string, encoding?: Encoding): NodeJSBuffer;
|
|
8
8
|
from(input: Uint8Array | ArrayBuffer | ArrayBufferView): NodeJSBuffer<ArrayBuffer>;
|
|
9
9
|
from(input: string, encoding?: Encoding): NodeJSBuffer<ArrayBuffer>;
|
|
10
|
+
from<TArrayBuffer extends ArrayBufferLike>(arrayBuffer: WithImplicitCoercion<TArrayBuffer>, byteOffset?: number, length?: number): Buffer<TArrayBuffer>;
|
|
10
11
|
concat(list: readonly Uint8Array[], totalLength?: number): NodeJSBuffer;
|
|
11
12
|
byteLength(input: string, encoding?: Encoding): number;
|
|
12
13
|
prototype: NodeJSBuffer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodejs-buffer.d.ts","sourceRoot":"","sources":["../../src/lib/nodejs-buffer.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,CAAA;AAE/C,UAAU,YAAY,CAAC,YAAY,SAAS,eAAe,GAAG,eAAe,CAC3E,SAAQ,UAAU,CAAC,YAAY,CAAC;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;CACtC;AAED,UAAU,uBAAuB;IAC/B,KAAK,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAA;IACtD,IAAI,CACF,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,eAAe,GAChD,YAAY,CAAC,WAAW,CAAC,CAAA;IAC5B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAC,WAAW,CAAC,CAAA;IACnE,MAAM,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,YAAY,CAAA;IACvE,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;IACtD,SAAS,EAAE,YAAY,CAAA;CACxB;AAMD,eAAO,MAAM,YAAY,EAAE,uBAAuB,GAAG,IAIT,CAAA"}
|
|
1
|
+
{"version":3,"file":"nodejs-buffer.d.ts","sourceRoot":"","sources":["../../src/lib/nodejs-buffer.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,CAAA;AAE/C,UAAU,YAAY,CAAC,YAAY,SAAS,eAAe,GAAG,eAAe,CAC3E,SAAQ,UAAU,CAAC,YAAY,CAAC;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;CACtC;AAED,UAAU,uBAAuB;IAC/B,KAAK,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAA;IACtD,IAAI,CACF,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,eAAe,GAChD,YAAY,CAAC,WAAW,CAAC,CAAA;IAC5B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAC,WAAW,CAAC,CAAA;IACnE,IAAI,CAAC,YAAY,SAAS,eAAe,EACvC,WAAW,EAAE,oBAAoB,CAAC,YAAY,CAAC,EAC/C,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAAC,YAAY,CAAC,CAAA;IACvB,MAAM,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,YAAY,CAAA;IACvE,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;IACtD,SAAS,EAAE,YAAY,CAAA;CACxB;AAMD,eAAO,MAAM,YAAY,EAAE,uBAAuB,GAAG,IAIT,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodejs-buffer.js","sourceRoot":"","sources":["../../src/lib/nodejs-buffer.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"nodejs-buffer.js","sourceRoot":"","sources":["../../src/lib/nodejs-buffer.ts"],"names":[],"mappings":";;;AAwBA,6EAA6E;AAC7E,6EAA6E;AAC7E,qBAAqB;AACrB,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,EAAc,CAAA;AACjE,QAAA,YAAY,GACtB,UAAkB,EAAE,CAAC,MAAM,CAAC,EAAE,SAAS,YAAY,UAAU;IAC9D,YAAY,IAAK,UAAkB,CAAC,MAAM,CAAC;IACzC,CAAC,CAAG,UAAkB,CAAC,MAAM,CAA6B;IAC1D,CAAC,CAAC,iCAAiC,CAAC,IAAI,CAAA","sourcesContent":["type Encoding = 'utf8' | 'base64' | 'base64url'\n\ninterface NodeJSBuffer<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>\n extends Uint8Array<TArrayBuffer> {\n byteLength: number\n toString(encoding?: Encoding): string\n}\n\ninterface NodeJSBufferConstructor {\n new (input: string, encoding?: Encoding): NodeJSBuffer\n from(\n input: Uint8Array | ArrayBuffer | ArrayBufferView,\n ): NodeJSBuffer<ArrayBuffer>\n from(input: string, encoding?: Encoding): NodeJSBuffer<ArrayBuffer>\n from<TArrayBuffer extends ArrayBufferLike>(\n arrayBuffer: WithImplicitCoercion<TArrayBuffer>,\n byteOffset?: number,\n length?: number,\n ): Buffer<TArrayBuffer>\n concat(list: readonly Uint8Array[], totalLength?: number): NodeJSBuffer\n byteLength(input: string, encoding?: Encoding): number\n prototype: NodeJSBuffer\n}\n\n// Avoids a direct reference to Node.js Buffer, which might not exist in some\n// environments (e.g. browsers, Deno, Bun) to prevent bundlers from trying to\n// include polyfills.\nconst BUFFER = /*#__PURE__*/ (() => 'Bu' + 'f'.repeat(2) + 'er')() as 'Buffer'\nexport const NodeJSBuffer: NodeJSBufferConstructor | null =\n (globalThis as any)?.[BUFFER]?.prototype instanceof Uint8Array &&\n 'byteLength' in (globalThis as any)[BUFFER]\n ? ((globalThis as any)[BUFFER] as NodeJSBufferConstructor)\n : /* v8 ignore next -- @preserve */ null\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utf8-from-bytes.d.ts","sourceRoot":"","sources":["../src/utf8-from-bytes.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB,WACQ,UAAU,KAAG,MAAM,QAWf,CAAA;AAE1C,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAE7D"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.utf8FromBytesNode = void 0;
|
|
4
|
+
exports.utf8FromBytesNative = utf8FromBytesNative;
|
|
5
|
+
const nodejs_buffer_js_1 = require("./lib/nodejs-buffer.js");
|
|
6
|
+
const Buffer = nodejs_buffer_js_1.NodeJSBuffer;
|
|
7
|
+
exports.utf8FromBytesNode = Buffer
|
|
8
|
+
? function utf8FromBytesNode(bytes) {
|
|
9
|
+
// @NOTE Buffer.from(bytes) creates a copy of the ArrayBuffer. The following
|
|
10
|
+
// allows us to avoid the copy by creating a Buffer that shares the same
|
|
11
|
+
// memory as the input Uint8Array.
|
|
12
|
+
const buffer = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
13
|
+
return buffer.toString('utf8');
|
|
14
|
+
}
|
|
15
|
+
: /* v8 ignore next -- @preserve */ null;
|
|
16
|
+
function utf8FromBytesNative(bytes) {
|
|
17
|
+
return new TextDecoder('utf-8').decode(bytes);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=utf8-from-bytes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utf8-from-bytes.js","sourceRoot":"","sources":["../src/utf8-from-bytes.ts"],"names":[],"mappings":";;;AAkBA,kDAEC;AApBD,6DAAqD;AAErD,MAAM,MAAM,GAAG,+BAAY,CAAA;AAEd,QAAA,iBAAiB,GAAG,MAAM;IACrC,CAAC,CAAC,SAAS,iBAAiB,CAAC,KAAiB;QAC1C,4EAA4E;QAC5E,wEAAwE;QACxE,kCAAkC;QAClC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CACxB,KAAK,CAAC,MAAM,EACZ,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,UAAU,CACjB,CAAA;QACD,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAChC,CAAC;IACH,CAAC,CAAC,iCAAiC,CAAC,IAAI,CAAA;AAE1C,SAAgB,mBAAmB,CAAC,KAAiB;IACnD,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC/C,CAAC","sourcesContent":["import { NodeJSBuffer } from './lib/nodejs-buffer.js'\n\nconst Buffer = NodeJSBuffer\n\nexport const utf8FromBytesNode = Buffer\n ? function utf8FromBytesNode(bytes: Uint8Array): string {\n // @NOTE Buffer.from(bytes) creates a copy of the ArrayBuffer. The following\n // allows us to avoid the copy by creating a Buffer that shares the same\n // memory as the input Uint8Array.\n const buffer = Buffer.from(\n bytes.buffer,\n bytes.byteOffset,\n bytes.byteLength,\n )\n return buffer.toString('utf8')\n }\n : /* v8 ignore next -- @preserve */ null\n\nexport function utf8FromBytesNative(bytes: Uint8Array): string {\n return new TextDecoder('utf-8').decode(bytes)\n}\n"]}
|
package/dist/utf8.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import { Base64Alphabet } from './uint8array.js';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a Uint8Array to a UTF-8 string.
|
|
4
|
+
*
|
|
5
|
+
* Uses Node.js Buffer when available for performance, falling back to
|
|
6
|
+
* TextDecoder in environments without Buffer support.
|
|
7
|
+
*
|
|
8
|
+
* @param bytes - The binary data to decode
|
|
9
|
+
* @returns The decoded string (as UTF-16 JavaScript string)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { utf8FromBytes } from '@atproto/lex-data'
|
|
14
|
+
*
|
|
15
|
+
* const bytes = new Uint8Array([72, 101, 108, 108, 111])
|
|
16
|
+
* utf8FromBytes(bytes) // 'Hello'
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare const utf8FromBytes: (bytes: Uint8Array) => string;
|
|
2
20
|
/**
|
|
3
21
|
* Counts the number of grapheme clusters (user-perceived characters) in a string.
|
|
4
22
|
*
|
package/dist/utf8.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utf8.d.ts","sourceRoot":"","sources":["../src/utf8.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"utf8.d.ts","sourceRoot":"","sources":["../src/utf8.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAUhD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,aAAa,+BAA2C,CAAA;AAErE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MACiC,CAAA;AAU5E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MACuB,CAAA;AAEjE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,KAAK,MACK,CAAA;AAE5E;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,EAAE,CAC3B,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,cAAc,KACtB,MAC2E,CAAA"}
|
package/dist/utf8.js
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.utf8FromBase64 = exports.utf8ToBase64 = exports.utf8Len = exports.graphemeLen = void 0;
|
|
3
|
+
exports.utf8FromBase64 = exports.utf8ToBase64 = exports.utf8Len = exports.graphemeLen = exports.utf8FromBytes = void 0;
|
|
4
4
|
const utf8_from_base64_js_1 = require("./utf8-from-base64.js");
|
|
5
|
+
const utf8_from_bytes_js_1 = require("./utf8-from-bytes.js");
|
|
5
6
|
const utf8_grapheme_len_js_1 = require("./utf8-grapheme-len.js");
|
|
6
7
|
const utf8_len_js_1 = require("./utf8-len.js");
|
|
7
8
|
const utf8_to_base64_js_1 = require("./utf8-to-base64.js");
|
|
9
|
+
/**
|
|
10
|
+
* Converts a Uint8Array to a UTF-8 string.
|
|
11
|
+
*
|
|
12
|
+
* Uses Node.js Buffer when available for performance, falling back to
|
|
13
|
+
* TextDecoder in environments without Buffer support.
|
|
14
|
+
*
|
|
15
|
+
* @param bytes - The binary data to decode
|
|
16
|
+
* @returns The decoded string (as UTF-16 JavaScript string)
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { utf8FromBytes } from '@atproto/lex-data'
|
|
21
|
+
*
|
|
22
|
+
* const bytes = new Uint8Array([72, 101, 108, 108, 111])
|
|
23
|
+
* utf8FromBytes(bytes) // 'Hello'
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
exports.utf8FromBytes = utf8_from_bytes_js_1.utf8FromBytesNode ?? utf8_from_bytes_js_1.utf8FromBytesNative;
|
|
8
27
|
/**
|
|
9
28
|
* Counts the number of grapheme clusters (user-perceived characters) in a string.
|
|
10
29
|
*
|
package/dist/utf8.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utf8.js","sourceRoot":"","sources":["../src/utf8.ts"],"names":[],"mappings":";;;AACA,+DAG8B;AAC9B,iEAA+E;AAC/E,+CAA2D;AAC3D,2DAA4E;AAE5E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACU,QAAA,WAAW;AACtB,iCAAiC,CAAC,wCAAiB,IAAI,0CAAmB,CAAA;AAE5E,iCAAiC;AACjC,IAAI,mBAAW,KAAK,0CAAmB,EAAE,CAAC;IACxC,aAAa;IACb,OAAO,CAAC,IAAI,CACV,oHAAoH,CACrH,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACU,QAAA,OAAO;AAClB,iCAAiC,CAAC,yBAAW,IAAI,4BAAc,CAAA;AAEjE;;;;;;;;;;;;;;;GAeG;AACU,QAAA,YAAY;AACvB,iCAAiC,CAAC,oCAAgB,IAAI,wCAAoB,CAAA;AAE5E;;;;;;;;;;;;;;;GAeG;AACU,QAAA,cAAc;AAIzB,iCAAiC,CAAC,wCAAkB,IAAI,4CAAsB,CAAA","sourcesContent":["import { Base64Alphabet } from './uint8array.js'\nimport {\n utf8FromBase64Node,\n utf8FromBase64Ponyfill,\n} from './utf8-from-base64.js'\nimport { graphemeLenNative, graphemeLenPonyfill } from './utf8-grapheme-len.js'\nimport { utf8LenCompute, utf8LenNode } from './utf8-len.js'\nimport { utf8ToBase64Node, utf8ToBase64Ponyfill } from './utf8-to-base64.js'\n\n/**\n * Counts the number of grapheme clusters (user-perceived characters) in a string.\n *\n * Grapheme clusters represent what users typically think of as \"characters\",\n * handling complex cases like:\n * - Emoji with skin tones and ZWJ sequences (e.g., family emoji)\n * - Combined characters (e.g., 'e' + combining accent)\n * - Regional indicator pairs (flag emoji)\n *\n * Uses native {@link Intl.Segmenter} when available, falling back to a ponyfill.\n *\n * @param str - The string to measure\n * @returns The number of grapheme clusters\n *\n * @example\n * ```typescript\n * import { graphemeLen } from '@atproto/lex-data'\n *\n * graphemeLen('hello') // 5\n * graphemeLen('cafe\\u0301') // 4 (cafe with combining accent)\n * graphemeLen('\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F467}\\u{200D}\\u{1F466}') // 1 (family emoji)\n * ```\n */\nexport const graphemeLen: (str: string) => number =\n /* v8 ignore next -- @preserve */ graphemeLenNative ?? graphemeLenPonyfill\n\n/* v8 ignore next -- @preserve */\nif (graphemeLen === graphemeLenPonyfill) {\n /*#__PURE__*/\n console.warn(\n '[@atproto/lex-data]: Intl.Segmenter is not available in this environment. Falling back to ponyfill implementation.',\n )\n}\n\n/**\n * Calculates the UTF-8 byte length of a string.\n *\n * Returns the number of bytes the string would occupy when encoded as UTF-8.\n * This is important for Lexicon validation where schemas specify byte limits.\n *\n * Uses Node.js Buffer.byteLength when available for performance,\n * falling back to a computed implementation.\n *\n * @param str - The string to measure\n * @returns The UTF-8 byte length\n *\n * @example\n * ```typescript\n * import { utf8Len } from '@atproto/lex-data'\n *\n * utf8Len('hello') // 5 (ASCII: 1 byte per char)\n * utf8Len('\\u00e9') // 2 (e with accent: 2 bytes)\n * utf8Len('\\u{1F600}') // 4 (emoji: 4 bytes)\n * utf8Len('\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F467}\\u{200D}\\u{1F466}') // 25 (family emoji)\n * ```\n */\nexport const utf8Len: (string: string) => number =\n /* v8 ignore next -- @preserve */ utf8LenNode ?? utf8LenCompute\n\n/**\n * Encodes a UTF-8 string to base64.\n *\n * First encodes the string as UTF-8 bytes, then encodes those bytes as base64.\n *\n * @param str - The string to encode\n * @param alphabet - The base64 alphabet to use ('base64' or 'base64url')\n * @returns The base64-encoded string\n *\n * @example\n * ```typescript\n * import { utf8ToBase64 } from '@atproto/lex-data'\n *\n * utf8ToBase64('Hello') // 'SGVsbG8='\n * ```\n */\nexport const utf8ToBase64: (str: string, alphabet?: Base64Alphabet) => string =\n /* v8 ignore next -- @preserve */ utf8ToBase64Node ?? utf8ToBase64Ponyfill\n\n/**\n * Decodes a base64 string to UTF-8.\n *\n * Decodes the base64 to bytes, then interprets those bytes as UTF-8 text.\n *\n * @param b64 - The base64 string to decode\n * @param alphabet - The base64 alphabet to use ('base64' or 'base64url')\n * @returns The decoded UTF-8 string\n *\n * @example\n * ```typescript\n * import { utf8FromBase64 } from '@atproto/lex-data'\n *\n * utf8FromBase64('SGVsbG8=') // 'Hello'\n * ```\n */\nexport const utf8FromBase64: (\n b64: string,\n alphabet?: Base64Alphabet,\n) => string =\n /* v8 ignore next -- @preserve */ utf8FromBase64Node ?? utf8FromBase64Ponyfill\n"]}
|
|
1
|
+
{"version":3,"file":"utf8.js","sourceRoot":"","sources":["../src/utf8.ts"],"names":[],"mappings":";;;AACA,+DAG8B;AAC9B,6DAA6E;AAC7E,iEAA+E;AAC/E,+CAA2D;AAC3D,2DAA4E;AAE5E;;;;;;;;;;;;;;;;GAgBG;AACU,QAAA,aAAa,GAAG,sCAAiB,IAAI,wCAAmB,CAAA;AAErE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACU,QAAA,WAAW;AACtB,iCAAiC,CAAC,wCAAiB,IAAI,0CAAmB,CAAA;AAE5E,iCAAiC;AACjC,IAAI,mBAAW,KAAK,0CAAmB,EAAE,CAAC;IACxC,aAAa;IACb,OAAO,CAAC,IAAI,CACV,oHAAoH,CACrH,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACU,QAAA,OAAO;AAClB,iCAAiC,CAAC,yBAAW,IAAI,4BAAc,CAAA;AAEjE;;;;;;;;;;;;;;;GAeG;AACU,QAAA,YAAY;AACvB,iCAAiC,CAAC,oCAAgB,IAAI,wCAAoB,CAAA;AAE5E;;;;;;;;;;;;;;;GAeG;AACU,QAAA,cAAc;AAIzB,iCAAiC,CAAC,wCAAkB,IAAI,4CAAsB,CAAA","sourcesContent":["import { Base64Alphabet } from './uint8array.js'\nimport {\n utf8FromBase64Node,\n utf8FromBase64Ponyfill,\n} from './utf8-from-base64.js'\nimport { utf8FromBytesNative, utf8FromBytesNode } from './utf8-from-bytes.js'\nimport { graphemeLenNative, graphemeLenPonyfill } from './utf8-grapheme-len.js'\nimport { utf8LenCompute, utf8LenNode } from './utf8-len.js'\nimport { utf8ToBase64Node, utf8ToBase64Ponyfill } from './utf8-to-base64.js'\n\n/**\n * Converts a Uint8Array to a UTF-8 string.\n *\n * Uses Node.js Buffer when available for performance, falling back to\n * TextDecoder in environments without Buffer support.\n *\n * @param bytes - The binary data to decode\n * @returns The decoded string (as UTF-16 JavaScript string)\n *\n * @example\n * ```typescript\n * import { utf8FromBytes } from '@atproto/lex-data'\n *\n * const bytes = new Uint8Array([72, 101, 108, 108, 111])\n * utf8FromBytes(bytes) // 'Hello'\n * ```\n */\nexport const utf8FromBytes = utf8FromBytesNode ?? utf8FromBytesNative\n\n/**\n * Counts the number of grapheme clusters (user-perceived characters) in a string.\n *\n * Grapheme clusters represent what users typically think of as \"characters\",\n * handling complex cases like:\n * - Emoji with skin tones and ZWJ sequences (e.g., family emoji)\n * - Combined characters (e.g., 'e' + combining accent)\n * - Regional indicator pairs (flag emoji)\n *\n * Uses native {@link Intl.Segmenter} when available, falling back to a ponyfill.\n *\n * @param str - The string to measure\n * @returns The number of grapheme clusters\n *\n * @example\n * ```typescript\n * import { graphemeLen } from '@atproto/lex-data'\n *\n * graphemeLen('hello') // 5\n * graphemeLen('cafe\\u0301') // 4 (cafe with combining accent)\n * graphemeLen('\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F467}\\u{200D}\\u{1F466}') // 1 (family emoji)\n * ```\n */\nexport const graphemeLen: (str: string) => number =\n /* v8 ignore next -- @preserve */ graphemeLenNative ?? graphemeLenPonyfill\n\n/* v8 ignore next -- @preserve */\nif (graphemeLen === graphemeLenPonyfill) {\n /*#__PURE__*/\n console.warn(\n '[@atproto/lex-data]: Intl.Segmenter is not available in this environment. Falling back to ponyfill implementation.',\n )\n}\n\n/**\n * Calculates the UTF-8 byte length of a string.\n *\n * Returns the number of bytes the string would occupy when encoded as UTF-8.\n * This is important for Lexicon validation where schemas specify byte limits.\n *\n * Uses Node.js Buffer.byteLength when available for performance,\n * falling back to a computed implementation.\n *\n * @param str - The string to measure\n * @returns The UTF-8 byte length\n *\n * @example\n * ```typescript\n * import { utf8Len } from '@atproto/lex-data'\n *\n * utf8Len('hello') // 5 (ASCII: 1 byte per char)\n * utf8Len('\\u00e9') // 2 (e with accent: 2 bytes)\n * utf8Len('\\u{1F600}') // 4 (emoji: 4 bytes)\n * utf8Len('\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F467}\\u{200D}\\u{1F466}') // 25 (family emoji)\n * ```\n */\nexport const utf8Len: (string: string) => number =\n /* v8 ignore next -- @preserve */ utf8LenNode ?? utf8LenCompute\n\n/**\n * Encodes a UTF-8 string to base64.\n *\n * First encodes the string as UTF-8 bytes, then encodes those bytes as base64.\n *\n * @param str - The string to encode\n * @param alphabet - The base64 alphabet to use ('base64' or 'base64url')\n * @returns The base64-encoded string\n *\n * @example\n * ```typescript\n * import { utf8ToBase64 } from '@atproto/lex-data'\n *\n * utf8ToBase64('Hello') // 'SGVsbG8='\n * ```\n */\nexport const utf8ToBase64: (str: string, alphabet?: Base64Alphabet) => string =\n /* v8 ignore next -- @preserve */ utf8ToBase64Node ?? utf8ToBase64Ponyfill\n\n/**\n * Decodes a base64 string to UTF-8.\n *\n * Decodes the base64 to bytes, then interprets those bytes as UTF-8 text.\n *\n * @param b64 - The base64 string to decode\n * @param alphabet - The base64 alphabet to use ('base64' or 'base64url')\n * @returns The decoded UTF-8 string\n *\n * @example\n * ```typescript\n * import { utf8FromBase64 } from '@atproto/lex-data'\n *\n * utf8FromBase64('SGVsbG8=') // 'Hello'\n * ```\n */\nexport const utf8FromBase64: (\n b64: string,\n alphabet?: Base64Alphabet,\n) => string =\n /* v8 ignore next -- @preserve */ utf8FromBase64Node ?? utf8FromBase64Ponyfill\n"]}
|