@fedify/vocab-tools 2.4.0-dev.1508 → 2.4.0-dev.1528

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-tools",
3
- "version": "2.4.0-dev.1508+68519bf4",
3
+ "version": "2.4.0-dev.1528+fea670ad",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts"
package/dist/mod.cjs CHANGED
@@ -8,7 +8,7 @@ let yaml = require("yaml");
8
8
  let es_toolkit = require("es-toolkit");
9
9
  //#region deno.json
10
10
  var name = "@fedify/vocab-tools";
11
- var version = "2.4.0-dev.1508+68519bf4";
11
+ var version = "2.4.0-dev.1528+fea670ad";
12
12
  //#endregion
13
13
  //#region src/type.ts
14
14
  const HEURISTICS_CONTEXTS = [
@@ -156,10 +156,10 @@ const scalarTypes = {
156
156
  return `${v} instanceof URL`;
157
157
  },
158
158
  encoder(v) {
159
- return `{ "@id": ${v}.href }`;
159
+ return `{ "@id": formatIri(${v}) }`;
160
160
  },
161
161
  compactEncoder(v) {
162
- return `${v}.href`;
162
+ return `formatIri(${v})`;
163
163
  },
164
164
  dataCheck(v) {
165
165
  return `${v} != null && typeof ${v} === "object" && "@id" in ${v}
@@ -167,22 +167,7 @@ const scalarTypes = {
167
167
  && ${v}["@id"] !== ""`;
168
168
  },
169
169
  decoder(v, baseUrlVar) {
170
- return `${v}["@id"].startsWith("at://")
171
- ? new URL("at://" +
172
- encodeURIComponent(
173
- ${v}["@id"].includes("/", 5)
174
- ? ${v}["@id"].slice(5, ${v}["@id"].indexOf("/", 5))
175
- : ${v}["@id"].slice(5)
176
- ) +
177
- (
178
- ${v}["@id"].includes("/", 5)
179
- ? ${v}["@id"].slice(${v}["@id"].indexOf("/", 5))
180
- : ""
181
- )
182
- )
183
- : URL.canParse(${v}["@id"]) && ${baseUrlVar}
184
- ? new URL(${v}["@id"])
185
- : new URL(${v}["@id"], ${baseUrlVar})`;
170
+ return `parseIri(${v}["@id"], ${baseUrlVar})`;
186
171
  }
187
172
  },
188
173
  "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString": {
@@ -324,10 +309,10 @@ const scalarTypes = {
324
309
  return `${v} instanceof URL`;
325
310
  },
326
311
  encoder(v) {
327
- return `{ "@value": ${v}.href }`;
312
+ return `{ "@value": formatIri(${v}) }`;
328
313
  },
329
314
  compactEncoder(v) {
330
- return `${v}.href`;
315
+ return `formatIri(${v})`;
331
316
  },
332
317
  dataCheck(v) {
333
318
  return `typeof ${v} === "object" && "@value" in ${v}
@@ -335,7 +320,7 @@ const scalarTypes = {
335
320
  && ${v}["@value"] !== "" && ${v}["@value"] !== "/"`;
336
321
  },
337
322
  decoder(v) {
338
- return `new URL(${v}["@value"])`;
323
+ return `parseIri(${v}["@value"])`;
339
324
  }
340
325
  },
