@fedify/vocab-tools 2.4.0-dev.2056 → 2.4.0-dev.2065
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 +1 -1
- package/dist/mod.cjs +181 -58
- package/dist/mod.js +181 -58
- package/package.json +1 -1
- package/src/__snapshots__/class.test.ts.deno.snap +21022 -7593
- package/src/__snapshots__/class.test.ts.node.snap +21022 -7593
- package/src/__snapshots__/class.test.ts.snap +21022 -7593
- package/src/class.ts +13 -0
- package/src/constructor.ts +1 -0
- package/src/property.ts +172 -58
package/dist/mod.js
CHANGED
|
@@ -7,7 +7,7 @@ import { parse } from "yaml";
|
|
|
7
7
|
import { pascalCase } from "es-toolkit";
|
|
8
8
|
//#region deno.json
|
|
9
9
|
var name = "@fedify/vocab-tools";
|
|
10
|
-
var version = "2.4.0-dev.
|
|
10
|
+
var version = "2.4.0-dev.2065+b09fdd33";
|
|
11
11
|
//#endregion
|
|
12
12
|
//#region src/type.ts
|
|
13
13
|
const HEURISTICS_CONTEXTS = [
|
|
@@ -1424,6 +1424,7 @@ async function* generateCloner(typeUri, types) {
|
|
|
1424
1424
|
{ id: values.id ?? this.id },
|
|
1425
1425
|
options
|
|
1426
1426
|
);
|
|
1427
|
+
copyPortableProvenance(this, clone);
|
|
1427
1428
|
`;
|
|
1428
1429
|
else yield `const clone = super.clone(values, options) as unknown as ${type.name};`;
|
|
1429
1430
|
for (const property of type.properties) {
|
|
@@ -1671,14 +1672,27 @@ async function* generateProperty(type, property, types, moduleVarNames) {
|
|
|
1671
1672
|
${JSON.stringify(version)},
|
|
1672
1673
|
);
|
|
1673
1674
|
return await tracer.startActiveSpan("activitypub.lookup_object", async (span) => {
|
|
1674
|
-
|
|
1675
|
+
const dereferencePortable = async (
|
|
1676
|
+
portableUrl: URL,
|
|
1677
|
+
extra: {
|
|
1678
|
+
inferredGateways?: readonly URL[];
|
|
1679
|
+
response?: RemoteDocument;
|
|
1680
|
+
contextLoader?: DocumentLoader;
|
|
1681
|
+
} = {},
|
|
1682
|
+
) => {
|
|
1675
1683
|
try {
|
|
1676
|
-
const obj = await dereferencePortableIri(
|
|
1684
|
+
const obj = await dereferencePortableIri(portableUrl, {
|
|
1677
1685
|
documentLoader,
|
|
1678
|
-
contextLoader,
|
|
1686
|
+
contextLoader: extra.contextLoader ?? contextLoader,
|
|
1679
1687
|
tracerProvider,
|
|
1680
1688
|
gateways: options.gateways,
|
|
1689
|
+
inferredGateways: extra.inferredGateways,
|
|
1690
|
+
response: extra.response,
|
|
1681
1691
|
verifyPortableObject: options.verifyPortableObject,
|
|
1692
|
+
referrer: {
|
|
1693
|
+
object: this,
|
|
1694
|
+
property: ${JSON.stringify(property.uri)},
|
|
1695
|
+
},
|
|
1682
1696
|
suppressError: options.suppressError,
|
|
1683
1697
|
crossOrigin: options.crossOrigin,
|
|
1684
1698
|
span,
|
|
@@ -1691,7 +1705,10 @@ async function* generateProperty(type, property, types, moduleVarNames) {
|
|
|
1691
1705
|
}),
|
|
1692
1706
|
});
|
|
1693
1707
|
if (obj != null) {
|
|
1694
|
-
span.setAttribute(
|
|
1708
|
+
span.setAttribute(
|
|
1709
|
+
"activitypub.object.id",
|
|
1710
|
+
(obj.id ?? portableUrl).href,
|
|
1711
|
+
);
|
|
1695
1712
|
span.setAttribute(
|
|
1696
1713
|
"activitypub.object.type",
|
|
1697
1714
|
// @ts-ignore: obj.constructor always has a typeId.
|
|
@@ -1708,8 +1725,21 @@ async function* generateProperty(type, property, types, moduleVarNames) {
|
|
|
1708
1725
|
} finally {
|
|
1709
1726
|
span.end();
|
|
1710
1727
|
}
|
|
1711
|
-
}
|
|
1728
|
+
};
|
|
1729
|
+
if (isPortableIri(url)) return await dereferencePortable(url);
|
|
1712
1730
|
const lookupUrl = formatIri(url);
|
|
1731
|
+
const portableMode = isPortableMode(this, options);
|
|
1732
|
+
if (portableMode) {
|
|
1733
|
+
const compatible = parseCompatibleEf61Reference(url);
|
|
1734
|
+
if (compatible === null) {
|
|
1735
|
+
span.end();
|
|
1736
|
+
return rejectMalformedCompatibleReference(lookupUrl, options);
|
|
1737
|
+
} else if (compatible != null) {
|
|
1738
|
+
return await dereferencePortable(compatible.id, {
|
|
1739
|
+
inferredGateways: [compatible.gateway],
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1713
1743
|
let fetchResult: RemoteDocument;
|
|
1714
1744
|
try {
|
|
1715
1745
|
fetchResult = await documentLoader(lookupUrl);
|
|
@@ -1729,52 +1759,90 @@ async function* generateProperty(type, property, types, moduleVarNames) {
|
|
|
1729
1759
|
throw error;
|
|
1730
1760
|
}
|
|
1731
1761
|
const { document, documentUrl } = fetchResult;
|
|
1732
|
-
|
|
1762
|
+
// In portable mode, the document may turn out to be a portable object,
|
|
1763
|
+
// which has to be verified with the same context documents:
|
|
1764
|
+
const snapshot = portableMode
|
|
1765
|
+
? createSnapshotContextLoader(contextLoader)
|
|
1766
|
+
: null;
|
|
1733
1767
|
try {
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1768
|
+
let claim: PortableResponseClaim | null | undefined;
|
|
1769
|
+
try {
|
|
1770
|
+
const baseUrl = parseIri(documentUrl);
|
|
1771
|
+
const obj = await this.#${property.singularName}_fromJsonLd(
|
|
1772
|
+
document,
|
|
1773
|
+
{
|
|
1774
|
+
documentLoader,
|
|
1775
|
+
contextLoader: snapshot?.loader ?? contextLoader,
|
|
1776
|
+
tracerProvider,
|
|
1777
|
+
baseUrl,
|
|
1778
|
+
}
|
|
1779
|
+
);
|
|
1780
|
+
if (snapshot == null) {
|
|
1781
|
+
markUnverifiedPortableClaim(obj, url, documentUrl);
|
|
1782
|
+
} else {
|
|
1783
|
+
claim = getPortableResponseClaim(documentUrl, obj.id);
|
|
1784
|
+
}
|
|
1785
|
+
if (claim === undefined) {
|
|
1786
|
+
if (
|
|
1787
|
+
obj?.id != null &&
|
|
1788
|
+
!isTrustedIriOrigin(options, obj.id, baseUrl)
|
|
1789
|
+
) {
|
|
1790
|
+
if (options.crossOrigin === "throw") {
|
|
1791
|
+
throw new Error(
|
|
1792
|
+
"The object's @id (" + obj.id.href + ") has a different origin " +
|
|
1793
|
+
"than the document URL (" + baseUrl.href + "); refusing to return " +
|
|
1794
|
+
"the object. If you want to bypass this check and are aware of" +
|
|
1795
|
+
'the security implications, set the crossOrigin option to "trust".'
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1798
|
+
getLogger(["fedify", "vocab"]).warn(
|
|
1799
|
+
"The object's @id ({objectId}) has a different origin than the document " +
|
|
1800
|
+
"URL ({documentUrl}); refusing to return the object. If you want to " +
|
|
1801
|
+
"bypass this check and are aware of the security implications, " +
|
|
1802
|
+
'set the crossOrigin option to "trust".',
|
|
1803
|
+
{ ...fetchResult, objectId: obj.id.href },
|
|
1804
|
+
);
|
|
1805
|
+
span.end();
|
|
1806
|
+
return null;
|
|
1807
|
+
}
|
|
1808
|
+
span.setAttribute("activitypub.object.id", (obj.id ?? url).href);
|
|
1809
|
+
span.setAttribute(
|
|
1810
|
+
"activitypub.object.type",
|
|
1811
|
+
// @ts-ignore: obj.constructor always has a typeId.
|
|
1812
|
+
obj.constructor.typeId.href
|
|
1745
1813
|
);
|
|
1814
|
+
span.end();
|
|
1815
|
+
return obj;
|
|
1746
1816
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
"
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1817
|
+
} catch (e) {
|
|
1818
|
+
if (options.suppressError) {
|
|
1819
|
+
getLogger(["fedify", "vocab"]).error(
|
|
1820
|
+
"Failed to parse {url}: {error}",
|
|
1821
|
+
{ error: e, url: lookupUrl }
|
|
1822
|
+
);
|
|
1823
|
+
span.end();
|
|
1824
|
+
return null;
|
|
1825
|
+
}
|
|
1826
|
+
span.setStatus({
|
|
1827
|
+
code: SpanStatusCode.ERROR,
|
|
1828
|
+
message: String(e),
|
|
1829
|
+
});
|
|
1830
|
+
span.end();
|
|
1831
|
+
throw e;
|
|
1755
1832
|
}
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
);
|
|
1762
|
-
return obj;
|
|
1763
|
-
} catch (e) {
|
|
1764
|
-
if (options.suppressError) {
|
|
1765
|
-
getLogger(["fedify", "vocab"]).error(
|
|
1766
|
-
"Failed to parse {url}: {error}",
|
|
1767
|
-
{ error: e, url: lookupUrl }
|
|
1768
|
-
);
|
|
1769
|
-
return null;
|
|
1833
|
+
// The document stands for a portable object, so it is verified as
|
|
1834
|
+
// one instead of being trusted because of where it came from:
|
|
1835
|
+
if (claim === null) {
|
|
1836
|
+
span.end();
|
|
1837
|
+
return rejectMalformedCompatibleReference(documentUrl, options);
|
|
1770
1838
|
}
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1839
|
+
return await dereferencePortable(claim.id, {
|
|
1840
|
+
inferredGateways: claim.inferredGateways,
|
|
1841
|
+
response: fetchResult,
|
|
1842
|
+
contextLoader: snapshot?.loader,
|
|
1774
1843
|
});
|
|
1775
|
-
throw e;
|
|
1776
1844
|
} finally {
|
|
1777
|
-
|
|
1845
|
+
snapshot?.release();
|
|
1778
1846
|
}
|
|
1779
1847
|
});
|
|
1780
1848
|
}
|
|
@@ -1887,18 +1955,31 @@ async function* generateProperty(type, property, types, moduleVarNames) {
|
|
|
1887
1955
|
if (this.${await getFieldName(property.uri)}.length < 1) return null;
|
|
1888
1956
|
let v = this.${await getFieldName(property.uri)}[0];
|
|
1889
1957
|
if (!(v instanceof URL) &&
|
|
1890
|
-
v.id != null &&
|
|
1891
|
-
!isTrustedIriOrigin(options, v.id, ${trustOwner}) &&
|
|
1892
1958
|
!this.${await getFieldName(property.uri, "#_trust")}.has(0)) {
|
|
1893
|
-
|
|
1959
|
+
if (v.id == null) {
|
|
1960
|
+
if (isUnsecuredPortableObject(this)) {
|
|
1961
|
+
warnUnverifiableEmbeddedObject(this, ${JSON.stringify(property.uri)});
|
|
1962
|
+
return null;
|
|
1963
|
+
}
|
|
1964
|
+
} else if (
|
|
1965
|
+
!isTrustedIriOrigin(options, v.id, ${trustOwner}) ||
|
|
1966
|
+
isUnsecuredPortableObject(this) ||
|
|
1967
|
+
mustDereferencePortableObject(this, ${trustOwner}, v.id, options)
|
|
1968
|
+
) {
|
|
1969
|
+
v = v.id;
|
|
1970
|
+
}
|
|
1894
1971
|
}
|
|
1895
1972
|
if (v instanceof URL) {
|
|
1896
1973
|
const fetched =
|
|
1897
1974
|
await this.#fetch${pascalCase(property.singularName)}(v, options);
|
|
1898
1975
|
if (fetched == null) return null;
|
|
1976
|
+
// Not cached, so that it is verified when the portable object
|
|
1977
|
+
// policy applies later:
|
|
1978
|
+
if (isUnverifiedPortableClaim(fetched)) return fetched;
|
|
1899
1979
|
this.${await getFieldName(property.uri)}[0] = fetched;
|
|
1900
1980
|
this.${await getFieldName(property.uri, "#_trust")}.add(0);
|
|
1901
1981
|
this._cachedJsonLd = undefined;
|
|
1982
|
+
recordPortableReferrer(this, fetched, ${JSON.stringify(property.uri)});
|
|
1902
1983
|
return fetched;
|
|
1903
1984
|
}
|
|
1904
1985
|
`;
|
|
@@ -1928,12 +2009,16 @@ async function* generateProperty(type, property, types, moduleVarNames) {
|
|
|
1928
2009
|
}
|
|
1929
2010
|
yield `
|
|
1930
2011
|
if (v?.id != null &&
|
|
1931
|
-
${type.trustEmbeddedObjects === false ? "" : "this.id != null && "}!isTrustedIriOrigin(options, v.id, ${trustOwner})
|
|
2012
|
+
((${type.trustEmbeddedObjects === false ? "" : "this.id != null && "}!isTrustedIriOrigin(options, v.id, ${trustOwner})) ||
|
|
2013
|
+
isUnsecuredPortableObject(this) ||
|
|
2014
|
+
mustDereferencePortableObject(
|
|
2015
|
+
this, ${trustOwner}, v.id, options,
|
|
2016
|
+
)) &&
|
|
1932
2017
|
!this.${await getFieldName(property.uri, "#_trust")}.has(0)) {
|
|
1933
2018
|
if (options.crossOrigin === "throw") {
|
|
1934
2019
|
throw new Error(
|
|
1935
2020
|
"The property object's @id (" + v.id.href + ") has a different " +
|
|
1936
|
-
"origin than the property owner's @id (" + ${type.trustEmbeddedObjects === false ? "\"untrusted metadata\"" : "this.id
|
|
2021
|
+
"origin than the property owner's @id (" + ${type.trustEmbeddedObjects === false ? "\"untrusted metadata\"" : "(this.id?.href ?? null)"} + "); " +
|
|
1937
2022
|
"refusing to return the object. If you want to bypass this " +
|
|
1938
2023
|
"check and are aware of the security implications, set the " +
|
|
1939
2024
|
'crossOrigin option to "trust".'
|
|
@@ -1945,10 +2030,11 @@ async function* generateProperty(type, property, types, moduleVarNames) {
|
|
|
1945
2030
|
"return the object. If you want to bypass this check and are " +
|
|
1946
2031
|
"aware of the security implications, set the crossOrigin option " +
|
|
1947
2032
|
'to "trust".',
|
|
1948
|
-
{ objectId: v.id.href, parentObjectId: ${type.trustEmbeddedObjects === false ? "\"untrusted metadata\"" : "this.id
|
|
2033
|
+
{ objectId: v.id.href, parentObjectId: ${type.trustEmbeddedObjects === false ? "\"untrusted metadata\"" : "(this.id?.href ?? null)"} },
|
|
1949
2034
|
);
|
|
1950
2035
|
return null;
|
|
1951
2036
|
}
|
|
2037
|
+
recordPortableReferrer(this, v, ${JSON.stringify(property.uri)});
|
|
1952
2038
|
return v;
|
|
1953
2039
|
}
|
|
1954
2040
|
`;
|
|
@@ -1995,18 +2081,34 @@ async function* generateProperty(type, property, types, moduleVarNames) {
|
|
|
1995
2081
|
for (let i = 0; i < vs.length; i++) {
|
|
1996
2082
|
let v = vs[i];
|
|
1997
2083
|
if (!(v instanceof URL) &&
|
|
1998
|
-
v.id != null &&
|
|
1999
|
-
!isTrustedIriOrigin(options, v.id, ${trustOwner}) &&
|
|
2000
2084
|
!this.${await getFieldName(property.uri, "#_trust")}.has(i)) {
|
|
2001
|
-
|
|
2085
|
+
if (v.id == null) {
|
|
2086
|
+
if (isUnsecuredPortableObject(this)) {
|
|
2087
|
+
warnUnverifiableEmbeddedObject(this, ${JSON.stringify(property.uri)});
|
|
2088
|
+
continue;
|
|
2089
|
+
}
|
|
2090
|
+
} else if (
|
|
2091
|
+
!isTrustedIriOrigin(options, v.id, ${trustOwner}) ||
|
|
2092
|
+
isUnsecuredPortableObject(this) ||
|
|
2093
|
+
mustDereferencePortableObject(this, ${trustOwner}, v.id, options)
|
|
2094
|
+
) {
|
|
2095
|
+
v = v.id;
|
|
2096
|
+
}
|
|
2002
2097
|
}
|
|
2003
2098
|
if (v instanceof URL) {
|
|
2004
2099
|
const fetched =
|
|
2005
2100
|
await this.#fetch${pascalCase(property.singularName)}(v, options);
|
|
2006
2101
|
if (fetched == null) continue;
|
|
2102
|
+
// Not cached, so that it is verified when the portable object
|
|
2103
|
+
// policy applies later:
|
|
2104
|
+
if (isUnverifiedPortableClaim(fetched)) {
|
|
2105
|
+
yield fetched;
|
|
2106
|
+
continue;
|
|
2107
|
+
}
|
|
2007
2108
|
vs[i] = fetched;
|
|
2008
2109
|
this.${await getFieldName(property.uri, "#_trust")}.add(i);
|
|
2009
2110
|
this._cachedJsonLd = undefined;
|
|
2111
|
+
recordPortableReferrer(this, fetched, ${JSON.stringify(property.uri)});
|
|
2010
2112
|
yield fetched;
|
|
2011
2113
|
continue;
|
|
2012
2114
|
}
|
|
@@ -2037,12 +2139,16 @@ async function* generateProperty(type, property, types, moduleVarNames) {
|
|
|
2037
2139
|
}
|
|
2038
2140
|
yield `
|
|
2039
2141
|
if (v?.id != null &&
|
|
2040
|
-
${type.trustEmbeddedObjects === false ? "" : "this.id != null && "}!isTrustedIriOrigin(options, v.id, ${trustOwner})
|
|
2142
|
+
((${type.trustEmbeddedObjects === false ? "" : "this.id != null && "}!isTrustedIriOrigin(options, v.id, ${trustOwner})) ||
|
|
2143
|
+
isUnsecuredPortableObject(this) ||
|
|
2144
|
+
mustDereferencePortableObject(
|
|
2145
|
+
this, ${trustOwner}, v.id, options,
|
|
2146
|
+
)) &&
|
|
2041
2147
|
!this.${await getFieldName(property.uri, "#_trust")}.has(i)) {
|
|
2042
2148
|
if (options.crossOrigin === "throw") {
|
|
2043
2149
|
throw new Error(
|
|
2044
2150
|
"The property object's @id (" + v.id.href + ") has a different " +
|
|
2045
|
-
"origin than the property owner's @id (" + ${type.trustEmbeddedObjects === false ? "\"untrusted metadata\"" : "this.id
|
|
2151
|
+
"origin than the property owner's @id (" + ${type.trustEmbeddedObjects === false ? "\"untrusted metadata\"" : "(this.id?.href ?? null)"} + "); " +
|
|
2046
2152
|
"refusing to return the object. If you want to bypass this " +
|
|
2047
2153
|
"check and are aware of the security implications, set the " +
|
|
2048
2154
|
'crossOrigin option to "trust".'
|
|
@@ -2054,10 +2160,11 @@ async function* generateProperty(type, property, types, moduleVarNames) {
|
|
|
2054
2160
|
"return the object. If you want to bypass this check and are " +
|
|
2055
2161
|
"aware of the security implications, set the crossOrigin " +
|
|
2056
2162
|
'option to "trust".',
|
|
2057
|
-
{ objectId: v.id.href, parentObjectId: ${type.trustEmbeddedObjects === false ? "\"untrusted metadata\"" : "this.id
|
|
2163
|
+
{ objectId: v.id.href, parentObjectId: ${type.trustEmbeddedObjects === false ? "\"untrusted metadata\"" : "(this.id?.href ?? null)"} },
|
|
2058
2164
|
);
|
|
2059
2165
|
continue;
|
|
2060
2166
|
}
|
|
2167
|
+
recordPortableReferrer(this, v, ${JSON.stringify(property.uri)});
|
|
2061
2168
|
yield v;
|
|
2062
2169
|
}
|
|
2063
2170
|
}
|
|
@@ -2079,7 +2186,23 @@ const INTERNAL_RUNTIME_IMPORTS = [
|
|
|
2079
2186
|
"isTrustedIriOrigin",
|
|
2080
2187
|
"normalizeJsonLdIris"
|
|
2081
2188
|
].join(",\n ");
|
|
2082
|
-
const PORTABLE_DEREFERENCE_IMPORTS = [
|
|
2189
|
+
const PORTABLE_DEREFERENCE_IMPORTS = [
|
|
2190
|
+
"copyPortableProvenance",
|
|
2191
|
+
"createSnapshotContextLoader",
|
|
2192
|
+
"dereferencePortableIri",
|
|
2193
|
+
"getPortableResponseClaim",
|
|
2194
|
+
"isPortableIri",
|
|
2195
|
+
"isPortableMode",
|
|
2196
|
+
"isUnsecuredPortableObject",
|
|
2197
|
+
"isUnverifiedPortableClaim",
|
|
2198
|
+
"markUnverifiedPortableClaim",
|
|
2199
|
+
"mustDereferencePortableObject",
|
|
2200
|
+
"parseCompatibleEf61Reference",
|
|
2201
|
+
"type PortableResponseClaim",
|
|
2202
|
+
"recordPortableReferrer",
|
|
2203
|
+
"rejectMalformedCompatibleReference",
|
|
2204
|
+
"warnUnverifiableEmbeddedObject"
|
|
2205
|
+
].join(",\n ");
|
|
2083
2206
|
const SIGNED_REPRESENTATION_IMPORTS = [
|
|
2084
2207
|
"enterSignedValueScope",
|
|
2085
2208
|
"resolveSignedValues",
|