@atproto/syntax 0.4.2 → 0.5.0

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 (77) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE.txt +1 -1
  3. package/dist/at-identifier.d.ts +1 -0
  4. package/dist/at-identifier.d.ts.map +1 -1
  5. package/dist/at-identifier.js +9 -0
  6. package/dist/at-identifier.js.map +1 -1
  7. package/dist/aturi.d.ts +8 -0
  8. package/dist/aturi.d.ts.map +1 -1
  9. package/dist/aturi.js +31 -40
  10. package/dist/aturi.js.map +1 -1
  11. package/dist/aturi_validation.d.ts +3 -2
  12. package/dist/aturi_validation.d.ts.map +1 -1
  13. package/dist/aturi_validation.js +13 -3
  14. package/dist/aturi_validation.js.map +1 -1
  15. package/dist/datetime.d.ts +128 -11
  16. package/dist/datetime.d.ts.map +1 -1
  17. package/dist/datetime.js +205 -79
  18. package/dist/datetime.js.map +1 -1
  19. package/dist/did.d.ts +3 -2
  20. package/dist/did.d.ts.map +1 -1
  21. package/dist/did.js +18 -13
  22. package/dist/did.js.map +1 -1
  23. package/dist/handle.d.ts +4 -3
  24. package/dist/handle.d.ts.map +1 -1
  25. package/dist/handle.js +9 -9
  26. package/dist/handle.js.map +1 -1
  27. package/dist/index.d.ts +7 -5
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +11 -22
  30. package/dist/index.js.map +1 -1
  31. package/dist/language.d.ts +18 -0
  32. package/dist/language.d.ts.map +1 -0
  33. package/dist/language.js +30 -0
  34. package/dist/language.js.map +1 -0
  35. package/dist/nsid.d.ts +4 -4
  36. package/dist/nsid.d.ts.map +1 -1
  37. package/dist/nsid.js +9 -11
  38. package/dist/nsid.js.map +1 -1
  39. package/dist/recordkey.d.ts +2 -2
  40. package/dist/recordkey.d.ts.map +1 -1
  41. package/dist/recordkey.js +10 -10
  42. package/dist/recordkey.js.map +1 -1
  43. package/dist/tid.d.ts +2 -2
  44. package/dist/tid.d.ts.map +1 -1
  45. package/dist/tid.js +5 -5
  46. package/dist/tid.js.map +1 -1
  47. package/dist/uri.d.ts +3 -0
  48. package/dist/uri.d.ts.map +1 -0
  49. package/dist/uri.js +7 -0
  50. package/dist/uri.js.map +1 -0
  51. package/package.json +7 -4
  52. package/src/at-identifier.ts +12 -1
  53. package/src/aturi.ts +30 -1
  54. package/src/aturi_validation.ts +20 -4
  55. package/src/datetime.ts +271 -92
  56. package/src/did.ts +25 -15
  57. package/src/handle.ts +17 -13
  58. package/src/index.ts +7 -5
  59. package/src/language.ts +39 -0
  60. package/src/nsid.ts +13 -7
  61. package/src/recordkey.ts +14 -12
  62. package/src/tid.ts +7 -5
  63. package/src/uri.ts +5 -0
  64. package/tests/aturi.test.ts +50 -2
  65. package/tests/datetime.test.ts +148 -61
  66. package/tests/did.test.ts +1 -0
  67. package/tests/handle.test.ts +1 -0
  68. package/tests/language.test.ts +88 -0
  69. package/tests/nsid.test.ts +1 -0
  70. package/tests/recordkey.test.ts +1 -0
  71. package/tests/tid.test.ts +1 -0
  72. package/tsconfig.build.json +5 -2
  73. package/tsconfig.build.tsbuildinfo +1 -1
  74. package/tsconfig.tests.json +6 -4
  75. package/vitest.config.ts +5 -0
  76. package/jest.config.js +0 -7
  77. package/tsconfig.tests.tsbuildinfo +0 -1
package/src/recordkey.ts CHANGED
@@ -17,32 +17,34 @@ const RECORD_KEY_REGEX = /^[a-zA-Z0-9_~.:-]{1,512}$/
17
17
  // RFC-3986, section 3.3). The above constraints satisfy this condition, by
