@fedify/vocab-runtime 2.4.0-dev.1599 → 2.4.0-dev.1618

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/deno.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab-runtime",
3
- "version": "2.4.0-dev.1599+7b19967f",
3
+ "version": "2.4.0-dev.1618+58858243",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts",
@@ -1,6 +1,6 @@
1
1
 
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_url = require("../url-BMDN9GnE.cjs");
3
+ const require_url = require("../url-Ck3dGEwH.cjs");
4
4
  const require_jsonld = require("../jsonld.cjs");
5
5
  //#region src/internal/jsonld-cache.ts
6
6
  const noJsonLdContext = Symbol("noJsonLdContext");
@@ -12,7 +12,7 @@ const noJsonLdContext = Symbol("noJsonLdContext");
12
12
  * Semantic Versioning decisions.
13
13
  */
14
14
  function isTrustedIriOrigin(options, left, right) {
15
- return options.crossOrigin === "trust" || left == null || right != null && require_url.haveSameIriOrigin(left, right);
15
+ return options.crossOrigin === "trust" || left == null || right != null && (require_url.haveSameIriOrigin(left, right) || require_url.haveSameFe34Origin(left, right));
16
16
  }
17
17
  /**
18
18
  * Normalizes portable IRIs in JSON-LD cache data.
@@ -1,5 +1,5 @@
1
1
 
2
- import { a as formatIri, o as haveSameIriOrigin } from "../url-BlFta-Eh.js";
2
+ import { a as formatIri, c as haveSameIriOrigin, s as haveSameFe34Origin } from "../url-m1YxGNZ0.js";
3
3
  import jsonld_default from "../jsonld.js";
4
4
  //#region src/internal/jsonld-cache.ts
5
5
  const noJsonLdContext = Symbol("noJsonLdContext");
@@ -11,7 +11,7 @@ const noJsonLdContext = Symbol("noJsonLdContext");
11
11
  * Semantic Versioning decisions.
12
12
  */
13
13
  function isTrustedIriOrigin(options, left, right) {
14
- return options.crossOrigin === "trust" || left == null || right != null && haveSameIriOrigin(left, right);
14
+ return options.crossOrigin === "trust" || left == null || right != null && (haveSameIriOrigin(left, right) || haveSameFe34Origin(left, right));
15
15
  }
16
16
  /**
17
17
  * Normalizes portable IRIs in JSON-LD cache data.
package/dist/mod.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_chunk = require("./chunk-M78iaK0I.cjs");
4
- const require_url = require("./url-BMDN9GnE.cjs");
4
+ const require_url = require("./url-Ck3dGEwH.cjs");
5
5
  let _logtape_logtape = require("@logtape/logtape");
6
6
  let _opentelemetry_api = require("@opentelemetry/api");
7
7
  let node_process = require("node:process");
@@ -4367,7 +4367,7 @@ const preloadedContexts = {
4367
4367
  //#endregion
4368
4368
  //#region deno.json
4369
4369
  var name = "@fedify/vocab-runtime";
4370
- var version = "2.4.0-dev.1599+7b19967f";
4370
+ var version = "2.4.0-dev.1618+58858243";
4371
4371
  //#endregion
4372
4372
  //#region src/link.ts
4373
4373
  const parametersNeedLowerCase = ["rel", "type"];
@@ -5564,8 +5564,10 @@ exports.exportMultibaseKey = exportMultibaseKey;
5564
5564
  exports.exportSpki = exportSpki;
5565
5565
  exports.formatIri = require_url.formatIri;
5566
5566
  exports.getDocumentLoader = getDocumentLoader;
5567
+ exports.getFe34Origin = require_url.getFe34Origin;
5567
5568
  exports.getRemoteDocument = getRemoteDocument;
5568
5569
  exports.getUserAgent = getUserAgent;
5570
+ exports.haveSameFe34Origin = require_url.haveSameFe34Origin;
5569
5571
  exports.haveSameIriOrigin = require_url.haveSameIriOrigin;
5570
5572
  exports.importDidKey = importDidKey;
5571
5573
  exports.importMultibaseKey = importMultibaseKey;
package/dist/mod.d.cts CHANGED
@@ -353,6 +353,24 @@ declare function canonicalizePortableUri(input: string): string;
353
353
  */
354
354
  declare function arePortableUrisEqual(left: string, right: string): boolean;
355
355
  /**
356
+ * Computes an IRI's FEP-fe34 origin.
357
+ *
358
+ * HTTP(S) IRIs use their web origin. FEP-ef61 portable ActivityPub IRIs and
359
+ * DID URLs use their DID as a cryptographic origin.
360
+ *
361
+ * @throws {TypeError} If the IRI does not have a supported FEP-fe34 origin.
362
+ * @since 2.4.0
363
+ */
364
+ declare function getFe34Origin(input: string | URL): string;
365
+ /**
366
+ * Checks whether two IRIs have the same FEP-fe34 origin.
367
+ *
368
+ * Malformed or unsupported IRIs are treated as non-matching.
369
+ *
370
+ * @since 2.4.0
371
+ */
372
+ declare function haveSameFe34Origin(left: string | URL, right: string | URL): boolean;
373
+ /**
356
374
  * Checks whether two IRIs have the same origin.
357
375
  */
358
376
  declare function haveSameIriOrigin(left: URL, right: URL): boolean;
@@ -364,4 +382,4 @@ declare function isValidPublicIPv4Address(address: string): boolean;
364
382
  declare function isValidPublicIPv6Address(address: string): boolean;
365
383
  declare function expandIPv6Address(address: string): string;
366
384
  //#endregion
367
- export { type AuthenticatedDocumentLoaderFactory, type CreateRequestOptions, type Decimal, type DidKeyVerificationMethod, type DocumentLoader, type DocumentLoaderFactory, type DocumentLoaderFactoryOptions, type DocumentLoaderOptions, FetchError, type GetDocumentLoaderOptions, type GetUserAgentOptions, type Json, LanguageString, type PropertyPreprocessor, type PropertyPreprocessorContext, type RemoteDocument, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, createActivityPubRequest, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, formatIri, getDocumentLoader, getRemoteDocument, getUserAgent, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, parseDecimal, parseDidKeyVerificationMethod, parseIri, parseJsonLdId, preloadedContexts, validatePublicUrl };
385
+ export { type AuthenticatedDocumentLoaderFactory, type CreateRequestOptions, type Decimal, type DidKeyVerificationMethod, type DocumentLoader, type DocumentLoaderFactory, type DocumentLoaderFactoryOptions, type DocumentLoaderOptions, FetchError, type GetDocumentLoaderOptions, type GetUserAgentOptions, type Json, LanguageString, type PropertyPreprocessor, type PropertyPreprocessorContext, type RemoteDocument, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, createActivityPubRequest, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, formatIri, getDocumentLoader, getFe34Origin, getRemoteDocument, getUserAgent, haveSameFe34Origin, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, parseDecimal, parseDidKeyVerificationMethod, parseIri, parseJsonLdId, preloadedContexts, validatePublicUrl };
package/dist/mod.d.ts CHANGED
@@ -353,6 +353,24 @@ declare function canonicalizePortableUri(input: string): string;
353
353
  */
354
354
  declare function arePortableUrisEqual(left: string, right: string): boolean;
355
355
  /**
356
+ * Computes an IRI's FEP-fe34 origin.
357
+ *
358
+ * HTTP(S) IRIs use their web origin. FEP-ef61 portable ActivityPub IRIs and
359
+ * DID URLs use their DID as a cryptographic origin.
360
+ *
361
+ * @throws {TypeError} If the IRI does not have a supported FEP-fe34 origin.
362
+ * @since 2.4.0
363
+ */
364
+ declare function getFe34Origin(input: string | URL): string;
365
+ /**
366
+ * Checks whether two IRIs have the same FEP-fe34 origin.
367
+ *
368
+ * Malformed or unsupported IRIs are treated as non-matching.
369
+ *
370
+ * @since 2.4.0
371
+ */
372
+ declare function haveSameFe34Origin(left: string | URL, right: string | URL): boolean;
373
+ /**
356
374
  * Checks whether two IRIs have the same origin.
357
375
  */
358
376
  declare function haveSameIriOrigin(left: URL, right: URL): boolean;
@@ -364,4 +382,4 @@ declare function isValidPublicIPv4Address(address: string): boolean;
364
382
  declare function isValidPublicIPv6Address(address: string): boolean;
365
383
  declare function expandIPv6Address(address: string): string;
366
384
  //#endregion
