@atproto/did 0.2.1 → 0.2.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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atproto/did
2
2
 
3
+ ## 0.2.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#4366](https://github.com/bluesky-social/atproto/pull/4366) [`261968fd6`](https://github.com/bluesky-social/atproto/commit/261968fd65014ded613e2bf085d61a7864b8fba7) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `extractPdsUrl` utility
8
+
3
9
  ## 0.2.1
4
10
 
5
11
  ### Patch Changes
package/dist/atproto.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { DidDocument } from './did-document.js';
2
3
  import { Did } from './did.js';
3
4
  export type AtprotoIdentityDidMethods = 'plc' | 'web';
4
5
  export type AtprotoDid = Did<AtprotoIdentityDidMethods>;
@@ -13,4 +14,5 @@ export declare function assertAtprotoDidWeb(input: unknown): asserts input is Di
13
14
  export declare function isAtprotoDidWeb(input: unknown): input is Did<'web'>;
14
15
  export type AtprotoAudience = `${AtprotoDid}#${string}`;
15
16
  export declare const isAtprotoAudience: (value: unknown) => value is AtprotoAudience;
17
+ export declare function extractPdsUrl(document: DidDocument<AtprotoIdentityDidMethods>): URL;
16
18
  //# sourceMappingURL=atproto.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"atproto.d.ts","sourceRoot":"","sources":["../src/atproto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAa9B,MAAM,MAAM,yBAAyB,GAAG,KAAK,GAAG,KAAK,CAAA;AACrD,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC,yBAAyB,CAAC,CAAA;AAEvD,eAAO,MAAM,gBAAgB,8EAE6C,CAAA;AAE1E,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,yDAGvC;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAa5E;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,CAgB7B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,CAcnE;AA+BD,MAAM,MAAM,eAAe,GAAG,GAAG,UAAU,IAAI,MAAM,EAAE,CAAA;AACvD,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,eAQ3D,CAAA"}
