@fedify/vocab-tools 2.4.0-dev.2155 → 2.4.0-dev.2159

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/src/class.ts CHANGED
@@ -304,6 +304,17 @@ export async function* generateClasses(
304
304
  isTemporalInstant,
305
305
  } from "@fedify/vocab-runtime/temporal";\n`;
306
306
  yield `
307
+ function canDecodeIri(iri: string): boolean {
308
+ try { parseIri(iri); return true; } catch { return iri.startsWith("at://"); }
309
+ }
310
+
311
+ function decodeIri(iri: string): URL {
312
+ return !URL.canParse(iri) && iri.startsWith("at://")
313
+ ? new URL("at://" + encodeURIComponent(iri.substring(5)))
314
+ : parseIri(iri);
315
+ }
316
+ `;
317
+ yield `
307
318
  function isValidLanguageTag(language: string): boolean {
308
319
  try {
309
320
  new Intl.Locale(language);
package/src/codec.ts CHANGED
@@ -6,12 +6,14 @@ import {
6
6
  areAllScalarTypes,
7
7
  emitOverride,
8
8
  getAllProperties,
9
+ getDataCheck,
9
10
  getDecoder,
10
11
  getDecoders,
11
12
  getEncoders,
12
13
  getSubtypes,
13
14
  getTypeNames,
14
15
  isCompactableType,
16
+ skipsUnparsable,
15
17
  } from "./type.ts";
16
18
 
17
19
  function* generatePreprocessorBlock(
@@ -591,7 +593,13 @@ export async function* generateDecoder(
591
593
  yield `
592
594
  const decoded =
593
595
  `;
596
+ const lenient = property.range.length == 1 &&
597
+ skipsUnparsable(property.range[0]);
594
598
  if (property.range.length == 1) {
599
+ if (lenient) {
600
+ yield getDataCheck(property.range[0], types, "v");
601
+ yield " ? ";
602
+ }
595
603
  yield getDecoder(
596
604
  property.range[0],
597
605
  types,
@@ -599,6 +607,7 @@ export async function* generateDecoder(
599
607
  "options",
600
608
  propertyBaseUrl,
601
609
  );
610
+ if (lenient) yield " : undefined";
602
611
  } else {
603
612
  const decoders = getDecoders(
604
613
  property.range,
package/src/property.ts CHANGED
@@ -477,18 +477,21 @@ async function* generateProperty(
477
477
  ${JSON.stringify(property.compactName)}];
478
478
  const doc = Array.isArray(prop) ? prop[0] : prop;
479
479
  if (doc != null && typeof doc === "object" && "@context" in doc) {
480
- `;
481
- yield `
482
- v = await this.#${property.singularName}_fromJsonLd(doc, {
483
- ...options,
484
- // Embedded objects get this object's own default, whether or
485
- // not they carry their own @context, as when this object was
486
- // parsed:
487
- verifyPortableObject: this._verifyPortableObject,
488
- baseUrl: ${cachedPropertyBaseUrl},
489
- });
490
- `;
491
- yield `
480
+ try {
481
+ v = await this.#${property.singularName}_fromJsonLd(doc, {
482
+ ...options,
483
+ // Keep the parent default for embedded objects.
484
+ verifyPortableObject: this._verifyPortableObject,
485
+ baseUrl: ${cachedPropertyBaseUrl},
486
+ });
487
+ } catch (error) {
488
+ if (!options.suppressError) throw error;
489
+ getLogger(["fedify", "vocab"]).debug(
490
+ "Failed to parse embedded value of {property}: {error}",
491
+ { property: ${JSON.stringify(property.uri)}, error },
492
+ );
493
+ return null;
494
+ }
492
495
  }
493
496
  }
494
497
  `;
@@ -647,18 +650,23 @@ async function* generateProperty(
647
650
  ${JSON.stringify(property.compactName)}];
648
651
  const obj = Array.isArray(prop) ? prop[i] : prop;
649
652
  if (obj != null && typeof obj === "object" && "@context" in obj) {
650
- `;
651
- yield `
652
- v = await this.#${property.singularName}_fromJsonLd(obj, {
653
- ...options,
654
- // Embedded objects get this object's own default, whether or
655
- // not they carry their own @context, as when this object was
656
- // parsed:
653
+ try {
654
+ v = await this.#${property.singularName}_fromJsonLd(obj, {
655
+ ...options,
656
+ // Keep the parent default for embedded objects.
657
657
  verifyPortableObject: this._verifyPortableObject,
658
- baseUrl: ${cachedPropertyBaseUrl},
659
- });
660
- `;
661
- yield `
658
+ baseUrl: ${cachedPropertyBaseUrl},
659
+ });
660
+ } catch (error) {
661
+ if (!options.suppressError) throw error;
662
+ getLogger(["fedify", "vocab"]).debug(
663
+ "Failed to parse embedded value of {property} at index {index}: {error}",
664
+ { property: ${
665
+ JSON.stringify(property.uri)
666
+ }, index: i, error },
667
+ );
668
+ continue;
669
+ }
662
670
  }
663
671
  }
664
672
  `;
package/src/type.ts CHANGED
@@ -19,6 +19,12 @@ interface ScalarType {
19
19
  compactEncoder?: (variable: string) => string;
20
20
  dataCheck(variable: string): string;
21
21
  decoder(variable: string, baseUrlVar: string): string;
22
+ /**
23
+ * Whether a value that fails `dataCheck()` should be skipped instead of
24
+ * being handed to `decoder()`. Set this for types decoded from untrusted
25
+ * remote input, where one malformed value must not fail the whole object.
26
+ */
27
+ skipUnparsable?: boolean;
22
28
  }
23
29
 
24
30
  const scalarTypes: Record<string, ScalarType> = {
@@ -319,13 +325,14 @@ const scalarTypes: Record<string, ScalarType> = {
319
325
  return `formatIri(${v})`;
320
326
  },
321
327
  dataCheck(v) {
322
- return `typeof ${v} === "object" && "@value" in ${v}
323
- && typeof ${v}["@value"] === "string"
324
- && ${v}["@value"] !== "" && ${v}["@value"] !== "/"`;
328
+ return `typeof ${v} === "object" &&
329
+ ((typeof ${v}["@id"] === "string" && canDecodeIri(${v}["@id"])) ||
330
+ (typeof ${v}["@value"] === "string" && canDecodeIri(${v}["@value"])))`;
325
331
  },
326
332
  decoder(v) {
327
- return `parseIri(${v}["@value"])`;
333
+ return `decodeIri(typeof ${v}["@id"] === "string" ? ${v}["@id"] : ${v}["@value"])`;
328
334
  },
335
+ skipUnparsable: true,
329
336
  },
330
337
  "fedify:gatewayUrl": {
331
338
  name: "URL",
@@ -658,6 +665,10 @@ export function getDecoder(
658
665
  throw new Error(`Unknown type: ${typeUri}`);
659
666
  }
660
667
 
668
+ export function skipsUnparsable(typeUri: string): boolean {
669
+ return scalarTypes[typeUri]?.skipUnparsable ?? false;
670
+ }
671
+
661
672
  export function getDataCheck(
662
673
  typeUri: string,
663
674
  types: Record<string, TypeSchema>,