@fedify/vocab 2.0.0-dev.0

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.
Files changed (167) hide show
  1. package/LICENSE +20 -0
  2. package/deno.json +31 -0
  3. package/dist/accept.yaml +15 -0
  4. package/dist/activity.yaml +98 -0
  5. package/dist/actor.test.d.ts +2 -0
  6. package/dist/actor.test.js +6095 -0
  7. package/dist/add.yaml +16 -0
  8. package/dist/announce.yaml +30 -0
  9. package/dist/application.yaml +324 -0
  10. package/dist/arrive.yaml +15 -0
  11. package/dist/article.yaml +46 -0
  12. package/dist/audio.yaml +11 -0
  13. package/dist/block.yaml +16 -0
  14. package/dist/chatmessage.yaml +50 -0
  15. package/dist/collection.yaml +154 -0
  16. package/dist/collectionpage.yaml +55 -0
  17. package/dist/create.yaml +28 -0
  18. package/dist/dataintegrityproof.yaml +56 -0
  19. package/dist/delete.yaml +27 -0
  20. package/dist/deno-B-ypIMwF.js +1282 -0
  21. package/dist/didservice.yaml +22 -0
  22. package/dist/dislike.yaml +14 -0
  23. package/dist/document.yaml +31 -0
  24. package/dist/emoji.yaml +12 -0
  25. package/dist/emojireact.yaml +17 -0
  26. package/dist/endpoints.yaml +85 -0
  27. package/dist/event.yaml +11 -0
  28. package/dist/export.yaml +9 -0
  29. package/dist/flag.yaml +15 -0
  30. package/dist/follow.yaml +19 -0
  31. package/dist/group.yaml +324 -0
  32. package/dist/hashtag.yaml +14 -0
  33. package/dist/ignore.yaml +14 -0
  34. package/dist/image.yaml +9 -0
  35. package/dist/intransitiveactivity.yaml +15 -0
  36. package/dist/invite.yaml +14 -0
  37. package/dist/join.yaml +14 -0
  38. package/dist/key.yaml +28 -0
  39. package/dist/leave.yaml +14 -0
  40. package/dist/like.yaml +16 -0
  41. package/dist/link.yaml +101 -0
  42. package/dist/listen.yaml +12 -0
  43. package/dist/lookup.test.d.ts +2 -0
  44. package/dist/lookup.test.js +690 -0
  45. package/dist/mention.yaml +9 -0
  46. package/dist/mod.cjs +42036 -0
  47. package/dist/mod.d.cts +15329 -0
  48. package/dist/mod.d.ts +15330 -0
  49. package/dist/mod.js +41936 -0
  50. package/dist/move.yaml +15 -0
  51. package/dist/multikey.yaml +36 -0
  52. package/dist/note.yaml +48 -0
  53. package/dist/object.yaml +404 -0
  54. package/dist/offer.yaml +15 -0
  55. package/dist/orderedcollection.yaml +39 -0
  56. package/dist/orderedcollectionpage.yaml +50 -0
  57. package/dist/organization.yaml +324 -0
  58. package/dist/page.yaml +11 -0
  59. package/dist/person.yaml +324 -0
  60. package/dist/place.yaml +75 -0
  61. package/dist/profile.yaml +26 -0
  62. package/dist/propertyvalue.yaml +32 -0
  63. package/dist/question.yaml +103 -0
  64. package/dist/read.yaml +13 -0
  65. package/dist/reject.yaml +14 -0
  66. package/dist/relationship.yaml +52 -0
  67. package/dist/remove.yaml +14 -0
  68. package/dist/service.yaml +324 -0
  69. package/dist/source.yaml +26 -0
  70. package/dist/tentativeaccept.yaml +14 -0
  71. package/dist/tentativereject.yaml +14 -0
  72. package/dist/tombstone.yaml +24 -0
  73. package/dist/travel.yaml +16 -0
  74. package/dist/type-CNuABalk.js +13 -0
  75. package/dist/type.test.d.ts +2 -0
  76. package/dist/type.test.js +24 -0
  77. package/dist/undo.yaml +26 -0
  78. package/dist/update.yaml +58 -0
  79. package/dist/utils-BSWXlrig.js +13 -0
  80. package/dist/video.yaml +11 -0
  81. package/dist/view.yaml +13 -0
  82. package/dist/vocab-DBispxj5.js +41603 -0
  83. package/dist/vocab.test.d.ts +2 -0
  84. package/dist/vocab.test.js +1304 -0
  85. package/package.json +79 -0
  86. package/scripts/codegen.ts +20 -0
  87. package/src/__snapshots__/vocab.test.ts.snap +7903 -0
  88. package/src/accept.yaml +15 -0
  89. package/src/activity.yaml +98 -0
  90. package/src/actor.test.ts +263 -0
  91. package/src/actor.ts +293 -0
  92. package/src/add.yaml +16 -0
  93. package/src/announce.yaml +30 -0
  94. package/src/application.yaml +324 -0
  95. package/src/arrive.yaml +15 -0
  96. package/src/article.yaml +46 -0
  97. package/src/audio.yaml +11 -0
  98. package/src/block.yaml +16 -0
  99. package/src/chatmessage.yaml +50 -0
  100. package/src/collection.yaml +154 -0
  101. package/src/collectionpage.yaml +55 -0
  102. package/src/constants.ts +11 -0
  103. package/src/create.yaml +28 -0
  104. package/src/dataintegrityproof.yaml +56 -0
  105. package/src/delete.yaml +27 -0
  106. package/src/didservice.yaml +22 -0
  107. package/src/dislike.yaml +14 -0
  108. package/src/document.yaml +31 -0
  109. package/src/emoji.yaml +12 -0
  110. package/src/emojireact.yaml +17 -0
  111. package/src/endpoints.yaml +85 -0
  112. package/src/event.yaml +11 -0
  113. package/src/export.yaml +9 -0
  114. package/src/flag.yaml +15 -0
  115. package/src/follow.yaml +19 -0
  116. package/src/group.yaml +324 -0
  117. package/src/handle.ts +104 -0
  118. package/src/hashtag.yaml +14 -0
  119. package/src/ignore.yaml +14 -0
  120. package/src/image.yaml +9 -0
  121. package/src/intransitiveactivity.yaml +15 -0
  122. package/src/invite.yaml +14 -0
  123. package/src/join.yaml +14 -0
  124. package/src/key.yaml +28 -0
  125. package/src/keys.ts +50 -0
  126. package/src/leave.yaml +14 -0
  127. package/src/like.yaml +16 -0
  128. package/src/link.yaml +101 -0
  129. package/src/listen.yaml +12 -0
  130. package/src/lookup.test.ts +681 -0
  131. package/src/lookup.ts +318 -0
  132. package/src/mention.yaml +9 -0
  133. package/src/mod.ts +57 -0
  134. package/src/move.yaml +15 -0
  135. package/src/multikey.yaml +36 -0
  136. package/src/note.yaml +48 -0
  137. package/src/object.yaml +404 -0
  138. package/src/offer.yaml +15 -0
  139. package/src/orderedcollection.yaml +39 -0
  140. package/src/orderedcollectionpage.yaml +50 -0
  141. package/src/organization.yaml +324 -0
  142. package/src/page.yaml +11 -0
  143. package/src/person.yaml +324 -0
  144. package/src/place.yaml +75 -0
  145. package/src/profile.yaml +26 -0
  146. package/src/propertyvalue.yaml +32 -0
  147. package/src/question.yaml +103 -0
  148. package/src/read.yaml +13 -0
  149. package/src/reject.yaml +14 -0
  150. package/src/relationship.yaml +52 -0
  151. package/src/remove.yaml +14 -0
  152. package/src/service.yaml +324 -0
  153. package/src/source.yaml +26 -0
  154. package/src/tentativeaccept.yaml +14 -0
  155. package/src/tentativereject.yaml +14 -0
  156. package/src/tombstone.yaml +24 -0
  157. package/src/travel.yaml +16 -0
  158. package/src/type.test.ts +20 -0
  159. package/src/type.ts +102 -0
  160. package/src/undo.yaml +26 -0
  161. package/src/update.yaml +58 -0
  162. package/src/utils.ts +9 -0
  163. package/src/video.yaml +11 -0
  164. package/src/view.yaml +13 -0
  165. package/src/vocab.bench.ts +204 -0
  166. package/src/vocab.test.ts +2014 -0
  167. package/tsdown.config.ts +65 -0
