@atproto/syntax 0.6.3 → 0.6.4
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 +17 -0
- package/dist/at-identifier.d.ts +2 -2
- package/dist/at-identifier.d.ts.map +1 -1
- package/dist/at-identifier.js.map +1 -1
- package/dist/aturi.d.ts +5 -5
- package/dist/aturi.d.ts.map +1 -1
- package/dist/aturi.js +1 -1
- package/dist/aturi.js.map +1 -1
- package/dist/aturi_validation.d.ts +3 -3
- package/dist/aturi_validation.d.ts.map +1 -1
- package/dist/aturi_validation.js.map +1 -1
- package/dist/language.js +1 -1
- package/dist/language.js.map +1 -1
- package/dist/nsid.d.ts +1 -1
- package/dist/nsid.d.ts.map +1 -1
- package/dist/nsid.js.map +1 -1
- package/package.json +15 -14
- package/benchmark.cjs +0 -208
- package/src/at-identifier.ts +0 -104
- package/src/aturi.ts +0 -197
- package/src/aturi_validation.ts +0 -321
- package/src/datetime.ts +0 -369
- package/src/did.ts +0 -71
- package/src/handle.ts +0 -128
- package/src/index.ts +0 -10
- package/src/language.ts +0 -39
- package/src/lib/result.ts +0 -11
- package/src/nsid.ts +0 -182
- package/src/recordkey.ts +0 -51
- package/src/tid.ts +0 -22
- package/src/uri.ts +0 -5
- package/tests/aturi-string.test.ts +0 -223
- package/tests/aturi.test.ts +0 -428
- package/tests/datetime.test.ts +0 -280
- package/tests/did.test.ts +0 -104
- package/tests/handle.test.ts +0 -239
- package/tests/language.test.ts +0 -88
- package/tests/nsid.test.ts +0 -174
- package/tests/recordkey.test.ts +0 -43
- package/tests/tid.test.ts +0 -43
- package/tsconfig.build.json +0 -12
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -8
- package/vitest.config.ts +0 -5
package/tests/datetime.test.ts
DELETED
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs'
|
|
2
|
-
import { describe, expect, it, test } from 'vitest'
|
|
3
|
-
import {
|
|
4
|
-
InvalidDatetimeError,
|
|
5
|
-
ensureValidDatetime,
|
|
6
|
-
isDatetimeString,
|
|
7
|
-
isDatetimeStringLenient,
|
|
8
|
-
normalizeDatetime,
|
|
9
|
-
normalizeDatetimeAlways,
|
|
10
|
-
} from '../src/index.js'
|
|
11
|
-
|
|
12
|
-
const interopValid = readLines(
|
|
13
|
-
`${__dirname}/interop-files/datetime_syntax_valid.txt`,
|
|
14
|
-
)
|
|
15
|
-
const interopInvalidSyntax = readLines(
|
|
16
|
-
`${__dirname}/interop-files/datetime_syntax_invalid.txt`,
|
|
17
|
-
)
|
|
18
|
-
const interopInvalidParse = readLines(
|
|
19
|
-
`${__dirname}/interop-files/datetime_parse_invalid.txt`,
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
// These strings come from the test suite in "@atproto/lexicon", kept around
|
|
23
|
-
// to ensure compatibility with legacy implementation.
|
|
24
|
-
const legacyValid = [
|
|
25
|
-
'2022-12-12T00:50:36.809Z',
|
|
26
|
-
'2022-12-12T00:50:36Z',
|
|
27
|
-
'2022-12-12T00:50:36.8Z',
|
|
28
|
-
'2022-12-12T00:50:36.80Z',
|
|
29
|
-
'2022-12-12T00:50:36+00:00',
|
|
30
|
-
'2022-12-12T00:50:36.8+00:00',
|
|
31
|
-
'2022-12-11T19:50:36-05:00',
|
|
32
|
-
'2022-12-11T19:50:36.8-05:00',
|
|
33
|
-
'2022-12-11T19:50:36.80-05:00',
|
|
34
|
-
'2022-12-11T19:50:36.809-05:00',
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
describe(ensureValidDatetime, () => {
|
|
38
|
-
describe('Interop valid', () => {
|
|
39
|
-
test.each(interopValid)('%s', (dt) => {
|
|
40
|
-
expect(() => ensureValidDatetime(dt)).not.toThrow()
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
describe('Interop invalid syntax', () => {
|
|
45
|
-
test.each(interopInvalidSyntax)('%s', (dt) => {
|
|
46
|
-
expect(() => ensureValidDatetime(dt)).toThrow(InvalidDatetimeError)
|
|
47
|
-
})
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
describe('Interop invalid parse', () => {
|
|
51
|
-
test.each(interopInvalidParse)('%s', (dt) => {
|
|
52
|
-
expect(() => ensureValidDatetime(dt)).toThrow(InvalidDatetimeError)
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('rejects datetime that normalizes past year 9999 due to negative offset', () => {
|
|
57
|
-
// 9999-12-31T23:59:00-00:01 is syntactically valid, but normalizing to
|
|
58
|
-
// UTC advances it to 10000-01-01T00:00:00Z, which is out of range
|
|
59
|
-
expect(() => ensureValidDatetime('9999-12-31T23:59:00-00:01')).toThrow(
|
|
60
|
-
InvalidDatetimeError,
|
|
61
|
-
)
|
|
62
|
-
})
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
describe(isDatetimeString, () => {
|
|
66
|
-
describe('Interop valid', () => {
|
|
67
|
-
test.each(interopValid)('%s', (dt) => {
|
|
68
|
-
expect(isDatetimeString(dt)).toBe(true)
|
|
69
|
-
})
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
describe('Interop invalid syntax', () => {
|
|
73
|
-
test.each(interopInvalidSyntax)('%s', (dt) => {
|
|
74
|
-
expect(isDatetimeString(dt)).toBe(false)
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
describe('Interop invalid parse', () => {
|
|
79
|
-
test.each(interopInvalidParse)('%s', (dt) => {
|
|
80
|
-
expect(isDatetimeString(dt)).toBe(false)
|
|
81
|
-
})
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
describe('succeeds on legacy valid', () => {
|
|
85
|
-
test.each(legacyValid)('%s', (dt) => {
|
|
86
|
-
expect(isDatetimeString(dt)).toBe(true)
|
|
87
|
-
})
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('rejects datetime that normalizes past year 9999 due to negative offset', () => {
|
|
91
|
-
// 9999-12-31T23:59:00-00:01 is syntactically valid, but normalizing to
|
|
92
|
-
// UTC advances it to 10000-01-01T00:00:00Z, which is out of range
|
|
93
|
-
expect(isDatetimeString('9999-12-31T23:59:00-00:01')).toBe(false)
|
|
94
|
-
})
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
describe(isDatetimeStringLenient, () => {
|
|
98
|
-
describe('Interop valid', () => {
|
|
99
|
-
test.each(interopValid)('%s', (dt) => {
|
|
100
|
-
expect(isDatetimeStringLenient(dt)).toBe(true)
|
|
101
|
-
})
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
// Because of it leniency, the "isDatetimeStringLenient" implementation does
|
|
105
|
-
// not fail on some of the invalid syntax cases, so these tests are skipped.
|
|
106
|
-
describe.skip('Interop invalid syntax', () => {
|
|
107
|
-
test.each(interopInvalidSyntax)('%s', (dt) => {
|
|
108
|
-
expect(isDatetimeStringLenient(dt)).toBe(false)
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
describe('Interop invalid parse', () => {
|
|
113
|
-
test.each(interopInvalidParse)('%s', (dt) => {
|
|
114
|
-
expect(isDatetimeStringLenient(dt)).toBe(false)
|
|
115
|
-
})
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
describe('Legacy valid', () => {
|
|
119
|
-
test.each(legacyValid)('%s', (dt) => {
|
|
120
|
-
expect(isDatetimeStringLenient(dt)).toBe(true)
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
describe(normalizeDatetime, () => {
|
|
126
|
-
describe('Interop valid', () => {
|
|
127
|
-
test.each(interopValid)('%s', (dt) => {
|
|
128
|
-
expect(() => normalizeDatetime(dt)).not.toThrow()
|
|
129
|
-
})
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
// @NOTE Normalize will actually succeed on some of the invalid syntax cases,
|
|
133
|
-
// because it is more lenient than the regex validation.
|
|
134
|
-
describe.skip('Interop invalid syntax', () => {
|
|
135
|
-
test.each(interopInvalidSyntax)('%s', (dt) => {
|
|
136
|
-
expect(() => normalizeDatetime(dt)).toThrow(InvalidDatetimeError)
|
|
137
|
-
})
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
describe('Interop invalid parse', () => {
|
|
141
|
-
test.each(interopInvalidParse)('%s', (dt) => {
|
|
142
|
-
expect(() => normalizeDatetime(dt)).toThrow(InvalidDatetimeError)
|
|
143
|
-
})
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('normalizes valid input', () => {
|
|
147
|
-
expect(normalizeDatetime('1234-04-12T23:20:50Z')).toEqual(
|
|
148
|
-
'1234-04-12T23:20:50.000Z',
|
|
149
|
-
)
|
|
150
|
-
expect(normalizeDatetime('1985-04-12T23:20:50Z')).toEqual(
|
|
151
|
-
'1985-04-12T23:20:50.000Z',
|
|
152
|
-
)
|
|
153
|
-
expect(normalizeDatetime('1985-04-12T23:20:50.123')).toEqual(
|
|
154
|
-
'1985-04-12T23:20:50.123Z',
|
|
155
|
-
)
|
|
156
|
-
expect(normalizeDatetime('1985-04-12 23:20:50.123')).toEqual(
|
|
157
|
-
'1985-04-12T23:20:50.123Z',
|
|
158
|
-
)
|
|
159
|
-
expect(normalizeDatetime('1985-04-12T10:20:50.1+01:00')).toEqual(
|
|
160
|
-
'1985-04-12T09:20:50.100Z',
|
|
161
|
-
)
|
|
162
|
-
expect(normalizeDatetime('Fri, 02 Jan 1999 12:34:56+1212')).toEqual(
|
|
163
|
-
'1999-01-02T00:22:56.000Z',
|
|
164
|
-
)
|
|
165
|
-
expect(normalizeDatetime('Fri, 02 Jan 1999 12:34:56Z')).toEqual(
|
|
166
|
-
'1999-01-02T12:34:56.000Z',
|
|
167
|
-
)
|
|
168
|
-
expect(normalizeDatetime('Fri, 02 Jan 1999 12:34:56 GMT')).toEqual(
|
|
169
|
-
'1999-01-02T12:34:56.000Z',
|
|
170
|
-
)
|
|
171
|
-
expect(normalizeDatetime('Fri, 02 Jan 1999 12:34:56 PST')).toEqual(
|
|
172
|
-
'1999-01-02T20:34:56.000Z',
|
|
173
|
-
)
|
|
174
|
-
expect(normalizeDatetime('Fri, 02 Jan 1999 12:34:56 EST')).toEqual(
|
|
175
|
-
'1999-01-02T17:34:56.000Z',
|
|
176
|
-
)
|
|
177
|
-
// @NOTE "(Central European Standard Time)" is not used by "Date" to infer
|
|
178
|
-
// the right timezone offset, so these will be parsed as UTC
|
|
179
|
-
expect(
|
|
180
|
-
normalizeDatetime(
|
|
181
|
-
'Fri, 02 Jan 1999 12:34:56 (Central European Standard Time)',
|
|
182
|
-
),
|
|
183
|
-
).toEqual('1999-01-02T12:34:56.000Z')
|
|
184
|
-
expect(normalizeDatetime('0001-01-01T00:00:00+01:00')).toEqual(
|
|
185
|
-
'0000-12-31T23:00:00.000Z',
|
|
186
|
-
)
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
it('accepts years 1-9', () => {
|
|
190
|
-
expect(normalizeDatetime('0009-12-31T23:59:59Z')).toEqual(
|
|
191
|
-
'0009-12-31T23:59:59.000Z',
|
|
192
|
-
)
|
|
193
|
-
expect(normalizeDatetime('0005-06-15T12:00:00Z')).toEqual(
|
|
194
|
-
'0005-06-15T12:00:00.000Z',
|
|
195
|
-
)
|
|
196
|
-
expect(normalizeDatetime('0001-01-01T00:00:00Z')).toEqual(
|
|
197
|
-
'0001-01-01T00:00:00.000Z',
|
|
198
|
-
)
|
|
199
|
-
expect(normalizeDatetime('0002-03-04T05:06:07.890Z')).toEqual(
|
|
200
|
-
'0002-03-04T05:06:07.890Z',
|
|
201
|
-
)
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
it('accepts single-digit years with leading zeros', () => {
|
|
205
|
-
expect(normalizeDatetime('0007-01-01T00:00:00Z')).toEqual(
|
|
206
|
-
'0007-01-01T00:00:00.000Z',
|
|
207
|
-
)
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
it('accepts year 1 with timezone offsets', () => {
|
|
211
|
-
// Year 1 with negative offset stays in valid range
|
|
212
|
-
expect(normalizeDatetime('0001-12-31T23:00:00-01:00')).toEqual(
|
|
213
|
-
'0002-01-01T00:00:00.000Z',
|
|
214
|
-
)
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
it('throws on invalid input', () => {
|
|
218
|
-
expect(() => normalizeDatetime('')).toThrow(InvalidDatetimeError)
|
|
219
|
-
expect(() => normalizeDatetime('blah')).toThrow(InvalidDatetimeError)
|
|
220
|
-
expect(() => normalizeDatetime('1999-19-39T23:20:50.123Z')).toThrow(
|
|
221
|
-
InvalidDatetimeError,
|
|
222
|
-
)
|
|
223
|
-
expect(() => normalizeDatetime('Fri, 02 Jan 1999 12:34:56 AFT')).toThrow(
|
|
224
|
-
InvalidDatetimeError,
|
|
225
|
-
)
|
|
226
|
-
expect(() => normalizeDatetime('-000001-12-31T23:00:00.000Z')).toThrow(
|
|
227
|
-
InvalidDatetimeError,
|
|
228
|
-
)
|
|
229
|
-
expect(() => normalizeDatetime('0000-01-01T00:00:00+01:00')).toThrow(
|
|
230
|
-
InvalidDatetimeError,
|
|
231
|
-
)
|
|
232
|
-
// 9999-12-31T23:59:00-00:01 is syntactically valid, but normalizing to
|
|
233
|
-
// UTC advances it to 10000-01-01T00:00:00Z, which is out of range
|
|
234
|
-
expect(() => normalizeDatetime('9999-12-31T23:59:00-00:01')).toThrow(
|
|
235
|
-
InvalidDatetimeError,
|
|
236
|
-
)
|
|
237
|
-
})
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
describe(normalizeDatetimeAlways, () => {
|
|
241
|
-
it('normalizes valid input', () => {
|
|
242
|
-
expect(normalizeDatetimeAlways('1985-04-12T23:20:50Z')).toEqual(
|
|
243
|
-
'1985-04-12T23:20:50.000Z',
|
|
244
|
-
)
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
it('normalizes invalid input', () => {
|
|
248
|
-
expect(normalizeDatetimeAlways('blah')).toEqual('1970-01-01T00:00:00.000Z')
|
|
249
|
-
expect(normalizeDatetimeAlways('0000-01-01T00:00:00+01:00')).toEqual(
|
|
250
|
-
'1970-01-01T00:00:00.000Z',
|
|
251
|
-
)
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
describe('Interop valid', () => {
|
|
255
|
-
test.each(interopValid)('%s', (dt) => {
|
|
256
|
-
// @NOTE we can't test the returned value as some will normalize while others won't.
|
|
257
|
-
expect(() => normalizeDatetimeAlways(dt)).not.toThrow()
|
|
258
|
-
})
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
describe('Interop invalid syntax', () => {
|
|
262
|
-
test.each(interopInvalidSyntax)('%s', (dt) => {
|
|
263
|
-
// @NOTE we can't test the returned value as some will normalize while others won't.
|
|
264
|
-
expect(() => normalizeDatetimeAlways(dt)).not.toThrow()
|
|
265
|
-
})
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
describe('Interop invalid parse', () => {
|
|
269
|
-
test.each(interopInvalidParse)('%s', (dt) => {
|
|
270
|
-
expect(normalizeDatetimeAlways(dt)).toEqual('1970-01-01T00:00:00.000Z')
|
|
271
|
-
})
|
|
272
|
-
})
|
|
273
|
-
})
|
|
274
|
-
|
|
275
|
-
function readLines(filePath: string): string[] {
|
|
276
|
-
return fs
|
|
277
|
-
.readFileSync(filePath, 'utf-8')
|
|
278
|
-
.split(/\r?\n/)
|
|
279
|
-
.filter((line) => !line.startsWith('#') && line.length > 0)
|
|
280
|
-
}
|
package/tests/did.test.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs'
|
|
2
|
-
import * as readline from 'node:readline'
|
|
3
|
-
import { describe, expect, it } from 'vitest'
|
|
4
|
-
import {
|
|
5
|
-
InvalidDidError,
|
|
6
|
-
ensureValidDid,
|
|
7
|
-
ensureValidDidRegex,
|
|
8
|
-
} from '../src/index.js'
|
|
9
|
-
|
|
10
|
-
describe('DID permissive validation', () => {
|
|
11
|
-
const expectValid = (h: string) => {
|
|
12
|
-
ensureValidDid(h)
|
|
13
|
-
ensureValidDidRegex(h)
|
|
14
|
-
}
|
|
15
|
-
const expectInvalid = (h: string) => {
|
|
16
|
-
expect(() => ensureValidDid(h)).toThrow(InvalidDidError)
|
|
17
|
-
expect(() => ensureValidDidRegex(h)).toThrow(InvalidDidError)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
it('enforces spec details', () => {
|
|
21
|
-
expectValid('did:method:val')
|
|
22
|
-
expectValid('did:method:VAL')
|
|
23
|
-
expectValid('did:method:val123')
|
|
24
|
-
expectValid('did:method:123')
|
|
25
|
-
expectValid('did:method:val-two')
|
|
26
|
-
expectValid('did:method:val_two')
|
|
27
|
-
expectValid('did:method:val.two')
|
|
28
|
-
expectValid('did:method:val:two')
|
|
29
|
-
expectValid('did:method:val%BB')
|
|
30
|
-
|
|
31
|
-
expectInvalid('did')
|
|
32
|
-
expectInvalid('didmethodval')
|
|
33
|
-
expectInvalid('method:did:val')
|
|
34
|
-
expectInvalid('did:method:')
|
|
35
|
-
expectInvalid('didmethod:val')
|
|
36
|
-
expectInvalid('did:methodval')
|
|
37
|
-
expectInvalid(':did:method:val')
|
|
38
|
-
expectInvalid('did.method.val')
|
|
39
|
-
expectInvalid('did:method:val:')
|
|
40
|
-
expectInvalid('did:method:val%')
|
|
41
|
-
expectInvalid('DID:method:val')
|
|
42
|
-
expectInvalid('did:METHOD:val')
|
|
43
|
-
expectInvalid('did:m123:val')
|
|
44
|
-
|
|
45
|
-
expectValid('did:method:' + 'v'.repeat(240))
|
|
46
|
-
expectInvalid('did:method:' + 'v'.repeat(8500))
|
|
47
|
-
|
|
48
|
-
expectValid('did:m:v')
|
|
49
|
-
expectValid('did:method::::val')
|
|
50
|
-
expectValid('did:method:-')
|
|
51
|
-
expectValid('did:method:-:_:.:%ab')
|
|
52
|
-
expectValid('did:method:.')
|
|
53
|
-
expectValid('did:method:_')
|
|
54
|
-
expectValid('did:method::.')
|
|
55
|
-
|
|
56
|
-
expectInvalid('did:method:val/two')
|
|
57
|
-
expectInvalid('did:method:val?two')
|
|
58
|
-
expectInvalid('did:method:val#two')
|
|
59
|
-
expectInvalid('did:method:val%')
|
|
60
|
-
|
|
61
|
-
expectValid(
|
|
62
|
-
'did:onion:2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid',
|
|
63
|
-
)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('allows some real DID values', () => {
|
|
67
|
-
expectValid('did:example:123456789abcdefghi')
|
|
68
|
-
expectValid('did:plc:7iza6de2dwap2sbkpav7c6c6')
|
|
69
|
-
expectValid('did:web:example.com')
|
|
70
|
-
expectValid('did:web:localhost%3A1234')
|
|
71
|
-
expectValid('did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N')
|
|
72
|
-
expectValid('did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a')
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('conforms to interop valid DIDs', () => {
|
|
76
|
-
const lineReader = readline.createInterface({
|
|
77
|
-
input: fs.createReadStream(
|
|
78
|
-
`${__dirname}/interop-files/did_syntax_valid.txt`,
|
|
79
|
-
),
|
|
80
|
-
terminal: false,
|
|
81
|
-
})
|
|
82
|
-
lineReader.on('line', (line) => {
|
|
83
|
-
if (line.startsWith('#') || line.length === 0) {
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
expectValid(line)
|
|
87
|
-
})
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('conforms to interop invalid DIDs', () => {
|
|
91
|
-
const lineReader = readline.createInterface({
|
|
92
|
-
input: fs.createReadStream(
|
|
93
|
-
`${__dirname}/interop-files/did_syntax_invalid.txt`,
|
|
94
|
-
),
|
|
95
|
-
terminal: false,
|
|
96
|
-
})
|
|
97
|
-
lineReader.on('line', (line) => {
|
|
98
|
-
if (line.startsWith('#') || line.length === 0) {
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
expectInvalid(line)
|
|
102
|
-
})
|
|
103
|
-
})
|
|
104
|
-
})
|
package/tests/handle.test.ts
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs'
|
|
2
|
-
import * as readline from 'node:readline'
|
|
3
|
-
import { describe, expect, it } from 'vitest'
|
|
4
|
-
import {
|
|
5
|
-
InvalidHandleError,
|
|
6
|
-
ensureValidHandle,
|
|
7
|
-
ensureValidHandleRegex,
|
|
8
|
-
normalizeAndEnsureValidHandle,
|
|
9
|
-
} from '../src/index.js'
|
|
10
|
-
|
|
11
|
-
describe('handle validation', () => {
|
|
12
|
-
const expectValid = (h: string) => {
|
|
13
|
-
ensureValidHandle(h)
|
|
14
|
-
ensureValidHandleRegex(h)
|
|
15
|
-
}
|
|
16
|
-
const expectInvalid = (h: string) => {
|
|
17
|
-
expect(() => ensureValidHandle(h)).toThrow(InvalidHandleError)
|
|
18
|
-
expect(() => ensureValidHandleRegex(h)).toThrow(InvalidHandleError)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
it('allows valid handles', () => {
|
|
22
|
-
expectValid('A.ISI.EDU')
|
|
23
|
-
expectValid('XX.LCS.MIT.EDU')
|
|
24
|
-
expectValid('SRI-NIC.ARPA')
|
|
25
|
-
expectValid('john.test')
|
|
26
|
-
expectValid('jan.test')
|
|
27
|
-
expectValid('a234567890123456789.test')
|
|
28
|
-
expectValid('john2.test')
|
|
29
|
-
expectValid('john-john.test')
|
|
30
|
-
expectValid('john.bsky.app')
|
|
31
|
-
expectValid('jo.hn')
|
|
32
|
-
expectValid('a.co')
|
|
33
|
-
expectValid('a.org')
|
|
34
|
-
expectValid('joh.n')
|
|
35
|
-
expectValid('j0.h0')
|
|
36
|
-
const longHandle =
|
|
37
|
-
'shoooort' + '.loooooooooooooooooooooooooong'.repeat(8) + '.test'
|
|
38
|
-
expect(longHandle.length).toEqual(253)
|
|
39
|
-
expectValid(longHandle)
|
|
40
|
-
expectValid('short.' + 'o'.repeat(63) + '.test')
|
|
41
|
-
expectValid('jaymome-johnber123456.test')
|
|
42
|
-
expectValid('jay.mome-johnber123456.test')
|
|
43
|
-
expectValid('john.test.bsky.app')
|
|
44
|
-
|
|
45
|
-
// NOTE: this probably isn't ever going to be a real domain, but my read of
|
|
46
|
-
// the RFC is that it would be possible
|
|
47
|
-
expectValid('john.t')
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
// NOTE: we may change this at the proto level; currently only disallowed at
|
|
51
|
-
// the registration level
|
|
52
|
-
it('allows .local and .arpa handles (proto-level)', () => {
|
|
53
|
-
expectValid('laptop.local')
|
|
54
|
-
expectValid('laptop.arpa')
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it('allows punycode handles', () => {
|
|
58
|
-
expectValid('xn--ls8h.test') // 💩.test
|
|
59
|
-
expectValid('xn--bcher-kva.tld') // bücher.tld
|
|
60
|
-
expectValid('xn--3jk.com')
|
|
61
|
-
expectValid('xn--w3d.com')
|
|
62
|
-
expectValid('xn--vqb.com')
|
|
63
|
-
expectValid('xn--ppd.com')
|
|
64
|
-
expectValid('xn--cs9a.com')
|
|
65
|
-
expectValid('xn--8r9a.com')
|
|
66
|
-
expectValid('xn--cfd.com')
|
|
67
|
-
expectValid('xn--5jk.com')
|
|
68
|
-
expectValid('xn--2lb.com')
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
it('allows onion (Tor) handles', () => {
|
|
72
|
-
expectValid('expyuzz4wqqyqhjn.onion')
|
|
73
|
-
expectValid('friend.expyuzz4wqqyqhjn.onion')
|
|
74
|
-
expectValid(
|
|
75
|
-
'g2zyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion',
|
|
76
|
-
)
|
|
77
|
-
expectValid(
|
|
78
|
-
'friend.g2zyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion',
|
|
79
|
-
)
|
|
80
|
-
expectValid(
|
|
81
|
-
'friend.g2zyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion',
|
|
82
|
-
)
|
|
83
|
-
expectValid(
|
|
84
|
-
'2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion',
|
|
85
|
-
)
|
|
86
|
-
expectValid(
|
|
87
|
-
'friend.2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion',
|
|
88
|
-
)
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it('throws on invalid handles', () => {
|
|
92
|
-
expectInvalid('did:thing.test')
|
|
93
|
-
expectInvalid('did:thing')
|
|
94
|
-
expectInvalid('john-.test')
|
|
95
|
-
expectInvalid('john.0')
|
|
96
|
-
expectInvalid('john.-')
|
|
97
|
-
expectInvalid('short.' + 'o'.repeat(64) + '.test')
|
|
98
|
-
expectInvalid('short' + '.loooooooooooooooooooooooong'.repeat(10) + '.test')
|
|
99
|
-
const longHandle =
|
|
100
|
-
'shooooort' + '.loooooooooooooooooooooooooong'.repeat(8) + '.test'
|
|
101
|
-
expect(longHandle.length).toEqual(254)
|
|
102
|
-
expectInvalid(longHandle)
|
|
103
|
-
expectInvalid('xn--bcher-.tld')
|
|
104
|
-
expectInvalid('john..test')
|
|
105
|
-
expectInvalid('jo_hn.test')
|
|
106
|
-
expectInvalid('-john.test')
|
|
107
|
-
expectInvalid('.john.test')
|
|
108
|
-
expectInvalid('jo!hn.test')
|
|
109
|
-
expectInvalid('jo%hn.test')
|
|
110
|
-
expectInvalid('jo&hn.test')
|
|
111
|
-
expectInvalid('jo@hn.test')
|
|
112
|
-
expectInvalid('jo*hn.test')
|
|
113
|
-
expectInvalid('jo|hn.test')
|
|
114
|
-
expectInvalid('jo:hn.test')
|
|
115
|
-
expectInvalid('jo/hn.test')
|
|
116
|
-
expectInvalid('john💩.test')
|
|
117
|
-
expectInvalid('bücher.test')
|
|
118
|
-
expectInvalid('john .test')
|
|
119
|
-
expectInvalid('john.test.')
|
|
120
|
-
expectInvalid('john')
|
|
121
|
-
expectInvalid('john.')
|
|
122
|
-
expectInvalid('.john')
|
|
123
|
-
expectInvalid('john.test.')
|
|
124
|
-
expectInvalid('.john.test')
|
|
125
|
-
expectInvalid(' john.test')
|
|
126
|
-
expectInvalid('john.test ')
|
|
127
|
-
expectInvalid('joh-.test')
|
|
128
|
-
expectInvalid('john.-est')
|
|
129
|
-
expectInvalid('john.tes-')
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
it('throws on "dotless" TLD handles', () => {
|
|
133
|
-
expectInvalid('org')
|
|
134
|
-
expectInvalid('ai')
|
|
135
|
-
expectInvalid('gg')
|
|
136
|
-
expectInvalid('io')
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
it('correctly validates corner cases (modern vs. old RFCs)', () => {
|
|
140
|
-
expectValid('12345.test')
|
|
141
|
-
expectValid('8.cn')
|
|
142
|
-
expectValid('4chan.org')
|
|
143
|
-
expectValid('4chan.o-g')
|
|
144
|
-
expectValid('blah.4chan.org')
|
|
145
|
-
expectValid('thing.a01')
|
|
146
|
-
expectValid('120.0.0.1.com')
|
|
147
|
-
expectValid('0john.test')
|
|
148
|
-
expectValid('9sta--ck.com')
|
|
149
|
-
expectValid('99stack.com')
|
|
150
|
-
expectValid('0ohn.test')
|
|
151
|
-
expectValid('john.t--t')
|
|
152
|
-
expectValid('thing.0aa.thing')
|
|
153
|
-
|
|
154
|
-
expectInvalid('cn.8')
|
|
155
|
-
expectInvalid('thing.0aa')
|
|
156
|
-
expectInvalid('thing.0aa')
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
it('does not allow IP addresses as handles', () => {
|
|
160
|
-
expectInvalid('127.0.0.1')
|
|
161
|
-
expectInvalid('192.168.0.142')
|
|
162
|
-
expectInvalid('fe80::7325:8a97:c100:94b')
|
|
163
|
-
expectInvalid('2600:3c03::f03c:9100:feb0:af1f')
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
it('is consistent with examples from stackoverflow', () => {
|
|
167
|
-
const okStackoverflow = [
|
|
168
|
-
'stack.com',
|
|
169
|
-
'sta-ck.com',
|
|
170
|
-
'sta---ck.com',
|
|
171
|
-
'sta--ck9.com',
|
|
172
|
-
'stack99.com',
|
|
173
|
-
'sta99ck.com',
|
|
174
|
-
'google.com.uk',
|
|
175
|
-
'google.co.in',
|
|
176
|
-
'google.com',
|
|
177
|
-
'maselkowski.pl',
|
|
178
|
-
'm.maselkowski.pl',
|
|
179
|
-
'xn--masekowski-d0b.pl',
|
|
180
|
-
'xn--fiqa61au8b7zsevnm8ak20mc4a87e.xn--fiqs8s',
|
|
181
|
-
'xn--stackoverflow.com',
|
|
182
|
-
'stackoverflow.xn--com',
|
|
183
|
-
'stackoverflow.co.uk',
|
|
184
|
-
'xn--masekowski-d0b.pl',
|
|
185
|
-
'xn--fiqa61au8b7zsevnm8ak20mc4a87e.xn--fiqs8s',
|
|
186
|
-
]
|
|
187
|
-
okStackoverflow.forEach(expectValid)
|
|
188
|
-
|
|
189
|
-
const badStackoverflow = [
|
|
190
|
-
'-notvalid.at-all',
|
|
191
|
-
'-thing.com',
|
|
192
|
-
'www.masełkowski.pl.com',
|
|
193
|
-
]
|
|
194
|
-
badStackoverflow.forEach(expectInvalid)
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
it('conforms to interop valid handles', () => {
|
|
198
|
-
const lineReader = readline.createInterface({
|
|
199
|
-
input: fs.createReadStream(
|
|
200
|
-
`${__dirname}/interop-files/handle_syntax_valid.txt`,
|
|
201
|
-
),
|
|
202
|
-
terminal: false,
|
|
203
|
-
})
|
|
204
|
-
lineReader.on('line', (line) => {
|
|
205
|
-
if (line.startsWith('#') || line.length === 0) {
|
|
206
|
-
return
|
|
207
|
-
}
|
|
208
|
-
expectValid(line)
|
|
209
|
-
})
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
it('conforms to interop invalid handles', () => {
|
|
213
|
-
const lineReader = readline.createInterface({
|
|
214
|
-
input: fs.createReadStream(
|
|
215
|
-
`${__dirname}/interop-files/handle_syntax_invalid.txt`,
|
|
216
|
-
),
|
|
217
|
-
terminal: false,
|
|
218
|
-
})
|
|
219
|
-
lineReader.on('line', (line) => {
|
|
220
|
-
if (line.startsWith('#') || line.length === 0) {
|
|
221
|
-
return
|
|
222
|
-
}
|
|
223
|
-
expectInvalid(line)
|
|
224
|
-
})
|
|
225
|
-
})
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
describe('normalization', () => {
|
|
229
|
-
it('normalizes handles', () => {
|
|
230
|
-
const normalized = normalizeAndEnsureValidHandle('JoHn.TeST')
|
|
231
|
-
expect(normalized).toBe('john.test')
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
it('throws on invalid normalized handles', () => {
|
|
235
|
-
expect(() => normalizeAndEnsureValidHandle('JoH!n.TeST')).toThrow(
|
|
236
|
-
InvalidHandleError,
|
|
237
|
-
)
|
|
238
|
-
})
|
|
239
|
-
})
|