@fedify/vocab 2.4.0-dev.2056 → 2.4.0-dev.2065
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 +1 -1
- package/dist/mod.cjs +16674 -9026
- package/dist/mod.d.cts +28 -2
- package/dist/mod.d.ts +28 -2
- package/dist/mod.js +16675 -9027
- package/dist-tests/{actor-BgeBgt_8.mjs → actor-B0IFZjgb.mjs} +2 -2
- package/dist-tests/actor.test.mjs +3 -3
- package/dist-tests/{esm-Df0Yo-Tp.mjs → esm-C9nxP5_e.mjs} +1 -1
- package/dist-tests/gateway.test.mjs +695 -7
- package/dist-tests/{lookup-C13R72YU.mjs → lookup-BnzAXNN7.mjs} +48 -49
- package/dist-tests/lookup.test.mjs +3 -3
- package/dist-tests/portable-lookup.test.mjs +100 -4
- package/dist-tests/signed-representation.test.mjs +1 -1
- package/dist-tests/translation.test.mjs +3 -3
- package/dist-tests/type.test.mjs +1 -1
- package/dist-tests/{vocab-BJGPw--L.mjs → vocab-BHtBRusa.mjs} +16631 -8982
- package/dist-tests/vocab.test.mjs +1 -1
- package/package.json +4 -4
- package/src/gateway.test.ts +980 -7
- package/src/lookup.ts +114 -65
- package/src/portable-lookup.test.ts +133 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import {
|
|
4
|
-
import { i as isActor, o as name, s as version } from "./actor-
|
|
3
|
+
import { A as Object$1 } from "./vocab-BHtBRusa.mjs";
|
|
4
|
+
import { i as isActor, o as name, s as version } from "./actor-B0IFZjgb.mjs";
|
|
5
5
|
import { t as getTypeId } from "./type-BMdjm4Eg.mjs";
|
|
6
|
-
import { canonicalizePortableUri, formatIri,
|
|
6
|
+
import { canonicalizePortableUri, formatIri, getDocumentLoader, haveSameFe34Origin, haveSameIriOrigin, parseIri } from "@fedify/vocab-runtime";
|
|
7
7
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
8
8
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
9
9
|
import { getLogger } from "@logtape/logtape";
|
|
10
|
-
import { PortableObjectRejectedError, dereferencePortableIri, getPortableGatewayCandidates, isPortableIri } from "@fedify/vocab-runtime/internal/portable-dereference";
|
|
10
|
+
import { PortableObjectRejectedError, createSnapshotContextLoader, dereferencePortableIri, getPortableGatewayCandidates, getPortableResponseClaim, isPortableIri, parseCompatibleEf61Reference } from "@fedify/vocab-runtime/internal/portable-dereference";
|
|
11
11
|
import { delay } from "es-toolkit";
|
|
12
12
|
//#region src/handle.ts
|
|
13
13
|
/**
|
|
@@ -269,35 +269,50 @@ async function lookupObjectInternal(identifier, options = {}) {
|
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
271
|
if (remoteDoc == null) return null;
|
|
272
|
-
|
|
273
|
-
let documentUrl;
|
|
272
|
+
const snapshot = options.verifyPortableObject == null ? null : createSnapshotContextLoader(options.contextLoader ?? getDocumentLoader({ userAgent: options.userAgent }));
|
|
274
273
|
try {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
274
|
+
let object;
|
|
275
|
+
let documentUrl;
|
|
276
|
+
try {
|
|
277
|
+
documentUrl = parseIri(remoteDoc.documentUrl);
|
|
278
|
+
object = await Object$1.fromJsonLd(remoteDoc.document, {
|
|
279
|
+
documentLoader,
|
|
280
|
+
contextLoader: snapshot?.loader ?? options.contextLoader,
|
|
281
|
+
tracerProvider: options.tracerProvider,
|
|
282
|
+
baseUrl: documentUrl
|
|
283
|
+
});
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (error instanceof TypeError) {
|
|
286
|
+
logger.debug("Failed to parse JSON-LD document: {error}\n{document}", {
|
|
287
|
+
...remoteDoc,
|
|
288
|
+
error
|
|
289
|
+
});
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
if (snapshot != null) {
|
|
295
|
+
const claim = getPortableResponseClaim(remoteDoc.documentUrl, object.id);
|
|
296
|
+
if (claim === null) {
|
|
297
|
+
logger.debug("Refusing the document {documentUrl}, as it claims to be a portable object with a malformed compatible identifier.", { documentUrl: remoteDoc.documentUrl });
|
|
298
|
+
return null;
|
|
299
|
+
} else if (claim != null) return await dereference(portable, claim.id, claim.inferredGateways, {
|
|
300
|
+
response: remoteDoc,
|
|
301
|
+
contextLoader: snapshot.loader
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (object.id != null && (options.crossOrigin !== "trust" || isPortableIri(object.id)) && !haveSameIriOrigin(object.id, documentUrl) && !haveSameFe34Origin(object.id, documentUrl)) {
|
|
305
|
+
if (options.crossOrigin === "throw") throw new Error(`The object's @id (${object.id.href}) has a different origin than the document URL (${remoteDoc.documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to "trust".`);
|
|
306
|
+
logger.warn("The object's @id ({objectId}) has a different origin than the document URL ({documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to \"trust\".", {
|
|
285
307
|
...remoteDoc,
|
|
286
|
-
|
|
308
|
+
objectId: object.id.href
|
|
287
309
|
});
|
|
288
310
|
return null;
|
|
289
311
|
}
|
|
290
|
-
|
|
312
|
+
return object;
|
|
313
|
+
} finally {
|
|
314
|
+
snapshot?.release();
|
|
291
315
|
}
|
|
292
|
-
if (object.id != null && (options.crossOrigin !== "trust" || isPortableIri(object.id)) && !haveSameIriOrigin(object.id, documentUrl) && !haveSameFe34Origin(object.id, documentUrl)) {
|
|
293
|
-
if (options.crossOrigin === "throw") throw new Error(`The object's @id (${object.id.href}) has a different origin than the document URL (${remoteDoc.documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to "trust".`);
|
|
294
|
-
logger.warn("The object's @id ({objectId}) has a different origin than the document URL ({documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to \"trust\".", {
|
|
295
|
-
...remoteDoc,
|
|
296
|
-
objectId: object.id.href
|
|
297
|
-
});
|
|
298
|
-
return null;
|
|
299
|
-
}
|
|
300
|
-
return object;
|
|
301
316
|
}
|
|
302
317
|
const PORTABLE_IRI_PATTERN = /^ap(?:\+ef61)?:/i;
|
|
303
318
|
/**
|
|
@@ -334,25 +349,8 @@ function parsePortableCandidate(iri) {
|
|
|
334
349
|
* the gateway to ask.
|
|
335
350
|
*/
|
|
336
351
|
function getCompatibleCandidate(href, options) {
|
|
337
|
-
if (options.verifyPortableObject == null
|
|
338
|
-
|
|
339
|
-
try {
|
|
340
|
-
const id = fromCompatibleEf61Id(url);
|
|
341
|
-
if (id == null) return void 0;
|
|
342
|
-
return {
|
|
343
|
-
id,
|
|
344
|
-
gateway: new URL(url.origin)
|
|
345
|
-
};
|
|
346
|
-
} catch (error) {
|
|
347
|
-
if (error instanceof TypeError) {
|
|
348
|
-
logger.debug("Invalid FEP-ef61 compatible identifier {href}: {error}", {
|
|
349
|
-
href,
|
|
350
|
-
error
|
|
351
|
-
});
|
|
352
|
-
return null;
|
|
353
|
-
}
|
|
354
|
-
throw error;
|
|
355
|
-
}
|
|
352
|
+
if (options.verifyPortableObject == null) return void 0;
|
|
353
|
+
return parseCompatibleEf61Reference(href);
|
|
356
354
|
}
|
|
357
355
|
/**
|
|
358
356
|
* Gets the origin of the WebFinger server that {@link lookupWebFinger} asks
|
|
@@ -405,14 +403,15 @@ async function lookupPortableObject(lookup, id, gateways) {
|
|
|
405
403
|
lookup.remaining -= selected.length;
|
|
406
404
|
return await dereference(lookup, id, selected);
|
|
407
405
|
}
|
|
408
|
-
async function dereference({ options, documentLoader }, id, gateways) {
|
|
406
|
+
async function dereference({ options, documentLoader }, id, gateways, extra = {}) {
|
|
409
407
|
const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
|
|
410
408
|
try {
|
|
411
409
|
return await dereferencePortableIri(id, {
|
|
412
410
|
documentLoader,
|
|
413
|
-
contextLoader: options.contextLoader ?? getDocumentLoader({ userAgent: options.userAgent }),
|
|
411
|
+
contextLoader: extra.contextLoader ?? options.contextLoader ?? getDocumentLoader({ userAgent: options.userAgent }),
|
|
414
412
|
tracerProvider,
|
|
415
|
-
gateways,
|
|
413
|
+
inferredGateways: gateways,
|
|
414
|
+
response: extra.response,
|
|
416
415
|
verifyPortableObject: options.verifyPortableObject,
|
|
417
416
|
crossOrigin: options.crossOrigin === "throw" ? "throw" : "ignore",
|
|
418
417
|
signal: options.signal,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import {
|
|
4
|
-
import { t as esm_default } from "./esm-
|
|
3
|
+
import { A as Object$1, F as Person, k as Note, s as Collection } from "./vocab-BHtBRusa.mjs";
|
|
4
|
+
import { t as esm_default } from "./esm-C9nxP5_e.mjs";
|
|
5
|
+
import { n as traverseCollection, t as lookupObject } from "./lookup-BnzAXNN7.mjs";
|
|
5
6
|
import { t as assertInstanceOf } from "./utils-Df2odC27.mjs";
|
|
6
|
-
import { n as traverseCollection, t as lookupObject } from "./lookup-C13R72YU.mjs";
|
|
7
7
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
8
8
|
import { deepStrictEqual, equal, ok, rejects } from "node:assert/strict";
|
|
9
9
|
//#region src/lookup.test.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import {
|
|
4
|
-
import { t as esm_default } from "./esm-
|
|
5
|
-
import { n as getActorHandle } from "./actor-
|
|
3
|
+
import { F as Person } from "./vocab-BHtBRusa.mjs";
|
|
4
|
+
import { t as esm_default } from "./esm-C9nxP5_e.mjs";
|
|
5
|
+
import { n as getActorHandle } from "./actor-B0IFZjgb.mjs";
|
|
6
|
+
import { t as lookupObject } from "./lookup-BnzAXNN7.mjs";
|
|
6
7
|
import { t as assertInstanceOf } from "./utils-Df2odC27.mjs";
|
|
7
|
-
import { t as lookupObject } from "./lookup-C13R72YU.mjs";
|
|
8
8
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
9
9
|
import { deepStrictEqual, equal, rejects } from "node:assert/strict";
|
|
10
10
|
import { FetchError, parseIri } from "@fedify/vocab-runtime";
|
|
@@ -331,5 +331,101 @@ test("getActorHandle() with FEP-ef61 portable actors", {
|
|
|
331
331
|
esm_default.hardReset();
|
|
332
332
|
}
|
|
333
333
|
});
|
|
334
|
+
test("lookupObject() reports inferred gateways as hints", async () => {
|
|
335
|
+
const calls = [];
|
|
336
|
+
const verifyPortableObject = async (_, options) => {
|
|
337
|
+
calls.push(options);
|
|
338
|
+
return { verified: true };
|
|
339
|
+
};
|
|
340
|
+
assertInstanceOf(await lookupObject(compatibleId, {
|
|
341
|
+
documentLoader: createLoader({ [compatibleId]: person() }),
|
|
342
|
+
contextLoader: mockDocumentLoader,
|
|
343
|
+
verifyPortableObject
|
|
344
|
+
}), Person);
|
|
345
|
+
deepStrictEqual(calls.length, 1);
|
|
346
|
+
deepStrictEqual(calls[0].gateways, void 0);
|
|
347
|
+
deepStrictEqual(calls[0].gatewayHints, [new URL("https://example.com")]);
|
|
348
|
+
});
|
|
349
|
+
test("lookupObject() verifies fetched documents that stand for portable objects", async (t) => {
|
|
350
|
+
const plainUrl = "https://example.com/users/alice";
|
|
351
|
+
const redirectingLoader = (documentUrl, document) => {
|
|
352
|
+
const fetched = [];
|
|
353
|
+
const loader = async (url) => {
|
|
354
|
+
fetched.push(url);
|
|
355
|
+
if (url !== plainUrl) throw new FetchError(url, "HTTP 404", new globalThis.Response(null, { status: 404 }));
|
|
356
|
+
return {
|
|
357
|
+
contextUrl: null,
|
|
358
|
+
documentUrl,
|
|
359
|
+
document
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
return Object.assign(loader, { fetched });
|
|
363
|
+
};
|
|
364
|
+
await t.step("a redirect to a compatible identifier", async () => {
|
|
365
|
+
const calls = [];
|
|
366
|
+
const documentLoader = redirectingLoader(compatibleId, person());
|
|
367
|
+
const actor = await lookupObject(plainUrl, {
|
|
368
|
+
documentLoader,
|
|
369
|
+
contextLoader: mockDocumentLoader,
|
|
370
|
+
verifyPortableObject: async (_, options) => {
|
|
371
|
+
calls.push(options);
|
|
372
|
+
return { verified: true };
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
assertInstanceOf(actor, Person);
|
|
376
|
+
deepStrictEqual(actor.id, parseIri(actorId));
|
|
377
|
+
deepStrictEqual(documentLoader.fetched, [plainUrl]);
|
|
378
|
+
deepStrictEqual(calls.length, 1);
|
|
379
|
+
deepStrictEqual(calls[0].documentUrl, new URL(compatibleId));
|
|
380
|
+
deepStrictEqual(calls[0].gatewayHints, [new URL("https://example.com")]);
|
|
381
|
+
equal(await lookupObject(plainUrl, {
|
|
382
|
+
documentLoader,
|
|
383
|
+
contextLoader: mockDocumentLoader,
|
|
384
|
+
verifyPortableObject: createVerifier(false)
|
|
385
|
+
}), null);
|
|
386
|
+
equal(await lookupObject(plainUrl, {
|
|
387
|
+
documentLoader: redirectingLoader(compatibleId, person(`ap://${did}/other`)),
|
|
388
|
+
contextLoader: mockDocumentLoader,
|
|
389
|
+
verifyPortableObject: createVerifier()
|
|
390
|
+
}), null);
|
|
391
|
+
});
|
|
392
|
+
await t.step("a compatible @id", async () => {
|
|
393
|
+
const documentLoader = redirectingLoader(plainUrl, person(compatibleId));
|
|
394
|
+
const verifyPortableObject = createVerifier();
|
|
395
|
+
equal(await lookupObject(plainUrl, {
|
|
396
|
+
documentLoader,
|
|
397
|
+
contextLoader: mockDocumentLoader,
|
|
398
|
+
verifyPortableObject
|
|
399
|
+
}), null);
|
|
400
|
+
deepStrictEqual(verifyPortableObject.documents, []);
|
|
401
|
+
const actor = await lookupObject(plainUrl, {
|
|
402
|
+
documentLoader,
|
|
403
|
+
contextLoader: mockDocumentLoader
|
|
404
|
+
});
|
|
405
|
+
assertInstanceOf(actor, Person);
|
|
406
|
+
deepStrictEqual(actor.id, new URL(compatibleId));
|
|
407
|
+
});
|
|
408
|
+
await t.step("a portable @id", async () => {
|
|
409
|
+
const documentLoader = redirectingLoader(plainUrl, person());
|
|
410
|
+
const actor = await lookupObject(plainUrl, {
|
|
411
|
+
documentLoader,
|
|
412
|
+
contextLoader: mockDocumentLoader,
|
|
413
|
+
verifyPortableObject: createVerifier()
|
|
414
|
+
});
|
|
415
|
+
assertInstanceOf(actor, Person);
|
|
416
|
+
deepStrictEqual(actor.id, parseIri(actorId));
|
|
417
|
+
equal(await lookupObject(plainUrl, {
|
|
418
|
+
documentLoader,
|
|
419
|
+
contextLoader: mockDocumentLoader,
|
|
420
|
+
verifyPortableObject: createVerifier(false)
|
|
421
|
+
}), null);
|
|
422
|
+
await rejects(() => lookupObject(plainUrl, {
|
|
423
|
+
documentLoader,
|
|
424
|
+
contextLoader: mockDocumentLoader,
|
|
425
|
+
verifyPortableObject: createVerifier(false),
|
|
426
|
+
crossOrigin: "throw"
|
|
427
|
+
}), /No gateway returned a valid portable object/);
|
|
428
|
+
});
|
|
429
|
+
});
|
|
334
430
|
//#endregion
|
|
335
431
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import {
|
|
3
|
+
import { M as OrderedCollection, X as vocab_exports, i as Announce, k as Note, u as Create, z as Question } from "./vocab-BHtBRusa.mjs";
|
|
4
4
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
5
5
|
import { deepStrictEqual, ok } from "node:assert/strict";
|
|
6
6
|
import { isMarkerSafeContext, retainSignedRepresentation } from "@fedify/vocab-runtime/internal/signed-representation";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import {
|
|
4
|
-
import "./actor-
|
|
5
|
-
import "./lookup-
|
|
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-B0IFZjgb.mjs";
|
|
5
|
+
import "./lookup-BnzAXNN7.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/dist-tests/type.test.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import {
|
|
3
|
+
import { E as Link, F as Person, J as getEntityTypeById, Y as isEntityType } from "./vocab-BHtBRusa.mjs";
|
|
4
4
|
import { t as getTypeId } from "./type-BMdjm4Eg.mjs";
|
|
5
5
|
import { test } from "@fedify/fixture";
|
|
6
6
|
import { deepStrictEqual } from "node:assert/strict";
|