@atproto/did 0.5.2 → 0.5.3
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 +10 -0
- package/dist/did-error.d.ts.map +1 -1
- package/dist/did-error.js +5 -4
- package/dist/did-error.js.map +1 -1
- package/dist/lib/number.d.ts +2 -0
- package/dist/lib/number.d.ts.map +1 -0
- package/dist/lib/number.js +2 -0
- package/dist/lib/number.js.map +1 -0
- package/package.json +8 -4
- package/jest.config.cjs +0 -22
- package/src/atproto.ts +0 -235
- package/src/did-document.ts +0 -159
- package/src/did-error.ts +0 -49
- package/src/did-ref.ts +0 -37
- package/src/did.ts +0 -265
- package/src/index.ts +0 -7
- package/src/lib/uri.ts +0 -78
- package/src/methods/plc.ts +0 -54
- package/src/methods/web.ts +0 -83
- package/src/methods.ts +0 -2
- package/src/utils.ts +0 -18
- package/tests/did-ref.test.ts +0 -69
- package/tests/methods/plc.test.ts +0 -72
- package/tests/methods/web.test.ts +0 -109
- package/tsconfig.build.json +0 -8
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -4
package/src/did.ts
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import { DidError, InvalidDidError } from './did-error.js'
|
|
3
|
-
|
|
4
|
-
const DID_PREFIX = 'did:'
|
|
5
|
-
const DID_PREFIX_LENGTH = DID_PREFIX.length
|
|
6
|
-
export { DID_PREFIX }
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Type representation of a Did, with method.
|
|
10
|
-
*
|
|
11
|
-
* ```bnf
|
|
12
|
-
* did = "did:" method-name ":" method-specific-id
|
|
13
|
-
* method-name = 1*method-char
|
|
14
|
-
* method-char = %x61-7A / DIGIT
|
|
15
|
-
* method-specific-id = *( *idchar ":" ) 1*idchar
|
|
16
|
-
* idchar = ALPHA / DIGIT / "." / "-" / "_" / pct-encoded
|
|
17
|
-
* pct-encoded = "%" HEXDIG HEXDIG
|
|
18
|
-
* ```
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```ts
|
|
22
|
-
* type DidWeb = Did<'web'> // `did:web:${string}`
|
|
23
|
-
* type DidCustom = Did<'web' | 'plc'> // `did:${'web' | 'plc'}:${string}`
|
|
24
|
-
* type DidNever = Did<' invalid 🥴 '> // never
|
|
25
|
-
* type DidFoo = Did<'foo' | ' invalid 🥴 '> // `did:foo:${string}`
|
|
26
|
-
* ```
|
|
27
|
-
*
|
|
28
|
-
* @see {@link https://www.w3.org/TR/did-core/#did-syntax}
|
|
29
|
-
*/
|
|
30
|
-
export type Did<M extends string = string> = `did:${AsDidMethod<M>}:${string}`
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* DID Method
|
|
34
|
-
*/
|
|
35
|
-
export type AsDidMethod<M> = string extends M
|
|
36
|
-
? string // can't know...
|
|
37
|
-
: AsDidMethodInternal<M, ''>
|
|
38
|
-
|
|
39
|
-
type AlphanumericChar = DigitChar | LowerAlphaChar
|
|
40
|
-
type DigitChar = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
|
|
41
|
-
type LowerAlphaChar =
|
|
42
|
-
| 'a'
|
|
43
|
-
| 'b'
|
|
44
|
-
| 'c'
|
|
45
|
-
| 'd'
|
|
46
|
-
| 'e'
|
|
47
|
-
| 'f'
|
|
48
|
-
| 'g'
|
|
49
|
-
| 'h'
|
|
50
|
-
| 'i'
|
|
51
|
-
| 'j'
|
|
52
|
-
| 'k'
|
|
53
|
-
| 'l'
|
|
54
|
-
| 'm'
|
|
55
|
-
| 'n'
|
|
56
|
-
| 'o'
|
|
57
|
-
| 'p'
|
|
58
|
-
| 'q'
|
|
59
|
-
| 'r'
|
|
60
|
-
| 's'
|
|
61
|
-
| 't'
|
|
62
|
-
| 'u'
|
|
63
|
-
| 'v'
|
|
64
|
-
| 'w'
|
|
65
|
-
| 'x'
|
|
66
|
-
| 'y'
|
|
67
|
-
| 'z'
|
|
68
|
-
|
|
69
|
-
type AsDidMethodInternal<
|
|
70
|
-
S,
|
|
71
|
-
Acc extends string,
|
|
72
|
-
> = S extends `${infer H}${infer T}`
|
|
73
|
-
? H extends AlphanumericChar
|
|
74
|
-
? AsDidMethodInternal<T, `${Acc}${H}`>
|
|
75
|
-
: never
|
|
76
|
-
: Acc extends ''
|
|
77
|
-
? never
|
|
78
|
-
: Acc
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* DID Method-name check function.
|
|
82
|
-
*
|
|
83
|
-
* Check if the input is a valid DID method name, at the position between
|
|
84
|
-
* `start` (inclusive) and `end` (exclusive).
|
|
85
|
-
*/
|
|
86
|
-
export function assertDidMethod(
|
|
87
|
-
input: string,
|
|
88
|
-
start = 0,
|
|
89
|
-
end = input.length,
|
|
90
|
-
): void {
|
|
91
|
-
if (
|
|
92
|
-
!Number.isFinite(end) ||
|
|
93
|
-
!Number.isFinite(start) ||
|
|
94
|
-
end < start ||
|
|
95
|
-
end > input.length
|
|
96
|
-
) {
|
|
97
|
-
throw new TypeError('Invalid start or end position')
|
|
98
|
-
}
|
|
99
|
-
if (end === start) {
|
|
100
|
-
throw new InvalidDidError(input, `Empty method name`)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
let c: number
|
|
104
|
-
for (let i = start; i < end; i++) {
|
|
105
|
-
c = input.charCodeAt(i)
|
|
106
|
-
if (
|
|
107
|
-
(c < 0x61 || c > 0x7a) && // a-z
|
|
108
|
-
(c < 0x30 || c > 0x39) // 0-9
|
|
109
|
-
) {
|
|
110
|
-
throw new InvalidDidError(
|
|
111
|
-
input,
|
|
112
|
-
`Invalid character at position ${i} in DID method name`,
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* This method assumes the input is a valid Did
|
|
120
|
-
*/
|
|
121
|
-
export function extractDidMethod<D extends Did>(did: D) {
|
|
122
|
-
const msidSep = did.indexOf(':', DID_PREFIX_LENGTH)
|
|
123
|
-
const method = did.slice(DID_PREFIX_LENGTH, msidSep)
|
|
124
|
-
return method as D extends Did<infer M> ? M : string
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* DID Method-specific identifier check function.
|
|
129
|
-
*
|
|
130
|
-
* Check if the input is a valid DID method-specific identifier, at the position
|
|
131
|
-
* between `start` (inclusive) and `end` (exclusive).
|
|
132
|
-
*/
|
|
133
|
-
export function assertDidMsid(
|
|
134
|
-
input: string,
|
|
135
|
-
start = 0,
|
|
136
|
-
end = input.length,
|
|
137
|
-
): void {
|
|
138
|
-
if (
|
|
139
|
-
!Number.isFinite(end) ||
|
|
140
|
-
!Number.isFinite(start) ||
|
|
141
|
-
end < start ||
|
|
142
|
-
end > input.length
|
|
143
|
-
) {
|
|
144
|
-
throw new TypeError('Invalid start or end position')
|
|
145
|
-
}
|
|
146
|
-
if (end === start) {
|
|
147
|
-
throw new InvalidDidError(input, `DID method-specific id must not be empty`)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
let c: number
|
|
151
|
-
for (let i = start; i < end; i++) {
|
|
152
|
-
c = input.charCodeAt(i)
|
|
153
|
-
|
|
154
|
-
// Check for frequent chars first
|
|
155
|
-
if (
|
|
156
|
-
(c < 0x61 || c > 0x7a) && // a-z
|
|
157
|
-
(c < 0x41 || c > 0x5a) && // A-Z
|
|
158
|
-
(c < 0x30 || c > 0x39) && // 0-9
|
|
159
|
-
c !== 0x2e && // .
|
|
160
|
-
c !== 0x2d && // -
|
|
161
|
-
c !== 0x5f // _
|
|
162
|
-
) {
|
|
163
|
-
// Less frequent chars are checked here
|
|
164
|
-
|
|
165
|
-
// ":"
|
|
166
|
-
if (c === 0x3a) {
|
|
167
|
-
if (i === end - 1) {
|
|
168
|
-
throw new InvalidDidError(input, `DID cannot end with ":"`)
|
|
169
|
-
}
|
|
170
|
-
continue
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// pct-encoded
|
|
174
|
-
if (c === 0x25) {
|
|
175
|
-
c = input.charCodeAt(++i)
|
|
176
|
-
if ((c < 0x30 || c > 0x39) && (c < 0x41 || c > 0x46)) {
|
|
177
|
-
throw new InvalidDidError(
|
|
178
|
-
input,
|
|
179
|
-
`Invalid pct-encoded character at position ${i}`,
|
|
180
|
-
)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
c = input.charCodeAt(++i)
|
|
184
|
-
if ((c < 0x30 || c > 0x39) && (c < 0x41 || c > 0x46)) {
|
|
185
|
-
throw new InvalidDidError(
|
|
186
|
-
input,
|
|
187
|
-
`Invalid pct-encoded character at position ${i}`,
|
|
188
|
-
)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// There must always be 2 HEXDIG after a "%"
|
|
192
|
-
if (i >= end) {
|
|
193
|
-
throw new InvalidDidError(
|
|
194
|
-
input,
|
|
195
|
-
`Incomplete pct-encoded character at position ${i - 2}`,
|
|
196
|
-
)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
continue
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
throw new InvalidDidError(
|
|
203
|
-
input,
|
|
204
|
-
`Disallowed character in DID at position ${i}`,
|
|
205
|
-
)
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export function assertDid(input: unknown): asserts input is Did {
|
|
211
|
-
if (typeof input !== 'string') {
|
|
212
|
-
throw new InvalidDidError(typeof input, `DID must be a string`)
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const { length } = input
|
|
216
|
-
if (length > 2048) {
|
|
217
|
-
throw new InvalidDidError(input, `DID is too long (2048 chars max)`)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (!input.startsWith(DID_PREFIX)) {
|
|
221
|
-
throw new InvalidDidError(input, `DID requires "${DID_PREFIX}" prefix`)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const idSep = input.indexOf(':', DID_PREFIX_LENGTH)
|
|
225
|
-
if (idSep === -1) {
|
|
226
|
-
throw new InvalidDidError(input, `Missing colon after method name`)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
assertDidMethod(input, DID_PREFIX_LENGTH, idSep)
|
|
230
|
-
assertDidMsid(input, idSep + 1, length)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export function isDid(input: unknown): input is Did {
|
|
234
|
-
try {
|
|
235
|
-
assertDid(input)
|
|
236
|
-
return true
|
|
237
|
-
} catch (err) {
|
|
238
|
-
if (err instanceof DidError) {
|
|
239
|
-
return false
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Unexpected TypeError (should never happen)
|
|
243
|
-
throw err
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export function asDid<T>(input: T) {
|
|
248
|
-
assertDid(input)
|
|
249
|
-
return input
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export const didSchema = z
|
|
253
|
-
.string()
|
|
254
|
-
.superRefine((value: string, ctx: z.RefinementCtx): value is Did => {
|
|
255
|
-
try {
|
|
256
|
-
assertDid(value)
|
|
257
|
-
return true
|
|
258
|
-
} catch (err) {
|
|
259
|
-
ctx.addIssue({
|
|
260
|
-
code: z.ZodIssueCode.custom,
|
|
261
|
-
message: err instanceof Error ? err.message : 'Unexpected error',
|
|
262
|
-
})
|
|
263
|
-
return false
|
|
264
|
-
}
|
|
265
|
-
})
|
package/src/index.ts
DELETED
package/src/lib/uri.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @see {@link https://www.w3.org/TR/did-1.0/#dfn-did-fragments}
|
|
3
|
-
* @see {@link https://datatracker.ietf.org/doc/html/rfc3986#section-3.5}
|
|
4
|
-
*/
|
|
5
|
-
export function isFragment(
|
|
6
|
-
value: string,
|
|
7
|
-
startIdx = 0,
|
|
8
|
-
endIdx = value.length,
|
|
9
|
-
): boolean {
|
|
10
|
-
let charCode: number
|
|
11
|
-
for (let i = startIdx; i < endIdx; i++) {
|
|
12
|
-
charCode = value.charCodeAt(i)
|
|
13
|
-
|
|
14
|
-
// fragment = *( pchar / "/" / "?" )
|
|
15
|
-
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
|
16
|
-
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
|
17
|
-
// pct-encoded = "%" HEXDIG HEXDIG
|
|
18
|
-
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
|
|
19
|
-
if (
|
|
20
|
-
(charCode >= 65 /* A */ && charCode <= 90) /* Z */ ||
|
|
21
|
-
(charCode >= 97 /* a */ && charCode <= 122) /* z */ ||
|
|
22
|
-
(charCode >= 48 /* 0 */ && charCode <= 57) /* 9 */ ||
|
|
23
|
-
charCode === 45 /* "-" */ ||
|
|
24
|
-
charCode === 46 /* "." */ ||
|
|
25
|
-
charCode === 95 /* "_" */ ||
|
|
26
|
-
charCode === 126 /* "~" */
|
|
27
|
-
) {
|
|
28
|
-
// unreserved
|
|
29
|
-
} else if (
|
|
30
|
-
charCode === 33 /* "!" */ ||
|
|
31
|
-
charCode === 36 /* "$" */ ||
|
|
32
|
-
charCode === 38 /* "&" */ ||
|
|
33
|
-
charCode === 39 /* "'" */ ||
|
|
34
|
-
charCode === 40 /* "(" */ ||
|
|
35
|
-
charCode === 41 /* ")" */ ||
|
|
36
|
-
charCode === 42 /* "*" */ ||
|
|
37
|
-
charCode === 43 /* "+" */ ||
|
|
38
|
-
charCode === 44 /* "," */ ||
|
|
39
|
-
charCode === 59 /* ";" */ ||
|
|
40
|
-
charCode === 61 /* "=" */
|
|
41
|
-
) {
|
|
42
|
-
// sub-delims
|
|
43
|
-
} else if (charCode === 58 /* ":" */ || charCode === 64 /* "@" */) {
|
|
44
|
-
// pchar extra
|
|
45
|
-
} else if (charCode === 47 /* "/" */ || charCode === 63 /* "?" */) {
|
|
46
|
-
// fragment extra
|
|
47
|
-
} else if (charCode === 37 /* "%" */) {
|
|
48
|
-
// pct-enc
|
|
49
|
-
if (i + 2 >= endIdx) return false
|
|
50
|
-
if (!isHexDigit(value.charCodeAt(i + 1))) return false
|
|
51
|
-
if (!isHexDigit(value.charCodeAt(i + 2))) return false
|
|
52
|
-
i += 2
|
|
53
|
-
} else {
|
|
54
|
-
return false
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return true
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function isHexDigit(code: number): boolean {
|
|
62
|
-
return (
|
|
63
|
-
(code >= 48 && code <= 57) || // 0-9
|
|
64
|
-
(code >= 65 && code <= 70) || // A-F
|
|
65
|
-
(code >= 97 && code <= 102) // a-f
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export const canParse =
|
|
70
|
-
URL.canParse?.bind(URL) ??
|
|
71
|
-
((url, base) => {
|
|
72
|
-
try {
|
|
73
|
-
new URL(url, base)
|
|
74
|
-
return true
|
|
75
|
-
} catch {
|
|
76
|
-
return false
|
|
77
|
-
}
|
|
78
|
-
})
|
package/src/methods/plc.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { InvalidDidError } from '../did-error.js'
|
|
2
|
-
import { Did } from '../did.js'
|
|
3
|
-
|
|
4
|
-
const DID_PLC_PREFIX = `did:plc:`
|
|
5
|
-
const DID_PLC_PREFIX_LENGTH = DID_PLC_PREFIX.length
|
|
6
|
-
const DID_PLC_LENGTH = 32
|
|
7
|
-
|
|
8
|
-
export { DID_PLC_PREFIX }
|
|
9
|
-
|
|
10
|
-
export function isDidPlc(input: unknown): input is Did<'plc'> {
|
|
11
|
-
// Optimization: equivalent to try/catch around "assertDidPlc"
|
|
12
|
-
if (typeof input !== 'string') return false
|
|
13
|
-
if (input.length !== DID_PLC_LENGTH) return false
|
|
14
|
-
if (!input.startsWith(DID_PLC_PREFIX)) return false
|
|
15
|
-
for (let i = DID_PLC_PREFIX_LENGTH; i < DID_PLC_LENGTH; i++) {
|
|
16
|
-
if (!isBase32Char(input.charCodeAt(i))) return false
|
|
17
|
-
}
|
|
18
|
-
return true
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function asDidPlc<T>(input: T) {
|
|
22
|
-
assertDidPlc(input)
|
|
23
|
-
return input
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function assertDidPlc(input: unknown): asserts input is Did<'plc'> {
|
|
27
|
-
if (typeof input !== 'string') {
|
|
28
|
-
throw new InvalidDidError(typeof input, `DID must be a string`)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (!input.startsWith(DID_PLC_PREFIX)) {
|
|
32
|
-
throw new InvalidDidError(input, `Invalid did:plc prefix`)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (input.length !== DID_PLC_LENGTH) {
|
|
36
|
-
throw new InvalidDidError(
|
|
37
|
-
input,
|
|
38
|
-
`did:plc must be ${DID_PLC_LENGTH} characters long`,
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// The following check is not necessary, as the check below is more strict:
|
|
43
|
-
|
|
44
|
-
// assertDidMsid(input, DID_PLC_PREFIX.length)
|
|
45
|
-
|
|
46
|
-
for (let i = DID_PLC_PREFIX_LENGTH; i < DID_PLC_LENGTH; i++) {
|
|
47
|
-
if (!isBase32Char(input.charCodeAt(i))) {
|
|
48
|
-
throw new InvalidDidError(input, `Invalid character at position ${i}`)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const isBase32Char = (c: number): boolean =>
|
|
54
|
-
(c >= 0x61 && c <= 0x7a) || (c >= 0x32 && c <= 0x37) // [a-z2-7]
|
package/src/methods/web.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { InvalidDidError } from '../did-error.js'
|
|
2
|
-
import { Did, assertDidMsid } from '../did.js'
|
|
3
|
-
import { canParse } from '../lib/uri.js'
|
|
4
|
-
|
|
5
|
-
export const DID_WEB_PREFIX = `did:web:` satisfies Did<'web'>
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* This function checks if the input is a valid Web DID, as per DID spec.
|
|
9
|
-
*/
|
|
10
|
-
export function isDidWeb(input: unknown): input is Did<'web'> {
|
|
11
|
-
// Optimization: make cheap checks first
|
|
12
|
-
if (typeof input !== 'string') return false
|
|
13
|
-
if (!input.startsWith(DID_WEB_PREFIX)) return false
|
|
14
|
-
if (input.charAt(DID_WEB_PREFIX.length) === ':') return false
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
assertDidMsid(input, DID_WEB_PREFIX.length)
|
|
18
|
-
} catch {
|
|
19
|
-
return false
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return canParse(buildDidWebUrl(input as Did<'web'>))
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function asDidWeb<T>(input: T) {
|
|
26
|
-
assertDidWeb(input)
|
|
27
|
-
return input
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function assertDidWeb(input: unknown): asserts input is Did<'web'> {
|
|
31
|
-
if (typeof input !== 'string') {
|
|
32
|
-
throw new InvalidDidError(typeof input, `DID must be a string`)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (!input.startsWith(DID_WEB_PREFIX)) {
|
|
36
|
-
throw new InvalidDidError(input, `Invalid did:web prefix`)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (input.charAt(DID_WEB_PREFIX.length) === ':') {
|
|
40
|
-
throw new InvalidDidError(input, 'did:web MSID must not start with a colon')
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Make sure every char is valid (per DID spec)
|
|
44
|
-
assertDidMsid(input, DID_WEB_PREFIX.length)
|
|
45
|
-
|
|
46
|
-
if (!canParse(buildDidWebUrl(input as Did<'web'>))) {
|
|
47
|
-
throw new InvalidDidError(input, 'Invalid Web DID')
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function didWebToUrl(did: Did<'web'>) {
|
|
52
|
-
try {
|
|
53
|
-
return new URL(buildDidWebUrl(did)) as URL & {
|
|
54
|
-
protocol: 'http:' | 'https:'
|
|
55
|
-
}
|
|
56
|
-
} catch (cause) {
|
|
57
|
-
throw new InvalidDidError(did, 'Invalid Web DID', cause)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function urlToDidWeb(url: URL): Did<'web'> {
|
|
62
|
-
const port = url.port ? `%3A${url.port}` : ''
|
|
63
|
-
const path = url.pathname === '/' ? '' : url.pathname.replaceAll('/', ':')
|
|
64
|
-
|
|
65
|
-
return `did:web:${url.hostname}${port}${path}`
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function buildDidWebUrl(did: Did<'web'>): string {
|
|
69
|
-
const hostIdx = DID_WEB_PREFIX.length
|
|
70
|
-
const pathIdx = did.indexOf(':', hostIdx)
|
|
71
|
-
|
|
72
|
-
const hostEnc =
|
|
73
|
-
pathIdx === -1 ? did.slice(hostIdx) : did.slice(hostIdx, pathIdx)
|
|
74
|
-
const host = hostEnc.replaceAll('%3A', ':')
|
|
75
|
-
const path = pathIdx === -1 ? '' : did.slice(pathIdx).replaceAll(':', '/')
|
|
76
|
-
const proto =
|
|
77
|
-
host.startsWith('localhost') &&
|
|
78
|
-
(host.length === 9 || host.charCodeAt(9) === 58) /* ':' */
|
|
79
|
-
? 'http'
|
|
80
|
-
: 'https'
|
|
81
|
-
|
|
82
|
-
return `${proto}://${host}${path}`
|
|
83
|
-
}
|
package/src/methods.ts
DELETED
package/src/utils.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export type Identifier<D extends string, I extends string> =
|
|
2
|
-
| `#${I}`
|
|
3
|
-
| `${D}#${I}`
|
|
4
|
-
export function matchesIdentifier<D extends string, I extends string>(
|
|
5
|
-
did: D,
|
|
6
|
-
id: I,
|
|
7
|
-
candidate: string,
|
|
8
|
-
): candidate is Identifier<D, I> {
|
|
9
|
-
// optimized implementation of:
|
|
10
|
-
// return candidate === `#${id}` || candidate === `${did}#${id}`
|
|
11
|
-
|
|
12
|
-
return candidate.charCodeAt(0) === 35 // '#'
|
|
13
|
-
? candidate.length === id.length + 1 && candidate.endsWith(id)
|
|
14
|
-
: candidate.length === id.length + 1 + did.length &&
|
|
15
|
-
candidate.charCodeAt(did.length) === 35 && // '#'
|
|
16
|
-
candidate.startsWith(did) &&
|
|
17
|
-
candidate.endsWith(id)
|
|
18
|
-
}
|
package/tests/did-ref.test.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { isAtprotoDidRefAbsolute } from '../src/atproto.js'
|
|
2
|
-
import { isDidRefRelative } from '../src/did-ref.js'
|
|
3
|
-
|
|
4
|
-
describe('isAtprotoDidRefAbsolute', () => {
|
|
5
|
-
it('accepts well-formed absolute references', () => {
|
|
6
|
-
expect(
|
|
7
|
-
isAtprotoDidRefAbsolute('did:plc:l3rouwludahu3ui3bt66mfvj#atproto'),
|
|
8
|
-
).toBe(true)
|
|
9
|
-
expect(isAtprotoDidRefAbsolute('did:web:example.com#service_id')).toBe(true)
|
|
10
|
-
expect(isAtprotoDidRefAbsolute('did:web:example.com#atproto_label')).toBe(
|
|
11
|
-
true,
|
|
12
|
-
)
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('rejects bare DIDs', () => {
|
|
16
|
-
expect(isAtprotoDidRefAbsolute('did:plc:l3rouwludahu3ui3bt66mfvj')).toBe(
|
|
17
|
-
false,
|
|
18
|
-
)
|
|
19
|
-
expect(isAtprotoDidRefAbsolute('did:web:example.com')).toBe(false)
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it('rejects relative references', () => {
|
|
23
|
-
expect(isAtprotoDidRefAbsolute('#atproto')).toBe(false)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('rejects malformed input', () => {
|
|
27
|
-
expect(isAtprotoDidRefAbsolute('')).toBe(false)
|
|
28
|
-
expect(isAtprotoDidRefAbsolute('did:plc:l3rouwludahu3ui3bt66mfvj#')).toBe(
|
|
29
|
-
false,
|
|
30
|
-
)
|
|
31
|
-
expect(
|
|
32
|
-
isAtprotoDidRefAbsolute('did:plc:l3rouwludahu3ui3bt66mfvj##foo'),
|
|
33
|
-
).toBe(false)
|
|
34
|
-
expect(
|
|
35
|
-
isAtprotoDidRefAbsolute('did:plc:l3rouwludahu3ui3bt66mfvj#a#b'),
|
|
36
|
-
).toBe(false)
|
|
37
|
-
expect(isAtprotoDidRefAbsolute('did:foo:bar#baz')).toBe(false)
|
|
38
|
-
expect(isAtprotoDidRefAbsolute(null)).toBe(false)
|
|
39
|
-
expect(isAtprotoDidRefAbsolute(123)).toBe(false)
|
|
40
|
-
})
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
describe('isDidRefRelative', () => {
|
|
44
|
-
it('accepts well-formed relative references', () => {
|
|
45
|
-
expect(isDidRefRelative('#atproto')).toBe(true)
|
|
46
|
-
expect(isDidRefRelative('#atproto_label')).toBe(true)
|
|
47
|
-
expect(isDidRefRelative('#a')).toBe(true)
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('narrows on a specific id when supplied', () => {
|
|
51
|
-
expect(isDidRefRelative('#atproto', 'atproto')).toBe(true)
|
|
52
|
-
expect(isDidRefRelative('#atproto_label', 'atproto')).toBe(false)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('rejects absolute references and bare strings', () => {
|
|
56
|
-
expect(isDidRefRelative('did:plc:l3rouwludahu3ui3bt66mfvj#atproto')).toBe(
|
|
57
|
-
false,
|
|
58
|
-
)
|
|
59
|
-
expect(isDidRefRelative('atproto')).toBe(false)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('rejects malformed input', () => {
|
|
63
|
-
expect(isDidRefRelative('')).toBe(false)
|
|
64
|
-
expect(isDidRefRelative('#')).toBe(false)
|
|
65
|
-
expect(isDidRefRelative('#a#b')).toBe(false)
|
|
66
|
-
expect(isDidRefRelative(null)).toBe(false)
|
|
67
|
-
expect(isDidRefRelative(123)).toBe(false)
|
|
68
|
-
})
|
|
69
|
-
})
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { InvalidDidError } from '../../src/did-error.js'
|
|
2
|
-
import { Did } from '../../src/did.js'
|
|
3
|
-
import { asDidPlc, assertDidPlc, isDidPlc } from '../../src/methods/plc.js'
|
|
4
|
-
|
|
5
|
-
const VALID: Did<'plc'>[] = [
|
|
6
|
-
'did:plc:l3rouwludahu3ui3bt66mfvj',
|
|
7
|
-
'did:plc:aaaaaaaaaaaaaaaaaaaaaaaa',
|
|
8
|
-
'did:plc:zzzzzzzzzzzzzzzzzzzzzzzz',
|
|
9
|
-
]
|
|
10
|
-
|
|
11
|
-
const INVALID: [value: unknown, message: string][] = [
|
|
12
|
-
['did:plc:l3rouwludahu3ui3bt66mfv0', 'Invalid character at position 31'],
|
|
13
|
-
['did:plc:l3rouwludahu3ui3bt66mfv1', 'Invalid character at position 31'],
|
|
14
|
-
['did:plc:l3rouwludahu3ui3bt66mfv9', 'Invalid character at position 31'],
|
|
15
|
-
['did:plc:l3rouwludahu3ui3bt66mfv', 'did:plc must be 32 characters long'],
|
|
16
|
-
['did:plc:l3rouwludahu3ui3bt66mfvja', 'did:plc must be 32 characters long'],
|
|
17
|
-
['did:plc:example.com:', 'did:plc must be 32 characters long'],
|
|
18
|
-
['did:plc:exam%3Aple.com%3A8080', 'did:plc must be 32 characters long'],
|
|
19
|
-
[3, 'DID must be a string'],
|
|
20
|
-
[{ toString: () => 'did:plc:foo.com' }, 'DID must be a string'],
|
|
21
|
-
[[''], 'DID must be a string'],
|
|
22
|
-
['random-string', 'Invalid did:plc prefix'],
|
|
23
|
-
['did plc', 'Invalid did:plc prefix'],
|
|
24
|
-
['lorem ipsum dolor sit', 'Invalid did:plc prefix'],
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
describe('isDidPlc', () => {
|
|
28
|
-
it('returns true for various valid dids', () => {
|
|
29
|
-
for (const did of VALID) {
|
|
30
|
-
expect(isDidPlc(did)).toBe(true)
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('returns false for invalid dids', () => {
|
|
35
|
-
for (const [did] of INVALID) {
|
|
36
|
-
expect(isDidPlc(did)).toBe(false)
|
|
37
|
-
}
|
|
38
|
-
})
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
describe('assertDidPlc', () => {
|
|
42
|
-
it('does not throw on valid dids', () => {
|
|
43
|
-
for (const did of VALID) {
|
|
44
|
-
expect(() => assertDidPlc(did)).not.toThrow()
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('throws if called with non string argument', () => {
|
|
49
|
-
for (const [val, message] of INVALID) {
|
|
50
|
-
expect(() => assertDidPlc(val)).toThrow(
|
|
51
|
-
new InvalidDidError(
|
|
52
|
-
typeof val === 'string' ? val : typeof val,
|
|
53
|
-
message,
|
|
54
|
-
),
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
describe('asDidPlc', () => {
|
|
61
|
-
it('returns the input for valid dids', () => {
|
|
62
|
-
for (const did of VALID) {
|
|
63
|
-
expect(asDidPlc(did)).toBe(did)
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('throws if called with invalid dids', () => {
|
|
68
|
-
for (const [val] of INVALID) {
|
|
69
|
-
expect(() => asDidPlc(val)).toThrow(InvalidDidError)
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
})
|