@fedify/vocab-tools 2.4.0-dev.1962 → 2.4.0-dev.1988
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/README.md +18 -0
- package/deno.json +1 -1
- package/dist/mod.cjs +56 -12
- package/dist/mod.d.cts +14 -0
- package/dist/mod.d.ts +14 -0
- package/dist/mod.js +56 -12
- package/dist/schema.yaml +15 -0
- package/package.json +1 -1
- package/src/__snapshots__/class.test.ts.deno.snap +3440 -318
- package/src/__snapshots__/class.test.ts.node.snap +3440 -318
- package/src/__snapshots__/class.test.ts.snap +3440 -318
- package/src/class.ts +38 -0
- package/src/codec.ts +28 -3
- package/src/property.ts +29 -8
- package/src/schema.ts +16 -0
- package/src/schema.yaml +15 -0
package/src/class.ts
CHANGED
|
@@ -283,6 +283,44 @@ function isValidLanguageTag(language: string): boolean {
|
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
`;
|
|
286
|
+
// Contexts are activated by expanded property IRIs left after compaction.
|
|
287
|
+
const extraContexts = Object.fromEntries(
|
|
288
|
+
Object.values(types).flatMap((type) =>
|
|
289
|
+
type.properties.flatMap((property) =>
|
|
290
|
+
property.extraContext == null
|
|
291
|
+
? []
|
|
292
|
+
: [[property.uri, property.extraContext]]
|
|
293
|
+
)
|
|
294
|
+
),
|
|
295
|
+
);
|
|
296
|
+
if (Object.keys(extraContexts).length > 0) {
|
|
297
|
+
yield `
|
|
298
|
+
const extraPropertyContexts: Readonly<Record<string, string>> =
|
|
299
|
+
${JSON.stringify(extraContexts)};
|
|
300
|
+
|
|
301
|
+
function getExtraContexts(document: unknown): string[] {
|
|
302
|
+
const contexts = new Set<string>();
|
|
303
|
+
const pending: unknown[] = [document];
|
|
304
|
+
while (pending.length > 0) {
|
|
305
|
+
const value = pending.pop();
|
|
306
|
+
if (value == null || typeof value !== "object") continue;
|
|
307
|
+
if (Array.isArray(value)) {
|
|
308
|
+
for (const item of value) pending.push(item);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if ("@value" in value) continue;
|
|
312
|
+
for (const [key, child] of globalThis.Object.entries(value)) {
|
|
313
|
+
if (key === "@context") continue;
|
|
314
|
+
if (globalThis.Object.hasOwn(extraPropertyContexts, key)) {
|
|
315
|
+
contexts.add(extraPropertyContexts[key]);
|
|
316
|
+
}
|
|
317
|
+
pending.push(child);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return [...contexts];
|
|
321
|
+
}
|
|
322
|
+
`;
|
|
323
|
+
}
|
|
286
324
|
yield "\n\n";
|
|
287
325
|
const portableIriKeys = new Set(["@id", "id"]);
|
|
288
326
|
for (const type of Object.values(types)) {
|
package/src/codec.ts
CHANGED
|
@@ -265,13 +265,35 @@ export async function* generateEncoder(
|
|
|
265
265
|
},
|
|
266
266
|
);
|
|
267
267
|
}
|
|
268
|
-
|
|
268
|
+
let docContext: Parameters<typeof jsonld.compact>[1] = options.context ??
|
|
269
269
|
${JSON.stringify(type.defaultContext)};
|
|
270
|
-
|
|
270
|
+
let compacted = await jsonld.compact(
|
|
271
271
|
values,
|
|
272
272
|
docContext,
|
|
273
273
|
{ documentLoader: options.contextLoader },
|
|
274
274
|
);
|
|
275
|
+
`;
|
|
276
|
+
if (
|
|
277
|
+
Object.values(types).some((t) =>
|
|
278
|
+
t.properties.some((p) => p.extraContext != null)
|
|
279
|
+
)
|
|
280
|
+
) {
|
|
281
|
+
yield `
|
|
282
|
+
if (options.context == null) {
|
|
283
|
+
const currentContexts = Array.isArray(docContext) ? docContext : [docContext];
|
|
284
|
+
const additionalContexts = getExtraContexts(compacted).filter(
|
|
285
|
+
context => !currentContexts.includes(context)
|
|
286
|
+
);
|
|
287
|
+
if (additionalContexts.length > 0) {
|
|
288
|
+
docContext = [...currentContexts, ...additionalContexts];
|
|
289
|
+
compacted = await jsonld.compact(values, docContext, {
|
|
290
|
+
documentLoader: options.contextLoader,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
`;
|
|
295
|
+
}
|
|
296
|
+
yield `
|
|
275
297
|
if (docContext != null) {
|
|
276
298
|
// Embed context
|
|
277
299
|
`;
|
|
@@ -309,7 +331,10 @@ export async function* generateEncoder(
|
|
|
309
331
|
protected ${emitOverride(typeUri, types)} isCompactable(): boolean {
|
|
310
332
|
`;
|
|
311
333
|
for (const property of type.properties) {
|
|
312
|
-
if (
|
|
334
|
+
if (
|
|
335
|
+
property.extraContext != null ||
|
|
336
|
+
!property.range.every((r) => isCompactableType(r, types))
|
|
337
|
+
) {
|
|
313
338
|
yield `
|
|
314
339
|
if (
|
|
315
340
|
this.${await getFieldName(property.uri)} != null &&
|
package/src/property.ts
CHANGED
|
@@ -29,6 +29,7 @@ async function* generateProperty(
|
|
|
29
29
|
moduleVarNames: ReadonlyMap<string, string>,
|
|
30
30
|
): AsyncIterable<string> {
|
|
31
31
|
const override = emitOverride(type.uri, types, property);
|
|
32
|
+
const trustOwner = type.trustEmbeddedObjects === false ? "null" : "this.id";
|
|
32
33
|
const doc = `\n/** ${property.description.replaceAll("\n", "\n * ")}\n */\n`;
|
|
33
34
|
const cachedPropertyBaseUrl = `(options as { baseUrl?: URL }).baseUrl ??
|
|
34
35
|
this._baseUrl ??
|
|
@@ -277,7 +278,7 @@ async function* generateProperty(
|
|
|
277
278
|
let v = this.${await getFieldName(property.uri)}[0];
|
|
278
279
|
if (!(v instanceof URL) &&
|
|
279
280
|
v.id != null &&
|
|
280
|
-
!isTrustedIriOrigin(options, v.id,
|
|
281
|
+
!isTrustedIriOrigin(options, v.id, ${trustOwner}) &&
|
|
281
282
|
!this.${await getFieldName(property.uri, "#_trust")}.has(0)) {
|
|
282
283
|
v = v.id;
|
|
283
284
|
}
|
|
@@ -317,12 +318,18 @@ async function* generateProperty(
|
|
|
317
318
|
}
|
|
318
319
|
yield `
|
|
319
320
|
if (v?.id != null &&
|
|
320
|
-
|
|
321
|
+
${
|
|
322
|
+
type.trustEmbeddedObjects === false ? "" : "this.id != null && "
|
|
323
|
+
}!isTrustedIriOrigin(options, v.id, ${trustOwner}) &&
|
|
321
324
|
!this.${await getFieldName(property.uri, "#_trust")}.has(0)) {
|
|
322
325
|
if (options.crossOrigin === "throw") {
|
|
323
326
|
throw new Error(
|
|
324
327
|
"The property object's @id (" + v.id.href + ") has a different " +
|
|
325
|
-
"origin than the property owner's @id (" +
|
|
328
|
+
"origin than the property owner's @id (" + ${
|
|
329
|
+
type.trustEmbeddedObjects === false
|
|
330
|
+
? '"untrusted metadata"'
|
|
331
|
+
: "this.id.href"
|
|
332
|
+
} + "); " +
|
|
326
333
|
"refusing to return the object. If you want to bypass this " +
|
|
327
334
|
"check and are aware of the security implications, set the " +
|
|
328
335
|
'crossOrigin option to "trust".'
|
|
@@ -334,7 +341,11 @@ async function* generateProperty(
|
|
|
334
341
|
"return the object. If you want to bypass this check and are " +
|
|
335
342
|
"aware of the security implications, set the crossOrigin option " +
|
|
336
343
|
'to "trust".',
|
|
337
|
-
{ objectId: v.id.href, parentObjectId:
|
|
344
|
+
{ objectId: v.id.href, parentObjectId: ${
|
|
345
|
+
type.trustEmbeddedObjects === false
|
|
346
|
+
? '"untrusted metadata"'
|
|
347
|
+
: "this.id.href"
|
|
348
|
+
} },
|
|
338
349
|
);
|
|
339
350
|
return null;
|
|
340
351
|
}
|
|
@@ -383,7 +394,7 @@ async function* generateProperty(
|
|
|
383
394
|
let v = vs[i];
|
|
384
395
|
if (!(v instanceof URL) &&
|
|
385
396
|
v.id != null &&
|
|
386
|
-
!isTrustedIriOrigin(options, v.id,
|
|
397
|
+
!isTrustedIriOrigin(options, v.id, ${trustOwner}) &&
|
|
387
398
|
!this.${await getFieldName(property.uri, "#_trust")}.has(i)) {
|
|
388
399
|
v = v.id;
|
|
389
400
|
}
|
|
@@ -424,12 +435,18 @@ async function* generateProperty(
|
|
|
424
435
|
}
|
|
425
436
|
yield `
|
|
426
437
|
if (v?.id != null &&
|
|
427
|
-
|
|
438
|
+
${
|
|
439
|
+
type.trustEmbeddedObjects === false ? "" : "this.id != null && "
|
|
440
|
+
}!isTrustedIriOrigin(options, v.id, ${trustOwner}) &&
|
|
428
441
|
!this.${await getFieldName(property.uri, "#_trust")}.has(i)) {
|
|
429
442
|
if (options.crossOrigin === "throw") {
|
|
430
443
|
throw new Error(
|
|
431
444
|
"The property object's @id (" + v.id.href + ") has a different " +
|
|
432
|
-
"origin than the property owner's @id (" +
|
|
445
|
+
"origin than the property owner's @id (" + ${
|
|
446
|
+
type.trustEmbeddedObjects === false
|
|
447
|
+
? '"untrusted metadata"'
|
|
448
|
+
: "this.id.href"
|
|
449
|
+
} + "); " +
|
|
433
450
|
"refusing to return the object. If you want to bypass this " +
|
|
434
451
|
"check and are aware of the security implications, set the " +
|
|
435
452
|
'crossOrigin option to "trust".'
|
|
@@ -441,7 +458,11 @@ async function* generateProperty(
|
|
|
441
458
|
"return the object. If you want to bypass this check and are " +
|
|
442
459
|
"aware of the security implications, set the crossOrigin " +
|
|
443
460
|
'option to "trust".',
|
|
444
|
-
{ objectId: v.id.href, parentObjectId:
|
|
461
|
+
{ objectId: v.id.href, parentObjectId: ${
|
|
462
|
+
type.trustEmbeddedObjects === false
|
|
463
|
+
? '"untrusted metadata"'
|
|
464
|
+
: "this.id.href"
|
|
465
|
+
} },
|
|
445
466
|
);
|
|
446
467
|
continue;
|
|
447
468
|
}
|
package/src/schema.ts
CHANGED
|
@@ -47,6 +47,14 @@ export interface TypeSchema {
|
|
|
47
47
|
*/
|
|
48
48
|
entity: boolean;
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Whether this type's id can establish same-origin trust for its own
|
|
52
|
+
* embedded entity-valued properties. Defaults to true. Set to false for
|
|
53
|
+
* metadata whose id does not identify an independently fetched resource.
|
|
54
|
+
* Locally constructed and explicitly trusted values remain trusted.
|
|
55
|
+
*/
|
|
56
|
+
trustEmbeddedObjects?: boolean;
|
|
57
|
+
|
|
50
58
|
/**
|
|
51
59
|
* Whether the type omits `@type` in JSON-LD serialization. When `true`,
|
|
52
60
|
* the generated `toJsonLd()` method will not emit `@type` (or `type` in
|
|
@@ -77,6 +85,14 @@ export interface TypeSchema {
|
|
|
77
85
|
}
|
|
78
86
|
|
|
79
87
|
export interface PropertySchemaBase {
|
|
88
|
+
/**
|
|
89
|
+
* A context URL added to the default serialization context when this
|
|
90
|
+
* property's expanded IRI appears in the compacted document. Explicit
|
|
91
|
+
* caller contexts are unchanged. Populated properties with this option
|
|
92
|
+
* use the JSON-LD processor instead of compact serialization heuristics.
|
|
93
|
+
*/
|
|
94
|
+
extraContext?: string;
|
|
95
|
+
|
|
80
96
|
/**
|
|
81
97
|
* The singular form of the property name. It is used as the name of the
|
|
82
98
|
* generated property accessors.
|
package/src/schema.yaml
CHANGED
|
@@ -72,6 +72,14 @@ $defs:
|
|
|
72
72
|
property-base:
|
|
73
73
|
type: object
|
|
74
74
|
properties:
|
|
75
|
+
extraContext:
|
|
76
|
+
description: >-
|
|
77
|
+
A context URL added to default serialization when this property's
|
|
78
|
+
expanded IRI appears in the compacted document. Explicit caller
|
|
79
|
+
contexts are unchanged. Populated properties use the JSON-LD
|
|
80
|
+
processor instead of compact serialization heuristics.
|
|
81
|
+
type: string
|
|
82
|
+
format: uri
|
|
75
83
|
singularName:
|
|
76
84
|
description: >-
|
|
77
85
|
The singular form of the property name. It is used as the name of the
|
|
@@ -250,6 +258,13 @@ properties:
|
|
|
250
258
|
|
|
251
259
|
The extended subtypes must have the consistent value of this flag.
|
|
252
260
|
type: boolean
|
|
261
|
+
trustEmbeddedObjects:
|
|
262
|
+
description: >-
|
|
263
|
+
Whether this type's id establishes same-origin trust for its own
|
|
264
|
+
embedded entity-valued properties. Defaults to true. Set to false
|
|
265
|
+
for metadata ids that do not identify independently fetched resources.
|
|
266
|
+
Locally constructed and explicitly trusted values remain trusted.
|
|
267
|
+
type: boolean
|
|
253
268
|
typeless:
|
|
254
269
|
description: >-
|
|
255
270
|
Whether the type omits @type in JSON-LD serialization. When true,
|