diff --git a/AMD_DEMO.md b/AMD_DEMO.md new file mode 100644 index 0000000..152d651 --- /dev/null +++ b/AMD_DEMO.md @@ -0,0 +1,217 @@ +# AMD 风格 Capability 组合功能演示 + +本演示展示 Sigil 新增的 AMD 风格依赖管理功能。 + +## 1. 基础依赖注入 + +### 部署基础 capability + +```bash +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "github-token", + "execute": "return \"ghp_example_token\";", + "type": "normal", + "description": "获取 GitHub API token" + }' +``` + +### 部署依赖该 capability 的服务 + +```bash +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "github-api", + "schema": { + "type": "object", + "properties": { + "endpoint": {"type": "string"}, + "method": {"type": "string", "default": "GET"} + }, + "required": ["endpoint"] + }, + "execute": "const token = await deps[\"github-token\"](); const response = await fetch(`https://api.github.com${input.endpoint}`, { method: input.method, headers: { \"Authorization\": `Bearer ${token}` } }); return response.json();", + "type": "normal", + "description": "GitHub API 客户端", + "requires": ["github-token"] + }' +``` + +## 2. 多依赖组合 + +### 部署翻译服务 + +```bash +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "translate", + "schema": { + "type": "object", + "properties": { + "text": {"type": "string"}, + "lang": {"type": "string", "default": "zh"} + }, + "required": ["text"] + }, + "execute": "return `[${input.lang}] ${input.text}`;", + "type": "normal" + }' +``` + +### 部署使用多个依赖的服务 + +```bash +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "repo-summary", + "schema": { + "type": "object", + "properties": { + "repo": {"type": "string"} + }, + "required": ["repo"] + }, + "execute": "const repoData = await deps[\"github-api\"]({endpoint: `/repos/${input.repo}`}); const summary = await deps[\"translate\"]({text: repoData.description, lang: \"zh\"}); return {name: repoData.name, description_cn: summary, stars: repoData.stargazers_count};", + "type": "normal", + "description": "获取仓库信息并翻译描述", + "requires": ["github-api", "translate"] + }' +``` + +## 3. 链式依赖 + +### A depends on B, B depends on C + +```bash +# 部署 C (基础服务) +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "base-logger", + "execute": "return `[LOG] ${new Date().toISOString()}`;", + "type": "normal" + }' + +# 部署 B (中间层,依赖 C) +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "middleware", + "schema": { + "type": "object", + "properties": { + "action": {"type": "string"} + } + }, + "execute": "const log = await deps[\"base-logger\"](); return `${log} [MIDDLEWARE] ${input.action || \"unknown\"}`;", + "type": "normal", + "requires": ["base-logger"] + }' + +# 部署 A (顶层,依赖 B) +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "api-handler", + "schema": { + "type": "object", + "properties": { + "request": {"type": "string"} + } + }, + "execute": "const mid = await deps[\"middleware\"]({action: \"handle_request\"}); return `${mid} [API] Processing: ${input.request}`;", + "type": "normal", + "requires": ["middleware"] + }' +``` + +## 4. 调用示例 + +```bash +# 调用 repo-summary (会自动使用其所有依赖) +curl "https://sigil.example.com/run/repo-summary?repo=microsoft/vscode" + +# 调用 api-handler (会自动使用链式依赖) +curl "https://sigil.example.com/run/api-handler?request=get_user_data" +``` + +## 5. 生成的代码结构 + +当使用 `requires` 时,生成的 Worker 代码包含: + +```javascript +export default { + async fetch(request) { + // ... 输入解析 ... + + // AMD deps - 每个依赖内联为函数 + const deps = { + 'github-token': async (params = {}) => { + const input = params; + return "ghp_example_token"; + }, + 'translate': async (params = {}) => { + const input = {}; + if (params.text !== undefined) input.text = params.text; + if (params.lang !== undefined) input.lang = params.lang; + if (input.lang === undefined) input.lang = "zh"; + return `[${input.lang}] ${input.text}`; + } + }; + + // Execute user function (with deps) + const __result = await (async (input, deps) => { + const repoData = await deps["github-api"]({endpoint: `/repos/${input.repo}`}); + const summary = await deps["translate"]({text: repoData.description, lang: "zh"}); + return {name: repoData.name, description_cn: summary, stars: repoData.stargazers_count}; + })(input, deps); + + // ... 输出处理 ... + } +}; +``` + +## 6. 循环依赖检测 + +尝试创建循环依赖会被自动检测并阻止: + +```bash +# 这会失败,因为会创建 A -> A 循环 +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "self-ref", + "execute": "const self = await deps[\"self-ref\"](); return self;", + "type": "normal", + "requires": ["self-ref"] + }' +``` + +错误响应: +```json +{ + "error": "Failed to resolve dependencies: Circular dependency detected: self-ref -> self-ref" +} +``` + +## 特性总结 + +✅ **依赖声明**: `requires` 字段声明依赖关系 +✅ **自动 bundle**: 依赖代码自动内联到主 capability +✅ **循环检测**: 防止无限递归的循环依赖 +✅ **参数解析**: 依赖支持 schema 参数验证 +✅ **向后兼容**: 现有 capability 不受影响 +✅ **递归解析**: 支持多层依赖链(A->B->C) +✅ **类型安全**: TypeScript 严格模式支持 \ No newline at end of file diff --git a/src/backend/worker-pool.ts b/src/backend/worker-pool.ts index 2bf0da4..0b6e68d 100644 --- a/src/backend/worker-pool.ts +++ b/src/backend/worker-pool.ts @@ -71,18 +71,17 @@ export class WorkerPool implements SigilBackend { Object.assign(deps, nestedDeps) } - // 对于 schema+execute 模式的依赖,从 code 中提取 execute body + // Use stored execute body if available, fall back to extraction from code + const executeBody = depMeta.execute || this.extractExecuteBodyFromWorkerCode(depCode) + if (depMeta.schema) { - // 从生成的 worker code 中提取用户的 execute body - // 这个比较 hacky,实际应该存储原始的 execute body - // 但为了兼容现有代码,我们从生成的代码中反向提取 deps[depName] = { - code: this.extractExecuteBodyFromWorkerCode(depCode), + code: executeBody, schema: depMeta.schema } - } else { - // 完整代码模式,直接使用 - deps[depName] = { code: depCode } + } else { + // Code mode or no schema — use stored execute or full code + deps[depName] = { code: depMeta.execute || depCode } } } @@ -169,6 +168,7 @@ export class WorkerPool implements SigilBackend { await this.kv.setCode(capability, finalCode) await this.kv.setMeta(capability, { type, ttl, created_at: now, bindings, description, tags, examples, schema, requires, + execute: execute || undefined, }) await this.kv.setLru(capability, { last_access: now, access_count: 0, deployed: true }) await this.kv.setRoute(capability, { worker_name: capability, subdomain: '' }) diff --git a/src/kv.ts b/src/kv.ts index 7bab160..86a9262 100644 --- a/src/kv.ts +++ b/src/kv.ts @@ -16,6 +16,7 @@ export interface KvMetaValue { examples?: string[] schema?: InputSchema requires?: string[] + execute?: string // original execute body, stored for AMD dependency resolution } export interface KvLruValue { diff --git a/src/router.ts b/src/router.ts index a27345d..5e426e2 100644 --- a/src/router.ts +++ b/src/router.ts @@ -85,8 +85,9 @@ async function handleDeploy(request: Request, env: RouterEnv): Promise return jsonError(400, 'Must specify either code or schema+execute') } - let code: string + let code: string | undefined let schema: InputSchema | undefined + let execute: string | undefined if (body.code) { code = body.code @@ -95,7 +96,8 @@ async function handleDeploy(request: Request, env: RouterEnv): Promise return jsonError(400, 'execute is required when using schema mode') } schema = body.schema || { type: 'object', properties: {} } - code = generateWorkerCode(schema, body.execute) + execute = body.execute + // Don't codegen here — let backend handle it (supports AMD requires) } // Check deploy cooldown @@ -104,6 +106,7 @@ async function handleDeploy(request: Request, env: RouterEnv): Promise const result = await env.backend.deploy({ name: body.name, code, + execute, schema, type: body.type, ttl: body.ttl,