@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 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
@@ -1 +1 @@
1
- {"version":3,"file":"did-error.d.ts","sourceRoot":"","sources":["../src/did-error.ts"],"names":[],"mappings":"AAAA,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"}
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 = (typeof cause?.['statusCode'] === 'number'
27
- ? cause['statusCode']
28
- : undefined) ??
29
- (typeof cause?.['status'] === 'number' ? cause['status'] : undefined);
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
  }
@@ -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,CAAC,OAAO,KAAK,EAAE,CAAC,YAAY,CAAC,KAAK,QAAQ;YACxC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC;YACrB,CAAC,CAAC,SAAS,CAAC;YACd,CAAC,OAAO,KAAK,EAAE,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;QAEvE,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":["export 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?.['statusCode'] === 'number'\n ? cause['statusCode']\n : undefined) ??\n (typeof cause?.['status'] === 'number' ? cause['status'] : 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"]}
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,2 @@
1
+ export declare const ifNumber: (value: unknown) => undefined | number;
2
+ //# sourceMappingURL=number.d.ts.map
@@ -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,2 @@
1
+ export const ifNumber = (value) => typeof value === 'number' ? value : undefined;
2
+ //# sourceMappingURL=number.js.map
@@ -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.2",
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
- }
@@ -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
- }