@takuhon/core
v0.14.0
Published
JSON Schema, validation, normalization, locale resolution, and JSON-LD generation for takuhon
Maintainers
Readme
@takuhon/core
JSON Schema (takuhon.schema.json), Ajv-backed validation, normalization, locale resolution, JSON-LD generation, storage/asset interfaces, and migration registry for Takuhon.
Installation
pnpm add @takuhon/core
# or
npm install @takuhon/core@takuhon/core is an ESM-only package and targets Node.js 22+, modern browsers, and Cloudflare Workers. It has no peer dependencies.
Usage
Validate
validate() checks an unknown value against the bundled JSON Schema and returns a discriminated result. On success the value is narrowed to Takuhon; on failure every issue is reported with a JSON Pointer and the failing Ajv keyword.
import { validate } from '@takuhon/core';
import type { Takuhon } from '@takuhon/core';
const result = validate(rawJson);
if (result.ok) {
const profile: Takuhon = result.data;
// …
} else {
for (const issue of result.errors) {
console.error(`${issue.pointer} (${issue.keyword}): ${issue.message}`);
}
}The schema itself and the list of accepted schemaVersion values are also exported:
import { schema, SCHEMA_VERSION, SUPPORTED_SCHEMA_VERSIONS } from '@takuhon/core';Normalize
normalize() canonicalizes a validated document: it sorts the links / careers / projects / skills arrays by their order field (stable) and drops blank entries from every LocalizedTitle / LocalizedBody map. The input is deep-cloned, never mutated, and the function is idempotent.
import { normalize } from '@takuhon/core';
const canonical = normalize(profile);Resolve locale
resolveLocale() collapses every per-locale map to a single string using a BCP-47 fallback chain: the requested locale, its regional roots (e.g. pt-BR → pt), settings.fallbackLocale, then settings.defaultLocale. The returned document records which tag produced profile.displayName in resolvedLocale.
import { normalize, resolveLocale } from '@takuhon/core';
const localized = resolveLocale(normalize(profile), 'ja');
console.log(localized.profile.displayName); // single string
console.log(localized.resolvedLocale); // 'ja' (or its fallback)The locale argument is optional; when omitted, settings.defaultLocale is used.
JSON-LD (Schema.org)
generateJsonLd() returns an array of JSON-LD objects ready to embed in a <script type="application/ld+json"> tag. The default output is a single ProfilePage with the Person inlined as its mainEntity. generatePersonJsonLd() and generateProfilePageJsonLd() are exposed for callers that need only one half.
import { generateJsonLd } from '@takuhon/core';
const ld = generateJsonLd(localized);
// e.g. embed in HTML:
// <script type="application/ld+json">{JSON.stringify(ld)}</script>Optional keys are omitted (not set to null) when their source value is absent or empty, and field insertion order is fixed — so JSON.stringify(generateJsonLd(x)) is deterministic for any given input.
Storage interface (preview)
TakuhonStorage and TakuhonAssetStorage define the persistence contracts that adapters implement. @takuhon/core itself ships only the types and the StorageError / NotFoundError / ConflictError exception family — no storage backend is bundled. Concrete adapters live under adapters/ in this monorepo: @takuhon/cloudflare for Workers KV (with R2 planned for assets) and @takuhon/static for Node.js filesystem.
import { ConflictError } from '@takuhon/core';
import type { TakuhonStorage } from '@takuhon/core';
declare const storage: TakuhonStorage;
const { data, version } = await storage.getProfile();
try {
await storage.saveProfile(updated, version); // optimistic lock via If-Match
} catch (err) {
if (err instanceof ConflictError) {
// someone else wrote first; err.currentVersion has the latest token
}
}Runtime requirements
@takuhon/core exports a validate() function that compiles the bundled JSON Schema with Ajv 8 at module load. Ajv 8 generates its validators with new Function(...), so the host runtime must permit dynamic code evaluation.
- ✅ Cloudflare Workers default runtime, Node.js, modern browsers
- ⚠ Environments that block
new Function(strict Content Security Policy without'unsafe-eval', some edge runtimes in their strict modes) cannot run the runtime validator as-is. If this becomes a deployment constraint, build a standalone validator with Ajv'sstandaloneCodehelper and ship it alongside the schema.
The Phase 1 milestones (validate, normalize, resolve-locale, JSON-LD) target Workers as the primary runtime, so the project ships the runtime-compilation path by default. Pre-compiled output is future work.
