@fedify/vocab 2.5.0-dev.2332 → 2.5.0-dev.2355
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 +851 -2
- package/dist/mod.d.cts +227 -0
- package/dist/mod.d.ts +227 -0
- package/dist/mod.js +851 -2
- package/dist-tests/{actor-DpNkhFU9.mjs → actor-BbIjMjNa.mjs} +2 -2
- package/dist-tests/actor.test.mjs +3 -3
- package/dist-tests/application.yaml +15 -0
- package/dist-tests/{esm-Cncdk-Cm.mjs → esm-rg6f9oLR.mjs} +1 -1
- package/dist-tests/gateway.test.mjs +3 -3
- package/dist-tests/group.yaml +15 -0
- package/dist-tests/license.test.mjs +538 -0
- package/dist-tests/link.yaml +25 -0
- package/dist-tests/{lookup-CIDHxy5l.mjs → lookup-DKLMyPMJ.mjs} +2 -2
- package/dist-tests/lookup.test.mjs +3 -3
- package/dist-tests/object.yaml +30 -0
- package/dist-tests/organization.yaml +15 -0
- package/dist-tests/person.yaml +15 -0
- package/dist-tests/portable-lookup.test.mjs +4 -4
- package/dist-tests/service.yaml +15 -0
- package/dist-tests/signed-representation.test.mjs +1 -1
- package/dist-tests/src-DVk5F9Ik.mjs +9 -0
- package/dist-tests/translation.test.mjs +3 -6
- package/dist-tests/type.test.mjs +1 -1
- package/dist-tests/{vocab-EFWWufqL.mjs → vocab-Cg2PG-xH.mjs} +851 -2
- package/dist-tests/vocab.test.mjs +9 -3
- package/package.json +4 -4
- package/src/__snapshots__/vocab.test.ts.snap +480 -33
- package/src/application.yaml +15 -0
- package/src/group.yaml +15 -0
- package/src/license.test.ts +588 -0
- package/src/link.yaml +25 -0
- package/src/object.yaml +30 -0
- package/src/organization.yaml +15 -0
- package/src/person.yaml +15 -0
- package/src/service.yaml +15 -0
- package/src/vocab.test.ts +18 -6
- package/LICENSE +0 -20
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import { F as Person, P as Organization, U as Service, a as Application, b as Group } from "./vocab-
|
|
3
|
+
import { F as Person, P as Organization, U as Service, a as Application, b as Group } from "./vocab-Cg2PG-xH.mjs";
|
|
4
4
|
import { t as getTypeId } from "./type-BMdjm4Eg.mjs";
|
|
5
5
|
import { canonicalizePortableUri, formatIri, fromCompatibleEf61Id, isGatewayUrl } from "@fedify/vocab-runtime";
|
|
6
6
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
@@ -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.5.0-dev.
|
|
11
|
+
var version = "2.5.0-dev.2355+c92a1cb1";
|
|
12
12
|
//#endregion
|
|
13
13
|
//#region src/actor.ts
|
|
14
14
|
const ACTOR_DISCOVERY_HISTOGRAM_BUCKETS = [
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import {
|
|
4
|
-
import { t as esm_default } from "./esm-
|
|
5
|
-
import { a as normalizeActorHandle, i as isActor, n as getActorHandle, r as getActorTypeName, t as getActorClassByTypeName } from "./actor-
|
|
3
|
+
import { $ as __exportAll, F as Person, P as Organization, U as Service, a as Application, b as Group } from "./vocab-Cg2PG-xH.mjs";
|
|
4
|
+
import { t as esm_default } from "./esm-rg6f9oLR.mjs";
|
|
5
|
+
import { a as normalizeActorHandle, i as isActor, n as getActorHandle, r as getActorTypeName, t as getActorClassByTypeName } from "./actor-BbIjMjNa.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
|
|
@@ -351,3 +351,18 @@ properties:
|
|
|
351
351
|
attached in some clients.
|
|
352
352
|
range:
|
|
353
353
|
- "http://www.w3.org/2001/XMLSchema#boolean"
|
|
354
|
+
|
|
355
|
+
- singularName: preferredLicense
|
|
356
|
+
functional: true
|
|
357
|
+
compactName: preferredLicense
|
|
358
|
+
uri: "https://w3id.org/fep/6757#preferredLicense"
|
|
359
|
+
extraContext: "https://w3id.org/fep/6757"
|
|
360
|
+
description: |
|
|
361
|
+
The URI of the license this actor prefers for the new objects it creates,
|
|
362
|
+
as defined by [FEP-6757](https://w3id.org/fep/6757). A client can use it
|
|
363
|
+
as the default `license` of a new object and let the user choose another.
|
|
364
|
+
|
|
365
|
+
This is not a fallback license: an object without its own `license` is not
|
|
366
|
+
licensed under its author's preferred license.
|
|
367
|
+
range:
|
|
368
|
+
- "fedify:absoluteIri"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import {
|
|
3
|
+
import { Q as __commonJSMin, et as __toESM } from "./vocab-Cg2PG-xH.mjs";
|
|
4
4
|
//#endregion
|
|
5
5
|
//#region ../../node_modules/.pnpm/regexparam@3.0.0/node_modules/regexparam/dist/index.mjs
|
|
6
6
|
var import_glob_to_regexp = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import { F as Person, M as OrderedCollection, S as Image, c as CollectionPage, k as Note, s as Collection, u as Create } from "./vocab-
|
|
4
|
-
import { t as esm_default } from "./esm-
|
|
5
|
-
import { n as traverseCollection, t as lookupObject } from "./lookup-
|
|
3
|
+
import { F as Person, M as OrderedCollection, S as Image, c as CollectionPage, k as Note, s as Collection, u as Create } from "./vocab-Cg2PG-xH.mjs";
|
|
4
|
+
import { t as esm_default } from "./esm-rg6f9oLR.mjs";
|
|
5
|
+
import { n as traverseCollection, t as lookupObject } from "./lookup-DKLMyPMJ.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";
|
package/dist-tests/group.yaml
CHANGED
|
@@ -351,3 +351,18 @@ properties:
|
|
|
351
351
|
attached in some clients.
|
|
352
352
|
range:
|
|
353
353
|
- "http://www.w3.org/2001/XMLSchema#boolean"
|
|
354
|
+
|
|
355
|
+
- singularName: preferredLicense
|
|
356
|
+
functional: true
|
|
357
|
+
compactName: preferredLicense
|
|
358
|
+
uri: "https://w3id.org/fep/6757#preferredLicense"
|
|
359
|
+
extraContext: "https://w3id.org/fep/6757"
|
|
360
|
+
description: |
|
|
361
|
+
The URI of the license this actor prefers for the new objects it creates,
|
|
362
|
+
as defined by [FEP-6757](https://w3id.org/fep/6757). A client can use it
|
|
363
|
+
as the default `license` of a new object and let the user choose another.
|
|
364
|
+
|
|
365
|
+
This is not a fallback license: an object without its own `license` is not
|
|
366
|
+
licensed under its author's preferred license.
|
|
367
|
+
range:
|
|
368
|
+
- "fedify:absoluteIri"
|
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
import "temporal-polyfill";
|
|
2
|
+
globalThis.addEventListener = () => {};
|
|
3
|
+
import { E as Link, F as Person, J as Video, P as Organization, S as Image, U as Service, a as Application, b as Group, k as Note, s as Collection, u as Create, z as Question } from "./vocab-Cg2PG-xH.mjs";
|
|
4
|
+
import "./src-DVk5F9Ik.mjs";
|
|
5
|
+
import { test } from "@fedify/fixture";
|
|
6
|
+
import { deepStrictEqual, ok } from "node:assert/strict";
|
|
7
|
+
import { getDocumentLoader, preloadedContexts } from "@fedify/vocab-runtime";
|
|
8
|
+
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
9
|
+
//#region src/license.test.ts
|
|
10
|
+
const AS = "https://www.w3.org/ns/activitystreams";
|
|
11
|
+
const FEP = "https://w3id.org/fep/6757";
|
|
12
|
+
const DC_LICENSE = "http://purl.org/dc/terms/license";
|
|
13
|
+
const PREFERRED_LICENSE = `${FEP}#preferredLicense`;
|
|
14
|
+
const SCHEMA_LICENSE = "https://schema.org/license";
|
|
15
|
+
const HTTP_SCHEMA_LICENSE = "http://schema.org/license";
|
|
16
|
+
const CC_LICENSE = "http://creativecommons.org/ns#license";
|
|
17
|
+
const BY = "https://creativecommons.org/licenses/by/4.0/";
|
|
18
|
+
const BY_NC = "https://creativecommons.org/licenses/by-nc/4.0/";
|
|
19
|
+
const BY_NC_ND = "https://creativecommons.org/licenses/by-nc-nd/4.0/";
|
|
20
|
+
const CC0 = "https://creativecommons.org/publicdomain/zero/1.0/";
|
|
21
|
+
const PD_MARK = "https://creativecommons.org/publicdomain/mark/1.0/";
|
|
22
|
+
const noteExample = {
|
|
23
|
+
"@context": [
|
|
24
|
+
AS,
|
|
25
|
+
FEP,
|
|
26
|
+
{ schema: "https://schema.org/" }
|
|
27
|
+
],
|
|
28
|
+
id: "https://social.example/note/857",
|
|
29
|
+
type: "Note",
|
|
30
|
+
content: "Hello, world!",
|
|
31
|
+
license: BY,
|
|
32
|
+
"schema:license": BY,
|
|
33
|
+
attachment: {
|
|
34
|
+
type: "Link",
|
|
35
|
+
href: "https://media.social.example/uploads/file339.jpg",
|
|
36
|
+
mediaType: "image/jpeg",
|
|
37
|
+
license: PD_MARK
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const personExample = {
|
|
41
|
+
"@context": [AS, FEP],
|
|
42
|
+
id: "https://social.example/user/1753",
|
|
43
|
+
type: "Person",
|
|
44
|
+
name: "Example User",
|
|
45
|
+
inbox: "https://social.example/collection/9257",
|
|
46
|
+
outbox: "https://social.example/collection/10772",
|
|
47
|
+
summary: "An example user who is very illustrative of important features",
|
|
48
|
+
license: BY_NC_ND,
|
|
49
|
+
icon: {
|
|
50
|
+
type: "Image",
|
|
51
|
+
license: BY_NC,
|
|
52
|
+
url: {
|
|
53
|
+
type: "Link",
|
|
54
|
+
mediaType: "image/jpeg",
|
|
55
|
+
href: "https://media.social.example/upload/7272.jpg"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
preferredLicense: BY_NC
|
|
59
|
+
};
|
|
60
|
+
function record(value) {
|
|
61
|
+
ok(value != null && typeof value === "object" && !Array.isArray(value));
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
const noFetch = (url) => {
|
|
65
|
+
throw new Error(`Unexpected fetch: ${url}`);
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* A context loader that serves only preloaded contexts and records them.
|
|
69
|
+
*/
|
|
70
|
+
function offlineContextLoader() {
|
|
71
|
+
const loaded = [];
|
|
72
|
+
const loader = getDocumentLoader();
|
|
73
|
+
const contextLoader = (url, options) => {
|
|
74
|
+
if (!(url in preloadedContexts)) throw new Error(`Unexpected context fetch: ${url}`);
|
|
75
|
+
loaded.push(url);
|
|
76
|
+
return loader(url, options);
|
|
77
|
+
};
|
|
78
|
+
return Object.assign(contextLoader, { loaded });
|
|
79
|
+
}
|
|
80
|
+
async function expand(json) {
|
|
81
|
+
return record((await jsonld.expand(json, { documentLoader: getDocumentLoader() }))[0]);
|
|
82
|
+
}
|
|
83
|
+
function contexts(json) {
|
|
84
|
+
const context = record(json)["@context"];
|
|
85
|
+
return Array.isArray(context) ? context : [context];
|
|
86
|
+
}
|
|
87
|
+
function note(licenses = [new URL(BY)]) {
|
|
88
|
+
return new Note({
|
|
89
|
+
id: new URL("https://social.example/note/857"),
|
|
90
|
+
content: "Hello, world!",
|
|
91
|
+
licenses
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
test("FEP-6757 context is preloaded", async () => {
|
|
95
|
+
ok(FEP in preloadedContexts);
|
|
96
|
+
deepStrictEqual(record((await getDocumentLoader()(FEP)).document)["@context"], {
|
|
97
|
+
license: {
|
|
98
|
+
"@id": DC_LICENSE,
|
|
99
|
+
"@type": "@id"
|
|
100
|
+
},
|
|
101
|
+
preferredLicense: {
|
|
102
|
+
"@id": PREFERRED_LICENSE,
|
|
103
|
+
"@type": "@id"
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
test("FEP-6757 note example is parsed offline", async () => {
|
|
108
|
+
const contextLoader = offlineContextLoader();
|
|
109
|
+
const parsed = await Note.fromJsonLd(noteExample, {
|
|
110
|
+
documentLoader: noFetch,
|
|
111
|
+
contextLoader
|
|
112
|
+
});
|
|
113
|
+
deepStrictEqual(parsed.licenses, [new URL(BY)]);
|
|
114
|
+
const attachments = await Array.fromAsync(parsed.getAttachments({
|
|
115
|
+
documentLoader: noFetch,
|
|
116
|
+
contextLoader
|
|
117
|
+
}));
|
|
118
|
+
deepStrictEqual(attachments.length, 1);
|
|
119
|
+
ok(attachments[0] instanceof Link);
|
|
120
|
+
deepStrictEqual(attachments[0].licenses, [new URL(PD_MARK)]);
|
|
121
|
+
ok(contextLoader.loaded.includes(FEP));
|
|
122
|
+
deepStrictEqual(await parsed.toJsonLd(), noteExample);
|
|
123
|
+
});
|
|
124
|
+
test("FEP-6757 person example is parsed offline", async () => {
|
|
125
|
+
const contextLoader = offlineContextLoader();
|
|
126
|
+
const person = await Person.fromJsonLd(personExample, {
|
|
127
|
+
documentLoader: noFetch,
|
|
128
|
+
contextLoader
|
|
129
|
+
});
|
|
130
|
+
deepStrictEqual(person.licenses, [new URL(BY_NC_ND)]);
|
|
131
|
+
deepStrictEqual(person.preferredLicense, new URL(BY_NC));
|
|
132
|
+
const icon = await person.getIcon({
|
|
133
|
+
documentLoader: noFetch,
|
|
134
|
+
contextLoader
|
|
135
|
+
});
|
|
136
|
+
ok(icon instanceof Image);
|
|
137
|
+
deepStrictEqual(icon.licenses, [new URL(BY_NC)]);
|
|
138
|
+
const url = icon.url;
|
|
139
|
+
ok(url instanceof Link);
|
|
140
|
+
deepStrictEqual(url.licenses, []);
|
|
141
|
+
deepStrictEqual(await person.toJsonLd(), personExample);
|
|
142
|
+
});
|
|
143
|
+
test("FEP-6757 license is written only as the canonical IRI", async () => {
|
|
144
|
+
for (const format of [void 0, "compact"]) {
|
|
145
|
+
const json = record(await note().toJsonLd({ format }));
|
|
146
|
+
deepStrictEqual(contexts(json).at(-1), FEP);
|
|
147
|
+
deepStrictEqual(json.license, BY);
|
|
148
|
+
const expanded = await expand(json);
|
|
149
|
+
deepStrictEqual(expanded[DC_LICENSE], [{ "@id": BY }]);
|
|
150
|
+
for (const synonym of [
|
|
151
|
+
SCHEMA_LICENSE,
|
|
152
|
+
HTTP_SCHEMA_LICENSE,
|
|
153
|
+
CC_LICENSE
|
|
154
|
+
]) ok(!(synonym in expanded));
|
|
155
|
+
}
|
|
156
|
+
deepStrictEqual(record((await note().toJsonLd({ format: "expand" }))[0])[DC_LICENSE], [{ "@id": BY }]);
|
|
157
|
+
});
|
|
158
|
+
test("FEP-6757 multiple licenses are alternatives in observed order", async () => {
|
|
159
|
+
const licenses = [new URL(BY), new URL(CC0)];
|
|
160
|
+
const json = record(await note(licenses).toJsonLd());
|
|
161
|
+
deepStrictEqual(json.license, [BY, CC0]);
|
|
162
|
+
deepStrictEqual((await Note.fromJsonLd(json, { documentLoader: noFetch })).licenses, licenses);
|
|
163
|
+
});
|
|
164
|
+
test("FEP-6757 objects without license metadata serialize as before", async () => {
|
|
165
|
+
const fields = {
|
|
166
|
+
id: new URL("https://social.example/note/1"),
|
|
167
|
+
content: "No license"
|
|
168
|
+
};
|
|
169
|
+
for (const format of [void 0, "compact"]) {
|
|
170
|
+
const json = record(await new Note(fields).toJsonLd({ format }));
|
|
171
|
+
ok(!JSON.stringify(json["@context"]).includes(FEP));
|
|
172
|
+
ok(!("license" in json));
|
|
173
|
+
const person = record(await new Person({ id: new URL("https://social.example/user/1") }).toJsonLd({ format }));
|
|
174
|
+
ok(!JSON.stringify(person["@context"]).includes(FEP));
|
|
175
|
+
ok(!("preferredLicense" in person));
|
|
176
|
+
}
|
|
177
|
+
deepStrictEqual(await new Note(fields).toJsonLd(), {
|
|
178
|
+
"@context": [
|
|
179
|
+
AS,
|
|
180
|
+
"https://w3id.org/security/data-integrity/v1",
|
|
181
|
+
"https://gotosocial.org/ns",
|
|
182
|
+
{
|
|
183
|
+
toot: "http://joinmastodon.org/ns#",
|
|
184
|
+
misskey: "https://misskey-hub.net/ns#",
|
|
185
|
+
fedibird: "http://fedibird.com/ns#",
|
|
186
|
+
sensitive: "as:sensitive",
|
|
187
|
+
Emoji: "toot:Emoji",
|
|
188
|
+
Hashtag: "as:Hashtag",
|
|
189
|
+
quote: {
|
|
190
|
+
"@id": "https://w3id.org/fep/044f#quote",
|
|
191
|
+
"@type": "@id"
|
|
192
|
+
},
|
|
193
|
+
quoteUrl: "as:quoteUrl",
|
|
194
|
+
_misskey_quote: "misskey:_misskey_quote",
|
|
195
|
+
quoteUri: "fedibird:quoteUri",
|
|
196
|
+
QuoteAuthorization: "https://w3id.org/fep/044f#QuoteAuthorization",
|
|
197
|
+
quoteAuthorization: {
|
|
198
|
+
"@id": "https://w3id.org/fep/044f#quoteAuthorization",
|
|
199
|
+
"@type": "@id"
|
|
200
|
+
},
|
|
201
|
+
emojiReactions: {
|
|
202
|
+
"@id": "fedibird:emojiReactions",
|
|
203
|
+
"@type": "@id"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
],
|
|
207
|
+
id: "https://social.example/note/1",
|
|
208
|
+
type: "Note",
|
|
209
|
+
content: "No license"
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
test("FEP-6757 preferredLicense on every actor type", async () => {
|
|
213
|
+
for (const Actor of [
|
|
214
|
+
Application,
|
|
215
|
+
Group,
|
|
216
|
+
Organization,
|
|
217
|
+
Person,
|
|
218
|
+
Service
|
|
219
|
+
]) {
|
|
220
|
+
const actor = new Actor({
|
|
221
|
+
id: new URL("https://social.example/actor"),
|
|
222
|
+
preferredLicense: new URL(BY_NC)
|
|
223
|
+
});
|
|
224
|
+
for (const format of [void 0, "compact"]) {
|
|
225
|
+
const json = record(await actor.toJsonLd({ format }));
|
|
226
|
+
deepStrictEqual(contexts(json).at(-1), FEP);
|
|
227
|
+
deepStrictEqual(json.preferredLicense, BY_NC);
|
|
228
|
+
ok(!("license" in json));
|
|
229
|
+
deepStrictEqual((await expand(json))[PREFERRED_LICENSE], [{ "@id": BY_NC }]);
|
|
230
|
+
const parsed = await Actor.fromJsonLd(json, { documentLoader: noFetch });
|
|
231
|
+
deepStrictEqual(parsed.preferredLicense, new URL(BY_NC));
|
|
232
|
+
deepStrictEqual(parsed.licenses, []);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
test("FEP-6757 licenses in activities compact to the license term", async () => {
|
|
237
|
+
const create = new Create({
|
|
238
|
+
id: new URL("https://social.example/create/1"),
|
|
239
|
+
actor: new URL("https://social.example/user/1753"),
|
|
240
|
+
object: note()
|
|
241
|
+
});
|
|
242
|
+
for (const format of [void 0, "compact"]) {
|
|
243
|
+
const json = record(await create.toJsonLd({ format }));
|
|
244
|
+
deepStrictEqual(contexts(json).at(-1), FEP);
|
|
245
|
+
deepStrictEqual(record(json.object).license, BY);
|
|
246
|
+
ok(!("license" in json));
|
|
247
|
+
ok(!JSON.stringify(json).includes("dc:license"));
|
|
248
|
+
}
|
|
249
|
+
const parsedNote = await Note.fromJsonLd(noteExample, { documentLoader: noFetch });
|
|
250
|
+
const forwarded = record(await new Create({
|
|
251
|
+
actor: new URL("https://social.example/user/1753"),
|
|
252
|
+
object: parsedNote
|
|
253
|
+
}).toJsonLd());
|
|
254
|
+
deepStrictEqual(contexts(forwarded).at(-1), FEP);
|
|
255
|
+
deepStrictEqual(record(forwarded.object).license, BY);
|
|
256
|
+
const question = record(await new Question({
|
|
257
|
+
id: new URL("https://social.example/question/1"),
|
|
258
|
+
licenses: [new URL(BY)]
|
|
259
|
+
}).toJsonLd());
|
|
260
|
+
deepStrictEqual(question.license, BY);
|
|
261
|
+
ok(!JSON.stringify(question).includes("dc:license"));
|
|
262
|
+
const inline = await Note.fromJsonLd({
|
|
263
|
+
"@context": [AS, { license: {
|
|
264
|
+
"@id": DC_LICENSE,
|
|
265
|
+
"@type": "@id"
|
|
266
|
+
} }],
|
|
267
|
+
id: "https://social.example/note/inline",
|
|
268
|
+
type: "Note",
|
|
269
|
+
license: BY
|
|
270
|
+
}, { documentLoader: noFetch });
|
|
271
|
+
const collection = record(await new Collection({
|
|
272
|
+
id: new URL("https://social.example/collection/1"),
|
|
273
|
+
items: [inline],
|
|
274
|
+
summary: "Licensed items"
|
|
275
|
+
}).toJsonLd());
|
|
276
|
+
deepStrictEqual(contexts(collection).at(-1), FEP);
|
|
277
|
+
const items = collection.items;
|
|
278
|
+
deepStrictEqual(items.license, BY);
|
|
279
|
+
});
|
|
280
|
+
test("FEP-6757 licenses of nested objects only", async () => {
|
|
281
|
+
const json = record(await new Note({
|
|
282
|
+
id: new URL("https://social.example/note/2"),
|
|
283
|
+
attachments: [new Link({
|
|
284
|
+
href: new URL(noteExample.attachment.href),
|
|
285
|
+
licenses: []
|
|
286
|
+
}), new Link({
|
|
287
|
+
href: new URL(noteExample.attachment.href),
|
|
288
|
+
licenses: [new URL(PD_MARK)]
|
|
289
|
+
})]
|
|
290
|
+
}).toJsonLd());
|
|
291
|
+
ok(!("license" in json));
|
|
292
|
+
const attachments = json.attachment;
|
|
293
|
+
ok(!("license" in attachments[0]));
|
|
294
|
+
deepStrictEqual(attachments[1].license, PD_MARK);
|
|
295
|
+
const links = (await expand(json))["https://www.w3.org/ns/activitystreams#attachment"];
|
|
296
|
+
deepStrictEqual(links[1][DC_LICENSE], [{ "@id": PD_MARK }]);
|
|
297
|
+
const person = record(await new Person({
|
|
298
|
+
id: new URL("https://social.example/user/1"),
|
|
299
|
+
icon: new Image({ licenses: [new URL(BY_NC)] })
|
|
300
|
+
}).toJsonLd());
|
|
301
|
+
ok(!("license" in person));
|
|
302
|
+
deepStrictEqual(record(person.icon).license, BY_NC);
|
|
303
|
+
const collection = await Collection.fromJsonLd({
|
|
304
|
+
"@context": [AS, FEP],
|
|
305
|
+
type: "Collection",
|
|
306
|
+
license: CC0,
|
|
307
|
+
items: [{
|
|
308
|
+
type: "Note",
|
|
309
|
+
content: "Unlicensed item"
|
|
310
|
+
}]
|
|
311
|
+
}, { documentLoader: noFetch });
|
|
312
|
+
deepStrictEqual(collection.licenses, [new URL(CC0)]);
|
|
313
|
+
const items = await Array.fromAsync(collection.getItems({ documentLoader: noFetch }));
|
|
314
|
+
ok(items[0] instanceof Note);
|
|
315
|
+
deepStrictEqual(items[0].licenses, []);
|
|
316
|
+
});
|
|
317
|
+
test("FEP-6757 modified clones write the canonical property", async () => {
|
|
318
|
+
const parsed = await Note.fromJsonLd({
|
|
319
|
+
"@context": [AS, { schema: "https://schema.org/" }],
|
|
320
|
+
id: "https://social.example/note/3",
|
|
321
|
+
type: "Note",
|
|
322
|
+
"schema:license": BY
|
|
323
|
+
}, { documentLoader: noFetch });
|
|
324
|
+
deepStrictEqual(parsed.licenses, [new URL(BY)]);
|
|
325
|
+
for (const clone of [parsed.clone({ content: "Edited" }), parsed.clone({ licenses: [new URL(CC0)] })]) {
|
|
326
|
+
const json = record(await clone.toJsonLd());
|
|
327
|
+
ok(!("schema:license" in json));
|
|
328
|
+
const expanded = await expand(json);
|
|
329
|
+
ok(!(SCHEMA_LICENSE in expanded));
|
|
330
|
+
deepStrictEqual(expanded[DC_LICENSE], [{ "@id": clone.licenses[0].href }]);
|
|
331
|
+
}
|
|
332
|
+
const compact = await expand(await parsed.toJsonLd({ format: "compact" }));
|
|
333
|
+
deepStrictEqual(compact[DC_LICENSE], [{ "@id": BY }]);
|
|
334
|
+
ok(!(SCHEMA_LICENSE in compact));
|
|
335
|
+
});
|
|
336
|
+
test("FEP-6757 caller-provided contexts", async () => {
|
|
337
|
+
for (const context of [
|
|
338
|
+
AS,
|
|
339
|
+
[AS, FEP],
|
|
340
|
+
[AS, "http://schema.org/"],
|
|
341
|
+
[
|
|
342
|
+
AS,
|
|
343
|
+
FEP,
|
|
344
|
+
"http://schema.org/"
|
|
345
|
+
],
|
|
346
|
+
[
|
|
347
|
+
AS,
|
|
348
|
+
"http://schema.org/",
|
|
349
|
+
FEP
|
|
350
|
+
]
|
|
351
|
+
]) {
|
|
352
|
+
const json = await note().toJsonLd({
|
|
353
|
+
format: "compact",
|
|
354
|
+
context
|
|
355
|
+
});
|
|
356
|
+
deepStrictEqual(record(json)["@context"], context);
|
|
357
|
+
deepStrictEqual((await expand(json))[DC_LICENSE], [{ "@id": BY }]);
|
|
358
|
+
deepStrictEqual((await Note.fromJsonLd(json, { documentLoader: noFetch })).licenses, [new URL(BY)]);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
test("FEP-6757 synonym precedence", async () => {
|
|
362
|
+
const id = "https://social.example/note/4";
|
|
363
|
+
const cases = [
|
|
364
|
+
[{
|
|
365
|
+
[DC_LICENSE]: [{ "@id": BY }],
|
|
366
|
+
[SCHEMA_LICENSE]: [{ "@id": CC0 }]
|
|
367
|
+
}, [BY]],
|
|
368
|
+
[{
|
|
369
|
+
[SCHEMA_LICENSE]: [{ "@id": CC0 }],
|
|
370
|
+
[CC_LICENSE]: [{ "@id": BY }]
|
|
371
|
+
}, [CC0]],
|
|
372
|
+
[{ [HTTP_SCHEMA_LICENSE]: [{ "@id": BY }] }, []],
|
|
373
|
+
[{ [CC_LICENSE]: [{ "@id": CC0 }, { "@value": BY }] }, [CC0, BY]],
|
|
374
|
+
[{
|
|
375
|
+
[DC_LICENSE]: [],
|
|
376
|
+
[SCHEMA_LICENSE]: [{ "@value": BY }]
|
|
377
|
+
}, [BY]],
|
|
378
|
+
[{
|
|
379
|
+
[DC_LICENSE]: [{ "@id": "CC-BY-4.0" }],
|
|
380
|
+
[SCHEMA_LICENSE]: [{ "@id": BY }]
|
|
381
|
+
}, []],
|
|
382
|
+
[{}, []]
|
|
383
|
+
];
|
|
384
|
+
for (const [properties, expected] of cases) deepStrictEqual((await Note.fromJsonLd({
|
|
385
|
+
"@id": id,
|
|
386
|
+
"@type": [`${AS}#Note`],
|
|
387
|
+
...properties
|
|
388
|
+
}, { documentLoader: noFetch })).licenses, expected.map((l) => new URL(l)));
|
|
389
|
+
});
|
|
390
|
+
test("FEP-6757 schema.org context order", async () => {
|
|
391
|
+
for (const [context, predicate, expected] of [[
|
|
392
|
+
[
|
|
393
|
+
AS,
|
|
394
|
+
FEP,
|
|
395
|
+
"http://schema.org/"
|
|
396
|
+
],
|
|
397
|
+
HTTP_SCHEMA_LICENSE,
|
|
398
|
+
[]
|
|
399
|
+
], [
|
|
400
|
+
[
|
|
401
|
+
AS,
|
|
402
|
+
"http://schema.org/",
|
|
403
|
+
FEP
|
|
404
|
+
],
|
|
405
|
+
DC_LICENSE,
|
|
406
|
+
[new URL(BY)]
|
|
407
|
+
]]) {
|
|
408
|
+
const json = {
|
|
409
|
+
"@context": context,
|
|
410
|
+
id: "https://social.example/note/5",
|
|
411
|
+
type: "Note",
|
|
412
|
+
license: BY
|
|
413
|
+
};
|
|
414
|
+
deepStrictEqual(Object.keys(await expand(json)).includes(predicate), true);
|
|
415
|
+
deepStrictEqual((await Note.fromJsonLd(json, {
|
|
416
|
+
documentLoader: noFetch,
|
|
417
|
+
contextLoader: offlineContextLoader()
|
|
418
|
+
})).licenses, expected);
|
|
419
|
+
}
|
|
420
|
+
for (const extra of [{}, { "@language": "und" }]) deepStrictEqual((await Note.fromJsonLd({
|
|
421
|
+
"@context": [AS, {
|
|
422
|
+
schema: "https://schema.org/",
|
|
423
|
+
...extra
|
|
424
|
+
}],
|
|
425
|
+
type: "Note",
|
|
426
|
+
"schema:license": BY
|
|
427
|
+
}, { documentLoader: noFetch })).licenses, [new URL(BY)]);
|
|
428
|
+
});
|
|
429
|
+
test("FEP-6757 PeerTube licences round-trip unchanged", async () => {
|
|
430
|
+
const video = {
|
|
431
|
+
"@context": [AS, {
|
|
432
|
+
sc: "http://schema.org/",
|
|
433
|
+
licence: "sc:license",
|
|
434
|
+
uuid: "sc:identifier"
|
|
435
|
+
}],
|
|
436
|
+
type: "Video",
|
|
437
|
+
id: "https://peertube.example/videos/watch/1",
|
|
438
|
+
name: "A video",
|
|
439
|
+
uuid: "e5a2d4b0-0000-4000-8000-000000000000",
|
|
440
|
+
licence: {
|
|
441
|
+
identifier: "1",
|
|
442
|
+
name: "Attribution"
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
const parsed = await Video.fromJsonLd(video, { documentLoader: noFetch });
|
|
446
|
+
deepStrictEqual(parsed.licenses, []);
|
|
447
|
+
deepStrictEqual(await parsed.toJsonLd(), video);
|
|
448
|
+
const create = {
|
|
449
|
+
"@context": video["@context"],
|
|
450
|
+
type: "Create",
|
|
451
|
+
id: "https://peertube.example/videos/watch/1/activity",
|
|
452
|
+
actor: "https://peertube.example/accounts/alice",
|
|
453
|
+
object: {
|
|
454
|
+
...video,
|
|
455
|
+
"@context": void 0
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
delete create.object["@context"];
|
|
459
|
+
deepStrictEqual(await (await Create.fromJsonLd(create, { documentLoader: noFetch })).toJsonLd(), create);
|
|
460
|
+
});
|
|
461
|
+
test("FEP-6757 rejects license names and relative identifiers", async () => {
|
|
462
|
+
const base = {
|
|
463
|
+
"@context": [AS, FEP],
|
|
464
|
+
id: "https://social.example/note/6",
|
|
465
|
+
type: "Note"
|
|
466
|
+
};
|
|
467
|
+
for (const license of [
|
|
468
|
+
"CC-BY-4.0",
|
|
469
|
+
"Creative Commons Attribution 4.0 International",
|
|
470
|
+
"licenses/by/4.0/",
|
|
471
|
+
"_:license"
|
|
472
|
+
]) {
|
|
473
|
+
const parsed = await Note.fromJsonLd({
|
|
474
|
+
...base,
|
|
475
|
+
license
|
|
476
|
+
}, { documentLoader: noFetch });
|
|
477
|
+
deepStrictEqual(parsed.licenses, []);
|
|
478
|
+
ok(!("license" in record(await parsed.toJsonLd())));
|
|
479
|
+
}
|
|
480
|
+
const mixed = await Note.fromJsonLd({
|
|
481
|
+
...base,
|
|
482
|
+
license: [
|
|
483
|
+
"CC-BY-4.0",
|
|
484
|
+
BY,
|
|
485
|
+
"urn:example:license"
|
|
486
|
+
]
|
|
487
|
+
}, { documentLoader: noFetch });
|
|
488
|
+
deepStrictEqual(mixed.licenses, [new URL(BY), new URL("urn:example:license")]);
|
|
489
|
+
deepStrictEqual(record(await mixed.toJsonLd()).license, [BY, "urn:example:license"]);
|
|
490
|
+
deepStrictEqual((await Note.fromJsonLd({
|
|
491
|
+
...base,
|
|
492
|
+
"@context": [
|
|
493
|
+
AS,
|
|
494
|
+
FEP,
|
|
495
|
+
{ "@base": "https://licenses.example/" }
|
|
496
|
+
],
|
|
497
|
+
license: "by/4.0/"
|
|
498
|
+
}, { documentLoader: noFetch })).licenses, [new URL("https://licenses.example/by/4.0/")]);
|
|
499
|
+
for (const value of [{
|
|
500
|
+
"@value": BY,
|
|
501
|
+
"@type": "http://www.w3.org/2001/XMLSchema#date"
|
|
502
|
+
}, {
|
|
503
|
+
"@value": "CC-BY-4.0",
|
|
504
|
+
"@language": "en"
|
|
505
|
+
}]) deepStrictEqual((await Note.fromJsonLd({
|
|
506
|
+
"@type": [`${AS}#Note`],
|
|
507
|
+
[DC_LICENSE]: [value]
|
|
508
|
+
}, { documentLoader: noFetch })).licenses, []);
|
|
509
|
+
deepStrictEqual((await Note.fromJsonLd({
|
|
510
|
+
"@type": [`${AS}#Note`],
|
|
511
|
+
[DC_LICENSE]: [{
|
|
512
|
+
"@value": BY,
|
|
513
|
+
"@type": "http://www.w3.org/2001/XMLSchema#anyURI"
|
|
514
|
+
}]
|
|
515
|
+
}, { documentLoader: noFetch })).licenses, [new URL(BY)]);
|
|
516
|
+
});
|
|
517
|
+
test("FEP-6757 license IRIs are never dereferenced", async () => {
|
|
518
|
+
const requested = [];
|
|
519
|
+
const documentLoader = (url) => {
|
|
520
|
+
requested.push(url);
|
|
521
|
+
throw new Error(`Unexpected fetch: ${url}`);
|
|
522
|
+
};
|
|
523
|
+
const contextLoader = offlineContextLoader();
|
|
524
|
+
const parsed = await Note.fromJsonLd(noteExample, {
|
|
525
|
+
documentLoader,
|
|
526
|
+
contextLoader
|
|
527
|
+
});
|
|
528
|
+
deepStrictEqual(parsed.licenses, [new URL(BY)]);
|
|
529
|
+
await parsed.clone({ content: "Edited" }).toJsonLd({ contextLoader });
|
|
530
|
+
deepStrictEqual((await Person.fromJsonLd(personExample, {
|
|
531
|
+
documentLoader,
|
|
532
|
+
contextLoader
|
|
533
|
+
})).preferredLicense, new URL(BY_NC));
|
|
534
|
+
deepStrictEqual(requested, []);
|
|
535
|
+
for (const url of contextLoader.loaded) ok(url in preloadedContexts);
|
|
536
|
+
});
|
|
537
|
+
//#endregion
|
|
538
|
+
export {};
|
package/dist-tests/link.yaml
CHANGED
|
@@ -112,3 +112,28 @@ properties:
|
|
|
112
112
|
range:
|
|
113
113
|
- "https://www.w3.org/ns/activitystreams#Link"
|
|
114
114
|
- "https://www.w3.org/ns/activitystreams#Object"
|
|
115
|
+
|
|
116
|
+
- pluralName: licenses
|
|
117
|
+
singularName: license
|
|
118
|
+
compactName: license
|
|
119
|
+
uri: "http://purl.org/dc/terms/license"
|
|
120
|
+
extraContext: "https://w3id.org/fep/6757"
|
|
121
|
+
redundantProperties:
|
|
122
|
+
- uri: "https://schema.org/license"
|
|
123
|
+
- uri: "http://creativecommons.org/ns#license"
|
|
124
|
+
redundantPropertiesWrite: canonical
|
|
125
|
+
description: |
|
|
126
|
+
The copyright licenses for the linked resource, as defined by
|
|
127
|
+
[FEP-6757](https://w3id.org/fep/6757). Each license is identified by its
|
|
128
|
+
URI, such as <https://creativecommons.org/publicdomain/mark/1.0/>; license
|
|
129
|
+
names and SPDX short identifiers are not license URIs. Multiple values are
|
|
130
|
+
alternatives that a consumer may choose from; their order has no meaning.
|
|
131
|
+
|
|
132
|
+
An empty list means the link carries no license metadata. Fedify does not
|
|
133
|
+
synthesize a default license for it.
|
|
134
|
+
|
|
135
|
+
The `https://schema.org/license` and
|
|
136
|
+
`http://creativecommons.org/ns#license` properties are read as synonyms
|
|
137
|
+
when this property is absent, but only this property is written.
|
|
138
|
+
range:
|
|
139
|
+
- "fedify:absoluteIri"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import { A as Object$1 } from "./vocab-
|
|
4
|
-
import { i as isActor, o as name, s as version } from "./actor-
|
|
3
|
+
import { A as Object$1 } from "./vocab-Cg2PG-xH.mjs";
|
|
4
|
+
import { i as isActor, o as name, s as version } from "./actor-BbIjMjNa.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";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import { A as Object$1, F as Person, k as Note, s as Collection } from "./vocab-
|
|
4
|
-
import { t as esm_default } from "./esm-
|
|
5
|
-
import { n as traverseCollection, t as lookupObject } from "./lookup-
|
|
3
|
+
import { A as Object$1, F as Person, k as Note, s as Collection } from "./vocab-Cg2PG-xH.mjs";
|
|
4
|
+
import { t as esm_default } from "./esm-rg6f9oLR.mjs";
|
|
5
|
+
import { n as traverseCollection, t as lookupObject } from "./lookup-DKLMyPMJ.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";
|