@@ -0,0 +1,1304 @@
1
+
2
+ import { Temporal } from "@js-temporal/polyfill";
3
+ globalThis.addEventListener = () => {};
4
+
5
+ import { Activity, Announce, Collection, Create, CryptographicKey, Follow, Hashtag, Link, Note, Object as Object$1, OrderedCollectionPage, Person, Place, Question, Source, vocab_exports } from "./vocab-DBispxj5.js";
6
+ import { assertInstanceOf } from "./utils-BSWXlrig.js";
7
+ import { mockDocumentLoader, test } from "@fedify/fixture";
8
+ import { deepStrictEqual, notDeepStrictEqual, ok, rejects, throws } from "node:assert/strict";
9
+ import { LanguageString, decodeMultibase } from "@fedify/vocab-runtime";
10
+ import { pascalCase } from "es-toolkit";
11
+ import { areAllScalarTypes, loadSchemaFiles } from "@fedify/vocab-tools";
12
+
13
+ //#region src/vocab.test.ts
14
+ test("new Object()", () => {
15
+ const obj = new Object$1({
16
+ name: "Test",
17
+ contents: [new LanguageString("Hello", "en"), new LanguageString("你好", "zh")]
18
+ });
19
+ deepStrictEqual(obj.name, "Test");
20
+ deepStrictEqual(obj.contents[0], new LanguageString("Hello", "en"));
21
+ deepStrictEqual(obj.contents[1], new LanguageString("你好", "zh"));
22
+ throws(() => new Object$1({ id: 123 }), TypeError);
23
+ throws(() => new Object$1({
24
+ name: "singular",
25
+ names: ["plural"]
26
+ }), TypeError);
27
+ throws(() => new Object$1({ name: 123 }), TypeError);
28
+ throws(() => new Object$1({ names: "foo" }), TypeError);
29
+ throws(() => new Object$1({ names: ["foo", 123] }), TypeError);
30
+ });
31
+ test("Object.clone()", () => {
32
+ const obj = new Object$1({
33
+ id: new URL("https://example.com/"),
34
+ name: "Test",
35
+ contents: [new LanguageString("Hello", "en"), new LanguageString("你好", "zh")]
36
+ });
37
+ const clone = obj.clone({ content: "Modified" });
38
+ assertInstanceOf(clone, Object$1);
39
+ deepStrictEqual(clone.id, new URL("https://example.com/"));
40
+ deepStrictEqual(clone.name, "Test");
41
+ deepStrictEqual(clone.content, "Modified");
42
+ const cloned2 = obj.clone({ id: new URL("https://example.com/modified") });
43
+ assertInstanceOf(cloned2, Object$1);
44
+ deepStrictEqual(cloned2.id, new URL("https://example.com/modified"));
45
+ deepStrictEqual(cloned2.name, "Test");
46
+ deepStrictEqual(cloned2.contents, [new LanguageString("Hello", "en"), new LanguageString("你好", "zh")]);
47
+ throws(() => obj.clone({ id: 123 }), TypeError);
48
+ throws(() => obj.clone({
49
+ name: "singular",
50
+ names: ["plural"]
51
+ }), TypeError);
52
+ throws(() => obj.clone({ name: 123 }), TypeError);
53
+ throws(() => obj.clone({ names: "foo" }), TypeError);
54
+ throws(() => obj.clone({ names: ["foo", 123] }), TypeError);
55
+ });
56
+ test("Object.fromJsonLd()", async () => {
57
+ const obj = await Object$1.fromJsonLd({
58
+ "@context": "https://www.w3.org/ns/activitystreams",
59
+ "type": "Object",
60
+ "name": "Test",
61
+ "contentMap": {
62
+ "en": "Hello",
63
+ "zh": "你好"
64
+ },
65
+ "source": {
66
+ "content": "Hello",
67
+ "mediaType": "text/plain"
68
+ },
69
+ "published": "2025-01-01 12:34:56"
70
+ }, {
71
+ documentLoader: mockDocumentLoader,
72
+ contextLoader: mockDocumentLoader
73
+ });
74
+ assertInstanceOf(obj, Object$1);
75
+ deepStrictEqual(obj.name, "Test");
76
+ deepStrictEqual(obj.contents, [new LanguageString("Hello", "en"), new LanguageString("你好", "zh")]);
77
+ assertInstanceOf(obj.source, Source);
78
+ deepStrictEqual(obj.source.content, "Hello");
79
+ deepStrictEqual(obj.source.mediaType, "text/plain");
80
+ deepStrictEqual(obj.published, Temporal.Instant.from("2025-01-01T12:34:56Z"));
81
+ const createJsonLd = {
82
+ "@context": "https://www.w3.org/ns/activitystreams",
83
+ "type": "Create",
84
+ "name": "Test",
85
+ "contentMap": {
86
+ "en": "Hello",
87
+ "zh": "你好"
88
+ },
89
+ "object": {
90
+ "type": "Note",
91
+ "content": "Content"
92
+ }
93
+ };
94
+ const create = await Object$1.fromJsonLd(createJsonLd, {
95
+ documentLoader: mockDocumentLoader,
96
+ contextLoader: mockDocumentLoader
97
+ });
98
+ assertInstanceOf(create, Create);
99
+ deepStrictEqual(create.name, "Test");
100
+ deepStrictEqual(create.contents, [new LanguageString("Hello", "en"), new LanguageString("你好", "zh")]);
101
+ deepStrictEqual(await create.toJsonLd(), createJsonLd);
102
+ const note = await create.getObject();
103
+ assertInstanceOf(note, Note);
104
+ deepStrictEqual(note.content, "Content");
105
+ const empty = await Object$1.fromJsonLd({});
106
+ assertInstanceOf(empty, Object$1);
107
+ await rejects(() => Object$1.fromJsonLd(null), TypeError);
108
+ await rejects(() => Object$1.fromJsonLd(void 0), TypeError);
109
+ });
110
+ test("Object.toJsonLd()", async () => {
111
+ const obj = new Object$1({
112
+ name: "Test",
113
+ contents: [new LanguageString("Hello", "en"), new LanguageString("你好", "zh")]
114
+ });
115
+ deepStrictEqual(await obj.toJsonLd({
116
+ format: "expand",
117
+ contextLoader: mockDocumentLoader
118
+ }), [{
119
+ "@type": ["https://www.w3.org/ns/activitystreams#Object"],
120
+ "https://www.w3.org/ns/activitystreams#name": [{ "@value": "Test" }],
121
+ "https://www.w3.org/ns/activitystreams#content": [{
122
+ "@value": "Hello",
123
+ "@language": "en"
124
+ }, {
125
+ "@value": "你好",
126
+ "@language": "zh"
127
+ }]
128
+ }]);
129
+ deepStrictEqual(await obj.toJsonLd({ contextLoader: mockDocumentLoader }), {
130
+ "@context": [
131
+ "https://www.w3.org/ns/activitystreams",
132
+ "https://w3id.org/security/data-integrity/v1",
133
+ {
134
+ fedibird: "http://fedibird.com/ns#",
135
+ sensitive: "as:sensitive",
136
+ emojiReactions: {
137
+ "@id": "fedibird:emojiReactions",
138
+ "@type": "@id"
139
+ }
140
+ }
141
+ ],
142
+ type: "Object",
143
+ name: "Test",
144
+ contentMap: {
145
+ en: "Hello",
146
+ zh: "你好"
147
+ }
148
+ });
149
+ });
150
+ test("Note.toJsonLd()", async () => {
151
+ const note = new Note({ tags: [new Hashtag({
152
+ name: "#Fedify",
153
+ href: new URL("https://fedify.dev/")
154
+ })] });
155
+ deepStrictEqual(await note.toJsonLd({ contextLoader: mockDocumentLoader }), {
156
+ "@context": [
157
+ "https://www.w3.org/ns/activitystreams",
158
+ "https://w3id.org/security/data-integrity/v1",
159
+ {
160
+ Emoji: "toot:Emoji",
161
+ Hashtag: "as:Hashtag",
162
+ _misskey_quote: "misskey:_misskey_quote",
163
+ fedibird: "http://fedibird.com/ns#",
164
+ misskey: "https://misskey-hub.net/ns#",
165
+ quoteUri: "fedibird:quoteUri",
166
+ quoteUrl: "as:quoteUrl",
167
+ sensitive: "as:sensitive",
168
+ toot: "http://joinmastodon.org/ns#",
169
+ emojiReactions: {
170
+ "@id": "fedibird:emojiReactions",
171
+ "@type": "@id"
172
+ }
173
+ }
174
+ ],
175
+ tag: {
176
+ "@context": ["https://www.w3.org/ns/activitystreams", { Hashtag: "as:Hashtag" }],
177
+ href: "https://fedify.dev/",
178
+ name: "#Fedify",
179
+ type: "Hashtag"
180
+ },
181
+ type: "Note"
182
+ });
183
+ const noteWithName = note.clone({ name: "Test" });
184
+ deepStrictEqual(await noteWithName.toJsonLd({ contextLoader: mockDocumentLoader }), await noteWithName.toJsonLd({
185
+ contextLoader: mockDocumentLoader,
186
+ format: "compact"
187
+ }));
188
+ });
189
+ test("Activity.fromJsonLd()", async () => {
190
+ const follow = await Activity.fromJsonLd({
191
+ "@context": "https://www.w3.org/ns/activitystreams",
192
+ id: "https://activitypub.academy/80c50305-7405-4e38-809f-697647a1f679",
193
+ type: "Follow",
194
+ actor: "https://activitypub.academy/users/egulia_anbeiss",
195
+ object: "https://example.com/users/hongminhee"
196
+ }, {
197
+ documentLoader: mockDocumentLoader,
198
+ contextLoader: mockDocumentLoader
199
+ });
200
+ assertInstanceOf(follow, Follow);
201
+ deepStrictEqual(follow.id, new URL("https://activitypub.academy/80c50305-7405-4e38-809f-697647a1f679"));
202
+ deepStrictEqual(follow.actorId, new URL("https://activitypub.academy/users/egulia_anbeiss"));
203
+ deepStrictEqual(follow.objectId, new URL("https://example.com/users/hongminhee"));
204
+ const create = await Activity.fromJsonLd({
205
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
206
+ type: "Create",
207
+ actor: "https://server.example/users/alice",
208
+ object: {
209
+ type: "Note",
210
+ content: "Hello world"
211
+ },
212
+ proof: {
213
+ type: "DataIntegrityProof",
214
+ cryptosuite: "eddsa-jcs-2022",
215
+ verificationMethod: "https://server.example/users/alice#ed25519-key",
216
+ proofPurpose: "assertionMethod",
217
+ proofValue: "z3sXaxjKs4M3BRicwWA9peyNPJvJqxtGsDmpt1jjoHCjgeUf71TRFz56osPSfDErszyLp5Ks1EhYSgpDaNM977Rg2",
218
+ created: "2023-02-24T23:36:38Z"
219
+ }
220
+ }, {
221
+ documentLoader: mockDocumentLoader,
222
+ contextLoader: mockDocumentLoader
223
+ });
224
+ const proofs = [];
225
+ for await (const proof of create.getProofs()) proofs.push(proof);
226
+ deepStrictEqual(proofs.length, 1);
227
+ deepStrictEqual(proofs[0].cryptosuite, "eddsa-jcs-2022");
228
+ deepStrictEqual(proofs[0].verificationMethodId, new URL("https://server.example/users/alice#ed25519-key"));
229
+ deepStrictEqual(proofs[0].proofPurpose, "assertionMethod");
230
+ deepStrictEqual(proofs[0].proofValue, decodeMultibase("z3sXaxjKs4M3BRicwWA9peyNPJvJqxtGsDmpt1jjoHCjgeUf71TRFz56osPSfDErszyLp5Ks1EhYSgpDaNM977Rg2"));
231
+ deepStrictEqual(proofs[0].created, Temporal.Instant.from("2023-02-24T23:36:38Z"));
232
+ });
233
+ test({
234
+ name: "Activity.getObject()",
235
+ permissions: {
236
+ env: true,
237
+ read: true
238
+ },
239
+ async fn() {
240
+ const activity = new Activity({ object: new URL("https://example.com/announce") });
241
+ const announce = await activity.getObject({
242
+ documentLoader: mockDocumentLoader,
243
+ contextLoader: mockDocumentLoader
244
+ });
245
+ assertInstanceOf(announce, Announce);
246
+ deepStrictEqual(announce.id, new URL("https://example.com/announce"));
247
+ const object = await announce.getObject();
248
+ assertInstanceOf(object, Object$1);
249
+ deepStrictEqual(object.id, new URL("https://example.com/object"));
250
+ deepStrictEqual(object.name, "Fetched object");
251
+ const jsonLd = await activity.toJsonLd();
252
+ deepStrictEqual(jsonLd, {
253
+ "@context": [
254
+ "https://w3id.org/identity/v1",
255
+ "https://www.w3.org/ns/activitystreams",
256
+ "https://w3id.org/security/v1",
257
+ "https://w3id.org/security/data-integrity/v1"
258
+ ],
259
+ type: "Activity",
260
+ object: {
261
+ id: "https://example.com/announce",
262
+ type: "Announce",
263
+ object: {
264
+ type: "Object",
265
+ id: "https://example.com/object",
266
+ name: "Fetched object"
267
+ }
268
+ }
269
+ });
270
+ const activity2 = new Activity({ object: new URL("https://example.com/not-found") });
271
+ deepStrictEqual(await activity2.getObject({ suppressError: true }), null);
272
+ const activity3 = await Activity.fromJsonLd({
273
+ "@context": "https://www.w3.org/ns/activitystreams",
274
+ type: "Create",
275
+ object: {
276
+ "@context": "https://www.w3.org/ns/activitystreams",
277
+ type: "Note",
278
+ content: "Hello world"
279
+ }
280
+ });
281
+ const object3 = await activity3.getObject();
282
+ assertInstanceOf(object3, Note);
283
+ deepStrictEqual(await object3.toJsonLd(), {
284
+ "@context": "https://www.w3.org/ns/activitystreams",
285
+ type: "Note",
286
+ content: "Hello world"
287
+ });
288
+ }
289
+ });
290
+ test({
291
+ name: "Activity.getObjects()",
292
+ permissions: {
293
+ env: true,
294
+ read: true
295
+ },
296
+ async fn() {
297
+ const activity = new Activity({ objects: [new URL("https://example.com/object"), new Object$1({ name: "Second object" })] });
298
+ const objects = await Array.fromAsync(activity.getObjects({
299
+ documentLoader: mockDocumentLoader,
300
+ contextLoader: mockDocumentLoader
301
+ }));
302
+ deepStrictEqual(objects.length, 2);
303
+ assertInstanceOf(objects[0], Object$1);
304
+ deepStrictEqual(objects[0].id, new URL("https://example.com/object"));
305
+ deepStrictEqual(objects[0].name, "Fetched object");
306
+ assertInstanceOf(objects[1], Object$1);
307
+ deepStrictEqual(objects[1].name, "Second object");
308
+ const activity2 = new Activity({ objects: [new URL("https://example.com/not-found"), new Object$1({ name: "Second object" })] });
309
+ const objects2 = await Array.fromAsync(activity2.getObjects({ suppressError: true }));
310
+ deepStrictEqual(objects2.length, 1);
311
+ assertInstanceOf(objects2[0], Object$1);
312
+ deepStrictEqual(objects2[0].name, "Second object");
313
+ }
314
+ });
315
+ test("Activity.clone()", async () => {
316
+ const activity = new Activity({
317
+ actor: new Person({ name: "John Doe" }),
318
+ object: new Object$1({ name: "Test" }),
319
+ name: "Test",
320
+ summary: "Test"
321
+ });
322
+ const clone = activity.clone({
323
+ object: new Object$1({ name: "Modified" }),
324
+ summary: "Modified"
325
+ });
326
+ deepStrictEqual((await activity.getActor())?.name, "John Doe");
327
+ deepStrictEqual((await clone.getActor())?.name, "John Doe");
328
+ deepStrictEqual((await activity.getObject())?.name, "Test");
329
+ deepStrictEqual((await clone.getObject())?.name, "Modified");
330
+ deepStrictEqual(activity.name, "Test");
331
+ deepStrictEqual(clone.name, "Test");
332
+ deepStrictEqual(activity.summary, "Test");
333
+ deepStrictEqual(clone.summary, "Modified");
334
+ throws(() => activity.clone({
335
+ summary: "singular",
336
+ summaries: ["plural"]
337
+ }), TypeError);
338
+ });
339
+ test("Question.voters", async () => {
340
+ const question = new Question({ voters: 123 });
341
+ const json = await question.toJsonLd({ format: "compact" });
342
+ ok(typeof json === "object" && json != null);
343
+ ok("votersCount" in json);
344
+ deepStrictEqual(json["votersCount"], 123);
345
+ });
346
+ test({
347
+ name: "Deno.inspect(Object)",
348
+ ignore: !("Deno" in globalThis),
349
+ fn() {
350
+ const obj = new Object$1({
351
+ id: new URL("https://example.com/"),
352
+ attribution: new URL("https://example.com/foo"),
353
+ name: "Test",
354
+ contents: [new LanguageString("Hello", "en"), new LanguageString("你好", "zh")]
355
+ });
356
+ deepStrictEqual(Deno.inspect(obj, {
357
+ colors: false,
358
+ sorted: true,
359
+ compact: false
360
+ }), "Deno" in globalThis ? "Object {\n attribution: URL \"https://example.com/foo\",\n contents: [\n <en> \"Hello\",\n <zh> \"你好\"\n ],\n id: URL \"https://example.com/\",\n name: \"Test\"\n}" : "Object {\n attribution: URL 'https://example.com/foo',\n contents: [\n <en> 'Hello',\n <zh> '你好'\n ],\n id: URL 'https://example.com/',\n name: 'Test'\n}");
361
+ }
362
+ });
363
+ test("Person.fromJsonLd()", async () => {
364
+ const person = await Person.fromJsonLd({
365
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
366
+ "id": "https://todon.eu/users/hongminhee",
367
+ "publicKey": {
368
+ "id": "https://todon.eu/users/hongminhee#main-key",
369
+ "owner": "https://todon.eu/users/hongminhee",
370
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxsRuvCkgJtflBTl4OVsm\nnt/J1mQfZasfJtN33dcZ3d1lJroxmgmMu69zjGEAwkNbMQaWNLqC4eogkJaeJ4RR\n5MHYXkL9nNilVoTkjX5BVit3puzs7XJ7WQnKQgQMI+ezn24GHsZ/v1JIo77lerX5\nk4HNwTNVt+yaZVQWaOMR3+6FwziQR6kd0VuG9/a9dgAnz2cEoORRC1i4W7IZaB1s\nZnh1WbHbevlGd72HSXll5rocPIHn8gq6xpBgpHwRphlRsgn4KHaJ6brXDIJjrnQh\nIe/YUBOGj/ImSEXhRwlFerKsoAVnZ0Hwbfa46qk44TAt8CyoPMWmpK6pt0ng4pQ2\nuwIDAQAB\n-----END PUBLIC KEY-----\n"
371
+ }
372
+ }, {
373
+ documentLoader: mockDocumentLoader,
374
+ contextLoader: mockDocumentLoader,
375
+ baseUrl: new URL("https://todon.eu/")
376
+ });
377
+ deepStrictEqual(person.publicKeyId, new URL("https://todon.eu/users/hongminhee#main-key"));
378
+ const publicKey = await person.getPublicKey({ documentLoader: mockDocumentLoader });
379
+ assertInstanceOf(publicKey, CryptographicKey);
380
+ deepStrictEqual(publicKey?.ownerId, new URL("https://todon.eu/users/hongminhee"));
381
+ const person2 = await Person.fromJsonLd({
382
+ "@context": ["https://www.w3.org/ns/activitystreams", { alsoKnownAs: {
383
+ "@id": "as:alsoKnownAs",
384
+ "@type": "@id"
385
+ } }],
386
+ "type": "Person",
387
+ "alsoKnownAs": "at://did:plc:x7xdowahlhm5xulzqw4ehv6q"
388
+ });
389
+ deepStrictEqual(person2.aliasId, new URL("at://did%3Aplc%3Ax7xdowahlhm5xulzqw4ehv6q"));
390
+ });
391
+ test("Person.toJsonLd()", async () => {
392
+ const person = new Person({ aliases: [new URL("https://example.com/alias")] });
393
+ deepStrictEqual(await person.toJsonLd(), {
394
+ "@context": [
395
+ "https://www.w3.org/ns/activitystreams",
396
+ "https://w3id.org/security/v1",
397
+ "https://w3id.org/security/data-integrity/v1",
398
+ "https://www.w3.org/ns/did/v1",
399
+ "https://w3id.org/security/multikey/v1",
400
+ {
401
+ PropertyValue: "schema:PropertyValue",
402
+ alsoKnownAs: {
403
+ "@id": "as:alsoKnownAs",
404
+ "@type": "@id"
405
+ },
406
+ movedTo: {
407
+ "@id": "as:movedTo",
408
+ "@type": "@id"
409
+ },
410
+ discoverable: "toot:discoverable",
411
+ featured: {
412
+ "@id": "toot:featured",
413
+ "@type": "@id"
414
+ },
415
+ featuredTags: {
416
+ "@id": "toot:featuredTags",
417
+ "@type": "@id"
418
+ },
419
+ indexable: "toot:indexable",
420
+ _misskey_followedMessage: "misskey:_misskey_followedMessage",
421
+ isCat: "misskey:isCat",
422
+ manuallyApprovesFollowers: "as:manuallyApprovesFollowers",
423
+ memorial: "toot:memorial",
424
+ misskey: "https://misskey-hub.net/ns#",
425
+ schema: "http://schema.org#",
426
+ suspended: "toot:suspended",
427
+ toot: "http://joinmastodon.org/ns#",
428
+ value: "schema:value",
429
+ Emoji: "toot:Emoji"
430
+ }
431
+ ],
432
+ alsoKnownAs: "https://example.com/alias",
433
+ type: "Person"
434
+ });
435
+ });
436
+ test("Collection.fromJsonLd()", async () => {
437
+ const collection = await Collection.fromJsonLd({
438
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/fep/5711"],
439
+ "type": "Collection",
440
+ "id": "https://example.com/collection/jzc50wc28l",
441
+ "inboxOf": "https://example.com/person/bup9a8eqm"
442
+ });
443
+ deepStrictEqual(collection.id, new URL("https://example.com/collection/jzc50wc28l"));
444
+ deepStrictEqual(collection.inboxOfId, new URL("https://example.com/person/bup9a8eqm"));
445
+ });
446
+ test("Note.quoteUrl", async () => {
447
+ const note = new Note({ quoteUrl: new URL("https://example.com/object") });
448
+ const expected = {
449
+ "@context": [
450
+ "https://www.w3.org/ns/activitystreams",
451
+ "https://w3id.org/security/data-integrity/v1",
452
+ {
453
+ Emoji: "toot:Emoji",
454
+ Hashtag: "as:Hashtag",
455
+ _misskey_quote: "misskey:_misskey_quote",
456
+ fedibird: "http://fedibird.com/ns#",
457
+ misskey: "https://misskey-hub.net/ns#",
458
+ quoteUri: "fedibird:quoteUri",
459
+ quoteUrl: "as:quoteUrl",
460
+ sensitive: "as:sensitive",
461
+ toot: "http://joinmastodon.org/ns#",
462
+ emojiReactions: {
463
+ "@id": "fedibird:emojiReactions",
464
+ "@type": "@id"
465
+ }
466
+ }
467
+ ],
468
+ _misskey_quote: "https://example.com/object",
469
+ quoteUri: "https://example.com/object",
470
+ quoteUrl: "https://example.com/object",
471
+ type: "Note"
472
+ };
473
+ deepStrictEqual(await note.toJsonLd(), expected);
474
+ deepStrictEqual(await note.toJsonLd({ format: "compact" }), expected);
475
+ const jsonLd = {
476
+ "@context": ["https://www.w3.org/ns/activitystreams", {
477
+ _misskey_quote: "misskey:_misskey_quote",
478
+ fedibird: "http://fedibird.com/ns#",
479
+ misskey: "https://misskey-hub.net/ns#",
480
+ quoteUri: "fedibird:quoteUri",
481
+ quoteUrl: "as:quoteUrl"
482
+ }],
483
+ type: "Note",
484
+ quoteUrl: "https://example.com/object",
485
+ _misskey_quote: "https://example.com/object2",
486
+ quoteUri: "https://example.com/object3"
487
+ };
488
+ const loaded = await Note.fromJsonLd(jsonLd);
489
+ deepStrictEqual(loaded.quoteUrl, new URL("https://example.com/object"));
490
+ delete jsonLd.quoteUrl;
491
+ const loaded2 = await Note.fromJsonLd(jsonLd);
492
+ deepStrictEqual(loaded2.quoteUrl, new URL("https://example.com/object2"));
493
+ delete jsonLd._misskey_quote;
494
+ const loaded3 = await Note.fromJsonLd(jsonLd);
495
+ deepStrictEqual(loaded3.quoteUrl, new URL("https://example.com/object3"));
496
+ });
497
+ test("Key.publicKey", async () => {
498
+ const jwk = {
499
+ kty: "RSA",
500
+ alg: "RS256",
501
+ n: "xsRuvCkgJtflBTl4OVsmnt_J1mQfZasfJtN33dcZ3d1lJroxmgmMu69zjGEAwkNbMQaWNLqC4eogkJaeJ4RR5MHYXkL9nNilVoTkjX5BVit3puzs7XJ7WQnKQgQMI-ezn24GHsZ_v1JIo77lerX5k4HNwTNVt-yaZVQWaOMR3-6FwziQR6kd0VuG9_a9dgAnz2cEoORRC1i4W7IZaB1sZnh1WbHbevlGd72HSXll5rocPIHn8gq6xpBgpHwRphlRsgn4KHaJ6brXDIJjrnQhIe_YUBOGj_ImSEXhRwlFerKsoAVnZ0Hwbfa46qk44TAt8CyoPMWmpK6pt0ng4pQ2uw",
502
+ e: "AQAB",
503
+ key_ops: ["verify"],
504
+ ext: true
505
+ };
506
+ const key = new CryptographicKey({ publicKey: await crypto.subtle.importKey("jwk", jwk, {
507
+ name: "RSASSA-PKCS1-v1_5",
508
+ hash: "SHA-256"
509
+ }, true, ["verify"]) });
510
+ const jsonLd = await key.toJsonLd({ contextLoader: mockDocumentLoader });
511
+ deepStrictEqual(jsonLd, {
512
+ "@context": "https://w3id.org/security/v1",
513
+ publicKeyPem: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxsRuvCkgJtflBTl4OVsm\nnt/J1mQfZasfJtN33dcZ3d1lJroxmgmMu69zjGEAwkNbMQaWNLqC4eogkJaeJ4RR\n5MHYXkL9nNilVoTkjX5BVit3puzs7XJ7WQnKQgQMI+ezn24GHsZ/v1JIo77lerX5\nk4HNwTNVt+yaZVQWaOMR3+6FwziQR6kd0VuG9/a9dgAnz2cEoORRC1i4W7IZaB1s\nZnh1WbHbevlGd72HSXll5rocPIHn8gq6xpBgpHwRphlRsgn4KHaJ6brXDIJjrnQh\nIe/YUBOGj/ImSEXhRwlFerKsoAVnZ0Hwbfa46qk44TAt8CyoPMWmpK6pt0ng4pQ2\nuwIDAQAB\n-----END PUBLIC KEY-----\n",
514
+ type: "CryptographicKey"
515
+ });
516
+ const loadedKey = await CryptographicKey.fromJsonLd(jsonLd, {
517
+ documentLoader: mockDocumentLoader,
518
+ contextLoader: mockDocumentLoader
519
+ });
520
+ notDeepStrictEqual(loadedKey.publicKey, null);
521
+ deepStrictEqual(await crypto.subtle.exportKey("jwk", loadedKey.publicKey), jwk);
522
+ });
523
+ test("Place.fromJsonLd()", async () => {
524
+ const place = await Place.fromJsonLd({
525
+ "@context": "https://www.w3.org/ns/activitystreams",
526
+ type: "Place",
527
+ name: "Fresno Area",
528
+ latitude: 36.75,
529
+ longitude: 119.7667,
530
+ radius: 15,
531
+ units: "miles"
532
+ }, {
533
+ documentLoader: mockDocumentLoader,
534
+ contextLoader: mockDocumentLoader
535
+ });
536
+ assertInstanceOf(place, Place);
537
+ deepStrictEqual(place.name, "Fresno Area");
538
+ deepStrictEqual(place.latitude, 36.75);
539
+ deepStrictEqual(place.longitude, 119.7667);
540
+ deepStrictEqual(place.radius, 15);
541
+ deepStrictEqual(place.units, "miles");
542
+ let jsonLd = await place.toJsonLd({ contextLoader: mockDocumentLoader });
543
+ deepStrictEqual(jsonLd, {
544
+ "@context": "https://www.w3.org/ns/activitystreams",
545
+ type: "Place",
546
+ name: "Fresno Area",
547
+ latitude: 36.75,
548
+ longitude: 119.7667,
549
+ radius: 15,
550
+ units: "miles"
551
+ });
552
+ jsonLd = await place.toJsonLd({
553
+ format: "compact",
554
+ contextLoader: mockDocumentLoader
555
+ });
556
+ deepStrictEqual(jsonLd, {
557
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
558
+ type: "Place",
559
+ name: "Fresno Area",
560
+ latitude: 36.75,
561
+ longitude: 119.7667,
562
+ radius: 15,
563
+ units: "miles"
564
+ });
565
+ });
566
+ test("Actor.getOutbox()", async () => {
567
+ const person = new Person({ outbox: new URL("https://example.com/orderedcollectionpage") });
568
+ const outbox = await person.getOutbox({ documentLoader: mockDocumentLoader });
569
+ assertInstanceOf(outbox, OrderedCollectionPage);
570
+ deepStrictEqual(outbox.totalItems, 1);
571
+ });
572
+ test("Link.fromJsonLd()", async () => {
573
+ const link = await Link.fromJsonLd({
574
+ "@context": "https://www.w3.org/ns/activitystreams",
575
+ "type": "Link",
576
+ "rel": "canonical",
577
+ "href": "at://did:plc:ia76kvnndjutgedggx2ibrem/app.bsky.feed.post/3lyxjjs27jkqg"
578
+ });
579
+ deepStrictEqual(link.rel, "canonical");
580
+ deepStrictEqual(link.href, new URL("at://did%3Aplc%3Aia76kvnndjutgedggx2ibrem/app.bsky.feed.post/3lyxjjs27jkqg"));
581
+ const link2 = await Link.fromJsonLd({
582
+ "@context": "https://www.w3.org/ns/activitystreams",
583
+ "type": "Link",
584
+ "href": "at://bnewbold.bsky.team/app.bsky.feed.post/3jwdwj2ctlk26"
585
+ });
586
+ deepStrictEqual(link2.href, new URL("at://bnewbold.bsky.team/app.bsky.feed.post/3jwdwj2ctlk26"));
587
+ const link3 = await Link.fromJsonLd({
588
+ "@context": "https://www.w3.org/ns/activitystreams",
589
+ "type": "Link",
590
+ "href": "at://did:plc:ia76kvnndjutgedggx2ibrem"
591
+ });
592
+ deepStrictEqual(link3.href, new URL("at://did%3Aplc%3Aia76kvnndjutgedggx2ibrem"));
593
+ });
594
+ test("Person.fromJsonLd() with relative URLs", async () => {
595
+ const json = {
596
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
597
+ id: "https://example.com/ap/actors/019382d3-63d7-7cf7-86e8-91e2551c306c",
598
+ type: "Person",
599
+ name: "Test User",
600
+ icon: {
601
+ type: "Image",
602
+ url: "/avatars/test-avatar.jpg"
603
+ }
604
+ };
605
+ const person = await Person.fromJsonLd(json, {
606
+ documentLoader: mockDocumentLoader,
607
+ contextLoader: mockDocumentLoader
608
+ });
609
+ const icon = await person.getIcon();
610
+ deepStrictEqual(icon?.url, new URL("https://example.com/avatars/test-avatar.jpg"));
611
+ const json2 = {
612
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
613
+ id: "https://example.com/ap/actors/019382d3-63d7-7cf7-86e8-91e2551c306c",
614
+ type: "Person",
615
+ name: "Test User",
616
+ icon: {
617
+ id: "https://media.example.com/avatars/test-avatar.jpg",
618
+ type: "Image",
619
+ url: "/avatars/test-avatar.jpg"
620
+ }
621
+ };
622
+ const person2 = await Person.fromJsonLd(json2, {
623
+ documentLoader: mockDocumentLoader,
624
+ contextLoader: mockDocumentLoader
625
+ });
626
+ const icon2 = await person2.getIcon();
627
+ deepStrictEqual(icon2?.url, new URL("https://media.example.com/avatars/test-avatar.jpg"));
628
+ });
629
+ test("Person.fromJsonLd() with relative URLs and baseUrl", async () => {
630
+ const json = {
631
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
632
+ "id": "https://example.com/ap/actors/019382d3-63d7-7cf7-86e8-91e2551c306c",
633
+ "type": "Person",
634
+ "name": "Test User",
635
+ "icon": {
636
+ "type": "Image",
637
+ "url": "/avatars/test-avatar.jpg"
638
+ }
639
+ };
640
+ const personWithBase = await Person.fromJsonLd(json, {
641
+ documentLoader: mockDocumentLoader,
642
+ contextLoader: mockDocumentLoader,
643
+ baseUrl: new URL("https://example.com")
644
+ });
645
+ const icon = await personWithBase.getIcon();
646
+ deepStrictEqual(icon?.url, new URL("https://example.com/avatars/test-avatar.jpg"));
647
+ });
648
+ test("FEP-fe34: Trust tracking in object construction", async () => {
649
+ const note = new Note({
650
+ id: new URL("https://example.com/note"),
651
+ content: "Hello World"
652
+ });
653
+ const create = new Create({
654
+ id: new URL("https://example.com/create"),
655
+ actor: new URL("https://example.com/actor"),
656
+ object: note
657
+ });
658
+ deepStrictEqual(create.objectId, new URL("https://example.com/note"));
659
+ const result = await create.getObject();
660
+ deepStrictEqual(result, note);
661
+ deepStrictEqual(result?.content, "Hello World");
662
+ });
663
+ test("FEP-fe34: Trust tracking in object cloning", () => {
664
+ const originalNote = new Note({
665
+ id: new URL("https://example.com/note"),
666
+ content: "Original content"
667
+ });
668
+ const create = new Create({
669
+ id: new URL("https://example.com/create"),
670
+ actor: new URL("https://example.com/actor"),
671
+ object: originalNote
672
+ });
673
+ const newNote = new Note({
674
+ id: new URL("https://example.com/new-note"),
675
+ content: "New content"
676
+ });
677
+ const clonedCreate = create.clone({ object: newNote });
678
+ deepStrictEqual(clonedCreate.objectId, new URL("https://example.com/new-note"));
679
+ });
680
+ test("FEP-fe34: crossOrigin ignore behavior (default)", async () => {
681
+ const crossOriginDocumentLoader = async (url) => {
682
+ if (url === "https://different-origin.com/note") return {
683
+ documentUrl: url,
684
+ contextUrl: null,
685
+ document: {
686
+ "@context": "https://www.w3.org/ns/activitystreams",
687
+ "@type": "Note",
688
+ "@id": "https://malicious.com/fake-note",
689
+ "content": "This is a spoofed note"
690
+ }
691
+ };
692
+ throw new Error("Document not found");
693
+ };
694
+ const create = new Create({
695
+ id: new URL("https://example.com/create"),
696
+ actor: new URL("https://example.com/actor"),
697
+ object: new URL("https://different-origin.com/note")
698
+ });
699
+ const result = await create.getObject({ documentLoader: crossOriginDocumentLoader });
700
+ deepStrictEqual(result, null);
701
+ });
702
+ test("FEP-fe34: crossOrigin throw behavior", async () => {
703
+ const crossOriginDocumentLoader = async (url) => {
704
+ if (url === "https://different-origin.com/note") return {
705
+ documentUrl: url,
706
+ contextUrl: null,
707
+ document: {
708
+ "@context": "https://www.w3.org/ns/activitystreams",
709
+ "@type": "Note",
710
+ "@id": "https://malicious.com/fake-note",
711
+ "content": "This is a spoofed note"
712
+ }
713
+ };
714
+ throw new Error("Document not found");
715
+ };
716
+ const create = new Create({
717
+ id: new URL("https://example.com/create"),
718
+ actor: new URL("https://example.com/actor"),
719
+ object: new URL("https://different-origin.com/note")
720
+ });
721
+ await rejects(() => create.getObject({
722
+ documentLoader: crossOriginDocumentLoader,
723
+ crossOrigin: "throw"
724
+ }), Error);
725
+ });
726
+ test("FEP-fe34: crossOrigin trust behavior", async () => {
727
+ const crossOriginDocumentLoader = async (url) => {
728
+ if (url === "https://different-origin.com/note") return {
729
+ documentUrl: url,
730
+ contextUrl: null,
731
+ document: {
732
+ "@context": "https://www.w3.org/ns/activitystreams",
733
+ "@type": "Note",
734
+ "@id": "https://malicious.com/fake-note",
735
+ "content": "This is a spoofed note"
736
+ }
737
+ };
738
+ throw new Error("Document not found");
739
+ };
740
+ const create = new Create({
741
+ id: new URL("https://example.com/create"),
742
+ actor: new URL("https://example.com/actor"),
743
+ object: new URL("https://different-origin.com/note")
744
+ });
745
+ const result = await create.getObject({
746
+ documentLoader: crossOriginDocumentLoader,
747
+ crossOrigin: "trust"
748
+ });
749
+ assertInstanceOf(result, Note);
750
+ deepStrictEqual(result?.id, new URL("https://malicious.com/fake-note"));
751
+ deepStrictEqual(result?.content, "This is a spoofed note");
752
+ });
753
+ test("FEP-fe34: Same origin objects are trusted", async () => {
754
+ const sameOriginDocumentLoader = async (url) => {
755
+ if (url === "https://example.com/note") return {
756
+ documentUrl: url,
757
+ contextUrl: null,
758
+ document: {
759
+ "@context": "https://www.w3.org/ns/activitystreams",
760
+ "@type": "Note",
761
+ "@id": "https://example.com/note",
762
+ "content": "This is a legitimate note"
763
+ }
764
+ };
765
+ throw new Error("Document not found");
766
+ };
767
+ const create = new Create({
768
+ id: new URL("https://example.com/create"),
769
+ actor: new URL("https://example.com/actor"),
770
+ object: new URL("https://example.com/note")
771
+ });
772
+ const result = await create.getObject({ documentLoader: sameOriginDocumentLoader });
773
+ assertInstanceOf(result, Note);
774
+ deepStrictEqual(result?.id, new URL("https://example.com/note"));
775
+ deepStrictEqual(result?.content, "This is a legitimate note");
776
+ });
777
+ test("FEP-fe34: Embedded cross-origin objects from JSON-LD are ignored by default", async () => {
778
+ const createDocumentLoader = async (url) => {
779
+ if (url === "https://example.com/create") return {
780
+ documentUrl: url,
781
+ contextUrl: null,
782
+ document: {
783
+ "@context": "https://www.w3.org/ns/activitystreams",
784
+ "@type": "Create",
785
+ "@id": "https://example.com/create",
786
+ "actor": "https://example.com/actor",
787
+ "object": {
788
+ "@type": "Note",
789
+ "@id": "https://different-origin.com/note",
790
+ "content": "Embedded note from JSON-LD"
791
+ }
792
+ }
793
+ };
794
+ throw new Error("Document not found");
795
+ };
796
+ const create = await Create.fromJsonLd(await createDocumentLoader("https://example.com/create").then((r) => r.document), { documentLoader: createDocumentLoader });
797
+ const objectDocumentLoader = async (url) => {
798
+ if (url === "https://different-origin.com/note") return {
799
+ documentUrl: url,
800
+ contextUrl: null,
801
+ document: {
802
+ "@context": "https://www.w3.org/ns/activitystreams",
803
+ "@type": "Note",
804
+ "@id": "https://different-origin.com/note",
805
+ "content": "Legitimate note from origin"
806
+ }
807
+ };
808
+ throw new Error("Document not found");
809
+ };
810
+ const result = await create.getObject({ documentLoader: objectDocumentLoader });
811
+ assertInstanceOf(result, Note);
812
+ deepStrictEqual(result?.content, "Legitimate note from origin");
813
+ });
814
+ test("FEP-fe34: Constructor vs JSON-LD parsing trust difference", async () => {
815
+ const constructorCreate = new Create({
816
+ id: new URL("https://example.com/create"),
817
+ actor: new URL("https://example.com/actor"),
818
+ object: new Note({
819
+ id: new URL("https://different-origin.com/note"),
820
+ content: "Constructor embedded note"
821
+ })
822
+ });
823
+ const constructorResult = await constructorCreate.getObject();
824
+ deepStrictEqual(constructorResult?.content, "Constructor embedded note");
825
+ const jsonLdCreate = await Create.fromJsonLd({
826
+ "@context": "https://www.w3.org/ns/activitystreams",
827
+ "@type": "Create",
828
+ "@id": "https://example.com/create",
829
+ "actor": "https://example.com/actor",
830
+ "object": {
831
+ "@type": "Note",
832
+ "@id": "https://different-origin.com/note",
833
+ "content": "JSON-LD embedded note"
834
+ }
835
+ });
836
+ const documentLoader = async (url) => {
837
+ if (url === "https://different-origin.com/note") return {
838
+ documentUrl: url,
839
+ contextUrl: null,
840
+ document: {
841
+ "@context": "https://www.w3.org/ns/activitystreams",
842
+ "@type": "Note",
843
+ "@id": "https://different-origin.com/note",
844
+ "content": "Fetched from origin"
845
+ }
846
+ };
847
+ throw new Error("Document not found");
848
+ };
849
+ const jsonLdResult = await jsonLdCreate.getObject({ documentLoader });
850
+ deepStrictEqual(jsonLdResult?.content, "Fetched from origin");
851
+ });
852
+ test("FEP-fe34: Array properties respect cross-origin policy", async () => {
853
+ const crossOriginDocumentLoader = async (url) => {
854
+ if (url === "https://different-origin.com/note1") return {
855
+ documentUrl: url,
856
+ contextUrl: null,
857
+ document: {
858
+ "@context": "https://www.w3.org/ns/activitystreams",
859
+ "@type": "Note",
860
+ "@id": "https://malicious.com/fake-note1",
861
+ "content": "Fake note 1"
862
+ }
863
+ };
864
+ else if (url === "https://example.com/note2") return {
865
+ documentUrl: url,
866
+ contextUrl: null,
867
+ document: {
868
+ "@context": "https://www.w3.org/ns/activitystreams",
869
+ "@type": "Note",
870
+ "@id": "https://example.com/note2",
871
+ "content": "Legitimate note 2"
872
+ }
873
+ };
874
+ throw new Error("Document not found");
875
+ };
876
+ const collection = new Collection({
877
+ id: new URL("https://example.com/collection"),
878
+ items: [new URL("https://different-origin.com/note1"), new URL("https://example.com/note2")]
879
+ });
880
+ const items = [];
881
+ for await (const item of collection.getItems({ documentLoader: crossOriginDocumentLoader })) items.push(item);
882
+ deepStrictEqual(items.length, 1);
883
+ assertInstanceOf(items[0], Note);
884
+ deepStrictEqual(items[0].content, "Legitimate note 2");
885
+ });
886
+ test("FEP-fe34: Array properties with crossOrigin trust option", async () => {
887
+ const crossOriginDocumentLoader = async (url) => {
888
+ if (url === "https://different-origin.com/note1") return {
889
+ documentUrl: url,
890
+ contextUrl: null,
891
+ document: {
892
+ "@context": "https://www.w3.org/ns/activitystreams",
893
+ "@type": "Note",
894
+ "@id": "https://malicious.com/fake-note1",
895
+ "content": "Fake note 1"
896
+ }
897
+ };
898
+ else if (url === "https://example.com/note2") return {
899
+ documentUrl: url,
900
+ contextUrl: null,
901
+ document: {
902
+ "@context": "https://www.w3.org/ns/activitystreams",
903
+ "@type": "Note",
904
+ "@id": "https://example.com/note2",
905
+ "content": "Legitimate note 2"
906
+ }
907
+ };
908
+ throw new Error("Document not found");
909
+ };
910
+ const collection = new Collection({
911
+ id: new URL("https://example.com/collection"),
912
+ items: [new URL("https://different-origin.com/note1"), new URL("https://example.com/note2")]
913
+ });
914
+ const items = [];
915
+ for await (const item of collection.getItems({
916
+ documentLoader: crossOriginDocumentLoader,
917
+ crossOrigin: "trust"
918
+ })) items.push(item);
919
+ deepStrictEqual(items.length, 2);
920
+ assertInstanceOf(items[0], Note);
921
+ assertInstanceOf(items[1], Note);
922
+ deepStrictEqual(items[0].content, "Fake note 1");
923
+ deepStrictEqual(items[1].content, "Legitimate note 2");
924
+ });
925
+ test("FEP-fe34: Embedded objects in arrays from JSON-LD respect cross-origin policy", async () => {
926
+ const collectionDocumentLoader = async (url) => {
927
+ if (url === "https://example.com/collection") return {
928
+ documentUrl: url,
929
+ contextUrl: null,
930
+ document: {
931
+ "@context": "https://www.w3.org/ns/activitystreams",
932
+ "@type": "Collection",
933
+ "@id": "https://example.com/collection",
934
+ "items": [{
935
+ "@type": "Note",
936
+ "@id": "https://example.com/trusted-note",
937
+ "content": "Trusted embedded note from JSON-LD"
938
+ }, {
939
+ "@type": "Note",
940
+ "@id": "https://different-origin.com/untrusted-note",
941
+ "content": "Untrusted embedded note from JSON-LD"
942
+ }]
943
+ }
944
+ };
945
+ throw new Error("Document not found");
946
+ };
947
+ const collection = await Collection.fromJsonLd(await collectionDocumentLoader("https://example.com/collection").then((r) => r.document), { documentLoader: collectionDocumentLoader });
948
+ const itemDocumentLoader = async (url) => {
949
+ if (url === "https://example.com/trusted-note") return {
950
+ documentUrl: url,
951
+ contextUrl: null,
952
+ document: {
953
+ "@context": "https://www.w3.org/ns/activitystreams",
954
+ "@type": "Note",
955
+ "@id": "https://example.com/trusted-note",
956
+ "content": "Trusted note from origin"
957
+ }
958
+ };
959
+ else if (url === "https://different-origin.com/untrusted-note") return {
960
+ documentUrl: url,
961
+ contextUrl: null,
962
+ document: {
963
+ "@context": "https://www.w3.org/ns/activitystreams",
964
+ "@type": "Note",
965
+ "@id": "https://different-origin.com/untrusted-note",
966
+ "content": "Legitimate note from actual origin"
967
+ }
968
+ };
969
+ throw new Error("Document not found");
970
+ };
971
+ const items = [];
972
+ for await (const item of collection.getItems({ documentLoader: itemDocumentLoader })) items.push(item);
973
+ deepStrictEqual(items.length, 2);
974
+ assertInstanceOf(items[0], Note);
975
+ deepStrictEqual(items[0].content, "Trusted embedded note from JSON-LD");
976
+ assertInstanceOf(items[1], Note);
977
+ deepStrictEqual(items[1].content, "Legitimate note from actual origin");
978
+ });
979
+ function getAllProperties(type, types$1) {
980
+ const props = type.properties;
981
+ if (type.extends != null) props.push(...getAllProperties(types$1[type.extends], types$1));
982
+ return props;
983
+ }
984
+ const ed25519PublicKey = new CryptographicKey({
985
+ id: new URL("https://example.com/person2#key4"),
986
+ owner: new URL("https://example.com/person2"),
987
+ publicKey: await crypto.subtle.importKey("jwk", {
988
+ crv: "Ed25519",
989
+ ext: true,
990
+ key_ops: ["verify"],
991
+ kty: "OKP",
992
+ x: "LR8epAGDe-cVq5p2Tx49CCfphpk1rNhkNoY9i-XEUfg"
993
+ }, "Ed25519", true, ["verify"])
994
+ });
995
+ const rsaPublicKey = new CryptographicKey({
996
+ id: new URL("https://example.com/key"),
997
+ owner: new URL("https://example.com/person"),
998
+ publicKey: await crypto.subtle.importKey("jwk", {
999
+ kty: "RSA",
1000
+ alg: "RS256",
1001
+ n: "yIB9rotX8G6r6_6toT-x24BUiQ_HaPH1Em9dOt4c94s-OPFoEdH7DY7Iym9A8LlH4JaGF8KD38bLHWe1S4x0jV3gHJKhK7veJfGZCKUENcQecBZ-YWUs5HWvUIX1vVB__0luHrg6BQKGOrSOE-WIAxyr0qsWCFfZzQrvSnUD2yvg1arJX2xhms14uxoRd5Kg9efKSCmmQaNEapicARUmFWrIEpGFa_nUUnqimssAGw1eZFqf3wA4TjhsuARBhGaJtv_3KEa016eMZxy3kDlOjZnXZTaTgWkXdodwUvy8563fes3Al6BlcS2iJ9qbtha8rSm0FHqoUKH73JsLPKQIwQ",
1002
+ e: "AQAB",
1003
+ key_ops: ["verify"],
1004
+ ext: true
1005
+ }, {
1006
+ name: "RSASSA-PKCS1-v1_5",
1007
+ hash: "SHA-256"
1008
+ }, true, ["verify"])
1009
+ });
1010
+ const sampleValues = {
1011
+ "http://www.w3.org/2001/XMLSchema#boolean": true,
1012
+ "http://www.w3.org/2001/XMLSchema#integer": -123,
1013
+ "http://www.w3.org/2001/XMLSchema#nonNegativeInteger": 123,
1014
+ "http://www.w3.org/2001/XMLSchema#float": 12.34,
1015
+ "http://www.w3.org/2001/XMLSchema#string": "hello",
1016
+ "http://www.w3.org/2001/XMLSchema#anyURI": new URL("https://example.com/"),
1017
+ "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString": new LanguageString("hello", "en"),
1018
+ "http://www.w3.org/2001/XMLSchema#dateTime": Temporal.Instant.from("2024-03-03T08:30:06.796196096Z"),
1019
+ "http://www.w3.org/2001/XMLSchema#duration": Temporal.Duration.from({ hours: 1 }),
1020
+ "https://w3id.org/security#cryptosuiteString": "eddsa-jcs-2022",
1021
+ "https://w3id.org/security#multibase": new Uint8Array([
1022
+ 143,
1023
+ 155,
1024
+ 90,
1025
+ 201,
1026
+ 20,
1027
+ 23,
1028
+ 208,
1029
+ 209,
1030
+ 136,
1031
+ 190,
1032
+ 250,
1033
+ 133,
1034
+ 143,
1035
+ 116,
1036
+ 68,
1037
+ 152,
1038
+ 29,
1039
+ 200,
1040
+ 121,
1041
+ 218,
1042
+ 186,
1043
+ 80,
1044
+ 152,
1045
+ 60,
1046
+ 67,
1047
+ 235,
1048
+ 207,
1049
+ 114,
1050
+ 95,
1051
+ 56,
1052
+ 88,
1053
+ 17,
1054
+ 159,
1055
+ 35,
1056
+ 197,
1057
+ 191,
1058
+ 132,
1059
+ 35,
1060
+ 118,
1061
+ 162,
1062
+ 29,
1063
+ 83,
1064
+ 192,
1065
+ 190,
1066
+ 26,
1067
+ 170,
1068
+ 150,
1069
+ 110,
1070
+ 48,
1071
+ 101,
1072
+ 89,
1073
+ 118,
1074
+ 240,
1075
+ 176,
1076
+ 219,
1077
+ 120,
1078
+ 13,
1079
+ 245,
1080
+ 193,
1081
+ 173,
1082
+ 63,
1083
+ 189,
1084
+ 243,
1085
+ 7
1086
+ ]),
1087
+ "fedify:langTag": new Intl.Locale("en-Latn-US"),
1088
+ "fedify:url": new URL("https://fedify.dev/"),
1089
+ "fedify:publicKey": rsaPublicKey.publicKey,
1090
+ "fedify:multibaseKey": ed25519PublicKey.publicKey,
1091
+ "fedify:proofPurpose": "assertionMethod",
1092
+ "fedify:units": "m"
1093
+ };
1094
+ const types = navigator?.userAgent === "Cloudflare-Workers" ? {} : await loadSchemaFiles(import.meta.dirname);
1095
+ for (const typeUri in types) {
1096
+ const type = types[typeUri];
1097
+ const cls = vocab_exports[type.name];
1098
+ sampleValues[typeUri] = new cls({
1099
+ "@id": "https://example.com/",
1100
+ "@type": typeUri
1101
+ });
1102
+ }
1103
+ const { assertSnapshot } = await import("@std/testing/snapshot").catch(() => ({ assertSnapshot: () => Promise.resolve() }));
1104
+ for (const typeUri in types) {
1105
+ const type = types[typeUri];
1106
+ const cls = vocab_exports[type.name];
1107
+ const allProperties = getAllProperties(type, types);
1108
+ const initValues = globalThis.Object.fromEntries(allProperties.map((property) => !property.functional ? [property.pluralName, property.range.map((t) => sampleValues[t])] : [property.singularName, sampleValues[property.range[0]]]));
1109
+ test(`new ${type.name}() [auto]`, async () => {
1110
+ const instance = new cls(initValues);
1111
+ for (const property of allProperties) {
1112
+ if (areAllScalarTypes(property.range, types)) {
1113
+ if (property.functional || property.singularAccessor) deepStrictEqual(instance[property.singularName], sampleValues[property.range[0]]);
1114
+ if (!property.functional) deepStrictEqual(instance[property.pluralName], property.range.map((t) => sampleValues[t]));
1115
+ } else {
1116
+ if (property.functional || property.singularAccessor) {
1117
+ deepStrictEqual(await instance[`get${pascalCase(property.singularName)}`].call(instance, { documentLoader: mockDocumentLoader }), sampleValues[property.range[0]]);
1118
+ deepStrictEqual(instance[`${property.singularName}Id`], sampleValues[property.range[0]].id);
1119
+ }
1120
+ if (!property.functional) {
1121
+ deepStrictEqual(await Array.fromAsync(instance[`get${pascalCase(property.pluralName)}`].call(instance, { documentLoader: mockDocumentLoader })), property.range.map((t) => sampleValues[t]));
1122
+ deepStrictEqual(instance[`${property.singularName}Ids`], property.range.map((t) => sampleValues[t].id).filter((i) => i != null));
1123
+ }
1124
+ }
1125
+ const empty = new cls({});
1126
+ for (const property$1 of allProperties) if (areAllScalarTypes(property$1.range, types)) {
1127
+ if (property$1.functional || property$1.singularAccessor) deepStrictEqual(empty[property$1.singularName], null);
1128
+ if (!property$1.functional) deepStrictEqual(empty[property$1.pluralName], []);
1129
+ } else {
1130
+ if (property$1.functional || property$1.singularAccessor) {
1131
+ deepStrictEqual(await empty[`get${pascalCase(property$1.singularName)}`].call(empty, { documentLoader: mockDocumentLoader }), null);
1132
+ deepStrictEqual(empty[`${property$1.singularName}Id`], null);
1133
+ }
1134
+ if (!property$1.functional) {
1135
+ deepStrictEqual(await Array.fromAsync(empty[`get${pascalCase(property$1.pluralName)}`].call(empty, { documentLoader: mockDocumentLoader })), []);
1136
+ deepStrictEqual(empty[`${property$1.singularName}Ids`], []);
1137
+ }
1138
+ }
1139
+ }
1140
+ for (const property of allProperties) if (!property.functional && property.singularAccessor) throws(() => new cls({
1141
+ [property.singularName]: sampleValues[property.range[0]],
1142
+ [property.pluralName]: property.range.map((t) => sampleValues[t])
1143
+ }), TypeError);
1144
+ const instance2 = new cls({
1145
+ id: new URL("https://example.com/"),
1146
+ ...globalThis.Object.fromEntries(allProperties.filter((p) => !areAllScalarTypes(p.range, types)).map((p) => p.functional ? [p.singularName, new URL("https://example.com/test")] : [p.pluralName, [new URL("https://example.com/test")]]))
1147
+ });
1148
+ for (const property of allProperties) {
1149
+ if (areAllScalarTypes(property.range, types)) continue;
1150
+ if (property.functional || property.singularAccessor) deepStrictEqual(instance2[`${property.singularName}Id`], new URL("https://example.com/test"));
1151
+ if (!property.functional) deepStrictEqual(instance2[`${property.singularName}Ids`], [new URL("https://example.com/test")]);
1152
+ }
1153
+ throws(() => new cls({ id: 123 }), TypeError, "The id must be a URL.");
1154
+ for (const property of allProperties) {
1155
+ const wrongValues = globalThis.Object.fromEntries(globalThis.Object.entries(initValues));
1156
+ if (property.functional) wrongValues[property.singularName] = {};
1157
+ else wrongValues[property.pluralName] = [{}];
1158
+ throws(() => new cls(wrongValues), TypeError);
1159
+ }
1160
+ });
1161
+ test(`${type.name}.clone() [auto]`, () => {
1162
+ const instance = new cls({});
1163
+ for (const property of allProperties) if (!property.functional && property.singularAccessor) throws(() => instance.clone({
1164
+ [property.singularName]: sampleValues[property.range[0]],
1165
+ [property.pluralName]: property.range.map((t) => sampleValues[t])
1166
+ }), TypeError);
1167
+ throws(() => instance.clone({ id: 123 }), TypeError, "The id must be a URL.");
1168
+ for (const property of allProperties) {
1169
+ const wrongValues = globalThis.Object.fromEntries(globalThis.Object.entries(initValues));
1170
+ if (property.functional) wrongValues[property.singularName] = {};
1171
+ else wrongValues[property.pluralName] = [{}];
1172
+ throws(() => instance.clone(wrongValues), TypeError);
1173
+ }
1174
+ });
1175
+ for (const property of allProperties) {
1176
+ if (areAllScalarTypes(property.range, types)) continue;
1177
+ const docLoader = async (url) => {
1178
+ if (url !== `https://example.com/test`) throw new Error("Not Found");
1179
+ return {
1180
+ documentUrl: url,
1181
+ contextUrl: null,
1182
+ document: await sampleValues[property.range[0]].toJsonLd({ contextLoader: mockDocumentLoader })
1183
+ };
1184
+ };
1185
+ if (property.functional || property.singularAccessor) test(`${type.name}.get${pascalCase(property.singularName)}() [auto]`, async () => {
1186
+ const instance = new cls({ [property.singularName]: new URL("https://example.com/test") });
1187
+ const value = await instance[`get${pascalCase(property.singularName)}`].call(instance, { documentLoader: docLoader });
1188
+ deepStrictEqual(value, sampleValues[property.range[0]]);
1189
+ if (property.untyped) return;
1190
+ const wrongRef = new cls({ [property.singularName]: new URL("https://example.com/wrong-type") });
1191
+ await rejects(() => wrongRef[`get${pascalCase(property.singularName)}`].call(wrongRef, { documentLoader: mockDocumentLoader }), TypeError);
1192
+ });
1193
+ if (!property.functional) test(`${type.name}.get${pascalCase(property.pluralName)}() [auto]`, async () => {
1194
+ const instance = new cls({ [property.pluralName]: [new URL("https://example.com/test")] });
1195
+ const value = instance[`get${pascalCase(property.pluralName)}`].call(instance, { documentLoader: docLoader });
1196
+ deepStrictEqual(await Array.fromAsync(value), [sampleValues[property.range[0]]]);
1197
+ if (property.untyped) return;
1198
+ const wrongRef = new cls({ [property.pluralName]: [new URL("https://example.com/wrong-type")] });
1199
+ await rejects(() => Array.fromAsync(wrongRef[`get${pascalCase(property.pluralName)}`].call(wrongRef, { documentLoader: mockDocumentLoader })), TypeError);
1200
+ });
1201
+ }
1202
+ test(`${type.name}.fromJsonLd() [auto]`, async () => {
1203
+ const instance = await cls.fromJsonLd({
1204
+ "@id": "https://example.com/",
1205
+ "@type": typeUri
1206
+ }, {
1207
+ documentLoader: mockDocumentLoader,
1208
+ contextLoader: mockDocumentLoader
1209
+ });
1210
+ assertInstanceOf(instance, cls);
1211
+ deepStrictEqual(instance.id, new URL("https://example.com/"));
1212
+ deepStrictEqual(await instance.toJsonLd(), {
1213
+ "@id": "https://example.com/",
1214
+ "@type": typeUri
1215
+ });
1216
+ deepStrictEqual(await instance.toJsonLd({
1217
+ format: "compact",
1218
+ contextLoader: mockDocumentLoader
1219
+ }), {
1220
+ "@context": type.defaultContext,
1221
+ "id": "https://example.com/",
1222
+ "type": type.compactName ?? (type.name === "DataIntegrityProof" ? type.name : type.uri)
1223
+ });
1224
+ if (type.extends != null) await rejects(() => cls.fromJsonLd({
1225
+ "@id": "https://example.com/",
1226
+ "@type": "https://example.com/"
1227
+ }), TypeError);
1228
+ await rejects(() => cls.fromJsonLd(null), TypeError);
1229
+ await rejects(() => cls.fromJsonLd(void 0), TypeError);
1230
+ });
1231
+ test(`${type.name}.toJsonLd() [auto]`, async () => {
1232
+ const instance = new cls({
1233
+ id: new URL("https://example.com/"),
1234
+ ...initValues
1235
+ });
1236
+ const jsonLd = await instance.toJsonLd({ contextLoader: mockDocumentLoader });
1237
+ deepStrictEqual(jsonLd["@context"], type.defaultContext);
1238
+ deepStrictEqual(jsonLd.id, "https://example.com/");
1239
+ const restored = await cls.fromJsonLd(jsonLd, {
1240
+ documentLoader: mockDocumentLoader,
1241
+ contextLoader: mockDocumentLoader
1242
+ });
1243
+ deepStrictEqual(restored, instance);
1244
+ deepStrictEqual(await restored.toJsonLd({ contextLoader: mockDocumentLoader }), jsonLd);
1245
+ const jsonLd2 = await instance.toJsonLd({
1246
+ contextLoader: mockDocumentLoader,
1247
+ format: "compact",
1248
+ context: "https://www.w3.org/ns/activitystreams"
1249
+ });
1250
+ deepStrictEqual(jsonLd2["@context"], "https://www.w3.org/ns/activitystreams");
1251
+ deepStrictEqual(jsonLd2.id, "https://example.com/");
1252
+ const restored2 = await cls.fromJsonLd(jsonLd2, {
1253
+ documentLoader: mockDocumentLoader,
1254
+ contextLoader: mockDocumentLoader
1255
+ });
1256
+ deepStrictEqual(restored2, instance);
1257
+ const expanded = await instance.toJsonLd({
1258
+ contextLoader: mockDocumentLoader,
1259
+ format: "expand"
1260
+ });
1261
+ const restored3 = await cls.fromJsonLd(expanded, {
1262
+ documentLoader: mockDocumentLoader,
1263
+ contextLoader: mockDocumentLoader
1264
+ });
1265
+ deepStrictEqual(restored3, instance);
1266
+ const instance2 = new cls({
1267
+ id: new URL("https://example.com/"),
1268
+ ...initValues,
1269
+ ...globalThis.Object.fromEntries(allProperties.filter((p) => !areAllScalarTypes(p.range, types)).map((p) => p.functional ? [p.singularName, new URL("https://example.com/test")] : [p.pluralName, [new URL("https://example.com/test")]]))
1270
+ });
1271
+ const jsonLd3 = await instance2.toJsonLd({ contextLoader: mockDocumentLoader });
1272
+ const restored4 = await cls.fromJsonLd(jsonLd3, {
1273
+ documentLoader: mockDocumentLoader,
1274
+ contextLoader: mockDocumentLoader
1275
+ });
1276
+ deepStrictEqual(restored4, instance2);
1277
+ rejects(() => instance.toJsonLd({ context: "https://www.w3.org/ns/activitystreams" }), TypeError);
1278
+ rejects(() => instance.toJsonLd({
1279
+ format: "expand",
1280
+ context: "https://www.w3.org/ns/activitystreams"
1281
+ }), TypeError);
1282
+ });
1283
+ if ("Deno" in globalThis) Deno.test(`Deno.inspect(${type.name}) [auto]`, async (t) => {
1284
+ const empty = new cls({});
1285
+ deepStrictEqual(Deno.inspect(empty), `${type.name} {}`);
1286
+ const instance = new cls({
1287
+ id: new URL("https://example.com/"),
1288
+ ...initValues
1289
+ });
1290
+ await assertSnapshot(t, Deno.inspect(instance));
1291
+ const instance2 = instance.clone(globalThis.Object.fromEntries(type.properties.filter((p) => !areAllScalarTypes(p.range, types)).map((p) => p.functional ? [p.singularName, new URL("https://example.com/")] : [p.pluralName, [new URL("https://example.com/")]])));
1292
+ await assertSnapshot(t, Deno.inspect(instance2));
1293
+ const instance3 = instance.clone(globalThis.Object.fromEntries(type.properties.filter((p) => !p.functional).map((p) => {
1294
+ ok(!p.functional);
1295
+ return [p.pluralName, [sampleValues[p.range[0]], sampleValues[p.range[0]]]];
1296
+ })));
1297
+ await assertSnapshot(t, Deno.inspect(instance3));
1298
+ });
1299
+ test(`${type.name}.typeId`, () => {
1300
+ deepStrictEqual(cls.typeId, new URL(type.uri));
1301
+ });
1302
+ }
1303
+
1304
+ //#endregion