@takuhon/core 0.25.0 → 1.1.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 CHANGED
@@ -1,5 +1,5 @@
1
1
  var $schema = "https://json-schema.org/draft/2020-12/schema";
2
- var $id = "https://takuhon.example/schemas/0.7.0/takuhon.schema.json";
2
+ var $id = "https://takuhon.example/schemas/1.1.0/takuhon.schema.json";
3
3
  var title = "Takuhon Profile";
4
4
  var description = "Portable profile data format consumed by @takuhon/core. The canonical contract for profile content authored as takuhon.json.";
5
5
  var type = "object";
@@ -7,10 +7,6 @@ var additionalProperties = false;
7
7
  var required = [
8
8
  "schemaVersion",
9
9
  "profile",
10
- "links",
11
- "careers",
12
- "projects",
13
- "skills",
14
10
  "contact",
15
11
  "settings",
16
12
  "meta"
@@ -197,9 +193,10 @@ var $defs = {
197
193
  additionalProperties: {
198
194
  type: "string",
199
195
  minLength: 1,
200
- maxLength: 200
196
+ maxLength: 200,
197
+ pattern: "\\S"
201
198
  },
202
- description: "Map of BCP-47 locale tag to short title-like string (max 200 chars per value)."
199
+ description: "Map of BCP-47 locale tag to short title-like string (max 200 chars per value). Each value must contain at least one non-whitespace character."
203
200
  },
204
201
  LocalizedBody: {
205
202
  type: "object",
@@ -211,9 +208,10 @@ var $defs = {
211
208
  additionalProperties: {
212
209
  type: "string",
213
210
  minLength: 1,
214
- maxLength: 5000
211
+ maxLength: 5000,
212
+ pattern: "\\S"
215
213
  },
216
- description: "Map of BCP-47 locale tag to body-length string (max 5000 chars per value)."
214
+ description: "Map of BCP-47 locale tag to body-length string (max 5000 chars per value). Each value must contain at least one non-whitespace character."
217
215
  },
218
216
  LinkType: {
219
217
  type: "string",
@@ -238,7 +236,7 @@ var $defs = {
238
236
  },
239
237
  Profile: {
240
238
  type: "object",
241
- additionalProperties: true,
239
+ additionalProperties: false,
242
240
  required: [
243
241
  "displayName"
244
242
  ],
@@ -262,7 +260,7 @@ var $defs = {
262
260
  },
263
261
  Avatar: {
264
262
  type: "object",
265
- additionalProperties: true,
263
+ additionalProperties: false,
266
264
  required: [
267
265
  "url"
268
266
  ],
@@ -280,7 +278,7 @@ var $defs = {
280
278
  },
281
279
  Address: {
282
280
  type: "object",
283
- additionalProperties: true,
281
+ additionalProperties: false,
284
282
  properties: {
285
283
  country: {
286
284
  $ref: "#/$defs/Iso3166Alpha2"
@@ -359,7 +357,7 @@ var $defs = {
359
357
  },
360
358
  Career: {
361
359
  type: "object",
362
- additionalProperties: true,
360
+ additionalProperties: false,
363
361
  required: [
364
362
  "id",
365
363
  "organization",
@@ -412,7 +410,7 @@ var $defs = {
412
410
  },
413
411
  Project: {
414
412
  type: "object",
415
- additionalProperties: true,
413
+ additionalProperties: false,
416
414
  required: [
417
415
  "id",
418
416
  "title"
@@ -469,7 +467,7 @@ var $defs = {
469
467
  },
470
468
  Skill: {
471
469
  type: "object",
472
- additionalProperties: true,
470
+ additionalProperties: false,
473
471
  required: [
474
472
  "id",
475
473
  "label"
@@ -500,7 +498,7 @@ var $defs = {
500
498
  },
501
499
  Contact: {
502
500
  type: "object",
503
- additionalProperties: true,
501
+ additionalProperties: false,
504
502
  properties: {
505
503
  email: {
506
504
  $ref: "#/$defs/Email"
@@ -516,7 +514,7 @@ var $defs = {
516
514
  },
517
515
  Settings: {
518
516
  type: "object",
519
- additionalProperties: true,
517
+ additionalProperties: false,
520
518
  required: [
521
519
  "defaultLocale",
522
520
  "availableLocales"
@@ -568,6 +566,39 @@ var $defs = {
568
566
  },
569
567
  publicVisibility: {
570
568
  $ref: "#/$defs/PublicVisibility"
569
+ },
570
+ contact: {
571
+ $ref: "#/$defs/ContactSettings"
572
+ }
573
+ }
574
+ },
575
+ ContactSettings: {
576
+ type: "object",
577
+ additionalProperties: false,
578
+ description: "Opt-in contact form configuration (added in 1.1.0). Only public values live here; the Turnstile site key is safe to embed in the page. The Turnstile secret, the recipient address, and the From label are provisioned out of band as environment configuration and are never stored in takuhon.json. Adapters that support the form mount the widget and a POST endpoint when 'enabled' is true.",
579
+ properties: {
580
+ enabled: {
581
+ type: "boolean",
582
+ "default": false,
583
+ description: "Master switch. When false (the default), no contact form is mounted even if a site key is present."
584
+ },
585
+ turnstileSiteKey: {
586
+ type: "string",
587
+ minLength: 1,
588
+ maxLength: 128,
589
+ description: "Public Cloudflare Turnstile site key, safe to embed in the page. Without it the widget is not mounted."
590
+ },
591
+ endpoint: {
592
+ type: "string",
593
+ minLength: 1,
594
+ maxLength: 2048,
595
+ description: "Override the POST endpoint the widget submits to. Defaults to /api/contact on the same origin."
596
+ },
597
+ subjectPrefix: {
598
+ type: "string",
599
+ minLength: 1,
600
+ maxLength: 128,
601
+ description: "Subject prefix for the delivered email, e.g. '[example.com contact]'."
571
602
  }
572
603
  }
573
604
  },
@@ -709,7 +740,7 @@ var $defs = {
709
740
  },
710
741
  Meta: {
711
742
  type: "object",
712
- additionalProperties: true,
743
+ additionalProperties: false,
713
744
  required: [
714
745
  "contentLicense"
715
746
  ],
@@ -774,7 +805,7 @@ var $defs = {
774
805
  },
775
806
  MetaPrivacy: {
776
807
  type: "object",
777
- additionalProperties: true,
808
+ additionalProperties: false,
778
809
  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.",
779
810
  properties: {
780
811
  hideCredentialIds: {
@@ -791,7 +822,7 @@ var $defs = {
791
822
  },
792
823
  Certification: {
793
824
  type: "object",
794
- additionalProperties: true,
825
+ additionalProperties: false,
795
826
  required: [
796
827
  "id",
797
828
  "title",
@@ -842,7 +873,7 @@ var $defs = {
842
873
  },
843
874
  Membership: {
844
875
  type: "object",
845
- additionalProperties: true,
876
+ additionalProperties: false,
846
877
  required: [
847
878
  "id",
848
879
  "organization",
@@ -892,7 +923,7 @@ var $defs = {
892
923
  },
893
924
  Volunteering: {
894
925
  type: "object",
895
- additionalProperties: true,
926
+ additionalProperties: false,
896
927
  required: [
897
928
  "id",
898
929
  "organization",
@@ -945,7 +976,7 @@ var $defs = {
945
976
  },
946
977
  Honor: {
947
978
  type: "object",
948
- additionalProperties: true,
979
+ additionalProperties: false,
949
980
  required: [
950
981
  "id",
951
982
  "title",
@@ -982,7 +1013,7 @@ var $defs = {
982
1013
  },
983
1014
  Education: {
984
1015
  type: "object",
985
- additionalProperties: true,
1016
+ additionalProperties: false,
986
1017
  required: [
987
1018
  "id",
988
1019
  "institution",
@@ -1041,7 +1072,7 @@ var $defs = {
1041
1072
  },
1042
1073
  Publication: {
1043
1074
  type: "object",
1044
- additionalProperties: true,
1075
+ additionalProperties: false,
1045
1076
  required: [
1046
1077
  "id",
1047
1078
  "title",
@@ -1093,7 +1124,7 @@ var $defs = {
1093
1124
  },
1094
1125
  Language: {
1095
1126
  type: "object",
1096
- additionalProperties: true,
1127
+ additionalProperties: false,
1097
1128
  required: [
1098
1129
  "id",
1099
1130
  "language",
@@ -1131,7 +1162,7 @@ var $defs = {
1131
1162
  },
1132
1163
  Course: {
1133
1164
  type: "object",
1134
- additionalProperties: true,
1165
+ additionalProperties: false,
1135
1166
  required: [
1136
1167
  "id",
1137
1168
  "title"
@@ -1175,7 +1206,7 @@ var $defs = {
1175
1206
  },
1176
1207
  Patent: {
1177
1208
  type: "object",
1178
- additionalProperties: true,
1209
+ additionalProperties: false,
1179
1210
  required: [
1180
1211
  "id",
1181
1212
  "title",
@@ -1240,7 +1271,7 @@ var $defs = {
1240
1271
  },
1241
1272
  TestScore: {
1242
1273
  type: "object",
1243
- additionalProperties: true,
1274
+ additionalProperties: false,
1244
1275
  required: [
1245
1276
  "id",
1246
1277
  "title",
@@ -1284,7 +1315,7 @@ var $defs = {
1284
1315
  },
1285
1316
  Recommendation: {
1286
1317
  type: "object",
1287
- additionalProperties: true,
1318
+ additionalProperties: false,
1288
1319
  required: [
1289
1320
  "id",
1290
1321
  "body",
@@ -1326,7 +1357,7 @@ var $defs = {
1326
1357
  },
1327
1358
  RecommendationAuthor: {
1328
1359
  type: "object",
1329
- additionalProperties: true,
1360
+ additionalProperties: false,
1330
1361
  required: [
1331
1362
  "name"
1332
1363
  ],
@@ -1557,6 +1588,7 @@ declare const schema: {
1557
1588
  type: string;
1558
1589
  minLength: number;
1559
1590
  maxLength: number;
1591
+ pattern: string;
1560
1592
  };
1561
1593
  description: string;
1562
1594
  };
@@ -1571,6 +1603,7 @@ declare const schema: {
1571
1603
  type: string;
1572
1604
  minLength: number;
1573
1605
  maxLength: number;
1606
+ pattern: string;
1574
1607
  };
1575
1608
  description: string;
1576
1609
  };
@@ -1882,6 +1915,39 @@ declare const schema: {
1882
1915
  publicVisibility: {
1883
1916
  $ref: string;
1884
1917
  };
1918
+ contact: {
1919
+ $ref: string;
1920
+ };
1921
+ };
1922
+ };
1923
+ ContactSettings: {
1924
+ type: string;
1925
+ additionalProperties: boolean;
1926
+ description: string;
1927
+ properties: {
1928
+ enabled: {
1929
+ type: string;
1930
+ default: boolean;
1931
+ description: string;
1932
+ };
1933
+ turnstileSiteKey: {
1934
+ type: string;
1935
+ minLength: number;
1936
+ maxLength: number;
1937
+ description: string;
1938
+ };
1939
+ endpoint: {
1940
+ type: string;
1941
+ minLength: number;
1942
+ maxLength: number;
1943
+ description: string;
1944
+ };
1945
+ subjectPrefix: {
1946
+ type: string;
1947
+ minLength: number;
1948
+ maxLength: number;
1949
+ description: string;
1950
+ };
1885
1951
  };
1886
1952
  };
1887
1953
  PublicVisibility: {
@@ -2596,8 +2662,8 @@ type Schema = typeof schemaJson;
2596
2662
  *
2597
2663
  * These mirror the canonical contract defined in `takuhon.schema.json`. The
2598
2664
  * published shape is sanity-checked at commit 1 by `__tests__/schema.test.ts`
2599
- * (top-level keys, `$defs`, required fields, hybrid `additionalProperties`
2600
- * splits, Spec §6 invariants) and by `__tests__/example.test.ts` (the bundled
2665
+ * (top-level keys, `$defs`, required fields, the closed `additionalProperties`
2666
+ * contract, Spec §6 invariants) and by `__tests__/example.test.ts` (the bundled
2601
2667
  * fixture is assigned to `Takuhon` via a boundary cast, and per-Spec invariants
2602
2668
  * are asserted at runtime). Those tests catch the kind of drift that changes
2603
2669
  * the published shape, but they do not enforce field-by-field parity between
@@ -2607,27 +2673,31 @@ type Schema = typeof schemaJson;
2607
2673
  * When the schema changes, update these types accordingly and add a migration
2608
2674
  * entry under `src/migrations/` for the next minor version.
2609
2675
  *
2610
- * Public surface scope (hybrid `additionalProperties` strategy):
2676
+ * `additionalProperties` policy (schema 1.0.0 — precise contract):
2611
2677
  *
2612
- * - **Closed** in the schema (no forward-compatible extras): the document
2613
- * root, `ContentLicense`, and every `Link` variant.
2614
- * - **Open** in the schema (`additionalProperties: true`): `Profile`,
2615
- * `Settings`, `Meta`, `Career`, `Project`, `Skill`, `Contact`, `Avatar`,
2616
- * `Address`, and the locale-keyed map shapes `LocalizedTitle` /
2617
- * `LocalizedBody`.
2678
+ * Every object in the contract is **closed** (`additionalProperties: false`):
2679
+ * the document root, `ContentLicense`, every `Link` variant, `PublicVisibility`,
2680
+ * `ActivitySettings`, and reversing the pre-1.0 hybrid strategy — every
2681
+ * content entity (`Profile`, `Career`, `Project`, `Skill`, `Education`, …)
2682
+ * plus `Settings`, `Meta`, and `MetaPrivacy`. An undeclared property (a
2683
+ * misspelled `tittle`) is therefore a validation error rather than silently
2684
+ * dropped data. The only intentionally open shapes are the locale-keyed maps
2685
+ * `LocalizedTitle` / `LocalizedBody`, whose keys are arbitrary BCP-47 tags
2686
+ * constrained by `propertyNames` with the value schema bounding each entry.
2618
2687
  *
2619
- * The public TypeScript surface intentionally omits an `[key: string]: unknown`
2620
- * index signature on the open containers. Declared properties stay accurately
2621
- * typed regardless of the choice what such a signature would change is
2622
- * access to *undeclared* keys: it would let consumers spell arbitrary property
2623
- * names without an error and force `unknown` narrowing for those reads, and it
2624
- * would dilute IDE autocomplete on every dotted access. Keeping the types
2625
- * focused on the canonical members preserves that ergonomics. Consumers that
2626
- * need to attach custom fields should extend the relevant interface locally:
2688
+ * This is the canonical "precise contract" stance: the schema fully defines the
2689
+ * accepted shape, so the JSON-LD and MCP surfaces that AI agents and search
2690
+ * engines read are trustworthy. The closure is deliberate and, for the 1.x
2691
+ * line, treated as irreversible-by-default a future minor can re-open
2692
+ * extension space *non-breakingly* via `patternProperties: { "^x-": {} }` (the
2693
+ * OpenAPI `x-` convention) should adopter demand appear, without ever loosening
2694
+ * the contract retroactively.
2627
2695
  *
2628
- * interface MyProfile extends Profile {
2629
- * customField: string;
2630
- * }
2696
+ * The public TypeScript surface therefore omits any `[key: string]: unknown`
2697
+ * index signature: declared properties stay accurately typed and IDE
2698
+ * autocomplete is not diluted. Consumers that need to carry their own data
2699
+ * alongside a profile should keep it in a sibling structure rather than
2700
+ * smuggling undeclared keys into the contract — schema 1.0.0 now rejects them.
2631
2701
  */
2632
2702
  /** BCP-47 language tag, e.g. 'en', 'ja', 'zh-Hant', 'pt-BR'. */
2633
2703
  type LocaleTag = string;
@@ -2917,6 +2987,26 @@ interface Settings {
2917
2987
  activity?: ActivitySettings;
2918
2988
  /** Per-section public visibility (added in 0.6.0). Absent = all sections visible. */
2919
2989
  publicVisibility?: PublicVisibility;
2990
+ /** Opt-in contact form (added in 1.1.0). Absent = no contact form. */
2991
+ contact?: ContactSettings;
2992
+ }
2993
+ /**
2994
+ * Owner-curated configuration for the opt-in contact form (added in 1.1.0).
2995
+ * Only public values live here: the Turnstile **site key** is safe to embed in
2996
+ * the page. The Turnstile secret, the recipient address, and the From label are
2997
+ * provisioned out of band as environment configuration and are never stored in
2998
+ * `takuhon.json`. Adapters that support the form mount the widget and a POST
2999
+ * endpoint when {@link enabled} is true; adapters that do not simply ignore it.
3000
+ */
3001
+ interface ContactSettings {
3002
+ /** Master switch; no contact form is mounted when false (the default). */
3003
+ enabled?: boolean;
3004
+ /** Public Cloudflare Turnstile site key, safe to embed; required to mount the widget. */
3005
+ turnstileSiteKey?: string;
3006
+ /** Override the POST endpoint the widget submits to. Defaults to `/api/contact` (same origin). */
3007
+ endpoint?: string;
3008
+ /** Subject prefix for the delivered email, e.g. `"[example.com contact]"`. */
3009
+ subjectPrefix?: string;
2920
3010
  }
2921
3011
  /**
2922
3012
  * Per-section public/private visibility (added in 0.6.0).
@@ -3322,7 +3412,7 @@ interface LocalizedTakuhon {
3322
3412
  * versions whose JSON Schema this package literally bundles, not the full
3323
3413
  * support window seen by end users.
3324
3414
  */
3325
- declare const SUPPORTED_SCHEMA_VERSIONS: readonly ["0.1.0", "0.2.0", "0.3.0", "0.4.0", "0.5.0", "0.6.0", "0.7.0"];
3415
+ declare const SUPPORTED_SCHEMA_VERSIONS: readonly ["0.1.0", "0.2.0", "0.3.0", "0.4.0", "0.5.0", "0.6.0", "0.7.0", "1.0.0", "1.1.0"];
3326
3416
  /**
3327
3417
  * A single validation failure.
3328
3418
  *
@@ -4452,6 +4542,6 @@ declare function stripImageMetadata(bytes: Uint8Array, mime: AcceptedImageMime):
4452
4542
  * A takuhon profile document's `schemaVersion` field must be migrate-compatible
4453
4543
  * with this version. See operational-lifecycle docs for the migration policy.
4454
4544
  */
4455
- declare const SCHEMA_VERSION = "0.7.0";
4545
+ declare const SCHEMA_VERSION = "1.1.0";
4456
4546
 
4457
4547
  export { ACCEPTED_IMAGE_MIME_TYPES, type AcceptedImageMime, type ActivitySettings, type ActivitySnapshot, type ActivityStorage, type Address, type AssetOptions, type AssetRecord, type Avatar, type Career, type Certification, type CodingTime, ConflictError, type Contact, type ContentLicense, type ContentLicenseAttribution, type ContributionCalendar, type ContributionDay, type Course, type CvDocument, type CvHeader, type CvSection, type CvSectionKind, DARK_PALETTE, type Education, type ExportOptions, type ExportedTakuhon, type GravatarOptions, type Honor, IMAGE_EXTENSIONS, type ImageInfo, ImportError, type Iso3166Alpha2, type IsoDateTime, LIGHT_PALETTE, type Language, type LanguageBreakdown, type LanguageProficiency, type Link, type LinkBuiltin, type LinkCustom, type LinkType, type LocaleTag, type LocalizedAddress, type LocalizedAvatar, type LocalizedBody, type LocalizedCareer, type LocalizedCertification, type LocalizedCourse, type LocalizedEducation, type LocalizedHonor, type LocalizedLanguage, type LocalizedLink, type LocalizedLinkBuiltin, type LocalizedLinkCustom, type LocalizedMembership, type LocalizedPatent, type LocalizedProfile, type LocalizedProject, type LocalizedPublication, type LocalizedRecommendation, type LocalizedRecommendationAuthor, type LocalizedTakuhon, type LocalizedTestScore, type LocalizedTitle, type LocalizedVolunteering, MAX_IMAGE_BYTES, MAX_IMAGE_DIMENSION, MAX_IMAGE_FRAMES, MCP_PROFILE_SECTIONS, MCP_RESOURCES, MCP_TOOLS, type McpInputSchema, type McpProfileSection, McpRequestError, type McpResourceDefinition, type McpResourceResult, type McpToolDefinition, type McpToolResult, type Membership, type Meta, type MetaPrivacy, type Migration, MigrationError, type NormalizedTakuhon, NotFoundError, type Palette, type Patent, type PatentStatus, type Profile, type Project, type PublicVisibility, type Publication, RANK_FULL_CODING_HOURS, RANK_FULL_CONTRIBUTIONS, RANK_TIER_THRESHOLDS, type RankInput, type RankTier, type RankTierLabel, type Recommendation, type RecommendationAuthor, type RenderActivitySvgOptions, SCHEMA_VERSION, SUPPORTED_SCHEMA_VERSIONS, type Schema, type Settings, type Skill, type Slug, StorageError, type Takuhon, type TakuhonAssetStorage, type TakuhonStorage, type TestScore, type ValidationError, type ValidationResult, type Volunteering, type YearMonth, applyPublicPrivacyFilter, computeLanguagePercentages, deriveCv, deriveRankTier, detectImageMime, executeMcpTool, exportTakuhon, formatCodingTime, formatDate, generateJsonLd, generatePersonJsonLd, generateProfilePageJsonLd, getPresentLabel, gravatarUrl, importTakuhon, isActivitySnapshot, migrateTakuhon, migrations, normalize, readImageInfo, readMcpResource, renderActivitySvg, resolveLocale, schema, stripImageMetadata, validate };