367
- export { type AuthenticatedDocumentLoaderFactory, type CreateRequestOptions, type Decimal, type DidKeyVerificationMethod, type DocumentLoader, type DocumentLoaderFactory, type DocumentLoaderFactoryOptions, type DocumentLoaderOptions, FetchError, type GetDocumentLoaderOptions, type GetUserAgentOptions, type Json, LanguageString, type PropertyPreprocessor, type PropertyPreprocessorContext, type RemoteDocument, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, createActivityPubRequest, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, formatIri, getDocumentLoader, getRemoteDocument, getUserAgent, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, parseDecimal, parseDidKeyVerificationMethod, parseIri, parseJsonLdId, preloadedContexts, validatePublicUrl };
385
+ export { type AuthenticatedDocumentLoaderFactory, type CreateRequestOptions, type Decimal, type DidKeyVerificationMethod, type DocumentLoader, type DocumentLoaderFactory, type DocumentLoaderFactoryOptions, type DocumentLoaderOptions, FetchError, type GetDocumentLoaderOptions, type GetUserAgentOptions, type Json, LanguageString, type PropertyPreprocessor, type PropertyPreprocessorContext, type RemoteDocument, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, createActivityPubRequest, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, formatIri, getDocumentLoader, getFe34Origin, getRemoteDocument, getUserAgent, haveSameFe34Origin, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, parseDecimal, parseDidKeyVerificationMethod, parseIri, parseJsonLdId, preloadedContexts, validatePublicUrl };
package/dist/mod.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { a as formatIri, c as isValidPublicIPv6Address, d as validatePublicUrl, i as expandIPv6Address, l as parseIri, n as arePortableUrisEqual, o as haveSameIriOrigin, r as canonicalizePortableUri, s as isValidPublicIPv4Address, t as UrlError, u as parseJsonLdId } from "./url-BlFta-Eh.js";
2
+ import { a as formatIri, c as haveSameIriOrigin, d as parseIri, f as parseJsonLdId, i as expandIPv6Address, l as isValidPublicIPv4Address, n as arePortableUrisEqual, o as getFe34Origin, p as validatePublicUrl, r as canonicalizePortableUri, s as haveSameFe34Origin, t as UrlError, u as isValidPublicIPv6Address } from "./url-m1YxGNZ0.js";
3
3
  import { getLogger } from "@logtape/logtape";
4
4
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
5
5
  import process from "node:process";
