@atproto/lex 0.0.19 โ†’ 0.0.21

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +87 -16
  3. package/package.json +7 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @atproto/lex
2
2
 
3
+ ## 0.0.21
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`5a2f884`](https://github.com/bluesky-social/atproto/commit/5a2f8847efd91252971fa243d21bd52ada7aa8f4), [`3dc3791`](https://github.com/bluesky-social/atproto/commit/3dc37915436dec7e18c7dc9dcf01b72cad53fdbe), [`3dc3791`](https://github.com/bluesky-social/atproto/commit/3dc37915436dec7e18c7dc9dcf01b72cad53fdbe), [`3dc3791`](https://github.com/bluesky-social/atproto/commit/3dc37915436dec7e18c7dc9dcf01b72cad53fdbe), [`3dc3791`](https://github.com/bluesky-social/atproto/commit/3dc37915436dec7e18c7dc9dcf01b72cad53fdbe), [`112b159`](https://github.com/bluesky-social/atproto/commit/112b159ec293a5c3fff41237474a3788fc47f9ca), [`3dc3791`](https://github.com/bluesky-social/atproto/commit/3dc37915436dec7e18c7dc9dcf01b72cad53fdbe), [`3dc3791`](https://github.com/bluesky-social/atproto/commit/3dc37915436dec7e18c7dc9dcf01b72cad53fdbe)]:
8
+ - @atproto/lex-client@0.0.16
9
+ - @atproto/lex-schema@0.0.15
10
+ - @atproto/lex-builder@0.0.18
11
+ - @atproto/lex-installer@0.0.21
12
+
13
+ ## 0.0.20
14
+
15
+ ### Patch Changes
16
+
17
+ - [#4689](https://github.com/bluesky-social/atproto/pull/4689) [`f7c2610`](https://github.com/bluesky-social/atproto/commit/f7c26103a6d4e24e5bedbb6fd908be140420e0dd) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Update readme
18
+
19
+ - Updated dependencies [[`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`f7c2610`](https://github.com/bluesky-social/atproto/commit/f7c26103a6d4e24e5bedbb6fd908be140420e0dd), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7)]:
20
+ - @atproto/lex-schema@0.0.14
21
+ - @atproto/lex-data@0.0.13
22
+ - @atproto/lex-client@0.0.15
23
+ - @atproto/lex-installer@0.0.20
24
+ - @atproto/lex-builder@0.0.17
25
+ - @atproto/lex-json@0.0.13
26
+
3
27
  ## 0.0.19
4
28
 
5
29
  ### Patch Changes
package/README.md CHANGED
@@ -20,7 +20,7 @@ const newPost = app.bsky.feed.post.$build({
20
20
  app.bsky.actor.profile.$validate({
21
21
  $type: 'app.bsky.actor.profile',
22
22
  displayName: 'Ha'.repeat(32) + '!',
23
- }) // Error: grapheme too big (maximum 64) at $.displayName (got 65)
23
+ }) // Error: grapheme too big (maximum 64, got 65) at $.displayName
24
24
  ```
25
25
 
26
26
  ```typescript
@@ -67,6 +67,7 @@ const posts = await client.list(app.bsky.feed.post, { limit: 10 })
67
67
  - [Labeler Configuration](#labeler-configuration)
68
68
  - [Low-Level XRPC](#low-level-xrpc)
69
69
  - [Utilities](#utilities)
70
+ - [Datetime Strings](#datetime-strings)
70
71
  - [Advanced Usage](#advanced-usage)
71
72
  - [Workflow Integration](#workflow-integration)
72
73
  - [Tree-Shaking](#tree-shaking)
@@ -74,6 +75,7 @@ const posts = await client.list(app.bsky.feed.post, { limit: 10 })
74
75
  - [Actions](#actions)
75
76
  - [Creating a Client from Another Client](#creating-a-client-from-another-client)
76
77
  - [Building Library-Style APIs with Actions](#building-library-style-apis-with-actions)
78
+ - [Standard Schema Compatibility](#standard-schema-compatibility)
77
79
  - [License](#license)
78
80
 
79
81
  <!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -223,13 +225,14 @@ function renderPost(p: app.bsky.feed.post.Main) {
223
225
  It is recommended to use the generated builders to create data that conforms to the schema. This ensures that all required fields are present.
224
226
 
225
227
  ```typescript
228
+ import { l } from '@atproto/lex'
226
229
  import * as app from './lexicons/app.js'
227
230
 
228
231
  // variable type will be inferred as "app.bsky.feed.post.Main"
229
232
  const post = app.bsky.feed.post.$build({
230
233
  // No need to specify $type when using $build
231
234
  text: 'Hello, world!',
232
- createdAt: new Date().toISOString(),
235
+ createdAt: l.toDatetimeString(new Date()),
233
236
  })
234
237
  ```
235
238
 
@@ -263,12 +266,13 @@ console.log(app.bsky.actor.defs.profileViewBasic.$type) // 'app.bsky.actor.defs#
263
266
  Returns `true` if data matches the schema, `false` otherwise. Acts as a TypeScript type guard:
264
267
 
265
268
  ```typescript
269
+ import { l } from '@atproto/lex'
266
270
  import * as app from './lexicons/app.js'
267
271
 
268
272
  const data = {
269
273
  $type: 'app.bsky.feed.post',
270
274
  text: 'Hello!',
271
- createdAt: new Date().toISOString(),
275
+ createdAt: l.toDatetimeString(new Date()),
272
276
  }
273
277
 
274
278
  if (app.bsky.feed.post.$check(data)) {
@@ -282,13 +286,14 @@ if (app.bsky.feed.post.$check(data)) {
282
286
  Validates and returns typed data, throwing an error if validation fails:
283
287
 
284
288
  ```typescript
289
+ import { l } from '@atproto/lex'
285
290
  import * as app from './lexicons/app.js'
286
291
 
287
292
  try {
288
293
  const post = app.bsky.feed.post.$main.$parse({
289
294
  $type: 'app.bsky.feed.post',
290
295
  text: 'Hello!',
291
- createdAt: new Date().toISOString(),
296
+ createdAt: l.toDatetimeString(new Date()),
292
297
  })
293
298
  // post is now typed and validated
294
299
  console.log(post.text)
@@ -306,12 +311,13 @@ try {
306
311
  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).
307
312
 
308
313
  ```typescript
314
+ import { l } from '@atproto/lex'
309
315
  import * as app from './lexicons/app.js'
310
316
 
311
317
  const value = {
312
318
  $type: 'app.bsky.feed.post',
313
319
  text: 'Hello!',
314
- createdAt: new Date().toISOString(),
320
+ createdAt: l.toDatetimeString(new Date()),
315
321
  }
316
322
 
317
323
  // Throws if no valid
@@ -325,12 +331,13 @@ value === result // true
325
331
  Returns a detailed validation result object without throwing:
326
332
 
327
333
  ```typescript
334
+ import { l } from '@atproto/lex'
328
335
  import * as app from './lexicons/app.js'
329
336
 
330
337
  const result = app.bsky.feed.post.$safeParse({
331
338
  $type: 'app.bsky.feed.post',
332
339
  text: 'Hello!',
333
- createdAt: new Date().toISOString(),
340
+ createdAt: l.toDatetimeString(new Date()),
334
341
  })
335
342
 
336
343
  if (result.success) {
@@ -345,6 +352,7 @@ if (result.success) {
345
352
  Builds data without needing to specify the `$type` property, and properly types the result:
346
353
 
347
354
  ```typescript
355
+ import { l } from '@atproto/lex'
348
356
  import * as app from './lexicons/app.js'
349
357
 
350
358
  // The type of the "like" variable will be "app.bsky.feed.like.Main"
@@ -353,7 +361,7 @@ const like = app.bsky.feed.like.$build({
353
361
  uri: 'at://did:plc:abc/app.bsky.feed.post/123',
354
362
  cid: 'bafyrei...',
355
363
  },
356
- createdAt: new Date().toISOString(),
364
+ createdAt: l.toDatetimeString(new Date()),
357
365
  })
358
366
  ```
359
367
 
@@ -605,11 +613,12 @@ const timeline = await client.call(
605
613
  Create a new record un the authenticated user's repo.
606
614
 
607
615
  ```typescript
616
+ import { l } from '@atproto/lex'
608
617
  import * as app from './lexicons/app.js'
609
618
 
610
619
  const result = await client.create(app.bsky.feed.post, {
611
620
  text: 'Hello, world!',
612
- createdAt: new Date().toISOString(),
621
+ createdAt: l.toDatetimeString(new Date()),
613
622
  })
614
623
 
615
624
  console.log(result.uri) // at://did:plc:...
@@ -757,7 +766,7 @@ if (result.success) {
757
766
  // All XrpcFailure types have these properties:
758
767
  result.shouldRetry() // boolean - whether the error is transient
759
768
 
760
- if (result.matchesSchema()) {
769
+ if (result.matchesSchemaErrors()) {
761
770
  // Check if the error matches a declared error in the schema.
762
771
  // TypeScript knows this is a declared error for the method.
763
772
  result.error // "HandleNotFound"
@@ -850,7 +859,7 @@ console.log(response.body)
850
859
 
851
860
  ## Utilities
852
861
 
853
- Various utilities for working with CIDs, string lengths, language tags, and low-level JSON encoding are exported from the package:
862
+ Various utilities for working with CIDs, datetime strings, string lengths, language tags, and low-level JSON encoding are exported from the package:
854
863
 
855
864
  ```typescript
856
865
  import {
@@ -859,6 +868,12 @@ import {
859
868
  ifCid, // Coerce to Cid or null
860
869
  isCid, // Type guard for Cid values
861
870
 
871
+ // Datetime string utilities
872
+ toDatetimeString, // Convert Date to DatetimeString (throws on invalid)
873
+ asDatetimeString, // Cast string to DatetimeString (throws on invalid)
874
+ isDatetimeString, // Type guard for DatetimeString
875
+ ifDatetimeString, // Returns DatetimeString or undefined
876
+
862
877
  // Blob references
863
878
  BlobRef, // { $type: 'blob', ref: Cid, mimeType: string, size: number }
864
879
  isBlobRef, // Type guard for BlobRef objects
@@ -886,6 +901,35 @@ utf8Len('๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ') // 25
886
901
  isLanguageString('en-US') // true
887
902
  ```
888
903
 
904
+ ### Datetime Strings
905
+
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 10 or after year 9999 will produce non-conforming strings). To ensure correctness and type safety, use the `DatetimeString` utilities exported from `@atproto/lex`:
907
+
908
+ - **`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
+ - **`asDatetimeString(input: string)`** - Validates and casts an arbitrary string to `DatetimeString`, throwing an `InvalidDatetimeError` if the string does not conform.
910
+ - **`isDatetimeString(input)`** - Type guard that returns `true` if the input is a valid `DatetimeString`.
911
+ - **`ifDatetimeString(input)`** - Returns the input as a `DatetimeString` if valid, or `undefined` otherwise.
912
+ - **`currentDatetimeString()`** - Returns the current date and time as `DatetimeString`.
913
+
914
+ ```typescript
915
+ import { l } from '@atproto/lex'
916
+
917
+ // Convert a Date object to a DatetimeString (or throws)
918
+ const someDate = new Date('2024-01-15T12:30:00Z')
919
+ const now = l.toDatetimeString(someDate)
920
+
921
+ // Get the current datetime as a DatetimeString
922
+ const now = l.currentDatetimeString()
923
+
924
+ // Validate and cast an existing string
925
+ const dt = l.asDatetimeString('2024-01-15T12:30:00.000Z')
926
+
927
+ // Type guard for conditional checks
928
+ if (l.isDatetimeString(someString)) {
929
+ // someString is now typed as DatetimeString
930
+ }
931
+ ```
932
+
889
933
  ## Advanced Usage
890
934
 
891
935
  ### Workflow Integration
@@ -991,7 +1035,7 @@ Actions receive:
991
1035
  Actions are called using `client.call()`, the same method used for XRPC queries and procedures:
992
1036
 
993
1037
  ```typescript
994
- import { Action, Client } from '@atproto/lex'
1038
+ import { Action, Client, l } from '@atproto/lex'
995
1039
  import * as app from './lexicons/app.js'
996
1040
 
997
1041
  // Define an action
@@ -1005,7 +1049,7 @@ export const likePost: Action<
1005
1049
  app.bsky.feed.like,
1006
1050
  {
1007
1051
  subject: { uri, cid },
1008
- createdAt: new Date().toISOString(),
1052
+ createdAt: l.toDatetimeString(new Date()),
1009
1053
  },
1010
1054
  options,
1011
1055
  )
@@ -1206,7 +1250,7 @@ Actions enable you to create high-level, convenience APIs similar to [@atproto/a
1206
1250
  #### Creating Posts
1207
1251
 
1208
1252
  ```typescript
1209
- import { Action } from '@atproto/lex'
1253
+ import { Action, l } from '@atproto/lex'
1210
1254
  import * as app from './lexicons/app.js'
1211
1255
 
1212
1256
  type PostInput = Partial<app.bsky.feed.post.Main> &
@@ -1221,7 +1265,7 @@ export const post: Action<PostInput, { uri: string; cid: string }> = async (
1221
1265
  app.bsky.feed.post,
1222
1266
  {
1223
1267
  ...record,
1224
- createdAt: record.createdAt || new Date().toISOString(),
1268
+ createdAt: record.createdAt || l.currentDatetimeString(),
1225
1269
  },
1226
1270
  options,
1227
1271
  )
@@ -1237,7 +1281,7 @@ await client.call(post, {
1237
1281
  #### Following Users
1238
1282
 
1239
1283
  ```typescript
1240
- import { Action } from '@atproto/lex'
1284
+ import { Action, l } from '@atproto/lex'
1241
1285
  import { AtUri } from '@atproto/syntax'
1242
1286
  import * as app from './lexicons/app.js'
1243
1287
 
@@ -1249,7 +1293,7 @@ export const follow: Action<
1249
1293
  app.bsky.graph.follow,
1250
1294
  {
1251
1295
  subject: did,
1252
- createdAt: new Date().toISOString(),
1296
+ createdAt: l.currentDatetimeString(),
1253
1297
  },
1254
1298
  options,
1255
1299
  )
@@ -1380,6 +1424,33 @@ await client.call(actions.post, { text: 'Hello!' })
1380
1424
  5. **Retries**: Implement retry logic for operations with optimistic concurrency control
1381
1425
  6. **Tree-shaking**: Export actions individually to allow tree-shaking (instead of bundling them in a single class)
1382
1426
 
1427
+ ### Standard Schema Compatibility
1428
+
1429
+ All generated schemas implement the [Standard Schema](https://standardschema.dev/) interface (`StandardSchemaV1`), which means they can be used with any library or framework that supports Standard Schema, such as form validation libraries, API frameworks, and more.
1430
+
1431
+ Every `Schema` instance exposes a `~standard` property conforming to the spec:
1432
+
1433
+ ```typescript
1434
+ import * as app from './lexicons/app.js'
1435
+
1436
+ // Use with any Standard Schema-compatible library
1437
+ const schema = app.bsky.feed.post
1438
+
1439
+ schema['~standard'].version // 1
1440
+ schema['~standard'].vendor // '@atproto/lex-schema'
1441
+
1442
+ // Validate using the Standard Schema interface
1443
+ const result = schema['~standard'].validate(someData)
1444
+
1445
+ if ('value' in result) {
1446
+ console.log(result.value) // Parsed and validated data
1447
+ } else {
1448
+ console.error(result.issues)
1449
+ }
1450
+ ```
1451
+
1452
+ When validated through the Standard Schema interface, schemas operate in "parse" mode, meaning transformations like defaults and coercions are applied to the output.
1453
+
1383
1454
  ## License
1384
1455
 
1385
1456
  MIT or Apache2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/lex",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "license": "MIT",
5
5
  "description": "Lexicon tooling for AT",
6
6
  "keywords": [
@@ -38,12 +38,12 @@
38
38
  "dependencies": {
39
39
  "tslib": "^2.8.1",
40
40
  "yargs": "^17.0.0",
41
- "@atproto/lex-builder": "^0.0.16",
42
- "@atproto/lex-client": "^0.0.14",
43
- "@atproto/lex-data": "^0.0.12",
44
- "@atproto/lex-json": "^0.0.12",
45
- "@atproto/lex-installer": "^0.0.19",
46
- "@atproto/lex-schema": "^0.0.13"
41
+ "@atproto/lex-builder": "^0.0.18",
42
+ "@atproto/lex-client": "^0.0.16",
43
+ "@atproto/lex-data": "^0.0.13",
44
+ "@atproto/lex-json": "^0.0.13",
45
+ "@atproto/lex-installer": "^0.0.21",
46
+ "@atproto/lex-schema": "^0.0.15"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/yargs": "^17.0.33",