@fedify/vocab-tools 2.2.0-dev.731 → 2.2.0-dev.766

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab-tools",
3
- "version": "2.2.0-dev.731+3d1ca85e",
3
+ "version": "2.2.0-dev.766+e04e54aa",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts"
package/dist/mod.cjs CHANGED
@@ -8,7 +8,7 @@ let yaml = require("yaml");
8
8
  let es_toolkit = require("es-toolkit");
9
9
  //#region deno.json
10
10
  var name = "@fedify/vocab-tools";
11
- var version = "2.2.0-dev.731+3d1ca85e";
11
+ var version = "2.2.0-dev.766+e04e54aa";
12
12
  //#endregion
13
13
  //#region src/type.ts
14
14
  const HEURISTICS_CONTEXTS = [
@@ -162,7 +162,7 @@ const scalarTypes = {
162
162
  return `${v}.href`;
163
163
  },
164
164
  dataCheck(v) {
165
- return `typeof ${v} === "object" && "@id" in ${v}
165
+ return `${v} != null && typeof ${v} === "object" && "@id" in ${v}
166
166
  && typeof ${v}["@id"] === "string"
167
167
  && ${v}["@id"] !== ""`;
168
168
  },
@@ -401,7 +401,7 @@ const scalarTypes = {
401
401
  return v;
402
402
  },
403
403
  dataCheck(v) {
404
- return `typeof ${v} === "object" && "@id" in ${v}
404
+ return `${v} != null && typeof ${v} === "object" && "@id" in ${v}
405
405
  && typeof ${v}["@id"] === "string"
406
406
  && ${v}["@id"].startsWith("https://w3id.org/security#")
407
407
  && [
@@ -431,6 +431,36 @@ const scalarTypes = {
431
431
  decoder(v) {
432
432
  return `${v}["@value"]`;
433
433
  }
434
+ },
435
+ "fedify:vocabEntityType": {
436
+ name: "$EntityType",
437
+ typeGuard(v) {
438
+ return `isEntityType(${v})`;
439
+ },
440
+ encoder(v) {
441
+ return `{ "@id": ${v}.typeId.href }`;
442
+ },
443
+ dataCheck(v) {
444
+ return `${v} != null && typeof ${v} === "object" && "@id" in ${v}
445
+ && typeof ${v}["@id"] === "string"
446
+ && ${v}["@id"] !== ""`;
447
+ },
448
+ decoder(v) {
449
+ return `(() => {
450
+ if (${v} == null || typeof ${v} !== "object" || !("@id" in ${v}) ||
451
+ typeof ${v}["@id"] !== "string" || ${v}["@id"] === "") {
452
+ return undefined;
453
+ }
454
+ const entityType = getEntityTypeById(${v}["@id"]);
455
+ if (entityType == null) {
456
+ getLogger(["fedify", "vocab"]).warn(
457
+ "Ignoring unknown vocabulary entity type reference: {typeId}",
458
+ { typeId: ${v}["@id"] },
459
+ );
460
+ }
461
+ return entityType;
462
+ })()`;
463
+ }
434
464
  }
435
465
  };
436
466
  function getTypeName(typeUri, types) {
@@ -592,6 +622,7 @@ function hasSingularAccessor(property) {
592
622
  }
593
623
  const XSD_STRING_URI = "http://www.w3.org/2001/XMLSchema#string";
594
624
  const XSD_DECIMAL_URI = "http://www.w3.org/2001/XMLSchema#decimal";
625
+ const FEDIFY_VOCAB_ENTITY_TYPE_URI = "fedify:vocabEntityType";
595
626
  /**
596
627
  * Validates schema combinations that cannot be represented safely by the
597
628
  * generated code.
@@ -608,6 +639,7 @@ function validateTypeSchemas(types) {
608
639
  const hasString = property.range.includes(XSD_STRING_URI);
609
640
  const hasDecimal = property.range.includes(XSD_DECIMAL_URI);
610
641
  if (hasString && hasDecimal) throw new TypeError(`The property ${type.name}.${property.singularName} cannot have both xsd:string and xsd:decimal in its range because the generated encoder cannot disambiguate them at runtime.`);
642
+ if (property.range.includes(FEDIFY_VOCAB_ENTITY_TYPE_URI) && property.range.length > 1) throw new TypeError(`The property ${type.name}.${property.singularName} cannot mix fedify:vocabEntityType with other range types because the generated decoder cannot disambiguate entity type references from ordinary IRIs.`);
611
643
  }
612
644
  }
613
645
  /**
@@ -1037,7 +1069,10 @@ async function* generateDecoder(typeUri, types) {
1037
1069
  continue;
1038
1070
  }
1039
1071
  `;
1040
- if (property.range.length == 1) yield `${variable}.push(${getDecoder(property.range[0], types, "v", "options", `(values["@id"] == null ? options.baseUrl : new URL(values["@id"]))`)})`;
1072
+ if (property.range.length == 1) yield `
1073
+ const decoded = ${getDecoder(property.range[0], types, "v", "options", `(values["@id"] == null ? options.baseUrl : new URL(values["@id"]))`)};
1074
+ if (typeof decoded === "undefined") continue;
1075
+ ${variable}.push(decoded);`;
1041
1076
  else {
1042
1077
  yield `
1043
1078
  const decoded =
@@ -1871,6 +1906,47 @@ async function* generateClass(typeUri, types) {
1871
1906
  yield "}\n\n";
1872
1907
  for await (const code of generateInspectorPostClass(typeUri, types)) yield code;
1873
1908
  }
1909
+ function* generateEntityTypeHelpers(sortedTypeUris, types) {
1910
+ const entityTypes = sortedTypeUris.filter((typeUri) => types[typeUri].entity).map((typeUri) => ({
1911
+ name: types[typeUri].name,
1912
+ uri: typeUri
1913
+ }));
1914
+ const entityTypeNames = entityTypes.map((entityType) => entityType.name);
1915
+ yield `/**
1916
+ * Constructor types for all generated vocabulary entity classes.
1917
+ */
1918
+ export type $EntityType =${entityTypeNames.length < 1 ? " never" : `\n | typeof ${entityTypeNames.join("\n | typeof ")}`};
1919
+
1920
+ const entityTypes: readonly $EntityType[] = [
1921
+ `;
1922
+ for (const entityTypeName of entityTypeNames) yield ` ${entityTypeName},\n`;
1923
+ yield `];
1924
+
1925
+ const entityTypeSet: ReadonlySet<$EntityType> = new Set(entityTypes);
1926
+
1927
+ const entityTypeIds: ReadonlyMap<string, $EntityType> = new Map<string, $EntityType>(
1928
+ [
1929
+ `;
1930
+ for (const entityType of entityTypes) yield ` [${JSON.stringify(entityType.uri)}, ${entityType.name}],\n`;
1931
+ yield ` ],
1932
+ );
1933
+
1934
+ /**
1935
+ * Checks whether the given value is a generated vocabulary entity class.
1936
+ */
1937
+ export function isEntityType(value: unknown): value is $EntityType {
1938
+ return entityTypeSet.has(value as $EntityType);
1939
+ }
1940
+
1941
+ /**
1942
+ * Gets the generated vocabulary entity class for the given type URI.
1943
+ */
1944
+ export function getEntityTypeById(id: string | URL): $EntityType | undefined {
1945
+ return entityTypeIds.get(typeof id === "string" ? id : id?.href);
1946
+ }
1947
+
1948
+ `;
1949
+ }
1874
1950
  /**
1875
1951
  * Generates the TypeScript classes from the given types.
1876
1952
  * @param types The types to generate classes from.
@@ -1903,6 +1979,7 @@ async function* generateClasses(types) {
1903
1979
  yield "\n\n";
1904
1980
  const sorted = sortTopologically(types);
1905
1981
  for (const typeUri of sorted) for await (const code of generateClass(typeUri, types)) yield code;
1982
+ for (const code of generateEntityTypeHelpers(sorted, types)) yield code;
1906
1983
  }
1907
1984
  //#endregion
1908
1985
  //#region src/generate.ts
package/dist/mod.js CHANGED
@@ -7,7 +7,7 @@ import { parse } from "yaml";
7
7
  import { pascalCase } from "es-toolkit";
8
8
  //#region deno.json
9
9
  var name = "@fedify/vocab-tools";
10
- var version = "2.2.0-dev.731+3d1ca85e";
10
+ var version = "2.2.0-dev.766+e04e54aa";
11
11
  //#endregion
12
12
  //#region src/type.ts
13
13
  const HEURISTICS_CONTEXTS = [
@@ -161,7 +161,7 @@ const scalarTypes = {
161
161
  return `${v}.href`;
162
162
  },
163
163
  dataCheck(v) {
164
- return `typeof ${v} === "object" && "@id" in ${v}
164
+ return `${v} != null && typeof ${v} === "object" && "@id" in ${v}
165
165
  && typeof ${v}["@id"] === "string"
166
166
  && ${v}["@id"] !== ""`;
167
167
  },
@@ -400,7 +400,7 @@ const scalarTypes = {
400
400
  return v;
401
401
  },
402
402
  dataCheck(v) {
403
- return `typeof ${v} === "object" && "@id" in ${v}
403
+ return `${v} != null && typeof ${v} === "object" && "@id" in ${v}
404
404
  && typeof ${v}["@id"] === "string"
405
405
  && ${v}["@id"].startsWith("https://w3id.org/security#")
406
406
  && [
@@ -430,6 +430,36 @@ const scalarTypes = {
430
430
  decoder(v) {
431
431
  return `${v}["@value"]`;
432
432
  }
433
+ },
434
+ "fedify:vocabEntityType": {
435
+ name: "$EntityType",
436
+ typeGuard(v) {
437
+ return `isEntityType(${v})`;
438
+ },
439
+ encoder(v) {
440
+ return `{ "@id": ${v}.typeId.href }`;
441
+ },
442
+ dataCheck(v) {
443
+ return `${v} != null && typeof ${v} === "object" && "@id" in ${v}
444
+ && typeof ${v}["@id"] === "string"
445
+ && ${v}["@id"] !== ""`;
446
+ },
447
+ decoder(v) {
448
+ return `(() => {
449
+ if (${v} == null || typeof ${v} !== "object" || !("@id" in ${v}) ||
450
+ typeof ${v}["@id"] !== "string" || ${v}["@id"] === "") {
451
+ return undefined;
452
+ }
453
+ const entityType = getEntityTypeById(${v}["@id"]);
454
+ if (entityType == null) {
455
+ getLogger(["fedify", "vocab"]).warn(
456
+ "Ignoring unknown vocabulary entity type reference: {typeId}",
457
+ { typeId: ${v}["@id"] },
458
+ );
459
+ }
460
+ return entityType;
461
+ })()`;
462
+ }
433
463
  }
434
464
  };
435
465
  function getTypeName(typeUri, types) {
@@ -591,6 +621,7 @@ function hasSingularAccessor(property) {
591
621
  }
592
622
  const XSD_STRING_URI = "http://www.w3.org/2001/XMLSchema#string";
593
623
  const XSD_DECIMAL_URI = "http://www.w3.org/2001/XMLSchema#decimal";
624
+ const FEDIFY_VOCAB_ENTITY_TYPE_URI = "fedify:vocabEntityType";
594
625
  /**
595
626
  * Validates schema combinations that cannot be represented safely by the
596
627
  * generated code.
@@ -607,6 +638,7 @@ function validateTypeSchemas(types) {
607
638
  const hasString = property.range.includes(XSD_STRING_URI);
608
639
  const hasDecimal = property.range.includes(XSD_DECIMAL_URI);
609
640
  if (hasString && hasDecimal) throw new TypeError(`The property ${type.name}.${property.singularName} cannot have both xsd:string and xsd:decimal in its range because the generated encoder cannot disambiguate them at runtime.`);
641
+ if (property.range.includes(FEDIFY_VOCAB_ENTITY_TYPE_URI) && property.range.length > 1) throw new TypeError(`The property ${type.name}.${property.singularName} cannot mix fedify:vocabEntityType with other range types because the generated decoder cannot disambiguate entity type references from ordinary IRIs.`);
610
642
  }
611
643
  }
612
644
  /**
@@ -1036,7 +1068,10 @@ async function* generateDecoder(typeUri, types) {
1036
1068
  continue;
1037
1069
  }
1038
1070
  `;
1039
- if (property.range.length == 1) yield `${variable}.push(${getDecoder(property.range[0], types, "v", "options", `(values["@id"] == null ? options.baseUrl : new URL(values["@id"]))`)})`;
1071
+ if (property.range.length == 1) yield `
1072
+ const decoded = ${getDecoder(property.range[0], types, "v", "options", `(values["@id"] == null ? options.baseUrl : new URL(values["@id"]))`)};
1073
+ if (typeof decoded === "undefined") continue;
1074
+ ${variable}.push(decoded);`;
1040
1075
  else {
1041
1076
  yield `
1042
1077
  const decoded =
@@ -1870,6 +1905,47 @@ async function* generateClass(typeUri, types) {
1870
1905
  yield "}\n\n";
1871
1906
  for await (const code of generateInspectorPostClass(typeUri, types)) yield code;
1872
1907
  }
1908
+ function* generateEntityTypeHelpers(sortedTypeUris, types) {
1909
+ const entityTypes = sortedTypeUris.filter((typeUri) => types[typeUri].entity).map((typeUri) => ({
1910
+ name: types[typeUri].name,
1911
+ uri: typeUri
1912
+ }));
1913
+ const entityTypeNames = entityTypes.map((entityType) => entityType.name);
1914
+ yield `/**
1915
+ * Constructor types for all generated vocabulary entity classes.
1916
+ */
1917
+ export type $EntityType =${entityTypeNames.length < 1 ? " never" : `\n | typeof ${entityTypeNames.join("\n | typeof ")}`};
1918
+
1919
+ const entityTypes: readonly $EntityType[] = [
1920
+ `;
1921
+ for (const entityTypeName of entityTypeNames) yield ` ${entityTypeName},\n`;
1922
+ yield `];
1923
+
1924
+ const entityTypeSet: ReadonlySet<$EntityType> = new Set(entityTypes);
1925
+
1926
+ const entityTypeIds: ReadonlyMap<string, $EntityType> = new Map<string, $EntityType>(
1927
+ [
1928
+ `;
1929
+ for (const entityType of entityTypes) yield ` [${JSON.stringify(entityType.uri)}, ${entityType.name}],\n`;
1930
+ yield ` ],
1931
+ );
1932
+
1933
+ /**
1934
+ * Checks whether the given value is a generated vocabulary entity class.
1935
+ */
1936
+ export function isEntityType(value: unknown): value is $EntityType {
1937
+ return entityTypeSet.has(value as $EntityType);
1938
+ }
1939
+
1940
+ /**
1941
+ * Gets the generated vocabulary entity class for the given type URI.
1942
+ */
1943
+ export function getEntityTypeById(id: string | URL): $EntityType | undefined {
1944
+ return entityTypeIds.get(typeof id === "string" ? id : id?.href);
1945
+ }
1946
+
1947
+ `;
1948
+ }
1873
1949
  /**
1874
1950
  * Generates the TypeScript classes from the given types.
1875
1951
  * @param types The types to generate classes from.
@@ -1902,6 +1978,7 @@ async function* generateClasses(types) {
1902
1978
  yield "\n\n";
1903
1979
  const sorted = sortTopologically(types);
1904
1980
  for (const typeUri of sorted) for await (const code of generateClass(typeUri, types)) yield code;
1981
+ for (const code of generateEntityTypeHelpers(sorted, types)) yield code;
1905
1982
  }
1906
1983
  //#endregion
1907
1984
  //#region src/generate.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab-tools",
3
- "version": "2.2.0-dev.731+3d1ca85e",
3
+ "version": "2.2.0-dev.766+e04e54aa",
4
4
  "description": "Code generator for Activity Vocabulary APIs",
5
5
  "homepage": "https://fedify.dev/",
6
6
  "repository": {