Files
Arcrun/n8n-nodes-kbdb/nodes/Kbdb/actions.ts
T
uncle6me-web 87f6de7c9f feat(n8n): KBDB community node 薄殼——對照表 那半的八個動作
leo 2026-08-15「有效才正式開發」下一格:紙上考卷驗過的動作集做成真的 n8n 節點。
詞彙照定案(sheet/field/record,InkStoneCo system-dev/wiki/kbdb-詞彙表.md)。

- 動作 ↔ API 映射收斂在 nodes/Kbdb/actions.ts(純函式),20 個 vitest 釘死
  ——就是動作對照表  那半的機器版;🔴/◐ 的動作刻意不存在,呼叫丟
  「沒有這個能力」(07-thin-shell §3.1:API 沒有的能力不拼裝)
- 對外走 cypher-executor /kbdb/* proxy(X-Arcrun-API-Key),不直連 kbdb worker
- Add a field 不掛:proxy 無 PATCH /kbdb/templates(實測 404);base 端
  PATCH /templates/:id 語意是 slots 整組覆蓋(本地實測),不是加一欄

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 19:42:53 +08:00

88 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* KBDB 動作 → HTTP 請求的純映射(薄殼,rule 07)。
*
* 每個動作=**恰好一個** API 呼叫。這個檔案刻意是純函式,讓「動作對到哪支 API」
* 可以被單元測試釘死——它就是 `system-dev/docs/4-guides/kbdb-動作對照表.md`(頂層
* InkStoneCo)標 ✅ 那半的機器版。
*
* 🔴 禁止在這裡拼裝:對照表標 🔴/◐ 而 API 沒有的能力(Archive、關係整組、
* Add a field),這裡**沒有**對應動作——不是漏做,是「回報沒有能力」的實作方式
* (見 .claude/rules/07-thin-shell.md §3.1D91/D92 正是拼裝出來的)。
*
* 對外的門=cypher-executor 的 /kbdb/* proxyX-Arcrun-API-Key 租戶隔離)。
* KBDB worker 本體只收系統內部的 Bearer,外面打一定 401(設計,不是故障)。
*/
export interface KbdbRequest {
method: 'GET' | 'POST' | 'PATCH';
path: string;
body?: Record<string, unknown>;
qs?: Record<string, string>;
}
export interface ActionParams {
sheet?: string;
recordId?: string;
values?: Record<string, string>;
slots?: string[];
description?: string;
query?: string;
mode?: 'keyword' | 'semantic';
filters?: { library?: string; source?: string; entry_type?: string };
}
function need<T>(v: T | undefined | null, what: string): T {
if (v === undefined || v === null || (typeof v === 'string' && v === '')) {
throw new Error(`缺少必要參數:${what}`);
}
return v;
}
export function buildRequest(resource: string, operation: string, p: ActionParams): KbdbRequest {
const key = `${resource}.${operation}`;
switch (key) {
// ── record ─────────────────────────────────────────────
case 'record.append':
return {
method: 'POST',
path: '/kbdb/records',
body: { template: need(p.sheet, 'sheet'), values: need(p.values, 'values') },
};
case 'record.get':
return { method: 'GET', path: `/kbdb/records/${encodeURIComponent(need(p.recordId, 'recordId'))}` };
case 'record.getMany':
return { method: 'GET', path: `/kbdb/records/by-template/${encodeURIComponent(need(p.sheet, 'sheet'))}` };
case 'record.update':
return {
method: 'PATCH',
path: `/kbdb/records/${encodeURIComponent(need(p.recordId, 'recordId'))}`,
body: { values: need(p.values, 'values') },
};
// ── sheet ──────────────────────────────────────────────
case 'sheet.create': {
const slots = need(p.slots, 'fields');
if (!Array.isArray(slots) || slots.length === 0) throw new Error('fields 至少要有一欄');
const body: Record<string, unknown> = { name: need(p.sheet, 'sheet'), slots };
if (p.description) body.description = p.description;
return { method: 'POST', path: '/kbdb/templates', body };
}
case 'sheet.getSchema':
return { method: 'GET', path: `/kbdb/templates/${encodeURIComponent(need(p.sheet, 'sheet'))}` };
case 'sheet.list':
return { method: 'GET', path: '/kbdb/templates' };
// ── search ─────────────────────────────────────────────
case 'search.search': {
const qs: Record<string, string> = { q: need(p.query, 'query') };
if (p.mode) qs.mode = p.mode;
for (const k of ['library', 'source', 'entry_type'] as const) {
const v = p.filters?.[k];
if (v) qs[k] = v;
}
return { method: 'GET', path: '/kbdb/search', qs };
}
default:
// 誠實回報,不拼裝(07-thin-shell §3.1;對照表「怎麼用這張表」第 2 條)
throw new Error(`KBDB 目前沒有「${key}」這個能力(API 端不存在,薄殼不拼裝)`);
}
}