@fedify/vocab 2.4.0-dev.2084 → 2.4.0-dev.2099

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/deno.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab",
3
- "version": "2.4.0-dev.2084+4b8bd325",
3
+ "version": "2.4.0-dev.2099+cd074fe2",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts"
package/dist/mod.cjs CHANGED
@@ -36,7 +36,7 @@ let _fedify_vocab_runtime_temporal = require("@fedify/vocab-runtime/temporal");
36
36
  let es_toolkit = require("es-toolkit");
37
37
  //#region deno.json
38
38
  var name = "@fedify/vocab";
39
- var version = "2.4.0-dev.2084+4b8bd325";
39
+ var version = "2.4.0-dev.2099+cd074fe2";
40
40
  //#endregion
41
41
  //#region src/type.ts
42
42
  function getTypeId(object) {
package/dist/mod.js CHANGED
@@ -12,7 +12,7 @@ import { isTemporalDuration, isTemporalInstant } from "@fedify/vocab-runtime/tem
12
12
  import { delay } from "es-toolkit";
13
13
  //#region deno.json
14
14
  var name = "@fedify/vocab";
15
- var version = "2.4.0-dev.2084+4b8bd325";
15
+ var version = "2.4.0-dev.2099+cd074fe2";
16
16
  //#endregion
17
17
  //#region src/type.ts
18
18
  function getTypeId(object) {
@@ -8,7 +8,7 @@ import { SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { domainToASCII, domainToUnicode } from "node:url";
9
9
  //#region deno.json
10
10
  var name = "@fedify/vocab";
11
- var version = "2.4.0-dev.2084+4b8bd325";
11
+ var version = "2.4.0-dev.2099+cd074fe2";
12
12
  //#endregion
13
13
  //#region src/actor.ts
14
14
  const ACTOR_DISCOVERY_HISTOGRAM_BUCKETS = [
@@ -2,7 +2,7 @@ import "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
3
  import { F as Person, P as Organization, Q as __exportAll, U as Service, a as Application, b as Group } from "./vocab-BHtBRusa.mjs";
4
4
  import { t as esm_default } from "./esm-C9nxP5_e.mjs";
5
- import { a as normalizeActorHandle, i as isActor, n as getActorHandle, r as getActorTypeName, t as getActorClassByTypeName } from "./actor-BxYUEpxo.mjs";
5
+ import { a as normalizeActorHandle, i as isActor, n as getActorHandle, r as getActorTypeName, t as getActorClassByTypeName } from "./actor-C7HZgokj.mjs";
6
6
  import { createTestMeterProvider, test } from "@fedify/fixture";
7
7
  import { deepStrictEqual, ok, rejects, strictEqual, throws } from "node:assert/strict";
8
8
  //#region ../../node_modules/.pnpm/fast-check@3.23.2/node_modules/fast-check/lib/esm/check/precondition/PreconditionFailure.js
@@ -2,7 +2,7 @@ import "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
3
  import { F as Person, M as OrderedCollection, c as CollectionPage, k as Note, s as Collection, u as Create } from "./vocab-BHtBRusa.mjs";
4
4
  import { t as esm_default } from "./esm-C9nxP5_e.mjs";
5
- import { n as traverseCollection } from "./lookup-Bu1eb3CS.mjs";
5
+ import { n as traverseCollection } from "./lookup-3UnwcwWD.mjs";
6
6
  import { t as assertInstanceOf } from "./utils-Df2odC27.mjs";
7
7
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
8
8
  import { deepStrictEqual, ok, rejects } from "node:assert/strict";
@@ -165,7 +165,8 @@ test("getObject() rejects objects with a mismatching portable ID", async (t) =>
165
165
  "another path": `ap://${did}/objects/2`,
166
166
  "a dot segment": `ap://${did}/objects/x/../1`,
167
167
  "a fragment": `ap://${did}/objects/1#fragment`,
168
- "a compatible identifier": `https://gw1.example${gatewayPath}`,
168
+ "a compatible identifier of another DID": `https://gw1.example/.well-known/apgateway/did:key:z6Mkdef/objects/1`,
169
+ "a compatible identifier of another path": `https://gw1.example/.well-known/apgateway/${did}/objects/2`,
169
170
  "an HTTP(S) ID": "https://gw1.example/objects/1"
170
171
  };
171
172
  for (const [name, id] of globalThis.Object.entries(mismatches)) await t.step(name, async () => {
@@ -856,14 +857,14 @@ test("accessors reject forged objects at compatible identifiers", async () => {
856
857
  "ignore",
857
858
  "trust"
858
859
  ]) {
859
- const verifier = createRecordingVerifier();
860
+ const verifier = createRecordingVerifier(() => ({ verified: false }));
860
861
  deepStrictEqual(await (await createHttpActivity(forgedId)).getObject({
861
862
  documentLoader: createLoader({ [forgedId]: forged }),
862
863
  contextLoader: mockDocumentLoader,
863
864
  verifyPortableObject: verifier,
864
865
  crossOrigin
865
866
  }), null);
866
- deepStrictEqual(verifier.calls, []);
867
+ deepStrictEqual(verifier.calls.map((c) => c.document), [forged]);
867
868
  deepStrictEqual(await (await createHttpActivity(forgedId)).getObject({
868
869
  documentLoader: createLoader({ [forgedId]: note() }),
869
870
  contextLoader: mockDocumentLoader,
@@ -923,7 +924,7 @@ test("accessors keep fetching compatible identifiers as HTTP(S) URLs without ver
923
924
  deepStrictEqual(await activity.getObject({
924
925
  documentLoader,
925
926
  contextLoader: mockDocumentLoader,
926
- verifyPortableObject: createVerifier()
927
+ verifyPortableObject: createVerifier(false)
927
928
  }), null);
928
929
  deepStrictEqual(documentLoader.fetched, [compatibleNoteId, compatibleNoteId]);
929
930
  });
@@ -937,7 +938,7 @@ test("accessors keep fetching compatible identifiers as HTTP(S) URLs without ver
937
938
  deepStrictEqual(await Array.fromAsync(activity.getObjects({
938
939
  documentLoader,
939
940
  contextLoader: mockDocumentLoader,
940
- verifyPortableObject: createVerifier()
941
+ verifyPortableObject: createVerifier(false)
941
942
  })), []);
942
943
  });
943
944
  await t.step("redirects to ordinary objects", async () => {
@@ -1066,7 +1067,7 @@ test("accessors verify fetched documents that stand for portable objects", async
1066
1067
  await t.step("a document that claims another object", async () => {
1067
1068
  for (const id of [
1068
1069
  `ap://${did}/objects/2`,
1069
- compatibleNoteId,
1070
+ gatewayUrl("https://gw.example", "/objects/2"),
1070
1071
  "https://gw.example/notes/1"
1071
1072
  ]) {
1072
1073
  const verifier = createRecordingVerifier();
@@ -1101,12 +1102,20 @@ test("accessors verify fetched documents that stand for portable objects", async
1101
1102
  await t.step("a compatible @id", async () => {
1102
1103
  const verifier = createRecordingVerifier();
1103
1104
  const url = "https://gw.example/notes/1";
1104
- deepStrictEqual(await (await createHttpActivity(url)).getObject({
1105
- documentLoader: createLoader({ [url]: note(compatibleNoteId) }),
1105
+ const documentLoader = createLoader({ [url]: note(compatibleNoteId) });
1106
+ const object = await (await createHttpActivity(url)).getObject({
1107
+ documentLoader,
1106
1108
  contextLoader: mockDocumentLoader,
1107
1109
  verifyPortableObject: verifier
1110
+ });
1111
+ assertInstanceOf(object, Note);
1112
+ deepStrictEqual(object.id, new URL(compatibleNoteId));
1113
+ deepStrictEqual(verifier.calls.length, 1);
1114
+ deepStrictEqual(await (await createHttpActivity(url)).getObject({
1115
+ documentLoader,
1116
+ contextLoader: mockDocumentLoader,
1117
+ verifyPortableObject: createVerifier(false)
1108
1118
  }), null);
1109
- deepStrictEqual(verifier.calls, []);
1110
1119
  });
1111
1120
  await t.step("a portable final URL", async () => {
1112
1121
  for (const [id, expected] of [[objectId, true], [`ap://${did}/objects/2`, false]]) deepStrictEqual(await (await createHttpActivity(plainUrl)).getObject({
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
3
  import { A as Object$1 } from "./vocab-BHtBRusa.mjs";
4
- import { i as isActor, o as name, s as version } from "./actor-BxYUEpxo.mjs";
4
+ import { i as isActor, o as name, s as version } from "./actor-C7HZgokj.mjs";
5
5
  import { t as getTypeId } from "./type-BMdjm4Eg.mjs";
6
6
  import { canonicalizePortableUri, formatIri, getDocumentLoader, haveSameFe34Origin, haveSameIriOrigin, parseIri } from "@fedify/vocab-runtime";
7
7
  import { lookupWebFinger } from "@fedify/webfinger";
@@ -2,7 +2,7 @@ import "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
3
  import { A as Object$1, F as Person, k as Note, s as Collection } from "./vocab-BHtBRusa.mjs";
4
4
  import { t as esm_default } from "./esm-C9nxP5_e.mjs";
5
- import { n as traverseCollection, t as lookupObject } from "./lookup-Bu1eb3CS.mjs";
5
+ import { n as traverseCollection, t as lookupObject } from "./lookup-3UnwcwWD.mjs";
6
6
  import { t as assertInstanceOf } from "./utils-Df2odC27.mjs";
7
7
  import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
8
8
  import { deepStrictEqual, equal, ok, rejects } from "node:assert/strict";
@@ -2,8 +2,8 @@ import "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
3
  import { F as Person } from "./vocab-BHtBRusa.mjs";
4
4
  import { t as esm_default } from "./esm-C9nxP5_e.mjs";
5
- import { n as getActorHandle } from "./actor-BxYUEpxo.mjs";
6
- import { t as lookupObject } from "./lookup-Bu1eb3CS.mjs";
5
+ import { n as getActorHandle } from "./actor-C7HZgokj.mjs";
6
+ import { t as lookupObject } from "./lookup-3UnwcwWD.mjs";
7
7
  import { t as assertInstanceOf } from "./utils-Df2odC27.mjs";
8
8
  import { mockDocumentLoader, test } from "@fedify/fixture";
9
9
  import { deepStrictEqual, equal, rejects } from "node:assert/strict";
@@ -110,13 +110,29 @@ test("lookupObject() with FEP-ef61 portable actors", {
110
110
  }), Person);
111
111
  deepStrictEqual(documentLoader.fetched, [compatibleId, other]);
112
112
  });
113
+ await t.step("accepts a compatible identifier as the ID", async () => {
114
+ esm_default.removeRoutes();
115
+ esm_default.get(webFingerPrefix, selfLinks(compatibleId));
116
+ const document = person(compatibleId);
117
+ const documentLoader = createLoader({ [compatibleId]: document });
118
+ const verifyPortableObject = createVerifier();
119
+ const actor = await lookupObject("@alice@example.com", {
120
+ documentLoader,
121
+ contextLoader: mockDocumentLoader,
122
+ verifyPortableObject
123
+ });
124
+ assertInstanceOf(actor, Person);
125
+ deepStrictEqual(actor.id, new URL(compatibleId));
126
+ deepStrictEqual(verifyPortableObject.documents, [document]);
127
+ });
113
128
  await t.step("rejects objects that fail the checks", async () => {
114
129
  esm_default.removeRoutes();
115
130
  esm_default.get(webFingerPrefix, selfLinks(compatibleId));
116
131
  for (const [document, verified] of [
117
132
  [person(), false],
118
133
  [person(`ap://${did}/other`), true],
119
- [person(compatibleId), true]
134
+ [person(`https://example.com/.well-known/apgateway/${did}/other`), true],
135
+ [person(compatibleId), false]
120
136
  ]) {
121
137
  const documentLoader = createLoader({ [compatibleId]: document });
122
138
  const verifyPortableObject = createVerifier(verified);
@@ -390,14 +406,22 @@ test("lookupObject() verifies fetched documents that stand for portable objects"
390
406
  }), null);
391
407
  });
392
408
  await t.step("a compatible @id", async () => {
393
- const documentLoader = redirectingLoader(plainUrl, person(compatibleId));
409
+ const document = person(compatibleId);
410
+ const documentLoader = redirectingLoader(plainUrl, document);
394
411
  const verifyPortableObject = createVerifier();
395
- equal(await lookupObject(plainUrl, {
412
+ const verified = await lookupObject(plainUrl, {
396
413
  documentLoader,
397
414
  contextLoader: mockDocumentLoader,
398
415
  verifyPortableObject
416
+ });
417
+ assertInstanceOf(verified, Person);
418
+ deepStrictEqual(verified.id, new URL(compatibleId));
419
+ deepStrictEqual(verifyPortableObject.documents, [document]);
420
+ equal(await lookupObject(plainUrl, {
421
+ documentLoader,
422
+ contextLoader: mockDocumentLoader,
423
+ verifyPortableObject: createVerifier(false)
399
424
  }), null);
400
- deepStrictEqual(verifyPortableObject.documents, []);
401
425
  const actor = await lookupObject(plainUrl, {
402
426
  documentLoader,
403
427
  contextLoader: mockDocumentLoader
@@ -1,8 +1,8 @@
1
1
  import "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
3
  import { A as Object$1, E as Link, F as Person, K as Translation, L as PropertyValue, P as Organization, U as Service, a as Application, b as Group, k as Note, o as Article, q as Update, s as Collection, u as Create } from "./vocab-BHtBRusa.mjs";
4
- import "./actor-BxYUEpxo.mjs";
5
- import "./lookup-Bu1eb3CS.mjs";
4
+ import "./actor-C7HZgokj.mjs";
5
+ import "./lookup-3UnwcwWD.mjs";
6
6
  import { test } from "@fedify/fixture";
7
7
  import { deepStrictEqual, ok, rejects } from "node:assert/strict";
8
8
  import { LanguageString as LanguageString$1, getDocumentLoader, preloadedContexts } from "@fedify/vocab-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab",
3
- "version": "2.4.0-dev.2084+4b8bd325",
3
+ "version": "2.4.0-dev.2099+cd074fe2",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,9 +43,9 @@
43
43
  "jsonld": "^9.0.0",
44
44
  "pkijs": "^3.3.3",
45
45
  "temporal-polyfill": "^1.0.1",
46
- "@fedify/vocab-tools": "2.4.0-dev.2084+4b8bd325",
47
- "@fedify/webfinger": "2.4.0-dev.2084+4b8bd325",
48
- "@fedify/vocab-runtime": "2.4.0-dev.2084+4b8bd325"
46
+ "@fedify/vocab-runtime": "2.4.0-dev.2099+cd074fe2",
47
+ "@fedify/vocab-tools": "2.4.0-dev.2099+cd074fe2",
48
+ "@fedify/webfinger": "2.4.0-dev.2099+cd074fe2"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^24.12.2",
@@ -262,7 +262,10 @@ test("getObject() rejects objects with a mismatching portable ID", async (t) =>
262
262
  "another path": `ap://${did}/objects/2`,
263
263
  "a dot segment": `ap://${did}/objects/x/../1`,
264
264
  "a fragment": `ap://${did}/objects/1#fragment`,
265
- "a compatible identifier": `https://gw1.example${gatewayPath}`,
265
+ "a compatible identifier of another DID":
266
+ `https://gw1.example/.well-known/apgateway/did:key:z6Mkdef/objects/1`,
267
+ "a compatible identifier of another path":
268
+ `https://gw1.example/.well-known/apgateway/${did}/objects/2`,
266
269
  "an HTTP(S) ID": "https://gw1.example/objects/1",
267
270
  };
268
271
  for (const [name, id] of globalThis.Object.entries(mismatches)) {
@@ -1239,8 +1242,10 @@ test("accessors reject forged objects at compatible identifiers", async () => {
1239
1242
  content: "Forged",
1240
1243
  };
1241
1244
  for (const crossOrigin of [undefined, "ignore", "trust"] as const) {
1242
- // Its @id is not a portable ID, so it fails the identity check:
1243
- const verifier = createRecordingVerifier();
1245
+ // Its @id, the compatible identifier itself, stands for the requested
1246
+ // portable object, so the proof policy decides, which rejects it for
1247
+ // lacking a proof:
1248
+ const verifier = createRecordingVerifier(() => ({ verified: false }));
1244
1249
  deepStrictEqual(
1245
1250
  await (await createHttpActivity(forgedId)).getObject({
1246
1251
  documentLoader: createLoader({ [forgedId]: forged }),
@@ -1250,7 +1255,7 @@ test("accessors reject forged objects at compatible identifiers", async () => {
1250
1255
  }),
1251
1256
  null,
1252
1257
  );
1253
- deepStrictEqual(verifier.calls, []);
1258
+ deepStrictEqual(verifier.calls.map((c) => c.document), [forged]);
1254
1259
  // With a portable ID, it fails the proof policy:
1255
1260
  deepStrictEqual(
1256
1261
  await (await createHttpActivity(forgedId)).getObject({
@@ -1335,7 +1340,7 @@ test("accessors keep fetching compatible identifiers as HTTP(S) URLs without ver
1335
1340
  await activity.getObject({
1336
1341
  documentLoader,
1337
1342
  contextLoader: mockDocumentLoader,
1338
- verifyPortableObject: createVerifier(),
1343
+ verifyPortableObject: createVerifier(false),
1339
1344
  }),
1340
1345
  null,
1341
1346
  );
@@ -1357,7 +1362,7 @@ test("accessors keep fetching compatible identifiers as HTTP(S) URLs without ver
1357
1362
  await Array.fromAsync(activity.getObjects({
1358
1363
  documentLoader,
1359
1364
  contextLoader: mockDocumentLoader,
1360
- verifyPortableObject: createVerifier(),
1365
+ verifyPortableObject: createVerifier(false),
1361
1366
  })),
1362
1367
  [],
1363
1368
  );
@@ -1540,7 +1545,7 @@ test("accessors verify fetched documents that stand for portable objects", async
1540
1545
  for (
1541
1546
  const id of [
1542
1547
  `ap://${did}/objects/2`,
1543
- compatibleNoteId,
1548
+ gatewayUrl("https://gw.example", "/objects/2"),
1544
1549
  "https://gw.example/notes/1",
1545
1550
  ]
1546
1551
  ) {
@@ -1583,17 +1588,27 @@ test("accessors verify fetched documents that stand for portable objects", async
1583
1588
  });
1584
1589
 
1585
1590
  await t.step("a compatible @id", async () => {
1591
+ // The compatible identifier stands for the portable object, so the
1592
+ // document is verified as that object, keeping its own @id:
1586
1593
  const verifier = createRecordingVerifier();
1587
1594
  const url = "https://gw.example/notes/1";
1595
+ const documentLoader = createLoader({ [url]: note(compatibleNoteId) });
1596
+ const object = await (await createHttpActivity(url)).getObject({
1597
+ documentLoader,
1598
+ contextLoader: mockDocumentLoader,
1599
+ verifyPortableObject: verifier,
1600
+ });
1601
+ assertInstanceOf(object, Note);
1602
+ deepStrictEqual(object.id, new URL(compatibleNoteId));
1603
+ deepStrictEqual(verifier.calls.length, 1);
1588
1604
  deepStrictEqual(
1589
1605
  await (await createHttpActivity(url)).getObject({
1590
- documentLoader: createLoader({ [url]: note(compatibleNoteId) }),
1606
+ documentLoader,
1591
1607
  contextLoader: mockDocumentLoader,
1592
- verifyPortableObject: verifier,
1608
+ verifyPortableObject: createVerifier(false),
1593
1609
  }),
1594
1610
  null,
1595
1611
  );
1596
- deepStrictEqual(verifier.calls, []);
1597
1612
  });
1598
1613
 
1599
1614
  await t.step("a portable final URL", async () => {
@@ -143,6 +143,25 @@ test("lookupObject() with FEP-ef61 portable actors", {
143
143
  deepStrictEqual(documentLoader.fetched, [compatibleId, other]);
144
144
  });
145
145
 
146
+ await t.step("accepts a compatible identifier as the ID", async () => {
147
+ // Some publishers, e.g., tootik, identify their portable actors by
148
+ // compatible identifiers; the document is verified as the portable
149
+ // object, and keeps its own ID:
150
+ fetchMock.removeRoutes();
151
+ fetchMock.get(webFingerPrefix, selfLinks(compatibleId));
152
+ const document = person(compatibleId);
153
+ const documentLoader = createLoader({ [compatibleId]: document });
154
+ const verifyPortableObject = createVerifier();
155
+ const actor = await lookupObject("@alice@example.com", {
156
+ documentLoader,
157
+ contextLoader: mockDocumentLoader,
158
+ verifyPortableObject,
159
+ });
160
+ assertInstanceOf(actor, Person);
161
+ deepStrictEqual(actor.id, new URL(compatibleId));
162
+ deepStrictEqual(verifyPortableObject.documents, [document]);
163
+ });
164
+
146
165
  await t.step("rejects objects that fail the checks", async () => {
147
166
  fetchMock.removeRoutes();
148
167
  fetchMock.get(webFingerPrefix, selfLinks(compatibleId));
@@ -151,8 +170,13 @@ test("lookupObject() with FEP-ef61 portable actors", {
151
170
  [person(), false],
152
171
  // Same DID, but another object:
153
172
  [person(`ap://${did}/other`), true],
154
- // A compatible identifier as the object ID is not portable:
155
- [person(compatibleId), true],
173
+ // A compatible identifier of another object:
174
+ [
175
+ person(`https://example.com/.well-known/apgateway/${did}/other`),
176
+ true,
177
+ ],
178
+ // A compatible identifier of the object, but unverified:
179
+ [person(compatibleId), false],
156
180
  ] as const
157
181
  ) {
158
182
  const documentLoader = createLoader({ [compatibleId]: document });
@@ -539,17 +563,26 @@ test("lookupObject() verifies fetched documents that stand for portable objects"
539
563
  });
540
564
 
541
565
  await t.step("a compatible @id", async () => {
542
- const documentLoader = redirectingLoader(plainUrl, person(compatibleId));
566
+ const document = person(compatibleId);
567
+ const documentLoader = redirectingLoader(plainUrl, document);
568
+ // The document stands for the portable object, and is verified as it:
543
569
  const verifyPortableObject = createVerifier();
570
+ const verified = await lookupObject(plainUrl, {
571
+ documentLoader,
572
+ contextLoader: mockDocumentLoader,
573
+ verifyPortableObject,
574
+ });
575
+ assertInstanceOf(verified, Person);
576
+ deepStrictEqual(verified.id, new URL(compatibleId));
577
+ deepStrictEqual(verifyPortableObject.documents, [document]);
544
578
  equal(
545
579
  await lookupObject(plainUrl, {
546
580
  documentLoader,
547
581
  contextLoader: mockDocumentLoader,
548
- verifyPortableObject,
582
+ verifyPortableObject: createVerifier(false),
549
583
  }),
550
584
  null,
551
585
  );
552
- deepStrictEqual(verifyPortableObject.documents, []);
553
586
  // Without verifyPortableObject, it is fetched as before:
554
587
  const actor = await lookupObject(plainUrl, {
555
588
  documentLoader,