// Component Registry Worker 型別定義 import { z } from 'zod'; // ── Cloudflare Bindings ────────────────────────────────────────────────────── export type Bindings = { AI: Ai; // KV key 格式: // comp:{hash_id}:{version} → 零件元數據(hash_id = cmp_ + sha256 前 8 碼) // idx:{canonical_id} → canonical_id → hash_id 反查索引 SUBMISSIONS_KV: KVNamespace; ANALYTICS_KV: KVNamespace; // 執行統計匯總(key = stats:{hash_id}:{version}) ENVIRONMENT: string; }; // ── Component Contract Schema(Zod)───────────────────────────────────────── // max_cold_start_ms 上限放寬至 500(從 50):實測 auth/ai 類零件含 crypto/init 步驟通常 100-300ms // no_network_syscall / no_filesystem_syscall 都改 optional:auth/api 類零件需要網路 syscall export const ConstraintsSchema = z.object({ max_size_kb: z.number().positive().max(8192), max_cold_start_ms: z.number().positive().max(500), no_network_syscall: z.boolean().optional(), no_filesystem_syscall: z.boolean().optional(), io_model: z.literal('stdin_stdout_json'), }); export const GherkinTestSchema = z.object({ scenario: z.string().min(1), given: z.string().min(1), then_contains: z.string().min(1), }); export const ComponentContractSchema = z.object({ // canonical_id:提交者填寫的可讀名稱(小寫底線),用於搜尋與 workflow 引用 // component_hash_id:由 Registry 在提交時派發,格式 cmp_{8碼hex},workflow 引用此 id 才能保證永久不壞 // 兩者都可以在 workflow 中引用,Registry 會互相解析 canonical_id: z.string().min(1).regex(/^[a-z][a-z0-9_]*$/, 'canonical_id 必須為小寫底線格式'), display_name: z.string().min(1), // category 擴充:auth (auth primitive)、ai (Claude/AI 推論)、platform (平台底層 crypto/system) category: z.enum(['logic', 'api', 'ui', 'style', 'anim', 'data', 'auth', 'ai', 'platform']), version: z.string().min(1).regex(/^v\d+$/, 'version 格式必須為 vN'), wasi_target: z.literal('preview1'), stability: z.enum(['floating', 'stable', 'pinned']), runtime_compat: z.array(z.enum(['cf-workers', 'workerd', 'wazero'])).min(1), constraints: ConstraintsSchema, input_schema: z.record(z.unknown()), output_schema: z.record(z.unknown()), gherkin_tests: z.array(GherkinTestSchema).min(2, '至少需要一個 happy path 和一個 error path'), // 選填欄位 component_type: z.enum(['wasm', 'service_binding']).optional(), max_size_kb: z.number().optional(), max_cold_start_ms: z.number().optional(), no_network_syscall: z.boolean().optional(), service_binding_key: z.string().optional(), description: z.string().optional(), // aliases:搜尋同義詞,不作為識別符使用 // 從 registry/aliases.yaml 的 scope 同義詞表自動合併,也可在 contract 內手動補充 // 未來接入 KBDB 後,canonical_id 將獲得系統派發的唯一 hash id aliases: z.array(z.string()).optional(), tags: z.array(z.string()).optional(), }); export type ComponentContract = z.infer; // ── 沙盒驗收步驟 ───────────────────────────────────────────────────────────── export type SandboxStep = 'size_check' | 'cold_start' | 'syscall_scan' | 'gherkin_tests' | 'runtime_compat'; export interface SandboxResult { success: boolean; failed_step?: SandboxStep; reason?: string; guide_anchor?: string; // 驗收通過後回傳兩個 id: component_hash_id: string; // cmp_{8碼hex},workflow 引用用,永久不變 canonical_id: string; // 可讀名稱,搜尋用 version: string; } // ── KBDB Block 格式 ────────────────────────────────────────────────────────── export interface KbdbBlock { block_id: string; template_id: string; user_id?: string; page_name?: string; } export interface KbdbSlots { [key: string]: string; } // ── 禁止的 WASM syscall(網路 + 檔案系統)──────────────────────────────────── export const FORBIDDEN_SYSCALLS = [ 'sock_connect', 'sock_accept', 'sock_recv', 'sock_send', 'sock_shutdown', 'fd_open', 'path_open', 'path_create_directory', 'path_remove_directory', 'path_rename', 'path_unlink_file', 'path_filestat_get', 'path_filestat_set_times', 'path_link', 'path_readlink', 'path_symlink', ] as const;