@takuhon/core 0.4.0 → 0.5.0

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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // takuhon.schema.json
2
2
  var takuhon_schema_default = {
3
3
  $schema: "https://json-schema.org/draft/2020-12/schema",
4
- $id: "https://takuhon.example/schemas/0.2.0/takuhon.schema.json",
4
+ $id: "https://takuhon.example/schemas/0.4.0/takuhon.schema.json",
5
5
  title: "Takuhon Profile",
6
6
  description: "Portable profile data format consumed by @takuhon/core. The canonical contract for profile content authored as takuhon.json.",
7
7
  type: "object",
@@ -89,6 +89,16 @@ var takuhon_schema_default = {
89
89
  maxItems: 50,
90
90
  items: { $ref: "#/$defs/Patent" }
91
91
  },
92
+ testScores: {
93
+ type: "array",
94
+ maxItems: 30,
95
+ items: { $ref: "#/$defs/TestScore" }
96
+ },
97
+ recommendations: {
98
+ type: "array",
99
+ maxItems: 50,
100
+ items: { $ref: "#/$defs/Recommendation" }
101
+ },
92
102
  contact: { $ref: "#/$defs/Contact" },
93
103
  settings: { $ref: "#/$defs/Settings" },
94
104
  meta: { $ref: "#/$defs/Meta" }
@@ -401,7 +411,7 @@ var takuhon_schema_default = {
401
411
  MetaPrivacy: {
402
412
  type: "object",
403
413
  additionalProperties: true,
404
- description: "Opt-out flags that strip personally identifying fields from public API output (GET /api/profile, /api/jsonld, /takuhon.json). Admin endpoints (PUT /api/admin/*, GET /api/export) ignore these flags. Privacy-by-default: omitting the object or individual flags is equivalent to true.",
414
+ description: "Opt-out flags that strip personally identifying fields from public API output (GET /api/profile, /api/jsonld, /takuhon.json). Admin endpoints (PUT /api/admin/*, GET /api/admin/export) ignore these flags. Privacy-by-default: omitting the object or individual flags is equivalent to true.",
405
415
  properties: {
406
416
  hideCredentialIds: {
407
417
  type: "boolean",
@@ -605,6 +615,74 @@ var takuhon_schema_default = {
605
615
  },
606
616
  order: { type: "integer", minimum: 0 }
607
617
  }
618
+ },
619
+ TestScore: {
620
+ type: "object",
621
+ additionalProperties: true,
622
+ required: ["id", "title", "score", "date"],
623
+ properties: {
624
+ id: { $ref: "#/$defs/Slug" },
625
+ title: { $ref: "#/$defs/LocalizedTitle" },
626
+ score: {
627
+ type: "string",
628
+ minLength: 1,
629
+ maxLength: 50,
630
+ description: "Free-form score string (e.g. '112 / 120', '330', 'N1 Pass', or a percentile). The validator does not interpret its contents."
631
+ },
632
+ date: { $ref: "#/$defs/YearMonth" },
633
+ relatedEducationId: {
634
+ $ref: "#/$defs/Slug",
635
+ description: "Optional reference to an education[].id (e.g. for a university course exam)."
636
+ },
637
+ description: { $ref: "#/$defs/LocalizedBody" },
638
+ url: { $ref: "#/$defs/Url" },
639
+ order: { type: "integer", minimum: 0 }
640
+ }
641
+ },
642
+ Recommendation: {
643
+ type: "object",
644
+ additionalProperties: true,
645
+ required: ["id", "body", "author"],
646
+ properties: {
647
+ id: { $ref: "#/$defs/Slug" },
648
+ body: { $ref: "#/$defs/LocalizedBody" },
649
+ author: { $ref: "#/$defs/RecommendationAuthor" },
650
+ relationship: {
651
+ $ref: "#/$defs/LocalizedTitle",
652
+ description: "How the recommender relates to the profile owner (e.g. 'managed directly', 'worked together')."
653
+ },
654
+ date: { $ref: "#/$defs/YearMonth" },
655
+ relatedCareerId: {
656
+ $ref: "#/$defs/Slug",
657
+ description: "Optional reference to a careers[].id (the position the recommendation pertains to)."
658
+ },
659
+ relatedEducationId: {
660
+ $ref: "#/$defs/Slug",
661
+ description: "Optional reference to an education[].id (e.g. a recommendation from a professor)."
662
+ },
663
+ order: { type: "integer", minimum: 0 }
664
+ }
665
+ },
666
+ RecommendationAuthor: {
667
+ type: "object",
668
+ additionalProperties: true,
669
+ required: ["name"],
670
+ properties: {
671
+ name: {
672
+ type: "string",
673
+ minLength: 1,
674
+ maxLength: 100,
675
+ description: "Recommender's name, in its original script. Owner-curated; takuhon does not verify it."
676
+ },
677
+ headline: {
678
+ $ref: "#/$defs/LocalizedTitle",
679
+ description: "Recommender's title / role / organization at the time of the recommendation."
680
+ },
681
+ url: {
682
+ $ref: "#/$defs/Url",
683
+ description: "Link to the recommender's profile, for external verification by the reader."
684
+ }
685
+ }
608
686
  }
609
687
  }
610
688
  };
@@ -615,7 +693,7 @@ var schema = takuhon_schema_default;
615
693
  // src/validate.ts
616
694
  import Ajv2020 from "ajv/dist/2020.js";
617
695
  import addFormats from "ajv-formats";
618
- var SUPPORTED_SCHEMA_VERSIONS = ["0.1.0", "0.2.0"];
696
+ var SUPPORTED_SCHEMA_VERSIONS = ["0.1.0", "0.2.0", "0.3.0", "0.4.0"];
619
697
  var ajv = new Ajv2020({
620
698
  allErrors: true,
621
699
  strict: true
@@ -682,7 +760,9 @@ var COERCED_ARRAY_KEYS = [
682
760
  "publications",
683
761
  "languages",
684
762
  "courses",
685
- "patents"
763
+ "patents",
764
+ "testScores",
765
+ "recommendations"
686
766
  ];
687
767
  function coerceMissingArrays(data) {
688
768
  const bag = data;
@@ -808,6 +888,17 @@ function normalize(data) {
808
888
  cleanOptionalLocalized(p, "description");
809
889
  }
810
890
  out.patents = stableSortByOrder(out.patents);
891
+ for (const t of out.testScores) {
892
+ cleanRequiredLocalized(t.title);
893
+ cleanOptionalLocalized(t, "description");
894
+ }
895
+ out.testScores = stableSortByOrder(out.testScores);
896
+ for (const r of out.recommendations) {
897
+ cleanRequiredLocalized(r.body);
898
+ cleanOptionalLocalized(r, "relationship");
899
+ cleanOptionalLocalized(r.author, "headline");
900
+ }
901
+ out.recommendations = stableSortByOrder(out.recommendations);
811
902
  return out;
812
903
  }
813
904
  function normalizeProfile(profile) {
@@ -859,7 +950,9 @@ var NORMALIZED_ARRAYS = [
859
950
  "publications",
860
951
  "languages",
861
952
  "courses",
862
- "patents"
953
+ "patents",
954
+ "testScores",
955
+ "recommendations"
863
956
  ];
864
957
 
865
958
  // src/locale-tag.ts
@@ -907,6 +1000,8 @@ function resolveLocale(data, locale, fallbackLocale) {
907
1000
  languages: data.languages.map((l) => resolveLanguage(l, candidates)),
908
1001
  courses: data.courses.map((c) => resolveCourse(c, candidates)),
909
1002
  patents: data.patents.map((p) => resolvePatent(p, candidates)),
1003
+ testScores: data.testScores.map((t) => resolveTestScore(t, candidates)),
1004
+ recommendations: data.recommendations.map((r) => resolveRecommendation(r, candidates)),
910
1005
  contact: data.contact,
911
1006
  settings: data.settings,
912
1007
  meta: data.meta,
@@ -1172,6 +1267,47 @@ function resolvePatent(patent, candidates) {
1172
1267
  if (patent.order !== void 0) out.order = patent.order;
1173
1268
  return out;
1174
1269
  }
1270
+ function resolveTestScore(testScore, candidates) {
1271
+ const out = {
1272
+ id: testScore.id,
1273
+ title: pickLocalized(testScore.title, candidates) ?? "",
1274
+ score: testScore.score,
1275
+ date: testScore.date
1276
+ };
1277
+ const description = pickLocalized(testScore.description, candidates);
1278
+ if (description !== void 0) out.description = description;
1279
+ if (testScore.relatedEducationId !== void 0) {
1280
+ out.relatedEducationId = testScore.relatedEducationId;
1281
+ }
1282
+ if (testScore.url !== void 0) out.url = testScore.url;
1283
+ if (testScore.order !== void 0) out.order = testScore.order;
1284
+ return out;
1285
+ }
1286
+ function resolveRecommendation(recommendation, candidates) {
1287
+ const out = {
1288
+ id: recommendation.id,
1289
+ body: pickLocalized(recommendation.body, candidates) ?? "",
1290
+ author: resolveRecommendationAuthor(recommendation.author, candidates)
1291
+ };
1292
+ const relationship = pickLocalized(recommendation.relationship, candidates);
1293
+ if (relationship !== void 0) out.relationship = relationship;
1294
+ if (recommendation.date !== void 0) out.date = recommendation.date;
1295
+ if (recommendation.relatedCareerId !== void 0) {
1296
+ out.relatedCareerId = recommendation.relatedCareerId;
1297
+ }
1298
+ if (recommendation.relatedEducationId !== void 0) {
1299
+ out.relatedEducationId = recommendation.relatedEducationId;
1300
+ }
1301
+ if (recommendation.order !== void 0) out.order = recommendation.order;
1302
+ return out;
1303
+ }
1304
+ function resolveRecommendationAuthor(author, candidates) {
1305
+ const out = { name: author.name };
1306
+ const headline = pickLocalized(author.headline, candidates);
1307
+ if (headline !== void 0) out.headline = headline;
1308
+ if (author.url !== void 0) out.url = author.url;
1309
+ return out;
1310
+ }
1175
1311
 
1176
1312
  // src/jsonld.ts
1177
1313
  var SAMEAS_IDENTITY_TYPES = /* @__PURE__ */ new Set([
@@ -1572,8 +1708,40 @@ var v0_1_0_to_v0_2_0 = {
1572
1708
  }
1573
1709
  };
1574
1710
 
1711
+ // src/migrations/v0.2.0-to-v0.3.0.ts
1712
+ var v0_2_0_to_v0_3_0 = {
1713
+ from: "0.2.0",
1714
+ to: "0.3.0",
1715
+ migrate(data) {
1716
+ const partial = data;
1717
+ return {
1718
+ ...data,
1719
+ schemaVersion: "0.3.0",
1720
+ testScores: partial.testScores ?? []
1721
+ };
1722
+ }
1723
+ };
1724
+
1725
+ // src/migrations/v0.3.0-to-v0.4.0.ts
1726
+ var v0_3_0_to_v0_4_0 = {
1727
+ from: "0.3.0",
1728
+ to: "0.4.0",
1729
+ migrate(data) {
1730
+ const partial = data;
1731
+ return {
1732
+ ...data,
1733
+ schemaVersion: "0.4.0",
1734
+ recommendations: partial.recommendations ?? []
1735
+ };
1736
+ }
1737
+ };
1738
+
1575
1739
  // src/migrations/index.ts
1576
- var migrations = [v0_1_0_to_v0_2_0];
1740
+ var migrations = [
1741
+ v0_1_0_to_v0_2_0,
1742
+ v0_2_0_to_v0_3_0,
1743
+ v0_3_0_to_v0_4_0
1744
+ ];
1577
1745
 
1578
1746
  // src/migrate.ts
1579
1747
  var MigrationError = class extends Error {
@@ -1621,7 +1789,7 @@ var ConflictError = class extends StorageError {
1621
1789
  };
1622
1790
 
1623
1791
  // src/index.ts
1624
- var SCHEMA_VERSION = "0.2.0";
1792
+ var SCHEMA_VERSION = "0.4.0";
1625
1793
  export {
1626
1794
  ConflictError,
1627
1795
  ImportError,