@fedify/vocab-tools 2.4.0-dev.1666 → 2.4.0-dev.1727

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
@@ -93,6 +93,7 @@ async function* generateClass(
93
93
  };
94
94
  #cachedJsonLd?: unknown;
95
95
  readonly #_baseUrl?: URL;
96
+ #shouldCacheJsonLd = true;
96
97
  readonly id: URL | null;
97
98
 
98
99
  protected get _documentLoader(): DocumentLoader | undefined {
@@ -126,6 +127,20 @@ async function* generateClass(
126
127
  protected get _baseUrl(): URL | undefined {
127
128
  return this.#_baseUrl;
128
129
  }
130
+
131
+ protected get _shouldCacheJsonLd(): boolean {
132
+ return this.#shouldCacheJsonLd;
133
+ }
134
+
135
+ protected set _shouldCacheJsonLd(value: boolean) {
136
+ this.#shouldCacheJsonLd = value;
137
+ }
138
+
139
+ protected static _shouldCacheDecodedJsonLd(value: unknown): boolean {
140
+ if (value == null || typeof value !== "object") return true;
141
+ if (!("_shouldCacheJsonLd" in value)) return true;
142
+ return (value as { _shouldCacheJsonLd: boolean })._shouldCacheJsonLd;
143
+ }
129
144
  `;
130
145
  }
131
146
  yield `
@@ -257,6 +272,18 @@ export async function* generateClasses(
257
272
  isTemporalDuration,
258
273
  isTemporalInstant,
259
274
  } from "@fedify/vocab-runtime/temporal";\n`;
275
+ yield `
276
+ function isValidLanguageTag(language: string): boolean {
277
+ try {
278
+ new Intl.Locale(language);
279
+ return true;
280
+ } catch (error) {
281
+ if (error instanceof RangeError) return false;
282
+ throw error;
283
+ }
284
+ }
285
+ `;
286
+ yield "\n\n";
260
287
  const portableIriKeys = new Set(["@id", "id"]);
261
288
  for (const type of Object.values(types)) {
262
289
  for (const property of type.properties) {
package/src/codec.ts CHANGED
@@ -207,7 +207,7 @@ export async function* generateEncoder(
207
207
  yield `
208
208
  array = [];
209
209
  for (const v of this.${await getFieldName(property.uri)}) {
210
- const element = (
210
+ let element = (
211
211
  `;
212
212
  if (!areAllScalarTypes(property.range, types)) {
213
213
  yield 'v instanceof URL ? { "@id": formatIri(v) } : ';
@@ -217,6 +217,11 @@ export async function* generateEncoder(
217
217
  }
218
218
  yield `
219
219
  );
220
+ if (Array.isArray(element)) {
221
+ if (element.length < 1) continue;
222
+ if (element.length == 1) element = element[0];
223
+ }
224
+ if (typeof element === "undefined") continue;
220
225
  `;
221
226
  if (isNonFunctionalProperty(property) && property.container === "graph") {
222
227
  yield `array.push({ "@graph": element });`;
@@ -254,7 +259,10 @@ export async function* generateEncoder(
254
259
  if (options.format === "expand") {
255
260
  return await jsonld.expand(
256
261
  values,
257
- { documentLoader: options.contextLoader },
262
+ {
263
+ documentLoader: options.contextLoader,
264
+ keepFreeFloatingNodes: true,
265
+ },
258
266
  );
259
267
  }
260
268
  const docContext = options.context ??
@@ -394,7 +402,17 @@ export async function* generateDecoder(
394
402
  // deno-lint-ignore no-explicit-any
395
403
  let values: Record<string, any[]> & { "@id"?: string };
396
404
  let expanded: unknown[];
397
- if (globalThis.Object.keys(json).length == 0) {
405
+ if (
406
+ "_fromSubclass" in options &&
407
+ options._fromSubclass &&
408
+ typeof json === "object" &&
409
+ !Array.isArray(json)
410
+ ) {
411
+ values =
412
+ // deno-lint-ignore no-explicit-any
413
+ json as (Record<string, any[]> & { "@id"?: string });
414
+ expanded = [values];
415
+ } else if (globalThis.Object.keys(json).length == 0) {
398
416
  values = {};
399
417
  expanded = [values];
400
418
  } else {
@@ -455,8 +473,9 @@ export async function* generateDecoder(
455
473
  `;
456
474
  } else {
457
475
  yield `
458
- delete values["@type"];
459
- const instance = await super.fromJsonLd(values, {
476
+ const superValues = structuredClone(values);
477
+ delete superValues["@type"];
478
+ const instance = await super.fromJsonLd(superValues, {
460
479
  ...options,
461
480
  // @ts-ignore: an internal option
462
481
  _fromSubclass: true,
@@ -466,6 +485,9 @@ export async function* generateDecoder(
466
485
  }
467
486
  `;
468
487
  }
488
+ yield `
489
+ let shouldCacheJsonLd = instance._shouldCacheJsonLd;
490
+ `;
469
491
  for (const property of type.properties) {
470
492
  const variable = await getFieldName(property.uri, "");
471
493
  yield await generateField(property, types, "const ");
@@ -482,13 +504,25 @@ export async function* generateDecoder(
482
504
  `;
483
505
  }
484
506
  }
507
+ let itemsExpression =
508
+ `${arrayVariable}.length === 1 && "@list" in ${arrayVariable}[0]
509
+ ? ${arrayVariable}[0]["@list"]
510
+ : ${arrayVariable}`;
511
+ if (
512
+ isNonFunctionalProperty(property) && property.container === "graph"
513
+ ) {
514
+ itemsExpression = `(${itemsExpression}).flatMap((item: unknown) =>
515
+ item != null && typeof item === "object" && "@graph" in item &&
516
+ Array.isArray((item as { "@graph": unknown })["@graph"])
517
+ ? (item as { "@graph": unknown[] })["@graph"]
518
+ : [item]
519
+ )`;
520
+ }
485
521
  yield `
486
522
  for (
487
523
  const v of ${arrayVariable} == null
488
524
  ? []
489
- : ${arrayVariable}.length === 1 && "@list" in ${arrayVariable}[0]
490
- ? ${arrayVariable}[0]["@list"]
491
- : ${arrayVariable}
525
+ : ${itemsExpression}
492
526
  ) {
493
527
  if (v == null) continue;
494
528
  `;
@@ -510,23 +544,18 @@ export async function* generateDecoder(
510
544
  }
511
545
  `;
512
546
  }
547
+ yield `
548
+ const decoded =
549
+ `;
513
550
  if (property.range.length == 1) {
514
- yield `
515
- const decoded = ${
516
- getDecoder(
517
- property.range[0],
518
- types,
519
- "v",
520
- "options",
521
- propertyBaseUrl,
522
- )
523
- };
524
- if (typeof decoded === "undefined") continue;
525
- ${variable}.push(decoded);`;
551
+ yield getDecoder(
552
+ property.range[0],
553
+ types,
554
+ "v",
555
+ "options",
556
+ propertyBaseUrl,
557
+ );
526
558
  } else {
527
- yield `
528
- const decoded =
529
- `;
530
559
  const decoders = getDecoders(
531
560
  property.range,
532
561
  types,
@@ -535,19 +564,33 @@ export async function* generateDecoder(
535
564
  propertyBaseUrl,
536
565
  );
537
566
  for (const code of decoders) yield code;
538
- yield `
567
+ }
568
+ yield `
539
569
  ;
540
- if (typeof decoded === "undefined") continue;
570
+ `;
571
+ yield `
572
+ if (typeof decoded === "undefined") {
573
+ shouldCacheJsonLd = false;
574
+ continue;
575
+ }
576
+ `;
577
+ yield `
578
+ if (!this._shouldCacheDecodedJsonLd(decoded)) {
579
+ shouldCacheJsonLd = false;
580
+ }
541
581
  ${variable}.push(decoded);
542
- `;
543
- }
582
+ `;
544
583
  yield `
545
584
  }
546
585
  instance.${await getFieldName(property.uri)} = ${variable};
547
586
  `;
548
587
  }
549
588
  yield `
550
- if (!("_fromSubclass" in options) || !options._fromSubclass) {
589
+ instance._shouldCacheJsonLd = shouldCacheJsonLd;
590
+ if (
591
+ shouldCacheJsonLd &&
592
+ (!("_fromSubclass" in options) || !options._fromSubclass)
593
+ ) {
551
594
  try {
552
595
  if (cacheJsonLd != null && cacheJsonLd !== expanded) {
553
596
  const compactArray = Array.isArray(json) && json.length === 1;
package/src/type.ts CHANGED
@@ -187,7 +187,8 @@ const scalarTypes: Record<string, ScalarType> = {
187
187
  dataCheck(v) {
188
188
  return `typeof ${v} === "object" && "@language" in ${v} && "@value" in ${v}
189
189
  && typeof ${v}["@language"] === "string"
190
- && typeof ${v}["@value"] === "string"`;
190
+ && typeof ${v}["@value"] === "string"
191
+ && isValidLanguageTag(${v}["@language"])`;
191
192
  },
192
193
  decoder(v) {
193
194
  return `new LanguageString(${v}["@value"], ${v}["@language"])`;