@atproto/lex 0.1.2 → 0.1.3
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 +12 -0
- package/README.md +162 -95
- package/bin/lex +8 -6
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @atproto/lex
|
|
2
2
|
|
|
3
|
+
## 0.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#5006](https://github.com/bluesky-social/atproto/pull/5006) [`60721e6`](https://github.com/bluesky-social/atproto/commit/60721e69c8db193eb817c4238ac447505ac855bc) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Fix README
|
|
8
|
+
|
|
9
|
+
- [#5006](https://github.com/bluesky-social/atproto/pull/5006) [`60721e6`](https://github.com/bluesky-social/atproto/commit/60721e69c8db193eb817c4238ac447505ac855bc) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Update `yargs` depedency
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`60721e6`](https://github.com/bluesky-social/atproto/commit/60721e69c8db193eb817c4238ac447505ac855bc), [`60721e6`](https://github.com/bluesky-social/atproto/commit/60721e69c8db193eb817c4238ac447505ac855bc)]:
|
|
12
|
+
- @atproto/lex-schema@0.1.2
|
|
13
|
+
- @atproto/lex-client@0.1.3
|
|
14
|
+
|
|
3
15
|
## 0.1.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
> [!IMPORTANT]
|
|
2
|
-
>
|
|
3
|
-
> This package is currently in **preview**. The API and features are subject to change before the stable release. See the [Changelog](./CHANGELOG.md) for version history.
|
|
4
|
-
|
|
5
1
|
Type-safe Lexicon tooling for AT Protocol data.
|
|
6
2
|
|
|
7
3
|
- Fetch and manage Lexicon schemas, generate TypeScript validators
|
|
@@ -54,14 +50,16 @@ const posts = await client.list(app.bsky.feed.post, { limit: 10 })
|
|
|
54
50
|
- [Type definitions](#type-definitions)
|
|
55
51
|
- [Building data](#building-data)
|
|
56
52
|
- [Validation Helpers](#validation-helpers)
|
|
57
|
-
- [
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
- [
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
- [Record / typed-object helpers](#record--typed-object-helpers)
|
|
54
|
+
- [`$type` - Type Identifier](#type---type-identifier)
|
|
55
|
+
- [`$build(data)` - Build with Defaults](#builddata---build-with-defaults)
|
|
56
|
+
- [`$isTypeOf(data)` - Type Discriminator](#istypeofdata---type-discriminator)
|
|
57
|
+
- [Universal validation helpers](#universal-validation-helpers)
|
|
58
|
+
- [`$matches(data)` - Type Guard](#matchesdata---type-guard)
|
|
59
|
+
- [`$assert(data)` - Type-Narrowing Assertion](#assertdata---type-narrowing-assertion)
|
|
60
|
+
- [`$parse(data)` - Parse and Validate](#parsedata---parse-and-validate)
|
|
61
|
+
- [`$validate(data)` - Validate a value against the schema](#validatedata---validate-a-value-against-the-schema)
|
|
62
|
+
- [`$safeParse(data, options?)` - Parse a value against a schema and get the resulting value](#safeparsedata-options---parse-a-value-against-a-schema-and-get-the-resulting-value)
|
|
65
63
|
- [Data Model](#data-model)
|
|
66
64
|
- [Types](#types)
|
|
67
65
|
- [JSON Encoding](#json-encoding)
|
|
@@ -120,6 +118,7 @@ const posts = await client.list(app.bsky.feed.post, { limit: 10 })
|
|
|
120
118
|
- [Packaging Actions as a Library](#packaging-actions-as-a-library)
|
|
121
119
|
- [Best Practices for Actions](#best-practices-for-actions)
|
|
122
120
|
- [Standard Schema Compatibility](#standard-schema-compatibility)
|
|
121
|
+
- [Validating Generic Schemas with `$check`](#validating-generic-schemas-with-check)
|
|
123
122
|
- [License](#license)
|
|
124
123
|
|
|
125
124
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
|
@@ -233,15 +232,16 @@ Options:
|
|
|
233
232
|
- `--clear` - Clear output directory before generating
|
|
234
233
|
- `--override` - Override existing files (has no effect with --clear)
|
|
235
234
|
- `--no-pretty` - Don't run prettier on generated files (prettier is enabled by default)
|
|
236
|
-
- `--ignore-errors` -
|
|
237
|
-
- `--
|
|
235
|
+
- `--ignore-errors` - Skip files that fail to parse or compile instead of aborting the build
|
|
236
|
+
- `--ignore-invalid-lexicons` - Skip lexicon files that fail validation instead of exiting with an error
|
|
238
237
|
- `--exclude <patterns...>` - List of strings or regex patterns to exclude lexicon documents by their IDs
|
|
239
238
|
- `--include <patterns...>` - List of strings or regex patterns to include lexicon documents by their IDs
|
|
240
239
|
- `--lib <package>` - Package name of the library to import the lex schema utility "l" from (default: `@atproto/lex`)
|
|
241
|
-
- `--
|
|
242
|
-
- `--
|
|
243
|
-
- `--
|
|
244
|
-
- `--
|
|
240
|
+
- `--import-ext <ext>` - File extension to use for import statements in generated files (default: `.js`). Use `--import-ext ""` to generate extension-less imports
|
|
241
|
+
- `--file-ext <ext>` - File extension to use for generated files (default: `.ts`)
|
|
242
|
+
- `--index-file` - Generate an "index" file that re-exports all root-level namespaces (disabled by default)
|
|
243
|
+
- `--defs-export` - When some definitions conflict with child namespaces, export lexicon definitions under a separate `$defs` namespace (e.g. `com.example.foo.$defs`)
|
|
244
|
+
- `--no-default-export` - Disable generation of a `default` export of the `main` schema in each schema's namespace file (default exports are enabled by default; see [Tree-Shaking](#tree-shaking))
|
|
245
245
|
|
|
246
246
|
### Generated Schema Structure
|
|
247
247
|
|
|
@@ -286,11 +286,12 @@ app.bsky.feed.post.$validate(post)
|
|
|
286
286
|
|
|
287
287
|
### Validation Helpers
|
|
288
288
|
|
|
289
|
-
|
|
289
|
+
Generated namespaces expose a handful of `$`-prefixed helpers bound to the namespace's `main` schema. They come in two groups:
|
|
290
290
|
|
|
291
|
-
|
|
291
|
+
- [**Universal validation helpers**](#universal-validation-helpers) are available on every schema's `main`: `$matches`, `$assert`, `$check`, `$parse`, `$safeParse`, `$validate`, `$safeValidate` (and `$cast` / `$ifMatches`). These work for records, typed objects, queries, procedures, and subscriptions.
|
|
292
|
+
- [**Record / typed-object helpers**](#record--typed-object-helpers) are only emitted for record and typed-object schemas: `$type`, `$build`, `$isTypeOf`.
|
|
292
293
|
|
|
293
|
-
|
|
294
|
+
In addition, every generated namespace file exports a top-level `$nsid` constant containing the NSID of the lexicon document:
|
|
294
295
|
|
|
295
296
|
```typescript
|
|
296
297
|
import * as app from './lexicons/app.js'
|
|
@@ -298,9 +299,13 @@ import * as app from './lexicons/app.js'
|
|
|
298
299
|
console.log(app.bsky.feed.defs.$nsid) // 'app.bsky.feed.defs'
|
|
299
300
|
```
|
|
300
301
|
|
|
301
|
-
|
|
302
|
+
The Schema instance itself (for example `app.bsky.feed.post.main`) also exposes the underlying methods both with and without the `$` prefix (e.g. `main.parse()` and `main.$parse()`).
|
|
303
|
+
|
|
304
|
+
#### Record / typed-object helpers
|
|
302
305
|
|
|
303
|
-
|
|
306
|
+
##### `$type` - Type Identifier
|
|
307
|
+
|
|
308
|
+
Returns the `$type` string of the schema (only available on record and typed-object schemas):
|
|
304
309
|
|
|
305
310
|
```typescript
|
|
306
311
|
import * as app from './lexicons/app.js'
|
|
@@ -309,7 +314,52 @@ console.log(app.bsky.feed.post.$type) // 'app.bsky.feed.post'
|
|
|
309
314
|
console.log(app.bsky.actor.defs.profileViewBasic.$type) // 'app.bsky.actor.defs#profileViewBasic'
|
|
310
315
|
```
|
|
311
316
|
|
|
312
|
-
|
|
317
|
+
Prefer `$type` over hard-coding the equivalent string literal in your code. The constant is emitted exactly once per schema in the generated namespace file, so every reference reuses the same string instance. Inlining `'app.bsky.feed.post'` everywhere instead leaks the same string into every call site, increases bundle size, and creates a typo-prone source of drift between your code and the schema.
|
|
318
|
+
|
|
319
|
+
##### `$build(data)` - Build with Defaults
|
|
320
|
+
|
|
321
|
+
Builds data by adding the `$type` property and properly types the result. This also allows to declare a variable with the correct type without having to explicitly specify it.
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
import { l } from '@atproto/lex'
|
|
325
|
+
import * as app from './lexicons/app.js'
|
|
326
|
+
|
|
327
|
+
// The type of the "like" variable will be "app.bsky.feed.like.Main" (no need to explicitly specify the type)
|
|
328
|
+
const like = app.bsky.feed.like.$build({
|
|
329
|
+
subject: {
|
|
330
|
+
uri: 'at://did:plc:abc/app.bsky.feed.post/123',
|
|
331
|
+
cid: 'bafyrei...',
|
|
332
|
+
},
|
|
333
|
+
createdAt: l.currentDatetimeString(),
|
|
334
|
+
})
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
> [!NOTE]
|
|
338
|
+
>
|
|
339
|
+
> `$build()` does not perform validation, and expects properly typed input data - use `$parse()` if you need validation.
|
|
340
|
+
|
|
341
|
+
##### `$isTypeOf(data)` - Type Discriminator
|
|
342
|
+
|
|
343
|
+
Discriminates (pre-validated) data based on its `$type` property, without re-validating. This is especially useful when working with union types:
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { l } from '@atproto/lex'
|
|
347
|
+
import * as app from './lexicons/app.js'
|
|
348
|
+
|
|
349
|
+
declare const data:
|
|
350
|
+
| app.bsky.feed.post.Main
|
|
351
|
+
| app.bsky.feed.like.Main
|
|
352
|
+
| l.Unknown$TypedObject
|
|
353
|
+
|
|
354
|
+
// Discriminate by $type without re-validating
|
|
355
|
+
if (app.bsky.feed.post.$isTypeOf(data)) {
|
|
356
|
+
// data is a post
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
#### Universal validation helpers
|
|
361
|
+
|
|
362
|
+
##### `$matches(data)` - Type Guard
|
|
313
363
|
|
|
314
364
|
Returns `true` if data matches the schema, `false` otherwise. Acts as a TypeScript type guard:
|
|
315
365
|
|
|
@@ -317,19 +367,45 @@ Returns `true` if data matches the schema, `false` otherwise. Acts as a TypeScri
|
|
|
317
367
|
import { l } from '@atproto/lex'
|
|
318
368
|
import * as app from './lexicons/app.js'
|
|
319
369
|
|
|
320
|
-
const data = {
|
|
370
|
+
const data: unknown = {
|
|
321
371
|
$type: 'app.bsky.feed.post',
|
|
322
372
|
text: 'Hello!',
|
|
323
373
|
createdAt: l.currentDatetimeString(),
|
|
324
374
|
}
|
|
325
375
|
|
|
326
|
-
if (app.bsky.feed.post.$
|
|
376
|
+
if (app.bsky.feed.post.$matches(data)) {
|
|
327
377
|
// TypeScript knows data is a Post here
|
|
328
378
|
console.log(data.text)
|
|
329
379
|
}
|
|
330
380
|
```
|
|
331
381
|
|
|
332
|
-
|
|
382
|
+
> [!NOTE]
|
|
383
|
+
>
|
|
384
|
+
> Performs validation so [`$isTypeOf`](#istypeofdata---type-discriminator) is preferred for pre-validated & properly typed data.
|
|
385
|
+
|
|
386
|
+
##### `$assert(data)` - Type-Narrowing Assertion
|
|
387
|
+
|
|
388
|
+
Throws if `data` does not match the schema. When the schema is statically known (e.g. `app.bsky.feed.post`), TypeScript narrows the type of `data` after the call:
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
import { l } from '@atproto/lex'
|
|
392
|
+
import * as app from './lexicons/app.js'
|
|
393
|
+
|
|
394
|
+
const data: unknown = {
|
|
395
|
+
$type: 'app.bsky.feed.post',
|
|
396
|
+
text: 'Hello!',
|
|
397
|
+
createdAt: l.currentDatetimeString(),
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
app.bsky.feed.post.$assert(data)
|
|
401
|
+
|
|
402
|
+
// TypeScript now knows data is app.bsky.feed.post.Main
|
|
403
|
+
console.log(data.text)
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
For library code that operates on a schema parameter whose type cannot be fully expressed, see [Validating Generic Schemas with `$check`](#validating-generic-schemas-with-check).
|
|
407
|
+
|
|
408
|
+
##### `$parse(data)` - Parse and Validate
|
|
333
409
|
|
|
334
410
|
Validates and returns typed data, throwing an error if validation fails:
|
|
335
411
|
|
|
@@ -338,11 +414,12 @@ import { l } from '@atproto/lex'
|
|
|
338
414
|
import * as app from './lexicons/app.js'
|
|
339
415
|
|
|
340
416
|
try {
|
|
341
|
-
const post = app.bsky.feed.post.$
|
|
417
|
+
const post = app.bsky.feed.post.$parse({
|
|
342
418
|
$type: 'app.bsky.feed.post',
|
|
343
419
|
text: 'Hello!',
|
|
344
420
|
createdAt: l.currentDatetimeString(),
|
|
345
421
|
})
|
|
422
|
+
|
|
346
423
|
// post is now typed and validated
|
|
347
424
|
console.log(post.text)
|
|
348
425
|
} catch (error) {
|
|
@@ -354,7 +431,7 @@ try {
|
|
|
354
431
|
>
|
|
355
432
|
> The `$parse` method will apply defaults defined in the schema for optional fields, as well as data coercion (e.g., CID strings to Cid types). This means that the returned value might be different from the input data if defaults were applied. Use `$validate()` for value validation.
|
|
356
433
|
|
|
357
|
-
|
|
434
|
+
##### `$validate(data)` - Validate a value against the schema
|
|
358
435
|
|
|
359
436
|
Validates an existing value against a schema, returning the value itself if, and only if, it already matches the schema (ie. without applying defaults or coercion).
|
|
360
437
|
|
|
@@ -374,7 +451,7 @@ const result = app.bsky.feed.post.$validate(value)
|
|
|
374
451
|
value === result // true
|
|
375
452
|
```
|
|
376
453
|
|
|
377
|
-
|
|
454
|
+
##### `$safeParse(data, options?)` - Parse a value against a schema and get the resulting value
|
|
378
455
|
|
|
379
456
|
Returns a detailed validation result object without throwing:
|
|
380
457
|
|
|
@@ -405,43 +482,6 @@ app.bsky.feed.post.$safeParse(data) // { strict: true } is the default
|
|
|
405
482
|
app.bsky.feed.post.$safeParse(data, { strict: false })
|
|
406
483
|
```
|
|
407
484
|
|
|
408
|
-
#### `$build(data)` - Build with Defaults
|
|
409
|
-
|
|
410
|
-
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:
|
|
411
|
-
|
|
412
|
-
```typescript
|
|
413
|
-
import { l } from '@atproto/lex'
|
|
414
|
-
import * as app from './lexicons/app.js'
|
|
415
|
-
|
|
416
|
-
// The type of the "like" variable will be "app.bsky.feed.like.Main"
|
|
417
|
-
const like = app.bsky.feed.like.$build({
|
|
418
|
-
subject: {
|
|
419
|
-
uri: 'at://did:plc:abc/app.bsky.feed.post/123',
|
|
420
|
-
cid: 'bafyrei...',
|
|
421
|
-
},
|
|
422
|
-
createdAt: l.currentDatetimeString(),
|
|
423
|
-
})
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
#### `$isTypeOf(data)` - Type Discriminator
|
|
427
|
-
|
|
428
|
-
Discriminates (pre-validated) data based on its `$type` property, without re-validating. This is especially useful when working with union types:
|
|
429
|
-
|
|
430
|
-
```typescript
|
|
431
|
-
import { l } from '@atproto/lex'
|
|
432
|
-
import * as app from './lexicons/app.js'
|
|
433
|
-
|
|
434
|
-
declare const data:
|
|
435
|
-
| app.bsky.feed.post.Main
|
|
436
|
-
| app.bsky.feed.like.Main
|
|
437
|
-
| l.Unknown$TypedObject
|
|
438
|
-
|
|
439
|
-
// Discriminate by $type without re-validating
|
|
440
|
-
if (app.bsky.feed.post.$isTypeOf(data)) {
|
|
441
|
-
// data is a post
|
|
442
|
-
}
|
|
443
|
-
```
|
|
444
|
-
|
|
445
485
|
## Data Model
|
|
446
486
|
|
|
447
487
|
The AT Protocol uses a [data model](https://atproto.com/specs/data-model) that extends JSON with two additional data structures: **CIDs** (content-addressed links) and **bytes** (for raw data). This data model can be encoded either as JSON for XRPC (HTTP API) or as [CBOR](https://dasl.ing/drisl.html) for storage and authentication (see [`@atproto/lex-cbor`](../lex-cbor)).
|
|
@@ -710,7 +750,7 @@ console.log(result.cid)
|
|
|
710
750
|
Options:
|
|
711
751
|
|
|
712
752
|
- `rkey` - Custom record key (auto-generated if not provided)
|
|
713
|
-
- `validate` -
|
|
753
|
+
- `validate` - Tri-state instruction to the PDS. `true` forces server-side schema validation, `false` explicitly disables it, and `undefined` (default) lets the PDS decide (it validates only collections whose schemas it knows)
|
|
714
754
|
- `validateRequest` - Validate the record locally against schema before submitting the request
|
|
715
755
|
- `swapCommit` - CID for optimistic concurrency control
|
|
716
756
|
|
|
@@ -752,8 +792,8 @@ await client.put(app.bsky.actor.profile, {
|
|
|
752
792
|
Options:
|
|
753
793
|
|
|
754
794
|
- `rkey` - Record key (required for non-literal keys)
|
|
755
|
-
- `validate` -
|
|
756
|
-
- `validateRequest` -
|
|
795
|
+
- `validate` - Tri-state instruction to the PDS. `true` forces server-side schema validation, `false` explicitly disables it, and `undefined` (default) lets the PDS decide (it validates only collections whose schemas it knows)
|
|
796
|
+
- `validateRequest` - Validate the record locally against schema before submitting the request
|
|
757
797
|
- `swapCommit` - Expected repo commit CID
|
|
758
798
|
- `swapRecord` - Expected record CID
|
|
759
799
|
|
|
@@ -785,6 +825,11 @@ for (const record of result.records) {
|
|
|
785
825
|
console.log(record.uri, record.value.text)
|
|
786
826
|
}
|
|
787
827
|
|
|
828
|
+
// Records that failed local schema validation are returned separately
|
|
829
|
+
for (const invalid of result.invalid) {
|
|
830
|
+
console.warn('Invalid record:', invalid)
|
|
831
|
+
}
|
|
832
|
+
|
|
788
833
|
// Pagination
|
|
789
834
|
if (result.cursor) {
|
|
790
835
|
const nextPage = await client.list(app.bsky.feed.post, {
|
|
@@ -794,6 +839,12 @@ if (result.cursor) {
|
|
|
794
839
|
}
|
|
795
840
|
```
|
|
796
841
|
|
|
842
|
+
The result includes:
|
|
843
|
+
|
|
844
|
+
- `records` - Records that successfully validated against the schema
|
|
845
|
+
- `invalid` - Records returned by the server that failed local schema validation (raw `LexMap` values)
|
|
846
|
+
- `cursor` - Pagination cursor (if more results are available)
|
|
847
|
+
|
|
797
848
|
#### `client.applyWrites()`
|
|
798
849
|
|
|
799
850
|
Perform an atomic batch of create, update, and delete operations in a single request.
|
|
@@ -830,7 +881,7 @@ for (const result of response.body.results) {
|
|
|
830
881
|
Options:
|
|
831
882
|
|
|
832
883
|
- `repo` - Repository identifier (defaults to authenticated user's DID)
|
|
833
|
-
- `validate` -
|
|
884
|
+
- `validate` - Tri-state instruction to the PDS. `true` forces server-side schema validation, `false` explicitly disables it, and `undefined` (default) lets the PDS decide (it validates only collections whose schemas it knows)
|
|
834
885
|
- `swapCommit` - CID for optimistic concurrency control
|
|
835
886
|
|
|
836
887
|
> [!NOTE]
|
|
@@ -869,13 +920,23 @@ if (result.success) {
|
|
|
869
920
|
// Handle success
|
|
870
921
|
console.log(result.body)
|
|
871
922
|
} else {
|
|
872
|
-
// Handle failure - result is an XrpcFailure
|
|
923
|
+
// Handle failure - result is an XrpcFailure.
|
|
924
|
+
//
|
|
925
|
+
// All XrpcFailure subclasses inherit from XrpcError and share these members:
|
|
926
|
+
result.error // string error code (e.g. "HandleNotFound", "UpstreamFailure")
|
|
927
|
+
result.message // string
|
|
928
|
+
result.shouldRetry() // boolean - whether the error is transient
|
|
929
|
+
|
|
930
|
+
if (result.matchesSchemaErrors()) {
|
|
931
|
+
// Check if the error matches a declared error in the schema.
|
|
932
|
+
// TypeScript narrows `result.error` to one of the method's declared error codes.
|
|
933
|
+
result.error // "HandleNotFound"
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// Branch on the specific error class to access additional members:
|
|
873
937
|
if (result instanceof XrpcResponseError) {
|
|
874
938
|
// The server responded with an error status code (4xx or 5xx).
|
|
875
939
|
// This is used for all error responses, whether or not they have a valid XRPC error payload.
|
|
876
|
-
|
|
877
|
-
result.error // string (e.g. "HandleNotFound", "AuthenticationRequired", "UpstreamFailure", etc.)
|
|
878
|
-
result.message // string
|
|
879
940
|
result.response.status // number
|
|
880
941
|
result.response.headers // Headers
|
|
881
942
|
result.payload // undefined | { body: unknown; encoding: string }
|
|
@@ -885,25 +946,11 @@ if (result.success) {
|
|
|
885
946
|
} else if (result instanceof XrpcInvalidResponseError) {
|
|
886
947
|
// The response was truly invalid (3xx redirect, malformed JSON, schema mismatch, etc.).
|
|
887
948
|
// This is a more specific error for responses that are not processable.
|
|
888
|
-
|
|
889
|
-
result.error // "UpstreamFailure"
|
|
890
|
-
result.message // string
|
|
891
949
|
result.response.status // number
|
|
892
950
|
result.response.headers // Headers
|
|
893
951
|
result.payload // undefined | { body: unknown; encoding: string }
|
|
894
952
|
} else if (result instanceof XrpcInternalError) {
|
|
895
953
|
// Something went wrong on the client side (network error, etc.)
|
|
896
|
-
result.error // "InternalServerError"
|
|
897
|
-
result.message // string
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
// All XrpcFailure types have these properties:
|
|
901
|
-
result.shouldRetry() // boolean - whether the error is transient
|
|
902
|
-
|
|
903
|
-
if (result.matchesSchemaErrors()) {
|
|
904
|
-
// Check if the error matches a declared error in the schema.
|
|
905
|
-
// TypeScript knows this is a declared error for the method.
|
|
906
|
-
result.error // "HandleNotFound"
|
|
907
954
|
}
|
|
908
955
|
}
|
|
909
956
|
```
|
|
@@ -1041,9 +1088,9 @@ import {
|
|
|
1041
1088
|
isLanguageString, // Validate language tags (e.g., 'en', 'pt-BR')
|
|
1042
1089
|
|
|
1043
1090
|
// Low-level JSON encoding helpers
|
|
1044
|
-
parseLexLink, // { $link: string } → Cid
|
|
1091
|
+
parseLexLink, // { $link: string } → Cid | undefined
|
|
1045
1092
|
encodeLexLink, // Cid → { $link: string }
|
|
1046
|
-
parseLexBytes, // { $bytes: string } → Uint8Array
|
|
1093
|
+
parseLexBytes, // { $bytes: string } → Uint8Array | undefined
|
|
1047
1094
|
encodeLexBytes, // Uint8Array → { $bytes: string }
|
|
1048
1095
|
} from '@atproto/lex'
|
|
1049
1096
|
|
|
@@ -1313,7 +1360,7 @@ An `Action` is a function with this signature:
|
|
|
1313
1360
|
type Action<Input, Output> = (
|
|
1314
1361
|
client: Client,
|
|
1315
1362
|
input: Input,
|
|
1316
|
-
options:
|
|
1363
|
+
options: ActionOptions,
|
|
1317
1364
|
) => Output | Promise<Output>
|
|
1318
1365
|
```
|
|
1319
1366
|
|
|
@@ -1321,7 +1368,7 @@ Actions receive:
|
|
|
1321
1368
|
|
|
1322
1369
|
- `client` - The Client instance (to make XRPC calls)
|
|
1323
1370
|
- `input` - The input data for the action
|
|
1324
|
-
- `options` -
|
|
1371
|
+
- `options` - `ActionOptions` (currently just `{ signal?: AbortSignal }`)
|
|
1325
1372
|
|
|
1326
1373
|
#### Using Actions
|
|
1327
1374
|
|
|
@@ -1636,10 +1683,10 @@ export const updateProfile: Action<ProfileUpdate, void> = async (
|
|
|
1636
1683
|
},
|
|
1637
1684
|
})
|
|
1638
1685
|
|
|
1639
|
-
const current = app.bsky.actor.profile
|
|
1686
|
+
const current = app.bsky.actor.profile.$safeValidate(res.body.record)
|
|
1640
1687
|
|
|
1641
1688
|
// Merge updates with current profile (if valid)
|
|
1642
|
-
const updated = app.bsky.actor.profile
|
|
1689
|
+
const updated = app.bsky.actor.profile.$build({
|
|
1643
1690
|
...(current.success ? current.value : undefined),
|
|
1644
1691
|
...updates,
|
|
1645
1692
|
})
|
|
@@ -1744,6 +1791,26 @@ if ('value' in result) {
|
|
|
1744
1791
|
|
|
1745
1792
|
When validated through the Standard Schema interface, schemas operate in "parse" mode, meaning transformations like defaults and coercions are applied to the output.
|
|
1746
1793
|
|
|
1794
|
+
### Validating Generic Schemas with `$check`
|
|
1795
|
+
|
|
1796
|
+
`$check(data)` is the non-narrowing counterpart to [`$assert(data)`](#assertdata---type-narrowing-assertion): both throw when `data` does not match the schema, but `$check` does not refine the static type of its argument.
|
|
1797
|
+
|
|
1798
|
+
`$check` is rarely needed in application code — prefer `$assert`. It is intended for library-style code that takes a schema as a generic parameter, where TypeScript cannot satisfy the assertion-signature requirement and `$assert` produces the following error:
|
|
1799
|
+
|
|
1800
|
+
> 'schema' needs an explicit type annotation.
|
|
1801
|
+
> Assertions require every name in the call target to be declared with an explicit type annotation. `ts(2775)`
|
|
1802
|
+
|
|
1803
|
+
In that situation, switch to `$check`:
|
|
1804
|
+
|
|
1805
|
+
```typescript
|
|
1806
|
+
import type { Schema } from '@atproto/lex'
|
|
1807
|
+
|
|
1808
|
+
function ensureMatches<S extends Schema>(schema: S, data: unknown) {
|
|
1809
|
+
// schema.$assert(data) // ❌ ts(2775): needs an explicit type annotation
|
|
1810
|
+
schema.$check(data) // ✅ throws on invalid, no type narrowing
|
|
1811
|
+
}
|
|
1812
|
+
```
|
|
1813
|
+
|
|
1747
1814
|
## License
|
|
1748
1815
|
|
|
1749
1816
|
MIT or Apache2
|
package/bin/lex
CHANGED
|
@@ -69,37 +69,37 @@ yargs(hideBin(process.argv))
|
|
|
69
69
|
describe:
|
|
70
70
|
'package name of the library to import the lex schema utility "l" from',
|
|
71
71
|
},
|
|
72
|
-
|
|
72
|
+
'import-ext': {
|
|
73
73
|
type: 'string',
|
|
74
74
|
default: '.js',
|
|
75
75
|
describe:
|
|
76
76
|
'file extension to use for import statements in generated files (e.g. ".ts", ".mts", ".cts"). Use --import-ext "" to generate extension-less imports.',
|
|
77
77
|
},
|
|
78
|
-
|
|
78
|
+
'file-ext': {
|
|
79
79
|
type: 'string',
|
|
80
80
|
default: '.ts',
|
|
81
81
|
describe:
|
|
82
82
|
'file extension to use for generated files (e.g. ".ts", ".mts", ".cts")',
|
|
83
83
|
},
|
|
84
|
-
|
|
84
|
+
'index-file': {
|
|
85
85
|
type: 'boolean',
|
|
86
86
|
default: false,
|
|
87
87
|
describe:
|
|
88
88
|
'generate an "index.<fileExt>" file that exports all root-level namespaces',
|
|
89
89
|
},
|
|
90
|
-
|
|
90
|
+
'defs-export': {
|
|
91
91
|
type: 'boolean',
|
|
92
92
|
default: false,
|
|
93
93
|
describe:
|
|
94
94
|
'when some definitions conflict with child namespaces, this option allows to export lexicon definitions under a separate $defs namespace (e.g. com.example.foo.$defs)',
|
|
95
95
|
},
|
|
96
|
-
|
|
96
|
+
'default-export': {
|
|
97
97
|
type: 'boolean',
|
|
98
98
|
default: true,
|
|
99
99
|
describe:
|
|
100
100
|
'whether to generate a default export for the "main" lexicon definition schema in the parent namespace file',
|
|
101
101
|
},
|
|
102
|
-
|
|
102
|
+
'ignore-invalid-lexicons': {
|
|
103
103
|
type: 'boolean',
|
|
104
104
|
default: false,
|
|
105
105
|
describe:
|
|
@@ -159,4 +159,6 @@ yargs(hideBin(process.argv))
|
|
|
159
159
|
})
|
|
160
160
|
},
|
|
161
161
|
)
|
|
162
|
+
.strictCommands()
|
|
163
|
+
.demandCommand(1)
|
|
162
164
|
.parseAsync()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/lex",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=22"
|
|
6
6
|
},
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"tslib": "^2.8.1",
|
|
39
|
-
"yargs": "^
|
|
39
|
+
"yargs": "^18.0.0",
|
|
40
40
|
"@atproto/lex-builder": "^0.1.2",
|
|
41
|
-
"@atproto/lex-client": "^0.1.
|
|
41
|
+
"@atproto/lex-client": "^0.1.3",
|
|
42
42
|
"@atproto/lex-data": "^0.1.1",
|
|
43
43
|
"@atproto/lex-json": "^0.1.0",
|
|
44
44
|
"@atproto/lex-installer": "^0.1.0",
|
|
45
|
-
"@atproto/lex-schema": "^0.1.
|
|
45
|
+
"@atproto/lex-schema": "^0.1.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/yargs": "^17.0.33",
|