1
+ {"version":3,"file":"atproto.d.ts","sourceRoot":"","sources":["../src/atproto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAa9B,MAAM,MAAM,yBAAyB,GAAG,KAAK,GAAG,KAAK,CAAA;AACrD,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC,yBAAyB,CAAC,CAAA;AAEvD,eAAO,MAAM,gBAAgB,8EAE6C,CAAA;AAE1E,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,yDAGvC;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAa5E;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,CAgB7B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,CAcnE;AA+BD,MAAM,MAAM,eAAe,GAAG,GAAG,UAAU,IAAI,MAAM,EAAE,CAAA;AACvD,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,eAQ3D,CAAA;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,WAAW,CAAC,yBAAyB,CAAC,GAC/C,GAAG,CAyBL"}
package/dist/atproto.js CHANGED
@@ -6,6 +6,7 @@ exports.asAtprotoDid = asAtprotoDid;
6
6
  exports.assertAtprotoDid = assertAtprotoDid;
7
7
  exports.assertAtprotoDidWeb = assertAtprotoDidWeb;
8
8
  exports.isAtprotoDidWeb = isAtprotoDidWeb;
9
+ exports.extractPdsUrl = extractPdsUrl;
9
10
  const zod_1 = require("zod");
10
11
  const did_error_js_1 = require("./did-error.js");
11
12
  const uri_js_1 = require("./lib/uri.js");
@@ -90,4 +91,23 @@ const isAtprotoAudience = (value) => {
90
91
  return ((0, uri_js_1.isFragment)(value, hashIndex + 1) && isAtprotoDid(value.slice(0, hashIndex)));
91
92
  };
92
93
  exports.isAtprotoAudience = isAtprotoAudience;
94
+ function extractPdsUrl(document) {
95
+ const service = document.service?.find((isAtprotoPersonalDataServerService), document);
96
+ if (!service) {
97
+ throw new did_error_js_1.DidError(document.id, `Identity "${document.id}" does not have a PDS URL`, 'did-service-not-found');
98
+ }
99
+ try {
100
+ return new URL(service.serviceEndpoint);
101
+ }
102
+ catch (cause) {
103
+ throw new did_error_js_1.DidError(document.id, `Invalid PDS URL in DID document: ${service.serviceEndpoint}`, 'did-document-format-error', undefined, cause);
104
+ }
105
+ }
106
+ function isAtprotoPersonalDataServerService(s) {
107
+ return (typeof s.serviceEndpoint === 'string' &&
108
+ s.type === 'AtprotoPersonalDataServer' &&
109
+ (s.id.startsWith('#')
110
+ ? s.id === '#atproto_pds'
111
+ : s.id === `${this.id}#atproto_pds`));
112
+ }
93
113
  //# sourceMappingURL=atproto.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"atproto.js","sourceRoot":"","sources":["../src/atproto.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod'\nimport { InvalidDidError } from './did-error.js'\nimport { Did } from './did.js'\nimport { isFragment } from './lib/uri.js'\nimport {\n DID_PLC_PREFIX,\n DID_WEB_PREFIX,\n assertDidPlc,\n assertDidWeb,\n isDidPlc,\n isDidWeb,\n} from './methods.js'\n\n// This file contains atproto-specific DID validation utilities.\n\nexport type AtprotoIdentityDidMethods = 'plc' | 'web'\nexport type AtprotoDid = Did<AtprotoIdentityDidMethods>\n\nexport const atprotoDidSchema = z\n .string()\n .refine(isAtprotoDid, `Atproto only allows \"plc\" and \"web\" DID methods`)\n\nexport function isAtprotoDid(input: unknown): input is AtprotoDid {\n return isDidPlc(input) || isAtprotoDidWeb(input)\n}\n\nexport function asAtprotoDid<T>(input: T) {\n assertAtprotoDid(input)\n return input\n}\n\nexport function assertAtprotoDid(input: unknown): asserts input is AtprotoDid {\n if (typeof input !== 'string') {\n throw new InvalidDidError(typeof input, `DID must be a string`)\n } else if (input.startsWith(DID_PLC_PREFIX)) {\n assertDidPlc(input)\n } else if (input.startsWith(DID_WEB_PREFIX)) {\n assertAtprotoDidWeb(input)\n } else {\n throw new InvalidDidError(\n input,\n `Atproto only allows \"plc\" and \"web\" DID methods`,\n )\n }\n}\n\nexport function assertAtprotoDidWeb(\n input: unknown,\n): asserts input is Did<'web'> {\n assertDidWeb(input)\n\n if (isDidWebWithPath(input)) {\n throw new InvalidDidError(\n input,\n `Atproto does not allow path components in Web DIDs`,\n )\n }\n\n if (isDidWebWithHttpsPort(input)) {\n throw new InvalidDidError(\n input,\n `Atproto does not allow port numbers in Web DIDs, except for localhost`,\n )\n }\n}\n\n/**\n * @see {@link https://atproto.com/specs/did#blessed-did-methods}\n */\nexport function isAtprotoDidWeb(input: unknown): input is Did<'web'> {\n if (!isDidWeb(input)) {\n return false\n }\n\n if (isDidWebWithPath(input)) {\n return false\n }\n\n if (isDidWebWithHttpsPort(input)) {\n return false\n }\n\n return true\n}\n\nfunction isDidWebWithPath(did: Did<'web'>): boolean {\n return did.includes(':', DID_WEB_PREFIX.length)\n}\n\nfunction isLocalhostDid(did: Did<'web'>): boolean {\n return (\n did === 'did:web:localhost' ||\n did.startsWith('did:web:localhost:') ||\n did.startsWith('did:web:localhost%3A')\n )\n}\n\nfunction isDidWebWithHttpsPort(did: Did<'web'>): boolean {\n if (isLocalhostDid(did)) return false\n\n const pathIdx = did.indexOf(':', DID_WEB_PREFIX.length)\n\n const hasPort =\n pathIdx === -1\n ? // No path component, check if there's a port separator anywhere after\n // the \"did:web:\" prefix\n did.includes('%3A', DID_WEB_PREFIX.length)\n : // There is a path component; if there is an encoded colon *before* it,\n // then there is a port number\n did.lastIndexOf('%3A', pathIdx) !== -1\n\n return hasPort\n}\n\nexport type AtprotoAudience = `${AtprotoDid}#${string}`\nexport const isAtprotoAudience = (value: unknown): value is AtprotoAudience => {\n if (typeof value !== 'string') return false\n const hashIndex = value.indexOf('#')\n if (hashIndex === -1) return false\n if (value.indexOf('#', hashIndex + 1) !== -1) return false\n return (\n isFragment(value, hashIndex + 1) && isAtprotoDid(value.slice(0, hashIndex))\n )\n}\n"]}
1
+ {"version":3,"file":"atproto.js","sourceRoot":"","sources":["../src/atproto.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod'\nimport { DidDocument, DidService } from './did-document.js'\nimport { DidError, InvalidDidError } from './did-error.js'\nimport { Did } from './did.js'\nimport { isFragment } from './lib/uri.js'\nimport {\n DID_PLC_PREFIX,\n DID_WEB_PREFIX,\n assertDidPlc,\n assertDidWeb,\n isDidPlc,\n isDidWeb,\n} from './methods.js'\n\n// This file contains atproto-specific DID validation utilities.\n\nexport type AtprotoIdentityDidMethods = 'plc' | 'web'\nexport type AtprotoDid = Did<AtprotoIdentityDidMethods>\n\nexport const atprotoDidSchema = z\n .string()\n .refine(isAtprotoDid, `Atproto only allows \"plc\" and \"web\" DID methods`)\n\nexport function isAtprotoDid(input: unknown): input is AtprotoDid {\n return isDidPlc(input) || isAtprotoDidWeb(input)\n}\n\nexport function asAtprotoDid<T>(input: T) {\n assertAtprotoDid(input)\n return input\n}\n\nexport function assertAtprotoDid(input: unknown): asserts input is AtprotoDid {\n if (typeof input !== 'string') {\n throw new InvalidDidError(typeof input, `DID must be a string`)\n } else if (input.startsWith(DID_PLC_PREFIX)) {\n assertDidPlc(input)\n } else if (input.startsWith(DID_WEB_PREFIX)) {\n assertAtprotoDidWeb(input)\n } else {\n throw new InvalidDidError(\n input,\n `Atproto only allows \"plc\" and \"web\" DID methods`,\n )\n }\n}\n\nexport function assertAtprotoDidWeb(\n input: unknown,\n): asserts input is Did<'web'> {\n assertDidWeb(input)\n\n if (isDidWebWithPath(input)) {\n throw new InvalidDidError(\n input,\n `Atproto does not allow path components in Web DIDs`,\n )\n }\n\n if (isDidWebWithHttpsPort(input)) {\n throw new InvalidDidError(\n input,\n `Atproto does not allow port numbers in Web DIDs, except for localhost`,\n )\n }\n}\n\n/**\n * @see {@link https://atproto.com/specs/did#blessed-did-methods}\n */\nexport function isAtprotoDidWeb(input: unknown): input is Did<'web'> {\n if (!isDidWeb(input)) {\n return false\n }\n\n if (isDidWebWithPath(input)) {\n return false\n }\n\n if (isDidWebWithHttpsPort(input)) {\n return false\n }\n\n return true\n}\n\nfunction isDidWebWithPath(did: Did<'web'>): boolean {\n return did.includes(':', DID_WEB_PREFIX.length)\n}\n\nfunction isLocalhostDid(did: Did<'web'>): boolean {\n return (\n did === 'did:web:localhost' ||\n did.startsWith('did:web:localhost:') ||\n did.startsWith('did:web:localhost%3A')\n )\n}\n\nfunction isDidWebWithHttpsPort(did: Did<'web'>): boolean {\n if (isLocalhostDid(did)) return false\n\n const pathIdx = did.indexOf(':', DID_WEB_PREFIX.length)\n\n const hasPort =\n pathIdx === -1\n ? // No path component, check if there's a port separator anywhere after\n // the \"did:web:\" prefix\n did.includes('%3A', DID_WEB_PREFIX.length)\n : // There is a path component; if there is an encoded colon *before* it,\n // then there is a port number\n did.lastIndexOf('%3A', pathIdx) !== -1\n\n return hasPort\n}\n\nexport type AtprotoAudience = `${AtprotoDid}#${string}`\nexport const isAtprotoAudience = (value: unknown): value is AtprotoAudience => {\n if (typeof value !== 'string') return false\n const hashIndex = value.indexOf('#')\n if (hashIndex === -1) return false\n if (value.indexOf('#', hashIndex + 1) !== -1) return false\n return (\n isFragment(value, hashIndex + 1) && isAtprotoDid(value.slice(0, hashIndex))\n )\n}\n\nexport function extractPdsUrl(\n document: DidDocument<AtprotoIdentityDidMethods>,\n): URL {\n const service = document.service?.find(\n isAtprotoPersonalDataServerService<AtprotoIdentityDidMethods>,\n document,\n )\n\n if (!service) {\n throw new DidError(\n document.id,\n `Identity \"${document.id}\" does not have a PDS URL`,\n 'did-service-not-found',\n )\n }\n\n try {\n return new URL(service.serviceEndpoint)\n } catch (cause) {\n throw new DidError(\n document.id,\n `Invalid PDS URL in DID document: ${service.serviceEndpoint}`,\n 'did-document-format-error',\n undefined,\n cause,\n )\n }\n}\n\nfunction isAtprotoPersonalDataServerService<M extends string>(\n this: DidDocument<M>,\n s: DidService,\n): s is {\n id: '#atproto_pds' | `${Did<M>}#atproto_pds`\n type: 'AtprotoPersonalDataServer'\n serviceEndpoint: string\n} {\n return (\n typeof s.serviceEndpoint === 'string' &&\n s.type === 'AtprotoPersonalDataServer' &&\n (s.id.startsWith('#')\n ? s.id === '#atproto_pds'\n : s.id === `${this.id}#atproto_pds`)\n )\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/did",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "license": "MIT",
5
5
  "description": "DID resolution and verification library",
6
6
  "keywords": [
package/src/atproto.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod'
2
- import { InvalidDidError } from './did-error.js'
2
+ import { DidDocument, DidService } from './did-document.js'
3
+ import { DidError, InvalidDidError } from './did-error.js'
3
4
  import { Did } from './did.js'
4
5
  import { isFragment } from './lib/uri.js'
5
6
  import {
@@ -122,3 +123,49 @@ export const isAtprotoAudience = (value: unknown): value is AtprotoAudience => {
122
123
  isFragment(value, hashIndex + 1) && isAtprotoDid(value.slice(0, hashIndex))
123
124
  )
124
125
  }
126
+
127
+ export function extractPdsUrl(
128
+ document: DidDocument<AtprotoIdentityDidMethods>,
129
+ ): URL {
130
+ const service = document.service?.find(
131
+ isAtprotoPersonalDataServerService<AtprotoIdentityDidMethods>,
132
+ document,
133
+ )
134
+
135
+ if (!service) {
136
+ throw new DidError(
137
+ document.id,
138
+ `Identity "${document.id}" does not have a PDS URL`,
139
+ 'did-service-not-found',
140
+ )
141
+ }
142
+
143
+ try {
144
+ return new URL(service.serviceEndpoint)
145
+ } catch (cause) {
146
+ throw new DidError(
147
+ document.id,
148
+ `Invalid PDS URL in DID document: ${service.serviceEndpoint}`,
149
+ 'did-document-format-error',
150
+ undefined,
151
+ cause,
152
+ )
153
+ }
154
+ }
155
+
156
+ function isAtprotoPersonalDataServerService<M extends string>(
157
+ this: DidDocument<M>,
158
+ s: DidService,
159
+ ): s is {
160
+ id: '#atproto_pds' | `${Did<M>}#atproto_pds`
161
+ type: 'AtprotoPersonalDataServer'
162
+ serviceEndpoint: string
163
+ } {
164
+ return (
165
+ typeof s.serviceEndpoint === 'string' &&
166
+ s.type === 'AtprotoPersonalDataServer' &&
167
+ (s.id.startsWith('#')
168
+ ? s.id === '#atproto_pds'
169
+ : s.id === `${this.id}#atproto_pds`)
170
+ )
171
+ }