@atproto/lex 0.0.21 → 0.0.23
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 +22 -0
- package/README.md +78 -19
- package/package.json +9 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @atproto/lex
|
|
2
2
|
|
|
3
|
+
## 0.0.23
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`527f5d4`](https://github.com/bluesky-social/atproto/commit/527f5d4c5d0c9264c2ff6f23ad06a41163fc6809), [`527f5d4`](https://github.com/bluesky-social/atproto/commit/527f5d4c5d0c9264c2ff6f23ad06a41163fc6809), [`c4df84c`](https://github.com/bluesky-social/atproto/commit/c4df84cd78df68ee8cb7289e7b61b3a032ad484e), [`527f5d4`](https://github.com/bluesky-social/atproto/commit/527f5d4c5d0c9264c2ff6f23ad06a41163fc6809), [`a99dd58`](https://github.com/bluesky-social/atproto/commit/a99dd58b5fe1995e571cf5e7b0105355583efa93), [`e5e5bcf`](https://github.com/bluesky-social/atproto/commit/e5e5bcf85fbc0d418f05724d684e7265be6a0be9), [`ac6bd18`](https://github.com/bluesky-social/atproto/commit/ac6bd18f1dc3397dd29008eff2a1e40702a4e138), [`c5c6c7d`](https://github.com/bluesky-social/atproto/commit/c5c6c7dac3b08e5f63cc918f57705573028ad797)]:
|
|
8
|
+
- @atproto/lex-schema@0.0.17
|
|
9
|
+
- @atproto/lex-client@0.0.18
|
|
10
|
+
- @atproto/lex-builder@0.0.20
|
|
11
|
+
- @atproto/lex-installer@0.0.23
|
|
12
|
+
|
|
13
|
+
## 0.0.22
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [[`6a88461`](https://github.com/bluesky-social/atproto/commit/6a88461c5aa9486269f0769b7a3d52f384581786), [`6a88461`](https://github.com/bluesky-social/atproto/commit/6a88461c5aa9486269f0769b7a3d52f384581786), [`df8328c`](https://github.com/bluesky-social/atproto/commit/df8328c3c2f211fe16ccf58fa9f3968465cbf2b0), [`6a88461`](https://github.com/bluesky-social/atproto/commit/6a88461c5aa9486269f0769b7a3d52f384581786), [`6a88461`](https://github.com/bluesky-social/atproto/commit/6a88461c5aa9486269f0769b7a3d52f384581786), [`6a88461`](https://github.com/bluesky-social/atproto/commit/6a88461c5aa9486269f0769b7a3d52f384581786)]:
|
|
18
|
+
- @atproto/lex-client@0.0.17
|
|
19
|
+
- @atproto/lex-schema@0.0.16
|
|
20
|
+
- @atproto/lex-data@0.0.14
|
|
21
|
+
- @atproto/lex-builder@0.0.19
|
|
22
|
+
- @atproto/lex-installer@0.0.22
|
|
23
|
+
- @atproto/lex-json@0.0.14
|
|
24
|
+
|
|
3
25
|
## 0.0.21
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Type-safe Lexicon tooling for AT Protocol data.
|
|
|
10
10
|
- Tree-shaking and composition friendly
|
|
11
11
|
|
|
12
12
|
```typescript
|
|
13
|
-
// Build data with generated
|
|
13
|
+
// Build and validate data with generated utilities
|
|
14
14
|
|
|
15
15
|
const newPost = app.bsky.feed.post.$build({
|
|
16
16
|
text: 'Hello, world!',
|
|
@@ -222,7 +222,7 @@ function renderPost(p: app.bsky.feed.post.Main) {
|
|
|
222
222
|
|
|
223
223
|
### Building data
|
|
224
224
|
|
|
225
|
-
It is recommended to use the generated builders to create data that conforms to the schema.
|
|
225
|
+
It is recommended to use the generated builders to create data that conforms to the schema. TypeScript ensures that all required fields are present at compile time.
|
|
226
226
|
|
|
227
227
|
```typescript
|
|
228
228
|
import { l } from '@atproto/lex'
|
|
@@ -234,6 +234,10 @@ const post = app.bsky.feed.post.$build({
|
|
|
234
234
|
text: 'Hello, world!',
|
|
235
235
|
createdAt: l.toDatetimeString(new Date()),
|
|
236
236
|
})
|
|
237
|
+
|
|
238
|
+
// For runtime validation, use $parse()/$validate() instead
|
|
239
|
+
const postWithDefaults = app.bsky.feed.post.$parse(post)
|
|
240
|
+
app.bsky.feed.post.$validate(post)
|
|
237
241
|
```
|
|
238
242
|
|
|
239
243
|
### Validation Helpers
|
|
@@ -326,7 +330,7 @@ const result = app.bsky.feed.post.$validate(value)
|
|
|
326
330
|
value === result // true
|
|
327
331
|
```
|
|
328
332
|
|
|
329
|
-
#### `$safeParse(data)` - Parse a value against a schema and get the resulting value
|
|
333
|
+
#### `$safeParse(data, options?)` - Parse a value against a schema and get the resulting value
|
|
330
334
|
|
|
331
335
|
Returns a detailed validation result object without throwing:
|
|
332
336
|
|
|
@@ -347,9 +351,19 @@ if (result.success) {
|
|
|
347
351
|
}
|
|
348
352
|
```
|
|
349
353
|
|
|
354
|
+
All schema methods that perform validation (`$parse`, `$safeParse`, `$validate`, `$safeValidate`) accept an optional `{ strict }` option. When `strict` is `false`, validation becomes more lenient: datetime string format checks are relaxed (e.g. datetimes without timezones are accepted; other string formats remain strict), blob MIME type and size constraints are not enforced, and non-raw CIDs are allowed in blob references. This is primarily used internally by the XRPC client when `strictResponseProcessing` is disabled, but can also be used directly:
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
// Strict mode (default) - rejects datetime without timezone
|
|
358
|
+
app.bsky.feed.post.$safeParse(data) // { strict: true } is the default
|
|
359
|
+
|
|
360
|
+
// Non-strict mode - accepts more lenient data
|
|
361
|
+
app.bsky.feed.post.$safeParse(data, { strict: false })
|
|
362
|
+
```
|
|
363
|
+
|
|
350
364
|
#### `$build(data)` - Build with Defaults
|
|
351
365
|
|
|
352
|
-
Builds data
|
|
366
|
+
Builds data by adding the `$type` property and properly types the result. Note that `$build()` does not perform validation - use `$parse()` if you need validation:
|
|
353
367
|
|
|
354
368
|
```typescript
|
|
355
369
|
import { l } from '@atproto/lex'
|
|
@@ -506,6 +520,8 @@ if (result.success) {
|
|
|
506
520
|
}
|
|
507
521
|
```
|
|
508
522
|
|
|
523
|
+
Both `xrpc()` and `xrpcSafe()` accept `validateRequest`, `validateResponse`, and `strictResponseProcessing` options to control validation and strictness per-call. See [Validation and Strictness Options](#validation-and-strictness-options) for details.
|
|
524
|
+
|
|
509
525
|
## Client API
|
|
510
526
|
|
|
511
527
|
The `Client` class provides high-level helpers for common AT Protocol "repo" operations: `create()`, `get()`, `put()`, `delete()`, `list()`, `uploadBlob()`, and more. A `Client` instance is typically useful for making requests in the context of an authenticated user session, as it automatically handles headers and provides default values based on the authenticated user's DID.
|
|
@@ -574,6 +590,29 @@ const client = new Client(session, {
|
|
|
574
590
|
})
|
|
575
591
|
```
|
|
576
592
|
|
|
593
|
+
#### Validation and Strictness Options
|
|
594
|
+
|
|
595
|
+
The `Client` constructor accepts options to control request/response validation and how invalid Lex data is handled. These defaults apply to all XRPC calls made through the client, and can be overridden per-call via `client.call()`, `client.xrpc()` or `client.xrpcSafe()`.
|
|
596
|
+
|
|
597
|
+
```typescript
|
|
598
|
+
const client = new Client(session, {
|
|
599
|
+
// Validate requests against the method's input schema (default: false)
|
|
600
|
+
validateRequest: true,
|
|
601
|
+
|
|
602
|
+
// Validate responses against the method's output schema (default: true)
|
|
603
|
+
validateResponse: true,
|
|
604
|
+
|
|
605
|
+
// Strictly process responses according to Lex encoding rules. When set to
|
|
606
|
+
// false, accepts responses containing invalid Lex data such as floats or
|
|
607
|
+
// malformed $bytes/$link objects (default: true)
|
|
608
|
+
strictResponseProcessing: false,
|
|
609
|
+
})
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
- **`validateRequest`** — When `true`, outgoing request bodies are validated against the Lexicon input schema before sending. Useful in development to catch errors early. Default: `false`.
|
|
613
|
+
- **`validateResponse`** — When `true`, incoming response bodies are validated against the Lexicon output schema. Disabling this can improve performance when you trust the upstream service. Default: `true`.
|
|
614
|
+
- **`strictResponseProcessing`** — When `true` (default), the client will strictly process responses according to Lex encoding rules, rejecting responses containing invalid Lex data (e.g. floating-point numbers, malformed `$bytes` or `$link` objects). When `false`, the client accepts such responses in a lenient mode: invalid values are returned as-is rather than being rejected or converted, `datetime` string format checks become more lenient (e.g. datetimes without timezones are accepted) while other string formats remain strict, blob MIME type and size constraints are not enforced, and legacy blob references are coerced into standard `BlobRef` objects. Default: `true`.
|
|
615
|
+
|
|
577
616
|
### Core Methods
|
|
578
617
|
|
|
579
618
|
#### `client.call()`
|
|
@@ -603,7 +642,6 @@ const timeline = await client.call(
|
|
|
603
642
|
},
|
|
604
643
|
{
|
|
605
644
|
signal: abortSignal,
|
|
606
|
-
headers: { 'custom-header': 'value' },
|
|
607
645
|
},
|
|
608
646
|
)
|
|
609
647
|
```
|
|
@@ -628,7 +666,8 @@ console.log(result.cid)
|
|
|
628
666
|
Options:
|
|
629
667
|
|
|
630
668
|
- `rkey` - Custom record key (auto-generated if not provided)
|
|
631
|
-
- `validate` -
|
|
669
|
+
- `validate` - Asks the PDS to validate the record against schema when processing the request
|
|
670
|
+
- `validateRequest` - Validate the record locally against schema before submitting the request
|
|
632
671
|
- `swapCommit` - CID for optimistic concurrency control
|
|
633
672
|
|
|
634
673
|
#### `client.get()`
|
|
@@ -669,6 +708,8 @@ await client.put(app.bsky.actor.profile, {
|
|
|
669
708
|
Options:
|
|
670
709
|
|
|
671
710
|
- `rkey` - Record key (required for non-literal keys)
|
|
711
|
+
- `validate` - Validate record against schema before updating (falls back to `validateRequest` option if not specified)
|
|
712
|
+
- `validateRequest` - Alternative way to enable validation (used if `validate` is not specified)
|
|
672
713
|
- `swapCommit` - Expected repo commit CID
|
|
673
714
|
- `swapRecord` - Expected record CID
|
|
674
715
|
|
|
@@ -725,7 +766,7 @@ The `xrpcSafe()` method returns a union type that includes the success case (`Xr
|
|
|
725
766
|
import {
|
|
726
767
|
Client,
|
|
727
768
|
XrpcResponseError,
|
|
728
|
-
|
|
769
|
+
XrpcInvalidResponseError,
|
|
729
770
|
XrpcInternalError,
|
|
730
771
|
} from '@atproto/lex'
|
|
731
772
|
import * as com from './lexicons/com.js'
|
|
@@ -743,20 +784,26 @@ if (result.success) {
|
|
|
743
784
|
} else {
|
|
744
785
|
// Handle failure - result is an XrpcFailure
|
|
745
786
|
if (result instanceof XrpcResponseError) {
|
|
746
|
-
// The server
|
|
747
|
-
|
|
787
|
+
// The server responded with an error status code (4xx or 5xx).
|
|
788
|
+
// This is used for all error responses, whether or not they have a valid XRPC error payload.
|
|
789
|
+
|
|
790
|
+
result.error // string (e.g. "HandleNotFound", "AuthenticationRequired", "UpstreamFailure", etc.)
|
|
748
791
|
result.message // string
|
|
749
792
|
result.response.status // number
|
|
750
793
|
result.response.headers // Headers
|
|
751
|
-
result.payload //
|
|
752
|
-
|
|
753
|
-
//
|
|
754
|
-
//
|
|
794
|
+
result.payload // undefined | { body: unknown; encoding: string }
|
|
795
|
+
|
|
796
|
+
// Coerce to a valid XRPC error payload using toJSON():
|
|
797
|
+
result.toJSON() // { error: string, message?: string }
|
|
798
|
+
} else if (result instanceof XrpcInvalidResponseError) {
|
|
799
|
+
// The response was truly invalid (3xx redirect, malformed JSON, schema mismatch, etc.).
|
|
800
|
+
// This is a more specific error for responses that are not processable.
|
|
801
|
+
|
|
755
802
|
result.error // "UpstreamFailure"
|
|
756
803
|
result.message // string
|
|
757
804
|
result.response.status // number
|
|
758
805
|
result.response.headers // Headers
|
|
759
|
-
result.payload //
|
|
806
|
+
result.payload // undefined | { body: unknown; encoding: string }
|
|
760
807
|
} else if (result instanceof XrpcInternalError) {
|
|
761
808
|
// Something went wrong on the client side (network error, etc.)
|
|
762
809
|
result.error // "InternalServerError"
|
|
@@ -776,9 +823,9 @@ if (result.success) {
|
|
|
776
823
|
|
|
777
824
|
The `XrpcFailure<M>` type is a union of three error classes:
|
|
778
825
|
|
|
779
|
-
1. **`XrpcResponseError`** - The server
|
|
826
|
+
1. **`XrpcResponseError`** - The server responded with a 4xx/5xx error status code. This is used for all error responses from the upstream server.
|
|
780
827
|
|
|
781
|
-
2. **`
|
|
828
|
+
2. **`XrpcInvalidResponseError`** - The upstream server returned a 2xx/3xx that does not comply with XRPC specifications for successful responses. A sub-class, `XrpcResponseValidationError`, is used for payload schema validation failures specifically.
|
|
782
829
|
|
|
783
830
|
3. **`XrpcInternalError`** - Client-side errors (network failures, timeouts, etc.)
|
|
784
831
|
|
|
@@ -857,6 +904,16 @@ console.log(response.headers)
|
|
|
857
904
|
console.log(response.body)
|
|
858
905
|
```
|
|
859
906
|
|
|
907
|
+
Validation and strictness options (`validateRequest`, `validateResponse`, `strictResponseProcessing`) can also be passed per-call to override the client defaults:
|
|
908
|
+
|
|
909
|
+
```typescript
|
|
910
|
+
const response = await client.xrpc(app.bsky.feed.getTimeline, {
|
|
911
|
+
params: { limit: 50 },
|
|
912
|
+
strictResponseProcessing: false, // Accept non-strict Lex data for this call
|
|
913
|
+
validateResponse: false, // Skip schema validation for this call
|
|
914
|
+
})
|
|
915
|
+
```
|
|
916
|
+
|
|
860
917
|
## Utilities
|
|
861
918
|
|
|
862
919
|
Various utilities for working with CIDs, datetime strings, string lengths, language tags, and low-level JSON encoding are exported from the package:
|
|
@@ -903,7 +960,7 @@ isLanguageString('en-US') // true
|
|
|
903
960
|
|
|
904
961
|
### Datetime Strings
|
|
905
962
|
|
|
906
|
-
Many AT Protocol records (such as posts, likes, and follows) include a `createdAt` field that expects a valid `DatetimeString`. While `new Date().toISOString()` produces a string that looks like a valid datetime, it is not guaranteed to always conform to the AT Protocol's [datetime format requirements](https://atproto.com/specs/lexicon#datetime) (for example, `Date` objects representing dates before year
|
|
963
|
+
Many AT Protocol records (such as posts, likes, and follows) include a `createdAt` field that expects a valid `DatetimeString`. While `new Date().toISOString()` produces a string that looks like a valid datetime, it is not guaranteed to always conform to the AT Protocol's [datetime format requirements](https://atproto.com/specs/lexicon#datetime) (for example, `Date` objects representing dates before year 0 or after year 9999 will produce non-conforming strings). To ensure correctness and type safety, use the `DatetimeString` utilities exported from `@atproto/lex`:
|
|
907
964
|
|
|
908
965
|
- **`toDatetimeString(date: Date)`** - Converts a `Date` object into a valid `DatetimeString`, throwing an `InvalidDatetimeError` if the date cannot be represented as a valid AT Protocol datetime.
|
|
909
966
|
- **`asDatetimeString(input: string)`** - Validates and casts an arbitrary string to `DatetimeString`, throwing an `InvalidDatetimeError` if the string does not conform.
|
|
@@ -1001,12 +1058,14 @@ if (isBlobRef(blobRef)) {
|
|
|
1001
1058
|
>
|
|
1002
1059
|
> ```typescript
|
|
1003
1060
|
> type LegacyBlobRef = {
|
|
1004
|
-
>
|
|
1061
|
+
> cid: string
|
|
1005
1062
|
> mimeType: string
|
|
1006
1063
|
> }
|
|
1007
1064
|
> ```
|
|
1008
1065
|
>
|
|
1009
1066
|
> These should no longer be used for new records, but existing records using this format might still be encountered. To handle legacy blob references when validating data, enable the `--allowLegacyBlobs` flag when generating TypeScript schemas with `lex build`. You can use `isLegacyBlobRef()` from `@atproto/lex` to discriminate legacy blob references.
|
|
1067
|
+
>
|
|
1068
|
+
> When using non-strict validation (e.g. `$safeParse(data, { strict: false })`), legacy blob references are automatically coerced into standard `BlobRef` objects with `size: -1`, even without `--allowLegacyBlobs`.
|
|
1010
1069
|
|
|
1011
1070
|
### Actions
|
|
1012
1071
|
|
|
@@ -1028,7 +1087,7 @@ Actions receive:
|
|
|
1028
1087
|
|
|
1029
1088
|
- `client` - The Client instance (to make XRPC calls)
|
|
1030
1089
|
- `input` - The input data for the action
|
|
1031
|
-
- `options` - Call options (signal
|
|
1090
|
+
- `options` - Call options (signal)
|
|
1032
1091
|
|
|
1033
1092
|
#### Using Actions
|
|
1034
1093
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/lex",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Lexicon tooling for AT",
|
|
6
6
|
"keywords": [
|
|
@@ -38,19 +38,20 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"tslib": "^2.8.1",
|
|
40
40
|
"yargs": "^17.0.0",
|
|
41
|
-
"@atproto/lex-builder": "^0.0.
|
|
42
|
-
"@atproto/lex-client": "^0.0.
|
|
43
|
-
"@atproto/lex-data": "^0.0.
|
|
44
|
-
"@atproto/lex-json": "^0.0.
|
|
45
|
-
"@atproto/lex-installer": "^0.0.
|
|
46
|
-
"@atproto/lex-schema": "^0.0.
|
|
41
|
+
"@atproto/lex-builder": "^0.0.20",
|
|
42
|
+
"@atproto/lex-client": "^0.0.18",
|
|
43
|
+
"@atproto/lex-data": "^0.0.14",
|
|
44
|
+
"@atproto/lex-json": "^0.0.14",
|
|
45
|
+
"@atproto/lex-installer": "^0.0.23",
|
|
46
|
+
"@atproto/lex-schema": "^0.0.17"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/yargs": "^17.0.33",
|
|
50
50
|
"vitest": "^4.0.16"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
|
-
"
|
|
53
|
+
"codegen": "./bin/lex build --override --indexFile --lexicons ./lexicons --out ./tests/lexicons --lib @atproto/lex-schema",
|
|
54
|
+
"prebuild": "pnpm run codegen",
|
|
54
55
|
"build": "tsc --build tsconfig.build.json",
|
|
55
56
|
"test": "vitest run"
|
|
56
57
|
}
|