@atproto/syntax 0.4.0 → 0.4.2
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 +14 -0
- package/LICENSE.txt +1 -1
- package/benchmark.js +208 -0
- package/dist/at-identifier.d.ts +5 -0
- package/dist/at-identifier.d.ts.map +1 -0
- package/dist/at-identifier.js +19 -0
- package/dist/at-identifier.js.map +1 -0
- package/dist/aturi.d.ts +8 -6
- package/dist/aturi.d.ts.map +1 -1
- package/dist/aturi.js +36 -45
- package/dist/aturi.js.map +1 -1
- package/dist/aturi_validation.d.ts +5 -2
- package/dist/aturi_validation.d.ts.map +1 -1
- package/dist/aturi_validation.js +54 -66
- package/dist/aturi_validation.js.map +1 -1
- package/dist/datetime.d.ts +11 -4
- package/dist/datetime.d.ts.map +1 -1
- package/dist/datetime.js +16 -16
- package/dist/datetime.js.map +1 -1
- package/dist/did.d.ts +3 -2
- package/dist/did.d.ts.map +1 -1
- package/dist/did.js +8 -8
- package/dist/did.js.map +1 -1
- package/dist/handle.d.ts +7 -6
- package/dist/handle.d.ts.map +1 -1
- package/dist/handle.js +28 -31
- package/dist/handle.js.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/nsid.d.ts +30 -5
- 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 +3 -2
- package/dist/recordkey.d.ts.map +1 -1
- package/dist/recordkey.js +33 -22
- package/dist/recordkey.js.map +1 -1
- package/dist/tid.d.ts +3 -2
- package/dist/tid.d.ts.map +1 -1
- package/dist/tid.js +7 -7
- package/dist/tid.js.map +1 -1
- package/package.json +1 -1
- package/src/at-identifier.ts +22 -0
- package/src/aturi.ts +59 -46
- package/src/aturi_validation.ts +62 -57
- package/src/datetime.ts +17 -4
- package/src/did.ts +5 -2
- package/src/handle.ts +23 -22
- package/src/index.ts +8 -7
- package/src/nsid.ts +146 -47
- package/src/recordkey.ts +40 -17
- package/src/tid.ts +4 -2
- package/tests/nsid.test.ts +117 -77
- package/tsconfig.build.json +1 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @atproto/syntax
|
|
2
2
|
|
|
3
|
+
## 0.4.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#4389](https://github.com/bluesky-social/atproto/pull/4389) [`bcae2b7`](https://github.com/bluesky-social/atproto/commit/bcae2b77b68da6dc2ec202651c8bf41fd5769f69) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Improve typing of various string formats
|
|
8
|
+
|
|
9
|
+
## 0.4.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#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
|
|
14
|
+
|
|
15
|
+
- [#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
|
|
16
|
+
|
|
3
17
|
## 0.4.0
|
|
4
18
|
|
|
5
19
|
### 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
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { DidString } from './did.js';
|
|
2
|
+
import { HandleString } from './handle.js';
|
|
3
|
+
export type AtIdentifierString = DidString | HandleString;
|
|
4
|
+
export declare function ensureValidAtIdentifier(input: string): asserts input is AtIdentifierString;
|
|
5
|
+
//# sourceMappingURL=at-identifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"at-identifier.d.ts","sourceRoot":"","sources":["../src/at-identifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAuB,MAAM,UAAU,CAAA;AACzD,OAAO,EACL,YAAY,EAGb,MAAM,aAAa,CAAA;AAEpB,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,YAAY,CAAA;AAEzD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,KAAK,IAAI,kBAAkB,CAUrC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureValidAtIdentifier = ensureValidAtIdentifier;
|
|
4
|
+
const did_js_1 = require("./did.js");
|
|
5
|
+
const handle_js_1 = require("./handle.js");
|
|
6
|
+
function ensureValidAtIdentifier(input) {
|
|
7
|
+
try {
|
|
8
|
+
if (input.startsWith('did:')) {
|
|
9
|
+
(0, did_js_1.ensureValidDidRegex)(input);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
(0, handle_js_1.ensureValidHandleRegex)(input);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
catch (cause) {
|
|
16
|
+
throw new handle_js_1.InvalidHandleError('Invalid DID or handle', { cause });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=at-identifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"at-identifier.js","sourceRoot":"","sources":["../src/at-identifier.ts"],"names":[],"mappings":";;AASA,0DAYC;AArBD,qCAAyD;AACzD,2CAIoB;AAIpB,SAAgB,uBAAuB,CACrC,KAAa;IAEb,IAAI,CAAC;QACH,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,IAAA,4BAAmB,EAAC,KAAK,CAAC,CAAA;QAC5B,CAAC;aAAM,CAAC;YACN,IAAA,kCAAsB,EAAC,KAAK,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,8BAAkB,CAAC,uBAAuB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IAClE,CAAC;AACH,CAAC","sourcesContent":["import { DidString, ensureValidDidRegex } from './did.js'\nimport {\n HandleString,\n InvalidHandleError,\n ensureValidHandleRegex,\n} from './handle.js'\n\nexport type AtIdentifierString = DidString | HandleString\n\nexport function ensureValidAtIdentifier(\n input: string,\n): asserts input is AtIdentifierString {\n try {\n if (input.startsWith('did:')) {\n ensureValidDidRegex(input)\n } else {\n ensureValidHandleRegex(input)\n }\n } catch (cause) {\n throw new InvalidHandleError('Invalid DID or handle', { cause })\n }\n}\n"]}
|
package/dist/aturi.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import { AtIdentifierString } from './at-identifier.js';
|
|
2
|
+
import { AtUriString } from './aturi_validation.js';
|
|
3
|
+
export * from './aturi_validation.js';
|
|
2
4
|
export declare const ATP_URI_REGEX: RegExp;
|
|
3
5
|
export declare class AtUri {
|
|
4
6
|
hash: string;
|
|
5
|
-
host:
|
|
7
|
+
host: AtIdentifierString;
|
|
6
8
|
pathname: string;
|
|
7
9
|
searchParams: URLSearchParams;
|
|
8
|
-
constructor(uri: string, base?: string);
|
|
10
|
+
constructor(uri: string, base?: string | AtUri);
|
|
9
11
|
static make(handleOrDid: string, collection?: string, rkey?: string): AtUri;
|
|
10
12
|
get protocol(): string;
|
|
11
|
-
get origin(): string
|
|
13
|
+
get origin(): `at://did:${string}:${string}` | `at://${string}.${string}`;
|
|
12
14
|
get hostname(): string;
|
|
13
15
|
set hostname(v: string);
|
|
14
16
|
get search(): string;
|
|
@@ -17,7 +19,7 @@ export declare class AtUri {
|
|
|
17
19
|
set collection(v: string);
|
|
18
20
|
get rkey(): string;
|
|
19
21
|
set rkey(v: string);
|
|
20
|
-
get href():
|
|
21
|
-
toString():
|
|
22
|
+
get href(): AtUriString;
|
|
23
|
+
toString(): AtUriString;
|
|
22
24
|
}
|
|
23
25
|
//# sourceMappingURL=aturi.d.ts.map
|
package/dist/aturi.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aturi.d.ts","sourceRoot":"","sources":["../src/aturi.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"aturi.d.ts","sourceRoot":"","sources":["../src/aturi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA2B,MAAM,oBAAoB,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGnD,cAAc,uBAAuB,CAAA;AAErC,eAAO,MAAM,aAAa,QAEyE,CAAA;AAInG,qBAAa,KAAK;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,kBAAkB,CAAA;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,eAAe,CAAA;gBAEjB,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK;IAgB9C,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;IAOnE,IAAI,QAAQ,WAEX;IAED,IAAI,MAAM,gEAET;IAED,IAAI,QAAQ,IAII,MAAM,CAFrB;IAED,IAAI,QAAQ,CAAC,CAAC,EAAE,MAAM,EAGrB;IAED,IAAI,MAAM,IAII,MAAM,CAFnB;IAED,IAAI,MAAM,CAAC,CAAC,EAAE,MAAM,EAEnB;IAED,IAAI,UAAU,IAII,MAAM,CAFvB;IAED,IAAI,UAAU,CAAC,CAAC,EAAE,MAAM,EAKvB;IAED,IAAI,IAAI,IAII,MAAM,CAFjB;IAED,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,EAKjB;IAED,IAAI,IAAI,gBAEP;IAED,QAAQ,IAAI,WAAW;CAexB"}
|
package/dist/aturi.js
CHANGED
|
@@ -15,7 +15,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.AtUri = exports.ATP_URI_REGEX = void 0;
|
|
18
|
-
|
|
18
|
+
const at_identifier_js_1 = require("./at-identifier.js");
|
|
19
|
+
const nsid_js_1 = require("./nsid.js");
|
|
20
|
+
__exportStar(require("./aturi_validation.js"), exports);
|
|
19
21
|
exports.ATP_URI_REGEX =
|
|
20
22
|
// proto- --did-------------- --name---------------- --path---- --query-- --hash--
|
|
21
23
|
/^(at:\/\/)?((?:did:[a-z0-9:%-]+)|(?:[a-z0-9][a-z0-9.:-]*))(\/[^?#\s]*)?(\?[^#\s]+)?(#[^\s]+)?$/i;
|
|
@@ -47,27 +49,15 @@ class AtUri {
|
|
|
47
49
|
writable: true,
|
|
48
50
|
value: void 0
|
|
49
51
|
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (!relativep) {
|
|
58
|
-
throw new Error(`Invalid path: ${uri}`);
|
|
59
|
-
}
|
|
60
|
-
Object.assign(parsed, relativep);
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
parsed = parse(uri);
|
|
64
|
-
if (!parsed) {
|
|
65
|
-
throw new Error(`Invalid at uri: ${uri}`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
this.hash = parsed.hash;
|
|
52
|
+
const parsed = base !== undefined
|
|
53
|
+
? typeof base === 'string'
|
|
54
|
+
? Object.assign(parse(base), parseRelative(uri))
|
|
55
|
+
: Object.assign({ host: base.host }, parseRelative(uri))
|
|
56
|
+
: parse(uri);
|
|
57
|
+
(0, at_identifier_js_1.ensureValidAtIdentifier)(parsed.host);
|
|
58
|
+
this.hash = parsed.hash ?? '';
|
|
69
59
|
this.host = parsed.host;
|
|
70
|
-
this.pathname = parsed.pathname;
|
|
60
|
+
this.pathname = parsed.pathname ?? '';
|
|
71
61
|
this.searchParams = parsed.searchParams;
|
|
72
62
|
}
|
|
73
63
|
static make(handleOrDid, collection, rkey) {
|
|
@@ -88,6 +78,7 @@ class AtUri {
|
|
|
88
78
|
return this.host;
|
|
89
79
|
}
|
|
90
80
|
set hostname(v) {
|
|
81
|
+
(0, at_identifier_js_1.ensureValidAtIdentifier)(v);
|
|
91
82
|
this.host = v;
|
|
92
83
|
}
|
|
93
84
|
get search() {
|
|
@@ -100,6 +91,7 @@ class AtUri {
|
|
|
100
91
|
return this.pathname.split('/').filter(Boolean)[0] || '';
|
|
101
92
|
}
|
|
102
93
|
set collection(v) {
|
|
94
|
+
(0, nsid_js_1.ensureValidNsid)(v);
|
|
103
95
|
const parts = this.pathname.split('/').filter(Boolean);
|
|
104
96
|
parts[0] = v;
|
|
105
97
|
this.pathname = parts.join('/');
|
|
@@ -109,8 +101,7 @@ class AtUri {
|
|
|
109
101
|
}
|
|
110
102
|
set rkey(v) {
|
|
111
103
|
const parts = this.pathname.split('/').filter(Boolean);
|
|
112
|
-
|
|
113
|
-
parts[0] = 'undefined';
|
|
104
|
+
parts[0] || (parts[0] = 'undefined');
|
|
114
105
|
parts[1] = v;
|
|
115
106
|
this.pathname = parts.join('/');
|
|
116
107
|
}
|
|
@@ -122,9 +113,9 @@ class AtUri {
|
|
|
122
113
|
if (!path.startsWith('/')) {
|
|
123
114
|
path = `/${path}`;
|
|
124
115
|
}
|
|
125
|
-
let qs =
|
|
126
|
-
if (
|
|
127
|
-
qs = `?${
|
|
116
|
+
let qs = '';
|
|
117
|
+
if (this.searchParams.size) {
|
|
118
|
+
qs = `?${this.searchParams.toString()}`;
|
|
128
119
|
}
|
|
129
120
|
let hash = this.hash;
|
|
130
121
|
if (hash && !hash.startsWith('#')) {
|
|
@@ -135,26 +126,26 @@ class AtUri {
|
|
|
135
126
|
}
|
|
136
127
|
exports.AtUri = AtUri;
|
|
137
128
|
function parse(str) {
|
|
138
|
-
const match = exports.ATP_URI_REGEX
|
|
139
|
-
if (match) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
129
|
+
const match = str.match(exports.ATP_URI_REGEX);
|
|
130
|
+
if (!match) {
|
|
131
|
+
throw new Error(`Invalid AT uri: ${str}`);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
host: match[2],
|
|
135
|
+
hash: match[5],
|
|
136
|
+
pathname: match[3],
|
|
137
|
+
searchParams: new URLSearchParams(match[4]),
|
|
138
|
+
};
|
|
148
139
|
}
|
|
149
140
|
function parseRelative(str) {
|
|
150
|
-
const match =
|
|
151
|
-
if (match) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
141
|
+
const match = str.match(RELATIVE_REGEX);
|
|
142
|
+
if (!match) {
|
|
143
|
+
throw new Error(`Invalid path: ${str}`);
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
hash: match[3],
|
|
147
|
+
pathname: match[1],
|
|
148
|
+
searchParams: new URLSearchParams(match[2]),
|
|
149
|
+
};
|
|
159
150
|
}
|
|
160
151
|
//# sourceMappingURL=aturi.js.map
|
package/dist/aturi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aturi.js","sourceRoot":"","sources":["../src/aturi.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"aturi.js","sourceRoot":"","sources":["../src/aturi.ts"],"names":[],"mappings":"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","sourcesContent":["import { AtIdentifierString, ensureValidAtIdentifier } from './at-identifier.js'\nimport { AtUriString } from './aturi_validation.js'\nimport { ensureValidNsid } from './nsid.js'\n\nexport * from './aturi_validation.js'\n\nexport const ATP_URI_REGEX =\n // proto- --did-------------- --name---------------- --path---- --query-- --hash--\n /^(at:\\/\\/)?((?:did:[a-z0-9:%-]+)|(?:[a-z0-9][a-z0-9.:-]*))(\\/[^?#\\s]*)?(\\?[^#\\s]+)?(#[^\\s]+)?$/i\n// --path----- --query-- --hash--\nconst RELATIVE_REGEX = /^(\\/[^?#\\s]*)?(\\?[^#\\s]+)?(#[^\\s]+)?$/i\n\nexport class AtUri {\n hash: string\n host: AtIdentifierString\n pathname: string\n searchParams: URLSearchParams\n\n constructor(uri: string, base?: string | AtUri) {\n const parsed =\n base !== undefined\n ? typeof base === 'string'\n ? Object.assign(parse(base), parseRelative(uri))\n : Object.assign({ host: base.host }, parseRelative(uri))\n : parse(uri)\n\n ensureValidAtIdentifier(parsed.host)\n\n this.hash = parsed.hash ?? ''\n this.host = parsed.host\n this.pathname = parsed.pathname ?? ''\n this.searchParams = parsed.searchParams\n }\n\n static make(handleOrDid: string, collection?: string, rkey?: string) {\n let str = handleOrDid\n if (collection) str += '/' + collection\n if (rkey) str += '/' + rkey\n return new AtUri(str)\n }\n\n get protocol() {\n return 'at:'\n }\n\n get origin() {\n return `at://${this.host}` as const\n }\n\n get hostname() {\n return this.host\n }\n\n set hostname(v: string) {\n ensureValidAtIdentifier(v)\n this.host = v\n }\n\n get search() {\n return this.searchParams.toString()\n }\n\n set search(v: string) {\n this.searchParams = new URLSearchParams(v)\n }\n\n get collection() {\n return this.pathname.split('/').filter(Boolean)[0] || ''\n }\n\n set collection(v: string) {\n ensureValidNsid(v)\n const parts = this.pathname.split('/').filter(Boolean)\n parts[0] = v\n this.pathname = parts.join('/')\n }\n\n get rkey() {\n return this.pathname.split('/').filter(Boolean)[1] || ''\n }\n\n set rkey(v: string) {\n const parts = this.pathname.split('/').filter(Boolean)\n parts[0] ||= 'undefined'\n parts[1] = v\n this.pathname = parts.join('/')\n }\n\n get href() {\n return this.toString()\n }\n\n toString(): AtUriString {\n let path = this.pathname || '/'\n if (!path.startsWith('/')) {\n path = `/${path}`\n }\n let qs = ''\n if (this.searchParams.size) {\n qs = `?${this.searchParams.toString()}`\n }\n let hash = this.hash\n if (hash && !hash.startsWith('#')) {\n hash = `#${hash}`\n }\n return `at://${this.host}${path}${qs}${hash}` as AtUriString\n }\n}\n\nfunction parse(str: string) {\n const match = str.match(ATP_URI_REGEX) as null | {\n 0: string\n 1: string | undefined // proto\n 2: string // host\n 3: string | undefined // path\n 4: string | undefined // query\n 5: string | undefined // hash\n }\n\n if (!match) {\n throw new Error(`Invalid AT uri: ${str}`)\n }\n\n return {\n host: match[2],\n hash: match[5],\n pathname: match[3],\n searchParams: new URLSearchParams(match[4]),\n }\n}\n\nfunction parseRelative(str: string) {\n const match = str.match(RELATIVE_REGEX) as null | {\n 0: string\n 1: string | undefined // path\n 2: string | undefined // query\n 3: string | undefined // hash\n }\n\n if (!match) {\n throw new Error(`Invalid path: ${str}`)\n }\n\n return {\n hash: match[3],\n pathname: match[1],\n searchParams: new URLSearchParams(match[2]),\n }\n}\n"]}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { AtIdentifierString } from './at-identifier.js';
|
|
2
|
+
import { NsidString } from './nsid.js';
|
|
3
|
+
export type AtUriString = `at://${AtIdentifierString}` | `at://${AtIdentifierString}/${NsidString}` | `at://${AtIdentifierString}/${NsidString}/${string}`;
|
|
4
|
+
export declare function ensureValidAtUri(input: string): asserts input is AtUriString;
|
|
5
|
+
export declare function ensureValidAtUriRegex(uri: string): asserts uri is AtUriString;
|
|
3
6
|
//# sourceMappingURL=aturi_validation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aturi_validation.d.ts","sourceRoot":"","sources":["../src/aturi_validation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"aturi_validation.d.ts","sourceRoot":"","sources":["../src/aturi_validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA2B,MAAM,oBAAoB,CAAA;AAGhF,OAAO,EAAE,UAAU,EAAe,MAAM,WAAW,CAAA;AAEnD,MAAM,MAAM,WAAW,GACnB,QAAQ,kBAAkB,EAAE,GAC5B,QAAQ,kBAAkB,IAAI,UAAU,EAAE,GAC1C,QAAQ,kBAAkB,IAAI,UAAU,IAAI,MAAM,EAAE,CAAA;AAgBxD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,WAAW,CAiF5E;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI,WAAW,CA4B7E"}
|