@atproto/lex 0.0.8 → 0.0.10
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 +28 -0
- package/README.md +311 -11
- package/bin/lex +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @atproto/lex
|
|
2
2
|
|
|
3
|
+
## 0.0.10
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`d78484f`](https://github.com/bluesky-social/atproto/commit/d78484f94d8ba1352ec66030115000d515c9dafe), [`d78484f`](https://github.com/bluesky-social/atproto/commit/d78484f94d8ba1352ec66030115000d515c9dafe), [`d78484f`](https://github.com/bluesky-social/atproto/commit/d78484f94d8ba1352ec66030115000d515c9dafe), [`d78484f`](https://github.com/bluesky-social/atproto/commit/d78484f94d8ba1352ec66030115000d515c9dafe), [`d78484f`](https://github.com/bluesky-social/atproto/commit/d78484f94d8ba1352ec66030115000d515c9dafe), [`d78484f`](https://github.com/bluesky-social/atproto/commit/d78484f94d8ba1352ec66030115000d515c9dafe)]:
|
|
8
|
+
- @atproto/lex-data@0.0.7
|
|
9
|
+
- @atproto/lex-schema@0.0.8
|
|
10
|
+
- @atproto/lex-builder@0.0.10
|
|
11
|
+
- @atproto/lex-client@0.0.8
|
|
12
|
+
- @atproto/lex-installer@0.0.10
|
|
13
|
+
- @atproto/lex-json@0.0.7
|
|
14
|
+
|
|
15
|
+
## 0.0.9
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [#4501](https://github.com/bluesky-social/atproto/pull/4501) [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add an `indexFile` option that allows generating an "index.ts" file that re-exports every tld namespaces.
|
|
20
|
+
|
|
21
|
+
- [#4501](https://github.com/bluesky-social/atproto/pull/4501) [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Export everything from `@atproto/lex-data` and `@atproto/lex-json`
|
|
22
|
+
|
|
23
|
+
- Updated dependencies [[`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98), [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98), [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98), [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98), [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98), [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98), [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98), [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98), [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98)]:
|
|
24
|
+
- @atproto/lex-builder@0.0.9
|
|
25
|
+
- @atproto/lex-client@0.0.7
|
|
26
|
+
- @atproto/lex-data@0.0.6
|
|
27
|
+
- @atproto/lex-schema@0.0.7
|
|
28
|
+
- @atproto/lex-installer@0.0.9
|
|
29
|
+
- @atproto/lex-json@0.0.6
|
|
30
|
+
|
|
3
31
|
## 0.0.8
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -53,7 +53,12 @@ app.bsky.actor.profile.$validate({
|
|
|
53
53
|
- [TypeScript Schemas](#typescript-schemas)
|
|
54
54
|
- [Generated Schema Structure](#generated-schema-structure)
|
|
55
55
|
- [Type definitions](#type-definitions)
|
|
56
|
+
- [Building data](#building-data)
|
|
56
57
|
- [Validation Helpers](#validation-helpers)
|
|
58
|
+
- [Data Model](#data-model)
|
|
59
|
+
- [Types](#types)
|
|
60
|
+
- [JSON Encoding](#json-encoding)
|
|
61
|
+
- [DAG-CBOR Encoding](#dag-cbor-encoding)
|
|
57
62
|
- [Client API](#client-api)
|
|
58
63
|
- [Creating a Client](#creating-a-client)
|
|
59
64
|
- [Core Methods](#core-methods)
|
|
@@ -61,6 +66,8 @@ app.bsky.actor.profile.$validate({
|
|
|
61
66
|
- [Authentication Methods](#authentication-methods)
|
|
62
67
|
- [Labeler Configuration](#labeler-configuration)
|
|
63
68
|
- [Low-Level XRPC](#low-level-xrpc)
|
|
69
|
+
- [Blob references](#blob-references)
|
|
70
|
+
- [Utilities](#utilities)
|
|
64
71
|
- [Advanced Usage](#advanced-usage)
|
|
65
72
|
- [Workflow Integration](#workflow-integration)
|
|
66
73
|
- [Tree-Shaking](#tree-shaking)
|
|
@@ -89,7 +96,7 @@ This creates:
|
|
|
89
96
|
|
|
90
97
|
> [!NOTE]
|
|
91
98
|
>
|
|
92
|
-
> The `lex` command might conflict with other binaries
|
|
99
|
+
> The `lex` command might conflict with other binaries installed on your system.
|
|
93
100
|
> If that happens, you can also run the CLI using `ts-lex`, `pnpm exec lex` or
|
|
94
101
|
> `npx @atproto/lex`.
|
|
95
102
|
|
|
@@ -193,6 +200,7 @@ Options:
|
|
|
193
200
|
- `--allowLegacyBlobs` - Allow generating schemas that accept legacy blob references (disabled by default; enable this if you encounter issues while processing records created a long time ago)
|
|
194
201
|
- `--importExt <ext>` - File extension to use for import statements in generated files (default: `.js`). Use `--importExt ""` to generate extension-less imports
|
|
195
202
|
- `--fileExt <ext>` - File extension to use for generated files (default: `.ts`)
|
|
203
|
+
- `--indexFile` - Generate an "index" file that re-exports all root-level namespaces (disabled by default)
|
|
196
204
|
|
|
197
205
|
### Generated Schema Structure
|
|
198
206
|
|
|
@@ -209,15 +217,25 @@ You can extract TypeScript types from the generated schemas for use in you appli
|
|
|
209
217
|
```typescript
|
|
210
218
|
import * as app from './lexicons/app.js'
|
|
211
219
|
|
|
212
|
-
|
|
213
|
-
type
|
|
220
|
+
function renderPost(p: app.bsky.feed.post.Main) {
|
|
221
|
+
console.log(p.$type) // 'app.bsky.feed.post'
|
|
222
|
+
console.log(p.text)
|
|
223
|
+
}
|
|
224
|
+
```
|
|
214
225
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
226
|
+
### Building data
|
|
227
|
+
|
|
228
|
+
It is recommended to use the generated builders to create data that conforms to the schema. This ensures that all required fields are present.
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
import * as app from './lexicons/app.js'
|
|
232
|
+
|
|
233
|
+
// variable type will be inferred as "app.bsky.feed.post.Main"
|
|
234
|
+
const post = app.bsky.feed.post.$build({
|
|
235
|
+
// No need to specify $type when using $build
|
|
236
|
+
text: 'Hello, world!',
|
|
219
237
|
createdAt: new Date().toISOString(),
|
|
220
|
-
}
|
|
238
|
+
})
|
|
221
239
|
```
|
|
222
240
|
|
|
223
241
|
### Validation Helpers
|
|
@@ -284,6 +302,10 @@ try {
|
|
|
284
302
|
}
|
|
285
303
|
```
|
|
286
304
|
|
|
305
|
+
> [!NOTE]
|
|
306
|
+
>
|
|
307
|
+
> 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. Disable this behavior by passing `{ allowTransform: false }` as the second argument to `$parse()`.
|
|
308
|
+
|
|
287
309
|
#### `$validate(data)` - Get Validation Result
|
|
288
310
|
|
|
289
311
|
Returns a detailed validation result object without throwing:
|
|
@@ -304,14 +326,18 @@ if (result.success) {
|
|
|
304
326
|
}
|
|
305
327
|
```
|
|
306
328
|
|
|
329
|
+
> [!NOTE]
|
|
330
|
+
>
|
|
331
|
+
> Like `$parse`, the `$validate` method will apply defaults and coercion. Disable this behavior by passing `{ allowTransform: false }` as the second argument to `$validate()`.
|
|
332
|
+
|
|
307
333
|
#### `$build(data)` - Build with Defaults
|
|
308
334
|
|
|
309
|
-
|
|
335
|
+
Builds data without needing to specify the `$type` property, and properly types the result:
|
|
310
336
|
|
|
311
337
|
```typescript
|
|
312
338
|
import * as app from './lexicons/app.js'
|
|
313
339
|
|
|
314
|
-
//
|
|
340
|
+
// The type of the "like" variable will be "app.bsky.feed.like.Main"
|
|
315
341
|
const like = app.bsky.feed.like.$build({
|
|
316
342
|
subject: {
|
|
317
343
|
uri: 'at://did:plc:abc/app.bsky.feed.post/123',
|
|
@@ -323,7 +349,7 @@ const like = app.bsky.feed.like.$build({
|
|
|
323
349
|
|
|
324
350
|
#### `$isTypeOf(data)` - Type Discriminator
|
|
325
351
|
|
|
326
|
-
Discriminates (
|
|
352
|
+
Discriminates (pre-validated) data based on its `$type` property, without re-validating. This is especially useful when working with union types:
|
|
327
353
|
|
|
328
354
|
```typescript
|
|
329
355
|
import { l } from '@atproto/lex'
|
|
@@ -340,6 +366,66 @@ if (app.bsky.feed.post.$isTypeOf(data)) {
|
|
|
340
366
|
}
|
|
341
367
|
```
|
|
342
368
|
|
|
369
|
+
## Data Model
|
|
370
|
+
|
|
371
|
+
The AT Protocol uses a [data model](https://atproto.com/specs/data-model) that extends JSON with two additional types: **CIDs** (content-addressed links) and **bytes**. This data is encoded as JSON for XRPC (HTTP API) or as [DAG-CBOR](https://ipld.io/docs/codecs/known/dag-cbor/) for storage and authentication (see [`@atproto/lex-cbor`](../lex-cbor)).
|
|
372
|
+
|
|
373
|
+
### Types
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
import type {
|
|
377
|
+
LexValue,
|
|
378
|
+
LexMap,
|
|
379
|
+
LexScalar,
|
|
380
|
+
TypedLexMap,
|
|
381
|
+
Cid,
|
|
382
|
+
} from '@atproto/lex'
|
|
383
|
+
import { isLexValue, isLexMap, isTypedLexMap, isCid } from '@atproto/lex'
|
|
384
|
+
|
|
385
|
+
// LexScalar: number | string | boolean | null | Cid | Uint8Array
|
|
386
|
+
// LexValue: LexScalar | LexValue[] | { [key: string]?: LexValue }
|
|
387
|
+
// LexMap: { [key: string]?: LexValue }
|
|
388
|
+
// TypedLexMap: LexMap & { $type: string }
|
|
389
|
+
// Cid: Content Identifier (link by hash)
|
|
390
|
+
|
|
391
|
+
if (isTypedLexMap(data)) {
|
|
392
|
+
console.log(data.$type) // some string
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### JSON Encoding
|
|
397
|
+
|
|
398
|
+
In JSON, CIDs are represented as `{"$link": "bafyrei..."}` and bytes as `{"$bytes": "base64..."}`:
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
import { lexParse, lexStringify, jsonToLex, lexToJson } from '@atproto/lex'
|
|
402
|
+
|
|
403
|
+
// Parse JSON string → data model (decodes $link and $bytes)
|
|
404
|
+
const data = lexParse('{"ref": {"$link": "bafyrei..."}}')
|
|
405
|
+
|
|
406
|
+
// Data model → JSON string (encodes CIDs and bytes)
|
|
407
|
+
const json = lexStringify({ ref: someCid, data: someBytes })
|
|
408
|
+
|
|
409
|
+
// Convert between parsed JSON objects and data model values
|
|
410
|
+
const lex = jsonToLex(jsonObject)
|
|
411
|
+
const obj = lexToJson(lexValue)
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### DAG-CBOR Encoding
|
|
415
|
+
|
|
416
|
+
Use `@atproto/lex-cbor` to encode/decode the data model to/from DAG-CBOR format for storage and authentication:
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
import { encode, decode } from '@atproto/lex-cbor'
|
|
420
|
+
import type { LexValue } from '@atproto/lex'
|
|
421
|
+
|
|
422
|
+
// Encode data model to DAG-CBOR bytes
|
|
423
|
+
const cborBytes = encode(someLexValue)
|
|
424
|
+
|
|
425
|
+
// Decode DAG-CBOR bytes to data model
|
|
426
|
+
const lexValue: LexValue = decode(cborBytes)
|
|
427
|
+
```
|
|
428
|
+
|
|
343
429
|
## Client API
|
|
344
430
|
|
|
345
431
|
### Creating a Client
|
|
@@ -372,6 +458,149 @@ const client = new Client(session)
|
|
|
372
458
|
|
|
373
459
|
For detailed OAuth setup, see the [@atproto/oauth-client](../../../oauth/oauth-client) documentation.
|
|
374
460
|
|
|
461
|
+
#### Authenticated Client with Password
|
|
462
|
+
|
|
463
|
+
For simpler use cases (CLI tools, scripts, server-to-server), you can use password-based authentication with `@atproto/lex-password-session`:
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
npm install @atproto/lex-password-session
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
import { Client } from '@atproto/lex'
|
|
471
|
+
import { PasswordSession } from '@atproto/lex-password-session'
|
|
472
|
+
import * as app from './lexicons/app.js'
|
|
473
|
+
|
|
474
|
+
// Create a session with app password credentials
|
|
475
|
+
const session = await PasswordSession.create({
|
|
476
|
+
service: 'https://bsky.social',
|
|
477
|
+
identifier: 'alice.bsky.social', // handle or email
|
|
478
|
+
password: 'xxxx-xxxx-xxxx-xxxx', // App password (not your main password)
|
|
479
|
+
|
|
480
|
+
// Called when session is created or refreshed - persist the session data
|
|
481
|
+
onUpdated: (data) => {
|
|
482
|
+
saveToStorage(data) // Your persistence logic
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
// Called when session becomes invalid - clean up stored data
|
|
486
|
+
onDeleted: (data) => {
|
|
487
|
+
removeFromStorage(data.did)
|
|
488
|
+
},
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
// Use the session with a Client
|
|
492
|
+
const client = new Client(session)
|
|
493
|
+
|
|
494
|
+
const profile = await client.call(app.bsky.actor.getProfile, {
|
|
495
|
+
actor: 'atproto.com',
|
|
496
|
+
})
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**Resuming a Session**
|
|
500
|
+
|
|
501
|
+
Resume a previously persisted session. The `resume()` method validates the session by refreshing it:
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
const savedData = loadFromStorage() // Your retrieval logic
|
|
505
|
+
|
|
506
|
+
// Resume validates the session by refreshing it
|
|
507
|
+
// Throws if the session is definitively invalid
|
|
508
|
+
const session = await PasswordSession.resume(savedData, {
|
|
509
|
+
onUpdated: (data) => saveToStorage(data),
|
|
510
|
+
onDeleted: (data) => removeFromStorage(data.did),
|
|
511
|
+
})
|
|
512
|
+
|
|
513
|
+
const client = new Client(session)
|
|
514
|
+
|
|
515
|
+
// Access session properties
|
|
516
|
+
console.log(session.did) // User's DID
|
|
517
|
+
console.log(session.handle) // User's handle
|
|
518
|
+
console.log(session.destroyed) // false (session is active)
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
**Logging Out**
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
await session.logout()
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
**Deleting a Session Without Resuming**
|
|
528
|
+
|
|
529
|
+
Delete a stored session without needing to resume it first:
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
const savedData = loadFromStorage()
|
|
533
|
+
|
|
534
|
+
// Delete the session directly - throws on transient errors (network, server down)
|
|
535
|
+
await PasswordSession.delete(savedData)
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
**Error Handling Hooks**
|
|
539
|
+
|
|
540
|
+
Handle transient errors (network issues, server unavailability) separately from permanent failures:
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
const session = await PasswordSession.create({
|
|
544
|
+
service: 'https://bsky.social',
|
|
545
|
+
identifier: 'alice.bsky.social',
|
|
546
|
+
password: 'xxxx-xxxx-xxxx-xxxx',
|
|
547
|
+
|
|
548
|
+
onUpdated: (data) => saveToStorage(data),
|
|
549
|
+
onDeleted: (data) => removeFromStorage(data.did),
|
|
550
|
+
|
|
551
|
+
// Called when refresh fails due to transient errors (network, server down)
|
|
552
|
+
// The session may still be valid - don't delete stored credentials
|
|
553
|
+
onUpdateFailure: (data, error) => {
|
|
554
|
+
console.warn('Session refresh failed, will retry:', error.message)
|
|
555
|
+
},
|
|
556
|
+
|
|
557
|
+
// Called when logout fails due to transient errors
|
|
558
|
+
// Consider retrying later to avoid orphaned sessions
|
|
559
|
+
onDeleteFailure: (data, error) => {
|
|
560
|
+
console.error('Logout failed, session may still be active:', error.message)
|
|
561
|
+
scheduleRetry(data) // Your retry logic
|
|
562
|
+
},
|
|
563
|
+
})
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Handling Two-Factor Authentication (2FA)**
|
|
567
|
+
|
|
568
|
+
> [!CAUTION]
|
|
569
|
+
>
|
|
570
|
+
> Two-factor authentication only applies when using **main account credentials**, which is **strongly discouraged**. Password authentication should be used with [app passwords](https://bsky.app/settings/app-passwords) only because they are designed for programmatic access (bots, scripts, CLI tools). For user-facing applications, use OAuth via [@atproto/oauth-client](../../../oauth/oauth-client) which provides better security and user control.
|
|
571
|
+
|
|
572
|
+
```typescript
|
|
573
|
+
import {
|
|
574
|
+
PasswordSession,
|
|
575
|
+
LexAuthFactorError,
|
|
576
|
+
} from '@atproto/lex-password-session'
|
|
577
|
+
|
|
578
|
+
async function loginWithMainCredentials(
|
|
579
|
+
identifier: string,
|
|
580
|
+
password: string,
|
|
581
|
+
authFactorToken?: string,
|
|
582
|
+
): Promise<PasswordSession> {
|
|
583
|
+
try {
|
|
584
|
+
return await PasswordSession.create({
|
|
585
|
+
service: 'https://bsky.social',
|
|
586
|
+
identifier,
|
|
587
|
+
password,
|
|
588
|
+
authFactorToken,
|
|
589
|
+
|
|
590
|
+
onUpdated: (data) => saveToStorage(data),
|
|
591
|
+
onDeleted: (data) => removeFromStorage(data.did),
|
|
592
|
+
})
|
|
593
|
+
} catch (err) {
|
|
594
|
+
if (err instanceof LexAuthFactorError && !authFactorToken) {
|
|
595
|
+
// 2FA required
|
|
596
|
+
const token = await promptUserFor2FACode(err.message)
|
|
597
|
+
return loginWithMainCredentials(identifier, password, token)
|
|
598
|
+
}
|
|
599
|
+
throw err
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
```
|
|
603
|
+
|
|
375
604
|
#### Creating a Client from Another Client
|
|
376
605
|
|
|
377
606
|
You can create a new `Client` instance from an existing client. The new client will share the same underlying configuration (authentication, headers, labelers, service proxy), with the ability to override specific settings.
|
|
@@ -767,6 +996,77 @@ console.log(response.headers)
|
|
|
767
996
|
console.log(response.body)
|
|
768
997
|
```
|
|
769
998
|
|
|
999
|
+
## Blob references
|
|
1000
|
+
|
|
1001
|
+
In AT Protocol, binary data (blobs) are referenced using `BlobRef`, which include metadata like MIME type and size. These references are what allow PDSs to determine which binary data ("files") is referenced by records.
|
|
1002
|
+
|
|
1003
|
+
```typescript
|
|
1004
|
+
import { BlobRef, isBlobRef } from '@atproto/lex'
|
|
1005
|
+
|
|
1006
|
+
const blobRef: BlobRef = {
|
|
1007
|
+
$type: 'blob',
|
|
1008
|
+
ref: parseCid('bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku'),
|
|
1009
|
+
mimeType: 'image/png',
|
|
1010
|
+
size: 12345,
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
if (isBlobRef(blobRef)) {
|
|
1014
|
+
console.log('Valid BlobRef:', blobRef.mimeType, blobRef.size)
|
|
1015
|
+
}
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
> [!NOTE]
|
|
1019
|
+
>
|
|
1020
|
+
> Historically, references to blobs were represented as simple objects with the following structure:
|
|
1021
|
+
>
|
|
1022
|
+
> ```typescript
|
|
1023
|
+
> type LegacyBlobRef = {
|
|
1024
|
+
> ref: string
|
|
1025
|
+
> mimeType: string
|
|
1026
|
+
> }
|
|
1027
|
+
> ```
|
|
1028
|
+
>
|
|
1029
|
+
> 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.
|
|
1030
|
+
|
|
1031
|
+
## Utilities
|
|
1032
|
+
|
|
1033
|
+
Various utilities for working with CIDs, string lengths, language tags, and low-level JSON encoding are available:
|
|
1034
|
+
|
|
1035
|
+
```typescript
|
|
1036
|
+
import {
|
|
1037
|
+
// CID utilities
|
|
1038
|
+
parseCid, // Parse CID string (throws on invalid)
|
|
1039
|
+
ifCid, // Coerce to Cid or null
|
|
1040
|
+
isCid, // Type guard for Cid values
|
|
1041
|
+
|
|
1042
|
+
// Blob references
|
|
1043
|
+
BlobRef, // { $type: 'blob', ref: Cid, mimeType: string, size: number }
|
|
1044
|
+
isBlobRef, // Type guard for BlobRef objects
|
|
1045
|
+
|
|
1046
|
+
// Equality
|
|
1047
|
+
lexEquals, // Deep equality (handles CIDs and bytes)
|
|
1048
|
+
|
|
1049
|
+
// String length for Lexicon validation
|
|
1050
|
+
graphemeLen, // Count user-perceived characters
|
|
1051
|
+
utf8Len, // Count UTF-8 bytes
|
|
1052
|
+
|
|
1053
|
+
// Language tag validation (BCP-47)
|
|
1054
|
+
isLanguageString, // Validate language tags (e.g., 'en', 'pt-BR')
|
|
1055
|
+
|
|
1056
|
+
// Low-level JSON encoding helpers
|
|
1057
|
+
parseLexLink, // { $link: string } → Cid
|
|
1058
|
+
encodeLexLink, // Cid → { $link: string }
|
|
1059
|
+
parseLexBytes, // { $bytes: string } → Uint8Array
|
|
1060
|
+
encodeLexBytes, // Uint8Array → { $bytes: string }
|
|
1061
|
+
} from '@atproto/lex'
|
|
1062
|
+
|
|
1063
|
+
// Examples
|
|
1064
|
+
const cid = parseCid('bafyreiabc...')
|
|
1065
|
+
graphemeLen('👨👩👧👦') // 1
|
|
1066
|
+
utf8Len('👨👩👧👦') // 25
|
|
1067
|
+
isLanguageString('en-US') // true
|
|
1068
|
+
```
|
|
1069
|
+
|
|
770
1070
|
## Advanced Usage
|
|
771
1071
|
|
|
772
1072
|
### Workflow Integration
|
package/bin/lex
CHANGED
|
@@ -94,6 +94,12 @@ yargs(hideBin(process.argv))
|
|
|
94
94
|
describe:
|
|
95
95
|
'file extension to use for generated files (e.g. ".ts", ".mts", ".cts")',
|
|
96
96
|
},
|
|
97
|
+
indexFile: {
|
|
98
|
+
type: 'boolean',
|
|
99
|
+
default: false,
|
|
100
|
+
describe:
|
|
101
|
+
'generate an "index.<fileExt>" file that exports all root-level namespaces',
|
|
102
|
+
},
|
|
97
103
|
ignoreInvalidLexicons: {
|
|
98
104
|
type: 'boolean',
|
|
99
105
|
default: false,
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("@atproto/lex-data"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("@atproto/lex-json"), exports);
|
|
4
6
|
tslib_1.__exportStar(require("@atproto/lex-schema"), exports);
|
|
5
7
|
tslib_1.__exportStar(require("@atproto/lex-client"), exports);
|
|
6
8
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,8DAAmC;AACnC,8DAAmC","sourcesContent":["export * from '@atproto/lex-schema'\nexport * from '@atproto/lex-client'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,4DAAiC;AACjC,4DAAiC;AACjC,8DAAmC;AACnC,8DAAmC","sourcesContent":["export * from '@atproto/lex-data'\nexport * from '@atproto/lex-json'\nexport * from '@atproto/lex-schema'\nexport * from '@atproto/lex-client'\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/lex",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Lexicon tooling for AT",
|
|
6
6
|
"keywords": [
|
|
@@ -36,10 +36,12 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"tslib": "^2.8.1",
|
|
38
38
|
"yargs": "^17.0.0",
|
|
39
|
-
"@atproto/lex-builder": "0.0.
|
|
40
|
-
"@atproto/lex-client": "0.0.
|
|
41
|
-
"@atproto/lex-
|
|
42
|
-
"@atproto/lex-
|
|
39
|
+
"@atproto/lex-builder": "0.0.10",
|
|
40
|
+
"@atproto/lex-client": "0.0.8",
|
|
41
|
+
"@atproto/lex-data": "0.0.7",
|
|
42
|
+
"@atproto/lex-json": "0.0.7",
|
|
43
|
+
"@atproto/lex-installer": "0.0.10",
|
|
44
|
+
"@atproto/lex-schema": "0.0.8"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
47
|
"@types/yargs": "^17.0.33",
|