@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @atproto/did
|
|
2
2
|
|
|
3
|
+
## 0.5.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#5099](https://github.com/bluesky-social/atproto/pull/5099) [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Update TypeScript build to rely on references to composite internal projects
|
|
8
|
+
|
|
9
|
+
- [#5099](https://github.com/bluesky-social/atproto/pull/5099) [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Bundle only necessary files in the NPM tarball, including the `CHANGELOG.md` and `README.md` files (if present).
|
|
10
|
+
|
|
11
|
+
- [#5099](https://github.com/bluesky-social/atproto/pull/5099) [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Build with `noImplicitAny` enabled
|
|
12
|
+
|
|
3
13
|
## 0.5.2
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
package/dist/did-error.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"did-error.d.ts","sourceRoot":"","sources":["../src/did-error.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"did-error.d.ts","sourceRoot":"","sources":["../src/did-error.ts"],"names":[],"mappings":"AAEA,qBAAa,QAAS,SAAQ,KAAK;aAEf,GAAG,EAAE,MAAM;aAEX,IAAI,EAAE,MAAM;aACZ,MAAM;IAJxB,YACkB,GAAG,EAAE,MAAM,EAC3B,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,MAAM,SAAM,EAC5B,KAAK,CAAC,EAAE,OAAO,EAGhB;IAED;;OAEG;IACH,IAAI,UAAU,WAEb;IAEQ,QAAQ,WAEhB;IAED,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAmBjD;CACF;AAED,qBAAa,eAAgB,SAAQ,QAAQ;IAC3C,YAAY,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAExD;CACF"}
|
package/dist/did-error.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ifNumber } from './lib/number.js';
|
|
1
2
|
export class DidError extends Error {
|
|
2
3
|
constructor(did, message, code, status = 400, cause) {
|
|
3
4
|
super(message, { cause });
|
|
@@ -23,10 +24,10 @@ export class DidError extends Error {
|
|
|
23
24
|
: typeof cause === 'string'
|
|
24
25
|
? cause
|
|
25
26
|
: 'An unknown error occurred';
|
|
26
|
-
const status =
|
|
27
|
-
?
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const status = typeof cause === 'object' && cause != null
|
|
28
|
+
? ('statusCode' in cause ? ifNumber(cause.statusCode) : undefined) ??
|
|
29
|
+
('status' in cause ? ifNumber(cause.status) : undefined)
|
|
30
|
+
: undefined;
|
|
30
31
|
return new DidError(did, message, 'did-unknown-error', status, cause);
|
|
31
32
|
}
|
|
32
33
|
}
|
package/dist/did-error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"did-error.js","sourceRoot":"","sources":["../src/did-error.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,YACkB,GAAW,EAC3B,OAAe,EACC,IAAY,EACZ,MAAM,GAAG,GAAG,EAC5B,KAAe;QAEf,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;mBANT,GAAG;oBAEH,IAAI;sBACJ,MAAM;IAIxB,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;IAEQ,QAAQ;QACf,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;IAC/E,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,KAAc,EAAE,GAAW;QACrC,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAA;QACd,CAAC;QAED,MAAM,OAAO,GACX,KAAK,YAAY,KAAK;YACpB,CAAC,CAAC,KAAK,CAAC,OAAO;YACf,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ;gBACzB,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,2BAA2B,CAAA;QAEnC,MAAM,MAAM,GACV,
|
|
1
|
+
{"version":3,"file":"did-error.js","sourceRoot":"","sources":["../src/did-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAE1C,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,YACkB,GAAW,EAC3B,OAAe,EACC,IAAY,EACZ,MAAM,GAAG,GAAG,EAC5B,KAAe;QAEf,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;mBANT,GAAG;oBAEH,IAAI;sBACJ,MAAM;IAIxB,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;IAEQ,QAAQ;QACf,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;IAC/E,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,KAAc,EAAE,GAAW;QACrC,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAA;QACd,CAAC;QAED,MAAM,OAAO,GACX,KAAK,YAAY,KAAK;YACpB,CAAC,CAAC,KAAK,CAAC,OAAO;YACf,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ;gBACzB,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,2BAA2B,CAAA;QAEnC,MAAM,MAAM,GACV,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,IAAI;YACxC,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBAChE,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC1D,CAAC,CAAC,SAAS,CAAA;QAEf,OAAO,IAAI,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;IACvE,CAAC;CACF;AAED,MAAM,OAAO,eAAgB,SAAQ,QAAQ;IAC3C,YAAY,GAAW,EAAE,OAAe,EAAE,KAAe;QACvD,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;IAChD,CAAC;CACF","sourcesContent":["import { ifNumber } from './lib/number.js'\n\nexport class DidError extends Error {\n constructor(\n public readonly did: string,\n message: string,\n public readonly code: string,\n public readonly status = 400,\n cause?: unknown,\n ) {\n super(message, { cause })\n }\n\n /**\n * For compatibility with error handlers in common HTTP frameworks.\n */\n get statusCode() {\n return this.status\n }\n\n override toString() {\n return `${this.constructor.name} ${this.code} (${this.did}): ${this.message}`\n }\n\n static from(cause: unknown, did: string): DidError {\n if (cause instanceof DidError) {\n return cause\n }\n\n const message =\n cause instanceof Error\n ? cause.message\n : typeof cause === 'string'\n ? cause\n : 'An unknown error occurred'\n\n const status =\n typeof cause === 'object' && cause != null\n ? ('statusCode' in cause ? ifNumber(cause.statusCode) : undefined) ??\n ('status' in cause ? ifNumber(cause.status) : undefined)\n : undefined\n\n return new DidError(did, message, 'did-unknown-error', status, cause)\n }\n}\n\nexport class InvalidDidError extends DidError {\n constructor(did: string, message: string, cause?: unknown) {\n super(did, message, 'did-invalid', 400, cause)\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"number.d.ts","sourceRoot":"","sources":["../../src/lib/number.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,UAAW,OAAO,KAAG,SAAS,GAAG,MACP,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"number.js","sourceRoot":"","sources":["../../src/lib/number.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAsB,EAAE,CAC7D,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA","sourcesContent":["export const ifNumber = (value: unknown): undefined | number =>\n typeof value === 'number' ? value : undefined\n"]}
|
package/package.json
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/did",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"engines": {
|
|
5
|
-
"node": ">=22"
|
|
6
|
-
},
|
|
3
|
+
"version": "0.5.3",
|
|
7
4
|
"license": "MIT",
|
|
8
5
|
"description": "DID resolution and verification library",
|
|
9
6
|
"keywords": [
|
|
@@ -18,6 +15,10 @@
|
|
|
18
15
|
"url": "https://github.com/bluesky-social/atproto",
|
|
19
16
|
"directory": "packages/did"
|
|
20
17
|
},
|
|
18
|
+
"files": [
|
|
19
|
+
"./dist",
|
|
20
|
+
"./CHANGELOG.md"
|
|
21
|
+
],
|
|
21
22
|
"type": "module",
|
|
22
23
|
"exports": {
|
|
23
24
|
".": {
|
|
@@ -25,6 +26,9 @@
|
|
|
25
26
|
"default": "./dist/index.js"
|
|
26
27
|
}
|
|
27
28
|
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22"
|
|
31
|
+
},
|
|
28
32
|
"dependencies": {
|
|
29
33
|
"zod": "^3.23.8"
|
|
30
34
|
},
|
package/jest.config.cjs
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/** @type {import('jest').Config} */
|
|
2
|
-
module.exports = {
|
|
3
|
-
displayName: 'DID',
|
|
4
|
-
transform: {
|
|
5
|
-
'^.+\\.(t|j)s$': [
|
|
6
|
-
'@swc/jest',
|
|
7
|
-
{
|
|
8
|
-
jsc: {
|
|
9
|
-
parser: { syntax: 'typescript', importAttributes: true },
|
|
10
|
-
experimental: { keepImportAttributes: true },
|
|
11
|
-
transform: {},
|
|
12
|
-
},
|
|
13
|
-
module: { type: 'es6' },
|
|
14
|
-
},
|
|
15
|
-
],
|
|
16
|
-
},
|
|
17
|
-
extensionsToTreatAsEsm: ['.ts'],
|
|
18
|
-
transformIgnorePatterns: [],
|
|
19
|
-
testTimeout: 60000,
|
|
20
|
-
setupFiles: ['<rootDir>/../../test.setup.ts'],
|
|
21
|
-
moduleNameMapper: { '^(\\.\\.?\\/.+)\\.js$': ['$1.ts', '$1.js'] },
|
|
22
|
-
}
|
package/src/atproto.ts
DELETED
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import { DidDocument, DidService } from './did-document.js'
|
|
3
|
-
import { DidError, InvalidDidError } from './did-error.js'
|
|
4
|
-
import { DidRefAbsolute, isDidRefAbsolute } from './did-ref.js'
|
|
5
|
-
import { Did } from './did.js'
|
|
6
|
-
import { canParse } from './lib/uri.js'
|
|
7
|
-
import {
|
|
8
|
-
DID_PLC_PREFIX,
|
|
9
|
-
DID_WEB_PREFIX,
|
|
10
|
-
assertDidPlc,
|
|
11
|
-
assertDidWeb,
|
|
12
|
-
isDidPlc,
|
|
13
|
-
isDidWeb,
|
|
14
|
-
} from './methods.js'
|
|
15
|
-
import { Identifier, matchesIdentifier } from './utils.js'
|
|
16
|
-
|
|
17
|
-
// This file contains atproto-specific DID validation utilities.
|
|
18
|
-
|
|
19
|
-
export type AtprotoIdentityDidMethods = 'plc' | 'web'
|
|
20
|
-
export type AtprotoDid = Did<AtprotoIdentityDidMethods>
|
|
21
|
-
export type AtprotoDidDocument = DidDocument<AtprotoIdentityDidMethods>
|
|
22
|
-
|
|
23
|
-
export const atprotoDidSchema = z
|
|
24
|
-
.string()
|
|
25
|
-
.refine(isAtprotoDid, `Atproto only allows "plc" and "web" DID methods`)
|
|
26
|
-
|
|
27
|
-
export function isAtprotoDid(input: unknown): input is AtprotoDid {
|
|
28
|
-
return isDidPlc(input) || isAtprotoDidWeb(input)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function asAtprotoDid<T>(input: T) {
|
|
32
|
-
assertAtprotoDid(input)
|
|
33
|
-
return input
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function assertAtprotoDid(input: unknown): asserts input is AtprotoDid {
|
|
37
|
-
if (typeof input !== 'string') {
|
|
38
|
-
throw new InvalidDidError(typeof input, `DID must be a string`)
|
|
39
|
-
} else if (input.startsWith(DID_PLC_PREFIX)) {
|
|
40
|
-
assertDidPlc(input)
|
|
41
|
-
} else if (input.startsWith(DID_WEB_PREFIX)) {
|
|
42
|
-
assertAtprotoDidWeb(input)
|
|
43
|
-
} else {
|
|
44
|
-
throw new InvalidDidError(
|
|
45
|
-
input,
|
|
46
|
-
`Atproto only allows "plc" and "web" DID methods`,
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function assertAtprotoDidWeb(
|
|
52
|
-
input: unknown,
|
|
53
|
-
): asserts input is Did<'web'> {
|
|
54
|
-
assertDidWeb(input)
|
|
55
|
-
|
|
56
|
-
if (isDidWebWithPath(input)) {
|
|
57
|
-
throw new InvalidDidError(
|
|
58
|
-
input,
|
|
59
|
-
`Atproto does not allow path components in Web DIDs`,
|
|
60
|
-
)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (isDidWebWithHttpsPort(input)) {
|
|
64
|
-
throw new InvalidDidError(
|
|
65
|
-
input,
|
|
66
|
-
`Atproto does not allow port numbers in Web DIDs, except for localhost`,
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* @see {@link https://atproto.com/specs/did#blessed-did-methods}
|
|
73
|
-
*/
|
|
74
|
-
export function isAtprotoDidWeb(input: unknown): input is Did<'web'> {
|
|
75
|
-
if (!isDidWeb(input)) {
|
|
76
|
-
return false
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (isDidWebWithPath(input)) {
|
|
80
|
-
return false
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (isDidWebWithHttpsPort(input)) {
|
|
84
|
-
return false
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return true
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function isDidWebWithPath(did: Did<'web'>): boolean {
|
|
91
|
-
return did.includes(':', DID_WEB_PREFIX.length)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function isLocalhostDid(did: Did<'web'>): boolean {
|
|
95
|
-
return (
|
|
96
|
-
did === 'did:web:localhost' ||
|
|
97
|
-
did.startsWith('did:web:localhost:') ||
|
|
98
|
-
did.startsWith('did:web:localhost%3A')
|
|
99
|
-
)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function isDidWebWithHttpsPort(did: Did<'web'>): boolean {
|
|
103
|
-
if (isLocalhostDid(did)) return false
|
|
104
|
-
|
|
105
|
-
const pathIdx = did.indexOf(':', DID_WEB_PREFIX.length)
|
|
106
|
-
|
|
107
|
-
const hasPort =
|
|
108
|
-
pathIdx === -1
|
|
109
|
-
? // No path component, check if there's a port separator anywhere after
|
|
110
|
-
// the "did:web:" prefix
|
|
111
|
-
did.includes('%3A', DID_WEB_PREFIX.length)
|
|
112
|
-
: // There is a path component; if there is an encoded colon *before* it,
|
|
113
|
-
// then there is a port number
|
|
114
|
-
did.lastIndexOf('%3A', pathIdx) !== -1
|
|
115
|
-
|
|
116
|
-
return hasPort
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export type AtprotoData<
|
|
120
|
-
M extends AtprotoIdentityDidMethods = AtprotoIdentityDidMethods,
|
|
121
|
-
> = {
|
|
122
|
-
did: Did<M>
|
|
123
|
-
aka?: string
|
|
124
|
-
key?: AtprotoVerificationMethod<M>
|
|
125
|
-
pds?: AtprotoPersonalDataServerService<M>
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function extractAtprotoData<M extends AtprotoIdentityDidMethods>(
|
|
129
|
-
document: DidDocument<M>,
|
|
130
|
-
): AtprotoData<M> {
|
|
131
|
-
return {
|
|
132
|
-
did: document.id,
|
|
133
|
-
aka: document.alsoKnownAs?.find(isAtprotoAka)?.slice(5),
|
|
134
|
-
key: document.verificationMethod?.find(
|
|
135
|
-
isAtprotoVerificationMethod<M>,
|
|
136
|
-
document,
|
|
137
|
-
),
|
|
138
|
-
pds: document.service?.find(
|
|
139
|
-
isAtprotoPersonalDataServerService<M>,
|
|
140
|
-
document,
|
|
141
|
-
),
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function extractPdsUrl(document: AtprotoDidDocument): URL {
|
|
146
|
-
const service = document.service?.find(
|
|
147
|
-
isAtprotoPersonalDataServerService,
|
|
148
|
-
document,
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
if (!service) {
|
|
152
|
-
throw new DidError(
|
|
153
|
-
document.id,
|
|
154
|
-
`Document ${document.id} does not contain a (valid) #atproto_pds service URL`,
|
|
155
|
-
'did-service-not-found',
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return new URL(service.serviceEndpoint)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export type AtprotoAka = `at://${string}`
|
|
163
|
-
export function isAtprotoAka(value: string): value is AtprotoAka {
|
|
164
|
-
return value.startsWith('at://')
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export type AtprotoPersonalDataServerService<
|
|
168
|
-
M extends AtprotoIdentityDidMethods = AtprotoIdentityDidMethods,
|
|
169
|
-
> = DidService & {
|
|
170
|
-
id: Identifier<Did<M>, 'atproto_pds'>
|
|
171
|
-
type: 'AtprotoPersonalDataServer'
|
|
172
|
-
serviceEndpoint: string
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function isAtprotoPersonalDataServerService<
|
|
176
|
-
M extends AtprotoIdentityDidMethods = AtprotoIdentityDidMethods,
|
|
177
|
-
>(
|
|
178
|
-
this: DidDocument<M>,
|
|
179
|
-
service: null | undefined | DidService,
|
|
180
|
-
): service is AtprotoPersonalDataServerService<M> {
|
|
181
|
-
return (
|
|
182
|
-
service?.type === 'AtprotoPersonalDataServer' &&
|
|
183
|
-
typeof service.serviceEndpoint === 'string' &&
|
|
184
|
-
canParse(service.serviceEndpoint) &&
|
|
185
|
-
matchesIdentifier(this.id, 'atproto_pds', service.id)
|
|
186
|
-
)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export const ATPROTO_VERIFICATION_METHOD_TYPES = Object.freeze([
|
|
190
|
-
'EcdsaSecp256r1VerificationKey2019',
|
|
191
|
-
'EcdsaSecp256k1VerificationKey2019',
|
|
192
|
-
'Multikey',
|
|
193
|
-
] as const)
|
|
194
|
-
export type SupportedAtprotoVerificationMethodType =
|
|
195
|
-
(typeof ATPROTO_VERIFICATION_METHOD_TYPES)[number]
|
|
196
|
-
|
|
197
|
-
type VerificationMethod = NonNullable<DidDocument['verificationMethod']>[number]
|
|
198
|
-
export type AtprotoVerificationMethod<
|
|
199
|
-
M extends AtprotoIdentityDidMethods = AtprotoIdentityDidMethods,
|
|
200
|
-
> = Extract<VerificationMethod, object> & {
|
|
201
|
-
id: Identifier<Did<M>, 'atproto'>
|
|
202
|
-
type: SupportedAtprotoVerificationMethodType
|
|
203
|
-
publicKeyMultibase: string
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export function isAtprotoVerificationMethod<
|
|
207
|
-
M extends AtprotoIdentityDidMethods = AtprotoIdentityDidMethods,
|
|
208
|
-
>(
|
|
209
|
-
this: DidDocument<M>,
|
|
210
|
-
method:
|
|
211
|
-
| null
|
|
212
|
-
| undefined
|
|
213
|
-
| NonNullable<DidDocument<M>['verificationMethod']>[number],
|
|
214
|
-
): method is AtprotoVerificationMethod<M> {
|
|
215
|
-
return (
|
|
216
|
-
typeof method === 'object' &&
|
|
217
|
-
typeof method?.publicKeyMultibase === 'string' &&
|
|
218
|
-
(ATPROTO_VERIFICATION_METHOD_TYPES as readonly unknown[]).includes(
|
|
219
|
-
method.type,
|
|
220
|
-
) &&
|
|
221
|
-
matchesIdentifier(this.id, 'atproto', method.id)
|
|
222
|
-
)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* An atproto-constrained absolute DID reference: `${AtprotoDid}#${fragment}`.
|
|
227
|
-
*/
|
|
228
|
-
export type AtprotoDidRefAbsolute = DidRefAbsolute<AtprotoIdentityDidMethods>
|
|
229
|
-
|
|
230
|
-
export function isAtprotoDidRefAbsolute(
|
|
231
|
-
value: unknown,
|
|
232
|
-
): value is AtprotoDidRefAbsolute {
|
|
233
|
-
if (!isDidRefAbsolute(value)) return false
|
|
234
|
-
return isAtprotoDid(value.slice(0, value.indexOf('#')))
|
|
235
|
-
}
|
package/src/did-document.ts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import { Did, didSchema } from './did.js'
|
|
3
|
-
import { isFragment } from './lib/uri.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* RFC3968 compliant URI
|
|
7
|
-
*
|
|
8
|
-
* @see {@link https://www.rfc-editor.org/rfc/rfc3986}
|
|
9
|
-
*/
|
|
10
|
-
const rfc3968UriSchema = z.string().url('RFC3968 compliant URI')
|
|
11
|
-
|
|
12
|
-
const didControllerSchema = z.union([didSchema, z.array(didSchema)])
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @note this schema is too permissive
|
|
16
|
-
*/
|
|
17
|
-
const didRelativeUriSchema = z.union([
|
|
18
|
-
rfc3968UriSchema.refine(
|
|
19
|
-
(value) => {
|
|
20
|
-
const fragmentIndex = value.indexOf('#')
|
|
21
|
-
if (fragmentIndex === -1) return false
|
|
22
|
-
return isFragment(value, fragmentIndex + 1)
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
message: 'Missing or invalid fragment in RFC3968 URI',
|
|
26
|
-
},
|
|
27
|
-
),
|
|
28
|
-
z
|
|
29
|
-
.string()
|
|
30
|
-
.refine((value) => value.charCodeAt(0) === 35 /* # */, {
|
|
31
|
-
message: 'Fragment must start with #',
|
|
32
|
-
})
|
|
33
|
-
.refine((value) => isFragment(value, 1), {
|
|
34
|
-
message: 'Invalid char in URI fragment',
|
|
35
|
-
}),
|
|
36
|
-
])
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @see {@link https://www.w3.org/TR/did-1.0/#verification-material Verification Material}
|
|
40
|
-
*/
|
|
41
|
-
const didVerificationMethodSchema = z.object({
|
|
42
|
-
id: didRelativeUriSchema,
|
|
43
|
-
type: z.string().min(1),
|
|
44
|
-
controller: didControllerSchema,
|
|
45
|
-
publicKeyJwk: z.record(z.string(), z.unknown()).optional(),
|
|
46
|
-
publicKeyMultibase: z.string().optional(),
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* The value of the id property MUST be a URI conforming to [RFC3986]. A
|
|
51
|
-
* conforming producer MUST NOT produce multiple service entries with the same
|
|
52
|
-
* id. A conforming consumer MUST produce an error if it detects multiple
|
|
53
|
-
* service entries with the same id.
|
|
54
|
-
*
|
|
55
|
-
* @note Normally, only rfc3968UriSchema should be allowed here. However, the
|
|
56
|
-
* did:plc uses relative URI. For this reason, we also allow relative URIs
|
|
57
|
-
* here.
|
|
58
|
-
*/
|
|
59
|
-
const didServiceIdSchema = didRelativeUriSchema
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* The value of the type property MUST be a string or a set of strings. In order
|
|
63
|
-
* to maximize interoperability, the service type and its associated properties
|
|
64
|
-
* SHOULD be registered in the DID Specification Registries
|
|
65
|
-
* [DID-SPEC-REGISTRIES].
|
|
66
|
-
*/
|
|
67
|
-
const didServiceTypeSchema = z.union([z.string(), z.array(z.string())])
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* The value of the serviceEndpoint property MUST be a string, a map, or a set
|
|
71
|
-
* composed of one or more strings and/or maps. All string values MUST be valid
|
|
72
|
-
* URIs conforming to [RFC3986] and normalized according to the Normalization
|
|
73
|
-
* and Comparison rules in RFC3986 and to any normalization rules in its
|
|
74
|
-
* applicable URI scheme specification.
|
|
75
|
-
*/
|
|
76
|
-
const didServiceEndpointSchema = z.union([
|
|
77
|
-
rfc3968UriSchema,
|
|
78
|
-
z.record(z.string(), rfc3968UriSchema),
|
|
79
|
-
z
|
|
80
|
-
.array(z.union([rfc3968UriSchema, z.record(z.string(), rfc3968UriSchema)]))
|
|
81
|
-
.nonempty(),
|
|
82
|
-
])
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Each service map MUST contain id, type, and serviceEndpoint properties.
|
|
86
|
-
* @see {@link https://www.w3.org/TR/did-core/#services}
|
|
87
|
-
*/
|
|
88
|
-
const didServiceSchema = z.object({
|
|
89
|
-
id: didServiceIdSchema,
|
|
90
|
-
type: didServiceTypeSchema,
|
|
91
|
-
serviceEndpoint: didServiceEndpointSchema,
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
export type DidService = z.infer<typeof didServiceSchema>
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* @see {@link https://www.w3.org/TR/did-1.0/#referring-to-verification-methods Referring to Verification Methods}
|
|
98
|
-
*/
|
|
99
|
-
const verificationMethodReference = z.union([
|
|
100
|
-
//
|
|
101
|
-
didRelativeUriSchema,
|
|
102
|
-
didVerificationMethodSchema,
|
|
103
|
-
])
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* @note This schema is incomplete
|
|
107
|
-
* @see {@link https://www.w3.org/TR/did-core/#production-0}
|
|
108
|
-
*/
|
|
109
|
-
export const didDocumentSchema = z.object({
|
|
110
|
-
'@context': z
|
|
111
|
-
.union([
|
|
112
|
-
z.literal('https://www.w3.org/ns/did/v1'),
|
|
113
|
-
z
|
|
114
|
-
.array(z.string().url())
|
|
115
|
-
.nonempty()
|
|
116
|
-
.refine((data) => data[0] === 'https://www.w3.org/ns/did/v1', {
|
|
117
|
-
message: 'First @context must be https://www.w3.org/ns/did/v1',
|
|
118
|
-
}),
|
|
119
|
-
])
|
|
120
|
-
// @NOTE @context is required by producers, but optional for consumers.
|
|
121
|
-
.optional(),
|
|
122
|
-
id: didSchema,
|
|
123
|
-
controller: didControllerSchema.optional(),
|
|
124
|
-
alsoKnownAs: z.array(rfc3968UriSchema).optional(),
|
|
125
|
-
service: z.array(didServiceSchema).optional(),
|
|
126
|
-
authentication: z.array(verificationMethodReference).optional(),
|
|
127
|
-
verificationMethod: z.array(didVerificationMethodSchema).optional(),
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
export type DidDocument<Method extends string = string> = z.infer<
|
|
131
|
-
typeof didDocumentSchema
|
|
132
|
-
> & { id: Did<Method> }
|
|
133
|
-
|
|
134
|
-
// @TODO: add other refinements ?
|
|
135
|
-
export const didDocumentValidator = didDocumentSchema
|
|
136
|
-
// Ensure that every service id is unique
|
|
137
|
-
.superRefine(({ id: did, service }, ctx) => {
|
|
138
|
-
if (service) {
|
|
139
|
-
const visited = new Set()
|
|
140
|
-
|
|
141
|
-
for (let i = 0; i < service.length; i++) {
|
|
142
|
-
const current = service[i]
|
|
143
|
-
|
|
144
|
-
const serviceId = current.id.startsWith('#')
|
|
145
|
-
? `${did}${current.id}`
|
|
146
|
-
: current.id
|
|
147
|
-
|
|
148
|
-
if (!visited.has(serviceId)) {
|
|
149
|
-
visited.add(serviceId)
|
|
150
|
-
} else {
|
|
151
|
-
ctx.addIssue({
|
|
152
|
-
code: z.ZodIssueCode.custom,
|
|
153
|
-
message: `Duplicate service id (${current.id}) found in the document`,
|
|
154
|
-
path: ['service', i, 'id'],
|
|
155
|
-
})
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
})
|
package/src/did-error.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
export class DidError extends Error {
|
|
2
|
-
constructor(
|
|
3
|
-
public readonly did: string,
|
|
4
|
-
message: string,
|
|
5
|
-
public readonly code: string,
|
|
6
|
-
public readonly status = 400,
|
|
7
|
-
cause?: unknown,
|
|
8
|
-
) {
|
|
9
|
-
super(message, { cause })
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* For compatibility with error handlers in common HTTP frameworks.
|
|
14
|
-
*/
|
|
15
|
-
get statusCode() {
|
|
16
|
-
return this.status
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
override toString() {
|
|
20
|
-
return `${this.constructor.name} ${this.code} (${this.did}): ${this.message}`
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
static from(cause: unknown, did: string): DidError {
|
|
24
|
-
if (cause instanceof DidError) {
|
|
25
|
-
return cause
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const message =
|
|
29
|
-
cause instanceof Error
|
|
30
|
-
? cause.message
|
|
31
|
-
: typeof cause === 'string'
|
|
32
|
-
? cause
|
|
33
|
-
: 'An unknown error occurred'
|
|
34
|
-
|
|
35
|
-
const status =
|
|
36
|
-
(typeof cause?.['statusCode'] === 'number'
|
|
37
|
-
? cause['statusCode']
|
|
38
|
-
: undefined) ??
|
|
39
|
-
(typeof cause?.['status'] === 'number' ? cause['status'] : undefined)
|
|
40
|
-
|
|
41
|
-
return new DidError(did, message, 'did-unknown-error', status, cause)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export class InvalidDidError extends DidError {
|
|
46
|
-
constructor(did: string, message: string, cause?: unknown) {
|
|
47
|
-
super(did, message, 'did-invalid', 400, cause)
|
|
48
|
-
}
|
|
49
|
-
}
|
package/src/did-ref.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Did, isDid } from './did.js'
|
|
2
|
-
import { isFragment } from './lib/uri.js'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* An absolute DID reference: `${Did}#${fragment}`.
|
|
6
|
-
*
|
|
7
|
-
* @see {@link https://www.w3.org/TR/did-core/#did-url-syntax}
|
|
8
|
-
*/
|
|
9
|
-
export type DidRefAbsolute<M extends string = string> = `${Did<M>}#${string}`
|
|
10
|
-
|
|
11
|
-
export const isDidRefAbsolute = (value: unknown): value is DidRefAbsolute => {
|
|
12
|
-
if (typeof value !== 'string') return false
|
|
13
|
-
const hashIndex = value.indexOf('#')
|
|
14
|
-
if (hashIndex === -1) return false // no '#'
|
|
15
|
-
if (hashIndex === value.length - 1) return false // empty fragment
|
|
16
|
-
if (value.includes('#', hashIndex + 1)) return false // more than one '#'
|
|
17
|
-
return isFragment(value, hashIndex + 1) && isDid(value.slice(0, hashIndex))
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* A relative DID reference (a `#fragment` resolved against the surrounding
|
|
22
|
-
* DID document's `id`). The optional `id` parameter narrows the fragment.
|
|
23
|
-
*/
|
|
24
|
-
export type DidRefRelative<I extends string = string> = `#${I}`
|
|
25
|
-
|
|
26
|
-
export function isDidRefRelative<I extends string = string>(
|
|
27
|
-
value: unknown,
|
|
28
|
-
id?: I,
|
|
29
|
-
): value is DidRefRelative<I> {
|
|
30
|
-
if (typeof value !== 'string') return false
|
|
31
|
-
if (value.charCodeAt(0) !== 35 /* '#' */) return false // doesn't start with '#'
|
|
32
|
-
if (value.length < 2) return false // empty fragment
|
|
33
|
-
if (value.includes('#', 1)) return false // more than one '#'
|
|
34
|
-
if (!isFragment(value, 1)) return false
|
|
35
|
-
if (id !== undefined && value !== `#${id}`) return false // id mismatch
|
|
36
|
-
return true
|
|
37
|
-
}
|