341
326
  "fedify:publicKey": {
@@ -805,7 +790,7 @@ async function* generateEncoder(typeUri, types) {
805
790
  for (const v of this.${await getFieldName(property.uri)}) {
806
791
  const item = (
807
792
  `;
808
- if (!areAllScalarTypes(property.range, types)) yield "v instanceof URL ? v.href : ";
793
+ if (!areAllScalarTypes(property.range, types)) yield "v instanceof URL ? formatIri(v) : ";
809
794
  const encoders = getEncoders(property.range, types, "v", "options", true);
810
795
  for (const code of encoders) yield code;
811
796
  yield `
@@ -836,7 +821,7 @@ async function* generateEncoder(typeUri, types) {
836
821
  }
837
822
  yield `
838
823
  ${type.typeless ? "" : `result["type"] = ${JSON.stringify(type.compactName ?? type.uri)};`}
839
- if (this.id != null) result["id"] = this.id.href;
824
+ if (this.id != null) result["id"] = formatIri(this.id);
840
825
  result["@context"] = ${JSON.stringify(type.defaultContext)};
841
826
  return result;
842
827
  }
@@ -864,7 +849,7 @@ async function* generateEncoder(typeUri, types) {
864
849
  for (const v of this.${await getFieldName(property.uri)}) {
865
850
  const element = (
866
851
  `;
867
- if (!areAllScalarTypes(property.range, types)) yield "v instanceof URL ? { \"@id\": v.href } : ";
852
+ if (!areAllScalarTypes(property.range, types)) yield "v instanceof URL ? { \"@id\": formatIri(v) } : ";
868
853
  for (const code of getEncoders(property.range, types, "v", "options")) yield code;
869
854
  yield `
870
855
  );
@@ -891,7 +876,7 @@ async function* generateEncoder(typeUri, types) {
891
876
  }
892
877
  yield `
893
878
  ${type.typeless ? "" : `values["@type"] = [${JSON.stringify(type.uri)}];`}
894
- if (this.id != null) values["@id"] = this.id.href;
879
+ if (this.id != null) values["@id"] = formatIri(this.id);
895
880
  if (options.format === "expand") {
896
881
  return await jsonld.expand(
897
882
  values,
@@ -1017,10 +1002,12 @@ async function* generateDecoder(typeUri, types, moduleVarNames) {
1017
1002
  };
1018
1003
  // deno-lint-ignore no-explicit-any
1019
1004
  let values: Record<string, any[]> & { "@id"?: string };
1005
+ let expanded: unknown[];
1020
1006
  if (globalThis.Object.keys(json).length == 0) {
1021
1007
  values = {};
1008
+ expanded = [values];
1022
1009
  } else {
1023
- const expanded = await jsonld.expand(json, {
1010
+ expanded = await jsonld.expand(json, {
1024
1011
  documentLoader: options.contextLoader,
1025
1012
  keepFreeFloatingNodes: true,
1026
1013
  });
@@ -1028,11 +1015,9 @@ async function* generateDecoder(typeUri, types, moduleVarNames) {
1028
1015
  // deno-lint-ignore no-explicit-any
1029
1016
  (expanded[0] ?? {}) as (Record<string, any[]> & { "@id"?: string });
1030
1017
  }
1031
- if (values["@id"] != null && !values["@id"].startsWith("_:") && !URL.canParse(values["@id"], options.baseUrl)) {
1032
- throw new TypeError("Invalid @id: " + values["@id"]);
1033
- }
1034
- if (options.baseUrl == null && values["@id"] != null && !values["@id"].startsWith("_:") && URL.canParse(values["@id"])) {
1035
- options = { ...options, baseUrl: new URL(values["@id"]) };
1018
+ const id = parseJsonLdId(values["@id"], options.baseUrl);
1019
+ if (id != null && options.baseUrl == null) {
1020
+ options = { ...options, baseUrl: id };
1036
1021
  }
1037
1022
  `;
1038
1023
  const subtypes = getSubtypes(typeUri, types, true);
@@ -1053,10 +1038,24 @@ async function* generateDecoder(typeUri, types, moduleVarNames) {
1053
1038
  throw new TypeError("Invalid type: " + values["@type"]);
1054
1039
  }
1055
1040
  }
1041
+ `;
1042
+ yield `
1043
+ let cacheJsonLd = !("_fromSubclass" in options) || !options._fromSubclass
1044
+ ? normalizeJsonLdIris(
1045
+ expanded,
1046
+ PORTABLE_IRI_KEYS,
1047
+ PORTABLE_IRI_PATTERN,
1048
+ )
1049
+ : undefined;
1050
+ if (cacheJsonLd != null && cacheJsonLd !== expanded) {
1051
+ cacheJsonLd = structuredClone(cacheJsonLd);
1052
+ }
1056
1053
  `;
1057
1054
  if (type.extends == null) yield `
1058
1055
  const instance = new this(
1059
- { id: values["@id"] != null && !values["@id"].startsWith("_:") && URL.canParse(values["@id"], options.baseUrl) ? new URL(values["@id"], options.baseUrl) : undefined },
1056
+ {
1057
+ id
1058
+ },
1060
1059
  options,
1061
1060
  );
1062
1061
  `;
@@ -1099,11 +1098,7 @@ async function* generateDecoder(typeUri, types, moduleVarNames) {
1099
1098
  if (typeof v === "object" && "@id" in v && !("@type" in v)
1100
1099
  && globalThis.Object.keys(v).length === 1) {
1101
1100
  if (v["@id"].startsWith("_:")) continue;
1102
- ${variable}.push(
1103
- !URL.canParse(v["@id"], ${propertyBaseUrl}) && v["@id"].startsWith("at://")
1104
- ? new URL("at://" + encodeURIComponent(v["@id"].substring(5)))
1105
- : new URL(v["@id"], ${propertyBaseUrl})
1106
- );
1101
+ ${variable}.push(parseIri(v["@id"], ${propertyBaseUrl}));
1107
1102
  continue;
1108
1103
  }
1109
1104
  `;
@@ -1131,7 +1126,19 @@ async function* generateDecoder(typeUri, types, moduleVarNames) {
1131
1126
  yield `
1132
1127
  if (!("_fromSubclass" in options) || !options._fromSubclass) {
1133
1128
  try {
1134
- instance._cachedJsonLd = structuredClone(json);
1129
+ if (cacheJsonLd != null && cacheJsonLd !== expanded) {
1130
+ const compactArray = Array.isArray(json) && json.length === 1;
1131
+ const jsonLd = compactArray ? json[0] : json;
1132
+ const normalized = cacheJsonLd;
1133
+ const cachedJsonLd = await compactJsonLdCache(
1134
+ normalized,
1135
+ jsonLd,
1136
+ options.contextLoader,
1137
+ );
1138
+ instance._cachedJsonLd = compactArray ? [cachedJsonLd] : cachedJsonLd;
1139
+ } else {
1140
+ instance._cachedJsonLd = structuredClone(json);
1141
+ }
1135
1142
  } catch {
1136
1143
  getLogger(["fedify", "vocab"]).warn(
1137
1144
  "Failed to cache JSON-LD: {json}",
@@ -1558,9 +1565,10 @@ async function* generateProperty(type, property, types, moduleVarNames) {
1558
1565
  ${JSON.stringify(version)},
1559
1566
  );
1560
1567
  return await tracer.startActiveSpan("activitypub.lookup_object", async (span) => {
1568
+ const lookupUrl = formatIri(url);
1561
1569
  let fetchResult: RemoteDocument;
1562
1570
  try {
1563
- fetchResult = await documentLoader(url.href);
1571
+ fetchResult = await documentLoader(lookupUrl);
1564
1572
  } catch (error) {
1565
1573
  span.setStatus({
1566
1574
  code: SpanStatusCode.ERROR,
@@ -1570,21 +1578,20 @@ async function* generateProperty(type, property, types, moduleVarNames) {
1570
1578
  if (options.suppressError) {
1571
1579
  getLogger(["fedify", "vocab"]).error(
1572
1580
  "Failed to fetch {url}: {error}",
1573
- { error, url: url.href }
1581
+ { error, url: lookupUrl }
1574
1582
  );
1575
1583
  return null;
1576
1584
  }
1577
1585
  throw error;
1578
1586
  }
1579
1587
  const { document, documentUrl } = fetchResult;
1580
- const baseUrl = new URL(documentUrl);
1588
+ const baseUrl = parseIri(documentUrl);
1581
1589
  try {
1582
1590
  const obj = await this.#${property.singularName}_fromJsonLd(
1583
1591
  document,
1584
1592
  { documentLoader, contextLoader, tracerProvider, baseUrl }
1585
1593
  );
1586
- if (options.crossOrigin !== "trust" && obj?.id != null &&
1587
- obj.id.origin !== baseUrl.origin) {
1594
+ if (obj?.id != null && !isTrustedIriOrigin(options, obj.id, baseUrl)) {
1588
1595
  if (options.crossOrigin === "throw") {
1589
1596
  throw new Error(
1590
1597
  "The object's @id (" + obj.id.href + ") has a different origin " +
@@ -1613,7 +1620,7 @@ async function* generateProperty(type, property, types, moduleVarNames) {
1613
1620
  if (options.suppressError) {
1614
1621
  getLogger(["fedify", "vocab"]).error(
1615
1622
  "Failed to parse {url}: {error}",
1616
- { error: e, url: url.href }
1623
+ { error: e, url: lookupUrl }
1617
1624
  );
1618
1625
  return null;
1619
1626
  }
@@ -1733,8 +1740,9 @@ async function* generateProperty(type, property, types, moduleVarNames) {
1733
1740
  }
1734
1741
  if (this.${await getFieldName(property.uri)}.length < 1) return null;
1735
1742
  let v = this.${await getFieldName(property.uri)}[0];
1736
- if (options.crossOrigin !== "trust" && !(v instanceof URL) &&
1737
- v.id != null && v.id.origin !== this.id?.origin &&
1743
+ if (!(v instanceof URL) &&
1744
+ v.id != null &&
1745
+ !isTrustedIriOrigin(options, v.id, this.id) &&
1738
1746
  !this.${await getFieldName(property.uri, "#_trust")}.has(0)) {
1739
1747
  v = v.id;
1740
1748
  }
@@ -1773,8 +1781,8 @@ async function* generateProperty(type, property, types, moduleVarNames) {
1773
1781
  `;
1774
1782
  }
1775
1783
  yield `
1776
- if (options.crossOrigin !== "trust" && v?.id != null &&
1777
- this.id != null && v.id.origin !== this.id.origin &&
1784
+ if (v?.id != null &&
1785
+ this.id != null && !isTrustedIriOrigin(options, v.id, this.id) &&
1778
1786
  !this.${await getFieldName(property.uri, "#_trust")}.has(0)) {
1779
1787
  if (options.crossOrigin === "throw") {
1780
1788
  throw new Error(
@@ -1838,8 +1846,9 @@ async function* generateProperty(type, property, types, moduleVarNames) {
1838
1846
  const vs = this.${await getFieldName(property.uri)};
1839
1847
  for (let i = 0; i < vs.length; i++) {
1840
1848
  let v = vs[i];
1841
- if (options.crossOrigin !== "trust" && !(v instanceof URL) &&
1842
- v.id != null && v.id.origin !== this.id?.origin &&
1849
+ if (!(v instanceof URL) &&
1850
+ v.id != null &&
1851
+ !isTrustedIriOrigin(options, v.id, this.id) &&
1843
1852
  !this.${await getFieldName(property.uri, "#_trust")}.has(i)) {
1844
1853
  v = v.id;
1845
1854
  }
@@ -1879,9 +1888,9 @@ async function* generateProperty(type, property, types, moduleVarNames) {
1879
1888
  `;
1880
1889
  }
1881
1890
  yield `
1882
- if (options.crossOrigin !== "trust" && v?.id != null &&
1883
- this.id != null && v.id.origin !== this.id.origin &&
1884
- !this.${await getFieldName(property.uri, "#_trust")}.has(0)) {
1891
+ if (v?.id != null &&
1892
+ this.id != null && !isTrustedIriOrigin(options, v.id, this.id) &&
1893
+ !this.${await getFieldName(property.uri, "#_trust")}.has(i)) {
1885
1894
  if (options.crossOrigin === "throw") {
1886
1895
  throw new Error(
1887
1896
  "The property object's @id (" + v.id.href + ") has a different " +
@@ -1914,6 +1923,33 @@ async function* generateProperties(typeUri, types, moduleVarNames) {
1914
1923
  }
1915
1924
  //#endregion
1916
1925
  //#region src/class.ts
1926
+ const XSD_ANY_URI = "http://www.w3.org/2001/XMLSchema#anyURI";
1927
+ const FEDIFY_URL = "fedify:url";
1928
+ const INTERNAL_RUNTIME_IMPORTS = [
1929
+ "compactJsonLdCache",
1930
+ "getJsonLdContext",
1931
+ "isTrustedIriOrigin",
1932
+ "normalizeJsonLdIris"
1933
+ ].join(",\n ");
1934
+ const RUNTIME_IMPORTS = [
1935
+ "canParseDecimal",
1936
+ "decodeMultibase",
1937
+ "type Decimal",
1938
+ "type DocumentLoader",
1939
+ "encodeMultibase",
1940
+ "exportMultibaseKey",
1941
+ "exportSpki",
1942
+ "formatIri",
1943
+ "getDocumentLoader",
1944
+ "importMultibaseKey",
1945
+ "importPem",
1946
+ "isDecimal",
1947
+ "LanguageString",
1948
+ "parseDecimal",
1949
+ "parseIri",
1950
+ "parseJsonLdId",
1951
+ "type RemoteDocument"
1952
+ ].join(",\n ");
1917
1953
  /**
1918
1954
  * Sorts the given types topologically so that the base types come before the
1919
1955
  * extended types.
@@ -2047,6 +2083,18 @@ export function getEntityTypeById(id: string | URL): $EntityType | undefined {
2047
2083
 
2048
2084
  `;
2049
2085
  }
2086
+ function canContainIriValue(property, types) {
2087
+ return property.range.some((typeUri) => typeUri === XSD_ANY_URI || typeUri === FEDIFY_URL || types[typeUri]?.entity);
2088
+ }
2089
+ function addKeys(keys, property) {
2090
+ keys.add(property.uri);
2091
+ if (property.compactName != null) keys.add(property.compactName);
2092
+ }
2093
+ function addPortableIriKeys(keys, property, types) {
2094
+ if (!canContainIriValue(property, types)) return;
2095
+ addKeys(keys, property);
2096
+ if ("redundantProperties" in property && property.redundantProperties != null) for (const redundantProperty of property.redundantProperties) addKeys(keys, redundantProperty);
2097
+ }
2050
2098
  /**
2051
2099
  * Generates the TypeScript classes from the given types.
2052
2100
  * @param types The types to generate classes from.
@@ -2054,33 +2102,21 @@ export function getEntityTypeById(id: string | URL): $EntityType | undefined {
2054
2102
  */
2055
2103
  async function* generateClasses(types) {
2056
2104
  validateTypeSchemas(types);
2057
- const runtimeImports = [
2058
- "canParseDecimal",
2059
- "decodeMultibase",
2060
- "type Decimal",
2061
- "type DocumentLoader",
2062
- "encodeMultibase",
2063
- "exportMultibaseKey",
2064
- "exportSpki",
2065
- "getDocumentLoader",
2066
- "importMultibaseKey",
2067
- "importPem",
2068
- "isDecimal",
2069
- "LanguageString",
2070
- "parseDecimal",
2071
- "type RemoteDocument"
2072
- ];
2073
2105
  yield "// deno-lint-ignore-file ban-unused-ignore no-explicit-any no-unused-vars prefer-const verbatim-module-syntax\n";
2074
2106
  yield "import jsonld from \"@fedify/vocab-runtime/jsonld\";\n";
2075
2107
  yield "import { getLogger } from \"@logtape/logtape\";\n";
2076
2108
  yield `import { type Span, SpanStatusCode, type TracerProvider, trace }
2077
2109
  from "@opentelemetry/api";\n`;
2078
- yield `import {\n ${runtimeImports.join(",\n ")}\n} from "@fedify/vocab-runtime";\n`;
2110
+ yield `import {\n ${RUNTIME_IMPORTS}\n} from "@fedify/vocab-runtime";\n`;
2111
+ yield `import {\n ${INTERNAL_RUNTIME_IMPORTS}\n} from "@fedify/vocab-runtime/internal/jsonld-cache";\n`;
2079
2112
  yield `import {
2080
2113
  isTemporalDuration,
2081
2114
  isTemporalInstant,
2082
2115
  } from "@fedify/vocab-runtime/temporal";\n`;
2083
- yield "\n\n";
2116
+ const portableIriKeys = new Set(["@id", "id"]);
2117
+ for (const type of Object.values(types)) for (const property of type.properties) addPortableIriKeys(portableIriKeys, property, types);
2118
+ yield "const PORTABLE_IRI_PATTERN = /^ap(?:\\+ef61)?:\\/\\//i;\n";
2119
+ yield `const PORTABLE_IRI_KEYS: ReadonlySet<string> = new Set(${JSON.stringify([...portableIriKeys].sort())});\n\n`;
2084
2120
  const moduleVarNames = /* @__PURE__ */ new Map();
2085
2121
  const sorted = sortTopologically(types);
2086
2122
  for (const typeUri of sorted) for (const property of types[typeUri].properties) {