@atproto/syntax 0.4.0 → 0.4.1
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 +8 -0
- package/LICENSE.txt +1 -1
- package/benchmark.js +208 -0
- package/dist/aturi_validation.d.ts.map +1 -1
- package/dist/aturi_validation.js +3 -11
- package/dist/aturi_validation.js.map +1 -1
- package/dist/datetime.d.ts.map +1 -1
- package/dist/did.d.ts.map +1 -1
- package/dist/handle.d.ts.map +1 -1
- package/dist/nsid.d.ts +28 -4
- package/dist/nsid.d.ts.map +1 -1
- package/dist/nsid.js +137 -47
- package/dist/nsid.js.map +1 -1
- package/dist/recordkey.d.ts.map +1 -1
- package/dist/tid.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/aturi_validation.ts +4 -10
- package/src/nsid.ts +144 -47
- package/tests/nsid.test.ts +117 -77
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @atproto/syntax
|
|
2
2
|
|
|
3
|
+
## 0.4.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#4108](https://github.com/bluesky-social/atproto/pull/4108) [`f9dc9aa4c`](https://github.com/bluesky-social/atproto/commit/f9dc9aa4c9eaf2f82d140fbf011a9015e7f1a00d) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `NSID.from(stringifiable)` utility
|
|
8
|
+
|
|
9
|
+
- [#4108](https://github.com/bluesky-social/atproto/pull/4108) [`f9dc9aa4c`](https://github.com/bluesky-social/atproto/commit/f9dc9aa4c9eaf2f82d140fbf011a9015e7f1a00d) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Improve performance of NSID validation
|
|
10
|
+
|
|
3
11
|
## 0.4.0
|
|
4
12
|
|
|
5
13
|
### Minor Changes
|
package/LICENSE.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Dual MIT/Apache-2.0 License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2022-2025 Bluesky PBC, and Contributors
|
|
3
|
+
Copyright (c) 2022-2025 Bluesky Social PBC, and Contributors
|
|
4
4
|
|
|
5
5
|
Except as otherwise noted in individual files, this software is licensed under the MIT license (<http://opensource.org/licenses/MIT>), or the Apache License, Version 2.0 (<http://www.apache.org/licenses/LICENSE-2.0>).
|
|
6
6
|
|
package/benchmark.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/* eslint-env node, commonjs */
|
|
2
|
+
|
|
3
|
+
const { validateNsid, validateNsidRegex } = require('.')
|
|
4
|
+
|
|
5
|
+
// $ node benchmark.js
|
|
6
|
+
// valid NSIDs {
|
|
7
|
+
// parsed: 181.56524884700775,
|
|
8
|
+
// regexp: 77.61082607507706,
|
|
9
|
+
// optimized: 60.183539509773254
|
|
10
|
+
// }
|
|
11
|
+
// invalid NSIDs {
|
|
12
|
+
// parsed: 128.7685609459877,
|
|
13
|
+
// regexp: 108.75775015354156,
|
|
14
|
+
// optimized: 53.196488440036774
|
|
15
|
+
// }
|
|
16
|
+
|
|
17
|
+
bench('valid NSIDs', true, [
|
|
18
|
+
'com.example.foo',
|
|
19
|
+
'o'.repeat(63) + '.foo.bar',
|
|
20
|
+
'com.' + 'o'.repeat(63) + '.foo',
|
|
21
|
+
'com.example.' + 'o'.repeat(63),
|
|
22
|
+
'com.' + 'middle.'.repeat(40) + 'foo',
|
|
23
|
+
'com.example.fooBar',
|
|
24
|
+
'net.users.bob.ping',
|
|
25
|
+
'a.b.c',
|
|
26
|
+
'm.xn--masekowski-d0b.pl',
|
|
27
|
+
'one.two.three',
|
|
28
|
+
'one.two.three.four-and.FiVe',
|
|
29
|
+
'one.2.three',
|
|
30
|
+
'a-0.b-1.c',
|
|
31
|
+
'a0.b1.cc',
|
|
32
|
+
'cn.8.lex.stuff',
|
|
33
|
+
'test.12345.record',
|
|
34
|
+
'a01.thing.record',
|
|
35
|
+
'a.0.c',
|
|
36
|
+
'xn--fiqs8s.xn--fiqa61au8b7zsevnm8ak20mc4a87e.record.two',
|
|
37
|
+
'a0.b1.c3',
|
|
38
|
+
'com.example.f00',
|
|
39
|
+
'onion.expyuzz4wqqyqhjn.spec.getThing',
|
|
40
|
+
'onion.g2zyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.lex.deleteThing',
|
|
41
|
+
'org.4chan.lex.getThing',
|
|
42
|
+
'cn.8.lex.stuff',
|
|
43
|
+
'onion.2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.lex.deleteThing',
|
|
44
|
+
'a.'.repeat(158) + 'a',
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
bench('invalid NSIDs', false, [
|
|
48
|
+
'a.'.repeat(158) + '9',
|
|
49
|
+
'a.'.repeat(154) + 'a😅.9',
|
|
50
|
+
'o'.repeat(64) + '.foo.bar',
|
|
51
|
+
'com.' + 'o'.repeat(64) + '.foo',
|
|
52
|
+
'com.example.' + 'o'.repeat(64),
|
|
53
|
+
'com.' + 'middle.'.repeat(50) + 'foo',
|
|
54
|
+
'com.example.foo.*',
|
|
55
|
+
'com.example.foo.blah*',
|
|
56
|
+
'com.example.foo.*blah',
|
|
57
|
+
'com.exa💩ple.thing',
|
|
58
|
+
'a-0.b-1.c-3',
|
|
59
|
+
'a-0.b-1.c-o',
|
|
60
|
+
'1.0.0.127.record',
|
|
61
|
+
'0two.example.foo',
|
|
62
|
+
'example.com',
|
|
63
|
+
'com.example',
|
|
64
|
+
'a.',
|
|
65
|
+
'.one.two.three',
|
|
66
|
+
'one.two.three ',
|
|
67
|
+
'one.two..three',
|
|
68
|
+
'one .two.three',
|
|
69
|
+
' one.two.three',
|
|
70
|
+
'com.atproto.feed.p@st',
|
|
71
|
+
'com.atproto.feed.p_st',
|
|
72
|
+
'com.atproto.feed.p*st',
|
|
73
|
+
'com.atproto.feed.po#t',
|
|
74
|
+
'com.atproto.feed.p!ot',
|
|
75
|
+
'com.example-.foo',
|
|
76
|
+
'com.-example.foo',
|
|
77
|
+
'com.example.0foo',
|
|
78
|
+
'com.example.f-o',
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
function bench(name, expectedResult, cases) {
|
|
82
|
+
const validators = {
|
|
83
|
+
parsed: (nsid) => validateNsid(nsid).success,
|
|
84
|
+
regexp: (nsid) => validateNsidRegex(nsid).success,
|
|
85
|
+
optimized: (nsid) => validateNsidOptimized(nsid).success,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const times = Object.fromEntries(Object.keys(validators).map((k) => [k, 0]))
|
|
89
|
+
|
|
90
|
+
for (let i = 0; i < 1000; i++) {
|
|
91
|
+
for (const [name, fn] of Object.entries(validators)) {
|
|
92
|
+
const start = performance.now()
|
|
93
|
+
for (let j = 0; j < 20; j++) {
|
|
94
|
+
for (const value of cases) {
|
|
95
|
+
if (fn(value) !== expectedResult) {
|
|
96
|
+
throw new Error(`Validator ${name} gave wrong result`)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
times[name] += performance.now() - start
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log(
|
|
105
|
+
name,
|
|
106
|
+
Object.fromEntries(
|
|
107
|
+
Object.entries(times).map(([k, v]) => [k, `${v.toFixed(2)} ms`]),
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** @param value {string} */
|
|
113
|
+
function validateNsidOptimized(value) {
|
|
114
|
+
const { length } = value
|
|
115
|
+
if (length > 253 + 1 + 63) {
|
|
116
|
+
return { success: false, message: 'NSID is too long (317 chars max)' }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let partCount = 1
|
|
120
|
+
let partStart = 0
|
|
121
|
+
let partHasLeadingDigit = false
|
|
122
|
+
let partHasHyphen = false
|
|
123
|
+
|
|
124
|
+
let charCode
|
|
125
|
+
for (let i = 0; i < length; i++) {
|
|
126
|
+
charCode = value.charCodeAt(i)
|
|
127
|
+
|
|
128
|
+
// Hot path: check frequent chars first
|
|
129
|
+
if (
|
|
130
|
+
(charCode >= 97 && charCode <= 122) /* a-z */ ||
|
|
131
|
+
(charCode >= 65 && charCode <= 90) /* A-Z */
|
|
132
|
+
) {
|
|
133
|
+
// All good
|
|
134
|
+
} else if (charCode >= 48 && charCode <= 57 /* 0-9 */) {
|
|
135
|
+
if (i === 0) {
|
|
136
|
+
return {
|
|
137
|
+
success: false,
|
|
138
|
+
message: 'NSID first part may not start with a digit',
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// All good
|
|
143
|
+
|
|
144
|
+
if (i === partStart) {
|
|
145
|
+
partHasLeadingDigit = true
|
|
146
|
+
}
|
|
147
|
+
} else if (charCode === 45 /* - */) {
|
|
148
|
+
if (i === partStart) {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
message: 'NSID part can not start with hyphen',
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (i === length - 1 || value.charCodeAt(i + 1) === 46 /* . */) {
|
|
155
|
+
return { success: false, message: 'NSID part can not end with hyphen' }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// All good
|
|
159
|
+
|
|
160
|
+
partHasHyphen = true
|
|
161
|
+
} else if (charCode === 46 /* . */) {
|
|
162
|
+
// Check prev part size
|
|
163
|
+
if (i === partStart) {
|
|
164
|
+
return { success: false, message: 'NSID parts can not be empty' }
|
|
165
|
+
}
|
|
166
|
+
if (i - partStart > 63) {
|
|
167
|
+
return { success: false, message: 'NSID part too long (max 63 chars)' }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// All good
|
|
171
|
+
|
|
172
|
+
partCount++
|
|
173
|
+
partStart = i + 1
|
|
174
|
+
partHasHyphen = false
|
|
175
|
+
partHasLeadingDigit = false
|
|
176
|
+
} else {
|
|
177
|
+
return {
|
|
178
|
+
success: false,
|
|
179
|
+
message:
|
|
180
|
+
'Disallowed characters in NSID (ASCII letters, digits, dashes, periods only)',
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Check last part size
|
|
186
|
+
if (length === partStart) {
|
|
187
|
+
return { success: false, message: 'NSID parts can not be empty' }
|
|
188
|
+
}
|
|
189
|
+
if (length - partStart > 63) {
|
|
190
|
+
return { success: false, message: 'NSID part too long (max 63 chars)' }
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check last part chars
|
|
194
|
+
if (partHasHyphen || partHasLeadingDigit) {
|
|
195
|
+
return {
|
|
196
|
+
success: false,
|
|
197
|
+
message:
|
|
198
|
+
'NSID name part must be only letters and digits (and no leading digit)',
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Check part count
|
|
203
|
+
if (partCount < 3) {
|
|
204
|
+
return { success: false, message: 'NSID needs at least three parts' }
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return { success: true, value }
|
|
208
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aturi_validation.d.ts","sourceRoot":"","sources":["../src/aturi_validation.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"aturi_validation.d.ts","sourceRoot":"","sources":["../src/aturi_validation.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,SA6E3C,CAAA;AAED,eAAO,MAAM,qBAAqB,GAAI,KAAK,MAAM,KAAG,IA4BnD,CAAA"}
|
package/dist/aturi_validation.js
CHANGED
|
@@ -51,10 +51,7 @@ const ensureValidAtUri = (uri) => {
|
|
|
51
51
|
if (parts[3].length === 0) {
|
|
52
52
|
throw new Error('ATURI can not have a slash after authority without a path segment');
|
|
53
53
|
}
|
|
54
|
-
|
|
55
|
-
(0, nsid_1.ensureValidNsid)(parts[3]);
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
54
|
+
if (!(0, nsid_1.isValidNsid)(parts[3])) {
|
|
58
55
|
throw new Error('ATURI requires first path segment (if supplied) to be valid NSID');
|
|
59
56
|
}
|
|
60
57
|
}
|
|
@@ -104,13 +101,8 @@ const ensureValidAtUriRegex = (uri) => {
|
|
|
104
101
|
throw new Error('ATURI authority must be a valid handle or DID');
|
|
105
102
|
}
|
|
106
103
|
}
|
|
107
|
-
if (groups.collection) {
|
|
108
|
-
|
|
109
|
-
(0, nsid_1.ensureValidNsidRegex)(groups.collection);
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
throw new Error('ATURI collection path segment must be a valid NSID');
|
|
113
|
-
}
|
|
104
|
+
if (groups.collection && !(0, nsid_1.isValidNsid)(groups.collection)) {
|
|
105
|
+
throw new Error('ATURI collection path segment must be a valid NSID');
|
|
114
106
|
}
|
|
115
107
|
if (uri.length > 8 * 1024) {
|
|
116
108
|
throw new Error('ATURI is far too long');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aturi_validation.js","sourceRoot":"","sources":["../src/aturi_validation.ts"],"names":[],"mappings":";;;AAAA,+BAA2D;AAC3D,qCAAoE;AACpE,
|
|
1
|
+
{"version":3,"file":"aturi_validation.js","sourceRoot":"","sources":["../src/aturi_validation.ts"],"names":[],"mappings":"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"}
|
package/dist/datetime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datetime.d.ts","sourceRoot":"","sources":["../src/datetime.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"datetime.d.ts","sourceRoot":"","sources":["../src/datetime.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,KAAG,IA4BnD,CAAA;AAID,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,KAAG,OAW/C,CAAA;AAYD,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,KAAG,MAkCjD,CAAA;AAMD,eAAO,MAAM,uBAAuB,GAAI,OAAO,MAAM,KAAG,MASvD,CAAA;AAID,qBAAa,oBAAqB,SAAQ,KAAK;CAAG"}
|
package/dist/did.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"did.d.ts","sourceRoot":"","sources":["../src/did.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"did.d.ts","sourceRoot":"","sources":["../src/did.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,IA8B5C,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,KAAK,MAAM,KAAG,IAUjD,CAAA;AAED,qBAAa,eAAgB,SAAQ,KAAK;CAAG"}
|
package/dist/handle.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,mBAAmB,CAAA;AAM9C,eAAO,MAAM,eAAe,UAY3B,CAAA;AAqBD,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,mBAAmB,CAAA;AAM9C,eAAO,MAAM,eAAe,UAY3B,CAAA;AAqBD,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,KAAG,IAkClD,CAAA;AAGD,eAAO,MAAM,sBAAsB,GAAI,QAAQ,MAAM,KAAG,IAWvD,CAAA;AAED,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MAEhD,CAAA;AAED,eAAO,MAAM,6BAA6B,GAAI,QAAQ,MAAM,KAAG,MAI9D,CAAA;AAED,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,KAAG,OAW9C,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,QAAQ,MAAM,KAAG,OAE3C,CAAA;AAED,qBAAa,kBAAmB,SAAQ,KAAK;CAAG;AAChD,6BAA6B;AAC7B,qBAAa,mBAAoB,SAAQ,KAAK;CAAG;AACjD,6BAA6B;AAC7B,qBAAa,sBAAuB,SAAQ,KAAK;CAAG;AACpD,6BAA6B;AAC7B,qBAAa,qBAAsB,SAAQ,KAAK;CAAG"}
|
package/dist/nsid.d.ts
CHANGED
|
@@ -1,15 +1,39 @@
|
|
|
1
1
|
export declare class NSID {
|
|
2
|
-
segments: string[];
|
|
3
|
-
static parse(
|
|
2
|
+
readonly segments: readonly string[];
|
|
3
|
+
static parse(input: string): NSID;
|
|
4
4
|
static create(authority: string, name: string): NSID;
|
|
5
5
|
static isValid(nsid: string): boolean;
|
|
6
|
+
static from(input: {
|
|
7
|
+
toString: () => string;
|
|
8
|
+
}): NSID;
|
|
6
9
|
constructor(nsid: string);
|
|
7
10
|
get authority(): string;
|
|
8
11
|
get name(): string | undefined;
|
|
9
12
|
toString(): string;
|
|
10
13
|
}
|
|
11
|
-
export declare
|
|
12
|
-
export declare
|
|
14
|
+
export declare function ensureValidNsid(nsid: string): void;
|
|
15
|
+
export declare function parseNsid(nsid: string): string[];
|
|
16
|
+
export declare function isValidNsid(nsid: string): boolean;
|
|
17
|
+
type ValidateResult<T> = {
|
|
18
|
+
success: true;
|
|
19
|
+
value: T;
|
|
20
|
+
} | {
|
|
21
|
+
success: false;
|
|
22
|
+
message: string;
|
|
23
|
+
};
|
|
24
|
+
export declare function validateNsid(input: string): ValidateResult<string[]>;
|
|
25
|
+
/**
|
|
26
|
+
* @deprecated Use {@link ensureValidNsid} if you care about error details,
|
|
27
|
+
* {@link parseNsid}/{@link NSID.parse} if you need the parsed segments, or
|
|
28
|
+
* {@link isValidNsid} if you just want a boolean.
|
|
29
|
+
*/
|
|
30
|
+
export declare function ensureValidNsidRegex(nsid: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Regexp based validation that behaves identically to the previous code but
|
|
33
|
+
* provides less detailed error messages (while being 20% to 50% faster).
|
|
34
|
+
*/
|
|
35
|
+
export declare function validateNsidRegex(value: string): ValidateResult<string>;
|
|
13
36
|
export declare class InvalidNsidError extends Error {
|
|
14
37
|
}
|
|
38
|
+
export {};
|
|
15
39
|
//# sourceMappingURL=nsid.d.ts.map
|
package/dist/nsid.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nsid.d.ts","sourceRoot":"","sources":["../src/nsid.ts"],"names":[],"mappings":"AAaA,qBAAa,IAAI;IACf,QAAQ,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"nsid.d.ts","sourceRoot":"","sources":["../src/nsid.ts"],"names":[],"mappings":"AAaA,qBAAa,IAAI;IACf,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IAEpC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIjC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKpD,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM;IAI3B,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;KAAE,GAAG,IAAI;gBAWxC,IAAI,EAAE,MAAM;IAIxB,IAAI,SAAS,WAKZ;IAED,IAAI,IAAI,uBAEP;IAED,QAAQ;CAGT;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGlD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAIhD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIjD;AAED,KAAK,cAAc,CAAC,CAAC,IACjB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAC3B;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAKvC,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,CA0DpE;AA4BD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGvD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAuBvE;AAED,qBAAa,gBAAiB,SAAQ,KAAK;CAAG"}
|
package/dist/nsid.js
CHANGED
|
@@ -7,38 +7,47 @@ number = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" / "0"
|
|
|
7
7
|
delim = "."
|
|
8
8
|
segment = alpha *( alpha / number / "-" )
|
|
9
9
|
authority = segment *( delim segment )
|
|
10
|
-
name = alpha *( alpha )
|
|
10
|
+
name = alpha *( alpha / number )
|
|
11
11
|
nsid = authority delim name
|
|
12
12
|
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.InvalidNsidError = exports.
|
|
15
|
+
exports.InvalidNsidError = exports.NSID = void 0;
|
|
16
|
+
exports.ensureValidNsid = ensureValidNsid;
|
|
17
|
+
exports.parseNsid = parseNsid;
|
|
18
|
+
exports.isValidNsid = isValidNsid;
|
|
19
|
+
exports.validateNsid = validateNsid;
|
|
20
|
+
exports.ensureValidNsidRegex = ensureValidNsidRegex;
|
|
21
|
+
exports.validateNsidRegex = validateNsidRegex;
|
|
16
22
|
class NSID {
|
|
17
|
-
static parse(
|
|
18
|
-
return new NSID(
|
|
23
|
+
static parse(input) {
|
|
24
|
+
return new NSID(input);
|
|
19
25
|
}
|
|
20
26
|
static create(authority, name) {
|
|
21
|
-
const
|
|
22
|
-
return new NSID(
|
|
27
|
+
const input = [...authority.split('.').reverse(), name].join('.');
|
|
28
|
+
return new NSID(input);
|
|
23
29
|
}
|
|
24
30
|
static isValid(nsid) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
return isValidNsid(nsid);
|
|
32
|
+
}
|
|
33
|
+
static from(input) {
|
|
34
|
+
if (input instanceof NSID) {
|
|
35
|
+
// No need to clone, NSID is immutable
|
|
36
|
+
return input;
|
|
28
37
|
}
|
|
29
|
-
|
|
30
|
-
return
|
|
38
|
+
if (Array.isArray(input)) {
|
|
39
|
+
return new NSID(input.join('.'));
|
|
31
40
|
}
|
|
41
|
+
return new NSID(String(input));
|
|
32
42
|
}
|
|
33
43
|
constructor(nsid) {
|
|
34
44
|
Object.defineProperty(this, "segments", {
|
|
35
45
|
enumerable: true,
|
|
36
46
|
configurable: true,
|
|
37
47
|
writable: true,
|
|
38
|
-
value:
|
|
48
|
+
value: void 0
|
|
39
49
|
});
|
|
40
|
-
|
|
41
|
-
this.segments = nsid.split('.');
|
|
50
|
+
this.segments = parseNsid(nsid);
|
|
42
51
|
}
|
|
43
52
|
get authority() {
|
|
44
53
|
return this.segments
|
|
@@ -54,53 +63,134 @@ class NSID {
|
|
|
54
63
|
}
|
|
55
64
|
}
|
|
56
65
|
exports.NSID = NSID;
|
|
66
|
+
function ensureValidNsid(nsid) {
|
|
67
|
+
const result = validateNsid(nsid);
|
|
68
|
+
if (!result.success)
|
|
69
|
+
throw new InvalidNsidError(result.message);
|
|
70
|
+
}
|
|
71
|
+
function parseNsid(nsid) {
|
|
72
|
+
const result = validateNsid(nsid);
|
|
73
|
+
if (!result.success)
|
|
74
|
+
throw new InvalidNsidError(result.message);
|
|
75
|
+
return result.value;
|
|
76
|
+
}
|
|
77
|
+
function isValidNsid(nsid) {
|
|
78
|
+
// Since the regex version is more performant for valid NSIDs, we use it when
|
|
79
|
+
// we don't care about error details.
|
|
80
|
+
return validateNsidRegex(nsid).success;
|
|
81
|
+
}
|
|
57
82
|
// Human readable constraints on NSID:
|
|
58
83
|
// - a valid domain in reversed notation
|
|
59
84
|
// - followed by an additional period-separated name, which is camel-case letters
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
85
|
+
function validateNsid(input) {
|
|
86
|
+
if (input.length > 253 + 1 + 63) {
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
message: 'NSID is too long (317 chars max)',
|
|
90
|
+
};
|
|
65
91
|
}
|
|
66
|
-
if (
|
|
67
|
-
|
|
92
|
+
if (hasDisallowedCharacters(input)) {
|
|
93
|
+
return {
|
|
94
|
+
success: false,
|
|
95
|
+
message: 'Disallowed characters in NSID (ASCII letters, digits, dashes, periods only)',
|
|
96
|
+
};
|
|
68
97
|
}
|
|
69
|
-
const
|
|
70
|
-
if (
|
|
71
|
-
|
|
98
|
+
const segments = input.split('.');
|
|
99
|
+
if (segments.length < 3) {
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
message: 'NSID needs at least three parts',
|
|
103
|
+
};
|
|
72
104
|
}
|
|
73
|
-
for (
|
|
74
|
-
const l = labels[i];
|
|
105
|
+
for (const l of segments) {
|
|
75
106
|
if (l.length < 1) {
|
|
76
|
-
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
message: 'NSID parts can not be empty',
|
|
110
|
+
};
|
|
77
111
|
}
|
|
78
112
|
if (l.length > 63) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
if (/^[0-9]/.test(l) && i === 0) {
|
|
85
|
-
throw new InvalidNsidError('NSID first part may not start with a digit');
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
message: 'NSID part too long (max 63 chars)',
|
|
116
|
+
};
|
|
86
117
|
}
|
|
87
|
-
if (
|
|
88
|
-
|
|
118
|
+
if (startsWithHyphen(l) || endsWithHyphen(l)) {
|
|
119
|
+
return {
|
|
120
|
+
success: false,
|
|
121
|
+
message: 'NSID parts can not start or end with hyphen',
|
|
122
|
+
};
|
|
89
123
|
}
|
|
90
124
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (!/^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(\.[a-zA-Z]([a-zA-Z0-9]{0,62})?)$/.test(nsid)) {
|
|
97
|
-
throw new InvalidNsidError("NSID didn't validate via regex");
|
|
125
|
+
if (startsWithNumber(segments[0])) {
|
|
126
|
+
return {
|
|
127
|
+
success: false,
|
|
128
|
+
message: 'NSID first part may not start with a digit',
|
|
129
|
+
};
|
|
98
130
|
}
|
|
99
|
-
if (
|
|
100
|
-
|
|
131
|
+
if (!isValidIdentifier(segments[segments.length - 1])) {
|
|
132
|
+
return {
|
|
133
|
+
success: false,
|
|
134
|
+
message: 'NSID name part must be only letters and digits (and no leading digit)',
|
|
135
|
+
};
|
|
101
136
|
}
|
|
102
|
-
|
|
103
|
-
|
|
137
|
+
return {
|
|
138
|
+
success: true,
|
|
139
|
+
value: segments,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function hasDisallowedCharacters(v) {
|
|
143
|
+
return !/^[a-zA-Z0-9.-]*$/.test(v);
|
|
144
|
+
}
|
|
145
|
+
function startsWithNumber(v) {
|
|
146
|
+
const charCode = v.charCodeAt(0);
|
|
147
|
+
return charCode >= 48 && charCode <= 57;
|
|
148
|
+
}
|
|
149
|
+
function startsWithHyphen(v) {
|
|
150
|
+
return v.charCodeAt(0) === 45; /* - */
|
|
151
|
+
}
|
|
152
|
+
function endsWithHyphen(v) {
|
|
153
|
+
return v.charCodeAt(v.length - 1) === 45; /* - */
|
|
154
|
+
}
|
|
155
|
+
function isValidIdentifier(v) {
|
|
156
|
+
// Note, since we already know that "v" only contains [a-zA-Z0-9-], we can
|
|
157
|
+
// simplify the following regex by checking only the first char and presence
|
|
158
|
+
// of "-".
|
|
159
|
+
// return /^[a-zA-Z][a-zA-Z0-9]*$/.test(v)
|
|
160
|
+
return !startsWithNumber(v) && !v.includes('-');
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* @deprecated Use {@link ensureValidNsid} if you care about error details,
|
|
164
|
+
* {@link parseNsid}/{@link NSID.parse} if you need the parsed segments, or
|
|
165
|
+
* {@link isValidNsid} if you just want a boolean.
|
|
166
|
+
*/
|
|
167
|
+
function ensureValidNsidRegex(nsid) {
|
|
168
|
+
const result = validateNsidRegex(nsid);
|
|
169
|
+
if (!result.success)
|
|
170
|
+
throw new InvalidNsidError(result.message);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Regexp based validation that behaves identically to the previous code but
|
|
174
|
+
* provides less detailed error messages (while being 20% to 50% faster).
|
|
175
|
+
*/
|
|
176
|
+
function validateNsidRegex(value) {
|
|
177
|
+
if (value.length > 253 + 1 + 63) {
|
|
178
|
+
return {
|
|
179
|
+
success: false,
|
|
180
|
+
message: 'NSID is too long (317 chars max)',
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (!/^[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?:\.[a-zA-Z](?:[a-zA-Z0-9]{0,62})?)$/.test(value)) {
|
|
184
|
+
return {
|
|
185
|
+
success: false,
|
|
186
|
+
message: "NSID didn't validate via regex",
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
success: true,
|
|
191
|
+
value,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
104
194
|
class InvalidNsidError extends Error {
|
|
105
195
|
}
|
|
106
196
|
exports.InvalidNsidError = InvalidNsidError;
|
package/dist/nsid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nsid.js","sourceRoot":"","sources":["../src/nsid.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;EAWE;;;
|
|
1
|
+
{"version":3,"file":"nsid.js","sourceRoot":"","sources":["../src/nsid.ts"],"names":[],"mappings":"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"}
|
package/dist/recordkey.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordkey.d.ts","sourceRoot":"","sources":["../src/recordkey.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"recordkey.d.ts","sourceRoot":"","sources":["../src/recordkey.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,IAUnD,CAAA;AAED,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,OAW/C,CAAA;AAED,qBAAa,qBAAsB,SAAQ,KAAK;CAAG"}
|
package/dist/tid.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tid.d.ts","sourceRoot":"","sources":["../src/tid.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"tid.d.ts","sourceRoot":"","sources":["../src/tid.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,IAQ5C,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,OAExC,CAAA;AAED,qBAAa,eAAgB,SAAQ,KAAK;CAAG"}
|
package/package.json
CHANGED
package/src/aturi_validation.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ensureValidDid, ensureValidDidRegex } from './did'
|
|
2
2
|
import { ensureValidHandle, ensureValidHandleRegex } from './handle'
|
|
3
|
-
import {
|
|
3
|
+
import { isValidNsid } from './nsid'
|
|
4
4
|
|
|
5
5
|
// Human-readable constraints on ATURI:
|
|
6
6
|
// - following regular URLs, a 8KByte hard total length limit
|
|
@@ -53,9 +53,7 @@ export const ensureValidAtUri = (uri: string) => {
|
|
|
53
53
|
'ATURI can not have a slash after authority without a path segment',
|
|
54
54
|
)
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
ensureValidNsid(parts[3])
|
|
58
|
-
} catch {
|
|
56
|
+
if (!isValidNsid(parts[3])) {
|
|
59
57
|
throw new Error(
|
|
60
58
|
'ATURI requires first path segment (if supplied) to be valid NSID',
|
|
61
59
|
)
|
|
@@ -117,12 +115,8 @@ export const ensureValidAtUriRegex = (uri: string): void => {
|
|
|
117
115
|
}
|
|
118
116
|
}
|
|
119
117
|
|
|
120
|
-
if (groups.collection) {
|
|
121
|
-
|
|
122
|
-
ensureValidNsidRegex(groups.collection)
|
|
123
|
-
} catch {
|
|
124
|
-
throw new Error('ATURI collection path segment must be a valid NSID')
|
|
125
|
-
}
|
|
118
|
+
if (groups.collection && !isValidNsid(groups.collection)) {
|
|
119
|
+
throw new Error('ATURI collection path segment must be a valid NSID')
|
|
126
120
|
}
|
|
127
121
|
|
|
128
122
|
if (uri.length > 8 * 1024) {
|
package/src/nsid.ts
CHANGED
|
@@ -6,35 +6,40 @@ number = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" / "0"
|
|
|
6
6
|
delim = "."
|
|
7
7
|
segment = alpha *( alpha / number / "-" )
|
|
8
8
|
authority = segment *( delim segment )
|
|
9
|
-
name = alpha *( alpha )
|
|
9
|
+
name = alpha *( alpha / number )
|
|
10
10
|
nsid = authority delim name
|
|
11
11
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
export class NSID {
|
|
15
|
-
segments: string[]
|
|
15
|
+
readonly segments: readonly string[]
|
|
16
16
|
|
|
17
|
-
static parse(
|
|
18
|
-
return new NSID(
|
|
17
|
+
static parse(input: string): NSID {
|
|
18
|
+
return new NSID(input)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
static create(authority: string, name: string): NSID {
|
|
22
|
-
const
|
|
23
|
-
return new NSID(
|
|
22
|
+
const input = [...authority.split('.').reverse(), name].join('.')
|
|
23
|
+
return new NSID(input)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
static isValid(nsid: string)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
static isValid(nsid: string) {
|
|
27
|
+
return isValidNsid(nsid)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static from(input: { toString: () => string }): NSID {
|
|
31
|
+
if (input instanceof NSID) {
|
|
32
|
+
// No need to clone, NSID is immutable
|
|
33
|
+
return input
|
|
34
|
+
}
|
|
35
|
+
if (Array.isArray(input)) {
|
|
36
|
+
return new NSID((input as string[]).join('.'))
|
|
32
37
|
}
|
|
38
|
+
return new NSID(String(input))
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
constructor(nsid: string) {
|
|
36
|
-
|
|
37
|
-
this.segments = nsid.split('.')
|
|
42
|
+
this.segments = parseNsid(nsid)
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
get authority() {
|
|
@@ -53,60 +58,152 @@ export class NSID {
|
|
|
53
58
|
}
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
export function ensureValidNsid(nsid: string): void {
|
|
62
|
+
const result = validateNsid(nsid)
|
|
63
|
+
if (!result.success) throw new InvalidNsidError(result.message)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function parseNsid(nsid: string): string[] {
|
|
67
|
+
const result = validateNsid(nsid)
|
|
68
|
+
if (!result.success) throw new InvalidNsidError(result.message)
|
|
69
|
+
return result.value
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function isValidNsid(nsid: string): boolean {
|
|
73
|
+
// Since the regex version is more performant for valid NSIDs, we use it when
|
|
74
|
+
// we don't care about error details.
|
|
75
|
+
return validateNsidRegex(nsid).success
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type ValidateResult<T> =
|
|
79
|
+
| { success: true; value: T }
|
|
80
|
+
| { success: false; message: string }
|
|
81
|
+
|
|
56
82
|
// Human readable constraints on NSID:
|
|
57
83
|
// - a valid domain in reversed notation
|
|
58
84
|
// - followed by an additional period-separated name, which is camel-case letters
|
|
59
|
-
export
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
'Disallowed characters in NSID (ASCII letters, digits, dashes, periods only)',
|
|
66
|
-
)
|
|
85
|
+
export function validateNsid(input: string): ValidateResult<string[]> {
|
|
86
|
+
if (input.length > 253 + 1 + 63) {
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
message: 'NSID is too long (317 chars max)',
|
|
90
|
+
}
|
|
67
91
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
92
|
+
if (hasDisallowedCharacters(input)) {
|
|
93
|
+
return {
|
|
94
|
+
success: false,
|
|
95
|
+
message:
|
|
96
|
+
'Disallowed characters in NSID (ASCII letters, digits, dashes, periods only)',
|
|
97
|
+
}
|
|
71
98
|
}
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
74
|
-
|
|
99
|
+
const segments = input.split('.')
|
|
100
|
+
if (segments.length < 3) {
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
message: 'NSID needs at least three parts',
|
|
104
|
+
}
|
|
75
105
|
}
|
|
76
|
-
for (
|
|
77
|
-
const l = labels[i]
|
|
106
|
+
for (const l of segments) {
|
|
78
107
|
if (l.length < 1) {
|
|
79
|
-
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
message: 'NSID parts can not be empty',
|
|
111
|
+
}
|
|
80
112
|
}
|
|
81
113
|
if (l.length > 63) {
|
|
82
|
-
|
|
114
|
+
return {
|
|
115
|
+
success: false,
|
|
116
|
+
message: 'NSID part too long (max 63 chars)',
|
|
117
|
+
}
|
|
83
118
|
}
|
|
84
|
-
if (l
|
|
85
|
-
|
|
119
|
+
if (startsWithHyphen(l) || endsWithHyphen(l)) {
|
|
120
|
+
return {
|
|
121
|
+
success: false,
|
|
122
|
+
message: 'NSID parts can not start or end with hyphen',
|
|
123
|
+
}
|
|
86
124
|
}
|
|
87
|
-
|
|
88
|
-
|
|
125
|
+
}
|
|
126
|
+
if (startsWithNumber(segments[0])) {
|
|
127
|
+
return {
|
|
128
|
+
success: false,
|
|
129
|
+
message: 'NSID first part may not start with a digit',
|
|
89
130
|
}
|
|
90
|
-
|
|
91
|
-
|
|
131
|
+
}
|
|
132
|
+
if (!isValidIdentifier(segments[segments.length - 1])) {
|
|
133
|
+
return {
|
|
134
|
+
success: false,
|
|
135
|
+
message:
|
|
92
136
|
'NSID name part must be only letters and digits (and no leading digit)',
|
|
93
|
-
)
|
|
94
137
|
}
|
|
95
138
|
}
|
|
139
|
+
return {
|
|
140
|
+
success: true,
|
|
141
|
+
value: segments,
|
|
142
|
+
}
|
|
96
143
|
}
|
|
97
144
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
145
|
+
function hasDisallowedCharacters(v) {
|
|
146
|
+
return !/^[a-zA-Z0-9.-]*$/.test(v)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function startsWithNumber(v: string) {
|
|
150
|
+
const charCode = v.charCodeAt(0)
|
|
151
|
+
return charCode >= 48 && charCode <= 57
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function startsWithHyphen(v: string) {
|
|
155
|
+
return v.charCodeAt(0) === 45 /* - */
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function endsWithHyphen(v: string) {
|
|
159
|
+
return v.charCodeAt(v.length - 1) === 45 /* - */
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function isValidIdentifier(v: string) {
|
|
163
|
+
// Note, since we already know that "v" only contains [a-zA-Z0-9-], we can
|
|
164
|
+
// simplify the following regex by checking only the first char and presence
|
|
165
|
+
// of "-".
|
|
166
|
+
|
|
167
|
+
// return /^[a-zA-Z][a-zA-Z0-9]*$/.test(v)
|
|
168
|
+
return !startsWithNumber(v) && !v.includes('-')
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @deprecated Use {@link ensureValidNsid} if you care about error details,
|
|
173
|
+
* {@link parseNsid}/{@link NSID.parse} if you need the parsed segments, or
|
|
174
|
+
* {@link isValidNsid} if you just want a boolean.
|
|
175
|
+
*/
|
|
176
|
+
export function ensureValidNsidRegex(nsid: string): void {
|
|
177
|
+
const result = validateNsidRegex(nsid)
|
|
178
|
+
if (!result.success) throw new InvalidNsidError(result.message)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Regexp based validation that behaves identically to the previous code but
|
|
183
|
+
* provides less detailed error messages (while being 20% to 50% faster).
|
|
184
|
+
*/
|
|
185
|
+
export function validateNsidRegex(value: string): ValidateResult<string> {
|
|
186
|
+
if (value.length > 253 + 1 + 63) {
|
|
187
|
+
return {
|
|
188
|
+
success: false,
|
|
189
|
+
message: 'NSID is too long (317 chars max)',
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
101
193
|
if (
|
|
102
|
-
!/^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(
|
|
103
|
-
|
|
194
|
+
!/^[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?:\.[a-zA-Z](?:[a-zA-Z0-9]{0,62})?)$/.test(
|
|
195
|
+
value,
|
|
104
196
|
)
|
|
105
197
|
) {
|
|
106
|
-
|
|
198
|
+
return {
|
|
199
|
+
success: false,
|
|
200
|
+
message: "NSID didn't validate via regex",
|
|
201
|
+
}
|
|
107
202
|
}
|
|
108
|
-
|
|
109
|
-
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
success: true,
|
|
206
|
+
value,
|
|
110
207
|
}
|
|
111
208
|
}
|
|
112
209
|
|
package/tests/nsid.test.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import * as fs from 'node:fs'
|
|
2
|
-
import * as readline from 'node:readline'
|
|
3
2
|
import {
|
|
4
3
|
InvalidNsidError,
|
|
5
4
|
NSID,
|
|
6
5
|
ensureValidNsid,
|
|
7
|
-
|
|
6
|
+
isValidNsid,
|
|
7
|
+
parseNsid,
|
|
8
|
+
validateNsid,
|
|
9
|
+
validateNsidRegex,
|
|
8
10
|
} from '../src'
|
|
9
11
|
|
|
10
12
|
describe('NSID parsing & creation', () => {
|
|
@@ -39,12 +41,30 @@ describe('NSID parsing & creation', () => {
|
|
|
39
41
|
|
|
40
42
|
describe('NSID validation', () => {
|
|
41
43
|
const expectValid = (h: string) => {
|
|
44
|
+
expect(isValidNsid(h)).toBe(true)
|
|
42
45
|
ensureValidNsid(h)
|
|
43
|
-
|
|
46
|
+
expect(parseNsid(h)).toEqual(h.split('.'))
|
|
47
|
+
expect(validateNsidRegex(h)).toMatchObject({
|
|
48
|
+
success: true,
|
|
49
|
+
value: expect.any(String),
|
|
50
|
+
})
|
|
51
|
+
expect(validateNsid(h)).toMatchObject({
|
|
52
|
+
success: true,
|
|
53
|
+
value: expect.any(Array),
|
|
54
|
+
})
|
|
44
55
|
}
|
|
45
56
|
const expectInvalid = (h: string) => {
|
|
57
|
+
expect(isValidNsid(h)).toBe(false)
|
|
58
|
+
expect(() => parseNsid(h)).toThrow(InvalidNsidError)
|
|
46
59
|
expect(() => ensureValidNsid(h)).toThrow(InvalidNsidError)
|
|
47
|
-
expect((
|
|
60
|
+
expect(validateNsidRegex(h)).toMatchObject({
|
|
61
|
+
success: false,
|
|
62
|
+
message: expect.any(String),
|
|
63
|
+
})
|
|
64
|
+
expect(validateNsid(h)).toMatchObject({
|
|
65
|
+
success: false,
|
|
66
|
+
message: expect.any(String),
|
|
67
|
+
})
|
|
48
68
|
}
|
|
49
69
|
|
|
50
70
|
it('enforces spec details', () => {
|
|
@@ -68,91 +88,111 @@ describe('NSID validation', () => {
|
|
|
68
88
|
const tooLongOverall = 'com.' + 'middle.'.repeat(50) + 'foo'
|
|
69
89
|
expect(tooLongOverall.length).toBe(357)
|
|
70
90
|
expectInvalid(tooLongOverall)
|
|
71
|
-
|
|
72
|
-
expectValid('com.example.fooBar')
|
|
73
|
-
expectValid('net.users.bob.ping')
|
|
74
|
-
expectValid('a.b.c')
|
|
75
|
-
expectValid('m.xn--masekowski-d0b.pl')
|
|
76
|
-
expectValid('one.two.three')
|
|
77
|
-
expectValid('one.two.three.four-and.FiVe')
|
|
78
|
-
expectValid('one.2.three')
|
|
79
|
-
expectValid('a-0.b-1.c')
|
|
80
|
-
expectValid('a0.b1.cc')
|
|
81
|
-
expectValid('cn.8.lex.stuff')
|
|
82
|
-
expectValid('test.12345.record')
|
|
83
|
-
expectValid('a01.thing.record')
|
|
84
|
-
expectValid('a.0.c')
|
|
85
|
-
expectValid('xn--fiqs8s.xn--fiqa61au8b7zsevnm8ak20mc4a87e.record.two')
|
|
86
|
-
expectValid('a0.b1.c3')
|
|
87
|
-
expectValid('com.example.f00')
|
|
88
|
-
|
|
89
|
-
expectInvalid('com.example.foo.*')
|
|
90
|
-
expectInvalid('com.example.foo.blah*')
|
|
91
|
-
expectInvalid('com.example.foo.*blah')
|
|
92
|
-
expectInvalid('com.exa💩ple.thing')
|
|
93
|
-
expectInvalid('a-0.b-1.c-3')
|
|
94
|
-
expectInvalid('a-0.b-1.c-o')
|
|
95
|
-
expectInvalid('1.0.0.127.record')
|
|
96
|
-
expectInvalid('0two.example.foo')
|
|
97
|
-
expectInvalid('example.com')
|
|
98
|
-
expectInvalid('com.example')
|
|
99
|
-
expectInvalid('a.')
|
|
100
|
-
expectInvalid('.one.two.three')
|
|
101
|
-
expectInvalid('one.two.three ')
|
|
102
|
-
expectInvalid('one.two..three')
|
|
103
|
-
expectInvalid('one .two.three')
|
|
104
|
-
expectInvalid(' one.two.three')
|
|
105
|
-
expectInvalid('com.exa💩ple.thing')
|
|
106
|
-
expectInvalid('com.atproto.feed.p@st')
|
|
107
|
-
expectInvalid('com.atproto.feed.p_st')
|
|
108
|
-
expectInvalid('com.atproto.feed.p*st')
|
|
109
|
-
expectInvalid('com.atproto.feed.po#t')
|
|
110
|
-
expectInvalid('com.atproto.feed.p!ot')
|
|
111
|
-
expectInvalid('com.example-.foo')
|
|
112
91
|
})
|
|
113
92
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
93
|
+
describe('valid NSIDs', () => {
|
|
94
|
+
for (const validNsid of [
|
|
95
|
+
'com.example.foo',
|
|
96
|
+
'o'.repeat(63) + '.foo.bar',
|
|
97
|
+
'com.' + 'o'.repeat(63) + '.foo',
|
|
98
|
+
'com.example.' + 'o'.repeat(63),
|
|
99
|
+
'com.' + 'middle.'.repeat(40) + 'foo',
|
|
100
|
+
|
|
101
|
+
'a-0.b-1.c',
|
|
102
|
+
'a.0.c',
|
|
103
|
+
'a.b.c',
|
|
104
|
+
'a0.b1.c3',
|
|
105
|
+
'a0.b1.cc',
|
|
106
|
+
'a01.thing.record',
|
|
107
|
+
'cn.8.lex.stuff',
|
|
108
|
+
'com.example.f00',
|
|
109
|
+
'com.example.fooBar',
|
|
110
|
+
'm.xn--masekowski-d0b.pl',
|
|
111
|
+
'net.users.bob.ping',
|
|
112
|
+
'one.2.three',
|
|
113
|
+
'one.two.three',
|
|
114
|
+
'one.two.three.four-and.FiVe',
|
|
115
|
+
'onion.2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.lex.deleteThing',
|
|
116
|
+
'onion.expyuzz4wqqyqhjn.spec.getThing',
|
|
117
117
|
'onion.g2zyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.lex.deleteThing',
|
|
118
|
-
|
|
118
|
+
'org.4chan.lex.getThing',
|
|
119
|
+
'test.12345.record',
|
|
120
|
+
'xn--fiqs8s.xn--fiqa61au8b7zsevnm8ak20mc4a87e.record.two',
|
|
121
|
+
]) {
|
|
122
|
+
it(validNsid, () => {
|
|
123
|
+
expectValid(validNsid)
|
|
124
|
+
})
|
|
125
|
+
}
|
|
119
126
|
})
|
|
120
127
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
'
|
|
126
|
-
|
|
128
|
+
describe('invalid NSIDs', () => {
|
|
129
|
+
for (const invalidNsid of [
|
|
130
|
+
'o'.repeat(64) + '.foo.bar',
|
|
131
|
+
'com.' + 'o'.repeat(64) + '.foo',
|
|
132
|
+
'com.example.' + 'o'.repeat(64),
|
|
133
|
+
'com.' + 'middle.'.repeat(50) + 'foo',
|
|
134
|
+
'com.example.foo.*',
|
|
135
|
+
'com.example.foo.blah*',
|
|
136
|
+
'com.example.foo.*blah',
|
|
137
|
+
'com.exa💩ple.thing',
|
|
138
|
+
'a-0.b-1.c-3',
|
|
139
|
+
'a-0.b-1.c-o',
|
|
140
|
+
'1.0.0.127.record',
|
|
141
|
+
'0two.example.foo',
|
|
142
|
+
'example.com',
|
|
143
|
+
'com.example',
|
|
144
|
+
'a.',
|
|
145
|
+
'.one.two.three',
|
|
146
|
+
'one.two.three ',
|
|
147
|
+
'one.two..three',
|
|
148
|
+
'one .two.three',
|
|
149
|
+
' one.two.three',
|
|
150
|
+
'com.atproto.feed.p@st',
|
|
151
|
+
'com.atproto.feed.p_st',
|
|
152
|
+
'com.atproto.feed.p*st',
|
|
153
|
+
'com.atproto.feed.po#t',
|
|
154
|
+
'com.atproto.feed.p!ot',
|
|
155
|
+
'com.example-.foo',
|
|
156
|
+
'com.-example.foo',
|
|
157
|
+
'com.example.0foo',
|
|
158
|
+
'com.example.f-o',
|
|
159
|
+
]) {
|
|
160
|
+
it(invalidNsid, () => {
|
|
161
|
+
expect(validateNsid(invalidNsid)).toMatchObject({
|
|
162
|
+
success: false,
|
|
163
|
+
message: expect.any(String),
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
}
|
|
127
167
|
})
|
|
128
168
|
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
)
|
|
134
|
-
terminal: false,
|
|
135
|
-
})
|
|
136
|
-
lineReader.on('line', (line) => {
|
|
169
|
+
describe('conforms to interop valid NSIDs', () => {
|
|
170
|
+
for (const line of fs
|
|
171
|
+
.readFileSync(`${__dirname}/interop-files/nsid_syntax_valid.txt`)
|
|
172
|
+
.toString()
|
|
173
|
+
.split('\n')) {
|
|
137
174
|
if (line.startsWith('#') || line.length === 0) {
|
|
138
|
-
|
|
175
|
+
continue
|
|
139
176
|
}
|
|
140
|
-
|
|
141
|
-
|
|
177
|
+
|
|
178
|
+
it(line, () => {
|
|
179
|
+
expectValid(line)
|
|
180
|
+
})
|
|
181
|
+
}
|
|
142
182
|
})
|
|
143
183
|
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
)
|
|
149
|
-
terminal: false,
|
|
150
|
-
})
|
|
151
|
-
lineReader.on('line', (line) => {
|
|
184
|
+
describe('conforms to interop invalid NSIDs', () => {
|
|
185
|
+
for (const line of fs
|
|
186
|
+
.readFileSync(`${__dirname}/interop-files/nsid_syntax_invalid.txt`)
|
|
187
|
+
.toString()
|
|
188
|
+
.split('\n')) {
|
|
152
189
|
if (line.startsWith('#') || line.length === 0) {
|
|
153
|
-
|
|
190
|
+
continue
|
|
154
191
|
}
|
|
155
|
-
|
|
156
|
-
|
|
192
|
+
|
|
193
|
+
it(line, () => {
|
|
194
|
+
expectInvalid(line)
|
|
195
|
+
})
|
|
196
|
+
}
|
|
157
197
|
})
|
|
158
198
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./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.
|
|
1
|
+
{"root":["./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 +1 @@
|
|
|
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.
|
|
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"}
|