@takuhon/cloudflare 0.3.0 → 0.4.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
@@ -4,6 +4,7 @@ import {
4
4
  createAdminApiApp,
5
5
  createAdminUiApp,
6
6
  createPublicApp,
7
+ localePrefixGetPath,
7
8
  problemResponse
8
9
  } from "@takuhon/api";
9
10
  import { validate } from "@takuhon/core";
@@ -182,7 +183,40 @@ var takuhon_default = {
182
183
  { id: "design-tokens", label: "Design tokens", category: "design", order: 6 },
183
184
  { id: "portuguese", label: "Portuguese (B2)", category: "language", order: 7 }
184
185
  ],
185
- certifications: [],
186
+ certifications: [
187
+ {
188
+ id: "iaap-cpacc-2022",
189
+ title: {
190
+ en: "Certified Professional in Accessibility Core Competencies (CPACC)",
191
+ ja: "\u30A2\u30AF\u30BB\u30B7\u30D3\u30EA\u30C6\u30A3\u5C02\u9580\u5BB6\u8A8D\u5B9A (CPACC)"
192
+ },
193
+ issuingOrganization: {
194
+ en: "International Association of Accessibility Professionals",
195
+ ja: "\u56FD\u969B\u30A2\u30AF\u30BB\u30B7\u30D3\u30EA\u30C6\u30A3\u5C02\u9580\u5BB6\u5354\u4F1A"
196
+ },
197
+ issueDate: "2022-06",
198
+ expirationDate: null,
199
+ credentialId: "CPACC-2022-PR-09231",
200
+ url: "https://example.org/iaap/credentials/cpacc",
201
+ order: 0
202
+ },
203
+ {
204
+ id: "iaap-was-2023",
205
+ title: {
206
+ en: "Web Accessibility Specialist (WAS)",
207
+ ja: "Web \u30A2\u30AF\u30BB\u30B7\u30D3\u30EA\u30C6\u30A3\u30B9\u30DA\u30B7\u30E3\u30EA\u30B9\u30C8 (WAS)"
208
+ },
209
+ issuingOrganization: {
210
+ en: "International Association of Accessibility Professionals",
211
+ ja: "\u56FD\u969B\u30A2\u30AF\u30BB\u30B7\u30D3\u30EA\u30C6\u30A3\u5C02\u9580\u5BB6\u5354\u4F1A"
212
+ },
213
+ issueDate: "2023-03",
214
+ expirationDate: "2026-03",
215
+ credentialId: "WAS-2023-PR-04522",
216
+ url: "https://example.org/iaap/credentials/was",
217
+ order: 1
218
+ }
219
+ ],
186
220
  memberships: [
187
221
  {
188
222
  id: "iaap-senior",
@@ -231,8 +265,53 @@ var takuhon_default = {
231
265
  order: 0
232
266
  }
233
267
  ],
234
- honors: [],
235
- education: [],
268
+ honors: [
269
+ {
270
+ id: "wai-recognized-contributor-2024",
271
+ title: {
272
+ en: "Recognized Contributor, Web Accessibility Initiative",
273
+ ja: "Web Accessibility Initiative \u8CA2\u732E\u8005\u8868\u5F70"
274
+ },
275
+ issuer: {
276
+ en: "W3C Web Accessibility Initiative",
277
+ ja: "W3C Web Accessibility Initiative"
278
+ },
279
+ description: {
280
+ en: "Recognized for sustained reviews of the WCAG 2.2 Authoring Practices documentation and for contributions to non-visual focus management patterns.",
281
+ ja: "WCAG 2.2 Authoring Practices \u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u306E\u7D99\u7D9A\u30EC\u30D3\u30E5\u30FC\u3001\u304A\u3088\u3073\u975E\u8996\u899A\u7684\u30D5\u30A9\u30FC\u30AB\u30B9\u7BA1\u7406\u30D1\u30BF\u30FC\u30F3\u3078\u306E\u8CA2\u732E\u3092\u8A55\u4FA1\u3002"
282
+ },
283
+ date: "2024-04",
284
+ url: "https://example.org/wai/recognitions/2024",
285
+ order: 0
286
+ }
287
+ ],
288
+ education: [
289
+ {
290
+ id: "westbrook-bsc-2018",
291
+ institution: {
292
+ en: "Westbrook University",
293
+ ja: "\u30A6\u30A7\u30B9\u30C8\u30D6\u30EB\u30C3\u30AF\u5927\u5B66"
294
+ },
295
+ degree: {
296
+ en: "BSc in Cognitive Science",
297
+ ja: "\u8A8D\u77E5\u79D1\u5B66\u5B66\u58EB"
298
+ },
299
+ fieldOfStudy: {
300
+ en: "Human-Computer Interaction",
301
+ ja: "\u30D2\u30E5\u30FC\u30DE\u30F3\u30FB\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u30FB\u30A4\u30F3\u30BF\u30E9\u30AF\u30B7\u30E7\u30F3"
302
+ },
303
+ description: {
304
+ en: "Senior thesis on screen-reader interaction patterns for non-visual users navigating dynamic web interfaces.",
305
+ ja: "\u5352\u696D\u7814\u7A76\u306F\u52D5\u7684 Web \u30A4\u30F3\u30BF\u30FC\u30D5\u30A7\u30FC\u30B9\u3092\u64CD\u4F5C\u3059\u308B\u975E\u8996\u899A\u30E6\u30FC\u30B6\u30FC\u5411\u3051\u30B9\u30AF\u30EA\u30FC\u30F3\u30EA\u30FC\u30C0\u30FC\u5BFE\u8A71\u30D1\u30BF\u30FC\u30F3\u306E\u5206\u6790\u3002"
306
+ },
307
+ grade: "magna cum laude",
308
+ startDate: "2014-09",
309
+ endDate: "2018-06",
310
+ isCurrent: false,
311
+ url: "https://example.org/westbrook",
312
+ order: 0
313
+ }
314
+ ],
236
315
  publications: [
237
316
  {
238
317
  id: "design-tokens-wcag-2024",
@@ -255,7 +334,28 @@ var takuhon_default = {
255
334
  order: 0
256
335
  }
257
336
  ],
258
- languages: [],
337
+ languages: [
338
+ {
339
+ id: "lang-en",
340
+ language: "en",
341
+ displayName: {
342
+ en: "English",
343
+ ja: "\u82F1\u8A9E"
344
+ },
345
+ proficiency: "native",
346
+ order: 0
347
+ },
348
+ {
349
+ id: "lang-pt",
350
+ language: "pt",
351
+ displayName: {
352
+ en: "Portuguese",
353
+ ja: "\u30DD\u30EB\u30C8\u30AC\u30EB\u8A9E"
354
+ },
355
+ proficiency: "intermediate",
356
+ order: 1
357
+ }
358
+ ],
259
359
  courses: [
260
360
  {
261
361
  id: "coursera-advanced-a11y-2023",
@@ -440,7 +540,7 @@ function createTakuhonWorker(opts) {
440
540
  origin: url.origin
441
541
  });
442
542
  const auditLogger = consoleAuditLogger;
443
- const router = new Hono();
543
+ const router = new Hono({ getPath: localePrefixGetPath });
444
544
  router.notFound(
445
545
  (c) => problemResponse(c, {
446
546
  slug: ERROR_SLUGS.notFound,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../../../examples/personal-profile/takuhon.json","../src/admin/cloudflare-cache-purger.ts","../src/admin/console-audit-logger.ts","../src/kv-storage.ts"],"sourcesContent":["import {\n ERROR_SLUGS,\n createAdminApiApp,\n createAdminUiApp,\n createPublicApp,\n problemResponse,\n type AuditLogger,\n type CachePurger,\n} from '@takuhon/api';\nimport { validate, type Takuhon } from '@takuhon/core';\nimport { Hono } from 'hono';\n\nimport exampleJson from '../../../examples/personal-profile/takuhon.json' with { type: 'json' };\n\nimport { CloudflareCachePurger } from './admin/cloudflare-cache-purger.js';\nimport { consoleAuditLogger } from './admin/console-audit-logger.js';\nimport { KvTakuhonStorage } from './kv-storage.js';\n\nexport interface Env {\n TAKUHON_KV: KVNamespace;\n /**\n * Admin bearer token. Provision via `wrangler secret put TAKUHON_ADMIN_TOKEN`.\n * Leave unset to disable admin writes entirely (every PUT/DELETE returns 401).\n */\n TAKUHON_ADMIN_TOKEN?: string;\n /**\n * Comma-separated Origin allowlist for browser-originating admin requests.\n * Empty / unset disables the check (deploy without a configured allowlist is\n * acceptable when the admin UI is same-origin; documented in the README).\n */\n TAKUHON_ADMIN_ORIGIN?: string;\n}\n\n/** Options accepted by {@link createTakuhonWorker}. */\nexport interface CreateTakuhonWorkerOptions {\n /**\n * Lazy producer for the fallback Takuhon document served when KV has no\n * stored profile yet. Called at most once per Worker invocation, on the\n * cold path where the storage layer returns no entry. Implementations\n * typically import a bundled `takuhon.json`, validate it once, and return\n * the resulting value.\n */\n readonly fallback: () => Takuhon;\n}\n\nfunction parseOrigins(raw: string | undefined): string[] {\n if (raw === undefined || raw === '') return [];\n return raw\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s !== '');\n}\n\n/**\n * Build a Cloudflare Worker handler for the takuhon adapter. Wires\n * `@takuhon/api`'s public/admin app factories to the KV-backed storage,\n * Cloudflare edge cache purger, and console audit logger that ship with\n * this package.\n *\n * This is the entry point used by projects scaffolded with\n * `create-takuhon`: their `src/index.ts` imports `createTakuhonWorker`,\n * passes a `fallback` that loads the project's own `takuhon.json`, and\n * `export default`s the returned handler. The default export of this\n * module is a convenience that calls the same factory with the monorepo's\n * bundled `personal-profile` fixture.\n */\nexport function createTakuhonWorker(opts: CreateTakuhonWorkerOptions): {\n fetch: (request: Request, env: Env) => Response | Promise<Response>;\n} {\n return {\n fetch(request: Request, env: Env): Response | Promise<Response> {\n const url = new URL(request.url);\n const storage = new KvTakuhonStorage(env.TAKUHON_KV);\n const cachePurger: CachePurger = new CloudflareCachePurger(() => caches.default, {\n origin: url.origin,\n });\n const auditLogger: AuditLogger = consoleAuditLogger;\n\n const router = new Hono();\n router.notFound((c) =>\n problemResponse(c, {\n slug: ERROR_SLUGS.notFound,\n status: 404,\n title: 'Not Found',\n detail: `No route matches ${new URL(c.req.url).pathname}.`,\n }),\n );\n router.route(\n '/api/admin',\n createAdminApiApp({\n storage,\n getAdminToken: () => env.TAKUHON_ADMIN_TOKEN,\n getAdminOrigins: () => parseOrigins(env.TAKUHON_ADMIN_ORIGIN),\n cachePurger,\n auditLogger,\n }),\n );\n router.route('/admin', createAdminUiApp());\n router.route('/', createPublicApp({ storage, fallback: opts.fallback }));\n\n return router.fetch(request, env);\n },\n };\n}\n\nfunction bundledFallback(): Takuhon {\n const r = validate(exampleJson);\n if (!r.ok) throw new Error('Bundled fixture failed validation.');\n return r.data;\n}\n\nexport default createTakuhonWorker({ fallback: bundledFallback });\n","{\n \"schemaVersion\": \"0.2.0\",\n \"profile\": {\n \"displayName\": {\n \"en\": \"Pat Rivera\",\n \"ja\": \"パット・リベラ\"\n },\n \"tagline\": {\n \"en\": \"Open-source maintainer and accessibility advocate\",\n \"ja\": \"オープンソースメンテナ / アクセシビリティ実践者\"\n },\n \"bio\": {\n \"en\": \"Pat Rivera is a fictional persona used to exercise every field of the takuhon profile schema. They maintain a handful of open-source libraries focused on accessibility tooling and frequently speak at local meetups.\",\n \"ja\": \"Pat Rivera は takuhon プロフィール schema の全フィールドを示すための架空の人物です。アクセシビリティ系のオープンソースライブラリを保守し、地域コミュニティで登壇しています。\"\n },\n \"avatar\": {\n \"url\": \"/assets/avatar.webp\",\n \"alt\": {\n \"en\": \"Pat Rivera smiling, wearing round glasses, in front of a soft gradient.\",\n \"ja\": \"ソフトなグラデーションを背景に微笑む Pat Rivera。丸眼鏡を着用。\"\n }\n },\n \"location\": {\n \"country\": \"PT\",\n \"region\": \"Lisbon\",\n \"locality\": {\n \"en\": \"Lisbon\",\n \"ja\": \"リスボン\"\n },\n \"display\": {\n \"en\": \"Lisbon, Portugal\",\n \"ja\": \"ポルトガル・リスボン\"\n }\n }\n },\n \"links\": [\n {\n \"id\": \"website\",\n \"type\": \"website\",\n \"label\": { \"en\": \"Personal site\" },\n \"url\": \"https://example.com/pat\",\n \"featured\": true,\n \"order\": 0\n },\n {\n \"id\": \"github\",\n \"type\": \"github\",\n \"url\": \"https://example.com/pat/github\",\n \"featured\": true,\n \"order\": 1\n },\n {\n \"id\": \"mastodon\",\n \"type\": \"mastodon\",\n \"url\": \"https://example.social/@pat\",\n \"order\": 2\n },\n {\n \"id\": \"blog\",\n \"type\": \"blog\",\n \"label\": {\n \"en\": \"Notes on accessible UI\",\n \"ja\": \"アクセシブル UI の覚え書き\"\n },\n \"url\": \"https://example.com/pat/blog\",\n \"order\": 3\n },\n {\n \"id\": \"newsletter\",\n \"type\": \"custom\",\n \"label\": {\n \"en\": \"Weekly newsletter\",\n \"ja\": \"週次ニュースレター\"\n },\n \"url\": \"https://example.com/pat/newsletter\",\n \"iconUrl\": \"https://example.com/assets/icons/newsletter.svg\",\n \"order\": 4\n }\n ],\n \"careers\": [\n {\n \"id\": \"stellar-ux\",\n \"organization\": {\n \"en\": \"Stellar UX Studio\",\n \"ja\": \"ステラ UX スタジオ\"\n },\n \"role\": {\n \"en\": \"Principal Accessibility Engineer\",\n \"ja\": \"プリンシパル・アクセシビリティエンジニア\"\n },\n \"description\": {\n \"en\": \"Lead the accessibility engineering practice across product surfaces, drive WCAG 2.2 conformance reviews, and mentor a team of five engineers.\",\n \"ja\": \"プロダクト全体のアクセシビリティ設計を統括。WCAG 2.2 適合レビューを主導し、5 名のエンジニアをメンタリング。\"\n },\n \"startDate\": \"2023-04\",\n \"endDate\": null,\n \"isCurrent\": true,\n \"url\": \"https://example.com/stellar\",\n \"order\": 0\n },\n {\n \"id\": \"harbor-labs\",\n \"organization\": {\n \"en\": \"Harbor Labs\",\n \"ja\": \"ハーバーラボ\"\n },\n \"role\": {\n \"en\": \"Senior Frontend Engineer\",\n \"ja\": \"シニアフロントエンドエンジニア\"\n },\n \"description\": {\n \"en\": \"Built the design system foundation and an accessible component library used across nine internal products.\",\n \"ja\": \"デザインシステム基盤と、社内 9 プロダクトで利用されるアクセシブルなコンポーネントライブラリを設計。\"\n },\n \"startDate\": \"2019-06\",\n \"endDate\": \"2023-03\",\n \"order\": 1\n }\n ],\n \"projects\": [\n {\n \"id\": \"axe-helpers\",\n \"title\": {\n \"en\": \"axe-helpers\",\n \"ja\": \"axe-helpers\"\n },\n \"description\": {\n \"en\": \"A tiny set of utilities that wraps axe-core for use in component-level integration tests.\",\n \"ja\": \"コンポーネント単位の統合テスト向けに axe-core をラップする小規模ユーティリティ集。\"\n },\n \"url\": \"https://example.com/axe-helpers\",\n \"tags\": [\"accessibility\", \"testing\", \"typescript\"],\n \"relatedCareerId\": \"stellar-ux\",\n \"startDate\": \"2023-09\",\n \"highlighted\": true,\n \"order\": 0\n },\n {\n \"id\": \"color-contrast-cli\",\n \"title\": {\n \"en\": \"color-contrast-cli\",\n \"ja\": \"color-contrast-cli\"\n },\n \"description\": {\n \"en\": \"Command-line tool that audits design tokens for WCAG contrast ratios.\",\n \"ja\": \"デザイントークンの WCAG コントラスト比を監査するコマンドラインツール。\"\n },\n \"url\": \"https://example.com/color-contrast-cli\",\n \"tags\": [\"accessibility\", \"cli\", \"design-tokens\"],\n \"startDate\": \"2021-02\",\n \"endDate\": \"2022-08\",\n \"order\": 1\n },\n {\n \"id\": \"meetup-talks\",\n \"title\": {\n \"en\": \"Local meetup talks\",\n \"ja\": \"地域コミュニティ登壇\"\n },\n \"order\": 2\n }\n ],\n \"skills\": [\n { \"id\": \"typescript\", \"label\": \"TypeScript\", \"category\": \"programming\", \"order\": 0 },\n { \"id\": \"react\", \"label\": \"React\", \"category\": \"programming\", \"order\": 1 },\n { \"id\": \"wcag-2-2\", \"label\": \"WCAG 2.2\", \"category\": \"design\", \"order\": 2 },\n { \"id\": \"aria\", \"label\": \"ARIA\", \"category\": \"design\", \"order\": 3 },\n { \"id\": \"storybook\", \"label\": \"Storybook\", \"category\": \"programming\", \"order\": 4 },\n { \"id\": \"playwright\", \"label\": \"Playwright\", \"category\": \"programming\", \"order\": 5 },\n { \"id\": \"design-tokens\", \"label\": \"Design tokens\", \"category\": \"design\", \"order\": 6 },\n { \"id\": \"portuguese\", \"label\": \"Portuguese (B2)\", \"category\": \"language\", \"order\": 7 }\n ],\n \"certifications\": [],\n \"memberships\": [\n {\n \"id\": \"iaap-senior\",\n \"organization\": {\n \"en\": \"International Association of Accessibility Professionals\",\n \"ja\": \"国際アクセシビリティ専門家協会\"\n },\n \"role\": {\n \"en\": \"Senior Member\",\n \"ja\": \"シニアメンバー\"\n },\n \"description\": {\n \"en\": \"Active participant in the Web Accessibility special interest group, contributing to monthly working sessions on WCAG 2.2 interpretation.\",\n \"ja\": \"Web Accessibility 専門部会に参加し、WCAG 2.2 解釈の月例セッションに貢献。\"\n },\n \"startDate\": \"2022-09\",\n \"endDate\": null,\n \"isCurrent\": true,\n \"url\": \"https://example.org/iaap\",\n \"order\": 0\n }\n ],\n \"volunteering\": [\n {\n \"id\": \"code-org-instructor\",\n \"organization\": {\n \"en\": \"Code.org\",\n \"ja\": \"Code.org\"\n },\n \"role\": {\n \"en\": \"Volunteer Instructor\",\n \"ja\": \"ボランティア講師\"\n },\n \"cause\": {\n \"en\": \"Education\",\n \"ja\": \"教育\"\n },\n \"description\": {\n \"en\": \"Taught introductory programming concepts to middle-school students in weekend workshops, with a focus on inclusive curriculum design.\",\n \"ja\": \"週末ワークショップで中学生にプログラミング入門を指導。インクルーシブなカリキュラム設計を重視。\"\n },\n \"startDate\": \"2021-09\",\n \"endDate\": \"2024-06\",\n \"isCurrent\": false,\n \"url\": \"https://example.org/code-org\",\n \"order\": 0\n }\n ],\n \"honors\": [],\n \"education\": [],\n \"publications\": [\n {\n \"id\": \"design-tokens-wcag-2024\",\n \"title\": {\n \"en\": \"Auditing Design Tokens for WCAG 2.2 Conformance\",\n \"ja\": \"デザイントークンの WCAG 2.2 適合性監査\"\n },\n \"publisher\": {\n \"en\": \"ACM SIGACCESS\",\n \"ja\": \"ACM SIGACCESS\"\n },\n \"description\": {\n \"en\": \"A method for statically analyzing design-token files to surface color-contrast violations before they reach implementation.\",\n \"ja\": \"実装前にデザイントークンファイルを静的解析し、色コントラスト違反を発見する手法を提案。\"\n },\n \"date\": \"2024-03\",\n \"url\": \"https://example.org/papers/design-tokens-wcag\",\n \"doi\": \"10.1145/3678901.3678910\",\n \"coAuthors\": [\"Jamie Chen\", \"Sofia Almeida\"],\n \"order\": 0\n }\n ],\n \"languages\": [],\n \"courses\": [\n {\n \"id\": \"coursera-advanced-a11y-2023\",\n \"title\": {\n \"en\": \"Advanced Web Accessibility Patterns\",\n \"ja\": \"Web アクセシビリティ応用パターン\"\n },\n \"provider\": {\n \"en\": \"Coursera (University of Michigan)\",\n \"ja\": \"Coursera (ミシガン大学)\"\n },\n \"courseNumber\": \"UMICH-A11Y-301\",\n \"description\": {\n \"en\": \"Covered ARIA authoring practices, accessible component patterns, and assistive technology testing workflows.\",\n \"ja\": \"ARIA オーサリングプラクティス、アクセシブルコンポーネントパターン、支援技術テストワークフローを扱う。\"\n },\n \"completionDate\": \"2023-11\",\n \"certificateUrl\": \"https://example.org/certificates/coursera/UMICH-A11Y-301\",\n \"order\": 0\n }\n ],\n \"patents\": [\n {\n \"id\": \"us-2024-a11y-test\",\n \"title\": {\n \"en\": \"Method and System for Automated Screen-Reader Output Verification\",\n \"ja\": \"スクリーンリーダー出力の自動検証手法およびシステム\"\n },\n \"patentNumber\": \"US 99,999,999 B2\",\n \"office\": \"USPTO\",\n \"status\": \"issued\",\n \"description\": {\n \"en\": \"An automated harness that captures screen-reader output and compares it against an authoring-time accessibility annotation.\",\n \"ja\": \"スクリーンリーダーの出力を自動的にキャプチャし、オーサリング時のアクセシビリティ注釈と照合する自動化ハーネス。\"\n },\n \"filingDate\": \"2022-04\",\n \"grantDate\": \"2024-03\",\n \"url\": \"https://patents.example.org/us-11987654\",\n \"coInventors\": [\"Jamie Chen\"],\n \"order\": 0\n },\n {\n \"id\": \"us-pending-focus\",\n \"title\": {\n \"en\": \"Predictive Focus Management for Single-Page Applications\",\n \"ja\": \"シングルページアプリケーション向け予測的フォーカス管理\"\n },\n \"patentNumber\": \"US 99/999,999\",\n \"office\": \"USPTO\",\n \"status\": \"pending\",\n \"description\": {\n \"en\": \"An algorithm that predicts the next likely focus target based on user navigation patterns to reduce focus loss in dynamic content.\",\n \"ja\": \"ユーザーのナビゲーションパターンから次のフォーカス候補を予測し、動的コンテンツにおけるフォーカス喪失を低減するアルゴリズム。\"\n },\n \"filingDate\": \"2024-08\",\n \"url\": \"https://patents.example.org/us-pending-focus\",\n \"order\": 1\n }\n ],\n \"contact\": {\n \"email\": \"pat@example.com\",\n \"showEmail\": false,\n \"formUrl\": \"https://example.com/pat/contact\"\n },\n \"settings\": {\n \"defaultLocale\": \"en\",\n \"fallbackLocale\": \"en\",\n \"availableLocales\": [\"en\", \"ja\"],\n \"theme\": \"default\",\n \"showPoweredBy\": true,\n \"enableJsonLd\": true,\n \"enableApi\": true,\n \"enableAnalytics\": false\n },\n \"meta\": {\n \"createdAt\": \"2026-01-15T09:00:00Z\",\n \"updatedAt\": \"2026-05-12T08:30:00Z\",\n \"generator\": \"Takuhon\",\n \"contentLicense\": {\n \"spdxId\": \"CC-BY-4.0\",\n \"url\": \"https://creativecommons.org/licenses/by/4.0/\",\n \"attribution\": {\n \"name\": \"Pat Rivera\",\n \"url\": \"https://example.com/pat\"\n }\n },\n \"privacy\": {\n \"hideCredentialIds\": true,\n \"hideEducationGrades\": true\n }\n }\n}\n","import type { CachePurger } from '@takuhon/api';\n\nexport interface CloudflareCachePurgerOptions {\n /**\n * Absolute origin (e.g. `https://example.com`) used to build the URLs\n * passed to `Cache.delete`. The Worker derives this from the incoming\n * request's URL so the same code works under any production hostname.\n */\n origin: string;\n /**\n * Locale codes to include when purging language-keyed cache entries.\n * Cloudflare caches `?lang=` query variants as distinct keys; we purge\n * a representative set on every write. Adapters can extend the list to\n * cover other locales the deploy serves.\n */\n langs?: string[];\n}\n\n/**\n * `CachePurger` backed by Cloudflare's colo-local `caches.default`.\n *\n * Limitations (documented in the adapter README):\n * - Cloudflare's `Cache.delete` clears the current colo only, not the\n * entire edge. Other colos honour the response's `Cache-Control`\n * `s-maxage` (5 minutes today) before refreshing.\n * - Truly global invalidation requires the REST `/purge_cache` API,\n * which needs a zone-scoped token; that's deferred to a later phase.\n */\nexport class CloudflareCachePurger implements CachePurger {\n private readonly origin: string;\n private readonly langs: string[];\n\n /**\n * `getCache` is a thunk so the Workers-only `caches` global is touched\n * lazily — public-only requests on this Worker never run admin handlers\n * and must not pay (or fail under Node tests) for the lookup.\n */\n constructor(\n private readonly getCache: () => Cache,\n opts: CloudflareCachePurgerOptions,\n ) {\n this.origin = opts.origin.replace(/\\/$/, '');\n this.langs = opts.langs ?? ['en', 'ja'];\n }\n\n async profileUpdated(): Promise<void> {\n await this.purge();\n }\n\n async profileDeleted(): Promise<void> {\n await this.purge();\n }\n\n private async purge(): Promise<void> {\n const cache = this.getCache();\n const targets = ['/', '/api/profile', '/api/jsonld', '/takuhon.json'];\n for (const lang of this.langs) {\n const q = `?lang=${encodeURIComponent(lang)}`;\n targets.push(`/api/profile${q}`, `/api/jsonld${q}`);\n }\n for (const path of targets) {\n await cache.delete(new Request(this.origin + path), { ignoreMethod: true });\n }\n }\n}\n","import type { AuditEvent, AuditLogger } from '@takuhon/api';\n\n/**\n * `AuditLogger` that writes a single line of JSON per event to `console.log`.\n *\n * Cloudflare captures these via Workers Tail / Logpush, where they can be\n * routed to R2, S3, or any downstream SIEM. Token bodies never reach this\n * sink — the upstream middleware only emits `sha256:<hex>` digests in\n * `actor.tokenHash`.\n */\nexport const consoleAuditLogger: AuditLogger = (event: AuditEvent): void => {\n console.log(JSON.stringify(event));\n};\n","import { ConflictError, NotFoundError, type Takuhon, type TakuhonStorage } from '@takuhon/core';\n\nexport const KV_KEY = 'TAKUHON_DATA';\n\nexport interface KvMetadata {\n version: string;\n updatedAt: string;\n}\n\n/**\n * Cloudflare KV implementation of the `TakuhonStorage` contract. Stores the\n * profile document as JSON under a single key (`TAKUHON_DATA`) and tracks the\n * optimistic-locking token inside KV value metadata.\n *\n * `version` is a fresh UUIDv4 on every successful write. Callers compare it\n * verbatim against the `If-Match` precondition; mismatches raise\n * `ConflictError` with `currentVersion` so the API layer can build the RFC\n * 7807 envelope without an extra round trip.\n */\nexport class KvTakuhonStorage implements TakuhonStorage {\n constructor(private readonly kv: KVNamespace) {}\n\n async getProfile(): Promise<{ data: Takuhon; version: string }> {\n const result = await this.kv.getWithMetadata<Takuhon, KvMetadata>(KV_KEY, 'json');\n if (result.value === null || !result.metadata?.version) {\n throw new NotFoundError(`No profile is stored at KV key \"${KV_KEY}\".`);\n }\n return { data: result.value, version: result.metadata.version };\n }\n\n async saveProfile(data: Takuhon, ifMatch?: string): Promise<{ version: string }> {\n if (ifMatch !== undefined) {\n const current = await this.kv.getWithMetadata<Takuhon, KvMetadata>(KV_KEY, 'json');\n const currentVersion = current.metadata?.version;\n if (currentVersion !== ifMatch) {\n throw new ConflictError(\n `If-Match preconditioned on version \"${ifMatch}\" but current is \"${currentVersion ?? 'absent'}\".`,\n { currentVersion },\n );\n }\n }\n const version = crypto.randomUUID();\n const updatedAt = new Date().toISOString();\n await this.kv.put(KV_KEY, JSON.stringify(data), {\n metadata: { version, updatedAt } satisfies KvMetadata,\n });\n return { version };\n }\n\n async deleteProfile(): Promise<void> {\n await this.kv.delete(KV_KEY);\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../../../examples/personal-profile/takuhon.json","../src/admin/cloudflare-cache-purger.ts","../src/admin/console-audit-logger.ts","../src/kv-storage.ts"],"sourcesContent":["import {\n ERROR_SLUGS,\n createAdminApiApp,\n createAdminUiApp,\n createPublicApp,\n localePrefixGetPath,\n problemResponse,\n type AuditLogger,\n type CachePurger,\n} from '@takuhon/api';\nimport { validate, type Takuhon } from '@takuhon/core';\nimport { Hono } from 'hono';\n\nimport exampleJson from '../../../examples/personal-profile/takuhon.json' with { type: 'json' };\n\nimport { CloudflareCachePurger } from './admin/cloudflare-cache-purger.js';\nimport { consoleAuditLogger } from './admin/console-audit-logger.js';\nimport { KvTakuhonStorage } from './kv-storage.js';\n\nexport interface Env {\n TAKUHON_KV: KVNamespace;\n /**\n * Admin bearer token. Provision via `wrangler secret put TAKUHON_ADMIN_TOKEN`.\n * Leave unset to disable admin writes entirely (every PUT/DELETE returns 401).\n */\n TAKUHON_ADMIN_TOKEN?: string;\n /**\n * Comma-separated Origin allowlist for browser-originating admin requests.\n * Empty / unset disables the check (deploy without a configured allowlist is\n * acceptable when the admin UI is same-origin; documented in the README).\n */\n TAKUHON_ADMIN_ORIGIN?: string;\n}\n\n/** Options accepted by {@link createTakuhonWorker}. */\nexport interface CreateTakuhonWorkerOptions {\n /**\n * Lazy producer for the fallback Takuhon document served when KV has no\n * stored profile yet. Called at most once per Worker invocation, on the\n * cold path where the storage layer returns no entry. Implementations\n * typically import a bundled `takuhon.json`, validate it once, and return\n * the resulting value.\n */\n readonly fallback: () => Takuhon;\n}\n\nfunction parseOrigins(raw: string | undefined): string[] {\n if (raw === undefined || raw === '') return [];\n return raw\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s !== '');\n}\n\n/**\n * Build a Cloudflare Worker handler for the takuhon adapter. Wires\n * `@takuhon/api`'s public/admin app factories to the KV-backed storage,\n * Cloudflare edge cache purger, and console audit logger that ship with\n * this package.\n *\n * This is the entry point used by projects scaffolded with\n * `create-takuhon`: their `src/index.ts` imports `createTakuhonWorker`,\n * passes a `fallback` that loads the project's own `takuhon.json`, and\n * `export default`s the returned handler. The default export of this\n * module is a convenience that calls the same factory with the monorepo's\n * bundled `personal-profile` fixture.\n */\nexport function createTakuhonWorker(opts: CreateTakuhonWorkerOptions): {\n fetch: (request: Request, env: Env) => Response | Promise<Response>;\n} {\n return {\n fetch(request: Request, env: Env): Response | Promise<Response> {\n const url = new URL(request.url);\n const storage = new KvTakuhonStorage(env.TAKUHON_KV);\n const cachePurger: CachePurger = new CloudflareCachePurger(() => caches.default, {\n origin: url.origin,\n });\n const auditLogger: AuditLogger = consoleAuditLogger;\n\n // `getPath` strips a leading `/{locale}` prefix before route\n // matching. This is the production-critical placement: Hono's\n // `route()` flattens each mounted sub-app's routes into this router\n // and dispatches with this router's `getPath` only, so a `getPath`\n // set on the public sub-app alone would not run here. The shared\n // function's allowlist guard never strips admin remainders, so\n // `/api/admin/*` and `/admin/*` mounts stay locale-agnostic.\n const router = new Hono({ getPath: localePrefixGetPath });\n router.notFound((c) =>\n problemResponse(c, {\n slug: ERROR_SLUGS.notFound,\n status: 404,\n title: 'Not Found',\n detail: `No route matches ${new URL(c.req.url).pathname}.`,\n }),\n );\n router.route(\n '/api/admin',\n createAdminApiApp({\n storage,\n getAdminToken: () => env.TAKUHON_ADMIN_TOKEN,\n getAdminOrigins: () => parseOrigins(env.TAKUHON_ADMIN_ORIGIN),\n cachePurger,\n auditLogger,\n }),\n );\n router.route('/admin', createAdminUiApp());\n router.route('/', createPublicApp({ storage, fallback: opts.fallback }));\n\n return router.fetch(request, env);\n },\n };\n}\n\nfunction bundledFallback(): Takuhon {\n const r = validate(exampleJson);\n if (!r.ok) throw new Error('Bundled fixture failed validation.');\n return r.data;\n}\n\nexport default createTakuhonWorker({ fallback: bundledFallback });\n","{\n \"schemaVersion\": \"0.2.0\",\n \"profile\": {\n \"displayName\": {\n \"en\": \"Pat Rivera\",\n \"ja\": \"パット・リベラ\"\n },\n \"tagline\": {\n \"en\": \"Open-source maintainer and accessibility advocate\",\n \"ja\": \"オープンソースメンテナ / アクセシビリティ実践者\"\n },\n \"bio\": {\n \"en\": \"Pat Rivera is a fictional persona used to exercise every field of the takuhon profile schema. They maintain a handful of open-source libraries focused on accessibility tooling and frequently speak at local meetups.\",\n \"ja\": \"Pat Rivera は takuhon プロフィール schema の全フィールドを示すための架空の人物です。アクセシビリティ系のオープンソースライブラリを保守し、地域コミュニティで登壇しています。\"\n },\n \"avatar\": {\n \"url\": \"/assets/avatar.webp\",\n \"alt\": {\n \"en\": \"Pat Rivera smiling, wearing round glasses, in front of a soft gradient.\",\n \"ja\": \"ソフトなグラデーションを背景に微笑む Pat Rivera。丸眼鏡を着用。\"\n }\n },\n \"location\": {\n \"country\": \"PT\",\n \"region\": \"Lisbon\",\n \"locality\": {\n \"en\": \"Lisbon\",\n \"ja\": \"リスボン\"\n },\n \"display\": {\n \"en\": \"Lisbon, Portugal\",\n \"ja\": \"ポルトガル・リスボン\"\n }\n }\n },\n \"links\": [\n {\n \"id\": \"website\",\n \"type\": \"website\",\n \"label\": { \"en\": \"Personal site\" },\n \"url\": \"https://example.com/pat\",\n \"featured\": true,\n \"order\": 0\n },\n {\n \"id\": \"github\",\n \"type\": \"github\",\n \"url\": \"https://example.com/pat/github\",\n \"featured\": true,\n \"order\": 1\n },\n {\n \"id\": \"mastodon\",\n \"type\": \"mastodon\",\n \"url\": \"https://example.social/@pat\",\n \"order\": 2\n },\n {\n \"id\": \"blog\",\n \"type\": \"blog\",\n \"label\": {\n \"en\": \"Notes on accessible UI\",\n \"ja\": \"アクセシブル UI の覚え書き\"\n },\n \"url\": \"https://example.com/pat/blog\",\n \"order\": 3\n },\n {\n \"id\": \"newsletter\",\n \"type\": \"custom\",\n \"label\": {\n \"en\": \"Weekly newsletter\",\n \"ja\": \"週次ニュースレター\"\n },\n \"url\": \"https://example.com/pat/newsletter\",\n \"iconUrl\": \"https://example.com/assets/icons/newsletter.svg\",\n \"order\": 4\n }\n ],\n \"careers\": [\n {\n \"id\": \"stellar-ux\",\n \"organization\": {\n \"en\": \"Stellar UX Studio\",\n \"ja\": \"ステラ UX スタジオ\"\n },\n \"role\": {\n \"en\": \"Principal Accessibility Engineer\",\n \"ja\": \"プリンシパル・アクセシビリティエンジニア\"\n },\n \"description\": {\n \"en\": \"Lead the accessibility engineering practice across product surfaces, drive WCAG 2.2 conformance reviews, and mentor a team of five engineers.\",\n \"ja\": \"プロダクト全体のアクセシビリティ設計を統括。WCAG 2.2 適合レビューを主導し、5 名のエンジニアをメンタリング。\"\n },\n \"startDate\": \"2023-04\",\n \"endDate\": null,\n \"isCurrent\": true,\n \"url\": \"https://example.com/stellar\",\n \"order\": 0\n },\n {\n \"id\": \"harbor-labs\",\n \"organization\": {\n \"en\": \"Harbor Labs\",\n \"ja\": \"ハーバーラボ\"\n },\n \"role\": {\n \"en\": \"Senior Frontend Engineer\",\n \"ja\": \"シニアフロントエンドエンジニア\"\n },\n \"description\": {\n \"en\": \"Built the design system foundation and an accessible component library used across nine internal products.\",\n \"ja\": \"デザインシステム基盤と、社内 9 プロダクトで利用されるアクセシブルなコンポーネントライブラリを設計。\"\n },\n \"startDate\": \"2019-06\",\n \"endDate\": \"2023-03\",\n \"order\": 1\n }\n ],\n \"projects\": [\n {\n \"id\": \"axe-helpers\",\n \"title\": {\n \"en\": \"axe-helpers\",\n \"ja\": \"axe-helpers\"\n },\n \"description\": {\n \"en\": \"A tiny set of utilities that wraps axe-core for use in component-level integration tests.\",\n \"ja\": \"コンポーネント単位の統合テスト向けに axe-core をラップする小規模ユーティリティ集。\"\n },\n \"url\": \"https://example.com/axe-helpers\",\n \"tags\": [\"accessibility\", \"testing\", \"typescript\"],\n \"relatedCareerId\": \"stellar-ux\",\n \"startDate\": \"2023-09\",\n \"highlighted\": true,\n \"order\": 0\n },\n {\n \"id\": \"color-contrast-cli\",\n \"title\": {\n \"en\": \"color-contrast-cli\",\n \"ja\": \"color-contrast-cli\"\n },\n \"description\": {\n \"en\": \"Command-line tool that audits design tokens for WCAG contrast ratios.\",\n \"ja\": \"デザイントークンの WCAG コントラスト比を監査するコマンドラインツール。\"\n },\n \"url\": \"https://example.com/color-contrast-cli\",\n \"tags\": [\"accessibility\", \"cli\", \"design-tokens\"],\n \"startDate\": \"2021-02\",\n \"endDate\": \"2022-08\",\n \"order\": 1\n },\n {\n \"id\": \"meetup-talks\",\n \"title\": {\n \"en\": \"Local meetup talks\",\n \"ja\": \"地域コミュニティ登壇\"\n },\n \"order\": 2\n }\n ],\n \"skills\": [\n { \"id\": \"typescript\", \"label\": \"TypeScript\", \"category\": \"programming\", \"order\": 0 },\n { \"id\": \"react\", \"label\": \"React\", \"category\": \"programming\", \"order\": 1 },\n { \"id\": \"wcag-2-2\", \"label\": \"WCAG 2.2\", \"category\": \"design\", \"order\": 2 },\n { \"id\": \"aria\", \"label\": \"ARIA\", \"category\": \"design\", \"order\": 3 },\n { \"id\": \"storybook\", \"label\": \"Storybook\", \"category\": \"programming\", \"order\": 4 },\n { \"id\": \"playwright\", \"label\": \"Playwright\", \"category\": \"programming\", \"order\": 5 },\n { \"id\": \"design-tokens\", \"label\": \"Design tokens\", \"category\": \"design\", \"order\": 6 },\n { \"id\": \"portuguese\", \"label\": \"Portuguese (B2)\", \"category\": \"language\", \"order\": 7 }\n ],\n \"certifications\": [\n {\n \"id\": \"iaap-cpacc-2022\",\n \"title\": {\n \"en\": \"Certified Professional in Accessibility Core Competencies (CPACC)\",\n \"ja\": \"アクセシビリティ専門家認定 (CPACC)\"\n },\n \"issuingOrganization\": {\n \"en\": \"International Association of Accessibility Professionals\",\n \"ja\": \"国際アクセシビリティ専門家協会\"\n },\n \"issueDate\": \"2022-06\",\n \"expirationDate\": null,\n \"credentialId\": \"CPACC-2022-PR-09231\",\n \"url\": \"https://example.org/iaap/credentials/cpacc\",\n \"order\": 0\n },\n {\n \"id\": \"iaap-was-2023\",\n \"title\": {\n \"en\": \"Web Accessibility Specialist (WAS)\",\n \"ja\": \"Web アクセシビリティスペシャリスト (WAS)\"\n },\n \"issuingOrganization\": {\n \"en\": \"International Association of Accessibility Professionals\",\n \"ja\": \"国際アクセシビリティ専門家協会\"\n },\n \"issueDate\": \"2023-03\",\n \"expirationDate\": \"2026-03\",\n \"credentialId\": \"WAS-2023-PR-04522\",\n \"url\": \"https://example.org/iaap/credentials/was\",\n \"order\": 1\n }\n ],\n \"memberships\": [\n {\n \"id\": \"iaap-senior\",\n \"organization\": {\n \"en\": \"International Association of Accessibility Professionals\",\n \"ja\": \"国際アクセシビリティ専門家協会\"\n },\n \"role\": {\n \"en\": \"Senior Member\",\n \"ja\": \"シニアメンバー\"\n },\n \"description\": {\n \"en\": \"Active participant in the Web Accessibility special interest group, contributing to monthly working sessions on WCAG 2.2 interpretation.\",\n \"ja\": \"Web Accessibility 専門部会に参加し、WCAG 2.2 解釈の月例セッションに貢献。\"\n },\n \"startDate\": \"2022-09\",\n \"endDate\": null,\n \"isCurrent\": true,\n \"url\": \"https://example.org/iaap\",\n \"order\": 0\n }\n ],\n \"volunteering\": [\n {\n \"id\": \"code-org-instructor\",\n \"organization\": {\n \"en\": \"Code.org\",\n \"ja\": \"Code.org\"\n },\n \"role\": {\n \"en\": \"Volunteer Instructor\",\n \"ja\": \"ボランティア講師\"\n },\n \"cause\": {\n \"en\": \"Education\",\n \"ja\": \"教育\"\n },\n \"description\": {\n \"en\": \"Taught introductory programming concepts to middle-school students in weekend workshops, with a focus on inclusive curriculum design.\",\n \"ja\": \"週末ワークショップで中学生にプログラミング入門を指導。インクルーシブなカリキュラム設計を重視。\"\n },\n \"startDate\": \"2021-09\",\n \"endDate\": \"2024-06\",\n \"isCurrent\": false,\n \"url\": \"https://example.org/code-org\",\n \"order\": 0\n }\n ],\n \"honors\": [\n {\n \"id\": \"wai-recognized-contributor-2024\",\n \"title\": {\n \"en\": \"Recognized Contributor, Web Accessibility Initiative\",\n \"ja\": \"Web Accessibility Initiative 貢献者表彰\"\n },\n \"issuer\": {\n \"en\": \"W3C Web Accessibility Initiative\",\n \"ja\": \"W3C Web Accessibility Initiative\"\n },\n \"description\": {\n \"en\": \"Recognized for sustained reviews of the WCAG 2.2 Authoring Practices documentation and for contributions to non-visual focus management patterns.\",\n \"ja\": \"WCAG 2.2 Authoring Practices ドキュメントの継続レビュー、および非視覚的フォーカス管理パターンへの貢献を評価。\"\n },\n \"date\": \"2024-04\",\n \"url\": \"https://example.org/wai/recognitions/2024\",\n \"order\": 0\n }\n ],\n \"education\": [\n {\n \"id\": \"westbrook-bsc-2018\",\n \"institution\": {\n \"en\": \"Westbrook University\",\n \"ja\": \"ウェストブルック大学\"\n },\n \"degree\": {\n \"en\": \"BSc in Cognitive Science\",\n \"ja\": \"認知科学学士\"\n },\n \"fieldOfStudy\": {\n \"en\": \"Human-Computer Interaction\",\n \"ja\": \"ヒューマン・コンピュータ・インタラクション\"\n },\n \"description\": {\n \"en\": \"Senior thesis on screen-reader interaction patterns for non-visual users navigating dynamic web interfaces.\",\n \"ja\": \"卒業研究は動的 Web インターフェースを操作する非視覚ユーザー向けスクリーンリーダー対話パターンの分析。\"\n },\n \"grade\": \"magna cum laude\",\n \"startDate\": \"2014-09\",\n \"endDate\": \"2018-06\",\n \"isCurrent\": false,\n \"url\": \"https://example.org/westbrook\",\n \"order\": 0\n }\n ],\n \"publications\": [\n {\n \"id\": \"design-tokens-wcag-2024\",\n \"title\": {\n \"en\": \"Auditing Design Tokens for WCAG 2.2 Conformance\",\n \"ja\": \"デザイントークンの WCAG 2.2 適合性監査\"\n },\n \"publisher\": {\n \"en\": \"ACM SIGACCESS\",\n \"ja\": \"ACM SIGACCESS\"\n },\n \"description\": {\n \"en\": \"A method for statically analyzing design-token files to surface color-contrast violations before they reach implementation.\",\n \"ja\": \"実装前にデザイントークンファイルを静的解析し、色コントラスト違反を発見する手法を提案。\"\n },\n \"date\": \"2024-03\",\n \"url\": \"https://example.org/papers/design-tokens-wcag\",\n \"doi\": \"10.1145/3678901.3678910\",\n \"coAuthors\": [\"Jamie Chen\", \"Sofia Almeida\"],\n \"order\": 0\n }\n ],\n \"languages\": [\n {\n \"id\": \"lang-en\",\n \"language\": \"en\",\n \"displayName\": {\n \"en\": \"English\",\n \"ja\": \"英語\"\n },\n \"proficiency\": \"native\",\n \"order\": 0\n },\n {\n \"id\": \"lang-pt\",\n \"language\": \"pt\",\n \"displayName\": {\n \"en\": \"Portuguese\",\n \"ja\": \"ポルトガル語\"\n },\n \"proficiency\": \"intermediate\",\n \"order\": 1\n }\n ],\n \"courses\": [\n {\n \"id\": \"coursera-advanced-a11y-2023\",\n \"title\": {\n \"en\": \"Advanced Web Accessibility Patterns\",\n \"ja\": \"Web アクセシビリティ応用パターン\"\n },\n \"provider\": {\n \"en\": \"Coursera (University of Michigan)\",\n \"ja\": \"Coursera (ミシガン大学)\"\n },\n \"courseNumber\": \"UMICH-A11Y-301\",\n \"description\": {\n \"en\": \"Covered ARIA authoring practices, accessible component patterns, and assistive technology testing workflows.\",\n \"ja\": \"ARIA オーサリングプラクティス、アクセシブルコンポーネントパターン、支援技術テストワークフローを扱う。\"\n },\n \"completionDate\": \"2023-11\",\n \"certificateUrl\": \"https://example.org/certificates/coursera/UMICH-A11Y-301\",\n \"order\": 0\n }\n ],\n \"patents\": [\n {\n \"id\": \"us-2024-a11y-test\",\n \"title\": {\n \"en\": \"Method and System for Automated Screen-Reader Output Verification\",\n \"ja\": \"スクリーンリーダー出力の自動検証手法およびシステム\"\n },\n \"patentNumber\": \"US 99,999,999 B2\",\n \"office\": \"USPTO\",\n \"status\": \"issued\",\n \"description\": {\n \"en\": \"An automated harness that captures screen-reader output and compares it against an authoring-time accessibility annotation.\",\n \"ja\": \"スクリーンリーダーの出力を自動的にキャプチャし、オーサリング時のアクセシビリティ注釈と照合する自動化ハーネス。\"\n },\n \"filingDate\": \"2022-04\",\n \"grantDate\": \"2024-03\",\n \"url\": \"https://patents.example.org/us-11987654\",\n \"coInventors\": [\"Jamie Chen\"],\n \"order\": 0\n },\n {\n \"id\": \"us-pending-focus\",\n \"title\": {\n \"en\": \"Predictive Focus Management for Single-Page Applications\",\n \"ja\": \"シングルページアプリケーション向け予測的フォーカス管理\"\n },\n \"patentNumber\": \"US 99/999,999\",\n \"office\": \"USPTO\",\n \"status\": \"pending\",\n \"description\": {\n \"en\": \"An algorithm that predicts the next likely focus target based on user navigation patterns to reduce focus loss in dynamic content.\",\n \"ja\": \"ユーザーのナビゲーションパターンから次のフォーカス候補を予測し、動的コンテンツにおけるフォーカス喪失を低減するアルゴリズム。\"\n },\n \"filingDate\": \"2024-08\",\n \"url\": \"https://patents.example.org/us-pending-focus\",\n \"order\": 1\n }\n ],\n \"contact\": {\n \"email\": \"pat@example.com\",\n \"showEmail\": false,\n \"formUrl\": \"https://example.com/pat/contact\"\n },\n \"settings\": {\n \"defaultLocale\": \"en\",\n \"fallbackLocale\": \"en\",\n \"availableLocales\": [\"en\", \"ja\"],\n \"theme\": \"default\",\n \"showPoweredBy\": true,\n \"enableJsonLd\": true,\n \"enableApi\": true,\n \"enableAnalytics\": false\n },\n \"meta\": {\n \"createdAt\": \"2026-01-15T09:00:00Z\",\n \"updatedAt\": \"2026-05-12T08:30:00Z\",\n \"generator\": \"Takuhon\",\n \"contentLicense\": {\n \"spdxId\": \"CC-BY-4.0\",\n \"url\": \"https://creativecommons.org/licenses/by/4.0/\",\n \"attribution\": {\n \"name\": \"Pat Rivera\",\n \"url\": \"https://example.com/pat\"\n }\n },\n \"privacy\": {\n \"hideCredentialIds\": true,\n \"hideEducationGrades\": true\n }\n }\n}\n","import type { CachePurger } from '@takuhon/api';\n\nexport interface CloudflareCachePurgerOptions {\n /**\n * Absolute origin (e.g. `https://example.com`) used to build the URLs\n * passed to `Cache.delete`. The Worker derives this from the incoming\n * request's URL so the same code works under any production hostname.\n */\n origin: string;\n /**\n * Locale codes to include when purging language-keyed cache entries.\n * Cloudflare caches `?lang=` query variants as distinct keys; we purge\n * a representative set on every write. Adapters can extend the list to\n * cover other locales the deploy serves.\n */\n langs?: string[];\n}\n\n/**\n * `CachePurger` backed by Cloudflare's colo-local `caches.default`.\n *\n * Limitations (documented in the adapter README):\n * - Cloudflare's `Cache.delete` clears the current colo only, not the\n * entire edge. Other colos honour the response's `Cache-Control`\n * `s-maxage` (5 minutes today) before refreshing.\n * - Truly global invalidation requires the REST `/purge_cache` API,\n * which needs a zone-scoped token; that's deferred to a later phase.\n */\nexport class CloudflareCachePurger implements CachePurger {\n private readonly origin: string;\n private readonly langs: string[];\n\n /**\n * `getCache` is a thunk so the Workers-only `caches` global is touched\n * lazily — public-only requests on this Worker never run admin handlers\n * and must not pay (or fail under Node tests) for the lookup.\n */\n constructor(\n private readonly getCache: () => Cache,\n opts: CloudflareCachePurgerOptions,\n ) {\n this.origin = opts.origin.replace(/\\/$/, '');\n this.langs = opts.langs ?? ['en', 'ja'];\n }\n\n async profileUpdated(): Promise<void> {\n await this.purge();\n }\n\n async profileDeleted(): Promise<void> {\n await this.purge();\n }\n\n private async purge(): Promise<void> {\n const cache = this.getCache();\n const targets = ['/', '/api/profile', '/api/jsonld', '/takuhon.json'];\n for (const lang of this.langs) {\n const q = `?lang=${encodeURIComponent(lang)}`;\n targets.push(`/api/profile${q}`, `/api/jsonld${q}`);\n }\n for (const path of targets) {\n await cache.delete(new Request(this.origin + path), { ignoreMethod: true });\n }\n }\n}\n","import type { AuditEvent, AuditLogger } from '@takuhon/api';\n\n/**\n * `AuditLogger` that writes a single line of JSON per event to `console.log`.\n *\n * Cloudflare captures these via Workers Tail / Logpush, where they can be\n * routed to R2, S3, or any downstream SIEM. Token bodies never reach this\n * sink — the upstream middleware only emits `sha256:<hex>` digests in\n * `actor.tokenHash`.\n */\nexport const consoleAuditLogger: AuditLogger = (event: AuditEvent): void => {\n console.log(JSON.stringify(event));\n};\n","import { ConflictError, NotFoundError, type Takuhon, type TakuhonStorage } from '@takuhon/core';\n\nexport const KV_KEY = 'TAKUHON_DATA';\n\nexport interface KvMetadata {\n version: string;\n updatedAt: string;\n}\n\n/**\n * Cloudflare KV implementation of the `TakuhonStorage` contract. Stores the\n * profile document as JSON under a single key (`TAKUHON_DATA`) and tracks the\n * optimistic-locking token inside KV value metadata.\n *\n * `version` is a fresh UUIDv4 on every successful write. Callers compare it\n * verbatim against the `If-Match` precondition; mismatches raise\n * `ConflictError` with `currentVersion` so the API layer can build the RFC\n * 7807 envelope without an extra round trip.\n */\nexport class KvTakuhonStorage implements TakuhonStorage {\n constructor(private readonly kv: KVNamespace) {}\n\n async getProfile(): Promise<{ data: Takuhon; version: string }> {\n const result = await this.kv.getWithMetadata<Takuhon, KvMetadata>(KV_KEY, 'json');\n if (result.value === null || !result.metadata?.version) {\n throw new NotFoundError(`No profile is stored at KV key \"${KV_KEY}\".`);\n }\n return { data: result.value, version: result.metadata.version };\n }\n\n async saveProfile(data: Takuhon, ifMatch?: string): Promise<{ version: string }> {\n if (ifMatch !== undefined) {\n const current = await this.kv.getWithMetadata<Takuhon, KvMetadata>(KV_KEY, 'json');\n const currentVersion = current.metadata?.version;\n if (currentVersion !== ifMatch) {\n throw new ConflictError(\n `If-Match preconditioned on version \"${ifMatch}\" but current is \"${currentVersion ?? 'absent'}\".`,\n { currentVersion },\n );\n }\n }\n const version = crypto.randomUUID();\n const updatedAt = new Date().toISOString();\n await this.kv.put(KV_KEY, JSON.stringify(data), {\n metadata: { version, updatedAt } satisfies KvMetadata,\n });\n return { version };\n }\n\n async deleteProfile(): Promise<void> {\n await this.kv.delete(KV_KEY);\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takuhon/cloudflare",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Cloudflare Workers adapter for takuhon — public API, Workers Assets, KV, R2 integrations",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Takuhon contributors",
@@ -45,8 +45,8 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "hono": "^4.12.19",
48
- "@takuhon/api": "0.3.0",
49
- "@takuhon/core": "0.3.0"
48
+ "@takuhon/api": "0.4.0",
49
+ "@takuhon/core": "0.4.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@cloudflare/workers-types": "^4.20260518.1",