@fedify/vocab-runtime 2.4.0-dev.2063 → 2.4.0-dev.2071
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/internal/portable-dereference.cjs +193 -21
- package/dist/internal/portable-dereference.d.cts +155 -15
- package/dist/internal/portable-dereference.d.ts +155 -15
- package/dist/internal/portable-dereference.js +187 -22
- package/dist/mod.cjs +1 -1
- package/dist/mod.d.cts +1 -1
- package/dist/mod.d.ts +1 -1
- package/dist/mod.js +1 -1
- package/dist/{portable--NcyA1R_.d.ts → portable-DH3947GK.d.ts} +6 -2
- package/dist/{portable-BykOD45s.d.cts → portable-comBI05Y.d.cts} +6 -2
- package/dist/tests/{body-CTGqQhNI.cjs → body-BA5q10mr.cjs} +1 -1
- package/dist/tests/{body-BN5x-zDQ.mjs → body-XAE-ooHH.mjs} +1 -1
- package/dist/tests/body.test.cjs +1 -1
- package/dist/tests/body.test.mjs +1 -1
- package/dist/tests/decimal.test.cjs +1 -1
- package/dist/tests/decimal.test.mjs +1 -1
- package/dist/tests/{docloader-BFxwbqMx.mjs → docloader-CKZdC6iZ.mjs} +2 -2
- package/dist/tests/{docloader-JGSpG-3l.cjs → docloader-D6BFa7gx.cjs} +2 -2
- package/dist/tests/docloader.test.cjs +2 -2
- package/dist/tests/docloader.test.mjs +2 -2
- package/dist/tests/internal/portable-dereference.test.cjs +87 -0
- package/dist/tests/internal/portable-dereference.test.mjs +87 -0
- package/dist/tests/{request-BT9FBhk6.mjs → request-DXvwmbWU.mjs} +1 -1
- package/dist/tests/{request-jWhPNG9V.cjs → request-D_TFIuTp.cjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/package.json +1 -1
- package/src/internal/portable-dereference.test.ts +32 -0
- package/src/internal/portable-dereference.ts +288 -28
- package/src/portable.ts +6 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { n as DocumentLoader } from "../docloader-Dr8wEkif.js";
|
|
3
|
-
import { r as PortableObjectVerifier } from "../portable
|
|
2
|
+
import { n as DocumentLoader, s as RemoteDocument } from "../docloader-Dr8wEkif.js";
|
|
3
|
+
import { r as PortableObjectVerifier } from "../portable-DH3947GK.js";
|
|
4
4
|
import { Span, TracerProvider } from "@opentelemetry/api";
|
|
5
5
|
|
|
6
6
|
//#region src/internal/portable-dereference.d.ts
|
|
@@ -69,22 +69,128 @@ declare function recordPortableReferrer(parent: object, child: unknown, property
|
|
|
69
69
|
*/
|
|
70
70
|
declare function copyPortableProvenance(from: object, to: object): void;
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
72
|
+
* Recognizes an FEP-ef61 compatible identifier to dereference as a portable
|
|
73
|
+
* object.
|
|
74
|
+
*
|
|
75
|
+
* @returns `undefined` if the URL is not a compatible identifier; `null` if
|
|
76
|
+
* it is a malformed compatible identifier; otherwise, the portable
|
|
77
|
+
* ID it stands for and the gateway it names.
|
|
78
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
79
|
+
* part of the public API contract. This is not considered public API for
|
|
80
|
+
* Semantic Versioning decisions.
|
|
81
|
+
*/
|
|
82
|
+
declare function parseCompatibleEf61Reference(url: URL | string): {
|
|
83
|
+
readonly id: URL;
|
|
84
|
+
readonly gateway: URL;
|
|
85
|
+
} | null | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* Checks whether a property accessor call on a vocabulary object applies the
|
|
88
|
+
* FEP-ef61 portable object policy, i.e., whether the `verifyPortableObject`
|
|
89
|
+
* option is given or the object is part of a chain of portable objects.
|
|
90
|
+
* In this mode, compatible identifiers are dereferenced as portable objects,
|
|
91
|
+
* and fetched documents that turn out to stand for portable objects are
|
|
92
|
+
* verified as such.
|
|
93
|
+
*
|
|
94
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
95
|
+
* part of the public API contract. This is not considered public API for
|
|
96
|
+
* Semantic Versioning decisions.
|
|
97
|
+
*/
|
|
98
|
+
declare function isPortableMode(object: object, options: {
|
|
99
|
+
readonly verifyPortableObject?: PortableObjectVerifier;
|
|
100
|
+
}): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Checks whether an object embedded in a property of a vocabulary object has
|
|
103
|
+
* to be dereferenced by its `@id` and verified as a portable object rather
|
|
104
|
+
* than trusted as embedded, even with `crossOrigin: "trust"`. This is the
|
|
105
|
+
* case in {@link isPortableMode | portable mode} if its `@id` is an FEP-ef61
|
|
106
|
+
* compatible identifier, or a portable IRI whose DID differs from the
|
|
107
|
+
* owner's portable ID.
|
|
108
|
+
*
|
|
109
|
+
* @param parent The object whose property embeds the object.
|
|
110
|
+
* @param ownerId The ID of the object that vouches for embedded objects, or
|
|
111
|
+
* `null` if embedded objects are not trusted by their owner.
|
|
112
|
+
* @param id The `@id` of the embedded object.
|
|
113
|
+
* @param options The options of the accessor.
|
|
114
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
115
|
+
* part of the public API contract. This is not considered public API for
|
|
116
|
+
* Semantic Versioning decisions.
|
|
117
|
+
*/
|
|
118
|
+
declare function mustDereferencePortableObject(parent: object, ownerId: URL | null, id: URL, options: {
|
|
119
|
+
readonly verifyPortableObject?: PortableObjectVerifier;
|
|
120
|
+
}): boolean;
|
|
121
|
+
/**
|
|
122
|
+
* The portable object that a document fetched as an ordinary HTTP(S)
|
|
123
|
+
* document claims to be.
|
|
124
|
+
*
|
|
125
|
+
* @internal
|
|
126
|
+
*/
|
|
127
|
+
interface PortableResponseClaim {
|
|
128
|
+
/** The portable ID that the document has to have. */
|
|
129
|
+
readonly id: URL;
|
|
130
|
+
/**
|
|
131
|
+
* The gateways inferred from the document's URL or `@id`, which are
|
|
132
|
+
* reported to the verifier as gateway hints.
|
|
133
|
+
*/
|
|
134
|
+
readonly inferredGateways: readonly URL[];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Finds out whether a document fetched as an ordinary HTTP(S) document
|
|
138
|
+
* claims to be a portable object: its final URL is a portable IRI or an
|
|
139
|
+
* FEP-ef61 compatible identifier, or else its `@id` is. The final URL takes
|
|
140
|
+
* precedence, so a document whose `@id` claims another object fails the
|
|
141
|
+
* identity check against the final URL.
|
|
142
|
+
*
|
|
143
|
+
* @param documentUrl The final URL of the document.
|
|
144
|
+
* @param objectId The `@id` of the parsed document.
|
|
145
|
+
* @returns `undefined` if the document does not claim to be a portable
|
|
146
|
+
* object; `null` if the claim is malformed; otherwise, the claim.
|
|
147
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
148
|
+
* part of the public API contract. This is not considered public API for
|
|
149
|
+
* Semantic Versioning decisions.
|
|
150
|
+
*/
|
|
151
|
+
declare function getPortableResponseClaim(documentUrl: string, objectId: URL | null): PortableResponseClaim | null | undefined;
|
|
152
|
+
/**
|
|
153
|
+
* Records that an object fetched as an ordinary HTTP(S) object stands for
|
|
154
|
+
* a portable object if its reference, its final URL, or its `@id` is
|
|
155
|
+
* a portable IRI or an FEP-ef61 compatible identifier. Accessors do not
|
|
156
|
+
* cache such objects in their parents, so that a later call with the
|
|
157
|
+
* `verifyPortableObject` option dereferences and verifies them.
|
|
158
|
+
*
|
|
159
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
160
|
+
* part of the public API contract. This is not considered public API for
|
|
161
|
+
* Semantic Versioning decisions.
|
|
162
|
+
*/
|
|
163
|
+
declare function markUnverifiedPortableClaim(object: object, reference: URL, documentUrl: string): void;
|
|
164
|
+
/**
|
|
165
|
+
* Checks whether an object was fetched as an ordinary HTTP(S) object although
|
|
166
|
+
* it stands for a portable object. See {@link markUnverifiedPortableClaim}.
|
|
167
|
+
*
|
|
168
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
169
|
+
* part of the public API contract. This is not considered public API for
|
|
170
|
+
* Semantic Versioning decisions.
|
|
171
|
+
*/
|
|
172
|
+
declare function isUnverifiedPortableClaim(object: object): boolean;
|
|
173
|
+
/**
|
|
174
|
+
* Rejects a malformed FEP-ef61 compatible identifier found in a reference,
|
|
175
|
+
* the final URL of a fetched document, or its `@id`, when it would be
|
|
176
|
+
* dereferenced as a portable object. It is rejected without a request (or
|
|
177
|
+
* without returning the fetched document), since it cannot be verified as
|
|
178
|
+
* a portable object, and fetching it as an ordinary HTTP(S) object would
|
|
179
|
+
* skip the portable object policy.
|
|
78
180
|
*
|
|
79
181
|
* @param url The rejected URL, for the log message.
|
|
80
|
-
* @param
|
|
182
|
+
* @param options The options of the accessor.
|
|
81
183
|
* @returns Always `null`.
|
|
82
|
-
* @throws {Error} If `crossOrigin` is `"throw"
|
|
184
|
+
* @throws {Error} If `crossOrigin` is `"throw"` and `suppressError` is not
|
|
185
|
+
* set.
|
|
83
186
|
* @internal Technically exported for generated vocabulary classes, but not
|
|
84
187
|
* part of the public API contract. This is not considered public API for
|
|
85
188
|
* Semantic Versioning decisions.
|
|
86
189
|
*/
|
|
87
|
-
declare function
|
|
190
|
+
declare function rejectMalformedCompatibleReference(url: string, options: {
|
|
191
|
+
readonly crossOrigin?: "ignore" | "throw" | "trust";
|
|
192
|
+
readonly suppressError?: boolean;
|
|
193
|
+
}): null;
|
|
88
194
|
/**
|
|
89
195
|
* Logs that an embedded object without `@id` in an object accepted without
|
|
90
196
|
* an integrity proof is dropped, because it cannot be dereferenced and
|
|
@@ -116,6 +222,25 @@ declare function isPortableIri(url: URL): boolean;
|
|
|
116
222
|
*/
|
|
117
223
|
declare function getPortableGatewayCandidates(url: URL, gateways?: readonly (string | URL)[]): URL[];
|
|
118
224
|
/**
|
|
225
|
+
* Creates a context loader that returns the same context documents for the
|
|
226
|
+
* whole dereference operation, so that the identity check, the proof
|
|
227
|
+
* verifier, and the parser interpret the fetched document identically even
|
|
228
|
+
* if the underlying loader is nondeterministic. Failed loads are not
|
|
229
|
+
* remembered, so a transient failure does not affect the next gateway.
|
|
230
|
+
*
|
|
231
|
+
* The parsed object keeps the loader for its own later dereferences, so
|
|
232
|
+
* `release()` turns it into a plain pass-through to the underlying loader
|
|
233
|
+
* once the operation is over.
|
|
234
|
+
*
|
|
235
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
236
|
+
* part of the public API contract. This is not considered public API for
|
|
237
|
+
* Semantic Versioning decisions.
|
|
238
|
+
*/
|
|
239
|
+
declare function createSnapshotContextLoader(contextLoader: DocumentLoader): {
|
|
240
|
+
loader: DocumentLoader;
|
|
241
|
+
release: () => void;
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
119
244
|
* Options for {@link dereferencePortableIri}.
|
|
120
245
|
*
|
|
121
246
|
* @internal Technically exported for generated vocabulary classes, but not
|
|
@@ -127,6 +252,20 @@ interface DereferencePortableIriOptions<T> {
|
|
|
127
252
|
contextLoader: DocumentLoader;
|
|
128
253
|
tracerProvider: TracerProvider;
|
|
129
254
|
gateways?: readonly (string | URL)[];
|
|
255
|
+
/**
|
|
256
|
+
* The gateways inferred from where the reference came from, e.g., the
|
|
257
|
+
* gateway of a compatible identifier. When given, they replace the
|
|
258
|
+
* `@gateway` location hints of the IRI, are asked before the explicit
|
|
259
|
+
* `gateways`, and are reported to the verifier as gateway hints unless
|
|
260
|
+
* `gateways` is given.
|
|
261
|
+
*/
|
|
262
|
+
inferredGateways?: readonly URL[];
|
|
263
|
+
/**
|
|
264
|
+
* An already fetched document to validate instead of fetching the object
|
|
265
|
+
* from gateways. It is validated the same way as a gateway's response,
|
|
266
|
+
* and nothing else is tried if it is rejected.
|
|
267
|
+
*/
|
|
268
|
+
response?: RemoteDocument;
|
|
130
269
|
verifyPortableObject?: PortableObjectVerifier;
|
|
131
270
|
referrer?: PortableReferrerLink;
|
|
132
271
|
suppressError?: boolean;
|
|
@@ -158,9 +297,10 @@ declare class PortableObjectRejectedError extends Error {
|
|
|
158
297
|
*
|
|
159
298
|
* Gateways are tried one by one until one of them returns a single-node
|
|
160
299
|
* document whose `@id`, if any, canonically matches the requested IRI and
|
|
161
|
-
* which passes `verifyPortableObject`. When there is no gateway to try, the
|
|
162
|
-
* loader is asked for the portable IRI itself, and its result is
|
|
163
|
-
* the same way.
|
|
300
|
+
* which passes `verifyPortableObject`. When there is no gateway to try, the
|
|
301
|
+
* document loader is asked for the portable IRI itself, and its result is
|
|
302
|
+
* validated the same way. If the `response` option is given, only that
|
|
303
|
+
* document is validated, and nothing is fetched.
|
|
164
304
|
*
|
|
165
305
|
* @returns The parsed object, or `null` if no valid object was retrieved and
|
|
166
306
|
* `suppressError` is set or the failure was a rejected object
|
|
@@ -176,4 +316,4 @@ declare function dereferencePortableIri<T extends {
|
|
|
176
316
|
id: URL | null;
|
|
177
317
|
}>(url: URL, options: DereferencePortableIriOptions<T>): Promise<T | null>;
|
|
178
318
|
//#endregion
|
|
179
|
-
export { DereferencePortableIriOptions, PortableObjectRejectedError, PortableReferrerLink, copyPortableProvenance, dereferencePortableIri, getPortableGatewayCandidates, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isUnsecuredPortableObject, recordPortableReferrer,
|
|
319
|
+
export { DereferencePortableIriOptions, PortableObjectRejectedError, PortableReferrerLink, PortableResponseClaim, copyPortableProvenance, createSnapshotContextLoader, dereferencePortableIri, getPortableGatewayCandidates, getPortableResponseClaim, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isPortableMode, isUnsecuredPortableObject, isUnverifiedPortableClaim, markUnverifiedPortableClaim, mustDereferencePortableObject, parseCompatibleEf61Reference, recordPortableReferrer, rejectMalformedCompatibleReference, warnUnverifiableEmbeddedObject };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { t as preloadedContexts } from "../contexts-DoZUIW7y.js";
|
|
3
|
-
import { a as formatIri, g as toCompatibleEf61Id, m as parseIri, o as fromCompatibleEf61Id, r as canonicalizePortableUri } from "../url-CEhi4hb4.js";
|
|
3
|
+
import { a as formatIri, c as haveSameFe34Origin, g as toCompatibleEf61Id, m as parseIri, o as fromCompatibleEf61Id, r as canonicalizePortableUri } from "../url-CEhi4hb4.js";
|
|
4
4
|
import jsonld_default from "../jsonld.js";
|
|
5
5
|
import { getLogger } from "@logtape/logtape";
|
|
6
6
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
@@ -117,24 +117,169 @@ function copyPortableProvenance(from, to) {
|
|
|
117
117
|
});
|
|
118
118
|
}
|
|
119
119
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
120
|
+
* Recognizes an FEP-ef61 compatible identifier to dereference as a portable
|
|
121
|
+
* object.
|
|
122
|
+
*
|
|
123
|
+
* @returns `undefined` if the URL is not a compatible identifier; `null` if
|
|
124
|
+
* it is a malformed compatible identifier; otherwise, the portable
|
|
125
|
+
* ID it stands for and the gateway it names.
|
|
126
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
127
|
+
* part of the public API contract. This is not considered public API for
|
|
128
|
+
* Semantic Versioning decisions.
|
|
129
|
+
*/
|
|
130
|
+
function parseCompatibleEf61Reference(url) {
|
|
131
|
+
if (typeof url === "string") {
|
|
132
|
+
if (!URL.canParse(url)) return void 0;
|
|
133
|
+
url = new URL(url);
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const id = fromCompatibleEf61Id(url);
|
|
137
|
+
if (id == null) return void 0;
|
|
138
|
+
return {
|
|
139
|
+
id,
|
|
140
|
+
gateway: new URL(url.origin)
|
|
141
|
+
};
|
|
142
|
+
} catch (error) {
|
|
143
|
+
if (error instanceof TypeError) {
|
|
144
|
+
logger.debug("Invalid FEP-ef61 compatible identifier {url}: {error}", {
|
|
145
|
+
url: url.href,
|
|
146
|
+
error
|
|
147
|
+
});
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Checks whether a property accessor call on a vocabulary object applies the
|
|
155
|
+
* FEP-ef61 portable object policy, i.e., whether the `verifyPortableObject`
|
|
156
|
+
* option is given or the object is part of a chain of portable objects.
|
|
157
|
+
* In this mode, compatible identifiers are dereferenced as portable objects,
|
|
158
|
+
* and fetched documents that turn out to stand for portable objects are
|
|
159
|
+
* verified as such.
|
|
160
|
+
*
|
|
161
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
162
|
+
* part of the public API contract. This is not considered public API for
|
|
163
|
+
* Semantic Versioning decisions.
|
|
164
|
+
*/
|
|
165
|
+
function isPortableMode(object, options) {
|
|
166
|
+
return options.verifyPortableObject != null || isInPortableChain(object);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Checks whether an object embedded in a property of a vocabulary object has
|
|
170
|
+
* to be dereferenced by its `@id` and verified as a portable object rather
|
|
171
|
+
* than trusted as embedded, even with `crossOrigin: "trust"`. This is the
|
|
172
|
+
* case in {@link isPortableMode | portable mode} if its `@id` is an FEP-ef61
|
|
173
|
+
* compatible identifier, or a portable IRI whose DID differs from the
|
|
174
|
+
* owner's portable ID.
|
|
175
|
+
*
|
|
176
|
+
* @param parent The object whose property embeds the object.
|
|
177
|
+
* @param ownerId The ID of the object that vouches for embedded objects, or
|
|
178
|
+
* `null` if embedded objects are not trusted by their owner.
|
|
179
|
+
* @param id The `@id` of the embedded object.
|
|
180
|
+
* @param options The options of the accessor.
|
|
181
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
182
|
+
* part of the public API contract. This is not considered public API for
|
|
183
|
+
* Semantic Versioning decisions.
|
|
184
|
+
*/
|
|
185
|
+
function mustDereferencePortableObject(parent, ownerId, id, options) {
|
|
186
|
+
if (!isPortableMode(parent, options)) return false;
|
|
187
|
+
if (isCompatibleEf61Iri(id)) return true;
|
|
188
|
+
if (!isPortableIri(id)) return false;
|
|
189
|
+
return ownerId == null || !isPortableIri(ownerId) || !haveSameFe34Origin(ownerId, id);
|
|
190
|
+
}
|
|
191
|
+
const PORTABLE_IRI_PATTERN = /^ap(?:\+ef61)?:/i;
|
|
192
|
+
/**
|
|
193
|
+
* Finds out whether a document fetched as an ordinary HTTP(S) document
|
|
194
|
+
* claims to be a portable object: its final URL is a portable IRI or an
|
|
195
|
+
* FEP-ef61 compatible identifier, or else its `@id` is. The final URL takes
|
|
196
|
+
* precedence, so a document whose `@id` claims another object fails the
|
|
197
|
+
* identity check against the final URL.
|
|
198
|
+
*
|
|
199
|
+
* @param documentUrl The final URL of the document.
|
|
200
|
+
* @param objectId The `@id` of the parsed document.
|
|
201
|
+
* @returns `undefined` if the document does not claim to be a portable
|
|
202
|
+
* object; `null` if the claim is malformed; otherwise, the claim.
|
|
203
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
204
|
+
* part of the public API contract. This is not considered public API for
|
|
205
|
+
* Semantic Versioning decisions.
|
|
206
|
+
*/
|
|
207
|
+
function getPortableResponseClaim(documentUrl, objectId) {
|
|
208
|
+
if (PORTABLE_IRI_PATTERN.test(documentUrl)) {
|
|
209
|
+
let id;
|
|
210
|
+
try {
|
|
211
|
+
id = parseIri(documentUrl);
|
|
212
|
+
if (canonicalizePortableUri(documentUrl) !== canonicalizePortableUri(formatIri(id))) return null;
|
|
213
|
+
} catch (error) {
|
|
214
|
+
if (error instanceof TypeError) return null;
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
id,
|
|
219
|
+
inferredGateways: getPortableGatewayCandidates(id)
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
for (const url of [documentUrl, objectId]) {
|
|
223
|
+
if (url == null) continue;
|
|
224
|
+
const compatible = parseCompatibleEf61Reference(url);
|
|
225
|
+
if (compatible === null) return null;
|
|
226
|
+
if (compatible != null) return {
|
|
227
|
+
id: compatible.id,
|
|
228
|
+
inferredGateways: [compatible.gateway]
|
|
229
|
+
};
|
|
230
|
+
if (url instanceof URL && isPortableIri(url)) return {
|
|
231
|
+
id: url,
|
|
232
|
+
inferredGateways: getPortableGatewayCandidates(url)
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const unverifiedPortableClaims = /* @__PURE__ */ new WeakSet();
|
|
237
|
+
/**
|
|
238
|
+
* Records that an object fetched as an ordinary HTTP(S) object stands for
|
|
239
|
+
* a portable object if its reference, its final URL, or its `@id` is
|
|
240
|
+
* a portable IRI or an FEP-ef61 compatible identifier. Accessors do not
|
|
241
|
+
* cache such objects in their parents, so that a later call with the
|
|
242
|
+
* `verifyPortableObject` option dereferences and verifies them.
|
|
243
|
+
*
|
|
244
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
245
|
+
* part of the public API contract. This is not considered public API for
|
|
246
|
+
* Semantic Versioning decisions.
|
|
247
|
+
*/
|
|
248
|
+
function markUnverifiedPortableClaim(object, reference, documentUrl) {
|
|
249
|
+
const objectId = getObjectId(object);
|
|
250
|
+
if (isPortableIri(reference) || isCompatibleEf61Iri(reference) || PORTABLE_IRI_PATTERN.test(documentUrl) || isCompatibleEf61Iri(documentUrl) || objectId != null && (isPortableIri(objectId) || isCompatibleEf61Iri(objectId))) unverifiedPortableClaims.add(object);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Checks whether an object was fetched as an ordinary HTTP(S) object although
|
|
254
|
+
* it stands for a portable object. See {@link markUnverifiedPortableClaim}.
|
|
255
|
+
*
|
|
256
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
257
|
+
* part of the public API contract. This is not considered public API for
|
|
258
|
+
* Semantic Versioning decisions.
|
|
259
|
+
*/
|
|
260
|
+
function isUnverifiedPortableClaim(object) {
|
|
261
|
+
return unverifiedPortableClaims.has(object);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Rejects a malformed FEP-ef61 compatible identifier found in a reference,
|
|
265
|
+
* the final URL of a fetched document, or its `@id`, when it would be
|
|
266
|
+
* dereferenced as a portable object. It is rejected without a request (or
|
|
267
|
+
* without returning the fetched document), since it cannot be verified as
|
|
268
|
+
* a portable object, and fetching it as an ordinary HTTP(S) object would
|
|
269
|
+
* skip the portable object policy.
|
|
126
270
|
*
|
|
127
271
|
* @param url The rejected URL, for the log message.
|
|
128
|
-
* @param
|
|
272
|
+
* @param options The options of the accessor.
|
|
129
273
|
* @returns Always `null`.
|
|
130
|
-
* @throws {Error} If `crossOrigin` is `"throw"
|
|
274
|
+
* @throws {Error} If `crossOrigin` is `"throw"` and `suppressError` is not
|
|
275
|
+
* set.
|
|
131
276
|
* @internal Technically exported for generated vocabulary classes, but not
|
|
132
277
|
* part of the public API contract. This is not considered public API for
|
|
133
278
|
* Semantic Versioning decisions.
|
|
134
279
|
*/
|
|
135
|
-
function
|
|
136
|
-
const message = "Refusing to dereference {url}
|
|
137
|
-
if (crossOrigin === "throw") throw new
|
|
280
|
+
function rejectMalformedCompatibleReference(url, options) {
|
|
281
|
+
const message = "Refusing to dereference {url}, because it is a malformed FEP-ef61 compatible identifier, which cannot be verified as a portable object.";
|
|
282
|
+
if (options.crossOrigin === "throw" && !options.suppressError) throw new PortableObjectRejectedError(message.replace("{url}", url));
|
|
138
283
|
logger.warn(message, { url });
|
|
139
284
|
return null;
|
|
140
285
|
}
|
|
@@ -242,12 +387,18 @@ function parseGatewayOrigin(gateway) {
|
|
|
242
387
|
* The parsed object keeps the loader for its own later dereferences, so
|
|
243
388
|
* `release()` turns it into a plain pass-through to the underlying loader
|
|
244
389
|
* once the operation is over.
|
|
390
|
+
*
|
|
391
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
392
|
+
* part of the public API contract. This is not considered public API for
|
|
393
|
+
* Semantic Versioning decisions.
|
|
245
394
|
*/
|
|
246
395
|
function createSnapshotContextLoader(contextLoader) {
|
|
396
|
+
for (let wrapped = snapshotLoaders.get(contextLoader); wrapped?.released; wrapped = snapshotLoaders.get(contextLoader)) contextLoader = wrapped.base;
|
|
247
397
|
const cache = /* @__PURE__ */ new Map();
|
|
248
398
|
let released = false;
|
|
249
399
|
const release = () => {
|
|
250
400
|
released = true;
|
|
401
|
+
state.released = true;
|
|
251
402
|
cache.clear();
|
|
252
403
|
};
|
|
253
404
|
const loader = async (url, options) => {
|
|
@@ -269,11 +420,17 @@ function createSnapshotContextLoader(contextLoader) {
|
|
|
269
420
|
}
|
|
270
421
|
return structuredClone(await promise);
|
|
271
422
|
};
|
|
423
|
+
const state = {
|
|
424
|
+
base: contextLoader,
|
|
425
|
+
released: false
|
|
426
|
+
};
|
|
427
|
+
snapshotLoaders.set(loader, state);
|
|
272
428
|
return {
|
|
273
429
|
loader,
|
|
274
430
|
release
|
|
275
431
|
};
|
|
276
432
|
}
|
|
433
|
+
const snapshotLoaders = /* @__PURE__ */ new WeakMap();
|
|
277
434
|
/**
|
|
278
435
|
* The error thrown by {@link dereferencePortableIri} under
|
|
279
436
|
* `crossOrigin: "throw"` when gateways returned objects but none of them
|
|
@@ -300,9 +457,10 @@ var PortableObjectRejection = class {
|
|
|
300
457
|
*
|
|
301
458
|
* Gateways are tried one by one until one of them returns a single-node
|
|
302
459
|
* document whose `@id`, if any, canonically matches the requested IRI and
|
|
303
|
-
* which passes `verifyPortableObject`. When there is no gateway to try, the
|
|
304
|
-
* loader is asked for the portable IRI itself, and its result is
|
|
305
|
-
* the same way.
|
|
460
|
+
* which passes `verifyPortableObject`. When there is no gateway to try, the
|
|
461
|
+
* document loader is asked for the portable IRI itself, and its result is
|
|
462
|
+
* validated the same way. If the `response` option is given, only that
|
|
463
|
+
* document is validated, and nothing is fetched.
|
|
306
464
|
*
|
|
307
465
|
* @returns The parsed object, or `null` if no valid object was retrieved and
|
|
308
466
|
* `suppressError` is set or the failure was a rejected object
|
|
@@ -318,7 +476,10 @@ async function dereferencePortableIri(url, options) {
|
|
|
318
476
|
url = parseIri(url);
|
|
319
477
|
const lookupUrl = formatIri(url);
|
|
320
478
|
const { span } = options;
|
|
321
|
-
const
|
|
479
|
+
const explicitGateways = options.gateways == null ? void 0 : getPortableGatewayCandidates(url, options.gateways);
|
|
480
|
+
const inferredGateways = options.inferredGateways ?? (explicitGateways == null ? getPortableGatewayCandidates(url) : []);
|
|
481
|
+
const gateways = [...inferredGateways];
|
|
482
|
+
for (const gateway of explicitGateways ?? []) if (!gateways.some((g) => g.href === gateway.href)) gateways.push(gateway);
|
|
322
483
|
const fail = (error) => {
|
|
323
484
|
span?.setStatus({
|
|
324
485
|
code: SpanStatusCode.ERROR,
|
|
@@ -339,7 +500,12 @@ async function dereferencePortableIri(url, options) {
|
|
|
339
500
|
let expectedId;
|
|
340
501
|
try {
|
|
341
502
|
expectedId = canonicalizePortableUri(lookupUrl);
|
|
342
|
-
|
|
503
|
+
if (options.response != null) requestUrls.push({
|
|
504
|
+
url: options.response.documentUrl,
|
|
505
|
+
gateway: null,
|
|
506
|
+
response: options.response
|
|
507
|
+
});
|
|
508
|
+
else for (const gateway of gateways) requestUrls.push({
|
|
343
509
|
url: toCompatibleEf61Id(url, gateway).href,
|
|
344
510
|
gateway
|
|
345
511
|
});
|
|
@@ -354,15 +520,14 @@ async function dereferencePortableIri(url, options) {
|
|
|
354
520
|
const contextLoader = snapshot.loader;
|
|
355
521
|
const attempts = [];
|
|
356
522
|
const { signal } = options;
|
|
357
|
-
const
|
|
358
|
-
const gatewayHints = options.gateways == null && gateways.length > 0 ? gateways : void 0;
|
|
523
|
+
const gatewayHints = explicitGateways == null && inferredGateways.length > 0 ? inferredGateways : void 0;
|
|
359
524
|
const referrer = buildReferrerChain(options.referrer);
|
|
360
525
|
try {
|
|
361
|
-
for (const { url: requestUrl, gateway } of requestUrls) {
|
|
526
|
+
for (const { url: requestUrl, gateway, response } of requestUrls) {
|
|
362
527
|
signal?.throwIfAborted();
|
|
363
528
|
let remoteDocument;
|
|
364
529
|
try {
|
|
365
|
-
remoteDocument = await options.documentLoader(requestUrl, signal == null ? void 0 : { signal });
|
|
530
|
+
remoteDocument = response ?? await options.documentLoader(requestUrl, signal == null ? void 0 : { signal });
|
|
366
531
|
} catch (error) {
|
|
367
532
|
signal?.throwIfAborted();
|
|
368
533
|
logger.debug("Failed to fetch {url} from {requestUrl}: {error}", {
|
|
@@ -476,4 +641,4 @@ async function checkPortableObjectId(document, expectedId, contextLoader) {
|
|
|
476
641
|
if (actualId !== expectedId) throw new PortableObjectRejection("its @id ({objectId}) does not match the requested portable ID", { objectId: id });
|
|
477
642
|
}
|
|
478
643
|
//#endregion
|
|
479
|
-
export { PortableObjectRejectedError, copyPortableProvenance, dereferencePortableIri, getPortableGatewayCandidates, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isUnsecuredPortableObject, recordPortableReferrer,
|
|
644
|
+
export { PortableObjectRejectedError, copyPortableProvenance, createSnapshotContextLoader, dereferencePortableIri, getPortableGatewayCandidates, getPortableResponseClaim, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isPortableMode, isUnsecuredPortableObject, isUnverifiedPortableClaim, markUnverifiedPortableClaim, mustDereferencePortableObject, parseCompatibleEf61Reference, recordPortableReferrer, rejectMalformedCompatibleReference, warnUnverifiableEmbeddedObject };
|
package/dist/mod.cjs
CHANGED
|
@@ -16,7 +16,7 @@ let _multiformats_base_x = require("@multiformats/base-x");
|
|
|
16
16
|
_multiformats_base_x = require_rolldown_runtime.__toESM(_multiformats_base_x, 1);
|
|
17
17
|
//#region deno.json
|
|
18
18
|
var name = "@fedify/vocab-runtime";
|
|
19
|
-
var version = "2.4.0-dev.
|
|
19
|
+
var version = "2.4.0-dev.2071+8b9dccb9";
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region src/request.ts
|
|
22
22
|
/**
|
package/dist/mod.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
2
|
import { a as DocumentLoaderOptions, c as getDocumentLoader, d as FetchError, f as GetUserAgentOptions, h as logRequest, i as DocumentLoaderFactoryOptions, l as getRemoteDocument, m as getUserAgent, n as DocumentLoader, o as GetDocumentLoaderOptions, p as createActivityPubRequest, r as DocumentLoaderFactory, s as RemoteDocument, t as AuthenticatedDocumentLoaderFactory, u as CreateRequestOptions } from "./docloader-Dr8wEkif.cjs";
|
|
3
|
-
import { i as PortableObjectVerifierOptions, n as PortableObjectVerification, r as PortableObjectVerifier, t as PortableObjectReferrer } from "./portable-
|
|
3
|
+
import { i as PortableObjectVerifierOptions, n as PortableObjectVerification, r as PortableObjectVerifier, t as PortableObjectReferrer } from "./portable-comBI05Y.cjs";
|
|
4
4
|
import { TracerProvider } from "@opentelemetry/api";
|
|
5
5
|
|
|
6
6
|
//#region src/contexts.d.ts
|
package/dist/mod.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
2
|
import { a as DocumentLoaderOptions, c as getDocumentLoader, d as FetchError, f as GetUserAgentOptions, h as logRequest, i as DocumentLoaderFactoryOptions, l as getRemoteDocument, m as getUserAgent, n as DocumentLoader, o as GetDocumentLoaderOptions, p as createActivityPubRequest, r as DocumentLoaderFactory, s as RemoteDocument, t as AuthenticatedDocumentLoaderFactory, u as CreateRequestOptions } from "./docloader-Dr8wEkif.js";
|
|
3
|
-
import { i as PortableObjectVerifierOptions, n as PortableObjectVerification, r as PortableObjectVerifier, t as PortableObjectReferrer } from "./portable
|
|
3
|
+
import { i as PortableObjectVerifierOptions, n as PortableObjectVerification, r as PortableObjectVerifier, t as PortableObjectReferrer } from "./portable-DH3947GK.js";
|
|
4
4
|
import { TracerProvider } from "@opentelemetry/api";
|
|
5
5
|
//#region src/contexts.d.ts
|
|
6
6
|
declare const preloadedContexts: Record<string, unknown>;
|
package/dist/mod.js
CHANGED
|
@@ -12,7 +12,7 @@ import { PublicKeyInfo } from "pkijs";
|
|
|
12
12
|
import baseX from "@multiformats/base-x";
|
|
13
13
|
//#region deno.json
|
|
14
14
|
var name = "@fedify/vocab-runtime";
|
|
15
|
-
var version = "2.4.0-dev.
|
|
15
|
+
var version = "2.4.0-dev.2071+8b9dccb9";
|
|
16
16
|
//#endregion
|
|
17
17
|
//#region src/request.ts
|
|
18
18
|
/**
|
|
@@ -42,8 +42,12 @@ interface PortableObjectVerifierOptions {
|
|
|
42
42
|
*/
|
|
43
43
|
readonly gateways?: readonly URL[];
|
|
44
44
|
/**
|
|
45
|
-
* The gateways
|
|
46
|
-
*
|
|
45
|
+
* The gateways inferred from the reference, in order, when the caller did
|
|
46
|
+
* not pass the `gateways` option: the `@gateway` location hints of
|
|
47
|
+
* a portable IRI, the gateway named by an FEP-ef61 compatible identifier,
|
|
48
|
+
* or the WebFinger server of a portable actor. They only tell where the
|
|
49
|
+
* document and related objects might be retrieved, and must not be
|
|
50
|
+
* trusted to act for the portable object's DID.
|
|
47
51
|
* @since 2.4.0
|
|
48
52
|
*/
|
|
49
53
|
readonly gatewayHints?: readonly URL[];
|
|
@@ -42,8 +42,12 @@ interface PortableObjectVerifierOptions {
|
|
|
42
42
|
*/
|
|
43
43
|
readonly gateways?: readonly URL[];
|
|
44
44
|
/**
|
|
45
|
-
* The gateways
|
|
46
|
-
*
|
|
45
|
+
* The gateways inferred from the reference, in order, when the caller did
|
|
46
|
+
* not pass the `gateways` option: the `@gateway` location hints of
|
|
47
|
+
* a portable IRI, the gateway named by an FEP-ef61 compatible identifier,
|
|
48
|
+
* or the WebFinger server of a portable actor. They only tell where the
|
|
49
|
+
* document and related objects might be retrieved, and must not be
|
|
50
|
+
* trusted to act for the portable object's DID.
|
|
47
51
|
* @since 2.4.0
|
|
48
52
|
*/
|
|
49
53
|
readonly gatewayHints?: readonly URL[];
|
package/dist/tests/body.test.cjs
CHANGED
package/dist/tests/body.test.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as readBoundedText, t as BodyTooLargeError } from "./body-
|
|
1
|
+
import { r as readBoundedText, t as BodyTooLargeError } from "./body-XAE-ooHH.mjs";
|
|
2
2
|
import { deepStrictEqual, ok, rejects } from "node:assert/strict";
|
|
3
3
|
import { test } from "node:test";
|
|
4
4
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-
|
|
2
|
-
import { n as MAX_BODY_SIZE, r as readBoundedText, t as BodyTooLargeError } from "./body-
|
|
1
|
+
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-DXvwmbWU.mjs";
|
|
2
|
+
import { n as MAX_BODY_SIZE, r as readBoundedText, t as BodyTooLargeError } from "./body-XAE-ooHH.mjs";
|
|
3
3
|
import { t as preloadedContexts } from "./contexts-BBX_qhBL.mjs";
|
|
4
4
|
import { t as HttpHeaderLink } from "./link-Cevmc87v.mjs";
|
|
5
5
|
import { t as UrlError, v as validatePublicUrl } from "./url-DgJM9WJn.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_request = require("./request-
|
|
2
|
-
const require_body = require("./body-
|
|
1
|
+
const require_request = require("./request-D_TFIuTp.cjs");
|
|
2
|
+
const require_body = require("./body-BA5q10mr.cjs");
|
|
3
3
|
const require_contexts = require("./contexts-Ch3v2Cgu.cjs");
|
|
4
4
|
const require_link = require("./link-DlKm8bEr.cjs");
|
|
5
5
|
const require_url = require("./url-BqlldzT8.cjs");
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-emK7D4bc.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-D_TFIuTp.cjs");
|
|
3
3
|
const require_contexts = require("./contexts-Ch3v2Cgu.cjs");
|
|
4
4
|
const require_url = require("./url-BqlldzT8.cjs");
|
|
5
|
-
const require_docloader = require("./docloader-
|
|
5
|
+
const require_docloader = require("./docloader-D6BFa7gx.cjs");
|
|
6
6
|
let node_test = require("node:test");
|
|
7
7
|
let node_zlib = require("node:zlib");
|
|
8
8
|
let node_assert = require("node:assert");
|