@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/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 ${RUNTIME_IMPORTS}\n} from "@fedify/vocab-runtime";\n`;
253
- yield `import {\n ${INTERNAL_RUNTIME_IMPORTS}\n} from "@fedify/vocab-runtime/internal/jsonld-cache";\n`;
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
- const portableIriKeys = new Set(["@id", "id"]);
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 ? formatIri(v) : ";
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"] = formatIri(this.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": formatIri(v) } : ';
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"] = formatIri(this.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
- const id = parseJsonLdId(values["@id"], options.baseUrl);
410
- if (id != null && options.baseUrl == null) {
411
- options = { ...options, baseUrl: id };
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(parseIri(v["@id"], ${propertyBaseUrl}));
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
- if (cacheJsonLd != null && cacheJsonLd !== expanded) {
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(lookupUrl);
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: lookupUrl }
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 = parseIri(documentUrl);
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 && !isTrustedIriOrigin(options, obj.id, baseUrl)) {
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: lookupUrl }
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 && !isTrustedIriOrigin(options, v.id, this.id) &&
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 && !isTrustedIriOrigin(options, v.id, this.id) &&
428
- !this.${await getFieldName(property.uri, "#_trust")}.has(i)) {
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": formatIri(${v}) }`;
160
+ return `{ "@id": ${v}.href }`;
161
161
  },
162
162
  compactEncoder(v) {
163
- return `formatIri(${v})`;
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 `parseIri(${v}["@id"], ${baseUrlVar})`;
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": formatIri(${v}) }`;
328
+ return `{ "@value": ${v}.href }`;
314
329
  },
315
330
  compactEncoder(v) {
316
- return `formatIri(${v})`;
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 `parseIri(${v}["@value"])`;
339
+ return `new URL(${v}["@value"])`;
325
340
  },
326
341
  },
327
342
  "fedify:publicKey": {