@takuhon/api 0.16.0 → 0.17.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/README.md CHANGED
@@ -4,7 +4,8 @@ Hono-based HTTP route handlers, RFC 7807 Problem Details error envelope, and res
4
4
 
5
5
  Ships:
6
6
 
7
- - `createPublicApp({ storage })` — public profile read endpoints, JSON-LD output, `/.well-known/takuhon.json`
7
+ - `createPublicApp({ storage })` — the server-rendered profile page at `GET /` (and `/<locale>/`) with Schema.org JSON-LD embedded in the HTML, plus the public read endpoints (`/api/profile`, `/api/jsonld`, `/api/schema`, `/takuhon.json`) and `/.well-known/takuhon.json`
8
+ - `renderProfileHtml` / `generateSite` / `renderCvHtml` — pure, `@takuhon/core`-only static HTML rendering (no DOM, no bundler) shared by the `GET /` route and `@takuhon/cli`'s `build` / `dev`
8
9
  - `createAdminApiApp({ storage, bearerToken, originAllowlist, cachePurger, auditLogger })` — `PUT`/`DELETE /api/admin/profile` with optimistic locking (`If-Match`), audit logging, and edge-cache purging
9
10
  - `createAdminUiApp()` — minimal HTML admin editor (token + JSON textarea + Save/Delete)
10
11
  - `CachePurger` and `AuditLogger` dependency-injection interfaces (with `noopCachePurger` / `noopAuditLogger` no-op implementations)
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Context, Hono } from 'hono';
2
2
  import { ContentfulStatusCode } from 'hono/utils/http-status';
3
- import { TakuhonStorage, Takuhon, ActivityStorage, TakuhonAssetStorage } from '@takuhon/core';
3
+ import { TakuhonStorage, Takuhon, ActivityStorage, TakuhonAssetStorage, LocalizedTakuhon, ActivitySnapshot, NormalizedTakuhon, CvDocument } from '@takuhon/core';
4
4
  export { applyPublicPrivacyFilter } from '@takuhon/core';
5
5
 
6
6
  /**
@@ -235,4 +235,139 @@ declare function createAdminUiApp(): Hono;
235
235
  */
236
236
  declare function adminAssetSecurityHeaders(): Record<string, string>;
237
237
 
