@fedify/vocab-tools 2.4.0-pr.936.41 → 2.4.1

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
@@ -14,10 +14,35 @@ const XSD_ANY_URI = "http://www.w3.org/2001/XMLSchema#anyURI";
14
14
  const FEDIFY_URL = "fedify:url";
15
15
  const INTERNAL_RUNTIME_IMPORTS = [
16
16
  "compactJsonLdCache",
17
+ "createScopedContextLoader",
17
18
  "getJsonLdContext",
19
+ "getPortableActorGateways",
18
20
  "isTrustedIriOrigin",
19
21
  "normalizeJsonLdIris",
20
22
  ].join(",\n ");
23
+ const PORTABLE_DEREFERENCE_IMPORTS = [
24
+ "copyPortableProvenance",
25
+ "createSnapshotContextLoader",
26
+ "dereferencePortableIri",
27
+ "getPortableResponseClaim",
28
+ "getReferrerGateways",
29
+ "isPortableIri",
30
+ "isPortableMode",
31
+ "isUnsecuredPortableObject",
32
+ "isUnverifiedPortableClaim",
33
+ "markUnverifiedPortableClaim",
34
+ "mustDereferencePortableObject",
35
+ "parseCompatibleEf61Reference",
36
+ "type PortableResponseClaim",
37
+ "recordPortableReferrer",
38
+ "rejectMalformedCompatibleReference",
39
+ "warnUnverifiableEmbeddedObject",
40
+ ].join(",\n ");
41
+ const SIGNED_REPRESENTATION_IMPORTS = [
42
+ "enterSignedValueScope",
43
+ "resolveSignedValues",
44
+ "retainedSignedValueRef",
45
+ ].join(",\n ");
21
46
  const RUNTIME_IMPORTS = [
22
47
  "canParseDecimal",
23
48
  "decodeMultibase",
@@ -33,10 +58,12 @@ const RUNTIME_IMPORTS = [
33
58
  "isDecimal",
34
59
  "isGatewayUrl",
35
60
  "LanguageString",
61
+ "normalizeLanguageTag",
36
62
  "parseDecimal",
37
63
  "parseGatewayUrl",
38
64
  "parseIri",
39
65
  "parseJsonLdId",
66
+ "type PortableObjectVerifier",
40
67
  "type RemoteDocument",
41
68
  ].join(",\n ");
42
69
 
@@ -86,6 +113,7 @@ async function* generateClass(
86
113
  readonly #documentLoader?: DocumentLoader;
87
114
  readonly #contextLoader?: DocumentLoader;
88
115
  readonly #tracerProvider?: TracerProvider;
116
+ readonly #verifyPortableObject?: PortableObjectVerifier;
89
117
  readonly #warning?: {
90
118
  category: string[];
91
119
  message: string;
@@ -93,6 +121,7 @@ async function* generateClass(
93
121
  };
94
122
  #cachedJsonLd?: unknown;
95
123
  readonly #_baseUrl?: URL;
124
+ #shouldCacheJsonLd = true;
96
125
  readonly id: URL | null;
97
126
 
98
127
  protected get _documentLoader(): DocumentLoader | undefined {
@@ -107,6 +136,10 @@ async function* generateClass(
107
136
  return this.#tracerProvider;
108
137
  }
109
138
 
139
+ protected get _verifyPortableObject(): PortableObjectVerifier | undefined {
140
+ return this.#verifyPortableObject;
141
+ }
142
+
110
143
  protected get _warning(): {
111
144
  category: string[];
112
145
  message: string;
@@ -126,6 +159,20 @@ async function* generateClass(
126
159
  protected get _baseUrl(): URL | undefined {
127
160
  return this.#_baseUrl;
128
161
  }
162
+
163
+ protected get _shouldCacheJsonLd(): boolean {
164
+ return this.#shouldCacheJsonLd;
165
+ }
166
+
167
+ protected set _shouldCacheJsonLd(value: boolean) {
168
+ this.#shouldCacheJsonLd = value;
169
+ }
170
+
171
+ protected static _shouldCacheDecodedJsonLd(value: unknown): boolean {
172
+ if (value == null || typeof value !== "object") return true;
173
+ if (!("_shouldCacheJsonLd" in value)) return true;
174
+ return (value as { _shouldCacheJsonLd: boolean })._shouldCacheJsonLd;
175
+ }
129
176
  `;
130
177
  }
131
178
  yield `
@@ -253,10 +300,79 @@ export async function* generateClasses(
253
300
  from "@opentelemetry/api";\n`;
254
301
  yield `import {\n ${RUNTIME_IMPORTS}\n} from "@fedify/vocab-runtime";\n`;
255
302
  yield `import {\n ${INTERNAL_RUNTIME_IMPORTS}\n} from "@fedify/vocab-runtime/internal/jsonld-cache";\n`;
303
+ yield `import {\n ${PORTABLE_DEREFERENCE_IMPORTS}\n} from "@fedify/vocab-runtime/internal/portable-dereference";\n`;
304
+ yield `import {\n ${SIGNED_REPRESENTATION_IMPORTS}\n} from "@fedify/vocab-runtime/internal/signed-representation";\n`;
256
305
  yield `import {
257
306
  isTemporalDuration,
258
307
  isTemporalInstant,
259
308
  } from "@fedify/vocab-runtime/temporal";\n`;
309
+ yield `
310
+ function canDecodeIri(iri: string): boolean {
311
+ try { parseIri(iri); return true; } catch { return iri.startsWith("at://"); }
312
+ }
313
+
314
+ function decodeIri(iri: string): URL {
315
+ return !URL.canParse(iri) && iri.startsWith("at://")
316
+ ? new URL("at://" + encodeURIComponent(iri.substring(5)))
317
+ : parseIri(iri);
318
+ }
319
+
320
+ function inspectIri(iri: URL): string {
321
+ // Inspecting an object must not throw, even for a URL that cannot be
322
+ // formatted as an IRI, e.g., a malformed portable ID:
323
+ try { return formatIri(iri); } catch { return iri.href; }
324
+ }
325
+ `;
326
+ yield `
327
+ function isValidLanguageTag(language: string): boolean {
328
+ try {
329
+ new Intl.Locale(normalizeLanguageTag(language));
330
+ return true;
331
+ } catch (error) {
332
+ if (error instanceof RangeError) return false;
333
+ throw error;
334
+ }
335
+ }
336
+ `;
337
+ // Contexts are activated by expanded property IRIs left after compaction.
338
+ const extraContexts = Object.fromEntries(
339
+ Object.values(types).flatMap((type) =>
340
+ type.properties.flatMap((property) =>
341
+ property.extraContext == null
342
+ ? []
343
+ : [[property.uri, property.extraContext]]
344
+ )
345
+ ),
346
+ );
347
+ if (Object.keys(extraContexts).length > 0) {
348
+ yield `
349
+ const extraPropertyContexts: Readonly<Record<string, string>> =
350
+ ${JSON.stringify(extraContexts)};
351
+
352
+ function getExtraContexts(document: unknown): string[] {
353
+ const contexts = new Set<string>();
354
+ const pending: unknown[] = [document];
355
+ while (pending.length > 0) {
356
+ const value = pending.pop();
357
+ if (value == null || typeof value !== "object") continue;
358
+ if (Array.isArray(value)) {
359
+ for (const item of value) pending.push(item);
360
+ continue;
361
+ }
362
+ if ("@value" in value) continue;
363
+ for (const [key, child] of globalThis.Object.entries(value)) {
364
+ if (key === "@context") continue;
365
+ if (globalThis.Object.hasOwn(extraPropertyContexts, key)) {
366
+ contexts.add(extraPropertyContexts[key]);
367
+ }
368
+ pending.push(child);
369
+ }
370
+ }
371
+ return [...contexts];
372
+ }
373
+ `;
374
+ }
375
+ yield "\n\n";
260
376
  const portableIriKeys = new Set(["@id", "id"]);
261
377
  for (const type of Object.values(types)) {
262
378
  for (const property of type.properties) {
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(
@@ -43,6 +45,7 @@ function* generatePreprocessorBlock(
43
45
  documentLoader: options.documentLoader,
44
46
  contextLoader: options.contextLoader,
45
47
  tracerProvider: options.tracerProvider,
48
+ verifyPortableObject: options.verifyPortableObject,
46
49
  baseUrl: ${baseUrlExpr},
47
50
  });
48
51
  if (_result instanceof Error) throw _result;
@@ -95,6 +98,10 @@ export async function* generateEncoder(
95
98
  ...options,
96
99
  contextLoader: options.contextLoader ?? getDocumentLoader(),
97
100
  };
101
+ // Joins the enclosing frame's signed-value scope, or starts one when this
102
+ // frame is the outermost. Only the owning frame puts retained signed
103
+ // child documents back in place of their placeholders.
104
+ const signedValues = enterSignedValueScope(options);
98
105
  `;
99
106
  if (isCompactableType(typeUri, types)) {
100
107
  yield `
@@ -127,6 +134,7 @@ export async function* generateEncoder(
127
134
  const item = (
128
135
  `;
129
136
  if (!areAllScalarTypes(property.range, types)) {
137
+ yield "retainedSignedValueRef(v, signedValues.scope) ?? (";
130
138
  yield "v instanceof URL ? formatIri(v) : ";
131
139
  }
132
140
  const encoders = getEncoders(
@@ -137,6 +145,7 @@ export async function* generateEncoder(
137
145
  true,
138
146
  );
139
147
  for (const code of encoders) yield code;
148
+ if (!areAllScalarTypes(property.range, types)) yield ")";
140
149
  yield `
141
150
  );
142
151
  compactItems.push(item);
@@ -180,7 +189,9 @@ export async function* generateEncoder(
180
189
  }
181
190
  if (this.id != null) result["id"] = formatIri(this.id);
182
191
  result["@context"] = ${JSON.stringify(type.defaultContext)};
183
- return result;
192
+ return signedValues.owner
193
+ ? resolveSignedValues(result, signedValues.scope)
194
+ : result;
184
195
  }
185
196
  `;
186
197
  }
@@ -207,16 +218,23 @@ export async function* generateEncoder(
207
218
  yield `
208
219
  array = [];
209
220
  for (const v of this.${await getFieldName(property.uri)}) {
210
- const element = (
221
+ let element = (
211
222
  `;
212
223
  if (!areAllScalarTypes(property.range, types)) {
224
+ yield "retainedSignedValueRef(v, signedValues.scope) ?? (";
213
225
  yield 'v instanceof URL ? { "@id": formatIri(v) } : ';
214
226
  }
215
227
  for (const code of getEncoders(property.range, types, "v", "options")) {
216
228
  yield code;
217
229
  }
230
+ if (!areAllScalarTypes(property.range, types)) yield ")";
218
231
  yield `
219
232
  );
233
+ if (Array.isArray(element)) {
234
+ if (element.length < 1) continue;
235
+ if (element.length == 1) element = element[0];
236
+ }
237
+ if (typeof element === "undefined") continue;
220
238
  `;
221
239
  if (isNonFunctionalProperty(property) && property.container === "graph") {
222
240
  yield `array.push({ "@graph": element });`;
@@ -254,16 +272,41 @@ export async function* generateEncoder(
254
272
  if (options.format === "expand") {
255
273
  return await jsonld.expand(
256
274
  values,
257
- { documentLoader: options.contextLoader },
275
+ {
276
+ documentLoader: options.contextLoader,
277
+ keepFreeFloatingNodes: true,
278
+ },
258
279
  );
259
280
  }
260
- const docContext = options.context ??
281
+ let docContext: Parameters<typeof jsonld.compact>[1] = options.context ??
261
282
  ${JSON.stringify(type.defaultContext)};
262
- const compacted = await jsonld.compact(
283
+ let compacted = await jsonld.compact(
263
284
  values,
264
285
  docContext,
265
286
  { documentLoader: options.contextLoader },
266
287
  );
288
+ `;
289
+ if (
290
+ Object.values(types).some((t) =>
291
+ t.properties.some((p) => p.extraContext != null)
292
+ )
293
+ ) {
294
+ yield `
295
+ if (options.context == null) {
296
+ const currentContexts = Array.isArray(docContext) ? docContext : [docContext];
297
+ const additionalContexts = getExtraContexts(compacted).filter(
298
+ context => !currentContexts.includes(context)
299
+ );
300
+ if (additionalContexts.length > 0) {
301
+ docContext = [...currentContexts, ...additionalContexts];
302
+ compacted = await jsonld.compact(values, docContext, {
303
+ documentLoader: options.contextLoader,
304
+ });
305
+ }
306
+ }
307
+ `;
308
+ }
309
+ yield `
267
310
  if (docContext != null) {
268
311
  // Embed context
269
312
  `;
@@ -295,13 +338,18 @@ export async function* generateEncoder(
295
338
  }
296
339
  yield `
297
340
  }
298
- return compacted;
341
+ return signedValues.owner
342
+ ? resolveSignedValues(compacted, signedValues.scope)
343
+ : compacted;
299
344
  }
300
345
 
301
346
  protected ${emitOverride(typeUri, types)} isCompactable(): boolean {
302
347
  `;
303
348
  for (const property of type.properties) {
304
- if (!property.range.every((r) => isCompactableType(r, types))) {
349
+ if (
350
+ property.extraContext != null ||
351
+ !property.range.every((r) => isCompactableType(r, types))
352
+ ) {
305
353
  yield `
306
354
  if (
307
355
  this.${await getFieldName(property.uri)} != null &&
@@ -331,6 +379,10 @@ export async function* generateDecoder(
331
379
  * - \`contextLoader\`: The loader for remote JSON-LD contexts.
332
380
  * - \`tracerProvider\`: The OpenTelemetry tracer provider to use.
333
381
  * If omitted, the global tracer provider is used.
382
+ * - \`verifyPortableObject\`: The default FEP-ef61 portable
383
+ * object verifier for the property accessors of the
384
+ * returned object and the objects obtained from it. It
385
+ * does not verify the given \`json\` itself.
334
386
  * @returns The object of this type.
335
387
  * @throws {TypeError} If the given \`json\` is invalid.
336
388
  */
@@ -340,6 +392,7 @@ export async function* generateDecoder(
340
392
  documentLoader?: DocumentLoader,
341
393
  contextLoader?: DocumentLoader,
342
394
  tracerProvider?: TracerProvider,
395
+ verifyPortableObject?: PortableObjectVerifier,
343
396
  baseUrl?: URL,
344
397
  } = {},
345
398
  ): Promise<${type.name}> {
@@ -378,6 +431,7 @@ export async function* generateDecoder(
378
431
  documentLoader?: DocumentLoader,
379
432
  contextLoader?: DocumentLoader,
380
433
  tracerProvider?: TracerProvider,
434
+ verifyPortableObject?: PortableObjectVerifier,
381
435
  baseUrl?: URL,
382
436
  } = {},
383
437
  ): Promise<${type.name}> {
@@ -394,7 +448,17 @@ export async function* generateDecoder(
394
448
  // deno-lint-ignore no-explicit-any
395
449
  let values: Record<string, any[]> & { "@id"?: string };
396
450
  let expanded: unknown[];
397
- if (globalThis.Object.keys(json).length == 0) {
451
+ if (
452
+ "_fromSubclass" in options &&
453
+ options._fromSubclass &&
454
+ typeof json === "object" &&
455
+ !Array.isArray(json)
456
+ ) {
457
+ values =
458
+ // deno-lint-ignore no-explicit-any
459
+ json as (Record<string, any[]> & { "@id"?: string });
460
+ expanded = [values];
461
+ } else if (globalThis.Object.keys(json).length == 0) {
398
462
  values = {};
399
463
  expanded = [values];
400
464
  } else {
@@ -455,8 +519,9 @@ export async function* generateDecoder(
455
519
  `;
456
520
  } else {
457
521
  yield `
458
- delete values["@type"];
459
- const instance = await super.fromJsonLd(values, {
522
+ const superValues = structuredClone(values);
523
+ delete superValues["@type"];
524
+ const instance = await super.fromJsonLd(superValues, {
460
525
  ...options,
461
526
  // @ts-ignore: an internal option
462
527
  _fromSubclass: true,
@@ -466,12 +531,18 @@ export async function* generateDecoder(
466
531
  }
467
532
  `;
468
533
  }
534
+ yield `
535
+ let shouldCacheJsonLd = instance._shouldCacheJsonLd;
536
+ `;
469
537
  for (const property of type.properties) {
470
538
  const variable = await getFieldName(property.uri, "");
471
539
  yield await generateField(property, types, "const ");
472
540
  const arrayVariable = `${variable}__array`;
541
+ const propertyValues = property.uri === "https://w3id.org/fep/ef61/gateways"
542
+ ? "getPortableActorGateways(values) as typeof values[string]"
543
+ : `values[${JSON.stringify(property.uri)}]`;
473
544
  yield `
474
- let ${arrayVariable} = values[${JSON.stringify(property.uri)}];
545
+ let ${arrayVariable} = ${propertyValues};
475
546
  `;
476
547
  if (property.functional && property.redundantProperties != null) {
477
548
  for (const prop of property.redundantProperties) {
@@ -482,13 +553,25 @@ export async function* generateDecoder(
482
553
  `;
483
554
  }
484
555
  }
556
+ let itemsExpression =
557
+ `${arrayVariable}.length === 1 && "@list" in ${arrayVariable}[0]
558
+ ? ${arrayVariable}[0]["@list"]
559
+ : ${arrayVariable}`;
560
+ if (
561
+ isNonFunctionalProperty(property) && property.container === "graph"
562
+ ) {
563
+ itemsExpression = `(${itemsExpression}).flatMap((item: unknown) =>
564
+ item != null && typeof item === "object" && "@graph" in item &&
565
+ Array.isArray((item as { "@graph": unknown })["@graph"])
566
+ ? (item as { "@graph": unknown[] })["@graph"]
567
+ : [item]
568
+ )`;
569
+ }
485
570
  yield `
486
571
  for (
487
572
  const v of ${arrayVariable} == null
488
573
  ? []
489
- : ${arrayVariable}.length === 1 && "@list" in ${arrayVariable}[0]
490
- ? ${arrayVariable}[0]["@list"]
491
- : ${arrayVariable}
574
+ : ${itemsExpression}
492
575
  ) {
493
576
  if (v == null) continue;
494
577
  `;
@@ -510,23 +593,25 @@ export async function* generateDecoder(
510
593
  }
511
594
  `;
512
595
  }
596
+ yield `
597
+ const decoded =
598
+ `;
599
+ const lenient = property.range.length == 1 &&
600
+ skipsUnparsable(property.range[0]);
513
601
  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);`;
602
+ if (lenient) {
603
+ yield getDataCheck(property.range[0], types, "v");
604
+ yield " ? ";
605
+ }
606
+ yield getDecoder(
607
+ property.range[0],
608
+ types,
609
+ "v",
610
+ "options",
611
+ propertyBaseUrl,
612
+ );
613
+ if (lenient) yield " : undefined";
526
614
  } else {
527
- yield `
528
- const decoded =
529
- `;
530
615
  const decoders = getDecoders(
531
616
  property.range,
532
617
  types,
@@ -535,19 +620,33 @@ export async function* generateDecoder(
535
620
  propertyBaseUrl,
536
621
  );
537
622
  for (const code of decoders) yield code;
538
- yield `
623
+ }
624
+ yield `
539
625
  ;
540
- if (typeof decoded === "undefined") continue;
626
+ `;
627
+ yield `
628
+ if (typeof decoded === "undefined") {
629
+ shouldCacheJsonLd = false;
630
+ continue;
631
+ }
632
+ `;
633
+ yield `
634
+ if (!this._shouldCacheDecodedJsonLd(decoded)) {
635
+ shouldCacheJsonLd = false;
636
+ }
541
637
  ${variable}.push(decoded);
542
- `;
543
- }
638
+ `;
544
639
  yield `
545
640
  }
546
641
  instance.${await getFieldName(property.uri)} = ${variable};
547
642
  `;
548
643
  }
549
644
  yield `
550
- if (!("_fromSubclass" in options) || !options._fromSubclass) {
645
+ instance._shouldCacheJsonLd = shouldCacheJsonLd;
646
+ if (
647
+ shouldCacheJsonLd &&
648
+ (!("_fromSubclass" in options) || !options._fromSubclass)
649
+ ) {
551
650
  try {
552
651
  if (cacheJsonLd != null && cacheJsonLd !== expanded) {
553
652
  const compactArray = Array.isArray(json) && json.length === 1;
@@ -89,6 +89,7 @@ export async function* generateConstructor(
89
89
  documentLoader?: DocumentLoader,
90
90
  contextLoader?: DocumentLoader,
91
91
  tracerProvider?: TracerProvider,
92
+ verifyPortableObject?: PortableObjectVerifier,
92
93
  } = {},
93
94
  ) {
94
95
  `;
@@ -97,6 +98,7 @@ export async function* generateConstructor(
97
98
  this.#documentLoader = options.documentLoader;
98
99
  this.#contextLoader = options.contextLoader;
99
100
  this.#tracerProvider = options.tracerProvider;
101
+ this.#verifyPortableObject = options.verifyPortableObject;
100
102
  const baseUrl = (options as { baseUrl?: URL }).baseUrl;
101
103
  this.#_baseUrl = baseUrl == null ? undefined : new URL(baseUrl.href);
102
104
  if ("$warning" in options) {
@@ -177,7 +179,7 @@ export async function* generateConstructor(
177
179
  if (Array.isArray(values.${property.pluralName}) &&
178
180
  values.${property.pluralName}.every(v => ${typeGuards})) {
179
181
  // @ts-ignore: type is checked above.
180
- this.${fieldName} = values.${property.pluralName};
182
+ this.${fieldName} = values.${property.pluralName}.slice();
181
183
  `;
182
184
  if (!allScalarTypes) {
183
185
  yield `
@@ -208,9 +210,17 @@ export async function* generateCloner(
208
210
  yield `
209
211
  /**
210
212
  * Clones this instance, optionally updating it with the given values.
213
+ *
214
+ * A clone never inherits a signed JSON-LD representation retained by
215
+ * \`signObject()\`: the clone may differ from the document that the proof
216
+ * covers, so embedding the original secured JSON in a parent document
217
+ * would be wrong. Sign the clone again if it has to be embedded as a
218
+ * secured child.
219
+ *
211
220
  * @param values The values to update the clone with.
212
221
  * @param options The options to use for cloning.
213
- * @returns The cloned instance.
222
+ * @returns The cloned instance with its own property arrays. Nested objects
223
+ * and URLs are shared with this instance.
214
224
  */
215
225
  ${emitOverride(typeUri, types)} clone(
216
226
  values:
@@ -221,6 +231,7 @@ export async function* generateCloner(
221
231
  options: {
222
232
  documentLoader?: DocumentLoader,
223
233
  contextLoader?: DocumentLoader,
234
+ verifyPortableObject?: PortableObjectVerifier,
224
235
  } = {}
225
236
  ): ${type.name} {
226
237
  if (this._warning != null) {
@@ -239,6 +250,7 @@ export async function* generateCloner(
239
250
  { id: values.id ?? this.id },
240
251
  options
241
252
  );
253
+ copyPortableProvenance(this, clone);
242
254
  `;
243
255
  } else {
244
256
  yield `const clone = super.clone(values, options) as unknown as ${type.name};`;
@@ -247,7 +259,7 @@ export async function* generateCloner(
247
259
  const fieldName = await getFieldName(property.uri);
248
260
  const trustFieldName = await getFieldName(property.uri, "#_trust");
249
261
  const allScalarTypes = areAllScalarTypes(property.range, types);
250
- yield `clone.${fieldName} = this.${fieldName};`;
262
+ yield `clone.${fieldName} = this.${fieldName}.slice();`;
251
263
  if (!allScalarTypes) {
252
264
  yield `clone.${trustFieldName} = new Set(this.${trustFieldName});`;
253
265
  }
@@ -311,7 +323,7 @@ export async function* generateCloner(
311
323
  if (Array.isArray(values.${property.pluralName}) &&
312
324
  values.${property.pluralName}.every(v => ${typeGuards})) {
313
325
  // @ts-ignore: type is checked above.
314
- clone.${fieldName} = values.${property.pluralName};
326
+ clone.${fieldName} = values.${property.pluralName}.slice();
315
327
  `;
316
328
  if (!allScalarTypes) {
317
329
  yield `
package/src/inspector.ts CHANGED
@@ -21,12 +21,12 @@ export async function* generateInspector(
21
21
  [Symbol.for("Deno.customInspect")]: (
22
22
  inspect: typeof Deno.inspect,
23
23
  options: Deno.InspectOptions,
24
- ): string => "URL " + inspect(this.id!.href, options),
24
+ ): string => "URL " + inspect(inspectIri(this.id!), options),
25
25
  [Symbol.for("nodejs.util.inspect.custom")]: (
26
26
  _depth: number,
27
27
  options: unknown,
28
28
  inspect: (value: unknown, options: unknown) => string,
29
- ): string => "URL " + inspect(this.id!.href, options),
29
+ ): string => "URL " + inspect(inspectIri(this.id!), options),
30
30
  };
31
31
  }
32
32
  `;
@@ -44,12 +44,12 @@ export async function* generateInspector(
44
44
  [Symbol.for("Deno.customInspect")]: (
45
45
  inspect: typeof Deno.inspect,
46
46
  options: Deno.InspectOptions,
47
- ): string => "URL " + inspect(v.href, options),
47
+ ): string => "URL " + inspect(inspectIri(v), options),
48
48
  [Symbol.for("nodejs.util.inspect.custom")]: (
49
49
  _depth: number,
50
50
  options: unknown,
51
51
  inspect: (value: unknown, options: unknown) => string,
52
- ): string => "URL " + inspect(v.href, options),
52
+ ): string => "URL " + inspect(inspectIri(v), options),
53
53
  }
54
54
  : v);
55
55
  `;