18
18
  // matching the "unreserved" characters allowed in generic URI paths.
19
19
 
20
- export function ensureValidRecordKey(
21
- rkey: string,
22
- ): asserts rkey is RecordKeyString {
20
+ export function ensureValidRecordKey<I extends string>(
21
+ input: I,
22
+ ): asserts input is I & RecordKeyString {
23
23
  if (
24
- rkey.length > RECORD_KEY_MAX_LENGTH ||
25
- rkey.length < RECORD_KEY_MIN_LENGTH
24
+ input.length > RECORD_KEY_MAX_LENGTH ||
25
+ input.length < RECORD_KEY_MIN_LENGTH
26
26
  ) {
27
27
  throw new InvalidRecordKeyError(
28
28
  `record key must be ${RECORD_KEY_MIN_LENGTH} to ${RECORD_KEY_MAX_LENGTH} characters`,
29
29
  )
30
30
  }
31
- if (RECORD_KEY_INVALID_VALUES.has(rkey)) {
31
+ if (RECORD_KEY_INVALID_VALUES.has(input)) {
32
32
  throw new InvalidRecordKeyError('record key can not be "." or ".."')
33
33
  }
34
34
  // simple regex to enforce most constraints via just regex and length.
35
- if (!RECORD_KEY_REGEX.test(rkey)) {
35
+ if (!RECORD_KEY_REGEX.test(input)) {
36
36
  throw new InvalidRecordKeyError('record key syntax not valid (regex)')
37
37
  }
38
38
  }
39
39
 
40
- export function isValidRecordKey(rkey: string): rkey is RecordKeyString {
40
+ export function isValidRecordKey<I extends string>(
41
+ input: I,
42
+ ): input is I & RecordKeyString {
41
43
  return (
42
- rkey.length >= RECORD_KEY_MIN_LENGTH &&
43
- rkey.length <= RECORD_KEY_MAX_LENGTH &&
44
- RECORD_KEY_REGEX.test(rkey) &&
45
- !RECORD_KEY_INVALID_VALUES.has(rkey)
44
+ input.length >= RECORD_KEY_MIN_LENGTH &&
45
+ input.length <= RECORD_KEY_MAX_LENGTH &&
46
+ RECORD_KEY_REGEX.test(input) &&
47
+ !RECORD_KEY_INVALID_VALUES.has(input)
46
48
  )
47
49
  }
48
50
 
package/src/tid.ts CHANGED
@@ -3,18 +3,20 @@ export type TidString = string
3
3
  const TID_LENGTH = 13
4
4
  const TID_REGEX = /^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/
5
5
 
6
- export function ensureValidTid(tid: string): asserts tid is TidString {
7
- if (tid.length !== TID_LENGTH) {
6
+ export function ensureValidTid<I extends string>(
7
+ input: I,
8
+ ): asserts input is I & TidString {
9
+ if (input.length !== TID_LENGTH) {
8
10
  throw new InvalidTidError(`TID must be ${TID_LENGTH} characters`)
9
11
  }
10
12
  // simple regex to enforce most constraints via just regex and length.
11
- if (!TID_REGEX.test(tid)) {
13
+ if (!TID_REGEX.test(input)) {
12
14
  throw new InvalidTidError('TID syntax not valid (regex)')
13
15
  }
14
16
  }
15
17
 
16
- export function isValidTid(tid: string): tid is TidString {
17
- return tid.length === TID_LENGTH && TID_REGEX.test(tid)
18
+ export function isValidTid<I extends string>(input: I): input is I & TidString {
19
+ return input.length === TID_LENGTH && TID_REGEX.test(input)
18
20
  }
19
21
 
20
22
  export class InvalidTidError extends Error {}
package/src/uri.ts ADDED
@@ -0,0 +1,5 @@
1
+ export type UriString = `${string}:${string}`
2
+
3
+ export function isValidUri<I extends string>(input: I): input is I & UriString {
4
+ return /^\w+:(?:\/\/)?[^\s/][^\s]*$/.test(input)
5
+ }
@@ -1,8 +1,9 @@
1
1
  import * as fs from 'node:fs'
2
2
  import * as readline from 'node:readline'
3
- import { AtUri, ensureValidAtUri, ensureValidAtUriRegex } from '../src/index'
3
+ import { describe, expect, it } from 'vitest'
4
+ import { AtUri, ensureValidAtUri, ensureValidAtUriRegex } from '../src'
4
5
 
5
- describe('At Uris', () => {
6
+ describe(AtUri, () => {
6
7
  it('parses valid at uris', () => {
7
8
  // input host path query hash
8
9
  type AtUriTest = [string, string, string, string, string]
@@ -521,5 +522,52 @@ describe('AtUri validation', () => {
521
522
  })
522
523
  })
523
524
 
525
+ it('properly checks that the did property is a valid did', () => {
526
+ const urip = new AtUri('at://did:example:123')
527
+ expect(urip.did).toBe('did:example:123')
528
+ urip.host = 'did:example:456'
529
+ expect(urip.did).toBe('did:example:456')
530
+ urip.host = 'foo.com'
531
+ expect(() => urip.did).toThrow()
532
+ })
533
+
534
+ it('properly checks that the collection is a valid nsid', () => {
535
+ const urip = new AtUri('at://foo.com')
536
+ expect(urip.collection).toBe('')
537
+ expect(() => urip.collectionSafe).toThrow()
538
+
539
+ urip.collection = 'com.example.foo'
540
+ expect(urip.collection).toBe('com.example.foo')
541
+ expect(urip.collectionSafe).toBe('com.example.foo')
542
+
543
+ urip.collection = 'com.other.foo'
544
+ expect(urip.collection).toBe('com.other.foo')
545
+ expect(urip.collectionSafe).toBe('com.other.foo')
546
+
547
+ expect(() => (urip.collection = 'not a valid nsid')).toThrow()
548
+ expect(urip.collection).toBe('com.other.foo') // unchanged after failed set
549
+
550
+ urip.unsafelySetCollection('not-a-valid-nsid')
551
+ expect(urip.collection).toBe('not-a-valid-nsid')
552
+ expect(() => urip.collectionSafe).toThrow()
553
+ })
554
+
555
+ it('properly checks that the rkey is a valid record key', () => {
556
+ const urip = new AtUri('at://foo.com')
557
+ expect(urip.rkey).toBe('')
558
+ expect(() => urip.rkeySafe).toThrow()
559
+
560
+ urip.rkey = 'valid_rkey-123'
561
+ expect(urip.rkey).toBe('valid_rkey-123')
562
+ expect(urip.rkeySafe).toBe('valid_rkey-123')
563
+
564
+ expect(() => (urip.rkey = 'not a valid rkey')).toThrow()
565
+ expect(urip.rkey).toBe('valid_rkey-123') // unchanged after failed set
566
+
567
+ urip.unsafelySetRkey('not a valid rkey')
568
+ expect(urip.rkey).toBe('not a valid rkey')
569
+ expect(() => urip.rkeySafe).toThrow()
570
+ })
571
+
524
572
  // NOTE: this package is currently more permissive than spec about AT URIs, so invalid cases are not errors
525
573
  })
@@ -1,5 +1,5 @@
1
1
  import * as fs from 'node:fs'
2
- import * as readline from 'node:readline'
2
+ import { describe, expect, it, test } from 'vitest'
3
3
  import {
4
4
  InvalidDatetimeError,
5
5
  ensureValidDatetime,
@@ -8,67 +8,111 @@ import {
8
8
  normalizeDatetimeAlways,
9
9
  } from '../src'
10
10
 
11
- describe('datetime validation', () => {
12
- const expectValid = (h: string) => {
13
- ensureValidDatetime(h)
14
- normalizeDatetime(h)
15
- normalizeDatetimeAlways(h)
16
- }
17
- const expectInvalid = (h: string) => {
18
- expect(() => ensureValidDatetime(h)).toThrow(InvalidDatetimeError)
19
- }
20
-
21
- it('conforms to interop valid datetimes', () => {
22
- const lineReader = readline.createInterface({
23
- input: fs.createReadStream(
24
- `${__dirname}/interop-files/datetime_syntax_valid.txt`,
25
- ),
26
- terminal: false,
27
- })
28
- lineReader.on('line', (line) => {
29
- if (line.startsWith('#') || line.length === 0) {
30
- return
31
- }
32
- if (!isValidDatetime(line)) {
33
- console.log(line)
34
- }
35
- expectValid(line)
36
- })
37
- })
38
-
39
- it('conforms to interop invalid datetimes', () => {
40
- const lineReader = readline.createInterface({
41
- input: fs.createReadStream(
42
- `${__dirname}/interop-files/datetime_syntax_invalid.txt`,
43
- ),
44
- terminal: false,
45
- })
46
- lineReader.on('line', (line) => {
47
- if (line.startsWith('#') || line.length === 0) {
48
- return
49
- }
50
- expectInvalid(line)
51
- })
52
- })
53
-
54
- it('conforms to interop invalid parse (semantics) datetimes', () => {
55
- const lineReader = readline.createInterface({
56
- input: fs.createReadStream(
57
- `${__dirname}/interop-files/datetime_parse_invalid.txt`,
58
- ),
59
- terminal: false,
60
- })
61
- lineReader.on('line', (line) => {
62
- if (line.startsWith('#') || line.length === 0) {
63
- return
64
- }
65
- expectInvalid(line)
66
- })
11
+ const interopValid = readLines(
12
+ `${__dirname}/interop-files/datetime_syntax_valid.txt`,
13
+ )
14
+ const interopInvalidSyntax = readLines(
15
+ `${__dirname}/interop-files/datetime_syntax_invalid.txt`,
16
+ )
17
+ const interopInvalidParse = readLines(
18
+ `${__dirname}/interop-files/datetime_parse_invalid.txt`,
19
+ )
20
+
21
+ describe(ensureValidDatetime, () => {
22
+ describe('valid interop', () => {
23
+ for (const dt of interopValid) {
24
+ test(dt, () => {
25
+ expect(() => ensureValidDatetime(dt)).not.toThrow()
26
+ })
27
+ }
28
+ })
29
+
30
+ describe('fails on interop (invalid syntax)', () => {
31
+ for (const dt of interopInvalidSyntax) {
32
+ test(dt, () => {
33
+ expect(() => ensureValidDatetime(dt)).toThrow(InvalidDatetimeError)
34
+ })
35
+ }
36
+ })
37
+
38
+ describe('fails on interop (invalid parse)', () => {
39
+ for (const dt of interopInvalidParse) {
40
+ test(dt, () => {
41
+ expect(() => ensureValidDatetime(dt)).toThrow(InvalidDatetimeError)
42
+ })
43
+ }
44
+ })
45
+
46
+ it('rejects datetime that normalizes past year 9999 due to negative offset', () => {
47
+ // 9999-12-31T23:59:00-00:01 is syntactically valid, but normalizing to
48
+ // UTC advances it to 10000-01-01T00:00:00Z, which is out of range
49
+ expect(() => ensureValidDatetime('9999-12-31T23:59:00-00:01')).toThrow(
50
+ InvalidDatetimeError,
51
+ )
52
+ })
53
+ })
54
+
55
+ describe(isValidDatetime, () => {
56
+ describe('valid interop', () => {
57
+ for (const dt of interopValid) {
58
+ test(dt, () => {
59
+ expect(isValidDatetime(dt)).toBe(true)
60
+ })
61
+ }
62
+ })
63
+
64
+ describe('fails on interop (invalid syntax)', () => {
65
+ for (const dt of interopInvalidSyntax) {
66
+ test(dt, () => {
67
+ expect(isValidDatetime(dt)).toBe(false)
68
+ })
69
+ }
70
+ })
71
+
72
+ describe('fails on interop (invalid parse)', () => {
73
+ for (const dt of interopInvalidParse) {
74
+ test(dt, () => {
75
+ expect(isValidDatetime(dt)).toBe(false)
76
+ })
77
+ }
78
+ })
79
+
80
+ it('rejects datetime that normalizes past year 9999 due to negative offset', () => {
81
+ // 9999-12-31T23:59:00-00:01 is syntactically valid, but normalizing to
82
+ // UTC advances it to 10000-01-01T00:00:00Z, which is out of range
83
+ expect(isValidDatetime('9999-12-31T23:59:00-00:01')).toBe(false)
67
84
  })
68
85
  })
69
86
 
70
- describe('normalization', () => {
71
- it('normalizes datetimes', () => {
87
+ describe(normalizeDatetime, () => {
88
+ describe('valid interop', () => {
89
+ for (const dt of interopValid) {
90
+ test(dt, () => {
91
+ expect(() => normalizeDatetime(dt)).not.toThrow()
92
+ })
93
+ }
94
+ })
95
+
96
+ // @NOTE Normalize will actually succeed on some of the invalid syntax cases,
97
+ // because it is more lenient than the regex validation.
98
+
99
+ // describe('fails on interop (invalid syntax)', () => {
100
+ // for (const dt of interopInvalidSyntax) {
101
+ // test(dt, () => {
102
+ // expect(() => normalizeDatetime(dt)).toThrow(InvalidDatetimeError)
103
+ // })
104
+ // }
105
+ // })
106
+
107
+ describe('fails on interop (invalid parse)', () => {
108
+ for (const dt of interopInvalidParse) {
109
+ test(dt, () => {
110
+ expect(() => normalizeDatetime(dt)).toThrow(InvalidDatetimeError)
111
+ })
112
+ }
113
+ })
114
+
115
+ it('normalizes valid input', () => {
72
116
  expect(normalizeDatetime('1234-04-12T23:20:50Z')).toEqual(
73
117
  '1234-04-12T23:20:50.000Z',
74
118
  )
@@ -89,7 +133,7 @@ describe('normalization', () => {
89
133
  )
90
134
  })
91
135
 
92
- it('throws on invalid normalized datetimes', () => {
136
+ it('throws on invalid input', () => {
93
137
  expect(() => normalizeDatetime('')).toThrow(InvalidDatetimeError)
94
138
  expect(() => normalizeDatetime('blah')).toThrow(InvalidDatetimeError)
95
139
  expect(() => normalizeDatetime('1999-19-39T23:20:50.123Z')).toThrow(
@@ -104,15 +148,58 @@ describe('normalization', () => {
104
148
  expect(() => normalizeDatetime('0001-01-01T00:00:00+01:00')).toThrow(
105
149
  InvalidDatetimeError,
106
150
  )
151
+ // 9999-12-31T23:59:00-00:01 is syntactically valid, but normalizing to
152
+ // UTC advances it to 10000-01-01T00:00:00Z, which is out of range
153
+ expect(() => normalizeDatetime('9999-12-31T23:59:00-00:01')).toThrow(
154
+ InvalidDatetimeError,
155
+ )
107
156
  })
157
+ })
108
158
 
109
- it('normalizes datetimes always', () => {
159
+ describe(normalizeDatetimeAlways, () => {
160
+ it('normalizes valid input', () => {
110
161
  expect(normalizeDatetimeAlways('1985-04-12T23:20:50Z')).toEqual(
111
162
  '1985-04-12T23:20:50.000Z',
112
163
  )
164
+ })
165
+
166
+ it('normalizes invalid input', () => {
113
167
  expect(normalizeDatetimeAlways('blah')).toEqual('1970-01-01T00:00:00.000Z')
114
168
  expect(normalizeDatetimeAlways('0000-01-01T00:00:00+01:00')).toEqual(
115
169
  '1970-01-01T00:00:00.000Z',
116
170
  )
117
171
  })
172
+
173
+ describe('valid interop', () => {
174
+ for (const dt of interopValid) {
175
+ test(dt, () => {
176
+ // @NOTE we can't test the returned value as some will normalize while others won't.
177
+ expect(() => normalizeDatetimeAlways(dt)).not.toThrow()
178
+ })
179
+ }
180
+ })
181
+
182
+ describe('succeeds on interop (invalid syntax)', () => {
183
+ for (const dt of interopInvalidSyntax) {
184
+ test(dt, () => {
185
+ // @NOTE we can't test the returned value as some will normalize while others won't.
186
+ expect(() => normalizeDatetimeAlways(dt)).not.toThrow()
187
+ })
188
+ }
189
+ })
190
+
191
+ describe('succeeds on interop invalid parse', () => {
192
+ for (const dt of interopInvalidParse) {
193
+ test(dt, () => {
194
+ expect(normalizeDatetimeAlways(dt)).toEqual('1970-01-01T00:00:00.000Z')
195
+ })
196
+ }
197
+ })
118
198
  })
199
+
200
+ function readLines(filePath: string): string[] {
201
+ return fs
202
+ .readFileSync(filePath, 'utf-8')
203
+ .split(/\r?\n/)
204
+ .filter((line) => !line.startsWith('#') && line.length > 0)
205
+ }
package/tests/did.test.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as fs from 'node:fs'
2
2
  import * as readline from 'node:readline'
3
+ import { describe, expect, it } from 'vitest'
3
4
  import { InvalidDidError, ensureValidDid, ensureValidDidRegex } from '../src'
4
5
 
5
6
  describe('DID permissive validation', () => {
@@ -1,5 +1,6 @@
1
1
  import * as fs from 'node:fs'
2
2
  import * as readline from 'node:readline'
3
+ import { describe, expect, it } from 'vitest'
3
4
  import {
4
5
  InvalidHandleError,
5
6
  ensureValidHandle,
@@ -0,0 +1,88 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { isValidLanguage, parseLanguageString } from '../src'
3
+
4
+ describe(isValidLanguage, () => {
5
+ it('validates BCP 47', () => {
6
+ // valid
7
+ expect(isValidLanguage('de')).toEqual(true)
8
+ expect(isValidLanguage('de-CH')).toEqual(true)
9
+ expect(isValidLanguage('de-DE-1901')).toEqual(true)
10
+ expect(isValidLanguage('es-419')).toEqual(true)
11
+ expect(isValidLanguage('sl-IT-nedis')).toEqual(true)
12
+ expect(isValidLanguage('mn-Cyrl-MN')).toEqual(true)
13
+ expect(isValidLanguage('x-fr-CH')).toEqual(true)
14
+ expect(
15
+ isValidLanguage('en-GB-boont-r-extended-sequence-x-private'),
16
+ ).toEqual(true)
17
+ expect(isValidLanguage('sr-Cyrl')).toEqual(true)
18
+ expect(isValidLanguage('hy-Latn-IT-arevela')).toEqual(true)
19
+ expect(isValidLanguage('i-klingon')).toEqual(true)
20
+ // invalid
21
+ expect(isValidLanguage('')).toEqual(false)
22
+ expect(isValidLanguage('x')).toEqual(false)
23
+ expect(isValidLanguage('de-CH-')).toEqual(false)
24
+ expect(isValidLanguage('i-bad-grandfathered')).toEqual(false)
25
+ })
26
+ })
27
+
28
+ describe(parseLanguageString, () => {
29
+ it('parses BCP 47', () => {
30
+ // valid
31
+ expect(parseLanguageString('de')).toEqual({
32
+ language: 'de',
33
+ })
34
+ expect(parseLanguageString('de-CH')).toEqual({
35
+ language: 'de',
36
+ region: 'CH',
37
+ })
38
+ expect(parseLanguageString('de-DE-1901')).toEqual({
39
+ language: 'de',
40
+ region: 'DE',
41
+ variant: '1901',
42
+ })
43
+ expect(parseLanguageString('es-419')).toEqual({
44
+ language: 'es',
45
+ region: '419',
46
+ })
47
+ expect(parseLanguageString('sl-IT-nedis')).toEqual({
48
+ language: 'sl',
49
+ region: 'IT',
50
+ variant: 'nedis',
51
+ })
52
+ expect(parseLanguageString('mn-Cyrl-MN')).toEqual({
53
+ language: 'mn',
54
+ script: 'Cyrl',
55
+ region: 'MN',
56
+ })
57
+ expect(parseLanguageString('x-fr-CH')).toEqual({
58
+ privateUse: 'x-fr-CH',
59
+ })
60
+ expect(
61
+ parseLanguageString('en-GB-boont-r-extended-sequence-x-private'),
62
+ ).toEqual({
63
+ language: 'en',
64
+ region: 'GB',
65
+ variant: 'boont',
66
+ extension: 'r-extended-sequence',
67
+ privateUse: 'x-private',
68
+ })
69
+ expect(parseLanguageString('sr-Cyrl')).toEqual({
70
+ language: 'sr',
71
+ script: 'Cyrl',
72
+ })
73
+ expect(parseLanguageString('hy-Latn-IT-arevela')).toEqual({
74
+ language: 'hy',
75
+ script: 'Latn',
76
+ region: 'IT',
77
+ variant: 'arevela',
78
+ })
79
+ expect(parseLanguageString('i-klingon')).toEqual({
80
+ grandfathered: 'i-klingon',
81
+ })
82
+ // invalid
83
+ expect(parseLanguageString('')).toEqual(null)
84
+ expect(parseLanguageString('x')).toEqual(null)
85
+ expect(parseLanguageString('de-CH-')).toEqual(null)
86
+ expect(parseLanguageString('i-bad-grandfathered')).toEqual(null)
87
+ })
88
+ })
@@ -1,4 +1,5 @@
1
1
  import * as fs from 'node:fs'
2
+ import { describe, expect, it } from 'vitest'
2
3
  import {
3
4
  InvalidNsidError,
4
5
  NSID,
@@ -1,5 +1,6 @@
1
1
  import * as fs from 'node:fs'
2
2
  import * as readline from 'node:readline'
3
+ import { describe, expect, it } from 'vitest'
3
4
  import { InvalidRecordKeyError, ensureValidRecordKey } from '../src'
4
5
 
5
6
  describe('recordkey validation', () => {
package/tests/tid.test.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as fs from 'node:fs'
2
2
  import * as readline from 'node:readline'
3
+ import { describe, expect, it } from 'vitest'
3
4
  import { InvalidTidError, ensureValidTid } from '../src'
4
5
 
5
6
  describe('tid validation', () => {
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "extends": "../../tsconfig/isomorphic.json",
3
+ "include": ["./src"],
4
+ "exclude": ["**/*.test.ts"],
3
5
  "compilerOptions": {
4
6
  "noImplicitAny": true,
7
+ "importHelpers": true,
8
+ "target": "ES2023",
5
9
  "rootDir": "./src",
6
10
  "outDir": "./dist"
7
- },
8
- "include": ["./src"]
11
+ }
9
12
  }
@@ -1 +1 @@
1
- {"root":["./src/at-identifier.ts","./src/aturi.ts","./src/aturi_validation.ts","./src/datetime.ts","./src/did.ts","./src/handle.ts","./src/index.ts","./src/nsid.ts","./src/recordkey.ts","./src/tid.ts"],"version":"5.8.2"}
1
+ {"root":["./src/at-identifier.ts","./src/aturi.ts","./src/aturi_validation.ts","./src/datetime.ts","./src/did.ts","./src/handle.ts","./src/index.ts","./src/language.ts","./src/nsid.ts","./src/recordkey.ts","./src/tid.ts","./src/uri.ts"],"version":"5.8.2"}
@@ -1,7 +1,9 @@
1
1
  {
2
- "extends": "../../tsconfig/tests.json",
2
+ "extends": "../../tsconfig/vitest.json",
3
+ "include": ["./tests", "./src/**/*.test.ts"],
3
4
  "compilerOptions": {
4
- "rootDir": "."
5
- },
6
- "include": ["./tests"]
5
+ "noImplicitAny": true,
6
+ "rootDir": "./",
7
+ "baseUrl": "./"
8
+ }
7
9
  }
@@ -0,0 +1,5 @@
1
+ import { defineProject } from 'vitest/config'
2
+
3
+ export default defineProject({
4
+ test: {},
5
+ })
package/jest.config.js DELETED
@@ -1,7 +0,0 @@
1
- /** @type {import('jest').Config} */
2
- module.exports = {
3
- displayName: 'Syntax',
4
- transform: { '^.+\\.(t|j)s$': '@swc/jest' },
5
- setupFiles: ['<rootDir>/../../jest.setup.ts'],
6
- moduleNameMapper: { '^(\\.\\.?\\/.+)\\.js$': ['$1.ts', '$1.js'] },
7
- }
@@ -1 +0,0 @@
1
- {"root":["./tests/aturi.test.ts","./tests/datetime.test.ts","./tests/did.test.ts","./tests/handle.test.ts","./tests/nsid.test.ts","./tests/recordkey.test.ts","./tests/tid.test.ts"],"version":"5.8.3"}