@@ -4363,7 +4363,7 @@ const preloadedContexts = {
4363
4363
  //#endregion
4364
4364
  //#region deno.json
4365
4365
  var name = "@fedify/vocab-runtime";
4366
- var version = "2.4.0-dev.1599+7b19967f";
4366
+ var version = "2.4.0-dev.1618+58858243";
4367
4367
  //#endregion
4368
4368
  //#region src/link.ts
4369
4369
  const parametersNeedLowerCase = ["rel", "type"];
@@ -5544,4 +5544,4 @@ LanguageString.prototype[Symbol.for("nodejs.util.inspect.custom")] = function(_d
5544
5544
  return `<${this.locale.baseName}> ${inspect(this.toString(), options)}`;
5545
5545
  };
5546
5546
  //#endregion
5547
- export { FetchError, LanguageString, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, createActivityPubRequest, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, formatIri, getDocumentLoader, getRemoteDocument, getUserAgent, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, parseDecimal, parseDidKeyVerificationMethod, parseIri, parseJsonLdId, preloadedContexts, validatePublicUrl };
5547
+ export { FetchError, LanguageString, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, createActivityPubRequest, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, formatIri, getDocumentLoader, getFe34Origin, getRemoteDocument, getUserAgent, haveSameFe34Origin, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, parseDecimal, parseDidKeyVerificationMethod, parseIri, parseJsonLdId, preloadedContexts, validatePublicUrl };
@@ -1,6 +1,6 @@
1
1
  require("./chunk-C2EiDwsr.cjs");
2
- require("./docloader-BiSxywul.cjs");
3
- require("./url-CtKcX6ma.cjs");
2
+ require("./docloader-QmR6VOqT.cjs");
3
+ require("./url-BvjYQdxL.cjs");
4
4
  require("./key-_wXwomh_.cjs");
5
5
  require("./multibase-Bz_UUDtL.cjs");
6
6
  require("./langstr-CbAxaeEZ.cjs");
@@ -1,5 +1,5 @@
1
- import "./docloader-CDde_fhH.mjs";
2
- import "./url-D7ay_5pk.mjs";
1
+ import "./docloader-CBVde8Va.mjs";
2
+ import "./url-a2D8NAgh.mjs";
3
3
  import "./key-CDGDH_vC.mjs";
4
4
  import "./multibase-B4bvakyA.mjs";
5
5
  import "./langstr-Di5AvKpB.mjs";
@@ -1,6 +1,6 @@
1
- import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-BBw3lNNz.mjs";
1
+ import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-BHx0fCb5.mjs";
2
2
  import { t as HttpHeaderLink } from "./link-NUUWCdnK.mjs";
3
- import { d as validatePublicUrl, t as UrlError } from "./url-D7ay_5pk.mjs";
3
+ import { p as validatePublicUrl, t as UrlError } from "./url-a2D8NAgh.mjs";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
6
6
  //#endregion
@@ -1,7 +1,7 @@
1
1
  require("./chunk-C2EiDwsr.cjs");
2
- const require_request = require("./request-C9VKqYdG.cjs");
2
+ const require_request = require("./request-DxSceLvB.cjs");
3
3
  const require_link = require("./link-FguCydMA.cjs");
4
- const require_url = require("./url-CtKcX6ma.cjs");
4
+ const require_url = require("./url-BvjYQdxL.cjs");
5
5
  let _logtape_logtape = require("@logtape/logtape");
6
6
  let _opentelemetry_api = require("@opentelemetry/api");
7
7
  //#endregion
@@ -1,7 +1,7 @@
1
1
  const require_chunk = require("./chunk-C2EiDwsr.cjs");
2
- const require_docloader = require("./docloader-BiSxywul.cjs");
3
- const require_request = require("./request-C9VKqYdG.cjs");
4
- const require_url = require("./url-CtKcX6ma.cjs");
2
+ const require_docloader = require("./docloader-QmR6VOqT.cjs");
3
+ const require_request = require("./request-DxSceLvB.cjs");
4
+ const require_url = require("./url-BvjYQdxL.cjs");
5
5
  let node_assert = require("node:assert");
6
6
  let node_test = require("node:test");
7
7
  //#region ../../node_modules/.pnpm/glob-to-regexp@0.4.1/node_modules/glob-to-regexp/index.js
@@ -1,6 +1,6 @@
1
- import { n as getRemoteDocument, r as preloadedContexts, t as getDocumentLoader } from "./docloader-CDde_fhH.mjs";
2
- import { t as FetchError } from "./request-BBw3lNNz.mjs";
3
- import { t as UrlError } from "./url-D7ay_5pk.mjs";
1
+ import { n as getRemoteDocument, r as preloadedContexts, t as getDocumentLoader } from "./docloader-CBVde8Va.mjs";
2
+ import { t as FetchError } from "./request-BHx0fCb5.mjs";
3
+ import { t as UrlError } from "./url-a2D8NAgh.mjs";
4
4
  import { deepStrictEqual, ok, rejects } from "node:assert";
5
5
  import { test } from "node:test";
6
6
  //#region \0rolldown/runtime.js
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-C2EiDwsr.cjs");
2
- const require_url = require("./url-CtKcX6ma.cjs");
2
+ const require_url = require("./url-BvjYQdxL.cjs");
3
3
  let node_assert = require("node:assert");
4
4
  let node_test = require("node:test");
5
5
  let jsonld_dist_jsonld_esm_js = require("jsonld/dist/jsonld.esm.js");
@@ -17,7 +17,7 @@ const noJsonLdContext = Symbol("noJsonLdContext");
17
17
  * Semantic Versioning decisions.
18
18
  */
19
19
  function isTrustedIriOrigin(options, left, right) {
20
- return options.crossOrigin === "trust" || left == null || right != null && require_url.haveSameIriOrigin(left, right);
20
+ return options.crossOrigin === "trust" || left == null || right != null && (require_url.haveSameIriOrigin(left, right) || require_url.haveSameFe34Origin(left, right));
21
21
  }
22
22
  /**
23
23
  * Normalizes portable IRIs in JSON-LD cache data.
@@ -1,4 +1,4 @@
1
- import { a as formatIri, l as parseIri, o as haveSameIriOrigin } from "./url-D7ay_5pk.mjs";
1
+ import { a as formatIri, c as haveSameIriOrigin, d as parseIri, s as haveSameFe34Origin } from "./url-a2D8NAgh.mjs";
2
2
  import { deepStrictEqual, ok, strictEqual } from "node:assert";
3
3
  import { test } from "node:test";
4
4
  import jsonld from "jsonld/dist/jsonld.esm.js";
@@ -15,7 +15,7 @@ const noJsonLdContext = Symbol("noJsonLdContext");
15
15
  * Semantic Versioning decisions.
16
16
  */
17
17
  function isTrustedIriOrigin(options, left, right) {
18
- return options.crossOrigin === "trust" || left == null || right != null && haveSameIriOrigin(left, right);
18
+ return options.crossOrigin === "trust" || left == null || right != null && (haveSameIriOrigin(left, right) || haveSameFe34Origin(left, right));
19
19
  }
20
20
  /**
21
21
  * Normalizes portable IRIs in JSON-LD cache data.
@@ -1,7 +1,7 @@
1
1
  import process from "node:process";
2
2
  //#region deno.json
3
3
  var name = "@fedify/vocab-runtime";
4
- var version = "2.4.0-dev.1599+7b19967f";
4
+ var version = "2.4.0-dev.1618+58858243";
5
5
  //#endregion
6
6
  //#region src/request.ts
7
7
  /**
@@ -3,7 +3,7 @@ let node_process = require("node:process");
3
3
  node_process = require_chunk.__toESM(node_process, 1);
4
4
  //#region deno.json
5
5
  var name = "@fedify/vocab-runtime";
6
- var version = "2.4.0-dev.1599+7b19967f";
6
+ var version = "2.4.0-dev.1618+58858243";
7
7
  //#endregion
8
8
  //#region src/request.ts
9
9
  /**
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-C2EiDwsr.cjs");
2
- const require_request = require("./request-C9VKqYdG.cjs");
2
+ const require_request = require("./request-DxSceLvB.cjs");
3
3
  let node_assert = require("node:assert");
4
4
  let node_test = require("node:test");
5
5
  let node_process = require("node:process");
@@ -1,4 +1,4 @@
1
- import { o as version, r as getUserAgent } from "./request-BBw3lNNz.mjs";
1
+ import { o as version, r as getUserAgent } from "./request-BHx0fCb5.mjs";
2
2
  import { deepStrictEqual } from "node:assert";
3
3
  import { test } from "node:test";
4
4
  import process from "node:process";
@@ -63,7 +63,7 @@ function canonicalizePortableUri(input) {
63
63
  const parsed = parsePortableIri(input);
64
64
  if (parsed == null) throw new TypeError("Invalid portable ActivityPub IRI.");
65
65
  const match = input.match(PORTABLE_IRI_PATTERN);
66
- return `ap+ef61://${normalizePortableAuthority(decodePortableAuthority(parsed.host).replace(DID_SCHEME_PATTERN, "did:"))}${normalizePortableComponent(match[3])}${match[5] == null ? "" : normalizePortableComponent(match[5])}`;
66
+ return `ap+ef61://${normalizePortableAuthority(getDidUrlOrigin(decodePortableAuthority(parsed.host)))}${normalizePortableComponent(match[3])}${match[5] == null ? "" : normalizePortableComponent(match[5])}`;
67
67
  }
68
68
  /**
69
69
  * Checks whether two FEP-ef61 portable ActivityPub URIs identify the same
@@ -88,6 +88,45 @@ function arePortableUrisEqual(left, right) {
88
88
  }
89
89
  }
90
90
  /**
91
+ * Computes an IRI's FEP-fe34 origin.
92
+ *
93
+ * HTTP(S) IRIs use their web origin. FEP-ef61 portable ActivityPub IRIs and
94
+ * DID URLs use their DID as a cryptographic origin.
95
+ *
96
+ * @throws {TypeError} If the IRI does not have a supported FEP-fe34 origin.
97
+ * @since 2.4.0
98
+ */
99
+ function getFe34Origin(input) {
100
+ if (input instanceof URL) {
101
+ const portable = normalizePortableUrl(input);
102
+ if (portable != null) return getPortableCryptographicOrigin(portable);
103
+ if (input.protocol === "did:") return getDidUrlOrigin(input.href);
104
+ if (input.protocol === "http:" || input.protocol === "https:") return input.origin;
105
+ throw new TypeError("Unsupported FEP-fe34 origin IRI.");
106
+ }
107
+ const portable = parsePortableIri(input);
108
+ if (portable != null) return getPortableCryptographicOrigin(portable);
109
+ if (DID_SCHEME_PATTERN.test(input)) return getDidUrlOrigin(input);
110
+ const parsed = new URL(input);
111
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") return parsed.origin;
112
+ throw new TypeError("Unsupported FEP-fe34 origin IRI.");
113
+ }
114
+ /**
115
+ * Checks whether two IRIs have the same FEP-fe34 origin.
116
+ *
117
+ * Malformed or unsupported IRIs are treated as non-matching.
118
+ *
119
+ * @since 2.4.0
120
+ */
121
+ function haveSameFe34Origin(left, right) {
122
+ try {
123
+ return getFe34Origin(left) === getFe34Origin(right);
124
+ } catch (error) {
125
+ if (error instanceof TypeError) return false;
126
+ throw error;
127
+ }
128
+ }
129
+ /**
91
130
  * Checks whether two IRIs have the same origin.
92
131
  */
93
132
  function haveSameIriOrigin(left, right) {
@@ -97,15 +136,25 @@ function getComparableIriOrigin(iri) {
97
136
  iri = normalizePortableUrl(iri) ?? iri;
98
137
  if (iri.origin !== "null") return iri.origin;
99
138
  if (iri.host !== "") {
100
- const host = iri.protocol === "ap+ef61:" ? encodeURIComponent(decodePortableAuthority(iri.host).replace(DID_SCHEME_PATTERN, "did:")) : iri.host;
139
+ const host = iri.protocol === "ap+ef61:" ? encodeURIComponent(getDidUrlOrigin(decodePortableAuthority(iri.host))) : iri.host;
101
140
  return `${iri.protocol}//${host}`;
102
141
  }
103
142
  return iri.href;
104
143
  }
144
+ function getPortableCryptographicOrigin(iri) {
145
+ return getDidUrlOrigin(decodePortableAuthority(iri.host));
146
+ }
147
+ function getDidUrlOrigin(iri) {
148
+ const did = iri.split(/[/?#]/, 1)[0].replace(DID_SCHEME_PATTERN, "did:");
149
+ if (!DID_PATTERN.test(did)) throw new TypeError("Invalid DID URL.");
150
+ const parts = did.split(":");
151
+ parts[1] = parts[1].toLowerCase();
152
+ return normalizePortableAuthority(parts.join(":"));
153
+ }
105
154
  function parsePortableIri(iri) {
106
155
  const match = iri.match(PORTABLE_IRI_PATTERN);
107
156
  if (match == null) return null;
108
- const authority = decodePortableAuthority(match[2]);
157
+ const authority = getDidUrlOrigin(decodePortableAuthority(match[2]));
109
158
  if (!DID_PATTERN.test(authority)) throw new TypeError("Invalid portable ActivityPub IRI authority.");
110
159
  if (match[3] === "") throw new TypeError("Invalid portable ActivityPub IRI path.");
111
160
  return new URL(`ap+ef61://${encodeURIComponent(authority)}${match[3]}${match[4] ?? ""}${match[5] ?? ""}`);
@@ -357,6 +406,18 @@ Object.defineProperty(exports, "formatIri", {
357
406
  return formatIri;
358
407
  }
359
408
  });
409
+ Object.defineProperty(exports, "getFe34Origin", {
410
+ enumerable: true,
411
+ get: function() {
412
+ return getFe34Origin;
413
+ }
414
+ });
415
+ Object.defineProperty(exports, "haveSameFe34Origin", {
416
+ enumerable: true,
417
+ get: function() {
418
+ return haveSameFe34Origin;
419
+ }
420
+ });
360
421
  Object.defineProperty(exports, "haveSameIriOrigin", {
361
422
  enumerable: true,
362
423
  get: function() {
@@ -62,7 +62,7 @@ function canonicalizePortableUri(input) {
62
62
  const parsed = parsePortableIri(input);
63
63
  if (parsed == null) throw new TypeError("Invalid portable ActivityPub IRI.");
64
64
  const match = input.match(PORTABLE_IRI_PATTERN);
65
- return `ap+ef61://${normalizePortableAuthority(decodePortableAuthority(parsed.host).replace(DID_SCHEME_PATTERN, "did:"))}${normalizePortableComponent(match[3])}${match[5] == null ? "" : normalizePortableComponent(match[5])}`;
65
+ return `ap+ef61://${normalizePortableAuthority(getDidUrlOrigin(decodePortableAuthority(parsed.host)))}${normalizePortableComponent(match[3])}${match[5] == null ? "" : normalizePortableComponent(match[5])}`;
66
66
  }
67
67
  /**
68
68
  * Checks whether two FEP-ef61 portable ActivityPub URIs identify the same
@@ -87,6 +87,45 @@ function arePortableUrisEqual(left, right) {
87
87
  }
88
88
  }
89
89
  /**
90
+ * Computes an IRI's FEP-fe34 origin.
91
+ *
92
+ * HTTP(S) IRIs use their web origin. FEP-ef61 portable ActivityPub IRIs and
93
+ * DID URLs use their DID as a cryptographic origin.
94
+ *
95
+ * @throws {TypeError} If the IRI does not have a supported FEP-fe34 origin.
96
+ * @since 2.4.0
97
+ */
98
+ function getFe34Origin(input) {
99
+ if (input instanceof URL) {
100
+ const portable = normalizePortableUrl(input);
101
+ if (portable != null) return getPortableCryptographicOrigin(portable);
102
+ if (input.protocol === "did:") return getDidUrlOrigin(input.href);
103
+ if (input.protocol === "http:" || input.protocol === "https:") return input.origin;
104
+ throw new TypeError("Unsupported FEP-fe34 origin IRI.");
105
+ }
106
+ const portable = parsePortableIri(input);
107
+ if (portable != null) return getPortableCryptographicOrigin(portable);
108
+ if (DID_SCHEME_PATTERN.test(input)) return getDidUrlOrigin(input);
109
+ const parsed = new URL(input);
110
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") return parsed.origin;
111
+ throw new TypeError("Unsupported FEP-fe34 origin IRI.");
112
+ }
113
+ /**
114
+ * Checks whether two IRIs have the same FEP-fe34 origin.
115
+ *
116
+ * Malformed or unsupported IRIs are treated as non-matching.
117
+ *
118
+ * @since 2.4.0
119
+ */
120
+ function haveSameFe34Origin(left, right) {
121
+ try {
122
+ return getFe34Origin(left) === getFe34Origin(right);
123
+ } catch (error) {
124
+ if (error instanceof TypeError) return false;
125
+ throw error;
126
+ }
127
+ }
128
+ /**
90
129
  * Checks whether two IRIs have the same origin.
91
130
  */
92
131
  function haveSameIriOrigin(left, right) {
@@ -96,15 +135,25 @@ function getComparableIriOrigin(iri) {
96
135
  iri = normalizePortableUrl(iri) ?? iri;
97
136
  if (iri.origin !== "null") return iri.origin;
98
137
  if (iri.host !== "") {
99
- const host = iri.protocol === "ap+ef61:" ? encodeURIComponent(decodePortableAuthority(iri.host).replace(DID_SCHEME_PATTERN, "did:")) : iri.host;
138
+ const host = iri.protocol === "ap+ef61:" ? encodeURIComponent(getDidUrlOrigin(decodePortableAuthority(iri.host))) : iri.host;
100
139
  return `${iri.protocol}//${host}`;
101
140
  }
102
141
  return iri.href;
103
142
  }
143
+ function getPortableCryptographicOrigin(iri) {
144
+ return getDidUrlOrigin(decodePortableAuthority(iri.host));
145
+ }
146
+ function getDidUrlOrigin(iri) {
147
+ const did = iri.split(/[/?#]/, 1)[0].replace(DID_SCHEME_PATTERN, "did:");
148
+ if (!DID_PATTERN.test(did)) throw new TypeError("Invalid DID URL.");
149
+ const parts = did.split(":");
150
+ parts[1] = parts[1].toLowerCase();
151
+ return normalizePortableAuthority(parts.join(":"));
152
+ }
104
153
  function parsePortableIri(iri) {
105
154
  const match = iri.match(PORTABLE_IRI_PATTERN);
106
155
  if (match == null) return null;
107
- const authority = decodePortableAuthority(match[2]);
156
+ const authority = getDidUrlOrigin(decodePortableAuthority(match[2]));
108
157
  if (!DID_PATTERN.test(authority)) throw new TypeError("Invalid portable ActivityPub IRI authority.");
109
158
  if (match[3] === "") throw new TypeError("Invalid portable ActivityPub IRI path.");
110
159
  return new URL(`ap+ef61://${encodeURIComponent(authority)}${match[3]}${match[4] ?? ""}${match[5] ?? ""}`);
@@ -326,4 +375,4 @@ function matchesIPv6Prefix(address, prefixWords, prefixLength) {
326
375
  return true;
327
376
  }
328
377
  //#endregion
329
- export { formatIri as a, isValidPublicIPv6Address as c, validatePublicUrl as d, expandIPv6Address as i, parseIri as l, arePortableUrisEqual as n, haveSameIriOrigin as o, canonicalizePortableUri as r, isValidPublicIPv4Address as s, UrlError as t, parseJsonLdId as u };
378
+ export { formatIri as a, haveSameIriOrigin as c, parseIri as d, parseJsonLdId as f, expandIPv6Address as i, isValidPublicIPv4Address as l, arePortableUrisEqual as n, getFe34Origin as o, validatePublicUrl as p, canonicalizePortableUri as r, haveSameFe34Origin as s, UrlError as t, isValidPublicIPv6Address as u };
@@ -1,5 +1,5 @@
1
1
  require("./chunk-C2EiDwsr.cjs");
2
- const require_url = require("./url-CtKcX6ma.cjs");
2
+ const require_url = require("./url-BvjYQdxL.cjs");
3
3
  let node_assert = require("node:assert");
4
4
  let node_test = require("node:test");
5
5
  //#region src/url.test.ts
@@ -12,8 +12,12 @@ let node_test = require("node:test");
12
12
  "AP+EF61://did:key:z6Mkabc/actor"
13
13
  ]) (0, node_assert.deepStrictEqual)(require_url.parseIri(iri), new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"));
14
14
  });
15
- (0, node_test.test)("parseIri() accepts DID schemes case-insensitively", () => {
16
- for (const iri of ["ap://DID:key:z6Mkabc/actor", "ap://DID%3Akey%3Az6Mkabc/actor"]) (0, node_assert.deepStrictEqual)(require_url.parseIri(iri), new URL("ap+ef61://DID%3Akey%3Az6Mkabc/actor"));
15
+ (0, node_test.test)("parseIri() normalizes DID scheme and method casing", () => {
16
+ for (const iri of [
17
+ "ap://DID:key:z6Mkabc/actor",
18
+ "ap://DID%3Akey%3Az6Mkabc/actor",
19
+ "ap://did:KEY:z6Mkabc/actor"
20
+ ]) (0, node_assert.deepStrictEqual)(require_url.parseIri(iri), new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"));
17
21
  });
18
22
  (0, node_test.test)("parseIri() accepts DID method names that start with digits", () => {
19
23
  (0, node_assert.deepStrictEqual)(require_url.parseIri("ap://did:3:abc/actor"), new URL("ap+ef61://did%3A3%3Aabc/actor"));
@@ -60,12 +64,50 @@ let node_test = require("node:test");
60
64
  ]) (0, node_assert.throws)(() => require_url.parseIri(iri), TypeError);
61
65
  });
62
66
  (0, node_test.test)("haveSameIriOrigin() compares portable IRI authorities", () => {
67
+ (0, node_assert.ok)(require_url.haveSameIriOrigin(new URL("ftp://example.com/pub/1"), new URL("ftp://example.com/pub/2")));
68
+ (0, node_assert.ok)(require_url.haveSameIriOrigin(new URL("mailto:alice@example.com"), new URL("mailto:alice@example.com")));
63
69
  (0, node_assert.ok)(require_url.haveSameIriOrigin(require_url.parseIri("ap://did:key:z6Mkabc/actor"), require_url.parseIri("ap://did:key:z6Mkabc/outbox")));
64
70
  (0, node_assert.ok)(require_url.haveSameIriOrigin(new URL("ap://did%3Akey%3Az6Mkabc/actor"), new URL("ap+ef61://did%3Akey%3Az6Mkabc/outbox")));
65
71
  (0, node_assert.ok)(require_url.haveSameIriOrigin(require_url.parseIri("ap://DID:key:z6Mkabc/actor"), require_url.parseIri("ap://did:key:z6Mkabc/outbox")));
66
72
  (0, node_assert.ok)(require_url.haveSameIriOrigin(new URL("ap+ef61://DID%3Akey%3Az6Mkabc/actor"), new URL("ap+ef61://did%3Akey%3Az6Mkabc/outbox")));
67
73
  (0, node_assert.ok)(!require_url.haveSameIriOrigin(require_url.parseIri("ap://did:key:z6Mkabc/actor"), require_url.parseIri("ap://did:key:z6Mkdef/actor")));
68
74
  });
75
+ (0, node_test.test)("getFe34Origin() computes web and cryptographic origins", () => {
76
+ (0, node_assert.deepStrictEqual)(require_url.getFe34Origin("https://Example.COM:443/users/alice"), "https://example.com");
77
+ (0, node_assert.deepStrictEqual)(require_url.getFe34Origin(new URL("http://example.com:8080/notes/1")), "http://example.com:8080");
78
+ (0, node_assert.deepStrictEqual)(require_url.getFe34Origin("ap://did:key:z6Mkabc/actor?gateways=https%3A%2F%2Fa.example"), "did:key:z6Mkabc");
79
+ (0, node_assert.deepStrictEqual)(require_url.getFe34Origin("ap+ef61://did%3Akey%3Az6Mkabc/objects/1#fragment"), "did:key:z6Mkabc");
80
+ (0, node_assert.deepStrictEqual)(require_url.getFe34Origin(new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor")), "did:key:z6Mkabc");
81
+ (0, node_assert.deepStrictEqual)(require_url.getFe34Origin("did:key:z6Mkabc#z6Mkabc"), "did:key:z6Mkabc");
82
+ (0, node_assert.deepStrictEqual)(require_url.getFe34Origin(new URL("did:key:z6Mkabc?service=activitypub#key")), "did:key:z6Mkabc");
83
+ (0, node_assert.deepStrictEqual)(require_url.getFe34Origin("did:key:z6Mkabc/path/to/resource?service=activitypub#key"), "did:key:z6Mkabc");
84
+ (0, node_assert.deepStrictEqual)(require_url.getFe34Origin("did:KEY:z6Mkabc#z6Mkabc"), "did:key:z6Mkabc");
85
+ (0, node_assert.deepStrictEqual)(require_url.getFe34Origin("ap://did%3Aweb%3Afoo%2Dbar.example/actor"), "did:web:foo-bar.example");
86
+ (0, node_assert.deepStrictEqual)(require_url.getFe34Origin("did:web:foo%2dbar.example#key"), "did:web:foo-bar.example");
87
+ });
88
+ (0, node_test.test)("getFe34Origin() rejects unsupported or malformed identifiers", () => {
89
+ for (const iri of [
90
+ "mailto:alice@example.com",
91
+ "ap://not-a-did/actor",
92
+ "ap://did:key/actor",
93
+ "did:key",
94
+ "did:key:",
95
+ "did:"
96
+ ]) (0, node_assert.throws)(() => require_url.getFe34Origin(iri), TypeError);
97
+ });
98
+ (0, node_test.test)("haveSameFe34Origin() compares web and cryptographic origins", () => {
99
+ (0, node_assert.ok)(require_url.haveSameFe34Origin("https://example.com/users/alice", "https://example.com/notes/1"));
100
+ (0, node_assert.ok)(!require_url.haveSameFe34Origin("https://example.com/users/alice", "http://example.com/users/alice"));
101
+ (0, node_assert.ok)(!require_url.haveSameFe34Origin("https://example.com/users/alice", "https://example.com:8443/users/alice"));
102
+ (0, node_assert.ok)(require_url.haveSameFe34Origin("ap://did:key:z6Mkabc/actor", "ap+ef61://did%3Akey%3Az6Mkabc/objects/1"));
103
+ (0, node_assert.ok)(require_url.haveSameFe34Origin("ap+ef61://did:key:z6Mkabc/actor", "did:key:z6Mkabc#z6Mkabc"));
104
+ (0, node_assert.ok)(require_url.haveSameFe34Origin("ap://did:KEY:z6Mkabc/actor", "did:key:z6Mkabc#z6Mkabc"));
105
+ (0, node_assert.ok)(require_url.haveSameFe34Origin("ap://did%3Aweb%3Afoo%2Dbar.example/actor", "ap://did:web:foo-bar.example/note"));
106
+ (0, node_assert.ok)(require_url.haveSameFe34Origin("ap://did%3Aexample%3Aabc%252fdef/actor", "did:example:abc%2Fdef#key"));
107
+ (0, node_assert.ok)(!require_url.haveSameFe34Origin("ap+ef61://did:key:z6Mkabc/actor", "did:key:z6Mkdef#z6Mkdef"));
108
+ (0, node_assert.ok)(!require_url.haveSameFe34Origin("ap+ef61://did:key:z6Mkabc/actor", "https://example.com/actor"));
109
+ (0, node_assert.ok)(!require_url.haveSameFe34Origin("ap://not-a-did/actor", "ap://not-a-did/actor"));
110
+ });
69
111
  (0, node_test.test)("parseIri() normalizes portable URL instances", () => {
70
112
  (0, node_assert.deepStrictEqual)(require_url.parseIri(new URL("ap+ef61://did%3Aexample%3Aabc%2Fdef/actor")), new URL("ap+ef61://did%3Aexample%3Aabc%252Fdef/actor"));
71
113
  (0, node_assert.throws)(() => require_url.parseIri("ap+ef61://not-a-did/actor"), TypeError);
@@ -1,4 +1,4 @@
1
- import { a as formatIri, c as isValidPublicIPv6Address, d as validatePublicUrl, i as expandIPv6Address, l as parseIri, n as arePortableUrisEqual, o as haveSameIriOrigin, r as canonicalizePortableUri, s as isValidPublicIPv4Address, t as UrlError, u as parseJsonLdId } from "./url-D7ay_5pk.mjs";
1
+ import { a as formatIri, c as haveSameIriOrigin, d as parseIri, f as parseJsonLdId, i as expandIPv6Address, l as isValidPublicIPv4Address, n as arePortableUrisEqual, o as getFe34Origin, p as validatePublicUrl, r as canonicalizePortableUri, s as haveSameFe34Origin, t as UrlError, u as isValidPublicIPv6Address } from "./url-a2D8NAgh.mjs";
2
2
  import { deepStrictEqual, ok, rejects, throws } from "node:assert";
3
3
  import { test } from "node:test";
4
4
  //#region src/url.test.ts
@@ -11,8 +11,12 @@ test("parseIri() accepts portable ActivityPub URI schemes", () => {
11
11
  "AP+EF61://did:key:z6Mkabc/actor"
12
12
  ]) deepStrictEqual(parseIri(iri), new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"));
13
13
  });
14
- test("parseIri() accepts DID schemes case-insensitively", () => {
15
- for (const iri of ["ap://DID:key:z6Mkabc/actor", "ap://DID%3Akey%3Az6Mkabc/actor"]) deepStrictEqual(parseIri(iri), new URL("ap+ef61://DID%3Akey%3Az6Mkabc/actor"));
14
+ test("parseIri() normalizes DID scheme and method casing", () => {
15
+ for (const iri of [
16
+ "ap://DID:key:z6Mkabc/actor",
17
+ "ap://DID%3Akey%3Az6Mkabc/actor",
18
+ "ap://did:KEY:z6Mkabc/actor"
19
+ ]) deepStrictEqual(parseIri(iri), new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"));
16
20
  });
17
21
  test("parseIri() accepts DID method names that start with digits", () => {
18
22
  deepStrictEqual(parseIri("ap://did:3:abc/actor"), new URL("ap+ef61://did%3A3%3Aabc/actor"));
@@ -59,12 +63,50 @@ test("parseIri() rejects malformed portable DID authorities", () => {
59
63
  ]) throws(() => parseIri(iri), TypeError);
60
64
  });
61
65
  test("haveSameIriOrigin() compares portable IRI authorities", () => {
66
+ ok(haveSameIriOrigin(new URL("ftp://example.com/pub/1"), new URL("ftp://example.com/pub/2")));
67
+ ok(haveSameIriOrigin(new URL("mailto:alice@example.com"), new URL("mailto:alice@example.com")));
62
68
  ok(haveSameIriOrigin(parseIri("ap://did:key:z6Mkabc/actor"), parseIri("ap://did:key:z6Mkabc/outbox")));
63
69
  ok(haveSameIriOrigin(new URL("ap://did%3Akey%3Az6Mkabc/actor"), new URL("ap+ef61://did%3Akey%3Az6Mkabc/outbox")));
64
70
  ok(haveSameIriOrigin(parseIri("ap://DID:key:z6Mkabc/actor"), parseIri("ap://did:key:z6Mkabc/outbox")));
65
71
  ok(haveSameIriOrigin(new URL("ap+ef61://DID%3Akey%3Az6Mkabc/actor"), new URL("ap+ef61://did%3Akey%3Az6Mkabc/outbox")));
66
72
  ok(!haveSameIriOrigin(parseIri("ap://did:key:z6Mkabc/actor"), parseIri("ap://did:key:z6Mkdef/actor")));
67
73
  });
74
+ test("getFe34Origin() computes web and cryptographic origins", () => {
75
+ deepStrictEqual(getFe34Origin("https://Example.COM:443/users/alice"), "https://example.com");
76
+ deepStrictEqual(getFe34Origin(new URL("http://example.com:8080/notes/1")), "http://example.com:8080");
77
+ deepStrictEqual(getFe34Origin("ap://did:key:z6Mkabc/actor?gateways=https%3A%2F%2Fa.example"), "did:key:z6Mkabc");
78
+ deepStrictEqual(getFe34Origin("ap+ef61://did%3Akey%3Az6Mkabc/objects/1#fragment"), "did:key:z6Mkabc");
79
+ deepStrictEqual(getFe34Origin(new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor")), "did:key:z6Mkabc");
80
+ deepStrictEqual(getFe34Origin("did:key:z6Mkabc#z6Mkabc"), "did:key:z6Mkabc");
81
+ deepStrictEqual(getFe34Origin(new URL("did:key:z6Mkabc?service=activitypub#key")), "did:key:z6Mkabc");
82
+ deepStrictEqual(getFe34Origin("did:key:z6Mkabc/path/to/resource?service=activitypub#key"), "did:key:z6Mkabc");
83
+ deepStrictEqual(getFe34Origin("did:KEY:z6Mkabc#z6Mkabc"), "did:key:z6Mkabc");
84
+ deepStrictEqual(getFe34Origin("ap://did%3Aweb%3Afoo%2Dbar.example/actor"), "did:web:foo-bar.example");
85
+ deepStrictEqual(getFe34Origin("did:web:foo%2dbar.example#key"), "did:web:foo-bar.example");
86
+ });
87
+ test("getFe34Origin() rejects unsupported or malformed identifiers", () => {
88
+ for (const iri of [
89
+ "mailto:alice@example.com",
90
+ "ap://not-a-did/actor",
91
+ "ap://did:key/actor",
92
+ "did:key",
93
+ "did:key:",
94
+ "did:"
95
+ ]) throws(() => getFe34Origin(iri), TypeError);
96
+ });
97
+ test("haveSameFe34Origin() compares web and cryptographic origins", () => {
98
+ ok(haveSameFe34Origin("https://example.com/users/alice", "https://example.com/notes/1"));
99
+ ok(!haveSameFe34Origin("https://example.com/users/alice", "http://example.com/users/alice"));
100
+ ok(!haveSameFe34Origin("https://example.com/users/alice", "https://example.com:8443/users/alice"));
101
+ ok(haveSameFe34Origin("ap://did:key:z6Mkabc/actor", "ap+ef61://did%3Akey%3Az6Mkabc/objects/1"));
102
+ ok(haveSameFe34Origin("ap+ef61://did:key:z6Mkabc/actor", "did:key:z6Mkabc#z6Mkabc"));
103
+ ok(haveSameFe34Origin("ap://did:KEY:z6Mkabc/actor", "did:key:z6Mkabc#z6Mkabc"));
104
+ ok(haveSameFe34Origin("ap://did%3Aweb%3Afoo%2Dbar.example/actor", "ap://did:web:foo-bar.example/note"));
105
+ ok(haveSameFe34Origin("ap://did%3Aexample%3Aabc%252fdef/actor", "did:example:abc%2Fdef#key"));
106
+ ok(!haveSameFe34Origin("ap+ef61://did:key:z6Mkabc/actor", "did:key:z6Mkdef#z6Mkdef"));
107
+ ok(!haveSameFe34Origin("ap+ef61://did:key:z6Mkabc/actor", "https://example.com/actor"));
108
+ ok(!haveSameFe34Origin("ap://not-a-did/actor", "ap://not-a-did/actor"));
109
+ });
68
110
  test("parseIri() normalizes portable URL instances", () => {
69
111
  deepStrictEqual(parseIri(new URL("ap+ef61://did%3Aexample%3Aabc%2Fdef/actor")), new URL("ap+ef61://did%3Aexample%3Aabc%252Fdef/actor"));
70
112
  throws(() => parseIri("ap+ef61://not-a-did/actor"), TypeError);
@@ -64,7 +64,7 @@ function canonicalizePortableUri(input) {
64
64
  const parsed = parsePortableIri(input);
65
65
  if (parsed == null) throw new TypeError("Invalid portable ActivityPub IRI.");
66
66
  const match = input.match(PORTABLE_IRI_PATTERN);
67
- return `ap+ef61://${normalizePortableAuthority(decodePortableAuthority(parsed.host).replace(DID_SCHEME_PATTERN, "did:"))}${normalizePortableComponent(match[3])}${match[5] == null ? "" : normalizePortableComponent(match[5])}`;
67
+ return `ap+ef61://${normalizePortableAuthority(getDidUrlOrigin(decodePortableAuthority(parsed.host)))}${normalizePortableComponent(match[3])}${match[5] == null ? "" : normalizePortableComponent(match[5])}`;
68
68
  }
69
69
  /**
70
70
  * Checks whether two FEP-ef61 portable ActivityPub URIs identify the same
@@ -89,6 +89,45 @@ function arePortableUrisEqual(left, right) {
89
89
  }
90
90
  }
91
91
  /**
92
+ * Computes an IRI's FEP-fe34 origin.
93
+ *
94
+ * HTTP(S) IRIs use their web origin. FEP-ef61 portable ActivityPub IRIs and
95
+ * DID URLs use their DID as a cryptographic origin.
96
+ *
97
+ * @throws {TypeError} If the IRI does not have a supported FEP-fe34 origin.
98
+ * @since 2.4.0
99
+ */
100
+ function getFe34Origin(input) {
101
+ if (input instanceof URL) {
102
+ const portable = normalizePortableUrl(input);
103
+ if (portable != null) return getPortableCryptographicOrigin(portable);
104
+ if (input.protocol === "did:") return getDidUrlOrigin(input.href);
105
+ if (input.protocol === "http:" || input.protocol === "https:") return input.origin;
106
+ throw new TypeError("Unsupported FEP-fe34 origin IRI.");
107
+ }
108
+ const portable = parsePortableIri(input);
109
+ if (portable != null) return getPortableCryptographicOrigin(portable);
110
+ if (DID_SCHEME_PATTERN.test(input)) return getDidUrlOrigin(input);
111
+ const parsed = new URL(input);
112
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") return parsed.origin;
113
+ throw new TypeError("Unsupported FEP-fe34 origin IRI.");
114
+ }
115
+ /**
116
+ * Checks whether two IRIs have the same FEP-fe34 origin.
117
+ *
118
+ * Malformed or unsupported IRIs are treated as non-matching.
119
+ *
120
+ * @since 2.4.0
121
+ */
122
+ function haveSameFe34Origin(left, right) {
123
+ try {
124
+ return getFe34Origin(left) === getFe34Origin(right);
125
+ } catch (error) {
126
+ if (error instanceof TypeError) return false;
127
+ throw error;
128
+ }
129
+ }
130
+ /**
92
131
  * Checks whether two IRIs have the same origin.
93
132
  */
94
133
  function haveSameIriOrigin(left, right) {
@@ -98,15 +137,25 @@ function getComparableIriOrigin(iri) {
98
137
  iri = normalizePortableUrl(iri) ?? iri;
99
138
  if (iri.origin !== "null") return iri.origin;
100
139
  if (iri.host !== "") {
101
- const host = iri.protocol === "ap+ef61:" ? encodeURIComponent(decodePortableAuthority(iri.host).replace(DID_SCHEME_PATTERN, "did:")) : iri.host;
140
+ const host = iri.protocol === "ap+ef61:" ? encodeURIComponent(getDidUrlOrigin(decodePortableAuthority(iri.host))) : iri.host;
102
141
  return `${iri.protocol}//${host}`;
103
142
  }
104
143
  return iri.href;
105
144
  }
145
+ function getPortableCryptographicOrigin(iri) {
146
+ return getDidUrlOrigin(decodePortableAuthority(iri.host));
147
+ }
148
+ function getDidUrlOrigin(iri) {
149
+ const did = iri.split(/[/?#]/, 1)[0].replace(DID_SCHEME_PATTERN, "did:");
150
+ if (!DID_PATTERN.test(did)) throw new TypeError("Invalid DID URL.");
151
+ const parts = did.split(":");
152
+ parts[1] = parts[1].toLowerCase();
153
+ return normalizePortableAuthority(parts.join(":"));
154
+ }
106
155
  function parsePortableIri(iri) {
107
156
  const match = iri.match(PORTABLE_IRI_PATTERN);
108
157
  if (match == null) return null;
109
- const authority = decodePortableAuthority(match[2]);
158
+ const authority = getDidUrlOrigin(decodePortableAuthority(match[2]));
110
159
  if (!DID_PATTERN.test(authority)) throw new TypeError("Invalid portable ActivityPub IRI authority.");
111
160
  if (match[3] === "") throw new TypeError("Invalid portable ActivityPub IRI path.");
112
161
  return new URL(`ap+ef61://${encodeURIComponent(authority)}${match[3]}${match[4] ?? ""}${match[5] ?? ""}`);
@@ -358,6 +407,18 @@ Object.defineProperty(exports, "formatIri", {
358
407
  return formatIri;
359
408
  }
360
409
  });
410
+ Object.defineProperty(exports, "getFe34Origin", {
411
+ enumerable: true,
412
+ get: function() {
413
+ return getFe34Origin;
414
+ }
415
+ });
416
+ Object.defineProperty(exports, "haveSameFe34Origin", {
417
+ enumerable: true,
418
+ get: function() {
419
+ return haveSameFe34Origin;
420
+ }
421
+ });
361
422
  Object.defineProperty(exports, "haveSameIriOrigin", {
362
423
  enumerable: true,
363
424
  get: function() {
@@ -63,7 +63,7 @@ function canonicalizePortableUri(input) {
63
63
  const parsed = parsePortableIri(input);
64
64
  if (parsed == null) throw new TypeError("Invalid portable ActivityPub IRI.");
65
65
  const match = input.match(PORTABLE_IRI_PATTERN);
66
- return `ap+ef61://${normalizePortableAuthority(decodePortableAuthority(parsed.host).replace(DID_SCHEME_PATTERN, "did:"))}${normalizePortableComponent(match[3])}${match[5] == null ? "" : normalizePortableComponent(match[5])}`;
66
+ return `ap+ef61://${normalizePortableAuthority(getDidUrlOrigin(decodePortableAuthority(parsed.host)))}${normalizePortableComponent(match[3])}${match[5] == null ? "" : normalizePortableComponent(match[5])}`;
67
67
  }
68
68
  /**
69
69
  * Checks whether two FEP-ef61 portable ActivityPub URIs identify the same
@@ -88,6 +88,45 @@ function arePortableUrisEqual(left, right) {
88
88
  }
89
89
  }
90
90
  /**
91
+ * Computes an IRI's FEP-fe34 origin.
92
+ *
93
+ * HTTP(S) IRIs use their web origin. FEP-ef61 portable ActivityPub IRIs and
94
+ * DID URLs use their DID as a cryptographic origin.
95
+ *
96
+ * @throws {TypeError} If the IRI does not have a supported FEP-fe34 origin.
97
+ * @since 2.4.0
98
+ */
99
+ function getFe34Origin(input) {
100
+ if (input instanceof URL) {
101
+ const portable = normalizePortableUrl(input);
102
+ if (portable != null) return getPortableCryptographicOrigin(portable);
103
+ if (input.protocol === "did:") return getDidUrlOrigin(input.href);
104
+ if (input.protocol === "http:" || input.protocol === "https:") return input.origin;
105
+ throw new TypeError("Unsupported FEP-fe34 origin IRI.");
106
+ }
107
+ const portable = parsePortableIri(input);
108
+ if (portable != null) return getPortableCryptographicOrigin(portable);
109
+ if (DID_SCHEME_PATTERN.test(input)) return getDidUrlOrigin(input);
110
+ const parsed = new URL(input);
111
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") return parsed.origin;
112
+ throw new TypeError("Unsupported FEP-fe34 origin IRI.");
113
+ }
114
+ /**
115
+ * Checks whether two IRIs have the same FEP-fe34 origin.
116
+ *
117
+ * Malformed or unsupported IRIs are treated as non-matching.
118
+ *
119
+ * @since 2.4.0
120
+ */
121
+ function haveSameFe34Origin(left, right) {
122
+ try {
123
+ return getFe34Origin(left) === getFe34Origin(right);
124
+ } catch (error) {
125
+ if (error instanceof TypeError) return false;
126
+ throw error;
127
+ }
128
+ }
129
+ /**
91
130
  * Checks whether two IRIs have the same origin.
92
131
  */
93
132
  function haveSameIriOrigin(left, right) {
@@ -97,15 +136,25 @@ function getComparableIriOrigin(iri) {
97
136
  iri = normalizePortableUrl(iri) ?? iri;
98
137
  if (iri.origin !== "null") return iri.origin;
99
138
  if (iri.host !== "") {
100
- const host = iri.protocol === "ap+ef61:" ? encodeURIComponent(decodePortableAuthority(iri.host).replace(DID_SCHEME_PATTERN, "did:")) : iri.host;
139
+ const host = iri.protocol === "ap+ef61:" ? encodeURIComponent(getDidUrlOrigin(decodePortableAuthority(iri.host))) : iri.host;
101
140
  return `${iri.protocol}//${host}`;
102
141
  }
103
142
  return iri.href;
104
143
  }
144
+ function getPortableCryptographicOrigin(iri) {
145
+ return getDidUrlOrigin(decodePortableAuthority(iri.host));
146
+ }
147
+ function getDidUrlOrigin(iri) {
148
+ const did = iri.split(/[/?#]/, 1)[0].replace(DID_SCHEME_PATTERN, "did:");
149
+ if (!DID_PATTERN.test(did)) throw new TypeError("Invalid DID URL.");
150
+ const parts = did.split(":");
151
+ parts[1] = parts[1].toLowerCase();
152
+ return normalizePortableAuthority(parts.join(":"));
153
+ }
105
154
  function parsePortableIri(iri) {
106
155
  const match = iri.match(PORTABLE_IRI_PATTERN);
107
156
  if (match == null) return null;
108
- const authority = decodePortableAuthority(match[2]);
157
+ const authority = getDidUrlOrigin(decodePortableAuthority(match[2]));
109
158
  if (!DID_PATTERN.test(authority)) throw new TypeError("Invalid portable ActivityPub IRI authority.");
110
159
  if (match[3] === "") throw new TypeError("Invalid portable ActivityPub IRI path.");
111
160
  return new URL(`ap+ef61://${encodeURIComponent(authority)}${match[3]}${match[4] ?? ""}${match[5] ?? ""}`);
@@ -327,4 +376,4 @@ function matchesIPv6Prefix(address, prefixWords, prefixLength) {
327
376
  return true;
328
377
  }
329
378
  //#endregion
330
- export { formatIri as a, isValidPublicIPv6Address as c, validatePublicUrl as d, expandIPv6Address as i, parseIri as l, arePortableUrisEqual as n, haveSameIriOrigin as o, canonicalizePortableUri as r, isValidPublicIPv4Address as s, UrlError as t, parseJsonLdId as u };
379
+ export { formatIri as a, haveSameIriOrigin as c, parseIri as d, parseJsonLdId as f, expandIPv6Address as i, isValidPublicIPv4Address as l, arePortableUrisEqual as n, getFe34Origin as o, validatePublicUrl as p, canonicalizePortableUri as r, haveSameFe34Origin as s, UrlError as t, isValidPublicIPv6Address as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab-runtime",
3
- "version": "2.4.0-dev.1599+7b19967f",
3
+ "version": "2.4.0-dev.1618+58858243",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,6 +1,6 @@
1
1
  import type { DocumentLoader } from "../docloader.ts";
2
2
  import jsonld from "../jsonld.ts";
3
- import { formatIri, haveSameIriOrigin } from "../url.ts";
3
+ import { formatIri, haveSameFe34Origin, haveSameIriOrigin } from "../url.ts";
4
4
 
5
5
  const noJsonLdContext = Symbol("noJsonLdContext");
6
6
 
@@ -28,7 +28,8 @@ export function isTrustedIriOrigin(
28
28
  right: URL | null | undefined,
29
29
  ): boolean {
30
30
  return options.crossOrigin === "trust" || left == null ||
31
- (right != null && haveSameIriOrigin(left, right));
31
+ (right != null &&
32
+ (haveSameIriOrigin(left, right) || haveSameFe34Origin(left, right)));
32
33
  }
33
34
 
34
35
  /**
package/src/mod.ts CHANGED
@@ -58,6 +58,8 @@ export {
58
58
  canonicalizePortableUri,
59
59
  expandIPv6Address,
60
60
  formatIri,
61
+ getFe34Origin,
62
+ haveSameFe34Origin,
61
63
  haveSameIriOrigin,
62
64
  isValidPublicIPv4Address,
63
65
  isValidPublicIPv6Address,
package/src/url.test.ts CHANGED
@@ -5,6 +5,8 @@ import {
5
5
  canonicalizePortableUri,
6
6
  expandIPv6Address,
7
7
  formatIri,
8
+ getFe34Origin,
9
+ haveSameFe34Origin,
8
10
  haveSameIriOrigin,
9
11
  isValidPublicIPv4Address,
10
12
  isValidPublicIPv6Address,
@@ -30,15 +32,16 @@ test("parseIri() accepts portable ActivityPub URI schemes", () => {
30
32
  }
31
33
  });
32
34
 
33
- test("parseIri() accepts DID schemes case-insensitively", () => {
35
+ test("parseIri() normalizes DID scheme and method casing", () => {
34
36
  const cases = [
35
37
  "ap://DID:key:z6Mkabc/actor",
36
38
  "ap://DID%3Akey%3Az6Mkabc/actor",
39
+ "ap://did:KEY:z6Mkabc/actor",
37
40
  ];
38
41
  for (const iri of cases) {
39
42
  deepStrictEqual(
40
43
  parseIri(iri),
41
- new URL("ap+ef61://DID%3Akey%3Az6Mkabc/actor"),
44
+ new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"),
42
45
  );
43
46
  }
44
47
  });
@@ -139,6 +142,14 @@ test("parseIri() rejects malformed portable DID authorities", () => {
139
142
  });
140
143
 
141
144
  test("haveSameIriOrigin() compares portable IRI authorities", () => {
145
+ ok(haveSameIriOrigin(
146
+ new URL("ftp://example.com/pub/1"),
147
+ new URL("ftp://example.com/pub/2"),
148
+ ));
149
+ ok(haveSameIriOrigin(
150
+ new URL("mailto:alice@example.com"),
151
+ new URL("mailto:alice@example.com"),
152
+ ));
142
153
  ok(haveSameIriOrigin(
143
154
  parseIri("ap://did:key:z6Mkabc/actor"),
144
155
  parseIri("ap://did:key:z6Mkabc/outbox"),
@@ -163,6 +174,122 @@ test("haveSameIriOrigin() compares portable IRI authorities", () => {
163
174
  );
164
175
  });
165
176
 
177
+ test("getFe34Origin() computes web and cryptographic origins", () => {
178
+ deepStrictEqual(
179
+ getFe34Origin("https://Example.COM:443/users/alice"),
180
+ "https://example.com",
181
+ );
182
+ deepStrictEqual(
183
+ getFe34Origin(new URL("http://example.com:8080/notes/1")),
184
+ "http://example.com:8080",
185
+ );
186
+ deepStrictEqual(
187
+ getFe34Origin(
188
+ "ap://did:key:z6Mkabc/actor?gateways=https%3A%2F%2Fa.example",
189
+ ),
190
+ "did:key:z6Mkabc",
191
+ );
192
+ deepStrictEqual(
193
+ getFe34Origin("ap+ef61://did%3Akey%3Az6Mkabc/objects/1#fragment"),
194
+ "did:key:z6Mkabc",
195
+ );
196
+ deepStrictEqual(
197
+ getFe34Origin(new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor")),
198
+ "did:key:z6Mkabc",
199
+ );
200
+ deepStrictEqual(
201
+ getFe34Origin("did:key:z6Mkabc#z6Mkabc"),
202
+ "did:key:z6Mkabc",
203
+ );
204
+ deepStrictEqual(
205
+ getFe34Origin(new URL("did:key:z6Mkabc?service=activitypub#key")),
206
+ "did:key:z6Mkabc",
207
+ );
208
+ deepStrictEqual(
209
+ getFe34Origin("did:key:z6Mkabc/path/to/resource?service=activitypub#key"),
210
+ "did:key:z6Mkabc",
211
+ );
212
+ deepStrictEqual(
213
+ getFe34Origin("did:KEY:z6Mkabc#z6Mkabc"),
214
+ "did:key:z6Mkabc",
215
+ );
216
+ deepStrictEqual(
217
+ getFe34Origin("ap://did%3Aweb%3Afoo%2Dbar.example/actor"),
218
+ "did:web:foo-bar.example",
219
+ );
220
+ deepStrictEqual(
221
+ getFe34Origin("did:web:foo%2dbar.example#key"),
222
+ "did:web:foo-bar.example",
223
+ );
224
+ });
225
+
226
+ test("getFe34Origin() rejects unsupported or malformed identifiers", () => {
227
+ for (
228
+ const iri of [
229
+ "mailto:alice@example.com",
230
+ "ap://not-a-did/actor",
231
+ "ap://did:key/actor",
232
+ "did:key",
233
+ "did:key:",
234
+ "did:",
235
+ ]
236
+ ) {
237
+ throws(() => getFe34Origin(iri), TypeError);
238
+ }
239
+ });
240
+
241
+ test("haveSameFe34Origin() compares web and cryptographic origins", () => {
242
+ ok(haveSameFe34Origin(
243
+ "https://example.com/users/alice",
244
+ "https://example.com/notes/1",
245
+ ));
246
+ ok(
247
+ !haveSameFe34Origin(
248
+ "https://example.com/users/alice",
249
+ "http://example.com/users/alice",
250
+ ),
251
+ );
252
+ ok(
253
+ !haveSameFe34Origin(
254
+ "https://example.com/users/alice",
255
+ "https://example.com:8443/users/alice",
256
+ ),
257
+ );
258
+ ok(haveSameFe34Origin(
259
+ "ap://did:key:z6Mkabc/actor",
260
+ "ap+ef61://did%3Akey%3Az6Mkabc/objects/1",
261
+ ));
262
+ ok(haveSameFe34Origin(
263
+ "ap+ef61://did:key:z6Mkabc/actor",
264
+ "did:key:z6Mkabc#z6Mkabc",
265
+ ));
266
+ ok(haveSameFe34Origin(
267
+ "ap://did:KEY:z6Mkabc/actor",
268
+ "did:key:z6Mkabc#z6Mkabc",
269
+ ));
270
+ ok(haveSameFe34Origin(
271
+ "ap://did%3Aweb%3Afoo%2Dbar.example/actor",
272
+ "ap://did:web:foo-bar.example/note",
273
+ ));
274
+ ok(haveSameFe34Origin(
275
+ "ap://did%3Aexample%3Aabc%252fdef/actor",
276
+ "did:example:abc%2Fdef#key",
277
+ ));
278
+ ok(
279
+ !haveSameFe34Origin(
280
+ "ap+ef61://did:key:z6Mkabc/actor",
281
+ "did:key:z6Mkdef#z6Mkdef",
282
+ ),
283
+ );
284
+ ok(
285
+ !haveSameFe34Origin(
286
+ "ap+ef61://did:key:z6Mkabc/actor",
287
+ "https://example.com/actor",
288
+ ),
289
+ );
290
+ ok(!haveSameFe34Origin("ap://not-a-did/actor", "ap://not-a-did/actor"));
291
+ });
292
+
166
293
  test("parseIri() normalizes portable URL instances", () => {
167
294
  deepStrictEqual(
168
295
  parseIri(new URL("ap+ef61://did%3Aexample%3Aabc%2Fdef/actor")),
package/src/url.ts CHANGED
@@ -93,7 +93,7 @@ export function canonicalizePortableUri(input: string): string {
93
93
  // decodePortableAuthority() reverses the shared percent-encoded authority
94
94
  // path here rather than the raw did:-prefixed branch.
95
95
  const authority = normalizePortableAuthority(
96
- decodePortableAuthority(parsed.host).replace(DID_SCHEME_PATTERN, "did:"),
96
+ getDidUrlOrigin(decodePortableAuthority(parsed.host)),
97
97
  );
98
98
  // Keep path and fragment text from the raw match to avoid URL dot-segment
99
99
  // normalization, but still encode raw characters and normalize
@@ -131,6 +131,56 @@ export function arePortableUrisEqual(
131
131
  }
132
132
  }
133
133
 
134
+ /**
135
+ * Computes an IRI's FEP-fe34 origin.
136
+ *
137
+ * HTTP(S) IRIs use their web origin. FEP-ef61 portable ActivityPub IRIs and
138
+ * DID URLs use their DID as a cryptographic origin.
139
+ *
140
+ * @throws {TypeError} If the IRI does not have a supported FEP-fe34 origin.
141
+ * @since 2.4.0
142
+ */
143
+ export function getFe34Origin(input: string | URL): string {
144
+ if (input instanceof URL) {
145
+ const portable = normalizePortableUrl(input);
146
+ if (portable != null) return getPortableCryptographicOrigin(portable);
147
+ if (input.protocol === "did:") return getDidUrlOrigin(input.href);
148
+ if (input.protocol === "http:" || input.protocol === "https:") {
149
+ return input.origin;
150
+ }
151
+ throw new TypeError("Unsupported FEP-fe34 origin IRI.");
152
+ }
153
+
154
+ const portable = parsePortableIri(input);
155
+ if (portable != null) return getPortableCryptographicOrigin(portable);
156
+ if (DID_SCHEME_PATTERN.test(input)) return getDidUrlOrigin(input);
157
+
158
+ const parsed = new URL(input);
159
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") {
160
+ return parsed.origin;
161
+ }
162
+ throw new TypeError("Unsupported FEP-fe34 origin IRI.");
163
+ }
164
+
165
+ /**
166
+ * Checks whether two IRIs have the same FEP-fe34 origin.
167
+ *
168
+ * Malformed or unsupported IRIs are treated as non-matching.
169
+ *
170
+ * @since 2.4.0
171
+ */
172
+ export function haveSameFe34Origin(
173
+ left: string | URL,
174
+ right: string | URL,
175
+ ): boolean {
176
+ try {
177
+ return getFe34Origin(left) === getFe34Origin(right);
178
+ } catch (error) {
179
+ if (error instanceof TypeError) return false;
180
+ throw error;
181
+ }
182
+ }
183
+
134
184
  /**
135
185
  * Checks whether two IRIs have the same origin.
136
186
  */
@@ -144,7 +194,7 @@ function getComparableIriOrigin(iri: URL): string {
144
194
  if (iri.host !== "") {
145
195
  const host = iri.protocol === "ap+ef61:"
146
196
  ? encodeURIComponent(
147
- decodePortableAuthority(iri.host).replace(DID_SCHEME_PATTERN, "did:"),
197
+ getDidUrlOrigin(decodePortableAuthority(iri.host)),
148
198
  )
149
199
  : iri.host;
150
200
  return `${iri.protocol}//${host}`;
@@ -152,6 +202,18 @@ function getComparableIriOrigin(iri: URL): string {
152
202
  return iri.href;
153
203
  }
154
204
 
205
+ function getPortableCryptographicOrigin(iri: URL): string {
206
+ return getDidUrlOrigin(decodePortableAuthority(iri.host));
207
+ }
208
+
209
+ function getDidUrlOrigin(iri: string): string {
210
+ const did = iri.split(/[/?#]/, 1)[0].replace(DID_SCHEME_PATTERN, "did:");
211
+ if (!DID_PATTERN.test(did)) throw new TypeError("Invalid DID URL.");
212
+ const parts = did.split(":");
213
+ parts[1] = parts[1].toLowerCase();
214
+ return normalizePortableAuthority(parts.join(":"));
215
+ }
216
+
155
217
  function parsePortableIri(iri: string): URL | null {
156
218
  const match = iri.match(PORTABLE_IRI_PATTERN);
157
219
  if (match == null) return null;
@@ -160,7 +222,7 @@ function parsePortableIri(iri: string): URL | null {
160
222
  // current FEP-ef61 interoperability, but normalize it to a percent-encoded
161
223
  // URL authority internally. The ap: URI syntax may change later; see:
162
224
  // https://bnewbold.leaflet.pub/3mph4hzvbdc2v
163
- const authority = decodePortableAuthority(match[2]);
225
+ const authority = getDidUrlOrigin(decodePortableAuthority(match[2]));
164
226
  if (!DID_PATTERN.test(authority)) {
165
227
  throw new TypeError("Invalid portable ActivityPub IRI authority.");
166
228
  }