@fedify/vocab-tools 2.4.0-dev.1567 → 2.4.0-dev.1570
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 +72 -108
- package/dist/mod.js +72 -108
- package/package.json +1 -1
- package/src/__snapshots__/class.test.ts.deno.snap +27312 -33533
- package/src/__snapshots__/class.test.ts.node.snap +27312 -33533
- package/src/__snapshots__/class.test.ts.snap +27312 -33533
- package/src/class.ts +21 -79
- package/src/codec.ts +17 -39
- package/src/property.ts +15 -17
- package/src/type.ts +21 -6
package/src/class.ts
CHANGED
|
@@ -3,41 +3,9 @@ import { generateCloner, generateConstructor } from "./constructor.ts";
|
|
|
3
3
|
import { generateFields } from "./field.ts";
|
|
4
4
|
import { generateInspector, generateInspectorPostClass } from "./inspector.ts";
|
|
5
5
|
import { generateProperties } from "./property.ts";
|
|
6
|
-
import {
|
|
7
|
-
type PropertySchema,
|
|
8
|
-
type TypeSchema,
|
|
9
|
-
validateTypeSchemas,
|
|
10
|
-
} from "./schema.ts";
|
|
6
|
+
import { type TypeSchema, validateTypeSchemas } from "./schema.ts";
|
|
11
7
|
import { emitOverride } from "./type.ts";
|
|
12
8
|
|
|
13
|
-
const XSD_ANY_URI = "http://www.w3.org/2001/XMLSchema#anyURI";
|
|
14
|
-
const FEDIFY_URL = "fedify:url";
|
|
15
|
-
const INTERNAL_RUNTIME_IMPORTS = [
|
|
16
|
-
"compactJsonLdCache",
|
|
17
|
-
"getJsonLdContext",
|
|
18
|
-
"isTrustedIriOrigin",
|
|
19
|
-
"normalizeJsonLdIris",
|
|
20
|
-
].join(",\n ");
|
|
21
|
-
const RUNTIME_IMPORTS = [
|
|
22
|
-
"canParseDecimal",
|
|
23
|
-
"decodeMultibase",
|
|
24
|
-
"type Decimal",
|
|
25
|
-
"type DocumentLoader",
|
|
26
|
-
"encodeMultibase",
|
|
27
|
-
"exportMultibaseKey",
|
|
28
|
-
"exportSpki",
|
|
29
|
-
"formatIri",
|
|
30
|
-
"getDocumentLoader",
|
|
31
|
-
"importMultibaseKey",
|
|
32
|
-
"importPem",
|
|
33
|
-
"isDecimal",
|
|
34
|
-
"LanguageString",
|
|
35
|
-
"parseDecimal",
|
|
36
|
-
"parseIri",
|
|
37
|
-
"parseJsonLdId",
|
|
38
|
-
"type RemoteDocument",
|
|
39
|
-
].join(",\n ");
|
|
40
|
-
|
|
41
9
|
/**
|
|
42
10
|
* Sorts the given types topologically so that the base types come before the
|
|
43
11
|
* extended types.
|
|
@@ -201,40 +169,6 @@ export function getEntityTypeById(id: string | URL): $EntityType | undefined {
|
|
|
201
169
|
`;
|
|
202
170
|
}
|
|
203
171
|
|
|
204
|
-
function canContainIriValue(
|
|
205
|
-
property: PropertySchema,
|
|
206
|
-
types: Record<string, TypeSchema>,
|
|
207
|
-
): boolean {
|
|
208
|
-
return property.range.some((typeUri) =>
|
|
209
|
-
typeUri === XSD_ANY_URI || typeUri === FEDIFY_URL || types[typeUri]?.entity
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function addKeys(
|
|
214
|
-
keys: Set<string>,
|
|
215
|
-
property: { uri: string; compactName?: string },
|
|
216
|
-
): void {
|
|
217
|
-
keys.add(property.uri);
|
|
218
|
-
if (property.compactName != null) keys.add(property.compactName);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function addPortableIriKeys(
|
|
222
|
-
keys: Set<string>,
|
|
223
|
-
property: PropertySchema,
|
|
224
|
-
types: Record<string, TypeSchema>,
|
|
225
|
-
): void {
|
|
226
|
-
if (!canContainIriValue(property, types)) return;
|
|
227
|
-
addKeys(keys, property);
|
|
228
|
-
if (
|
|
229
|
-
"redundantProperties" in property &&
|
|
230
|
-
property.redundantProperties != null
|
|
231
|
-
) {
|
|
232
|
-
for (const redundantProperty of property.redundantProperties) {
|
|
233
|
-
addKeys(keys, redundantProperty);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
172
|
/**
|
|
239
173
|
* Generates the TypeScript classes from the given types.
|
|
240
174
|
* @param types The types to generate classes from.
|
|
@@ -244,27 +178,35 @@ export async function* generateClasses(
|
|
|
244
178
|
types: Record<string, TypeSchema>,
|
|
245
179
|
): AsyncIterable<string> {
|
|
246
180
|
validateTypeSchemas(types);
|
|
181
|
+
const runtimeImports = [
|
|
182
|
+
"canParseDecimal",
|
|
183
|
+
"decodeMultibase",
|
|
184
|
+
"type Decimal",
|
|
185
|
+
"type DocumentLoader",
|
|
186
|
+
"encodeMultibase",
|
|
187
|
+
"exportMultibaseKey",
|
|
188
|
+
"exportSpki",
|
|
189
|
+
"getDocumentLoader",
|
|
190
|
+
"importMultibaseKey",
|
|
191
|
+
"importPem",
|
|
192
|
+
"isDecimal",
|
|
193
|
+
"LanguageString",
|
|
194
|
+
"parseDecimal",
|
|
195
|
+
"type RemoteDocument",
|
|
196
|
+
];
|
|
247
197
|
yield "// deno-lint-ignore-file ban-unused-ignore no-explicit-any no-unused-vars prefer-const verbatim-module-syntax\n";
|
|
248
198
|
yield 'import jsonld from "@fedify/vocab-runtime/jsonld";\n';
|
|
249
199
|
yield 'import { getLogger } from "@logtape/logtape";\n';
|
|
250
200
|
yield `import { type Span, SpanStatusCode, type TracerProvider, trace }
|
|
251
201
|
from "@opentelemetry/api";\n`;
|
|
252
|
-
yield `import {\n ${
|
|
253
|
-
|
|
202
|
+
yield `import {\n ${
|
|
203
|
+
runtimeImports.join(",\n ")
|
|
204
|
+
}\n} from "@fedify/vocab-runtime";\n`;
|
|
254
205
|
yield `import {
|
|
255
206
|
isTemporalDuration,
|
|
256
207
|
isTemporalInstant,
|
|
257
208
|
} from "@fedify/vocab-runtime/temporal";\n`;
|
|
258
|
-
|
|
259
|
-
for (const type of Object.values(types)) {
|
|
260
|
-
for (const property of type.properties) {
|
|
261
|
-
addPortableIriKeys(portableIriKeys, property, types);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
yield "const PORTABLE_IRI_PATTERN = /^ap(?:\\+ef61)?:\\/\\//i;\n";
|
|
265
|
-
yield `const PORTABLE_IRI_KEYS: ReadonlySet<string> = new Set(${
|
|
266
|
-
JSON.stringify([...portableIriKeys].sort())
|
|
267
|
-
});\n\n`;
|
|
209
|
+
yield "\n\n";
|
|
268
210
|
const moduleVarNames = new Map<string, string>();
|
|
269
211
|
const sorted = sortTopologically(types);
|
|
270
212
|
for (const typeUri of sorted) {
|
package/src/codec.ts
CHANGED
|
@@ -127,7 +127,7 @@ export async function* generateEncoder(
|
|
|
127
127
|
const item = (
|
|
128
128
|
`;
|
|
129
129
|
if (!areAllScalarTypes(property.range, types)) {
|
|
130
|
-
yield "v instanceof URL ?
|
|
130
|
+
yield "v instanceof URL ? v.href : ";
|
|
131
131
|
}
|
|
132
132
|
const encoders = getEncoders(
|
|
133
133
|
property.range,
|
|
@@ -178,7 +178,7 @@ export async function* generateEncoder(
|
|
|
178
178
|
? ""
|
|
179
179
|
: `result["type"] = ${JSON.stringify(type.compactName ?? type.uri)};`
|
|
180
180
|
}
|
|
181
|
-
if (this.id != null) result["id"] =
|
|
181
|
+
if (this.id != null) result["id"] = this.id.href;
|
|
182
182
|
result["@context"] = ${JSON.stringify(type.defaultContext)};
|
|
183
183
|
return result;
|
|
184
184
|
}
|
|
@@ -210,7 +210,7 @@ export async function* generateEncoder(
|
|
|
210
210
|
const element = (
|
|
211
211
|
`;
|
|
212
212
|
if (!areAllScalarTypes(property.range, types)) {
|
|
213
|
-
yield 'v instanceof URL ? { "@id":
|
|
213
|
+
yield 'v instanceof URL ? { "@id": v.href } : ';
|
|
214
214
|
}
|
|
215
215
|
for (const code of getEncoders(property.range, types, "v", "options")) {
|
|
216
216
|
yield code;
|
|
@@ -250,7 +250,7 @@ export async function* generateEncoder(
|
|
|
250
250
|
}
|
|
251
251
|
yield `
|
|
252
252
|
${type.typeless ? "" : `values["@type"] = [${JSON.stringify(type.uri)}];`}
|
|
253
|
-
if (this.id != null) values["@id"] =
|
|
253
|
+
if (this.id != null) values["@id"] = this.id.href;
|
|
254
254
|
if (options.format === "expand") {
|
|
255
255
|
return await jsonld.expand(
|
|
256
256
|
values,
|
|
@@ -393,12 +393,10 @@ export async function* generateDecoder(
|
|
|
393
393
|
};
|
|
394
394
|
// deno-lint-ignore no-explicit-any
|
|
395
395
|
let values: Record<string, any[]> & { "@id"?: string };
|
|
396
|
-
let expanded: unknown[];
|
|
397
396
|
if (globalThis.Object.keys(json).length == 0) {
|
|
398
397
|
values = {};
|
|
399
|
-
expanded = [values];
|
|
400
398
|
} else {
|
|
401
|
-
expanded = await jsonld.expand(json, {
|
|
399
|
+
const expanded = await jsonld.expand(json, {
|
|
402
400
|
documentLoader: options.contextLoader,
|
|
403
401
|
keepFreeFloatingNodes: true,
|
|
404
402
|
});
|
|
@@ -406,9 +404,11 @@ export async function* generateDecoder(
|
|
|
406
404
|
// deno-lint-ignore no-explicit-any
|
|
407
405
|
(expanded[0] ?? {}) as (Record<string, any[]> & { "@id"?: string });
|
|
408
406
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
407
|
+
if (values["@id"] != null && !values["@id"].startsWith("_:") && !URL.canParse(values["@id"], options.baseUrl)) {
|
|
408
|
+
throw new TypeError("Invalid @id: " + values["@id"]);
|
|
409
|
+
}
|
|
410
|
+
if (options.baseUrl == null && values["@id"] != null && !values["@id"].startsWith("_:") && URL.canParse(values["@id"])) {
|
|
411
|
+
options = { ...options, baseUrl: new URL(values["@id"]) };
|
|
412
412
|
}
|
|
413
413
|
`;
|
|
414
414
|
const subtypes = getSubtypes(typeUri, types, true);
|
|
@@ -432,24 +432,10 @@ export async function* generateDecoder(
|
|
|
432
432
|
}
|
|
433
433
|
}
|
|
434
434
|
`;
|
|
435
|
-
yield `
|
|
436
|
-
let cacheJsonLd = !("_fromSubclass" in options) || !options._fromSubclass
|
|
437
|
-
? normalizeJsonLdIris(
|
|
438
|
-
expanded,
|
|
439
|
-
PORTABLE_IRI_KEYS,
|
|
440
|
-
PORTABLE_IRI_PATTERN,
|
|
441
|
-
)
|
|
442
|
-
: undefined;
|
|
443
|
-
if (cacheJsonLd != null && cacheJsonLd !== expanded) {
|
|
444
|
-
cacheJsonLd = structuredClone(cacheJsonLd);
|
|
445
|
-
}
|
|
446
|
-
`;
|
|
447
435
|
if (type.extends == null) {
|
|
448
436
|
yield `
|
|
449
437
|
const instance = new this(
|
|
450
|
-
{
|
|
451
|
-
id
|
|
452
|
-
},
|
|
438
|
+
{ id: values["@id"] != null && !values["@id"].startsWith("_:") && URL.canParse(values["@id"], options.baseUrl) ? new URL(values["@id"], options.baseUrl) : undefined },
|
|
453
439
|
options,
|
|
454
440
|
);
|
|
455
441
|
`;
|
|
@@ -505,7 +491,11 @@ export async function* generateDecoder(
|
|
|
505
491
|
if (typeof v === "object" && "@id" in v && !("@type" in v)
|
|
506
492
|
&& globalThis.Object.keys(v).length === 1) {
|
|
507
493
|
if (v["@id"].startsWith("_:")) continue;
|
|
508
|
-
${variable}.push(
|
|
494
|
+
${variable}.push(
|
|
495
|
+
!URL.canParse(v["@id"], ${propertyBaseUrl}) && v["@id"].startsWith("at://")
|
|
496
|
+
? new URL("at://" + encodeURIComponent(v["@id"].substring(5)))
|
|
497
|
+
: new URL(v["@id"], ${propertyBaseUrl})
|
|
498
|
+
);
|
|
509
499
|
continue;
|
|
510
500
|
}
|
|
511
501
|
`;
|
|
@@ -549,19 +539,7 @@ export async function* generateDecoder(
|
|
|
549
539
|
yield `
|
|
550
540
|
if (!("_fromSubclass" in options) || !options._fromSubclass) {
|
|
551
541
|
try {
|
|
552
|
-
|
|
553
|
-
const compactArray = Array.isArray(json) && json.length === 1;
|
|
554
|
-
const jsonLd = compactArray ? json[0] : json;
|
|
555
|
-
const normalized = cacheJsonLd;
|
|
556
|
-
const cachedJsonLd = await compactJsonLdCache(
|
|
557
|
-
normalized,
|
|
558
|
-
jsonLd,
|
|
559
|
-
options.contextLoader,
|
|
560
|
-
);
|
|
561
|
-
instance._cachedJsonLd = compactArray ? [cachedJsonLd] : cachedJsonLd;
|
|
562
|
-
} else {
|
|
563
|
-
instance._cachedJsonLd = structuredClone(json);
|
|
564
|
-
}
|
|
542
|
+
instance._cachedJsonLd = structuredClone(json);
|
|
565
543
|
} catch {
|
|
566
544
|
getLogger(["fedify", "vocab"]).warn(
|
|
567
545
|
"Failed to cache JSON-LD: {json}",
|
package/src/property.ts
CHANGED
|
@@ -86,10 +86,9 @@ async function* generateProperty(
|
|
|
86
86
|
${JSON.stringify(metadata.version)},
|
|
87
87
|
);
|
|
88
88
|
return await tracer.startActiveSpan("activitypub.lookup_object", async (span) => {
|
|
89
|
-
const lookupUrl = formatIri(url);
|
|
90
89
|
let fetchResult: RemoteDocument;
|
|
91
90
|
try {
|
|
92
|
-
fetchResult = await documentLoader(
|
|
91
|
+
fetchResult = await documentLoader(url.href);
|
|
93
92
|
} catch (error) {
|
|
94
93
|
span.setStatus({
|
|
95
94
|
code: SpanStatusCode.ERROR,
|
|
@@ -99,20 +98,21 @@ async function* generateProperty(
|
|
|
99
98
|
if (options.suppressError) {
|
|
100
99
|
getLogger(["fedify", "vocab"]).error(
|
|
101
100
|
"Failed to fetch {url}: {error}",
|
|
102
|
-
{ error, url:
|
|
101
|
+
{ error, url: url.href }
|
|
103
102
|
);
|
|
104
103
|
return null;
|
|
105
104
|
}
|
|
106
105
|
throw error;
|
|
107
106
|
}
|
|
108
107
|
const { document, documentUrl } = fetchResult;
|
|
109
|
-
const baseUrl =
|
|
108
|
+
const baseUrl = new URL(documentUrl);
|
|
110
109
|
try {
|
|
111
110
|
const obj = await this.#${property.singularName}_fromJsonLd(
|
|
112
111
|
document,
|
|
113
112
|
{ documentLoader, contextLoader, tracerProvider, baseUrl }
|
|
114
113
|
);
|
|
115
|
-
if (obj?.id != null &&
|
|
114
|
+
if (options.crossOrigin !== "trust" && obj?.id != null &&
|
|
115
|
+
obj.id.origin !== baseUrl.origin) {
|
|
116
116
|
if (options.crossOrigin === "throw") {
|
|
117
117
|
throw new Error(
|
|
118
118
|
"The object's @id (" + obj.id.href + ") has a different origin " +
|
|
@@ -141,7 +141,7 @@ async function* generateProperty(
|
|
|
141
141
|
if (options.suppressError) {
|
|
142
142
|
getLogger(["fedify", "vocab"]).error(
|
|
143
143
|
"Failed to parse {url}: {error}",
|
|
144
|
-
{ error: e, url:
|
|
144
|
+
{ error: e, url: url.href }
|
|
145
145
|
);
|
|
146
146
|
return null;
|
|
147
147
|
}
|
|
@@ -275,9 +275,8 @@ async function* generateProperty(
|
|
|
275
275
|
}
|
|
276
276
|
if (this.${await getFieldName(property.uri)}.length < 1) return null;
|
|
277
277
|
let v = this.${await getFieldName(property.uri)}[0];
|
|
278
|
-
if (!(v instanceof URL) &&
|
|
279
|
-
v.id != null &&
|
|
280
|
-
!isTrustedIriOrigin(options, v.id, this.id) &&
|
|
278
|
+
if (options.crossOrigin !== "trust" && !(v instanceof URL) &&
|
|
279
|
+
v.id != null && v.id.origin !== this.id?.origin &&
|
|
281
280
|
!this.${await getFieldName(property.uri, "#_trust")}.has(0)) {
|
|
282
281
|
v = v.id;
|
|
283
282
|
}
|
|
@@ -316,8 +315,8 @@ async function* generateProperty(
|
|
|
316
315
|
`;
|
|
317
316
|
}
|
|
318
317
|
yield `
|
|
319
|
-
if (v?.id != null &&
|
|
320
|
-
this.id != null &&
|
|
318
|
+
if (options.crossOrigin !== "trust" && v?.id != null &&
|
|
319
|
+
this.id != null && v.id.origin !== this.id.origin &&
|
|
321
320
|
!this.${await getFieldName(property.uri, "#_trust")}.has(0)) {
|
|
322
321
|
if (options.crossOrigin === "throw") {
|
|
323
322
|
throw new Error(
|
|
@@ -381,9 +380,8 @@ async function* generateProperty(
|
|
|
381
380
|
const vs = this.${await getFieldName(property.uri)};
|
|
382
381
|
for (let i = 0; i < vs.length; i++) {
|
|
383
382
|
let v = vs[i];
|
|
384
|
-
if (!(v instanceof URL) &&
|
|
385
|
-
v.id != null &&
|
|
386
|
-
!isTrustedIriOrigin(options, v.id, this.id) &&
|
|
383
|
+
if (options.crossOrigin !== "trust" && !(v instanceof URL) &&
|
|
384
|
+
v.id != null && v.id.origin !== this.id?.origin &&
|
|
387
385
|
!this.${await getFieldName(property.uri, "#_trust")}.has(i)) {
|
|
388
386
|
v = v.id;
|
|
389
387
|
}
|
|
@@ -423,9 +421,9 @@ async function* generateProperty(
|
|
|
423
421
|
`;
|
|
424
422
|
}
|
|
425
423
|
yield `
|
|
426
|
-
if (v?.id != null &&
|
|
427
|
-
this.id != null &&
|
|
428
|
-
!this.${await getFieldName(property.uri, "#_trust")}.has(
|
|
424
|
+
if (options.crossOrigin !== "trust" && v?.id != null &&
|
|
425
|
+
this.id != null && v.id.origin !== this.id.origin &&
|
|
426
|
+
!this.${await getFieldName(property.uri, "#_trust")}.has(0)) {
|
|
429
427
|
if (options.crossOrigin === "throw") {
|
|
430
428
|
throw new Error(
|
|
431
429
|
"The property object's @id (" + v.id.href + ") has a different " +
|
package/src/type.ts
CHANGED
|
@@ -157,10 +157,10 @@ const scalarTypes: Record<string, ScalarType> = {
|
|
|
157
157
|
return `${v} instanceof URL`;
|
|
158
158
|
},
|
|
159
159
|
encoder(v) {
|
|
160
|
-
return `{ "@id":
|
|
160
|
+
return `{ "@id": ${v}.href }`;
|
|
161
161
|
},
|
|
162
162
|
compactEncoder(v) {
|
|
163
|
-
return
|
|
163
|
+
return `${v}.href`;
|
|
164
164
|
},
|
|
165
165
|
dataCheck(v) {
|
|
166
166
|
return `${v} != null && typeof ${v} === "object" && "@id" in ${v}
|
|
@@ -168,7 +168,22 @@ const scalarTypes: Record<string, ScalarType> = {
|
|
|
168
168
|
&& ${v}["@id"] !== ""`;
|
|
169
169
|
},
|
|
170
170
|
decoder(v, baseUrlVar) {
|
|
171
|
-
return
|
|
171
|
+
return `${v}["@id"].startsWith("at://")
|
|
172
|
+
? new URL("at://" +
|
|
173
|
+
encodeURIComponent(
|
|
174
|
+
${v}["@id"].includes("/", 5)
|
|
175
|
+
? ${v}["@id"].slice(5, ${v}["@id"].indexOf("/", 5))
|
|
176
|
+
: ${v}["@id"].slice(5)
|
|
177
|
+
) +
|
|
178
|
+
(
|
|
179
|
+
${v}["@id"].includes("/", 5)
|
|
180
|
+
? ${v}["@id"].slice(${v}["@id"].indexOf("/", 5))
|
|
181
|
+
: ""
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
: URL.canParse(${v}["@id"]) && ${baseUrlVar}
|
|
185
|
+
? new URL(${v}["@id"])
|
|
186
|
+
: new URL(${v}["@id"], ${baseUrlVar})`;
|
|
172
187
|
},
|
|
173
188
|
},
|
|
174
189
|
"http://www.w3.org/1999/02/22-rdf-syntax-ns#langString": {
|
|
@@ -310,10 +325,10 @@ const scalarTypes: Record<string, ScalarType> = {
|
|
|
310
325
|
return `${v} instanceof URL`;
|
|
311
326
|
},
|
|
312
327
|
encoder(v) {
|
|
313
|
-
return `{ "@value":
|
|
328
|
+
return `{ "@value": ${v}.href }`;
|
|
314
329
|
},
|
|
315
330
|
compactEncoder(v) {
|
|
316
|
-
return
|
|
331
|
+
return `${v}.href`;
|
|
317
332
|
},
|
|
318
333
|
dataCheck(v) {
|
|
319
334
|
return `typeof ${v} === "object" && "@value" in ${v}
|
|
@@ -321,7 +336,7 @@ const scalarTypes: Record<string, ScalarType> = {
|
|
|
321
336
|
&& ${v}["@value"] !== "" && ${v}["@value"] !== "/"`;
|
|
322
337
|
},
|
|
323
338
|
decoder(v) {
|
|
324
|
-
return `
|
|
339
|
+
return `new URL(${v}["@value"])`;
|
|
325
340
|
},
|
|
326
341
|
},
|
|
327
342
|
"fedify:publicKey": {
|