238
- export { type AdminApiAppDeps, type AuditEvent, type AuditEventType, type AuditLogger, type BuildProblemInput, type CachePurger, ERROR_SLUGS, type ErrorSlug, LOCALE_AWARE_REMAINDERS, type ProblemDetails, type ProblemFieldError, type ProblemResponseInput, type PublicAppDeps, adminAssetSecurityHeaders, buildProblem, createAdminApiApp, createAdminUiApp, createPublicApp, localePrefixGetPath, noopAuditLogger, noopCachePurger, pathLocaleFromUrl, problemResponse, stripLocalePrefix };
238
+ /**
239
+ * Pure HTML helpers shared by the static-site renderer ({@link
240
+ * import('./build-html.js')}) and the CV renderer ({@link
241
+ * import('./cv-html.js')}). All are string-in / string-out with no I/O, so they
242
+ * stay unit-testable and keep both renderers' escaping behavior identical.
243
+ */
244
+ /** Escape text for use in HTML element content or double/single-quoted attributes. */
245
+ declare function escapeHtml(value: string): string;
246
+
247
+ /**
248
+ * Pure HTML rendering for `takuhon build`.
249
+ *
250
+ * {@link renderProfileHtml} turns one locale-resolved {@link LocalizedTakuhon}
251
+ * into a complete, self-contained static HTML document: semantic markup for
252
+ * every profile section, an inline stylesheet, optional Schema.org JSON-LD,
253
+ * and the `<head>` metadata (`<title>`, description, canonical, hreflang
254
+ * alternates) the caller supplies.
255
+ *
256
+ * This is a deliberately separate, simpler surface from the React
257
+ * `@takuhon/ui` (which is delivered as CSS-Modules components needing a
258
+ * bundler). It reuses only `@takuhon/core` and has no DOM/browser dependency,
259
+ * so it renders in plain Node and is unit-testable as a pure string function.
260
+ *
261
+ * Security: every piece of profile-derived text is escaped before it reaches
262
+ * the markup ({@link escapeHtml}), and the JSON-LD payload is `<`/`>`/`&`
263
+ * unicode-escaped so it cannot break out of its `<script>` element.
264
+ */
265
+
266
+ /** One entry in the human-facing locale switcher. */
267
+ interface LocaleLink {
268
+ locale: string;
269
+ href: string;
270
+ current: boolean;
271
+ }
272
+ /** One `<link rel="alternate" hreflang>` entry. */
273
+ interface Alternate {
274
+ hreflang: string;
275
+ href: string;
276
+ }
277
+ interface RenderInput {
278
+ /** The locale-resolved document to render. */
279
+ localized: LocalizedTakuhon;
280
+ /** Absolute canonical URL for this page (only when `--base-url` was given). */
281
+ canonicalUrl?: string;
282
+ /** hreflang alternates (empty when no base URL is available). */
283
+ alternates: readonly Alternate[];
284
+ /** Human locale switcher links (rendered only when more than one locale). */
285
+ localeNav: readonly LocaleLink[];
286
+ /** Whether to emit Schema.org JSON-LD (mirrors `settings.enableJsonLd`). */
287
+ jsonLd: boolean;
288
+ /**
289
+ * Synced developer-activity snapshot, rendered as a self-owned inline SVG
290
+ * section. The caller gates it on `settings.activity.enabled`; an absent (or
291
+ * metric-less) snapshot omits the section entirely.
292
+ */
293
+ activitySnapshot?: ActivitySnapshot;
294
+ }
295
+ /** Render a complete static HTML document for one locale-resolved profile. */
296
+ declare function renderProfileHtml(input: RenderInput): string;
297
+
298
+ /**
299
+ * Shared static-site generation for `takuhon build` and `takuhon dev`.
300
+ *
301
+ * {@link generateSite} turns one validated, normalized, privacy-filtered profile
302
+ * into the full set of pages the static surface exposes: one per available
303
+ * locale. `build` writes each page's {@link SitePage.file} to disk; `dev` serves
304
+ * each page's {@link SitePage.route} from memory. Keeping the per-locale loop,
305
+ * the locale switcher links, and the canonical/hreflang logic here means both
306
+ * commands share a single source of truth for the site's structure — the only
307
+ * difference between them is disk write vs. in-memory serve.
308
+ *
309
+ * This reuses `@takuhon/core` only (validate/normalize/resolve happen upstream;
310
+ * this module just resolves each locale and renders), so it stays bundler-free
311
+ * and unit-testable as a pure function.
312
+ */
313
+
314
+ /** One generated page: a serve route, a relative output file, and its HTML. */
315
+ interface SitePage {
316
+ /** URL path used by `dev` (default locale at `/`, others at `/<locale>/`). */
317
+ readonly route: string;
318
+ /** Output path used by `build`, relative to the output dir. */
319
+ readonly file: string;
320
+ /** Rendered HTML document. */
321
+ readonly html: string;
322
+ }
323
+ interface GenerateOptions {
324
+ /**
325
+ * Site origin (e.g. `https://me.example`). When set, pages carry absolute
326
+ * canonical + hreflang links; when absent those are omitted (the human locale
327
+ * switcher is always relative either way).
328
+ */
329
+ readonly baseUrl?: string;
330
+ /**
331
+ * Synced developer-activity snapshot (the `activity.json` beside the
332
+ * profile). Rendered as an inline-SVG section only while
333
+ * `settings.activity.enabled` is true — the same opt-in gate the public
334
+ * `GET /api/activity` applies — so disabling the feature drops the section
335
+ * even if a stale snapshot remains on disk. The snapshot is locale-agnostic
336
+ * and shared by every page.
337
+ */
338
+ readonly activitySnapshot?: ActivitySnapshot | null;
339
+ /**
340
+ * Also emit a print-ready CV/résumé page per locale (the default locale at
341
+ * `cv.html` / route `/cv/`, others at `<locale>/cv.html` / `/<locale>/cv/`).
342
+ * `takuhon build` gates this behind `--cv`; `takuhon dev` always enables it so
343
+ * the page is previewable. Off by default, so a plain build is unchanged.
344
+ */
345
+ readonly cv?: boolean;
346
+ }
347
+ /**
348
+ * Generate every page for a profile: the default locale first, then the rest,
349
+ * de-duplicated. Schema.org JSON-LD is emitted unless `settings.enableJsonLd`
350
+ * is explicitly `false`.
351
+ */
352
+ declare function generateSite(profile: NormalizedTakuhon, options?: GenerateOptions): SitePage[];
353
+
354
+ /**
355
+ * Pure HTML rendering of a {@link CvDocument} for `takuhon build --cv`.
356
+ *
357
+ * {@link renderCvHtml} turns one locale-resolved CV (from `@takuhon/core`'s
358
+ * {@link deriveCv}) into a complete, self-contained static HTML résumé: an
359
+ * inline stylesheet sized for A4 with an `@media print` block, so the page
360
+ * reads well on screen and the browser's "Save as PDF" produces a clean,
361
+ * single-column résumé. No external resources are referenced, so the page works
362
+ * under a strict `img-src 'self'` / no-network policy (the same self-owned
363
+ * stance the profile page and the activity card take).
364
+ *
365
+ * Security: every CV-derived string is escaped before it reaches the markup
366
+ * ({@link escapeHtml}), and any URL is scheme-checked ({@link safeUrl}) so a
367
+ * hostile document cannot smuggle a `javascript:` href into the résumé.
368
+ */
369
+
370
+ /** Render a complete static HTML résumé document for one locale-resolved CV. */
371
+ declare function renderCvHtml(cv: CvDocument): string;
372
+
373
+ export { type AdminApiAppDeps, type Alternate, type AuditEvent, type AuditEventType, type AuditLogger, type BuildProblemInput, type CachePurger, ERROR_SLUGS, type ErrorSlug, type GenerateOptions, LOCALE_AWARE_REMAINDERS, type LocaleLink, type ProblemDetails, type ProblemFieldError, type ProblemResponseInput, type PublicAppDeps, type RenderInput, type SitePage, adminAssetSecurityHeaders, buildProblem, createAdminApiApp, createAdminUiApp, createPublicApp, escapeHtml, generateSite, localePrefixGetPath, noopAuditLogger, noopCachePurger, pathLocaleFromUrl, problemResponse, renderCvHtml, renderProfileHtml, stripLocalePrefix };