feat(cypher-executor): 同步查詢 trigger + graph_neighbors 查詢面 workflow 示範 #28

Merged
Leo merged 4 commits from feat/sync-query-trigger into main 2026-07-07 09:39:19 +00:00
2 changed files with 61 additions and 0 deletions
Showing only changes of commit 06ff0e21ce - Show all commits
@@ -36,6 +36,11 @@ import type { Bindings, ComponentRunner, ServiceBinding } from '../types';
const WASM_HTTP_RUNNER_IDS: ReadonlySet<string> = new Set([
// 通用 HTTP 零件
'http_request',
// 通用 code 零件(sandbox inline JSArcrun#10 / 07-thin-shell §3.5 code-node):獨立 Worker
// URL 走 wasmWorkerUrl 通用推導(arcrun-code.{WORKER_SUBDOMAIN}.workers.dev
// self-hosted 由 WORKER_SUBDOMAIN var 注入自己的 subdomain,無寫死官方域名)。
// 漏這行 = workflow 寫 `component: code` 落到 step 8 直接「找不到零件」(#29 發現)。
'code',
// gmail / telegram / line_notify / google_sheets 已降級為 recipe2026-05-29 Phase 2):
// recipe:gmail_send / telegram_send / line_notify_send / google_sheets_read|append
// 走 step 6 KV recipe 解析,不再是零件。零件目錄已刪。
@@ -0,0 +1,56 @@
/**
* component-loader`code` 零件解析測試(#29 發現的白名單缺口)。
*
* 背景:workflow 寫 `component: code` 時,component-loader 解析鏈 step 1-6 都不命中
* (非 builtin / 非 URL / 非 hash / 非邏輯零件 binding / 非 recipe),必須靠 step 7 的
* WASM_HTTP_RUNNER_IDS 白名單 → wasmWorkerUrl 通用推導。白名單漏 `code` 就會落到
* step 8 直接丟「找不到零件」——graph_neighbors 等用 code 節點的 workflow 全滅。
*
* 驗證:
* 1. `code` 能被解析成 runner(不 throw)。
* 2. runner 打的 URL = arcrun-code.{WORKER_SUBDOMAIN}.workers.dev(通用推導、無寫死
* 官方 code.arcrun.dev → self-hosted 換 WORKER_SUBDOMAIN 即落地)。
*/
import { describe, it, expect, vi, afterEach } from 'vitest';
import { env } from 'cloudflare:test';
import { createComponentLoader, wasmWorkerUrl } from '../src/lib/component-loader';
import type { Bindings } from '../src/types';
afterEach(() => {
vi.unstubAllGlobals();
});
describe('component-loadercode 零件解析(WASM HTTP runner 白名單)', () => {
it('wasmWorkerUrl 通用推導:code → arcrun-code.{subdomain}.workers.devself-hosted 換 subdomain 即成立)', () => {
expect(wasmWorkerUrl('code', 'uncle6-me')).toBe('https://arcrun-code.uncle6-me.workers.dev');
expect(wasmWorkerUrl('code', 'my-selfhosted-sub')).toBe('https://arcrun-code.my-selfhosted-sub.workers.dev');
});
it('component: code 能被解析成 runner,且 fetch 打 subdomain 推導的 URL', async () => {
const fakeEnv = {
...env,
WORKER_SUBDOMAIN: 'test-sub',
} as unknown as Bindings;
const loader = createComponentLoader(fakeEnv);
// 白名單漏 `code` 時這裡就 throw「找不到零件 "code"」(#29 的故障型態)
const runner = await loader('code');
expect(typeof runner).toBe('function');
// stub fetch 捕 URL:證明 runner 打的是通用推導 URL,非寫死官方域名
const calledUrls: string[] = [];
const fetchSpy = vi.fn(async (url: unknown) => {
calledUrls.push(String(url));
return new Response(JSON.stringify({ success: true, data: { ok: true } }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
});
vi.stubGlobal('fetch', fetchSpy);
const result = await runner({ code: 'return {ok:true}', input: {} }) as Record<string, unknown>;
expect(fetchSpy).toHaveBeenCalledTimes(1);
expect(calledUrls[0]).toBe('https://arcrun-code.test-sub.workers.dev');
expect(result.success).toBe(true);
});
});