@@ -835,7 +835,23 @@ const note = await create.getObject();
835835const note2 = await create .getObject ();
836836~~~~
837837
838- Hydrating the property also affects the JSON-LD representation of the object.
838+ When hydration caches a fetched object, it replaces the property's URL in the
839+ instance whose accessor was called. An unverified portable claim can be
840+ returned without replacing that URL or clearing the JSON-LD cache, so a later
841+ accessor call can fetch it again.
842+ When the result is cached, all references to that instance see the hydrated
843+ property, including references
844+ from a source and its [ shallow clone] ( #immutability ) to a shared nested object,
845+ or from an activity that embeds it. Hydrating a top-level property of a clone
846+ does not update the source's property, because their property arrays are
847+ separate.
848+
849+ Hydration can also affect ` toJsonLd() ` output, including output from activities
850+ that embed the hydrated object. Objects parsed with ` fromJsonLd() ` may retain
851+ cached JSON-LD. Caching a fetched object clears the JSON-LD cache only on the
852+ instance whose accessor fetched it; parent caches remain intact. A parent may
853+ therefore serialize differently on its own and when embedded in another
854+ activity.
839855
840856For example, since the following code does not hydrate the ` object ` property,
841857the JSON-LD representation of the ` Create ` object has the ` object ` property
@@ -904,16 +920,13 @@ attributes are simplified or omitted for readability):
904920Immutability
905921------------
906922
907- Every object in the Activity Vocabulary is represented as an immutable object.
908- This means that you cannot change the properties of the object after the object
909- is instantiated. This is for ensuring the consistency of the objects and the
910- safety of the objects in the concurrent environment.
923+ Activity Vocabulary objects are immutable in the sense that you cannot assign
924+ new property values after construction. Dereferencing accessors can still
925+ cache fetched objects through [ property hydration] ( #property-hydration ) .
911926
912- In order to change the properties of the object, you need to clone the object
913- with the new properties. Fortunately, the objects have a ` clone() ` method that
914- takes an object with the new properties and returns a new object with the new
915- properties. The following shows an example of changing the ` ~Object.content `
916- property of a ` Note ` object:
927+ To replace property values, use ` clone() ` . It returns a new instance with the
928+ given values and leaves the source's property values unchanged. The following
929+ example replaces the ` ~Object.content ` property of a ` Note ` object:
917930
918931~~~~ typescript{8-10} twoslash
919932import { Note } from "@fedify/vocab";
@@ -929,8 +942,40 @@ const noteInChinese = noteInEnglish.clone({
929942});
930943~~~~
931944
932- Parameters of the ` clone() ` method share the same type with parameters of
933- the constructor.
945+ The ` clone() ` method accepts the same property values as the constructor.
946+ It makes a * shallow copy* : the clone has its own property arrays, but nested
947+ objects and URLs are shared with the source. Hydrating a shared nested object
948+ through either instance changes that nested object for both when their accessors
949+ return the shared instance. A parsed source can return a fresh object instead:
950+ if its cached JSON-LD contains an embedded object with its own ` @context ` , the
951+ accessor re-parses that object on each call. Hydrating that returned object
952+ does not update the nested instance stored in the source or clone.
953+
954+ If you need to preserve the received representation while resolving properties,
955+ keep the received JSON-LD document. To resolve properties in a separate object
956+ graph, serialize and re-parse the object before hydrating any properties rather
957+ than using ` clone() ` :
958+
959+ ~~~~ typescript twoslash
960+ import { Create } from " @fedify/vocab" ;
961+ import type { DocumentLoader } from " @fedify/vocab-runtime" ;
962+ declare const original: Create ;
963+ declare const documentLoader: DocumentLoader ;
964+ declare const contextLoader: DocumentLoader ;
965+ // ---cut-before---
966+ const options = { documentLoader , contextLoader };
967+ const copy = await Create .fromJsonLd (await original .toJsonLd (), options );
968+ // Resolve properties on copy while keeping original separate:
969+ const note = await copy .getObject (options );
970+ if (note != null ) await note .getAttribution (options );
971+ ~~~~
972+
973+ This round trip separates nested vocabulary objects; it does not preserve the
974+ original JSON bytes. Pass any custom loaders again, as in the example above.
975+ The copy also does not inherit trust in embedded objects. Under the
976+ [ origin-based security model] ( #origin-based-security-model ) , accessors may fetch
977+ cross-origin objects again, even if the original already hydrated them. The
978+ fetched data may have changed, or the fetch may fail.
934979
935980
936981Looking up remote objects
0 commit comments