@takuhon/core 0.4.0 → 0.6.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.d.ts +312 -17
- package/dist/index.js +175 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/takuhon.schema.json +80 -2
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.
|
|
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 = [
|
|
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.
|
|
1792
|
+
var SCHEMA_VERSION = "0.4.0";
|
|
1625
1793
|
export {
|
|
1626
1794
|
ConflictError,
|
|
1627
1795
|
ImportError,
|