var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/compose.js
var compose;
var init_compose = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/compose.js"() {
compose = (middleware, onError, onNotFound) => {
return (context, next) => {
let index = -1;
return dispatch(0);
async function dispatch(i) {
if (i <= index) {
throw new Error("next() called multiple times");
}
index = i;
let res;
let isError = false;
let handler;
if (middleware[i]) {
handler = middleware[i][0][0];
context.req.routeIndex = i;
} else {
handler = i === middleware.length && next || void 0;
}
if (handler) {
try {
res = await handler(context, () => dispatch(i + 1));
} catch (err) {
if (err instanceof Error && onError) {
context.error = err;
res = await onError(err, context);
isError = true;
} else {
throw err;
}
}
} else {
if (context.finalized === false && onNotFound) {
res = await onNotFound(context);
}
}
if (res && (context.finalized === false || isError)) {
context.res = res;
}
return context;
}
};
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/http-exception.js
var init_http_exception = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/http-exception.js"() {
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/request/constants.js
var GET_MATCH_RESULT;
var init_constants = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/request/constants.js"() {
GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/utils/body.js
async function parseFormData(request, options) {
const formData = await request.formData();
if (formData) {
return convertFormDataToBodyData(formData, options);
}
return {};
}
function convertFormDataToBodyData(formData, options) {
const form = /* @__PURE__ */ Object.create(null);
formData.forEach((value, key) => {
const shouldParseAllValues = options.all || key.endsWith("[]");
if (!shouldParseAllValues) {
form[key] = value;
} else {
handleParsingAllValues(form, key, value);
}
});
if (options.dot) {
Object.entries(form).forEach(([key, value]) => {
const shouldParseDotValues = key.includes(".");
if (shouldParseDotValues) {
handleParsingNestedValues(form, key, value);
delete form[key];
}
});
}
return form;
}
var parseBody, handleParsingAllValues, handleParsingNestedValues;
var init_body = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/utils/body.js"() {
init_request();
parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
const { all = false, dot = false } = options;
const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;
const contentType = headers.get("Content-Type");
if (contentType?.startsWith("multipart/form-data") || contentType?.startsWith("application/x-www-form-urlencoded")) {
return parseFormData(request, { all, dot });
}
return {};
};
handleParsingAllValues = (form, key, value) => {
if (form[key] !== void 0) {
if (Array.isArray(form[key])) {
;
form[key].push(value);
} else {
form[key] = [form[key], value];
}
} else {
if (!key.endsWith("[]")) {
form[key] = value;
} else {
form[key] = [value];
}
}
};
handleParsingNestedValues = (form, key, value) => {
if (/(?:^|\.)__proto__\./.test(key)) {
return;
}
let nestedForm = form;
const keys = key.split(".");
keys.forEach((key2, index) => {
if (index === keys.length - 1) {
nestedForm[key2] = value;
} else {
if (!nestedForm[key2] || typeof nestedForm[key2] !== "object" || Array.isArray(nestedForm[key2]) || nestedForm[key2] instanceof File) {
nestedForm[key2] = /* @__PURE__ */ Object.create(null);
}
nestedForm = nestedForm[key2];
}
});
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/utils/url.js
var splitPath, splitRoutingPath, extractGroupsFromPath, replaceGroupMarks, patternCache, getPattern, tryDecode, tryDecodeURI, getPath, getPathNoStrict, mergePath, checkOptionalParameter, _decodeURI, _getQueryParam, getQueryParam, getQueryParams, decodeURIComponent_;
var init_url = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/utils/url.js"() {
splitPath = (path) => {
const paths = path.split("/");
if (paths[0] === "") {
paths.shift();
}
return paths;
};
splitRoutingPath = (routePath) => {
const { groups, path } = extractGroupsFromPath(routePath);
const paths = splitPath(path);
return replaceGroupMarks(paths, groups);
};
extractGroupsFromPath = (path) => {
const groups = [];
path = path.replace(/\{[^}]+\}/g, (match2, index) => {
const mark = `@${index}`;
groups.push([mark, match2]);
return mark;
});
return { groups, path };
};
replaceGroupMarks = (paths, groups) => {
for (let i = groups.length - 1; i >= 0; i--) {
const [mark] = groups[i];
for (let j = paths.length - 1; j >= 0; j--) {
if (paths[j].includes(mark)) {
paths[j] = paths[j].replace(mark, groups[i][1]);
break;
}
}
}
return paths;
};
patternCache = {};
getPattern = (label, next) => {
if (label === "*") {
return "*";
}
const match2 = label.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
if (match2) {
const cacheKey = `${label}#${next}`;
if (!patternCache[cacheKey]) {
if (match2[2]) {
patternCache[cacheKey] = next && next[0] !== ":" && next[0] !== "*" ? [cacheKey, match2[1], new RegExp(`^${match2[2]}(?=/${next})`)] : [label, match2[1], new RegExp(`^${match2[2]}$`)];
} else {
patternCache[cacheKey] = [label, match2[1], true];
}
}
return patternCache[cacheKey];
}
return null;
};
tryDecode = (str2, decoder) => {
try {
return decoder(str2);
} catch {
return str2.replace(/(?:%[0-9A-Fa-f]{2})+/g, (match2) => {
try {
return decoder(match2);
} catch {
return match2;
}
});
}
};
tryDecodeURI = (str2) => tryDecode(str2, decodeURI);
getPath = (request) => {
const url = request.url;
const start = url.indexOf("/", url.indexOf(":") + 4);
let i = start;
for (; i < url.length; i++) {
const charCode = url.charCodeAt(i);
if (charCode === 37) {
const queryIndex = url.indexOf("?", i);
const hashIndex = url.indexOf("#", i);
const end = queryIndex === -1 ? hashIndex === -1 ? void 0 : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex);
const path = url.slice(start, end);
return tryDecodeURI(path.includes("%25") ? path.replace(/%25/g, "%2525") : path);
} else if (charCode === 63 || charCode === 35) {
break;
}
}
return url.slice(start, i);
};
getPathNoStrict = (request) => {
const result = getPath(request);
return result.length > 1 && result.at(-1) === "/" ? result.slice(0, -1) : result;
};
mergePath = (base, sub, ...rest) => {
if (rest.length) {
sub = mergePath(sub, ...rest);
}
return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`;
};
checkOptionalParameter = (path) => {
if (path.charCodeAt(path.length - 1) !== 63 || !path.includes(":")) {
return null;
}
const segments = path.split("/");
const results = [];
let basePath = "";
segments.forEach((segment) => {
if (segment !== "" && !/\:/.test(segment)) {
basePath += "/" + segment;
} else if (/\:/.test(segment)) {
if (/\?/.test(segment)) {
if (results.length === 0 && basePath === "") {
results.push("/");
} else {
results.push(basePath);
}
const optionalSegment = segment.replace("?", "");
basePath += "/" + optionalSegment;
results.push(basePath);
} else {
basePath += "/" + segment;
}
}
});
return results.filter((v, i, a) => a.indexOf(v) === i);
};
_decodeURI = (value) => {
if (!/[%+]/.test(value)) {
return value;
}
if (value.indexOf("+") !== -1) {
value = value.replace(/\+/g, " ");
}
return value.indexOf("%") !== -1 ? tryDecode(value, decodeURIComponent_) : value;
};
_getQueryParam = (url, key, multiple) => {
let encoded;
if (!multiple && key && !/[%+]/.test(key)) {
let keyIndex2 = url.indexOf("?", 8);
if (keyIndex2 === -1) {
return void 0;
}
if (!url.startsWith(key, keyIndex2 + 1)) {
keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1);
}
while (keyIndex2 !== -1) {
const trailingKeyCode = url.charCodeAt(keyIndex2 + key.length + 1);
if (trailingKeyCode === 61) {
const valueIndex = keyIndex2 + key.length + 2;
const endIndex = url.indexOf("&", valueIndex);
return _decodeURI(url.slice(valueIndex, endIndex === -1 ? void 0 : endIndex));
} else if (trailingKeyCode == 38 || isNaN(trailingKeyCode)) {
return "";
}
keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1);
}
encoded = /[%+]/.test(url);
if (!encoded) {
return void 0;
}
}
const results = {};
encoded ??= /[%+]/.test(url);
let keyIndex = url.indexOf("?", 8);
while (keyIndex !== -1) {
const nextKeyIndex = url.indexOf("&", keyIndex + 1);
let valueIndex = url.indexOf("=", keyIndex);
if (valueIndex > nextKeyIndex && nextKeyIndex !== -1) {
valueIndex = -1;
}
let name = url.slice(
keyIndex + 1,
valueIndex === -1 ? nextKeyIndex === -1 ? void 0 : nextKeyIndex : valueIndex
);
if (encoded) {
name = _decodeURI(name);
}
keyIndex = nextKeyIndex;
if (name === "") {
continue;
}
let value;
if (valueIndex === -1) {
value = "";
} else {
value = url.slice(valueIndex + 1, nextKeyIndex === -1 ? void 0 : nextKeyIndex);
if (encoded) {
value = _decodeURI(value);
}
}
if (multiple) {
if (!(results[name] && Array.isArray(results[name]))) {
results[name] = [];
}
;
results[name].push(value);
} else {
results[name] ??= value;
}
}
return key ? results[key] : results;
};
getQueryParam = _getQueryParam;
getQueryParams = (url, key) => {
return _getQueryParam(url, key, true);
};
decodeURIComponent_ = decodeURIComponent;
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/request.js
var tryDecodeURIComponent, HonoRequest;
var init_request = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/request.js"() {
init_http_exception();
init_constants();
init_body();
init_url();
tryDecodeURIComponent = (str2) => tryDecode(str2, decodeURIComponent_);
HonoRequest = class {
/**
* `.raw` can get the raw Request object.
*
* @see {@link https://hono.dev/docs/api/request#raw}
*
* @example
* ```ts
* // For Cloudflare Workers
* app.post('/', async (c) => {
* const metadata = c.req.raw.cf?.hostMetadata?
* ...
* })
* ```
*/
raw;
#validatedData;
// Short name of validatedData
#matchResult;
routeIndex = 0;
/**
* `.path` can get the pathname of the request.
*
* @see {@link https://hono.dev/docs/api/request#path}
*
* @example
* ```ts
* app.get('/about/me', (c) => {
* const pathname = c.req.path // `/about/me`
* })
* ```
*/
path;
bodyCache = {};
constructor(request, path = "/", matchResult = [[]]) {
this.raw = request;
this.path = path;
this.#matchResult = matchResult;
this.#validatedData = {};
}
param(key) {
return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
}
#getDecodedParam(key) {
const paramKey = this.#matchResult[0][this.routeIndex][1][key];
const param = this.#getParamValue(paramKey);
return param && /\%/.test(param) ? tryDecodeURIComponent(param) : param;
}
#getAllDecodedParams() {
const decoded = {};
const keys = Object.keys(this.#matchResult[0][this.routeIndex][1]);
for (const key of keys) {
const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
if (value !== void 0) {
decoded[key] = /\%/.test(value) ? tryDecodeURIComponent(value) : value;
}
}
return decoded;
}
#getParamValue(paramKey) {
return this.#matchResult[1] ? this.#matchResult[1][paramKey] : paramKey;
}
query(key) {
return getQueryParam(this.url, key);
}
queries(key) {
return getQueryParams(this.url, key);
}
header(name) {
if (name) {
return this.raw.headers.get(name) ?? void 0;
}
const headerData = {};
this.raw.headers.forEach((value, key) => {
headerData[key] = value;
});
return headerData;
}
async parseBody(options) {
return parseBody(this, options);
}
#cachedBody = (key) => {
const { bodyCache, raw: raw2 } = this;
const cachedBody = bodyCache[key];
if (cachedBody) {
return cachedBody;
}
const anyCachedKey = Object.keys(bodyCache)[0];
if (anyCachedKey) {
return bodyCache[anyCachedKey].then((body) => {
if (anyCachedKey === "json") {
body = JSON.stringify(body);
}
return new Response(body)[key]();
});
}
return bodyCache[key] = raw2[key]();
};
/**
* `.json()` can parse Request body of type `application/json`
*
* @see {@link https://hono.dev/docs/api/request#json}
*
* @example
* ```ts
* app.post('/entry', async (c) => {
* const body = await c.req.json()
* })
* ```
*/
json() {
return this.#cachedBody("text").then((text) => JSON.parse(text));
}
/**
* `.text()` can parse Request body of type `text/plain`
*
* @see {@link https://hono.dev/docs/api/request#text}
*
* @example
* ```ts
* app.post('/entry', async (c) => {
* const body = await c.req.text()
* })
* ```
*/
text() {
return this.#cachedBody("text");
}
/**
* `.arrayBuffer()` parse Request body as an `ArrayBuffer`
*
* @see {@link https://hono.dev/docs/api/request#arraybuffer}
*
* @example
* ```ts
* app.post('/entry', async (c) => {
* const body = await c.req.arrayBuffer()
* })
* ```
*/
arrayBuffer() {
return this.#cachedBody("arrayBuffer");
}
/**
* Parses the request body as a `Blob`.
* @example
* ```ts
* app.post('/entry', async (c) => {
* const body = await c.req.blob();
* });
* ```
* @see https://hono.dev/docs/api/request#blob
*/
blob() {
return this.#cachedBody("blob");
}
/**
* Parses the request body as `FormData`.
* @example
* ```ts
* app.post('/entry', async (c) => {
* const body = await c.req.formData();
* });
* ```
* @see https://hono.dev/docs/api/request#formdata
*/
formData() {
return this.#cachedBody("formData");
}
/**
* Adds validated data to the request.
*
* @param target - The target of the validation.
* @param data - The validated data to add.
*/
addValidatedData(target, data) {
this.#validatedData[target] = data;
}
valid(target) {
return this.#validatedData[target];
}
/**
* `.url()` can get the request url strings.
*
* @see {@link https://hono.dev/docs/api/request#url}
*
* @example
* ```ts
* app.get('/about/me', (c) => {
* const url = c.req.url // `http://localhost:8787/about/me`
* ...
* })
* ```
*/
get url() {
return this.raw.url;
}
/**
* `.method()` can get the method name of the request.
*
* @see {@link https://hono.dev/docs/api/request#method}
*
* @example
* ```ts
* app.get('/about/me', (c) => {
* const method = c.req.method // `GET`
* })
* ```
*/
get method() {
return this.raw.method;
}
get [GET_MATCH_RESULT]() {
return this.#matchResult;
}
/**
* `.matchedRoutes()` can return a matched route in the handler
*
* @deprecated
*
* Use matchedRoutes helper defined in "hono/route" instead.
*
* @see {@link https://hono.dev/docs/api/request#matchedroutes}
*
* @example
* ```ts
* app.use('*', async function logger(c, next) {
* await next()
* c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
* const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]')
* console.log(
* method,
* ' ',
* path,
* ' '.repeat(Math.max(10 - path.length, 0)),
* name,
* i === c.req.routeIndex ? '<- respond from here' : ''
* )
* })
* })
* ```
*/
get matchedRoutes() {
return this.#matchResult[0].map(([[, route]]) => route);
}
/**
* `routePath()` can retrieve the path registered within the handler
*
* @deprecated
*
* Use routePath helper defined in "hono/route" instead.
*
* @see {@link https://hono.dev/docs/api/request#routepath}
*
* @example
* ```ts
* app.get('/posts/:id', (c) => {
* return c.json({ path: c.req.routePath })
* })
* ```
*/
get routePath() {
return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path;
}
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/utils/html.js
var HtmlEscapedCallbackPhase, raw, resolveCallback;
var init_html = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/utils/html.js"() {
HtmlEscapedCallbackPhase = {
Stringify: 1,
BeforeStream: 2,
Stream: 3
};
raw = (value, callbacks) => {
const escapedString = new String(value);
escapedString.isEscaped = true;
escapedString.callbacks = callbacks;
return escapedString;
};
resolveCallback = async (str2, phase, preserveCallbacks, context, buffer) => {
if (typeof str2 === "object" && !(str2 instanceof String)) {
if (!(str2 instanceof Promise)) {
str2 = str2.toString();
}
if (str2 instanceof Promise) {
str2 = await str2;
}
}
const callbacks = str2.callbacks;
if (!callbacks?.length) {
return Promise.resolve(str2);
}
if (buffer) {
buffer[0] += str2;
} else {
buffer = [str2];
}
const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context }))).then(
(res) => Promise.all(
res.filter(Boolean).map((str22) => resolveCallback(str22, phase, false, context, buffer))
).then(() => buffer[0])
);
if (preserveCallbacks) {
return raw(await resStr, callbacks);
} else {
return resStr;
}
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/context.js
var TEXT_PLAIN, setDefaultContentType, createResponseInstance, Context;
var init_context = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/context.js"() {
init_request();
init_html();
TEXT_PLAIN = "text/plain; charset=UTF-8";
setDefaultContentType = (contentType, headers) => {
return {
"Content-Type": contentType,
...headers
};
};
createResponseInstance = (body, init) => new Response(body, init);
Context = class {
#rawRequest;
#req;
/**
* `.env` can get bindings (environment variables, secrets, KV namespaces, D1 database, R2 bucket etc.) in Cloudflare Workers.
*
* @see {@link https://hono.dev/docs/api/context#env}
*
* @example
* ```ts
* // Environment object for Cloudflare Workers
* app.get('*', async c => {
* const counter = c.env.COUNTER
* })
* ```
*/
env = {};
#var;
finalized = false;
/**
* `.error` can get the error object from the middleware if the Handler throws an error.
*
* @see {@link https://hono.dev/docs/api/context#error}
*
* @example
* ```ts
* app.use('*', async (c, next) => {
* await next()
* if (c.error) {
* // do something...
* }
* })
* ```
*/
error;
#status;
#executionCtx;
#res;
#layout;
#renderer;
#notFoundHandler;
#preparedHeaders;
#matchResult;
#path;
/**
* Creates an instance of the Context class.
*
* @param req - The Request object.
* @param options - Optional configuration options for the context.
*/
constructor(req, options) {
this.#rawRequest = req;
if (options) {
this.#executionCtx = options.executionCtx;
this.env = options.env;
this.#notFoundHandler = options.notFoundHandler;
this.#path = options.path;
this.#matchResult = options.matchResult;
}
}
/**
* `.req` is the instance of {@link HonoRequest}.
*/
get req() {
this.#req ??= new HonoRequest(this.#rawRequest, this.#path, this.#matchResult);
return this.#req;
}
/**
* @see {@link https://hono.dev/docs/api/context#event}
* The FetchEvent associated with the current request.
*
* @throws Will throw an error if the context does not have a FetchEvent.
*/
get event() {
if (this.#executionCtx && "respondWith" in this.#executionCtx) {
return this.#executionCtx;
} else {
throw Error("This context has no FetchEvent");
}
}
/**
* @see {@link https://hono.dev/docs/api/context#executionctx}
* The ExecutionContext associated with the current request.
*
* @throws Will throw an error if the context does not have an ExecutionContext.
*/
get executionCtx() {
if (this.#executionCtx) {
return this.#executionCtx;
} else {
throw Error("This context has no ExecutionContext");
}
}
/**
* @see {@link https://hono.dev/docs/api/context#res}
* The Response object for the current request.
*/
get res() {
return this.#res ||= createResponseInstance(null, {
headers: this.#preparedHeaders ??= new Headers()
});
}
/**
* Sets the Response object for the current request.
*
* @param _res - The Response object to set.
*/
set res(_res) {
if (this.#res && _res) {
_res = createResponseInstance(_res.body, _res);
for (const [k, v] of this.#res.headers.entries()) {
if (k === "content-type") {
continue;
}
if (k === "set-cookie") {
const cookies = this.#res.headers.getSetCookie();
_res.headers.delete("set-cookie");
for (const cookie of cookies) {
_res.headers.append("set-cookie", cookie);
}
} else {
_res.headers.set(k, v);
}
}
}
this.#res = _res;
this.finalized = true;
}
/**
* `.render()` can create a response within a layout.
*
* @see {@link https://hono.dev/docs/api/context#render-setrenderer}
*
* @example
* ```ts
* app.get('/', (c) => {
* return c.render('Hello!')
* })
* ```
*/
render = (...args) => {
this.#renderer ??= (content) => this.html(content);
return this.#renderer(...args);
};
/**
* Sets the layout for the response.
*
* @param layout - The layout to set.
* @returns The layout function.
*/
setLayout = (layout) => this.#layout = layout;
/**
* Gets the current layout for the response.
*
* @returns The current layout function.
*/
getLayout = () => this.#layout;
/**
* `.setRenderer()` can set the layout in the custom middleware.
*
* @see {@link https://hono.dev/docs/api/context#render-setrenderer}
*
* @example
* ```tsx
* app.use('*', async (c, next) => {
* c.setRenderer((content) => {
* return c.html(
*
*
* {content}
*
*
* )
* })
* await next()
* })
* ```
*/
setRenderer = (renderer) => {
this.#renderer = renderer;
};
/**
* `.header()` can set headers.
*
* @see {@link https://hono.dev/docs/api/context#header}
*
* @example
* ```ts
* app.get('/welcome', (c) => {
* // Set headers
* c.header('X-Message', 'Hello!')
* c.header('Content-Type', 'text/plain')
*
* return c.body('Thank you for coming')
* })
* ```
*/
header = (name, value, options) => {
if (this.finalized) {
this.#res = createResponseInstance(this.#res.body, this.#res);
}
const headers = this.#res ? this.#res.headers : this.#preparedHeaders ??= new Headers();
if (value === void 0) {
headers.delete(name);
} else if (options?.append) {
headers.append(name, value);
} else {
headers.set(name, value);
}
};
status = (status) => {
this.#status = status;
};
/**
* `.set()` can set the value specified by the key.
*
* @see {@link https://hono.dev/docs/api/context#set-get}
*
* @example
* ```ts
* app.use('*', async (c, next) => {
* c.set('message', 'Hono is hot!!')
* await next()
* })
* ```
*/
set = (key, value) => {
this.#var ??= /* @__PURE__ */ new Map();
this.#var.set(key, value);
};
/**
* `.get()` can use the value specified by the key.
*
* @see {@link https://hono.dev/docs/api/context#set-get}
*
* @example
* ```ts
* app.get('/', (c) => {
* const message = c.get('message')
* return c.text(`The message is "${message}"`)
* })
* ```
*/
get = (key) => {
return this.#var ? this.#var.get(key) : void 0;
};
/**
* `.var` can access the value of a variable.
*
* @see {@link https://hono.dev/docs/api/context#var}
*
* @example
* ```ts
* const result = c.var.client.oneMethod()
* ```
*/
// c.var.propName is a read-only
get var() {
if (!this.#var) {
return {};
}
return Object.fromEntries(this.#var);
}
#newResponse(data, arg, headers) {
const responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders ?? new Headers();
if (typeof arg === "object" && "headers" in arg) {
const argHeaders = arg.headers instanceof Headers ? arg.headers : new Headers(arg.headers);
for (const [key, value] of argHeaders) {
if (key.toLowerCase() === "set-cookie") {
responseHeaders.append(key, value);
} else {
responseHeaders.set(key, value);
}
}
}
if (headers) {
for (const [k, v] of Object.entries(headers)) {
if (typeof v === "string") {
responseHeaders.set(k, v);
} else {
responseHeaders.delete(k);
for (const v2 of v) {
responseHeaders.append(k, v2);
}
}
}
}
const status = typeof arg === "number" ? arg : arg?.status ?? this.#status;
return createResponseInstance(data, { status, headers: responseHeaders });
}
newResponse = (...args) => this.#newResponse(...args);
/**
* `.body()` can return the HTTP response.
* You can set headers with `.header()` and set HTTP status code with `.status`.
* This can also be set in `.text()`, `.json()` and so on.
*
* @see {@link https://hono.dev/docs/api/context#body}
*
* @example
* ```ts
* app.get('/welcome', (c) => {
* // Set headers
* c.header('X-Message', 'Hello!')
* c.header('Content-Type', 'text/plain')
* // Set HTTP status code
* c.status(201)
*
* // Return the response body
* return c.body('Thank you for coming')
* })
* ```
*/
body = (data, arg, headers) => this.#newResponse(data, arg, headers);
/**
* `.text()` can render text as `Content-Type:text/plain`.
*
* @see {@link https://hono.dev/docs/api/context#text}
*
* @example
* ```ts
* app.get('/say', (c) => {
* return c.text('Hello!')
* })
* ```
*/
text = (text, arg, headers) => {
return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text) : this.#newResponse(
text,
arg,
setDefaultContentType(TEXT_PLAIN, headers)
);
};
/**
* `.json()` can render JSON as `Content-Type:application/json`.
*
* @see {@link https://hono.dev/docs/api/context#json}
*
* @example
* ```ts
* app.get('/api', (c) => {
* return c.json({ message: 'Hello!' })
* })
* ```
*/
json = (object, arg, headers) => {
return this.#newResponse(
JSON.stringify(object),
arg,
setDefaultContentType("application/json", headers)
);
};
html = (html, arg, headers) => {
const res = (html2) => this.#newResponse(html2, arg, setDefaultContentType("text/html; charset=UTF-8", headers));
return typeof html === "object" ? resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res) : res(html);
};
/**
* `.redirect()` can Redirect, default status code is 302.
*
* @see {@link https://hono.dev/docs/api/context#redirect}
*
* @example
* ```ts
* app.get('/redirect', (c) => {
* return c.redirect('/')
* })
* app.get('/redirect-permanently', (c) => {
* return c.redirect('/', 301)
* })
* ```
*/
redirect = (location, status) => {
const locationString = String(location);
this.header(
"Location",
// Multibyes should be encoded
// eslint-disable-next-line no-control-regex
!/[^\x00-\xFF]/.test(locationString) ? locationString : encodeURI(locationString)
);
return this.newResponse(null, status ?? 302);
};
/**
* `.notFound()` can return the Not Found Response.
*
* @see {@link https://hono.dev/docs/api/context#notfound}
*
* @example
* ```ts
* app.get('/notfound', (c) => {
* return c.notFound()
* })
* ```
*/
notFound = () => {
this.#notFoundHandler ??= () => createResponseInstance();
return this.#notFoundHandler(this);
};
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router.js
var METHOD_NAME_ALL, METHOD_NAME_ALL_LOWERCASE, METHODS, MESSAGE_MATCHER_IS_ALREADY_BUILT, UnsupportedPathError;
var init_router = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router.js"() {
METHOD_NAME_ALL = "ALL";
METHOD_NAME_ALL_LOWERCASE = "all";
METHODS = ["get", "post", "put", "delete", "options", "patch"];
MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built.";
UnsupportedPathError = class extends Error {
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/utils/constants.js
var COMPOSED_HANDLER;
var init_constants2 = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/utils/constants.js"() {
COMPOSED_HANDLER = "__COMPOSED_HANDLER";
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/hono-base.js
var notFoundHandler, errorHandler, Hono;
var init_hono_base = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/hono-base.js"() {
init_compose();
init_context();
init_router();
init_constants2();
init_url();
notFoundHandler = (c) => {
return c.text("404 Not Found", 404);
};
errorHandler = (err, c) => {
if ("getResponse" in err) {
const res = err.getResponse();
return c.newResponse(res.body, res);
}
console.error(err);
return c.text("Internal Server Error", 500);
};
Hono = class _Hono {
get;
post;
put;
delete;
options;
patch;
all;
on;
use;
/*
This class is like an abstract class and does not have a router.
To use it, inherit the class and implement router in the constructor.
*/
router;
getPath;
// Cannot use `#` because it requires visibility at JavaScript runtime.
_basePath = "/";
#path = "/";
routes = [];
constructor(options = {}) {
const allMethods = [...METHODS, METHOD_NAME_ALL_LOWERCASE];
allMethods.forEach((method) => {
this[method] = (args1, ...args) => {
if (typeof args1 === "string") {
this.#path = args1;
} else {
this.#addRoute(method, this.#path, args1);
}
args.forEach((handler) => {
this.#addRoute(method, this.#path, handler);
});
return this;
};
});
this.on = (method, path, ...handlers) => {
for (const p of [path].flat()) {
this.#path = p;
for (const m of [method].flat()) {
handlers.map((handler) => {
this.#addRoute(m.toUpperCase(), this.#path, handler);
});
}
}
return this;
};
this.use = (arg1, ...handlers) => {
if (typeof arg1 === "string") {
this.#path = arg1;
} else {
this.#path = "*";
handlers.unshift(arg1);
}
handlers.forEach((handler) => {
this.#addRoute(METHOD_NAME_ALL, this.#path, handler);
});
return this;
};
const { strict, ...optionsWithoutStrict } = options;
Object.assign(this, optionsWithoutStrict);
this.getPath = strict ?? true ? options.getPath ?? getPath : getPathNoStrict;
}
#clone() {
const clone = new _Hono({
router: this.router,
getPath: this.getPath
});
clone.errorHandler = this.errorHandler;
clone.#notFoundHandler = this.#notFoundHandler;
clone.routes = this.routes;
return clone;
}
#notFoundHandler = notFoundHandler;
// Cannot use `#` because it requires visibility at JavaScript runtime.
errorHandler = errorHandler;
/**
* `.route()` allows grouping other Hono instance in routes.
*
* @see {@link https://hono.dev/docs/api/routing#grouping}
*
* @param {string} path - base Path
* @param {Hono} app - other Hono instance
* @returns {Hono} routed Hono instance
*
* @example
* ```ts
* const app = new Hono()
* const app2 = new Hono()
*
* app2.get("/user", (c) => c.text("user"))
* app.route("/api", app2) // GET /api/user
* ```
*/
route(path, app2) {
const subApp = this.basePath(path);
app2.routes.map((r) => {
let handler;
if (app2.errorHandler === errorHandler) {
handler = r.handler;
} else {
handler = async (c, next) => (await compose([], app2.errorHandler)(c, () => r.handler(c, next))).res;
handler[COMPOSED_HANDLER] = r.handler;
}
subApp.#addRoute(r.method, r.path, handler);
});
return this;
}
/**
* `.basePath()` allows base paths to be specified.
*
* @see {@link https://hono.dev/docs/api/routing#base-path}
*
* @param {string} path - base Path
* @returns {Hono} changed Hono instance
*
* @example
* ```ts
* const api = new Hono().basePath('/api')
* ```
*/
basePath(path) {
const subApp = this.#clone();
subApp._basePath = mergePath(this._basePath, path);
return subApp;
}
/**
* `.onError()` handles an error and returns a customized Response.
*
* @see {@link https://hono.dev/docs/api/hono#error-handling}
*
* @param {ErrorHandler} handler - request Handler for error
* @returns {Hono} changed Hono instance
*
* @example
* ```ts
* app.onError((err, c) => {
* console.error(`${err}`)
* return c.text('Custom Error Message', 500)
* })
* ```
*/
onError = (handler) => {
this.errorHandler = handler;
return this;
};
/**
* `.notFound()` allows you to customize a Not Found Response.
*
* @see {@link https://hono.dev/docs/api/hono#not-found}
*
* @param {NotFoundHandler} handler - request handler for not-found
* @returns {Hono} changed Hono instance
*
* @example
* ```ts
* app.notFound((c) => {
* return c.text('Custom 404 Message', 404)
* })
* ```
*/
notFound = (handler) => {
this.#notFoundHandler = handler;
return this;
};
/**
* `.mount()` allows you to mount applications built with other frameworks into your Hono application.
*
* @see {@link https://hono.dev/docs/api/hono#mount}
*
* @param {string} path - base Path
* @param {Function} applicationHandler - other Request Handler
* @param {MountOptions} [options] - options of `.mount()`
* @returns {Hono} mounted Hono instance
*
* @example
* ```ts
* import { Router as IttyRouter } from 'itty-router'
* import { Hono } from 'hono'
* // Create itty-router application
* const ittyRouter = IttyRouter()
* // GET /itty-router/hello
* ittyRouter.get('/hello', () => new Response('Hello from itty-router'))
*
* const app = new Hono()
* app.mount('/itty-router', ittyRouter.handle)
* ```
*
* @example
* ```ts
* const app = new Hono()
* // Send the request to another application without modification.
* app.mount('/app', anotherApp, {
* replaceRequest: (req) => req,
* })
* ```
*/
mount(path, applicationHandler, options) {
let replaceRequest;
let optionHandler;
if (options) {
if (typeof options === "function") {
optionHandler = options;
} else {
optionHandler = options.optionHandler;
if (options.replaceRequest === false) {
replaceRequest = (request) => request;
} else {
replaceRequest = options.replaceRequest;
}
}
}
const getOptions = optionHandler ? (c) => {
const options2 = optionHandler(c);
return Array.isArray(options2) ? options2 : [options2];
} : (c) => {
let executionContext = void 0;
try {
executionContext = c.executionCtx;
} catch {
}
return [c.env, executionContext];
};
replaceRequest ||= (() => {
const mergedPath = mergePath(this._basePath, path);
const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
return (request) => {
const url = new URL(request.url);
url.pathname = url.pathname.slice(pathPrefixLength) || "/";
return new Request(url, request);
};
})();
const handler = async (c, next) => {
const res = await applicationHandler(replaceRequest(c.req.raw), ...getOptions(c));
if (res) {
return res;
}
await next();
};
this.#addRoute(METHOD_NAME_ALL, mergePath(path, "*"), handler);
return this;
}
#addRoute(method, path, handler) {
method = method.toUpperCase();
path = mergePath(this._basePath, path);
const r = { basePath: this._basePath, path, method, handler };
this.router.add(method, path, [handler, r]);
this.routes.push(r);
}
#handleError(err, c) {
if (err instanceof Error) {
return this.errorHandler(err, c);
}
throw err;
}
#dispatch(request, executionCtx, env, method) {
if (method === "HEAD") {
return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))();
}
const path = this.getPath(request, { env });
const matchResult = this.router.match(method, path);
const c = new Context(request, {
path,
matchResult,
env,
executionCtx,
notFoundHandler: this.#notFoundHandler
});
if (matchResult[0].length === 1) {
let res;
try {
res = matchResult[0][0][0][0](c, async () => {
c.res = await this.#notFoundHandler(c);
});
} catch (err) {
return this.#handleError(err, c);
}
return res instanceof Promise ? res.then(
(resolved) => resolved || (c.finalized ? c.res : this.#notFoundHandler(c))
).catch((err) => this.#handleError(err, c)) : res ?? this.#notFoundHandler(c);
}
const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler);
return (async () => {
try {
const context = await composed(c);
if (!context.finalized) {
throw new Error(
"Context is not finalized. Did you forget to return a Response object or `await next()`?"
);
}
return context.res;
} catch (err) {
return this.#handleError(err, c);
}
})();
}
/**
* `.fetch()` will be entry point of your app.
*
* @see {@link https://hono.dev/docs/api/hono#fetch}
*
* @param {Request} request - request Object of request
* @param {Env} Env - env Object
* @param {ExecutionContext} - context of execution
* @returns {Response | Promise} response of request
*
*/
fetch = (request, ...rest) => {
return this.#dispatch(request, rest[1], rest[0], request.method);
};
/**
* `.request()` is a useful method for testing.
* You can pass a URL or pathname to send a GET request.
* app will return a Response object.
* ```ts
* test('GET /hello is ok', async () => {
* const res = await app.request('/hello')
* expect(res.status).toBe(200)
* })
* ```
* @see https://hono.dev/docs/api/hono#request
*/
request = (input, requestInit, Env, executionCtx) => {
if (input instanceof Request) {
return this.fetch(requestInit ? new Request(input, requestInit) : input, Env, executionCtx);
}
input = input.toString();
return this.fetch(
new Request(
/^https?:\/\//.test(input) ? input : `http://localhost${mergePath("/", input)}`,
requestInit
),
Env,
executionCtx
);
};
/**
* `.fire()` automatically adds a global fetch event listener.
* This can be useful for environments that adhere to the Service Worker API, such as non-ES module Cloudflare Workers.
* @deprecated
* Use `fire` from `hono/service-worker` instead.
* ```ts
* import { Hono } from 'hono'
* import { fire } from 'hono/service-worker'
*
* const app = new Hono()
* // ...
* fire(app)
* ```
* @see https://hono.dev/docs/api/hono#fire
* @see https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
* @see https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/
*/
fire = () => {
addEventListener("fetch", (event) => {
event.respondWith(this.#dispatch(event.request, event, void 0, event.request.method));
});
};
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/matcher.js
function match(method, path) {
const matchers = this.buildAllMatchers();
const match2 = (method2, path2) => {
const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
const staticMatch = matcher[2][path2];
if (staticMatch) {
return staticMatch;
}
const match3 = path2.match(matcher[0]);
if (!match3) {
return [[], emptyParam];
}
const index = match3.indexOf("", 1);
return [matcher[1][index], match3];
};
this.match = match2;
return match2(method, path);
}
var emptyParam;
var init_matcher = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/matcher.js"() {
init_router();
emptyParam = [];
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/node.js
function compareKey(a, b) {
if (a.length === 1) {
return b.length === 1 ? a < b ? -1 : 1 : -1;
}
if (b.length === 1) {
return 1;
}
if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {
return 1;
} else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {
return -1;
}
if (a === LABEL_REG_EXP_STR) {
return 1;
} else if (b === LABEL_REG_EXP_STR) {
return -1;
}
return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length;
}
var LABEL_REG_EXP_STR, ONLY_WILDCARD_REG_EXP_STR, TAIL_WILDCARD_REG_EXP_STR, PATH_ERROR, regExpMetaChars, Node;
var init_node = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/node.js"() {
LABEL_REG_EXP_STR = "[^/]+";
ONLY_WILDCARD_REG_EXP_STR = ".*";
TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
PATH_ERROR = /* @__PURE__ */ Symbol();
regExpMetaChars = new Set(".\\+*[^]$()");
Node = class _Node {
#index;
#varIndex;
#children = /* @__PURE__ */ Object.create(null);
insert(tokens, index, paramMap, context, pathErrorCheckOnly) {
if (tokens.length === 0) {
if (this.#index !== void 0) {
throw PATH_ERROR;
}
if (pathErrorCheckOnly) {
return;
}
this.#index = index;
return;
}
const [token, ...restTokens] = tokens;
const pattern = token === "*" ? restTokens.length === 0 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
let node;
if (pattern) {
const name = pattern[1];
let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
if (name && pattern[2]) {
if (regexpStr === ".*") {
throw PATH_ERROR;
}
regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
if (/\((?!\?:)/.test(regexpStr)) {
throw PATH_ERROR;
}
}
node = this.#children[regexpStr];
if (!node) {
if (Object.keys(this.#children).some(
(k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
)) {
throw PATH_ERROR;
}
if (pathErrorCheckOnly) {
return;
}
node = this.#children[regexpStr] = new _Node();
if (name !== "") {
node.#varIndex = context.varIndex++;
}
}
if (!pathErrorCheckOnly && name !== "") {
paramMap.push([name, node.#varIndex]);
}
} else {
node = this.#children[token];
if (!node) {
if (Object.keys(this.#children).some(
(k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
)) {
throw PATH_ERROR;
}
if (pathErrorCheckOnly) {
return;
}
node = this.#children[token] = new _Node();
}
}
node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly);
}
buildRegExpStr() {
const childKeys = Object.keys(this.#children).sort(compareKey);
const strList = childKeys.map((k) => {
const c = this.#children[k];
return (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + c.buildRegExpStr();
});
if (typeof this.#index === "number") {
strList.unshift(`#${this.#index}`);
}
if (strList.length === 0) {
return "";
}
if (strList.length === 1) {
return strList[0];
}
return "(?:" + strList.join("|") + ")";
}
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/trie.js
var Trie;
var init_trie = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/trie.js"() {
init_node();
Trie = class {
#context = { varIndex: 0 };
#root = new Node();
insert(path, index, pathErrorCheckOnly) {
const paramAssoc = [];
const groups = [];
for (let i = 0; ; ) {
let replaced = false;
path = path.replace(/\{[^}]+\}/g, (m) => {
const mark = `@\\${i}`;
groups[i] = [mark, m];
i++;
replaced = true;
return mark;
});
if (!replaced) {
break;
}
}
const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
for (let i = groups.length - 1; i >= 0; i--) {
const [mark] = groups[i];
for (let j = tokens.length - 1; j >= 0; j--) {
if (tokens[j].indexOf(mark) !== -1) {
tokens[j] = tokens[j].replace(mark, groups[i][1]);
break;
}
}
}
this.#root.insert(tokens, index, paramAssoc, this.#context, pathErrorCheckOnly);
return paramAssoc;
}
buildRegExp() {
let regexp = this.#root.buildRegExpStr();
if (regexp === "") {
return [/^$/, [], []];
}
let captureIndex = 0;
const indexReplacementMap = [];
const paramReplacementMap = [];
regexp = regexp.replace(/#(\d+)|@(\d+)|\.\*\$/g, (_, handlerIndex, paramIndex) => {
if (handlerIndex !== void 0) {
indexReplacementMap[++captureIndex] = Number(handlerIndex);
return "$()";
}
if (paramIndex !== void 0) {
paramReplacementMap[Number(paramIndex)] = ++captureIndex;
return "";
}
return "";
});
return [new RegExp(`^${regexp}`), indexReplacementMap, paramReplacementMap];
}
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/router.js
function buildWildcardRegExp(path) {
return wildcardRegExpCache[path] ??= new RegExp(
path === "*" ? "" : `^${path.replace(
/\/\*$|([.\\+*[^\]$()])/g,
(_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)"
)}$`
);
}
function clearWildcardRegExpCache() {
wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
}
function buildMatcherFromPreprocessedRoutes(routes) {
const trie = new Trie();
const handlerData = [];
if (routes.length === 0) {
return nullMatcher;
}
const routesWithStaticPathFlag = routes.map(
(route) => [!/\*|\/:/.test(route[0]), ...route]
).sort(
([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length
);
const staticMap = /* @__PURE__ */ Object.create(null);
for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {
const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];
if (pathErrorCheckOnly) {
staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
} else {
j++;
}
let paramAssoc;
try {
paramAssoc = trie.insert(path, j, pathErrorCheckOnly);
} catch (e) {
throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
}
if (pathErrorCheckOnly) {
continue;
}
handlerData[j] = handlers.map(([h, paramCount]) => {
const paramIndexMap = /* @__PURE__ */ Object.create(null);
paramCount -= 1;
for (; paramCount >= 0; paramCount--) {
const [key, value] = paramAssoc[paramCount];
paramIndexMap[key] = value;
}
return [h, paramIndexMap];
});
}
const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
for (let i = 0, len = handlerData.length; i < len; i++) {
for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
const map = handlerData[i][j]?.[1];
if (!map) {
continue;
}
const keys = Object.keys(map);
for (let k = 0, len3 = keys.length; k < len3; k++) {
map[keys[k]] = paramReplacementMap[map[keys[k]]];
}
}
}
const handlerMap = [];
for (const i in indexReplacementMap) {
handlerMap[i] = handlerData[indexReplacementMap[i]];
}
return [regexp, handlerMap, staticMap];
}
function findMiddleware(middleware, path) {
if (!middleware) {
return void 0;
}
for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {
if (buildWildcardRegExp(k).test(path)) {
return [...middleware[k]];
}
}
return void 0;
}
var nullMatcher, wildcardRegExpCache, RegExpRouter;
var init_router2 = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/router.js"() {
init_router();
init_url();
init_matcher();
init_node();
init_trie();
nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
RegExpRouter = class {
name = "RegExpRouter";
#middleware;
#routes;
constructor() {
this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
}
add(method, path, handler) {
const middleware = this.#middleware;
const routes = this.#routes;
if (!middleware || !routes) {
throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
}
if (!middleware[method]) {
;
[middleware, routes].forEach((handlerMap) => {
handlerMap[method] = /* @__PURE__ */ Object.create(null);
Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => {
handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];
});
});
}
if (path === "/*") {
path = "*";
}
const paramCount = (path.match(/\/:/g) || []).length;
if (/\*$/.test(path)) {
const re = buildWildcardRegExp(path);
if (method === METHOD_NAME_ALL) {
Object.keys(middleware).forEach((m) => {
middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
});
} else {
middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
}
Object.keys(middleware).forEach((m) => {
if (method === METHOD_NAME_ALL || method === m) {
Object.keys(middleware[m]).forEach((p) => {
re.test(p) && middleware[m][p].push([handler, paramCount]);
});
}
});
Object.keys(routes).forEach((m) => {
if (method === METHOD_NAME_ALL || method === m) {
Object.keys(routes[m]).forEach(
(p) => re.test(p) && routes[m][p].push([handler, paramCount])
);
}
});
return;
}
const paths = checkOptionalParameter(path) || [path];
for (let i = 0, len = paths.length; i < len; i++) {
const path2 = paths[i];
Object.keys(routes).forEach((m) => {
if (method === METHOD_NAME_ALL || method === m) {
routes[m][path2] ||= [
...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
];
routes[m][path2].push([handler, paramCount - len + i + 1]);
}
});
}
}
match = match;
buildAllMatchers() {
const matchers = /* @__PURE__ */ Object.create(null);
Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => {
matchers[method] ||= this.#buildMatcher(method);
});
this.#middleware = this.#routes = void 0;
clearWildcardRegExpCache();
return matchers;
}
#buildMatcher(method) {
const routes = [];
let hasOwnRoute = method === METHOD_NAME_ALL;
[this.#middleware, this.#routes].forEach((r) => {
const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : [];
if (ownRoute.length !== 0) {
hasOwnRoute ||= true;
routes.push(...ownRoute);
} else if (method !== METHOD_NAME_ALL) {
routes.push(
...Object.keys(r[METHOD_NAME_ALL]).map((path) => [path, r[METHOD_NAME_ALL][path]])
);
}
});
if (!hasOwnRoute) {
return null;
} else {
return buildMatcherFromPreprocessedRoutes(routes);
}
}
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/prepared-router.js
var init_prepared_router = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/prepared-router.js"() {
init_router();
init_matcher();
init_router2();
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/index.js
var init_reg_exp_router = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/index.js"() {
init_router2();
init_prepared_router();
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/smart-router/router.js
var SmartRouter;
var init_router3 = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/smart-router/router.js"() {
init_router();
SmartRouter = class {
name = "SmartRouter";
#routers = [];
#routes = [];
constructor(init) {
this.#routers = init.routers;
}
add(method, path, handler) {
if (!this.#routes) {
throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
}
this.#routes.push([method, path, handler]);
}
match(method, path) {
if (!this.#routes) {
throw new Error("Fatal error");
}
const routers = this.#routers;
const routes = this.#routes;
const len = routers.length;
let i = 0;
let res;
for (; i < len; i++) {
const router = routers[i];
try {
for (let i2 = 0, len2 = routes.length; i2 < len2; i2++) {
router.add(...routes[i2]);
}
res = router.match(method, path);
} catch (e) {
if (e instanceof UnsupportedPathError) {
continue;
}
throw e;
}
this.match = router.match.bind(router);
this.#routers = [router];
this.#routes = void 0;
break;
}
if (i === len) {
throw new Error("Fatal error");
}
this.name = `SmartRouter + ${this.activeRouter.name}`;
return res;
}
get activeRouter() {
if (this.#routes || this.#routers.length !== 1) {
throw new Error("No active router has been determined yet.");
}
return this.#routers[0];
}
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/smart-router/index.js
var init_smart_router = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/smart-router/index.js"() {
init_router3();
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/trie-router/node.js
var emptyParams, hasChildren, Node2;
var init_node2 = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/trie-router/node.js"() {
init_router();
init_url();
emptyParams = /* @__PURE__ */ Object.create(null);
hasChildren = (children) => {
for (const _ in children) {
return true;
}
return false;
};
Node2 = class _Node2 {
#methods;
#children;
#patterns;
#order = 0;
#params = emptyParams;
constructor(method, handler, children) {
this.#children = children || /* @__PURE__ */ Object.create(null);
this.#methods = [];
if (method && handler) {
const m = /* @__PURE__ */ Object.create(null);
m[method] = { handler, possibleKeys: [], score: 0 };
this.#methods = [m];
}
this.#patterns = [];
}
insert(method, path, handler) {
this.#order = ++this.#order;
let curNode = this;
const parts = splitRoutingPath(path);
const possibleKeys = [];
for (let i = 0, len = parts.length; i < len; i++) {
const p = parts[i];
const nextP = parts[i + 1];
const pattern = getPattern(p, nextP);
const key = Array.isArray(pattern) ? pattern[0] : p;
if (key in curNode.#children) {
curNode = curNode.#children[key];
if (pattern) {
possibleKeys.push(pattern[1]);
}
continue;
}
curNode.#children[key] = new _Node2();
if (pattern) {
curNode.#patterns.push(pattern);
possibleKeys.push(pattern[1]);
}
curNode = curNode.#children[key];
}
curNode.#methods.push({
[method]: {
handler,
possibleKeys: possibleKeys.filter((v, i, a) => a.indexOf(v) === i),
score: this.#order
}
});
return curNode;
}
#pushHandlerSets(handlerSets, node, method, nodeParams, params) {
for (let i = 0, len = node.#methods.length; i < len; i++) {
const m = node.#methods[i];
const handlerSet = m[method] || m[METHOD_NAME_ALL];
const processedSet = {};
if (handlerSet !== void 0) {
handlerSet.params = /* @__PURE__ */ Object.create(null);
handlerSets.push(handlerSet);
if (nodeParams !== emptyParams || params && params !== emptyParams) {
for (let i2 = 0, len2 = handlerSet.possibleKeys.length; i2 < len2; i2++) {
const key = handlerSet.possibleKeys[i2];
const processed = processedSet[handlerSet.score];
handlerSet.params[key] = params?.[key] && !processed ? params[key] : nodeParams[key] ?? params?.[key];
processedSet[handlerSet.score] = true;
}
}
}
}
}
search(method, path) {
const handlerSets = [];
this.#params = emptyParams;
const curNode = this;
let curNodes = [curNode];
const parts = splitPath(path);
const curNodesQueue = [];
const len = parts.length;
let partOffsets = null;
for (let i = 0; i < len; i++) {
const part = parts[i];
const isLast = i === len - 1;
const tempNodes = [];
for (let j = 0, len2 = curNodes.length; j < len2; j++) {
const node = curNodes[j];
const nextNode = node.#children[part];
if (nextNode) {
nextNode.#params = node.#params;
if (isLast) {
if (nextNode.#children["*"]) {
this.#pushHandlerSets(handlerSets, nextNode.#children["*"], method, node.#params);
}
this.#pushHandlerSets(handlerSets, nextNode, method, node.#params);
} else {
tempNodes.push(nextNode);
}
}
for (let k = 0, len3 = node.#patterns.length; k < len3; k++) {
const pattern = node.#patterns[k];
const params = node.#params === emptyParams ? {} : { ...node.#params };
if (pattern === "*") {
const astNode = node.#children["*"];
if (astNode) {
this.#pushHandlerSets(handlerSets, astNode, method, node.#params);
astNode.#params = params;
tempNodes.push(astNode);
}
continue;
}
const [key, name, matcher] = pattern;
if (!part && !(matcher instanceof RegExp)) {
continue;
}
const child = node.#children[key];
if (matcher instanceof RegExp) {
if (partOffsets === null) {
partOffsets = new Array(len);
let offset = path[0] === "/" ? 1 : 0;
for (let p = 0; p < len; p++) {
partOffsets[p] = offset;
offset += parts[p].length + 1;
}
}
const restPathString = path.substring(partOffsets[i]);
const m = matcher.exec(restPathString);
if (m) {
params[name] = m[0];
this.#pushHandlerSets(handlerSets, child, method, node.#params, params);
if (hasChildren(child.#children)) {
child.#params = params;
const componentCount = m[0].match(/\//)?.length ?? 0;
const targetCurNodes = curNodesQueue[componentCount] ||= [];
targetCurNodes.push(child);
}
continue;
}
}
if (matcher === true || matcher.test(part)) {
params[name] = part;
if (isLast) {
this.#pushHandlerSets(handlerSets, child, method, params, node.#params);
if (child.#children["*"]) {
this.#pushHandlerSets(
handlerSets,
child.#children["*"],
method,
params,
node.#params
);
}
} else {
child.#params = params;
tempNodes.push(child);
}
}
}
}
const shifted = curNodesQueue.shift();
curNodes = shifted ? tempNodes.concat(shifted) : tempNodes;
}
if (handlerSets.length > 1) {
handlerSets.sort((a, b) => {
return a.score - b.score;
});
}
return [handlerSets.map(({ handler, params }) => [handler, params])];
}
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/trie-router/router.js
var TrieRouter;
var init_router4 = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/trie-router/router.js"() {
init_url();
init_node2();
TrieRouter = class {
name = "TrieRouter";
#node;
constructor() {
this.#node = new Node2();
}
add(method, path, handler) {
const results = checkOptionalParameter(path);
if (results) {
for (let i = 0, len = results.length; i < len; i++) {
this.#node.insert(method, results[i], handler);
}
return;
}
this.#node.insert(method, path, handler);
}
match(method, path) {
return this.#node.search(method, path);
}
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/trie-router/index.js
var init_trie_router = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/trie-router/index.js"() {
init_router4();
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/hono.js
var Hono2;
var init_hono = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/hono.js"() {
init_hono_base();
init_reg_exp_router();
init_smart_router();
init_trie_router();
Hono2 = class extends Hono {
/**
* Creates an instance of the Hono class.
*
* @param options - Optional configuration options for the Hono instance.
*/
constructor(options = {}) {
super(options);
this.router = options.router ?? new SmartRouter({
routers: [new RegExpRouter(), new TrieRouter()]
});
}
};
}
});
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/index.js
var init_dist = __esm({
"cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/index.js"() {
init_hono();
}
});
// cypher-executor/src/types.ts
async function kvGetNodeOutput(store, nodeId) {
try {
const val = await store.kv.get(`${store.runId}:node:${nodeId}`, "json");
return val;
} catch {
return void 0;
}
}
async function kvSetNodeOutput(store, nodeId, output) {
try {
await store.kv.put(
`${store.runId}:node:${nodeId}`,
JSON.stringify(output),
{ expirationTtl: 3600 }
);
} catch {
}
}
var WorkflowPaused, ExecutionError;
var init_types = __esm({
"cypher-executor/src/types.ts"() {
"use strict";
WorkflowPaused = class extends Error {
task_id;
run_id;
paused_node_id;
trace_so_far;
constructor(task_id, run_id, paused_node_id, trace_so_far) {
super(`workflow paused at node ${paused_node_id} waiting for task ${task_id}`);
this.name = "WorkflowPaused";
this.task_id = task_id;
this.run_id = run_id;
this.paused_node_id = paused_node_id;
this.trace_so_far = trace_so_far;
}
};
ExecutionError = class extends Error {
failed_node;
failed_input;
trace;
constructor(message, failed_node, failed_input, trace) {
super(message);
this.name = "ExecutionError";
this.failed_node = failed_node;
this.failed_input = failed_input;
this.trace = trace;
}
};
}
});
// cypher-executor/src/lib/hash.ts
async function deriveRecipeHash(canonicalId) {
return "rec_" + await sha256Prefix(canonicalId);
}
function isComponentHash(id) {
return /^cmp_[0-9a-f]{8}$/.test(id);
}
function isRecipeHash(id) {
return /^rec_[0-9a-f]{8}$/.test(id);
}
async function sha256Prefix(input) {
const data = new TextEncoder().encode(input);
const buf = await crypto.subtle.digest("SHA-256", data);
return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 8);
}
var init_hash = __esm({
"cypher-executor/src/lib/hash.ts"() {
"use strict";
}
});
// cypher-executor/src/routes/recipes.ts
async function installRecipeRecord(kv, recipe) {
const uuid = recipe.uuid;
const { canonical_id, hash_id } = recipe;
const listRaw = await kv.get(kIdxCanonical(canonical_id));
const uuids = listRaw ? JSON.parse(listRaw) : [];
if (!uuids.includes(uuid)) uuids.push(uuid);
await Promise.all([
kv.put(`recipe:${uuid}`, JSON.stringify(recipe)),
kv.put(kIdxCanonical(canonical_id), JSON.stringify(uuids)),
kv.put(kIdxInstalled(canonical_id), uuid),
kv.put(`idx:${hash_id}`, canonical_id)
]);
}
async function fetchMarketStat(env, canonicalId) {
try {
const base = (env.KBDB_BASE_URL ?? "https://kbdb.finally.click").replace(/\/$/, "");
const headers = {};
if (env.KBDB_INTERNAL_TOKEN) headers["Authorization"] = `Bearer ${env.KBDB_INTERNAL_TOKEN}`;
const res = await fetch(`${base}/recipe-stats/${encodeURIComponent(canonicalId)}`, { headers });
if (!res.ok) return null;
const json = await res.json();
if (!json.stat) return null;
return {
success_count: json.stat.success_count ?? 0,
failure_count: json.stat.failure_count ?? 0
};
} catch {
return null;
}
}
async function listAllRecipes(kv) {
const list = await kv.list({ prefix: "recipe:" });
const all = (await Promise.all(
list.keys.map((k) => kv.get(k.name, "json"))
)).filter(Boolean);
const hasUuid = new Set(all.filter((r) => r.uuid).map((r) => r.canonical_id));
return all.filter((r) => r.uuid || !hasUuid.has(r.canonical_id));
}
async function resolveRecipe(id, kv) {
const direct = await kv.get(`recipe:${id}`, "json");
if (direct && direct.uuid) return direct;
let canonicalId = id;
if (id.startsWith("rec_")) {
const looked = await kv.get(`idx:${id}`);
if (!looked) return direct;
canonicalId = looked;
}
const installedUuid = await kv.get(kIdxInstalled(canonicalId));
if (installedUuid) {
const byUuid = await kv.get(`recipe:${installedUuid}`, "json");
if (byUuid) return byUuid;
}
return direct ?? await kv.get(`recipe:${canonicalId}`, "json");
}
async function resolveAuthRecipe(service, kv) {
return kv.get(`auth_recipe:${service}`, "json");
}
var recipesRouter, kIdxCanonical, kIdxInstalled;
var init_recipes = __esm({
"cypher-executor/src/routes/recipes.ts"() {
"use strict";
init_dist();
init_hash();
recipesRouter = new Hono2();
kIdxCanonical = (canonicalId) => `idx:canonical:${canonicalId}`;
kIdxInstalled = (canonicalId) => `idx:installed:${canonicalId}`;
recipesRouter.post("/recipes", async (c) => {
let body;
try {
body = await c.req.json();
} catch {
return c.json({ success: false, error: "request body \u5FC5\u9808\u70BA JSON" }, 400);
}
const canonicalId = (body.canonical_id ?? "").trim().toLowerCase();
if (!canonicalId) return c.json({ success: false, error: "canonical_id \u5FC5\u586B" }, 400);
if (!body.endpoint) return c.json({ success: false, error: "endpoint \u5FC5\u586B" }, 400);
const hashId = await deriveRecipeHash(canonicalId);
const now2 = Date.now();
const existing = await resolveRecipe(canonicalId, c.env.RECIPES);
const recipe = {
uuid: existing?.uuid ?? crypto.randomUUID(),
author: body.author ?? existing?.author ?? "local",
derived_from: body.derived_from ?? existing?.derived_from,
canonical_id: canonicalId,
hash_id: hashId,
display_name: body.display_name,
description: body.description,
endpoint: body.endpoint,
method: (body.method ?? "POST").toUpperCase(),
headers: body.headers,
body: body.body,
// ③ payload/回應/binding 三層(3.12):全選填,沒給就是 undefined=既有行為
body_template: body.body_template,
response_map: body.response_map,
auth: body.auth,
binding_name: body.binding_name,
auth_service: body.auth_service,
credentials_required: body.credentials_required,
created_at: existing?.created_at ?? now2,
updated_at: now2
};
await installRecipeRecord(c.env.RECIPES, recipe);
return c.json({ success: true, recipe });
});
recipesRouter.post("/recipes/submit", async (c) => {
let body;
try {
body = await c.req.json();
} catch {
return c.json({ success: false, error: "request body \u5FC5\u9808\u70BA JSON" }, 400);
}
const canonicalId = (body.canonical_id ?? "").trim().toLowerCase();
if (!canonicalId) return c.json({ success: false, error: "canonical_id \u5FC5\u586B" }, 400);
if (!body.endpoint) return c.json({ success: false, error: "endpoint \u5FC5\u586B" }, 400);
const hashId = await deriveRecipeHash(canonicalId);
const now2 = Date.now();
const recipe = {
uuid: crypto.randomUUID(),
author: body.author ?? body.submitter ?? "anonymous",
derived_from: body.derived_from,
canonical_id: canonicalId,
hash_id: hashId,
display_name: body.display_name,
description: body.description,
endpoint: body.endpoint,
method: (body.method ?? "POST").toUpperCase(),
headers: body.headers,
body: body.body,
auth_service: body.auth_service,
credentials_required: body.credentials_required,
created_at: now2,
updated_at: now2
};
await installRecipeRecord(c.env.RECIPES, recipe);
const kbdbBase2 = (c.env.KBDB_BASE_URL ?? "https://kbdb.finally.click").replace(/\/$/, "");
const evidence = {
content: canonicalId,
entry_type: "recipe_submission",
metadata_json: JSON.stringify({
uuid: recipe.uuid,
canonical_id: canonicalId,
author: recipe.author,
submitter: body.submitter ?? "unknown",
claimed_stat: body.stat ?? null,
submitted_at: now2
})
};
const kbdbHeaders = { "Content-Type": "application/json" };
if (c.env.KBDB_INTERNAL_TOKEN) kbdbHeaders["Authorization"] = `Bearer ${c.env.KBDB_INTERNAL_TOKEN}`;
c.executionCtx.waitUntil(
fetch(`${kbdbBase2}/entries`, {
method: "POST",
headers: kbdbHeaders,
body: JSON.stringify(evidence)
}).catch(() => void 0)
);
return c.json({ success: true, recipe, evidence_recorded: true });
});
recipesRouter.post("/recipes/migrate-uuid", async (c) => {
const list = await c.env.RECIPES.list({ prefix: "recipe:" });
let migrated = 0, skipped = 0;
const errors = [];
for (const k of list.keys) {
try {
const rec = await c.env.RECIPES.get(k.name, "json");
if (!rec || !rec.canonical_id) {
skipped++;
continue;
}
if (rec.uuid) {
skipped++;
continue;
}
const migrated_recipe = {
...rec,
uuid: crypto.randomUUID(),
author: rec.author ?? "system"
// 舊種子歸 system
};
await installRecipeRecord(c.env.RECIPES, migrated_recipe);
migrated++;
} catch (e) {
errors.push(`${k.name}: ${e instanceof Error ? e.message : String(e)}`);
}
}
return c.json({ success: errors.length === 0, migrated, skipped, errors });
});
recipesRouter.get("/recipes/:id", async (c) => {
const id = c.req.param("id");
const recipe = await resolveRecipe(id, c.env.RECIPES);
if (!recipe) return c.json({ success: false, error: `\u627E\u4E0D\u5230 recipe: ${id}` }, 404);
return c.json({ success: true, recipe });
});
recipesRouter.get("/recipes", async (c) => {
const list = await c.env.RECIPES.list({ prefix: "recipe:" });
const all = (await Promise.all(
list.keys.map((k) => c.env.RECIPES.get(k.name, "json"))
)).filter(Boolean);
const hasUuidVersion = new Set(all.filter((r) => r.uuid).map((r) => r.canonical_id));
const recipes = all.filter((r) => r.uuid || !hasUuidVersion.has(r.canonical_id));
return c.json({ success: true, recipes, count: recipes.length });
});
recipesRouter.get("/public-recipes", async (c) => {
const q = (c.req.query("q") ?? "").trim().toLowerCase();
const limit = Math.min(Number(c.req.query("limit") ?? 50), 200);
const offset = Number(c.req.query("offset") ?? 0);
const all = await listAllRecipes(c.env.RECIPES);
const matched = q ? all.filter((r) => r.canonical_id.toLowerCase().includes(q) || (r.display_name ?? "").toLowerCase().includes(q) || (r.description ?? "").toLowerCase().includes(q)) : all;
if (q && matched.length === 0) {
return c.json({
found: false,
query: q,
hint: `\u516C\u5EAB\u7121\u7B26\u5408\u300C${q}\u300D\u7684 recipe\u3002\u53EF\u81EA\u884C\u5EFA\u7ACB\u4E26 submit-p \u6295\u7A3F\u6210\u70BA\u4F5C\u8005\uFF08app-store \u6A21\u578B\uFF09\u3002`
});
}
const page = matched.slice(offset, offset + limit);
const withStats = await Promise.all(
page.map(async (r) => ({
uuid: r.uuid,
canonical_id: r.canonical_id,
author: r.author,
display_name: r.display_name,
description: r.description,
market_stat: await fetchMarketStat(c.env, r.uuid ?? r.canonical_id)
// §7.5.h per-uuid
}))
);
return c.json({ found: true, recipes: withStats, count: matched.length });
});
recipesRouter.get("/public-recipes/:canonical_id", async (c) => {
const canonicalId = c.req.param("canonical_id").trim().toLowerCase();
const author = c.req.query("author");
const all = await listAllRecipes(c.env.RECIPES);
let versions = all.filter((r) => r.canonical_id === canonicalId);
if (author) versions = versions.filter((r) => r.author === author);
if (versions.length === 0) {
return c.json({
found: false,
canonical_id: canonicalId,
hint: `\u516C\u5EAB\u7121 recipe\u300C${canonicalId}\u300D${author ? `\uFF08author=${author}\uFF09` : ""}\u3002\u53EF\u81EA\u884C\u5EFA\u7ACB\u4E26 submit-p \u6295\u7A3F\u6210\u70BA\u4F5C\u8005\uFF08app-store \u6A21\u578B\uFF09\u3002`
});
}
let best = versions[0];
let bestStat = null;
let bestScore = -1;
for (const v of versions) {
const stat = await fetchMarketStat(c.env, v.uuid ?? v.canonical_id);
const score = stat?.success_count ?? 0;
if (score > bestScore) {
bestScore = score;
best = v;
bestStat = stat;
}
}
return c.json({ found: true, recipe: best, market_stat: bestStat });
});
recipesRouter.delete("/recipes/:id", async (c) => {
const id = c.req.param("id");
const recipe = await resolveRecipe(id, c.env.RECIPES);
if (!recipe) return c.json({ success: false, error: `\u627E\u4E0D\u5230 recipe: ${id}` }, 404);
const canonicalId = recipe.canonical_id;
const ops = [
c.env.RECIPES.delete(`idx:${recipe.hash_id}`),
c.env.RECIPES.delete(`recipe:${canonicalId}`)
// 舊 key(若存在)
];
if (recipe.uuid) {
ops.push(c.env.RECIPES.delete(`recipe:${recipe.uuid}`));
const listRaw = await c.env.RECIPES.get(kIdxCanonical(canonicalId));
const uuids = listRaw ? JSON.parse(listRaw) : [];
const left = uuids.filter((u) => u !== recipe.uuid);
if (left.length > 0) {
ops.push(c.env.RECIPES.put(kIdxCanonical(canonicalId), JSON.stringify(left)));
const installed = await c.env.RECIPES.get(kIdxInstalled(canonicalId));
if (installed === recipe.uuid) ops.push(c.env.RECIPES.put(kIdxInstalled(canonicalId), left[0]));
} else {
ops.push(c.env.RECIPES.delete(kIdxCanonical(canonicalId)));
ops.push(c.env.RECIPES.delete(kIdxInstalled(canonicalId)));
}
}
await Promise.all(ops);
return c.json({ success: true, deleted: recipe.uuid ?? canonicalId });
});
recipesRouter.post("/auth-recipes", async (c) => {
let body;
try {
body = await c.req.json();
} catch {
return c.json({ success: false, error: "request body \u5FC5\u9808\u70BA JSON" }, 400);
}
const service = (body.service ?? "").trim().toLowerCase();
if (!service) return c.json({ success: false, error: "service \u5FC5\u586B" }, 400);
if (!body.primitive) return c.json({ success: false, error: "primitive \u5FC5\u586B" }, 400);
if (!body.base_url) return c.json({ success: false, error: "base_url \u5FC5\u586B" }, 400);
if (!body.required_secrets?.length) return c.json({ success: false, error: "required_secrets \u5FC5\u586B" }, 400);
if (!body.inject) return c.json({ success: false, error: "inject \u5FC5\u586B" }, 400);
const missingHelp = body.required_secrets.filter((s) => !s.help_url || !/^https?:\/\//.test(s.help_url));
if (missingHelp.length > 0) {
return c.json({
success: false,
error: `\u6BCF\u500B required_secret \u5FC5\u9808\u6709 help_url\uFF08\u5B98\u65B9\u6587\u4EF6\u9023\u7D50\uFF0Chttp(s)://\uFF09\u3002\u7F3A\uFF1A${missingHelp.map((s) => s.key).join(", ")}`,
requires: "help_url"
}, 400);
}
const now2 = Date.now();
const existing = await c.env.RECIPES.get(`auth_recipe:${service}`, "json");
const recipe = {
kind: "auth_recipe",
service,
version: body.version ?? 1,
primitive: body.primitive,
base_url: body.base_url,
display_name: body.display_name,
description: body.description,
service_account_kind: body.service_account_kind,
token_exchange: body.token_exchange,
required_secrets: body.required_secrets,
inject: body.inject,
created_at: existing?.created_at ?? now2,
updated_at: now2
};
await c.env.RECIPES.put(`auth_recipe:${service}`, JSON.stringify(recipe));
return c.json({ success: true, recipe });
});
recipesRouter.get("/auth-recipes", async (c) => {
const list = await c.env.RECIPES.list({ prefix: "auth_recipe:" });
const recipes = await Promise.all(
list.keys.map((k) => c.env.RECIPES.get(k.name, "json"))
);
return c.json({ success: true, recipes: recipes.filter(Boolean), count: recipes.length });
});
recipesRouter.get("/auth-recipes/:service", async (c) => {
const service = c.req.param("service");
const recipe = await resolveAuthRecipe(service, c.env.RECIPES);
if (!recipe) return c.json({ success: false, error: `\u627E\u4E0D\u5230 auth recipe: ${service}` }, 404);
return c.json({ success: true, recipe });
});
recipesRouter.delete("/auth-recipes/:service", async (c) => {
const service = c.req.param("service");
const recipe = await resolveAuthRecipe(service, c.env.RECIPES);
if (!recipe) return c.json({ success: false, error: `\u627E\u4E0D\u5230 auth recipe: ${service}` }, 404);
await c.env.RECIPES.delete(`auth_recipe:${service}`);
return c.json({ success: true, deleted: service });
});
}
});
// cypher-executor/src/lib/constants.ts
var VALID_EDGE_TYPES, SEMANTIC_EDGE_MAP, BUILTIN_COMPONENTS;
var init_constants3 = __esm({
"cypher-executor/src/lib/constants.ts"() {
"use strict";
VALID_EDGE_TYPES = /* @__PURE__ */ new Set([
// 現有
"PIPE",
"IF",
"FOREACH",
"CONTINUE",
// 新增:執行語意
"IS_A",
"ON_SUCCESS",
"ON_FAIL",
// 新增:條件語意(SDD workflow-discovery 3.11)—— 讀上游 if_control/switch 的 branch
"ON_TRUE",
"ON_FALSE",
"ON_BRANCH",
// 新增:觸發語意
"ON_CLICK",
"CALLS_SUBFLOW",
// 新增:結構語意(記錄圖結構,不執行)
"CONTAINS",
"HAS_STYLE",
"HAS_BEHAVIOR"
]);
SEMANTIC_EDGE_MAP = {
// 中文語意詞
"\u5B8C\u6210\u5F8C": "PIPE",
"\u5931\u6557\u6642": "ON_FAIL",
"\u5C0D\u6BCF\u500B": "FOREACH",
"\u689D\u4EF6\u6EFF\u8DB3\u6642": "IF",
// 條件分支語意(SDD workflow-discovery 3.11):讓意圖工作流寫得出兩條路
"\u6210\u7ACB\u6642": "ON_TRUE",
"\u70BA\u771F\u6642": "ON_TRUE",
"\u4E0D\u6210\u7ACB\u6642": "ON_FALSE",
"\u70BA\u5047\u6642": "ON_FALSE",
"\u5426\u5247": "ON_FALSE",
// 英文別名
"SUCCESS": "ON_SUCCESS",
"FAIL": "ON_FAIL",
"TRUE": "ON_TRUE",
"FALSE": "ON_FALSE",
"ELSE": "ON_FALSE",
"BRANCH": "ON_BRANCH",
"CLICK": "ON_CLICK",
"SUBFLOW": "CALLS_SUBFLOW"
};
BUILTIN_COMPONENTS = /* @__PURE__ */ new Map([
["comp_passthrough", (ctx) => ctx],
["comp_uppercase", (ctx) => {
const c = ctx;
return { ...c, text: String(c.text || "").toUpperCase() };
}],
["comp_counter", (ctx) => {
const c = ctx;
return { ...c, count: (Number(c.count) || 0) + 1 };
}]
]);
}
});
// cypher-executor/src/lib/recipe-payload.ts
function getPath2(obj, path) {
let cur = obj;
for (const part of path.split(".")) {
if (cur === null || cur === void 0) return void 0;
if (typeof cur !== "object") return void 0;
cur = cur[part];
}
return cur;
}
function renderBodyTemplate(template, ctx) {
if (template === void 0 || template === null) return void 0;
return renderValue(template, ctx);
}
function renderValue(v, ctx) {
if (typeof v === "string") return renderString(v, ctx);
if (Array.isArray(v)) return v.map((item) => renderValue(item, ctx));
if (v !== null && typeof v === "object") {
const out = {};
for (const [k, val] of Object.entries(v)) {
out[k] = renderValue(val, ctx);
}
return out;
}
return v;
}
function renderString(s, ctx) {
const single = s.match(/^\s*\{\{([\w.]+)\}\}\s*$/);
if (single) {
const val = getPath2(ctx, single[1]);
return val === void 0 ? s : val;
}
return s.replace(/\{\{([\w.]+)\}\}/g, (_, key) => {
const val = getPath2(ctx, key);
if (val === void 0) return `{{${key}}}`;
return typeof val === "string" ? val : JSON.stringify(val);
});
}
function applyResponseMap(body, map) {
if (!map) return { raw: body };
let picked = map.text_path ? getPath2(body, map.text_path) : body;
if (map.thinking_model && Array.isArray(picked)) {
const real = picked.filter(
(p) => !(p && typeof p === "object" && p.thought === true)
);
const last = real[real.length - 1];
picked = last && typeof last === "object" ? last.text : last;
}
if (typeof picked !== "string") return { text: void 0, raw: body };
return { text: sanitize(picked, map), raw: body };
}
function sanitize(input, map) {
let s = input.trim();
if (map.answer_marker) {
const idx = s.lastIndexOf(map.answer_marker);
if (idx >= 0) s = s.slice(idx + map.answer_marker.length);
}
const prefixes = map.strip_prefixes ?? [];
if (prefixes.length > 0) {
let changed = true;
while (changed) {
changed = false;
s = s.trimStart();
for (const p of prefixes) {
if (p && s.startsWith(p)) {
s = s.slice(p.length);
changed = true;
}
}
}
}
return s.trim();
}
var init_recipe_payload = __esm({
"cypher-executor/src/lib/recipe-payload.ts"() {
"use strict";
}
});
// cypher-executor/src/lib/component-loader.ts
function wasmWorkerUrl(canonicalId, subdomain) {
const kebab = canonicalId.replace(/_/g, "-");
return `https://arcrun-${kebab}.${subdomain}.workers.dev`;
}
function createComponentLoader(env) {
return async (componentId) => {
if (componentId === "trigger_workflow") {
return makeTriggerWorkflowRunner(env);
}
const builtin = BUILTIN_COMPONENTS.get(componentId);
if (builtin) return builtin;
if (componentId.startsWith("http://") || componentId.startsWith("https://")) {
return makeHttpRunner(componentId);
}
if (isComponentHash(componentId)) {
const canonicalId = await env.WEBHOOKS.get(`idx:${componentId}`);
if (canonicalId) {
const runner = makeLogicRunner(canonicalId, env);
if (runner) return runner;
}
throw new Error(`\u627E\u4E0D\u5230\u96F6\u4EF6 hash "${componentId}"\uFF0C\u8ACB\u78BA\u8A8D\u5DF2\u900F\u904E acr push \u4E0A\u50B3`);
}
if (isRecipeHash(componentId)) {
const recipe = await resolveRecipe(componentId, env.RECIPES);
if (recipe) return pickRecipeRunner(recipe, env);
throw new Error(`\u627E\u4E0D\u5230 recipe hash "${componentId}"\uFF0C\u8ACB\u78BA\u8A8D\u5DF2\u900F\u904E acr push \u4E0A\u50B3`);
}
const logicRunner = makeLogicRunner(componentId, env);
if (logicRunner) return logicRunner;
const authRecipe = await resolveAuthRecipe(componentId, env.RECIPES);
if (authRecipe) return makeAuthRecipeRunner(authRecipe);
const kvRecipe = await resolveRecipe(componentId, env.RECIPES);
if (kvRecipe) return pickRecipeRunner(kvRecipe, env);
if (WASM_HTTP_RUNNER_IDS.has(componentId)) {
return makeHttpRunner(wasmWorkerUrl(componentId, env.WORKER_SUBDOMAIN));
}
throw new Error(
`\u627E\u4E0D\u5230\u96F6\u4EF6 "${componentId}"\u3002
\u908F\u8F2F\u96F6\u4EF6\uFF1A${Object.keys(LOGIC_BINDING_MAP).join(", ")}
\u6216\u50B3\u5165\u5916\u90E8 URL\uFF08https://...\uFF09\u3001recipe hash\uFF08rec_xxxxxxxx\uFF09\u3001\u96F6\u4EF6 hash\uFF08cmp_xxxxxxxx\uFF09`
);
};
}
function makeTriggerWorkflowRunner(env) {
return async (ctx) => {
const c = ctx && typeof ctx === "object" ? ctx : {};
const workflowName = String(c.workflow_name ?? "");
const apiKey = String(c.api_key ?? "");
const input = c.input && typeof c.input === "object" ? c.input : {};
const wait = c.wait !== false;
if (!workflowName) return { success: false, error: "trigger_workflow \u7F3A workflow_name" };
if (!apiKey) return { success: false, error: "trigger_workflow \u7F3A api_key" };
const wfKey = `${apiKey}:wf:${workflowName}`;
const wfRaw = await env.WEBHOOKS.get(wfKey, "text");
if (!wfRaw) return { success: false, error: `\u627E\u4E0D\u5230 workflow "${workflowName}" (key=${wfKey})` };
let record;
try {
record = JSON.parse(wfRaw);
} catch {
return { success: false, error: `workflow "${workflowName}" KV \u5167\u5BB9\u975E JSON` };
}
if (!record.graph) return { success: false, error: `workflow "${workflowName}" \u7F3A graph \u6B04\u4F4D` };
const { executeWebhookGraph: executeWebhookGraph2 } = await Promise.resolve().then(() => (init_webhook_handlers(), webhook_handlers_exports));
const triggerContext = { ...input, _triggered_by: "trigger_workflow" };
if (wait) {
const r = await executeWebhookGraph2(env, record.graph, triggerContext, workflowName, apiKey);
const isPaused = !r.success && typeof r.error === "string" && /workflow paused/i.test(r.error);
return {
success: r.success || isPaused,
triggered_workflow: workflowName,
status: r.success ? "completed" : isPaused ? "running_async" : "failed",
sub_result: r
};
} else {
void executeWebhookGraph2(env, record.graph, triggerContext, workflowName, apiKey).catch((e) => console.error("[trigger_workflow] fire-and-forget fail", workflowName, e));
return { success: true, triggered_workflow: workflowName, mode: "fire_and_forget" };
}
};
}
function makeHttpRunner(url) {
return async (ctx) => {
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(ctx)
});
if (!res.ok) {
const text = await res.text();
return { success: false, status: res.status, error: text.slice(0, 200) };
}
try {
return await res.json();
} catch {
return { success: true, data: await res.text() };
}
};
}
function makeLogicRunner(canonicalId, env) {
const bindingKey = LOGIC_BINDING_MAP[canonicalId];
if (!bindingKey) return null;
const svc = env[bindingKey];
if (svc) {
return async (ctx) => {
const res = await svc.fetch(new Request("https://component/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(ctx)
}));
if (!res.ok) {
const text = await res.text();
return { success: false, error: `${canonicalId} \u56DE\u50B3 ${res.status}: ${text.slice(0, 200)}` };
}
try {
return await res.json();
} catch {
return { success: false, error: `${canonicalId} \u56DE\u50B3\u975E JSON` };
}
};
}
return makeHttpRunner(wasmWorkerUrl(canonicalId, env.WORKER_SUBDOMAIN));
}
function pickRecipeRunner(recipe, env) {
return recipe.auth === "binding" ? makeBindingRecipeRunner(recipe, env) : makeRecipeRunner(recipe);
}
function makeBindingRecipeRunner(recipe, env) {
return async (ctx) => {
const ctxObj = ctx && typeof ctx === "object" ? ctx : {};
const name = recipe.binding_name ?? "AI";
const binding = env[name];
if (!binding) {
return {
success: false,
error: `recipe "${recipe.canonical_id}" \u5BA3\u544A auth: binding\u3001binding_name: "${name}"\uFF0C\u4F46\u9019\u500B\u90E8\u7F72\u6C92\u6709\u7D81\u5B9A ${name}\u3002\u8ACB\u5728 wrangler.toml \u88DC\u4E0A\u8A72 binding \u5F8C\u91CD\u65B0\u90E8\u7F72\u3002`
};
}
const target = recipe.endpoint;
const payload = renderBodyTemplate(recipe.body_template ?? recipe.body, ctxObj) ?? Object.fromEntries(Object.entries(ctxObj).filter(([k]) => !k.startsWith("_")));
try {
const runner = binding;
if (typeof runner.run !== "function") {
return {
success: false,
error: `binding "${name}" \u6C92\u6709 run() \u65B9\u6CD5\uFF0C\u76EE\u524D binding \u578B\u53EA\u652F\u63F4 run(model, input) \u5F62\u72C0\uFF08\u5982 env.AI\uFF09\u3002`
};
}
const data = await runner.run(target, payload);
if (recipe.response_map) {
const normalized = applyResponseMap(data, recipe.response_map);
return { success: true, data, text: normalized.text };
}
return { success: true, data };
} catch (e) {
return {
success: false,
error: `binding "${name}" \u547C\u53EB\u5931\u6557\uFF08${target}\uFF09\uFF1A${e instanceof Error ? e.message : String(e)}`
};
}
};
}
function makeRecipeRunner(recipe) {
return async (ctx) => {
const ctxObj = ctx && typeof ctx === "object" ? ctx : {};
const authPath = ctxObj._auth_path ?? {};
const interpolate2 = (s) => s.replace(
/\{\{(auth\.)?(\w+)\}\}/g,
(_, authPrefix, k) => String(authPrefix ? authPath[k] ?? "" : ctxObj[k] ?? "")
);
const method = (recipe.method ?? "POST").toUpperCase();
const authHeaders = ctxObj._auth_headers ?? {};
const headers = {
"Content-Type": "application/json",
...authHeaders
};
for (const [k, v] of Object.entries(recipe.headers ?? {})) {
headers[k] = interpolate2(v);
}
let bodyStr;
if (recipe.body_template) {
bodyStr = JSON.stringify(renderBodyTemplate(recipe.body_template, ctxObj));
} else if (recipe.body) {
bodyStr = interpolate2(JSON.stringify(recipe.body));
} else if (method !== "GET") {
const bodyObj = Object.fromEntries(
Object.entries(ctxObj).filter(([k]) => !k.startsWith("_"))
);
bodyStr = JSON.stringify(bodyObj);
}
const res = await fetch(interpolate2(recipe.endpoint), {
method,
headers,
body: bodyStr
});
const data = await readBodyOnce(res);
if (recipe.response_map) {
const normalized = applyResponseMap(data, recipe.response_map);
return { success: res.ok, status: res.status, data, text: normalized.text };
}
return { success: res.ok, status: res.status, data };
};
}
function makeAuthRecipeRunner(recipe) {
return async (ctx) => {
const ctxObj = ctx && typeof ctx === "object" ? ctx : {};
const authHeaders = ctxObj._auth_headers ?? {};
const authQuery = ctxObj._auth_query ?? {};
const path = typeof ctxObj._path === "string" ? ctxObj._path : "";
const method = (ctxObj.method ?? "POST").toUpperCase();
const url = new URL(recipe.base_url.replace(/\/$/, "") + path);
for (const [k, v] of Object.entries(authQuery)) {
url.searchParams.set(k, v);
}
const headers = {
"Content-Type": "application/json",
...authHeaders
};
const bodyObj = Object.fromEntries(
Object.entries(ctxObj).filter(([k]) => !k.startsWith("_") && k !== "method")
);
const res = await fetch(url.toString(), {
method,
headers,
body: method !== "GET" ? JSON.stringify(bodyObj) : void 0
});
const data = await readBodyOnce(res);
return { success: res.ok, status: res.status, data };
};
}
async function readBodyOnce(res) {
const text = await res.text();
try {
return JSON.parse(text);
} catch {
return text;
}
}
var WASM_HTTP_RUNNER_IDS, LOGIC_BINDING_MAP;
var init_component_loader = __esm({
"cypher-executor/src/lib/component-loader.ts"() {
"use strict";
init_constants3();
init_hash();
init_recipes();
init_recipe_payload();
WASM_HTTP_RUNNER_IDS = /* @__PURE__ */ new Set([
// 通用 HTTP 零件
"http_request",
// 通用 code 零件(sandbox inline JS,Arcrun#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 已降級為 recipe(2026-05-29 Phase 2):
// recipe:gmail_send / telegram_send / line_notify_send / google_sheets_read|append
// 走 step 6 KV recipe 解析,不再是零件。零件目錄已刪。
"cron",
// Auth primitives
"auth_static_key",
"auth_service_account",
"auth_oauth2",
"auth_mtls"
]);
LOGIC_BINDING_MAP = {
if_control: "SVC_IF_CONTROL",
switch: "SVC_SWITCH",
foreach_control: "SVC_FOREACH_CONTROL",
filter: "SVC_FILTER",
merge: "SVC_MERGE",
try_catch: "SVC_TRY_CATCH",
wait: "SVC_WAIT",
set: "SVC_SET",
array_ops: "SVC_ARRAY_OPS",
string_ops: "SVC_STRING_OPS",
number_ops: "SVC_NUMBER_OPS",
date_ops: "SVC_DATE_OPS",
validate_json: "SVC_VALIDATE_JSON"
// ai_transform_compile / ai_transform_run 已刪除(2026-05-29):
// Arcrun 是 AI 呼叫的工具,工作流不該內嵌 AI 節點回頭呼叫 AI(n8n 才需要,因它沒大腦)。
};
}
});
// cypher-executor/src/lib/wasi-shim.ts
async function routedKvGet(env, apiKey, key) {
if (key.startsWith("auth_recipe:")) {
return env.RECIPES.get(key);
}
const credMatch = key.match(/^([^:]+):cred:.+$/);
if (credMatch) {
if (credMatch[1] !== apiKey) {
return null;
}
return env.CREDENTIALS_KV.get(key);
}
return null;
}
async function routedKvPut(env, apiKey, key, value, ttlSeconds) {
const oauth2Match = key.match(/^([^:]+):oauth2:.+$/);
if (oauth2Match && oauth2Match[1] === apiKey) {
const opts = ttlSeconds > 0 ? { expirationTtl: ttlSeconds } : void 0;
await env.CREDENTIALS_KV.put(key, value, opts);
return;
}
}
async function rsaPkcs1Sha256Sign(data, pkcs8) {
const cryptoKey = await crypto.subtle.importKey(
"pkcs8",
pkcs8,
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
false,
["sign"]
);
const sig = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", cryptoKey, data);
return new Uint8Array(sig);
}
function secretGet(env, ref) {
if (!/^CRED_/.test(ref)) return null;
const value = env[ref];
return typeof value === "string" ? value : null;
}
function createArcrunHostFunctions(env, apiKey) {
return {
kv_get: (key) => routedKvGet(env, apiKey, key),
kv_put: (key, value, ttlSeconds) => routedKvPut(env, apiKey, key, value, ttlSeconds),
crypto_sign_rs256: (data, pkcs8) => rsaPkcs1Sha256Sign(data, pkcs8),
secret_get: async (ref) => secretGet(env, ref)
};
}
var init_wasi_shim = __esm({
"cypher-executor/src/lib/wasi-shim.ts"() {
"use strict";
}
});
// cypher-executor/src/routes/kbdb-proxy.ts
function kbdbBase(env) {
const base = (env.KBDB_BASE_URL ?? "https://arcrun-kbdb.uncle6-me.workers.dev").replace(/\/$/, "");
const headers = { "Content-Type": "application/json" };
if (env.KBDB_INTERNAL_TOKEN) headers["Authorization"] = `Bearer ${env.KBDB_INTERNAL_TOKEN}`;
return { base, headers };
}
function tenant(c) {
return c.req.header("X-Arcrun-API-Key") ?? null;
}
function graphBase(env) {
if (env.KBDB_GRAPH_URL) return env.KBDB_GRAPH_URL.replace(/\/$/, "");
return `https://kbdb-graph-plugin.${env.WORKER_SUBDOMAIN}.workers.dev`;
}
var kbdbProxyRouter, NEED_KEY;
var init_kbdb_proxy = __esm({
"cypher-executor/src/routes/kbdb-proxy.ts"() {
"use strict";
init_dist();
kbdbProxyRouter = new Hono2();
NEED_KEY = { error: "\u7F3A\u5C11 X-Arcrun-API-Key header" };
kbdbProxyRouter.post("/kbdb/templates", async (c) => {
const owner = tenant(c);
if (!owner) return c.json(NEED_KEY, 401);
const body = await c.req.json().catch(() => null);
if (!body || !body.name || !Array.isArray(body.slots)) {
return c.json({ error: "name \u8207 slots[] \u5FC5\u586B" }, 400);
}
const { base, headers } = kbdbBase(c.env);
const res = await fetch(`${base}/templates`, {
method: "POST",
headers,
// created_by 帶上租戶當溯源,但 template 本身全域可見可用
body: JSON.stringify({ name: body.name, slots: body.slots, description: body.description, created_by: owner })
});
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
});
kbdbProxyRouter.get("/kbdb/templates", async (c) => {
if (!tenant(c)) return c.json(NEED_KEY, 401);
const { base, headers } = kbdbBase(c.env);
const res = await fetch(`${base}/templates`, { headers });
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
});
kbdbProxyRouter.get("/kbdb/templates/:idOrName", async (c) => {
if (!tenant(c)) return c.json(NEED_KEY, 401);
const { base, headers } = kbdbBase(c.env);
const res = await fetch(`${base}/templates/${encodeURIComponent(c.req.param("idOrName"))}`, { headers });
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
});
kbdbProxyRouter.post("/kbdb/records", async (c) => {
const owner = tenant(c);
if (!owner) return c.json(NEED_KEY, 401);
const body = await c.req.json().catch(() => null);
if (!body || !body.template || !body.values) {
return c.json({ error: "template \u8207 values \u5FC5\u586B" }, 400);
}
const { base, headers } = kbdbBase(c.env);
const res = await fetch(`${base}/records`, {
method: "POST",
headers,
// 強制以租戶身份隔離:忽略 caller 自帶 owner_id,一律用 header 身份(防跨租戶寫入)
body: JSON.stringify({ template: body.template, values: body.values, owner_id: owner })
});
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
});
kbdbProxyRouter.get("/kbdb/records/by-template/:template", async (c) => {
const owner = tenant(c);
if (!owner) return c.json(NEED_KEY, 401);
const { base, headers } = kbdbBase(c.env);
const res = await fetch(
`${base}/records/by-template/${encodeURIComponent(c.req.param("template"))}?owner_id=${encodeURIComponent(owner)}`,
{ headers }
);
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
});
kbdbProxyRouter.get("/kbdb/records/:recordId", async (c) => {
if (!tenant(c)) return c.json(NEED_KEY, 401);
const { base, headers } = kbdbBase(c.env);
const res = await fetch(`${base}/records/${encodeURIComponent(c.req.param("recordId"))}`, { headers });
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
});
kbdbProxyRouter.get("/kbdb/search", async (c) => {
const owner = tenant(c);
if (!owner) return c.json(NEED_KEY, 401);
const q = c.req.query("q");
if (!q) return c.json({ error: "q \u5FC5\u586B" }, 400);
const { base, headers } = kbdbBase(c.env);
const params = new URLSearchParams({ q, owner_id: owner });
for (const k of ["entry_type", "source", "library", "mode"]) {
const v = c.req.query(k);
if (v) params.set(k, v);
}
const res = await fetch(`${base}/entries/search?${params.toString()}`, { headers });
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
});
kbdbProxyRouter.post("/kbdb/entries", async (c) => {
const owner = tenant(c);
if (!owner) return c.json(NEED_KEY, 401);
const body = await c.req.json().catch(() => null);
if (!body || !body.entry_type) return c.json({ error: "entry_type \u5FC5\u586B" }, 400);
const { base, headers } = kbdbBase(c.env);
const res = await fetch(`${base}/entries`, {
method: "POST",
headers,
// 強制以租戶身份隔離:忽略 caller 自帶 owner_id,一律用 header 身份(防跨租戶寫入)
body: JSON.stringify({ ...body, owner_id: owner })
});
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
});
kbdbProxyRouter.get("/kbdb/entries", async (c) => {
const owner = tenant(c);
if (!owner) return c.json(NEED_KEY, 401);
const { base, headers } = kbdbBase(c.env);
const params = new URLSearchParams();
params.set("owner_id", owner);
for (const k of ["entry_type", "parent_id", "page_name", "source", "library", "limit", "offset"]) {
const v = c.req.query(k);
if (v) params.set(k, v);
}
const q = c.req.query("q") || c.req.query("search");
if (q) params.set("q", q);
const res = await fetch(`${base}/entries?${params.toString()}`, { headers });
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
});
kbdbProxyRouter.get("/kbdb/entries/:id", async (c) => {
if (!tenant(c)) return c.json(NEED_KEY, 401);
const { base, headers } = kbdbBase(c.env);
const res = await fetch(`${base}/entries/${encodeURIComponent(c.req.param("id"))}`, { headers });
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
});
kbdbProxyRouter.get("/kbdb/graph/neighbors/:name", async (c) => {
if (!tenant(c)) return c.json(NEED_KEY, 401);
const base = graphBase(c.env);
const headers = {};
if (c.env.KBDB_INTERNAL_TOKEN) headers["Authorization"] = `Bearer ${c.env.KBDB_INTERNAL_TOKEN}`;
try {
const res = await fetch(`${base}/graph/neighbors/${encodeURIComponent(c.req.param("name"))}`, { headers });
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
} catch (e) {
return c.json({ error: `kbdb-graph-plugin \u4E0D\u53EF\u9054\uFF08${base}\uFF09\uFF1A${e instanceof Error ? e.message : String(e)}` }, 502);
}
});
kbdbProxyRouter.get("/kbdb/map", async (c) => {
if (!tenant(c)) return c.json(NEED_KEY, 401);
const { base, headers } = kbdbBase(c.env);
const owner = c.req.query("owner_id");
const qs = owner ? `?owner_id=${encodeURIComponent(owner)}` : "";
try {
const res = await fetch(`${base}/map${qs}`, { headers });
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
} catch (e) {
return c.json({ success: false, error: `KBDB \u4E0D\u53EF\u9054\uFF08${base}\uFF09\uFF1A${e instanceof Error ? e.message : String(e)}` }, 502);
}
});
kbdbProxyRouter.get("/kbdb/map/:library", async (c) => {
if (!tenant(c)) return c.json(NEED_KEY, 401);
const { base, headers } = kbdbBase(c.env);
const owner = c.req.query("owner_id");
const qs = owner ? `?owner_id=${encodeURIComponent(owner)}` : "";
try {
const res = await fetch(`${base}/map/${encodeURIComponent(c.req.param("library"))}${qs}`, { headers });
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
} catch (e) {
return c.json({ success: false, error: `KBDB \u4E0D\u53EF\u9054\uFF08${base}\uFF09\uFF1A${e instanceof Error ? e.message : String(e)}` }, 502);
}
});
kbdbProxyRouter.patch("/kbdb/entries/:id", async (c) => {
if (!tenant(c)) return c.json(NEED_KEY, 401);
const body = await c.req.json().catch(() => ({}));
const { owner_id: _drop, ...patch } = body ?? {};
const { base, headers } = kbdbBase(c.env);
const res = await fetch(`${base}/entries/${encodeURIComponent(c.req.param("id"))}`, {
method: "PATCH",
headers,
body: JSON.stringify(patch)
});
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
});
}
});
// cypher-executor/src/routes/credentials.ts
async function deriveSecretRef(apiKey, name) {
const hash8 = await sha256Prefix(apiKey);
return `CRED_${name.toUpperCase()}_${hash8.toUpperCase()}`;
}
async function storeCredential(env, apiKey, name, value, service) {
const secretRef = await deriveSecretRef(apiKey, name);
await putWorkerSecret(env, secretRef, value);
await upsertCredentialEntry(env, apiKey, name, service, "standard", secretRef);
}
function validateName(name) {
return typeof name === "string" && /^\w+$/.test(name);
}
function validSensitivity(s) {
return s === "standard" || s === "high";
}
async function putWorkerSecret(env, secretRef, value) {
if (!env.CF_SECRETS_API_TOKEN || !env.CF_ACCOUNT_ID) {
throw new Error(
"\u6B64 worker \u7F3A CF_SECRETS_API_TOKEN / CF_ACCOUNT_ID \u8A2D\u5B9A\uFF0C\u5BEB\u5165\u8DEF\u5F91\u672A\u5C31\u7DD2\uFF08\u898B credential-store-migration.md T3\uFF1Aacr init/update \u61C9\u78BA\u4FDD\u9019\u5169\u9805\u5C31\u7DD2\uFF09"
);
}
const url = `https://api.cloudflare.com/client/v4/accounts/${env.CF_ACCOUNT_ID}/workers/scripts/${CYPHER_SCRIPT_NAME}/secrets`;
const res = await fetch(url, {
method: "PUT",
headers: {
Authorization: `Bearer ${env.CF_SECRETS_API_TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ name: secretRef, text: value, type: "secret_text" })
});
const body = await res.json().catch(() => null);
if (!res.ok || !body?.success) {
const detail = body?.errors?.map((e) => e.message).filter(Boolean).join("; ") || `HTTP ${res.status}`;
throw new Error(`CF Workers Secrets \u5BEB\u5165\u5931\u6557\uFF1A${detail}`);
}
}
async function deleteWorkerSecret(env, secretRef) {
if (!env.CF_SECRETS_API_TOKEN || !env.CF_ACCOUNT_ID) {
throw new Error("\u6B64 worker \u7F3A CF_SECRETS_API_TOKEN / CF_ACCOUNT_ID \u8A2D\u5B9A\uFF0C\u522A\u9664\u8DEF\u5F91\u672A\u5C31\u7DD2");
}
const url = `https://api.cloudflare.com/client/v4/accounts/${env.CF_ACCOUNT_ID}/workers/scripts/${CYPHER_SCRIPT_NAME}/secrets/${secretRef}`;
const res = await fetch(url, {
method: "DELETE",
headers: { Authorization: `Bearer ${env.CF_SECRETS_API_TOKEN}` }
});
if (res.status === 404) return;
const body = await res.json().catch(() => null);
if (!res.ok || !body?.success) {
const detail = body?.errors?.map((e) => e.message).filter(Boolean).join("; ") || `HTTP ${res.status}`;
throw new Error(`CF Workers Secrets \u522A\u9664\u5931\u6557\uFF1A${detail}`);
}
}
function parseMeta(row) {
try {
const m = row.metadata_json ? JSON.parse(row.metadata_json) : {};
return {
service: typeof m.service === "string" ? m.service : null,
sensitivity: m.sensitivity === "high" ? "high" : "standard",
secret_ref: typeof m.secret_ref === "string" ? m.secret_ref : "",
last_used_at: typeof m.last_used_at === "number" ? m.last_used_at : null
};
} catch {
return { service: null, sensitivity: "standard", secret_ref: "", last_used_at: null };
}
}
async function kbdbCredFetch(env, path, init) {
const { base, headers } = kbdbBase(env);
return fetch(`${base}${path}`, {
...init,
headers: { ...headers, ...init?.headers }
});
}
function invalidateCredentialCache(apiKey) {
delete dirCache[apiKey];
}
async function getCredentialDirectory(env, apiKey) {
const now2 = Date.now();
const cached = dirCache[apiKey];
if (cached && now2 - cached.fetchedAt < DIR_CACHE_TTL_MS) return cached.rows;
const qs = new URLSearchParams({ owner_id: apiKey, entry_type: CREDENTIAL_ENTRY_TYPE, limit: "200" });
const res = await kbdbCredFetch(env, `/entries?${qs.toString()}`);
if (!res.ok) {
return [];
}
const body = await res.json().catch(() => null);
const rows = (body?.entries ?? []).filter((e) => !!e.page_name).map((e) => {
const meta = parseMeta(e);
return {
id: e.id,
name: e.page_name,
secret_ref: meta.secret_ref,
service: meta.service,
sensitivity: meta.sensitivity,
last_used_at: meta.last_used_at
};
});
dirCache[apiKey] = { rows, fetchedAt: now2 };
return rows;
}
async function getCredentialSecretRefs(env, apiKey) {
const rows = await getCredentialDirectory(env, apiKey);
const out = {};
for (const r of rows) {
if (r.secret_ref) out[r.name] = r.secret_ref;
}
return out;
}
function touchLastUsed(env, apiKey, names) {
const cached = dirCache[apiKey];
if (!cached || names.length === 0) return;
const now2 = Math.floor(Date.now() / 1e3);
for (const r of cached.rows) {
if (!names.includes(r.name)) continue;
const meta = {
service: r.service,
sensitivity: r.sensitivity,
secret_ref: r.secret_ref,
last_used_at: now2
};
kbdbCredFetch(env, `/entries/${encodeURIComponent(r.id)}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ metadata_json: JSON.stringify(meta) })
}).catch(() => {
});
r.last_used_at = now2;
}
}
async function findCredentialEntry(env, apiKey, name) {
const qs = new URLSearchParams({
owner_id: apiKey,
entry_type: CREDENTIAL_ENTRY_TYPE,
page_name: name,
limit: "1"
});
const res = await kbdbCredFetch(env, `/entries?${qs.toString()}`);
if (!res.ok) throw new Error(`KBDB /entries \u67E5\u8A62\u5931\u6557\uFF1AHTTP ${res.status}`);
const body = await res.json().catch(() => null);
return body?.entries?.[0] ?? null;
}
async function upsertCredentialEntry(env, apiKey, name, service, sensitivity, secretRef) {
const existing = await findCredentialEntry(env, apiKey, name);
const meta = {
service,
sensitivity,
secret_ref: secretRef,
last_used_at: existing ? parseMeta(existing).last_used_at : null
};
if (existing) {
const res = await kbdbCredFetch(env, `/entries/${encodeURIComponent(existing.id)}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ metadata_json: JSON.stringify(meta) })
});
if (!res.ok) throw new Error(`credential \u76EE\u9304\u66F4\u65B0\u5931\u6557\uFF1AHTTP ${res.status}`);
} else {
const res = await kbdbCredFetch(env, `/entries`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
entry_type: CREDENTIAL_ENTRY_TYPE,
owner_id: apiKey,
page_name: name,
metadata_json: JSON.stringify(meta)
})
});
if (!res.ok) throw new Error(`credential \u76EE\u9304\u5EFA\u7ACB\u5931\u6557\uFF1AHTTP ${res.status}`);
}
invalidateCredentialCache(apiKey);
}
async function listCredentialRows(env, apiKey) {
const qs = new URLSearchParams({ owner_id: apiKey, entry_type: CREDENTIAL_ENTRY_TYPE, limit: "200" });
const res = await kbdbCredFetch(env, `/entries?${qs.toString()}`);
if (!res.ok) throw new Error(`credential \u76EE\u9304\u67E5\u8A62\u5931\u6557\uFF1AHTTP ${res.status}`);
const body = await res.json().catch(() => null);
const rows = (body?.entries ?? []).filter((e) => !!e.page_name).map((e) => {
const meta = parseMeta(e);
return { name: e.page_name, service: meta.service, sensitivity: meta.sensitivity, created_at: e.created_at, last_used_at: meta.last_used_at };
});
return rows;
}
async function hasCredential(env, apiKey, name) {
const entry = await findCredentialEntry(env, apiKey, name);
return entry !== null;
}
async function writeCredential(env, apiKey, name, value, service, sensitivityRaw) {
const sensitivity = validSensitivity(sensitivityRaw) ? sensitivityRaw : "standard";
const secretRef = await deriveSecretRef(apiKey, name);
await putWorkerSecret(env, secretRef, value);
await upsertCredentialEntry(env, apiKey, name, service ?? null, sensitivity, secretRef);
return { secretRef, sensitivity };
}
var credentialsRouter, CYPHER_SCRIPT_NAME, CREDENTIAL_ENTRY_TYPE, DIR_CACHE_TTL_MS, dirCache;
var init_credentials = __esm({
"cypher-executor/src/routes/credentials.ts"() {
"use strict";
init_dist();
init_hash();
init_kbdb_proxy();
credentialsRouter = new Hono2();
CYPHER_SCRIPT_NAME = "arcrun-cypher-executor";
CREDENTIAL_ENTRY_TYPE = "credential";
DIR_CACHE_TTL_MS = 6e4;
dirCache = {};
credentialsRouter.post("/credentials", async (c) => {
const apiKey = c.req.header("X-Arcrun-API-Key");
if (!apiKey) {
return c.json({ error: "\u7F3A\u5C11 X-Arcrun-API-Key header" }, 401);
}
const body = await c.req.json().catch(() => null);
if (!validateName(body?.name)) {
return c.json({ error: "name \u5FC5\u586B\uFF0C\u53EA\u80FD\u5305\u542B\u82F1\u6587\u5B57\u6BCD\u3001\u6578\u5B57\u548C\u5E95\u7DDA" }, 400);
}
if (!body?.value || typeof body.value !== "string") {
return c.json({ error: "value \u5FC5\u586B\uFF08credential \u660E\u6587\u503C\uFF0C\u7D93 TLS \u50B3\u8F38\uFF09" }, 400);
}
try {
const { secretRef, sensitivity } = await writeCredential(
c.env,
apiKey,
body.name,
body.value,
body.service,
body.sensitivity
);
return c.json({ success: true, name: body.name, service: body.service ?? null, sensitivity, secret_ref: secretRef });
} catch (e) {
return c.json({ success: false, error: e instanceof Error ? e.message : String(e) }, 502);
}
});
credentialsRouter.put("/credentials/:name", async (c) => {
const apiKey = c.req.header("X-Arcrun-API-Key");
if (!apiKey) {
return c.json({ error: "\u7F3A\u5C11 X-Arcrun-API-Key header" }, 401);
}
const name = c.req.param("name");
if (!validateName(name)) {
return c.json({ error: "name \u53EA\u80FD\u5305\u542B\u82F1\u6587\u5B57\u6BCD\u3001\u6578\u5B57\u548C\u5E95\u7DDA" }, 400);
}
const body = await c.req.json().catch(() => null);
if (!body?.value || typeof body.value !== "string") {
return c.json({ error: "value \u5FC5\u586B\uFF08credential \u660E\u6587\u503C\uFF0C\u7D93 TLS \u50B3\u8F38\uFF09" }, 400);
}
try {
const { secretRef, sensitivity } = await writeCredential(
c.env,
apiKey,
name,
body.value,
body.service,
body.sensitivity
);
return c.json({ success: true, name, service: body.service ?? null, sensitivity, secret_ref: secretRef });
} catch (e) {
return c.json({ success: false, error: e instanceof Error ? e.message : String(e) }, 502);
}
});
credentialsRouter.delete("/credentials/:name", async (c) => {
const apiKey = c.req.header("X-Arcrun-API-Key");
if (!apiKey) {
return c.json({ error: "\u7F3A\u5C11 X-Arcrun-API-Key header" }, 401);
}
const name = c.req.param("name");
try {
const entry = await findCredentialEntry(c.env, apiKey, name);
if (entry) {
const meta = parseMeta(entry);
if (meta.secret_ref) await deleteWorkerSecret(c.env, meta.secret_ref);
const res = await kbdbCredFetch(c.env, `/entries/${encodeURIComponent(entry.id)}`, { method: "DELETE" });
if (!res.ok) throw new Error(`credential \u76EE\u9304\u522A\u9664\u5931\u6557\uFF1AHTTP ${res.status}`);
invalidateCredentialCache(apiKey);
return c.json({ success: true, name, source: "workers-secrets" });
}
await c.env.CREDENTIALS_KV.delete(`${apiKey}:cred:${name}`);
return c.json({ success: true, name, source: "legacy-kv" });
} catch (e) {
return c.json({ success: false, error: e instanceof Error ? e.message : String(e) }, 502);
}
});
credentialsRouter.get("/credentials/catalog", async (c) => {
const apiKey = c.req.header("X-Arcrun-API-Key");
if (!apiKey) {
return c.json({ error: "\u7F3A\u5C11 X-Arcrun-API-Key header" }, 401);
}
try {
const rows = await listCredentialRows(c.env, apiKey);
return c.json({ success: true, credentials: rows, total: rows.length });
} catch (e) {
return c.json({ success: false, error: e instanceof Error ? e.message : String(e) }, 502);
}
});
credentialsRouter.get("/credentials", async (c) => {
const apiKey = c.req.header("X-Arcrun-API-Key");
if (!apiKey) {
return c.json({ error: "\u7F3A\u5C11 X-Arcrun-API-Key header" }, 401);
}
try {
const rows = await listCredentialRows(c.env, apiKey);
return c.json({ success: true, credentials: rows, total: rows.length });
} catch (e) {
return c.json({ success: false, error: e instanceof Error ? e.message : String(e) }, 502);
}
});
}
});
// cypher-executor/src/actions/auth-dispatcher.ts
async function resolveSecretsFromNewHome(env, apiKey, names) {
const resolved = {};
if (names.length === 0) return resolved;
const refs = await getCredentialSecretRefs(env, apiKey);
if (Object.keys(refs).length === 0) return resolved;
const secretGet2 = createArcrunHostFunctions(env, apiKey).secret_get;
if (!secretGet2) return resolved;
const resolvedNames = [];
for (const name of names) {
const ref = refs[name];
if (!ref) continue;
const value = await secretGet2(ref);
if (value === null) continue;
resolved[name] = value;
resolvedNames.push(name);
}
if (resolvedNames.length > 0) touchLastUsed(env, apiKey, resolvedNames);
return resolved;
}
async function tryAuthDispatch(componentId, input, env, apiKey) {
if (AUTH_PRIMITIVE_IDS.has(componentId)) {
return null;
}
let service = componentId;
const apiRecipe = await resolveRecipe(componentId, env.RECIPES);
if (apiRecipe?.auth_service) {
service = apiRecipe.auth_service;
}
const recipe = await resolveAuthRecipe(service, env.RECIPES);
if (!recipe) return null;
if (!SUPPORTED_PRIMITIVES.has(recipe.primitive)) return null;
const secretNames = recipe.required_secrets.filter((s) => !s.optional).map((s) => s.key);
const resolvedSecrets = await resolveSecretsFromNewHome(env, apiKey, secretNames);
const primitiveUrl = wasmWorkerUrl(`auth_${recipe.primitive}`, env.WORKER_SUBDOMAIN);
const res = await fetch(primitiveUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
action: "authenticate",
api_key: apiKey,
service,
// 只在有取到值時帶上(空物件也無妨,WASM 對 nil/空 map 同樣 fallback)
resolved_secrets: resolvedSecrets
})
});
if (!res.ok) {
const text = await res.text().catch(() => "");
throw new Error(
`auth primitive "${recipe.primitive}" \u56DE\u50B3 ${res.status}: ${text.slice(0, 200)}`
);
}
const result = await res.json().catch(() => null);
if (!result || result.success === false) {
throw new Error(
`auth primitive \u5931\u6557: ${result?.error ?? "\u672A\u77E5\u932F\u8AA4"}`
);
}
return {
...input,
_auth_headers: result.auth_headers ?? {},
_auth_query: result.auth_query ?? {},
_auth_body: result.auth_body ?? {},
_auth_path: result.auth_path ?? {}
};
}
function collectCredentialNames(value, out) {
if (typeof value === "string") {
for (const m of value.matchAll(CREDENTIAL_REF)) out.add(m[1]);
} else if (Array.isArray(value)) {
for (const v of value) collectCredentialNames(v, out);
} else if (value && typeof value === "object") {
for (const v of Object.values(value)) collectCredentialNames(v, out);
}
}
function replaceCredentialRefs(value, resolved) {
if (typeof value === "string") {
return value.replace(
CREDENTIAL_REF,
(orig, name) => Object.prototype.hasOwnProperty.call(resolved, name) ? resolved[name] : orig
);
}
if (Array.isArray(value)) return value.map((v) => replaceCredentialRefs(v, resolved));
if (value && typeof value === "object") {
const out = {};
for (const [k, v] of Object.entries(value)) {
out[k] = replaceCredentialRefs(v, resolved);
}
return out;
}
return value;
}
async function resolveCredentialRefs(data, env, apiKey) {
const names = /* @__PURE__ */ new Set();
collectCredentialNames(data, names);
if (names.size === 0) return data;
const nameList = [...names];
const resolvedSecrets = await resolveSecretsFromNewHome(env, apiKey, nameList);
const url = wasmWorkerUrl("auth_static_key", env.WORKER_SUBDOMAIN);
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
action: "resolve_credentials",
api_key: apiKey,
names: nameList,
resolved_secrets: resolvedSecrets
})
});
if (!res.ok) {
const text = await res.text().catch(() => "");
throw new Error(`credential resolve \u56DE\u50B3 ${res.status}: ${text.slice(0, 200)}`);
}
const result = await res.json().catch(() => null);
if (!result || result.success === false) {
throw new Error(`credential resolve \u5931\u6557: ${result?.error ?? "\u672A\u77E5\u932F\u8AA4"}`);
}
return replaceCredentialRefs(data, result.credentials ?? {});
}
var SUPPORTED_PRIMITIVES, AUTH_PRIMITIVE_IDS, CREDENTIAL_REF;
var init_auth_dispatcher = __esm({
"cypher-executor/src/actions/auth-dispatcher.ts"() {
"use strict";
init_recipes();
init_component_loader();
init_wasi_shim();
init_credentials();
SUPPORTED_PRIMITIVES = /* @__PURE__ */ new Set(["static_key", "service_account", "oauth2"]);
AUTH_PRIMITIVE_IDS = /* @__PURE__ */ new Set([
"auth_static_key",
"auth_service_account",
"auth_oauth2",
"auth_mtls"
]);
CREDENTIAL_REF = /\{\{credential\.(\w+)\}\}/g;
}
});
// cypher-executor/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/index.mjs
function setErrorMap(map) {
overrideErrorMap = map;
}
function getErrorMap() {
return overrideErrorMap;
}
function addIssueToContext(ctx, issueData) {
const overrideMap = getErrorMap();
const issue = makeIssue({
issueData,
data: ctx.data,
path: ctx.path,
errorMaps: [
ctx.common.contextualErrorMap,
ctx.schemaErrorMap,
overrideMap,
overrideMap === errorMap ? void 0 : errorMap
// then global default map
].filter((x) => !!x)
});
ctx.common.issues.push(issue);
}
function __classPrivateFieldGet(receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
}
function __classPrivateFieldSet(receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value), value;
}
function processCreateParams(params) {
if (!params)
return {};
const { errorMap: errorMap2, invalid_type_error, required_error, description } = params;
if (errorMap2 && (invalid_type_error || required_error)) {
throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`);
}
if (errorMap2)
return { errorMap: errorMap2, description };
const customMap = (iss, ctx) => {
var _a, _b;
const { message } = params;
if (iss.code === "invalid_enum_value") {
return { message: message !== null && message !== void 0 ? message : ctx.defaultError };
}
if (typeof ctx.data === "undefined") {
return { message: (_a = message !== null && message !== void 0 ? message : required_error) !== null && _a !== void 0 ? _a : ctx.defaultError };
}
if (iss.code !== "invalid_type")
return { message: ctx.defaultError };
return { message: (_b = message !== null && message !== void 0 ? message : invalid_type_error) !== null && _b !== void 0 ? _b : ctx.defaultError };
};
return { errorMap: customMap, description };
}
function timeRegexSource(args) {
let regex = `([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d`;
if (args.precision) {
regex = `${regex}\\.\\d{${args.precision}}`;
} else if (args.precision == null) {
regex = `${regex}(\\.\\d+)?`;
}
return regex;
}
function timeRegex(args) {
return new RegExp(`^${timeRegexSource(args)}$`);
}
function datetimeRegex(args) {
let regex = `${dateRegexSource}T${timeRegexSource(args)}`;
const opts = [];
opts.push(args.local ? `Z?` : `Z`);
if (args.offset)
opts.push(`([+-]\\d{2}:?\\d{2})`);
regex = `${regex}(${opts.join("|")})`;
return new RegExp(`^${regex}$`);
}
function isValidIP(ip, version) {
if ((version === "v4" || !version) && ipv4Regex.test(ip)) {
return true;
}
if ((version === "v6" || !version) && ipv6Regex.test(ip)) {
return true;
}
return false;
}
function floatSafeRemainder(val, step) {
const valDecCount = (val.toString().split(".")[1] || "").length;
const stepDecCount = (step.toString().split(".")[1] || "").length;
const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;
const valInt = parseInt(val.toFixed(decCount).replace(".", ""));
const stepInt = parseInt(step.toFixed(decCount).replace(".", ""));
return valInt % stepInt / Math.pow(10, decCount);
}
function deepPartialify(schema) {
if (schema instanceof ZodObject) {
const newShape = {};
for (const key in schema.shape) {
const fieldSchema = schema.shape[key];
newShape[key] = ZodOptional.create(deepPartialify(fieldSchema));
}
return new ZodObject({
...schema._def,
shape: () => newShape
});
} else if (schema instanceof ZodArray) {
return new ZodArray({
...schema._def,
type: deepPartialify(schema.element)
});
} else if (schema instanceof ZodOptional) {
return ZodOptional.create(deepPartialify(schema.unwrap()));
} else if (schema instanceof ZodNullable) {
return ZodNullable.create(deepPartialify(schema.unwrap()));
} else if (schema instanceof ZodTuple) {
return ZodTuple.create(schema.items.map((item) => deepPartialify(item)));
} else {
return schema;
}
}
function mergeValues(a, b) {
const aType = getParsedType(a);
const bType = getParsedType(b);
if (a === b) {
return { valid: true, data: a };
} else if (aType === ZodParsedType.object && bType === ZodParsedType.object) {
const bKeys = util.objectKeys(b);
const sharedKeys = util.objectKeys(a).filter((key) => bKeys.indexOf(key) !== -1);
const newObj = { ...a, ...b };
for (const key of sharedKeys) {
const sharedValue = mergeValues(a[key], b[key]);
if (!sharedValue.valid) {
return { valid: false };
}
newObj[key] = sharedValue.data;
}
return { valid: true, data: newObj };
} else if (aType === ZodParsedType.array && bType === ZodParsedType.array) {
if (a.length !== b.length) {
return { valid: false };
}
const newArray = [];
for (let index = 0; index < a.length; index++) {
const itemA = a[index];
const itemB = b[index];
const sharedValue = mergeValues(itemA, itemB);
if (!sharedValue.valid) {
return { valid: false };
}
newArray.push(sharedValue.data);
}
return { valid: true, data: newArray };
} else if (aType === ZodParsedType.date && bType === ZodParsedType.date && +a === +b) {
return { valid: true, data: a };
} else {
return { valid: false };
}
}
function createZodEnum(values, params) {
return new ZodEnum({
values,
typeName: ZodFirstPartyTypeKind.ZodEnum,
...processCreateParams(params)
});
}
function custom(check, params = {}, fatal) {
if (check)
return ZodAny.create().superRefine((data, ctx) => {
var _a, _b;
if (!check(data)) {
const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
const _fatal = (_b = (_a = p.fatal) !== null && _a !== void 0 ? _a : fatal) !== null && _b !== void 0 ? _b : true;
const p2 = typeof p === "string" ? { message: p } : p;
ctx.addIssue({ code: "custom", ...p2, fatal: _fatal });
}
});
return ZodAny.create();
}
var util, objectUtil, ZodParsedType, getParsedType, ZodIssueCode, quotelessJson, ZodError, errorMap, overrideErrorMap, makeIssue, EMPTY_PATH, ParseStatus, INVALID, DIRTY, OK, isAborted, isDirty, isValid, isAsync, errorUtil, _ZodEnum_cache, _ZodNativeEnum_cache, ParseInputLazyPath, handleResult, ZodType, cuidRegex, cuid2Regex, ulidRegex, uuidRegex, nanoidRegex, durationRegex, emailRegex, _emojiRegex, emojiRegex, ipv4Regex, ipv6Regex, base64Regex, dateRegexSource, dateRegex, ZodString, ZodNumber, ZodBigInt, ZodBoolean, ZodDate, ZodSymbol, ZodUndefined, ZodNull, ZodAny, ZodUnknown, ZodNever, ZodVoid, ZodArray, ZodObject, ZodUnion, getDiscriminator, ZodDiscriminatedUnion, ZodIntersection, ZodTuple, ZodRecord, ZodMap, ZodSet, ZodFunction, ZodLazy, ZodLiteral, ZodEnum, ZodNativeEnum, ZodPromise, ZodEffects, ZodOptional, ZodNullable, ZodDefault, ZodCatch, ZodNaN, BRAND, ZodBranded, ZodPipeline, ZodReadonly, late, ZodFirstPartyTypeKind, instanceOfType, stringType, numberType, nanType, bigIntType, booleanType, dateType, symbolType, undefinedType, nullType, anyType, unknownType, neverType, voidType, arrayType, objectType, strictObjectType, unionType, discriminatedUnionType, intersectionType, tupleType, recordType, mapType, setType, functionType, lazyType, literalType, enumType, nativeEnumType, promiseType, effectsType, optionalType, nullableType, preprocessType, pipelineType, ostring, onumber, oboolean, coerce, NEVER, z;
var init_lib = __esm({
"cypher-executor/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/index.mjs"() {
(function(util2) {
util2.assertEqual = (val) => val;
function assertIs(_arg) {
}
util2.assertIs = assertIs;
function assertNever(_x) {
throw new Error();
}
util2.assertNever = assertNever;
util2.arrayToEnum = (items) => {
const obj = {};
for (const item of items) {
obj[item] = item;
}
return obj;
};
util2.getValidEnumValues = (obj) => {
const validKeys = util2.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== "number");
const filtered = {};
for (const k of validKeys) {
filtered[k] = obj[k];
}
return util2.objectValues(filtered);
};
util2.objectValues = (obj) => {
return util2.objectKeys(obj).map(function(e) {
return obj[e];
});
};
util2.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object) => {
const keys = [];
for (const key in object) {
if (Object.prototype.hasOwnProperty.call(object, key)) {
keys.push(key);
}
}
return keys;
};
util2.find = (arr, checker) => {
for (const item of arr) {
if (checker(item))
return item;
}
return void 0;
};
util2.isInteger = typeof Number.isInteger === "function" ? (val) => Number.isInteger(val) : (val) => typeof val === "number" && isFinite(val) && Math.floor(val) === val;
function joinValues(array, separator = " | ") {
return array.map((val) => typeof val === "string" ? `'${val}'` : val).join(separator);
}
util2.joinValues = joinValues;
util2.jsonStringifyReplacer = (_, value) => {
if (typeof value === "bigint") {
return value.toString();
}
return value;
};
})(util || (util = {}));
(function(objectUtil2) {
objectUtil2.mergeShapes = (first, second) => {
return {
...first,
...second
// second overwrites first
};
};
})(objectUtil || (objectUtil = {}));
ZodParsedType = util.arrayToEnum([
"string",
"nan",
"number",
"integer",
"float",
"boolean",
"date",
"bigint",
"symbol",
"function",
"undefined",
"null",
"array",
"object",
"unknown",
"promise",
"void",
"never",
"map",
"set"
]);
getParsedType = (data) => {
const t = typeof data;
switch (t) {
case "undefined":
return ZodParsedType.undefined;
case "string":
return ZodParsedType.string;
case "number":
return isNaN(data) ? ZodParsedType.nan : ZodParsedType.number;
case "boolean":
return ZodParsedType.boolean;
case "function":
return ZodParsedType.function;
case "bigint":
return ZodParsedType.bigint;
case "symbol":
return ZodParsedType.symbol;
case "object":
if (Array.isArray(data)) {
return ZodParsedType.array;
}
if (data === null) {
return ZodParsedType.null;
}
if (data.then && typeof data.then === "function" && data.catch && typeof data.catch === "function") {
return ZodParsedType.promise;
}
if (typeof Map !== "undefined" && data instanceof Map) {
return ZodParsedType.map;
}
if (typeof Set !== "undefined" && data instanceof Set) {
return ZodParsedType.set;
}
if (typeof Date !== "undefined" && data instanceof Date) {
return ZodParsedType.date;
}
return ZodParsedType.object;
default:
return ZodParsedType.unknown;
}
};
ZodIssueCode = util.arrayToEnum([
"invalid_type",
"invalid_literal",
"custom",
"invalid_union",
"invalid_union_discriminator",
"invalid_enum_value",
"unrecognized_keys",
"invalid_arguments",
"invalid_return_type",
"invalid_date",
"invalid_string",
"too_small",
"too_big",
"invalid_intersection_types",
"not_multiple_of",
"not_finite"
]);
quotelessJson = (obj) => {
const json = JSON.stringify(obj, null, 2);
return json.replace(/"([^"]+)":/g, "$1:");
};
ZodError = class _ZodError extends Error {
constructor(issues) {
super();
this.issues = [];
this.addIssue = (sub) => {
this.issues = [...this.issues, sub];
};
this.addIssues = (subs = []) => {
this.issues = [...this.issues, ...subs];
};
const actualProto = new.target.prototype;
if (Object.setPrototypeOf) {
Object.setPrototypeOf(this, actualProto);
} else {
this.__proto__ = actualProto;
}
this.name = "ZodError";
this.issues = issues;
}
get errors() {
return this.issues;
}
format(_mapper) {
const mapper = _mapper || function(issue) {
return issue.message;
};
const fieldErrors = { _errors: [] };
const processError = (error) => {
for (const issue of error.issues) {
if (issue.code === "invalid_union") {
issue.unionErrors.map(processError);
} else if (issue.code === "invalid_return_type") {
processError(issue.returnTypeError);
} else if (issue.code === "invalid_arguments") {
processError(issue.argumentsError);
} else if (issue.path.length === 0) {
fieldErrors._errors.push(mapper(issue));
} else {
let curr = fieldErrors;
let i = 0;
while (i < issue.path.length) {
const el = issue.path[i];
const terminal = i === issue.path.length - 1;
if (!terminal) {
curr[el] = curr[el] || { _errors: [] };
} else {
curr[el] = curr[el] || { _errors: [] };
curr[el]._errors.push(mapper(issue));
}
curr = curr[el];
i++;
}
}
}
};
processError(this);
return fieldErrors;
}
static assert(value) {
if (!(value instanceof _ZodError)) {
throw new Error(`Not a ZodError: ${value}`);
}
}
toString() {
return this.message;
}
get message() {
return JSON.stringify(this.issues, util.jsonStringifyReplacer, 2);
}
get isEmpty() {
return this.issues.length === 0;
}
flatten(mapper = (issue) => issue.message) {
const fieldErrors = {};
const formErrors = [];
for (const sub of this.issues) {
if (sub.path.length > 0) {
fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || [];
fieldErrors[sub.path[0]].push(mapper(sub));
} else {
formErrors.push(mapper(sub));
}
}
return { formErrors, fieldErrors };
}
get formErrors() {
return this.flatten();
}
};
ZodError.create = (issues) => {
const error = new ZodError(issues);
return error;
};
errorMap = (issue, _ctx) => {
let message;
switch (issue.code) {
case ZodIssueCode.invalid_type:
if (issue.received === ZodParsedType.undefined) {
message = "Required";
} else {
message = `Expected ${issue.expected}, received ${issue.received}`;
}
break;
case ZodIssueCode.invalid_literal:
message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util.jsonStringifyReplacer)}`;
break;
case ZodIssueCode.unrecognized_keys:
message = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, ", ")}`;
break;
case ZodIssueCode.invalid_union:
message = `Invalid input`;
break;
case ZodIssueCode.invalid_union_discriminator:
message = `Invalid discriminator value. Expected ${util.joinValues(issue.options)}`;
break;
case ZodIssueCode.invalid_enum_value:
message = `Invalid enum value. Expected ${util.joinValues(issue.options)}, received '${issue.received}'`;
break;
case ZodIssueCode.invalid_arguments:
message = `Invalid function arguments`;
break;
case ZodIssueCode.invalid_return_type:
message = `Invalid function return type`;
break;
case ZodIssueCode.invalid_date:
message = `Invalid date`;
break;
case ZodIssueCode.invalid_string:
if (typeof issue.validation === "object") {
if ("includes" in issue.validation) {
message = `Invalid input: must include "${issue.validation.includes}"`;
if (typeof issue.validation.position === "number") {
message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`;
}
} else if ("startsWith" in issue.validation) {
message = `Invalid input: must start with "${issue.validation.startsWith}"`;
} else if ("endsWith" in issue.validation) {
message = `Invalid input: must end with "${issue.validation.endsWith}"`;
} else {
util.assertNever(issue.validation);
}
} else if (issue.validation !== "regex") {
message = `Invalid ${issue.validation}`;
} else {
message = "Invalid";
}
break;
case ZodIssueCode.too_small:
if (issue.type === "array")
message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`;
else if (issue.type === "string")
message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`;
else if (issue.type === "number")
message = `Number must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${issue.minimum}`;
else if (issue.type === "date")
message = `Date must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${new Date(Number(issue.minimum))}`;
else
message = "Invalid input";
break;
case ZodIssueCode.too_big:
if (issue.type === "array")
message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`;
else if (issue.type === "string")
message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`;
else if (issue.type === "number")
message = `Number must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`;
else if (issue.type === "bigint")
message = `BigInt must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`;
else if (issue.type === "date")
message = `Date must be ${issue.exact ? `exactly` : issue.inclusive ? `smaller than or equal to` : `smaller than`} ${new Date(Number(issue.maximum))}`;
else
message = "Invalid input";
break;
case ZodIssueCode.custom:
message = `Invalid input`;
break;
case ZodIssueCode.invalid_intersection_types:
message = `Intersection results could not be merged`;
break;
case ZodIssueCode.not_multiple_of:
message = `Number must be a multiple of ${issue.multipleOf}`;
break;
case ZodIssueCode.not_finite:
message = "Number must be finite";
break;
default:
message = _ctx.defaultError;
util.assertNever(issue);
}
return { message };
};
overrideErrorMap = errorMap;
makeIssue = (params) => {
const { data, path, errorMaps, issueData } = params;
const fullPath = [...path, ...issueData.path || []];
const fullIssue = {
...issueData,
path: fullPath
};
if (issueData.message !== void 0) {
return {
...issueData,
path: fullPath,
message: issueData.message
};
}
let errorMessage = "";
const maps = errorMaps.filter((m) => !!m).slice().reverse();
for (const map of maps) {
errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message;
}
return {
...issueData,
path: fullPath,
message: errorMessage
};
};
EMPTY_PATH = [];
ParseStatus = class _ParseStatus {
constructor() {
this.value = "valid";
}
dirty() {
if (this.value === "valid")
this.value = "dirty";
}
abort() {
if (this.value !== "aborted")
this.value = "aborted";
}
static mergeArray(status, results) {
const arrayValue = [];
for (const s of results) {
if (s.status === "aborted")
return INVALID;
if (s.status === "dirty")
status.dirty();
arrayValue.push(s.value);
}
return { status: status.value, value: arrayValue };
}
static async mergeObjectAsync(status, pairs) {
const syncPairs = [];
for (const pair of pairs) {
const key = await pair.key;
const value = await pair.value;
syncPairs.push({
key,
value
});
}
return _ParseStatus.mergeObjectSync(status, syncPairs);
}
static mergeObjectSync(status, pairs) {
const finalObject = {};
for (const pair of pairs) {
const { key, value } = pair;
if (key.status === "aborted")
return INVALID;
if (value.status === "aborted")
return INVALID;
if (key.status === "dirty")
status.dirty();
if (value.status === "dirty")
status.dirty();
if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) {
finalObject[key.value] = value.value;
}
}
return { status: status.value, value: finalObject };
}
};
INVALID = Object.freeze({
status: "aborted"
});
DIRTY = (value) => ({ status: "dirty", value });
OK = (value) => ({ status: "valid", value });
isAborted = (x) => x.status === "aborted";
isDirty = (x) => x.status === "dirty";
isValid = (x) => x.status === "valid";
isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
(function(errorUtil2) {
errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {};
errorUtil2.toString = (message) => typeof message === "string" ? message : message === null || message === void 0 ? void 0 : message.message;
})(errorUtil || (errorUtil = {}));
ParseInputLazyPath = class {
constructor(parent, value, path, key) {
this._cachedPath = [];
this.parent = parent;
this.data = value;
this._path = path;
this._key = key;
}
get path() {
if (!this._cachedPath.length) {
if (this._key instanceof Array) {
this._cachedPath.push(...this._path, ...this._key);
} else {
this._cachedPath.push(...this._path, this._key);
}
}
return this._cachedPath;
}
};
handleResult = (ctx, result) => {
if (isValid(result)) {
return { success: true, data: result.value };
} else {
if (!ctx.common.issues.length) {
throw new Error("Validation failed but no issues detected.");
}
return {
success: false,
get error() {
if (this._error)
return this._error;
const error = new ZodError(ctx.common.issues);
this._error = error;
return this._error;
}
};
}
};
ZodType = class {
constructor(def) {
this.spa = this.safeParseAsync;
this._def = def;
this.parse = this.parse.bind(this);
this.safeParse = this.safeParse.bind(this);
this.parseAsync = this.parseAsync.bind(this);
this.safeParseAsync = this.safeParseAsync.bind(this);
this.spa = this.spa.bind(this);
this.refine = this.refine.bind(this);
this.refinement = this.refinement.bind(this);
this.superRefine = this.superRefine.bind(this);
this.optional = this.optional.bind(this);
this.nullable = this.nullable.bind(this);
this.nullish = this.nullish.bind(this);
this.array = this.array.bind(this);
this.promise = this.promise.bind(this);
this.or = this.or.bind(this);
this.and = this.and.bind(this);
this.transform = this.transform.bind(this);
this.brand = this.brand.bind(this);
this.default = this.default.bind(this);
this.catch = this.catch.bind(this);
this.describe = this.describe.bind(this);
this.pipe = this.pipe.bind(this);
this.readonly = this.readonly.bind(this);
this.isNullable = this.isNullable.bind(this);
this.isOptional = this.isOptional.bind(this);
}
get description() {
return this._def.description;
}
_getType(input) {
return getParsedType(input.data);
}
_getOrReturnCtx(input, ctx) {
return ctx || {
common: input.parent.common,
data: input.data,
parsedType: getParsedType(input.data),
schemaErrorMap: this._def.errorMap,
path: input.path,
parent: input.parent
};
}
_processInputParams(input) {
return {
status: new ParseStatus(),
ctx: {
common: input.parent.common,
data: input.data,
parsedType: getParsedType(input.data),
schemaErrorMap: this._def.errorMap,
path: input.path,
parent: input.parent
}
};
}
_parseSync(input) {
const result = this._parse(input);
if (isAsync(result)) {
throw new Error("Synchronous parse encountered promise.");
}
return result;
}
_parseAsync(input) {
const result = this._parse(input);
return Promise.resolve(result);
}
parse(data, params) {
const result = this.safeParse(data, params);
if (result.success)
return result.data;
throw result.error;
}
safeParse(data, params) {
var _a;
const ctx = {
common: {
issues: [],
async: (_a = params === null || params === void 0 ? void 0 : params.async) !== null && _a !== void 0 ? _a : false,
contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap
},
path: (params === null || params === void 0 ? void 0 : params.path) || [],
schemaErrorMap: this._def.errorMap,
parent: null,
data,
parsedType: getParsedType(data)
};
const result = this._parseSync({ data, path: ctx.path, parent: ctx });
return handleResult(ctx, result);
}
async parseAsync(data, params) {
const result = await this.safeParseAsync(data, params);
if (result.success)
return result.data;
throw result.error;
}
async safeParseAsync(data, params) {
const ctx = {
common: {
issues: [],
contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap,
async: true
},
path: (params === null || params === void 0 ? void 0 : params.path) || [],
schemaErrorMap: this._def.errorMap,
parent: null,
data,
parsedType: getParsedType(data)
};
const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx });
const result = await (isAsync(maybeAsyncResult) ? maybeAsyncResult : Promise.resolve(maybeAsyncResult));
return handleResult(ctx, result);
}
refine(check, message) {
const getIssueProperties = (val) => {
if (typeof message === "string" || typeof message === "undefined") {
return { message };
} else if (typeof message === "function") {
return message(val);
} else {
return message;
}
};
return this._refinement((val, ctx) => {
const result = check(val);
const setError = () => ctx.addIssue({
code: ZodIssueCode.custom,
...getIssueProperties(val)
});
if (typeof Promise !== "undefined" && result instanceof Promise) {
return result.then((data) => {
if (!data) {
setError();
return false;
} else {
return true;
}
});
}
if (!result) {
setError();
return false;
} else {
return true;
}
});
}
refinement(check, refinementData) {
return this._refinement((val, ctx) => {
if (!check(val)) {
ctx.addIssue(typeof refinementData === "function" ? refinementData(val, ctx) : refinementData);
return false;
} else {
return true;
}
});
}
_refinement(refinement) {
return new ZodEffects({
schema: this,
typeName: ZodFirstPartyTypeKind.ZodEffects,
effect: { type: "refinement", refinement }
});
}
superRefine(refinement) {
return this._refinement(refinement);
}
optional() {
return ZodOptional.create(this, this._def);
}
nullable() {
return ZodNullable.create(this, this._def);
}
nullish() {
return this.nullable().optional();
}
array() {
return ZodArray.create(this, this._def);
}
promise() {
return ZodPromise.create(this, this._def);
}
or(option) {
return ZodUnion.create([this, option], this._def);
}
and(incoming) {
return ZodIntersection.create(this, incoming, this._def);
}
transform(transform) {
return new ZodEffects({
...processCreateParams(this._def),
schema: this,
typeName: ZodFirstPartyTypeKind.ZodEffects,
effect: { type: "transform", transform }
});
}
default(def) {
const defaultValueFunc = typeof def === "function" ? def : () => def;
return new ZodDefault({
...processCreateParams(this._def),
innerType: this,
defaultValue: defaultValueFunc,
typeName: ZodFirstPartyTypeKind.ZodDefault
});
}
brand() {
return new ZodBranded({
typeName: ZodFirstPartyTypeKind.ZodBranded,
type: this,
...processCreateParams(this._def)
});
}
catch(def) {
const catchValueFunc = typeof def === "function" ? def : () => def;
return new ZodCatch({
...processCreateParams(this._def),
innerType: this,
catchValue: catchValueFunc,
typeName: ZodFirstPartyTypeKind.ZodCatch
});
}
describe(description) {
const This = this.constructor;
return new This({
...this._def,
description
});
}
pipe(target) {
return ZodPipeline.create(this, target);
}
readonly() {
return ZodReadonly.create(this);
}
isOptional() {
return this.safeParse(void 0).success;
}
isNullable() {
return this.safeParse(null).success;
}
};
cuidRegex = /^c[^\s-]{8,}$/i;
cuid2Regex = /^[0-9a-z]+$/;
ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/;
uuidRegex = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i;
nanoidRegex = /^[a-z0-9_-]{21}$/i;
durationRegex = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/;
emailRegex = /^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i;
_emojiRegex = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`;
ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
ipv6Regex = /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/;
base64Regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
dateRegexSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))`;
dateRegex = new RegExp(`^${dateRegexSource}$`);
ZodString = class _ZodString extends ZodType {
_parse(input) {
if (this._def.coerce) {
input.data = String(input.data);
}
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.string) {
const ctx2 = this._getOrReturnCtx(input);
addIssueToContext(ctx2, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.string,
received: ctx2.parsedType
});
return INVALID;
}
const status = new ParseStatus();
let ctx = void 0;
for (const check of this._def.checks) {
if (check.kind === "min") {
if (input.data.length < check.value) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
minimum: check.value,
type: "string",
inclusive: true,
exact: false,
message: check.message
});
status.dirty();
}
} else if (check.kind === "max") {
if (input.data.length > check.value) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_big,
maximum: check.value,
type: "string",
inclusive: true,
exact: false,
message: check.message
});
status.dirty();
}
} else if (check.kind === "length") {
const tooBig = input.data.length > check.value;
const tooSmall = input.data.length < check.value;
if (tooBig || tooSmall) {
ctx = this._getOrReturnCtx(input, ctx);
if (tooBig) {
addIssueToContext(ctx, {
code: ZodIssueCode.too_big,
maximum: check.value,
type: "string",
inclusive: true,
exact: true,
message: check.message
});
} else if (tooSmall) {
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
minimum: check.value,
type: "string",
inclusive: true,
exact: true,
message: check.message
});
}
status.dirty();
}
} else if (check.kind === "email") {
if (!emailRegex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "email",
code: ZodIssueCode.invalid_string,
message: check.message
});
status.dirty();
}
} else if (check.kind === "emoji") {
if (!emojiRegex) {
emojiRegex = new RegExp(_emojiRegex, "u");
}
if (!emojiRegex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "emoji",
code: ZodIssueCode.invalid_string,
message: check.message
});
status.dirty();
}
} else if (check.kind === "uuid") {
if (!uuidRegex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "uuid",
code: ZodIssueCode.invalid_string,
message: check.message
});
status.dirty();
}
} else if (check.kind === "nanoid") {
if (!nanoidRegex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "nanoid",
code: ZodIssueCode.invalid_string,
message: check.message
});
status.dirty();
}
} else if (check.kind === "cuid") {
if (!cuidRegex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "cuid",
code: ZodIssueCode.invalid_string,
message: check.message
});
status.dirty();
}
} else if (check.kind === "cuid2") {
if (!cuid2Regex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "cuid2",
code: ZodIssueCode.invalid_string,
message: check.message
});
status.dirty();
}
} else if (check.kind === "ulid") {
if (!ulidRegex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "ulid",
code: ZodIssueCode.invalid_string,
message: check.message
});
status.dirty();
}
} else if (check.kind === "url") {
try {
new URL(input.data);
} catch (_a) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "url",
code: ZodIssueCode.invalid_string,
message: check.message
});
status.dirty();
}
} else if (check.kind === "regex") {
check.regex.lastIndex = 0;
const testResult = check.regex.test(input.data);
if (!testResult) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "regex",
code: ZodIssueCode.invalid_string,
message: check.message
});
status.dirty();
}
} else if (check.kind === "trim") {
input.data = input.data.trim();
} else if (check.kind === "includes") {
if (!input.data.includes(check.value, check.position)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_string,
validation: { includes: check.value, position: check.position },
message: check.message
});
status.dirty();
}
} else if (check.kind === "toLowerCase") {
input.data = input.data.toLowerCase();
} else if (check.kind === "toUpperCase") {
input.data = input.data.toUpperCase();
} else if (check.kind === "startsWith") {
if (!input.data.startsWith(check.value)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_string,
validation: { startsWith: check.value },
message: check.message
});
status.dirty();
}
} else if (check.kind === "endsWith") {
if (!input.data.endsWith(check.value)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_string,
validation: { endsWith: check.value },
message: check.message
});
status.dirty();
}
} else if (check.kind === "datetime") {
const regex = datetimeRegex(check);
if (!regex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_string,
validation: "datetime",
message: check.message
});
status.dirty();
}
} else if (check.kind === "date") {
const regex = dateRegex;
if (!regex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_string,
validation: "date",
message: check.message
});
status.dirty();
}
} else if (check.kind === "time") {
const regex = timeRegex(check);
if (!regex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_string,
validation: "time",
message: check.message
});
status.dirty();
}
} else if (check.kind === "duration") {
if (!durationRegex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "duration",
code: ZodIssueCode.invalid_string,
message: check.message
});
status.dirty();
}
} else if (check.kind === "ip") {
if (!isValidIP(input.data, check.version)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "ip",
code: ZodIssueCode.invalid_string,
message: check.message
});
status.dirty();
}
} else if (check.kind === "base64") {
if (!base64Regex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "base64",
code: ZodIssueCode.invalid_string,
message: check.message
});
status.dirty();
}
} else {
util.assertNever(check);
}
}
return { status: status.value, value: input.data };
}
_regex(regex, validation, message) {
return this.refinement((data) => regex.test(data), {
validation,
code: ZodIssueCode.invalid_string,
...errorUtil.errToObj(message)
});
}
_addCheck(check) {
return new _ZodString({
...this._def,
checks: [...this._def.checks, check]
});
}
email(message) {
return this._addCheck({ kind: "email", ...errorUtil.errToObj(message) });
}
url(message) {
return this._addCheck({ kind: "url", ...errorUtil.errToObj(message) });
}
emoji(message) {
return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(message) });
}
uuid(message) {
return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(message) });
}
nanoid(message) {
return this._addCheck({ kind: "nanoid", ...errorUtil.errToObj(message) });
}
cuid(message) {
return this._addCheck({ kind: "cuid", ...errorUtil.errToObj(message) });
}
cuid2(message) {
return this._addCheck({ kind: "cuid2", ...errorUtil.errToObj(message) });
}
ulid(message) {
return this._addCheck({ kind: "ulid", ...errorUtil.errToObj(message) });
}
base64(message) {
return this._addCheck({ kind: "base64", ...errorUtil.errToObj(message) });
}
ip(options) {
return this._addCheck({ kind: "ip", ...errorUtil.errToObj(options) });
}
datetime(options) {
var _a, _b;
if (typeof options === "string") {
return this._addCheck({
kind: "datetime",
precision: null,
offset: false,
local: false,
message: options
});
}
return this._addCheck({
kind: "datetime",
precision: typeof (options === null || options === void 0 ? void 0 : options.precision) === "undefined" ? null : options === null || options === void 0 ? void 0 : options.precision,
offset: (_a = options === null || options === void 0 ? void 0 : options.offset) !== null && _a !== void 0 ? _a : false,
local: (_b = options === null || options === void 0 ? void 0 : options.local) !== null && _b !== void 0 ? _b : false,
...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message)
});
}
date(message) {
return this._addCheck({ kind: "date", message });
}
time(options) {
if (typeof options === "string") {
return this._addCheck({
kind: "time",
precision: null,
message: options
});
}
return this._addCheck({
kind: "time",
precision: typeof (options === null || options === void 0 ? void 0 : options.precision) === "undefined" ? null : options === null || options === void 0 ? void 0 : options.precision,
...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message)
});
}
duration(message) {
return this._addCheck({ kind: "duration", ...errorUtil.errToObj(message) });
}
regex(regex, message) {
return this._addCheck({
kind: "regex",
regex,
...errorUtil.errToObj(message)
});
}
includes(value, options) {
return this._addCheck({
kind: "includes",
value,
position: options === null || options === void 0 ? void 0 : options.position,
...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message)
});
}
startsWith(value, message) {
return this._addCheck({
kind: "startsWith",
value,
...errorUtil.errToObj(message)
});
}
endsWith(value, message) {
return this._addCheck({
kind: "endsWith",
value,
...errorUtil.errToObj(message)
});
}
min(minLength, message) {
return this._addCheck({
kind: "min",
value: minLength,
...errorUtil.errToObj(message)
});
}
max(maxLength, message) {
return this._addCheck({
kind: "max",
value: maxLength,
...errorUtil.errToObj(message)
});
}
length(len, message) {
return this._addCheck({
kind: "length",
value: len,
...errorUtil.errToObj(message)
});
}
/**
* @deprecated Use z.string().min(1) instead.
* @see {@link ZodString.min}
*/
nonempty(message) {
return this.min(1, errorUtil.errToObj(message));
}
trim() {
return new _ZodString({
...this._def,
checks: [...this._def.checks, { kind: "trim" }]
});
}
toLowerCase() {
return new _ZodString({
...this._def,
checks: [...this._def.checks, { kind: "toLowerCase" }]
});
}
toUpperCase() {
return new _ZodString({
...this._def,
checks: [...this._def.checks, { kind: "toUpperCase" }]
});
}
get isDatetime() {
return !!this._def.checks.find((ch) => ch.kind === "datetime");
}
get isDate() {
return !!this._def.checks.find((ch) => ch.kind === "date");
}
get isTime() {
return !!this._def.checks.find((ch) => ch.kind === "time");
}
get isDuration() {
return !!this._def.checks.find((ch) => ch.kind === "duration");
}
get isEmail() {
return !!this._def.checks.find((ch) => ch.kind === "email");
}
get isURL() {
return !!this._def.checks.find((ch) => ch.kind === "url");
}
get isEmoji() {
return !!this._def.checks.find((ch) => ch.kind === "emoji");
}
get isUUID() {
return !!this._def.checks.find((ch) => ch.kind === "uuid");
}
get isNANOID() {
return !!this._def.checks.find((ch) => ch.kind === "nanoid");
}
get isCUID() {
return !!this._def.checks.find((ch) => ch.kind === "cuid");
}
get isCUID2() {
return !!this._def.checks.find((ch) => ch.kind === "cuid2");
}
get isULID() {
return !!this._def.checks.find((ch) => ch.kind === "ulid");
}
get isIP() {
return !!this._def.checks.find((ch) => ch.kind === "ip");
}
get isBase64() {
return !!this._def.checks.find((ch) => ch.kind === "base64");
}
get minLength() {
let min = null;
for (const ch of this._def.checks) {
if (ch.kind === "min") {
if (min === null || ch.value > min)
min = ch.value;
}
}
return min;
}
get maxLength() {
let max = null;
for (const ch of this._def.checks) {
if (ch.kind === "max") {
if (max === null || ch.value < max)
max = ch.value;
}
}
return max;
}
};
ZodString.create = (params) => {
var _a;
return new ZodString({
checks: [],
typeName: ZodFirstPartyTypeKind.ZodString,
coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false,
...processCreateParams(params)
});
};
ZodNumber = class _ZodNumber extends ZodType {
constructor() {
super(...arguments);
this.min = this.gte;
this.max = this.lte;
this.step = this.multipleOf;
}
_parse(input) {
if (this._def.coerce) {
input.data = Number(input.data);
}
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.number) {
const ctx2 = this._getOrReturnCtx(input);
addIssueToContext(ctx2, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.number,
received: ctx2.parsedType
});
return INVALID;
}
let ctx = void 0;
const status = new ParseStatus();
for (const check of this._def.checks) {
if (check.kind === "int") {
if (!util.isInteger(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: "integer",
received: "float",
message: check.message
});
status.dirty();
}
} else if (check.kind === "min") {
const tooSmall = check.inclusive ? input.data < check.value : input.data <= check.value;
if (tooSmall) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
minimum: check.value,
type: "number",
inclusive: check.inclusive,
exact: false,
message: check.message
});
status.dirty();
}
} else if (check.kind === "max") {
const tooBig = check.inclusive ? input.data > check.value : input.data >= check.value;
if (tooBig) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_big,
maximum: check.value,
type: "number",
inclusive: check.inclusive,
exact: false,
message: check.message
});
status.dirty();
}
} else if (check.kind === "multipleOf") {
if (floatSafeRemainder(input.data, check.value) !== 0) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.not_multiple_of,
multipleOf: check.value,
message: check.message
});
status.dirty();
}
} else if (check.kind === "finite") {
if (!Number.isFinite(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.not_finite,
message: check.message
});
status.dirty();
}
} else {
util.assertNever(check);
}
}
return { status: status.value, value: input.data };
}
gte(value, message) {
return this.setLimit("min", value, true, errorUtil.toString(message));
}
gt(value, message) {
return this.setLimit("min", value, false, errorUtil.toString(message));
}
lte(value, message) {
return this.setLimit("max", value, true, errorUtil.toString(message));
}
lt(value, message) {
return this.setLimit("max", value, false, errorUtil.toString(message));
}
setLimit(kind, value, inclusive, message) {
return new _ZodNumber({
...this._def,
checks: [
...this._def.checks,
{
kind,
value,
inclusive,
message: errorUtil.toString(message)
}
]
});
}
_addCheck(check) {
return new _ZodNumber({
...this._def,
checks: [...this._def.checks, check]
});
}
int(message) {
return this._addCheck({
kind: "int",
message: errorUtil.toString(message)
});
}
positive(message) {
return this._addCheck({
kind: "min",
value: 0,
inclusive: false,
message: errorUtil.toString(message)
});
}
negative(message) {
return this._addCheck({
kind: "max",
value: 0,
inclusive: false,
message: errorUtil.toString(message)
});
}
nonpositive(message) {
return this._addCheck({
kind: "max",
value: 0,
inclusive: true,
message: errorUtil.toString(message)
});
}
nonnegative(message) {
return this._addCheck({
kind: "min",
value: 0,
inclusive: true,
message: errorUtil.toString(message)
});
}
multipleOf(value, message) {
return this._addCheck({
kind: "multipleOf",
value,
message: errorUtil.toString(message)
});
}
finite(message) {
return this._addCheck({
kind: "finite",
message: errorUtil.toString(message)
});
}
safe(message) {
return this._addCheck({
kind: "min",
inclusive: true,
value: Number.MIN_SAFE_INTEGER,
message: errorUtil.toString(message)
})._addCheck({
kind: "max",
inclusive: true,
value: Number.MAX_SAFE_INTEGER,
message: errorUtil.toString(message)
});
}
get minValue() {
let min = null;
for (const ch of this._def.checks) {
if (ch.kind === "min") {
if (min === null || ch.value > min)
min = ch.value;
}
}
return min;
}
get maxValue() {
let max = null;
for (const ch of this._def.checks) {
if (ch.kind === "max") {
if (max === null || ch.value < max)
max = ch.value;
}
}
return max;
}
get isInt() {
return !!this._def.checks.find((ch) => ch.kind === "int" || ch.kind === "multipleOf" && util.isInteger(ch.value));
}
get isFinite() {
let max = null, min = null;
for (const ch of this._def.checks) {
if (ch.kind === "finite" || ch.kind === "int" || ch.kind === "multipleOf") {
return true;
} else if (ch.kind === "min") {
if (min === null || ch.value > min)
min = ch.value;
} else if (ch.kind === "max") {
if (max === null || ch.value < max)
max = ch.value;
}
}
return Number.isFinite(min) && Number.isFinite(max);
}
};
ZodNumber.create = (params) => {
return new ZodNumber({
checks: [],
typeName: ZodFirstPartyTypeKind.ZodNumber,
coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false,
...processCreateParams(params)
});
};
ZodBigInt = class _ZodBigInt extends ZodType {
constructor() {
super(...arguments);
this.min = this.gte;
this.max = this.lte;
}
_parse(input) {
if (this._def.coerce) {
input.data = BigInt(input.data);
}
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.bigint) {
const ctx2 = this._getOrReturnCtx(input);
addIssueToContext(ctx2, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.bigint,
received: ctx2.parsedType
});
return INVALID;
}
let ctx = void 0;
const status = new ParseStatus();
for (const check of this._def.checks) {
if (check.kind === "min") {
const tooSmall = check.inclusive ? input.data < check.value : input.data <= check.value;
if (tooSmall) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
type: "bigint",
minimum: check.value,
inclusive: check.inclusive,
message: check.message
});
status.dirty();
}
} else if (check.kind === "max") {
const tooBig = check.inclusive ? input.data > check.value : input.data >= check.value;
if (tooBig) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_big,
type: "bigint",
maximum: check.value,
inclusive: check.inclusive,
message: check.message
});
status.dirty();
}
} else if (check.kind === "multipleOf") {
if (input.data % check.value !== BigInt(0)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.not_multiple_of,
multipleOf: check.value,
message: check.message
});
status.dirty();
}
} else {
util.assertNever(check);
}
}
return { status: status.value, value: input.data };
}
gte(value, message) {
return this.setLimit("min", value, true, errorUtil.toString(message));
}
gt(value, message) {
return this.setLimit("min", value, false, errorUtil.toString(message));
}
lte(value, message) {
return this.setLimit("max", value, true, errorUtil.toString(message));
}
lt(value, message) {
return this.setLimit("max", value, false, errorUtil.toString(message));
}
setLimit(kind, value, inclusive, message) {
return new _ZodBigInt({
...this._def,
checks: [
...this._def.checks,
{
kind,
value,
inclusive,
message: errorUtil.toString(message)
}
]
});
}
_addCheck(check) {
return new _ZodBigInt({
...this._def,
checks: [...this._def.checks, check]
});
}
positive(message) {
return this._addCheck({
kind: "min",
value: BigInt(0),
inclusive: false,
message: errorUtil.toString(message)
});
}
negative(message) {
return this._addCheck({
kind: "max",
value: BigInt(0),
inclusive: false,
message: errorUtil.toString(message)
});
}
nonpositive(message) {
return this._addCheck({
kind: "max",
value: BigInt(0),
inclusive: true,
message: errorUtil.toString(message)
});
}
nonnegative(message) {
return this._addCheck({
kind: "min",
value: BigInt(0),
inclusive: true,
message: errorUtil.toString(message)
});
}
multipleOf(value, message) {
return this._addCheck({
kind: "multipleOf",
value,
message: errorUtil.toString(message)
});
}
get minValue() {
let min = null;
for (const ch of this._def.checks) {
if (ch.kind === "min") {
if (min === null || ch.value > min)
min = ch.value;
}
}
return min;
}
get maxValue() {
let max = null;
for (const ch of this._def.checks) {
if (ch.kind === "max") {
if (max === null || ch.value < max)
max = ch.value;
}
}
return max;
}
};
ZodBigInt.create = (params) => {
var _a;
return new ZodBigInt({
checks: [],
typeName: ZodFirstPartyTypeKind.ZodBigInt,
coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false,
...processCreateParams(params)
});
};
ZodBoolean = class extends ZodType {
_parse(input) {
if (this._def.coerce) {
input.data = Boolean(input.data);
}
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.boolean) {
const ctx = this._getOrReturnCtx(input);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.boolean,
received: ctx.parsedType
});
return INVALID;
}
return OK(input.data);
}
};
ZodBoolean.create = (params) => {
return new ZodBoolean({
typeName: ZodFirstPartyTypeKind.ZodBoolean,
coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false,
...processCreateParams(params)
});
};
ZodDate = class _ZodDate extends ZodType {
_parse(input) {
if (this._def.coerce) {
input.data = new Date(input.data);
}
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.date) {
const ctx2 = this._getOrReturnCtx(input);
addIssueToContext(ctx2, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.date,
received: ctx2.parsedType
});
return INVALID;
}
if (isNaN(input.data.getTime())) {
const ctx2 = this._getOrReturnCtx(input);
addIssueToContext(ctx2, {
code: ZodIssueCode.invalid_date
});
return INVALID;
}
const status = new ParseStatus();
let ctx = void 0;
for (const check of this._def.checks) {
if (check.kind === "min") {
if (input.data.getTime() < check.value) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
message: check.message,
inclusive: true,
exact: false,
minimum: check.value,
type: "date"
});
status.dirty();
}
} else if (check.kind === "max") {
if (input.data.getTime() > check.value) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_big,
message: check.message,
inclusive: true,
exact: false,
maximum: check.value,
type: "date"
});
status.dirty();
}
} else {
util.assertNever(check);
}
}
return {
status: status.value,
value: new Date(input.data.getTime())
};
}
_addCheck(check) {
return new _ZodDate({
...this._def,
checks: [...this._def.checks, check]
});
}
min(minDate, message) {
return this._addCheck({
kind: "min",
value: minDate.getTime(),
message: errorUtil.toString(message)
});
}
max(maxDate, message) {
return this._addCheck({
kind: "max",
value: maxDate.getTime(),
message: errorUtil.toString(message)
});
}
get minDate() {
let min = null;
for (const ch of this._def.checks) {
if (ch.kind === "min") {
if (min === null || ch.value > min)
min = ch.value;
}
}
return min != null ? new Date(min) : null;
}
get maxDate() {
let max = null;
for (const ch of this._def.checks) {
if (ch.kind === "max") {
if (max === null || ch.value < max)
max = ch.value;
}
}
return max != null ? new Date(max) : null;
}
};
ZodDate.create = (params) => {
return new ZodDate({
checks: [],
coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false,
typeName: ZodFirstPartyTypeKind.ZodDate,
...processCreateParams(params)
});
};
ZodSymbol = class extends ZodType {
_parse(input) {
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.symbol) {
const ctx = this._getOrReturnCtx(input);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.symbol,
received: ctx.parsedType
});
return INVALID;
}
return OK(input.data);
}
};
ZodSymbol.create = (params) => {
return new ZodSymbol({
typeName: ZodFirstPartyTypeKind.ZodSymbol,
...processCreateParams(params)
});
};
ZodUndefined = class extends ZodType {
_parse(input) {
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.undefined) {
const ctx = this._getOrReturnCtx(input);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.undefined,
received: ctx.parsedType
});
return INVALID;
}
return OK(input.data);
}
};
ZodUndefined.create = (params) => {
return new ZodUndefined({
typeName: ZodFirstPartyTypeKind.ZodUndefined,
...processCreateParams(params)
});
};
ZodNull = class extends ZodType {
_parse(input) {
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.null) {
const ctx = this._getOrReturnCtx(input);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.null,
received: ctx.parsedType
});
return INVALID;
}
return OK(input.data);
}
};
ZodNull.create = (params) => {
return new ZodNull({
typeName: ZodFirstPartyTypeKind.ZodNull,
...processCreateParams(params)
});
};
ZodAny = class extends ZodType {
constructor() {
super(...arguments);
this._any = true;
}
_parse(input) {
return OK(input.data);
}
};
ZodAny.create = (params) => {
return new ZodAny({
typeName: ZodFirstPartyTypeKind.ZodAny,
...processCreateParams(params)
});
};
ZodUnknown = class extends ZodType {
constructor() {
super(...arguments);
this._unknown = true;
}
_parse(input) {
return OK(input.data);
}
};
ZodUnknown.create = (params) => {
return new ZodUnknown({
typeName: ZodFirstPartyTypeKind.ZodUnknown,
...processCreateParams(params)
});
};
ZodNever = class extends ZodType {
_parse(input) {
const ctx = this._getOrReturnCtx(input);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.never,
received: ctx.parsedType
});
return INVALID;
}
};
ZodNever.create = (params) => {
return new ZodNever({
typeName: ZodFirstPartyTypeKind.ZodNever,
...processCreateParams(params)
});
};
ZodVoid = class extends ZodType {
_parse(input) {
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.undefined) {
const ctx = this._getOrReturnCtx(input);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.void,
received: ctx.parsedType
});
return INVALID;
}
return OK(input.data);
}
};
ZodVoid.create = (params) => {
return new ZodVoid({
typeName: ZodFirstPartyTypeKind.ZodVoid,
...processCreateParams(params)
});
};
ZodArray = class _ZodArray extends ZodType {
_parse(input) {
const { ctx, status } = this._processInputParams(input);
const def = this._def;
if (ctx.parsedType !== ZodParsedType.array) {
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.array,
received: ctx.parsedType
});
return INVALID;
}
if (def.exactLength !== null) {
const tooBig = ctx.data.length > def.exactLength.value;
const tooSmall = ctx.data.length < def.exactLength.value;
if (tooBig || tooSmall) {
addIssueToContext(ctx, {
code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small,
minimum: tooSmall ? def.exactLength.value : void 0,
maximum: tooBig ? def.exactLength.value : void 0,
type: "array",
inclusive: true,
exact: true,
message: def.exactLength.message
});
status.dirty();
}
}
if (def.minLength !== null) {
if (ctx.data.length < def.minLength.value) {
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
minimum: def.minLength.value,
type: "array",
inclusive: true,
exact: false,
message: def.minLength.message
});
status.dirty();
}
}
if (def.maxLength !== null) {
if (ctx.data.length > def.maxLength.value) {
addIssueToContext(ctx, {
code: ZodIssueCode.too_big,
maximum: def.maxLength.value,
type: "array",
inclusive: true,
exact: false,
message: def.maxLength.message
});
status.dirty();
}
}
if (ctx.common.async) {
return Promise.all([...ctx.data].map((item, i) => {
return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i));
})).then((result2) => {
return ParseStatus.mergeArray(status, result2);
});
}
const result = [...ctx.data].map((item, i) => {
return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i));
});
return ParseStatus.mergeArray(status, result);
}
get element() {
return this._def.type;
}
min(minLength, message) {
return new _ZodArray({
...this._def,
minLength: { value: minLength, message: errorUtil.toString(message) }
});
}
max(maxLength, message) {
return new _ZodArray({
...this._def,
maxLength: { value: maxLength, message: errorUtil.toString(message) }
});
}
length(len, message) {
return new _ZodArray({
...this._def,
exactLength: { value: len, message: errorUtil.toString(message) }
});
}
nonempty(message) {
return this.min(1, message);
}
};
ZodArray.create = (schema, params) => {
return new ZodArray({
type: schema,
minLength: null,
maxLength: null,
exactLength: null,
typeName: ZodFirstPartyTypeKind.ZodArray,
...processCreateParams(params)
});
};
ZodObject = class _ZodObject extends ZodType {
constructor() {
super(...arguments);
this._cached = null;
this.nonstrict = this.passthrough;
this.augment = this.extend;
}
_getCached() {
if (this._cached !== null)
return this._cached;
const shape = this._def.shape();
const keys = util.objectKeys(shape);
return this._cached = { shape, keys };
}
_parse(input) {
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.object) {
const ctx2 = this._getOrReturnCtx(input);
addIssueToContext(ctx2, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.object,
received: ctx2.parsedType
});
return INVALID;
}
const { status, ctx } = this._processInputParams(input);
const { shape, keys: shapeKeys } = this._getCached();
const extraKeys = [];
if (!(this._def.catchall instanceof ZodNever && this._def.unknownKeys === "strip")) {
for (const key in ctx.data) {
if (!shapeKeys.includes(key)) {
extraKeys.push(key);
}
}
}
const pairs = [];
for (const key of shapeKeys) {
const keyValidator = shape[key];
const value = ctx.data[key];
pairs.push({
key: { status: "valid", value: key },
value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)),
alwaysSet: key in ctx.data
});
}
if (this._def.catchall instanceof ZodNever) {
const unknownKeys = this._def.unknownKeys;
if (unknownKeys === "passthrough") {
for (const key of extraKeys) {
pairs.push({
key: { status: "valid", value: key },
value: { status: "valid", value: ctx.data[key] }
});
}
} else if (unknownKeys === "strict") {
if (extraKeys.length > 0) {
addIssueToContext(ctx, {
code: ZodIssueCode.unrecognized_keys,
keys: extraKeys
});
status.dirty();
}
} else if (unknownKeys === "strip") ;
else {
throw new Error(`Internal ZodObject error: invalid unknownKeys value.`);
}
} else {
const catchall = this._def.catchall;
for (const key of extraKeys) {
const value = ctx.data[key];
pairs.push({
key: { status: "valid", value: key },
value: catchall._parse(
new ParseInputLazyPath(ctx, value, ctx.path, key)
//, ctx.child(key), value, getParsedType(value)
),
alwaysSet: key in ctx.data
});
}
}
if (ctx.common.async) {
return Promise.resolve().then(async () => {
const syncPairs = [];
for (const pair of pairs) {
const key = await pair.key;
const value = await pair.value;
syncPairs.push({
key,
value,
alwaysSet: pair.alwaysSet
});
}
return syncPairs;
}).then((syncPairs) => {
return ParseStatus.mergeObjectSync(status, syncPairs);
});
} else {
return ParseStatus.mergeObjectSync(status, pairs);
}
}
get shape() {
return this._def.shape();
}
strict(message) {
errorUtil.errToObj;
return new _ZodObject({
...this._def,
unknownKeys: "strict",
...message !== void 0 ? {
errorMap: (issue, ctx) => {
var _a, _b, _c, _d;
const defaultError = (_c = (_b = (_a = this._def).errorMap) === null || _b === void 0 ? void 0 : _b.call(_a, issue, ctx).message) !== null && _c !== void 0 ? _c : ctx.defaultError;
if (issue.code === "unrecognized_keys")
return {
message: (_d = errorUtil.errToObj(message).message) !== null && _d !== void 0 ? _d : defaultError
};
return {
message: defaultError
};
}
} : {}
});
}
strip() {
return new _ZodObject({
...this._def,
unknownKeys: "strip"
});
}
passthrough() {
return new _ZodObject({
...this._def,
unknownKeys: "passthrough"
});
}
// const AugmentFactory =
// (def: Def) =>
// (
// augmentation: Augmentation
// ): ZodObject<
// extendShape, Augmentation>,
// Def["unknownKeys"],
// Def["catchall"]
// > => {
// return new ZodObject({
// ...def,
// shape: () => ({
// ...def.shape(),
// ...augmentation,
// }),
// }) as any;
// };
extend(augmentation) {
return new _ZodObject({
...this._def,
shape: () => ({
...this._def.shape(),
...augmentation
})
});
}
/**
* Prior to zod@1.0.12 there was a bug in the
* inferred type of merged objects. Please
* upgrade if you are experiencing issues.
*/
merge(merging) {
const merged = new _ZodObject({
unknownKeys: merging._def.unknownKeys,
catchall: merging._def.catchall,
shape: () => ({
...this._def.shape(),
...merging._def.shape()
}),
typeName: ZodFirstPartyTypeKind.ZodObject
});
return merged;
}
// merge<
// Incoming extends AnyZodObject,
// Augmentation extends Incoming["shape"],
// NewOutput extends {
// [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation
// ? Augmentation[k]["_output"]
// : k extends keyof Output
// ? Output[k]
// : never;
// },
// NewInput extends {
// [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation
// ? Augmentation[k]["_input"]
// : k extends keyof Input
// ? Input[k]
// : never;
// }
// >(
// merging: Incoming
// ): ZodObject<
// extendShape>,
// Incoming["_def"]["unknownKeys"],
// Incoming["_def"]["catchall"],
// NewOutput,
// NewInput
// > {
// const merged: any = new ZodObject({
// unknownKeys: merging._def.unknownKeys,
// catchall: merging._def.catchall,
// shape: () =>
// objectUtil.mergeShapes(this._def.shape(), merging._def.shape()),
// typeName: ZodFirstPartyTypeKind.ZodObject,
// }) as any;
// return merged;
// }
setKey(key, schema) {
return this.augment({ [key]: schema });
}
// merge(
// merging: Incoming
// ): //ZodObject = (merging) => {
// ZodObject<
// extendShape>,
// Incoming["_def"]["unknownKeys"],
// Incoming["_def"]["catchall"]
// > {
// // const mergedShape = objectUtil.mergeShapes(
// // this._def.shape(),
// // merging._def.shape()
// // );
// const merged: any = new ZodObject({
// unknownKeys: merging._def.unknownKeys,
// catchall: merging._def.catchall,
// shape: () =>
// objectUtil.mergeShapes(this._def.shape(), merging._def.shape()),
// typeName: ZodFirstPartyTypeKind.ZodObject,
// }) as any;
// return merged;
// }
catchall(index) {
return new _ZodObject({
...this._def,
catchall: index
});
}
pick(mask) {
const shape = {};
util.objectKeys(mask).forEach((key) => {
if (mask[key] && this.shape[key]) {
shape[key] = this.shape[key];
}
});
return new _ZodObject({
...this._def,
shape: () => shape
});
}
omit(mask) {
const shape = {};
util.objectKeys(this.shape).forEach((key) => {
if (!mask[key]) {
shape[key] = this.shape[key];
}
});
return new _ZodObject({
...this._def,
shape: () => shape
});
}
/**
* @deprecated
*/
deepPartial() {
return deepPartialify(this);
}
partial(mask) {
const newShape = {};
util.objectKeys(this.shape).forEach((key) => {
const fieldSchema = this.shape[key];
if (mask && !mask[key]) {
newShape[key] = fieldSchema;
} else {
newShape[key] = fieldSchema.optional();
}
});
return new _ZodObject({
...this._def,
shape: () => newShape
});
}
required(mask) {
const newShape = {};
util.objectKeys(this.shape).forEach((key) => {
if (mask && !mask[key]) {
newShape[key] = this.shape[key];
} else {
const fieldSchema = this.shape[key];
let newField = fieldSchema;
while (newField instanceof ZodOptional) {
newField = newField._def.innerType;
}
newShape[key] = newField;
}
});
return new _ZodObject({
...this._def,
shape: () => newShape
});
}
keyof() {
return createZodEnum(util.objectKeys(this.shape));
}
};
ZodObject.create = (shape, params) => {
return new ZodObject({
shape: () => shape,
unknownKeys: "strip",
catchall: ZodNever.create(),
typeName: ZodFirstPartyTypeKind.ZodObject,
...processCreateParams(params)
});
};
ZodObject.strictCreate = (shape, params) => {
return new ZodObject({
shape: () => shape,
unknownKeys: "strict",
catchall: ZodNever.create(),
typeName: ZodFirstPartyTypeKind.ZodObject,
...processCreateParams(params)
});
};
ZodObject.lazycreate = (shape, params) => {
return new ZodObject({
shape,
unknownKeys: "strip",
catchall: ZodNever.create(),
typeName: ZodFirstPartyTypeKind.ZodObject,
...processCreateParams(params)
});
};
ZodUnion = class extends ZodType {
_parse(input) {
const { ctx } = this._processInputParams(input);
const options = this._def.options;
function handleResults(results) {
for (const result of results) {
if (result.result.status === "valid") {
return result.result;
}
}
for (const result of results) {
if (result.result.status === "dirty") {
ctx.common.issues.push(...result.ctx.common.issues);
return result.result;
}
}
const unionErrors = results.map((result) => new ZodError(result.ctx.common.issues));
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_union,
unionErrors
});
return INVALID;
}
if (ctx.common.async) {
return Promise.all(options.map(async (option) => {
const childCtx = {
...ctx,
common: {
...ctx.common,
issues: []
},
parent: null
};
return {
result: await option._parseAsync({
data: ctx.data,
path: ctx.path,
parent: childCtx
}),
ctx: childCtx
};
})).then(handleResults);
} else {
let dirty = void 0;
const issues = [];
for (const option of options) {
const childCtx = {
...ctx,
common: {
...ctx.common,
issues: []
},
parent: null
};
const result = option._parseSync({
data: ctx.data,
path: ctx.path,
parent: childCtx
});
if (result.status === "valid") {
return result;
} else if (result.status === "dirty" && !dirty) {
dirty = { result, ctx: childCtx };
}
if (childCtx.common.issues.length) {
issues.push(childCtx.common.issues);
}
}
if (dirty) {
ctx.common.issues.push(...dirty.ctx.common.issues);
return dirty.result;
}
const unionErrors = issues.map((issues2) => new ZodError(issues2));
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_union,
unionErrors
});
return INVALID;
}
}
get options() {
return this._def.options;
}
};
ZodUnion.create = (types, params) => {
return new ZodUnion({
options: types,
typeName: ZodFirstPartyTypeKind.ZodUnion,
...processCreateParams(params)
});
};
getDiscriminator = (type) => {
if (type instanceof ZodLazy) {
return getDiscriminator(type.schema);
} else if (type instanceof ZodEffects) {
return getDiscriminator(type.innerType());
} else if (type instanceof ZodLiteral) {
return [type.value];
} else if (type instanceof ZodEnum) {
return type.options;
} else if (type instanceof ZodNativeEnum) {
return util.objectValues(type.enum);
} else if (type instanceof ZodDefault) {
return getDiscriminator(type._def.innerType);
} else if (type instanceof ZodUndefined) {
return [void 0];
} else if (type instanceof ZodNull) {
return [null];
} else if (type instanceof ZodOptional) {
return [void 0, ...getDiscriminator(type.unwrap())];
} else if (type instanceof ZodNullable) {
return [null, ...getDiscriminator(type.unwrap())];
} else if (type instanceof ZodBranded) {
return getDiscriminator(type.unwrap());
} else if (type instanceof ZodReadonly) {
return getDiscriminator(type.unwrap());
} else if (type instanceof ZodCatch) {
return getDiscriminator(type._def.innerType);
} else {
return [];
}
};
ZodDiscriminatedUnion = class _ZodDiscriminatedUnion extends ZodType {
_parse(input) {
const { ctx } = this._processInputParams(input);
if (ctx.parsedType !== ZodParsedType.object) {
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.object,
received: ctx.parsedType
});
return INVALID;
}
const discriminator = this.discriminator;
const discriminatorValue = ctx.data[discriminator];
const option = this.optionsMap.get(discriminatorValue);
if (!option) {
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_union_discriminator,
options: Array.from(this.optionsMap.keys()),
path: [discriminator]
});
return INVALID;
}
if (ctx.common.async) {
return option._parseAsync({
data: ctx.data,
path: ctx.path,
parent: ctx
});
} else {
return option._parseSync({
data: ctx.data,
path: ctx.path,
parent: ctx
});
}
}
get discriminator() {
return this._def.discriminator;
}
get options() {
return this._def.options;
}
get optionsMap() {
return this._def.optionsMap;
}
/**
* The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor.
* However, it only allows a union of objects, all of which need to share a discriminator property. This property must
* have a different value for each object in the union.
* @param discriminator the name of the discriminator property
* @param types an array of object schemas
* @param params
*/
static create(discriminator, options, params) {
const optionsMap = /* @__PURE__ */ new Map();
for (const type of options) {
const discriminatorValues = getDiscriminator(type.shape[discriminator]);
if (!discriminatorValues.length) {
throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`);
}
for (const value of discriminatorValues) {
if (optionsMap.has(value)) {
throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`);
}
optionsMap.set(value, type);
}
}
return new _ZodDiscriminatedUnion({
typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion,
discriminator,
options,
optionsMap,
...processCreateParams(params)
});
}
};
ZodIntersection = class extends ZodType {
_parse(input) {
const { status, ctx } = this._processInputParams(input);
const handleParsed = (parsedLeft, parsedRight) => {
if (isAborted(parsedLeft) || isAborted(parsedRight)) {
return INVALID;
}
const merged = mergeValues(parsedLeft.value, parsedRight.value);
if (!merged.valid) {
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_intersection_types
});
return INVALID;
}
if (isDirty(parsedLeft) || isDirty(parsedRight)) {
status.dirty();
}
return { status: status.value, value: merged.data };
};
if (ctx.common.async) {
return Promise.all([
this._def.left._parseAsync({
data: ctx.data,
path: ctx.path,
parent: ctx
}),
this._def.right._parseAsync({
data: ctx.data,
path: ctx.path,
parent: ctx
})
]).then(([left, right]) => handleParsed(left, right));
} else {
return handleParsed(this._def.left._parseSync({
data: ctx.data,
path: ctx.path,
parent: ctx
}), this._def.right._parseSync({
data: ctx.data,
path: ctx.path,
parent: ctx
}));
}
}
};
ZodIntersection.create = (left, right, params) => {
return new ZodIntersection({
left,
right,
typeName: ZodFirstPartyTypeKind.ZodIntersection,
...processCreateParams(params)
});
};
ZodTuple = class _ZodTuple extends ZodType {
_parse(input) {
const { status, ctx } = this._processInputParams(input);
if (ctx.parsedType !== ZodParsedType.array) {
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.array,
received: ctx.parsedType
});
return INVALID;
}
if (ctx.data.length < this._def.items.length) {
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
minimum: this._def.items.length,
inclusive: true,
exact: false,
type: "array"
});
return INVALID;
}
const rest = this._def.rest;
if (!rest && ctx.data.length > this._def.items.length) {
addIssueToContext(ctx, {
code: ZodIssueCode.too_big,
maximum: this._def.items.length,
inclusive: true,
exact: false,
type: "array"
});
status.dirty();
}
const items = [...ctx.data].map((item, itemIndex) => {
const schema = this._def.items[itemIndex] || this._def.rest;
if (!schema)
return null;
return schema._parse(new ParseInputLazyPath(ctx, item, ctx.path, itemIndex));
}).filter((x) => !!x);
if (ctx.common.async) {
return Promise.all(items).then((results) => {
return ParseStatus.mergeArray(status, results);
});
} else {
return ParseStatus.mergeArray(status, items);
}
}
get items() {
return this._def.items;
}
rest(rest) {
return new _ZodTuple({
...this._def,
rest
});
}
};
ZodTuple.create = (schemas, params) => {
if (!Array.isArray(schemas)) {
throw new Error("You must pass an array of schemas to z.tuple([ ... ])");
}
return new ZodTuple({
items: schemas,
typeName: ZodFirstPartyTypeKind.ZodTuple,
rest: null,
...processCreateParams(params)
});
};
ZodRecord = class _ZodRecord extends ZodType {
get keySchema() {
return this._def.keyType;
}
get valueSchema() {
return this._def.valueType;
}
_parse(input) {
const { status, ctx } = this._processInputParams(input);
if (ctx.parsedType !== ZodParsedType.object) {
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.object,
received: ctx.parsedType
});
return INVALID;
}
const pairs = [];
const keyType = this._def.keyType;
const valueType = this._def.valueType;
for (const key in ctx.data) {
pairs.push({
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)),
value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)),
alwaysSet: key in ctx.data
});
}
if (ctx.common.async) {
return ParseStatus.mergeObjectAsync(status, pairs);
} else {
return ParseStatus.mergeObjectSync(status, pairs);
}
}
get element() {
return this._def.valueType;
}
static create(first, second, third) {
if (second instanceof ZodType) {
return new _ZodRecord({
keyType: first,
valueType: second,
typeName: ZodFirstPartyTypeKind.ZodRecord,
...processCreateParams(third)
});
}
return new _ZodRecord({
keyType: ZodString.create(),
valueType: first,
typeName: ZodFirstPartyTypeKind.ZodRecord,
...processCreateParams(second)
});
}
};
ZodMap = class extends ZodType {
get keySchema() {
return this._def.keyType;
}
get valueSchema() {
return this._def.valueType;
}
_parse(input) {
const { status, ctx } = this._processInputParams(input);
if (ctx.parsedType !== ZodParsedType.map) {
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.map,
received: ctx.parsedType
});
return INVALID;
}
const keyType = this._def.keyType;
const valueType = this._def.valueType;
const pairs = [...ctx.data.entries()].map(([key, value], index) => {
return {
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, "key"])),
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, "value"]))
};
});
if (ctx.common.async) {
const finalMap = /* @__PURE__ */ new Map();
return Promise.resolve().then(async () => {
for (const pair of pairs) {
const key = await pair.key;
const value = await pair.value;
if (key.status === "aborted" || value.status === "aborted") {
return INVALID;
}
if (key.status === "dirty" || value.status === "dirty") {
status.dirty();
}
finalMap.set(key.value, value.value);
}
return { status: status.value, value: finalMap };
});
} else {
const finalMap = /* @__PURE__ */ new Map();
for (const pair of pairs) {
const key = pair.key;
const value = pair.value;
if (key.status === "aborted" || value.status === "aborted") {
return INVALID;
}
if (key.status === "dirty" || value.status === "dirty") {
status.dirty();
}
finalMap.set(key.value, value.value);
}
return { status: status.value, value: finalMap };
}
}
};
ZodMap.create = (keyType, valueType, params) => {
return new ZodMap({
valueType,
keyType,
typeName: ZodFirstPartyTypeKind.ZodMap,
...processCreateParams(params)
});
};
ZodSet = class _ZodSet extends ZodType {
_parse(input) {
const { status, ctx } = this._processInputParams(input);
if (ctx.parsedType !== ZodParsedType.set) {
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.set,
received: ctx.parsedType
});
return INVALID;
}
const def = this._def;
if (def.minSize !== null) {
if (ctx.data.size < def.minSize.value) {
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
minimum: def.minSize.value,
type: "set",
inclusive: true,
exact: false,
message: def.minSize.message
});
status.dirty();
}
}
if (def.maxSize !== null) {
if (ctx.data.size > def.maxSize.value) {
addIssueToContext(ctx, {
code: ZodIssueCode.too_big,
maximum: def.maxSize.value,
type: "set",
inclusive: true,
exact: false,
message: def.maxSize.message
});
status.dirty();
}
}
const valueType = this._def.valueType;
function finalizeSet(elements2) {
const parsedSet = /* @__PURE__ */ new Set();
for (const element of elements2) {
if (element.status === "aborted")
return INVALID;
if (element.status === "dirty")
status.dirty();
parsedSet.add(element.value);
}
return { status: status.value, value: parsedSet };
}
const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i)));
if (ctx.common.async) {
return Promise.all(elements).then((elements2) => finalizeSet(elements2));
} else {
return finalizeSet(elements);
}
}
min(minSize, message) {
return new _ZodSet({
...this._def,
minSize: { value: minSize, message: errorUtil.toString(message) }
});
}
max(maxSize, message) {
return new _ZodSet({
...this._def,
maxSize: { value: maxSize, message: errorUtil.toString(message) }
});
}
size(size, message) {
return this.min(size, message).max(size, message);
}
nonempty(message) {
return this.min(1, message);
}
};
ZodSet.create = (valueType, params) => {
return new ZodSet({
valueType,
minSize: null,
maxSize: null,
typeName: ZodFirstPartyTypeKind.ZodSet,
...processCreateParams(params)
});
};
ZodFunction = class _ZodFunction extends ZodType {
constructor() {
super(...arguments);
this.validate = this.implement;
}
_parse(input) {
const { ctx } = this._processInputParams(input);
if (ctx.parsedType !== ZodParsedType.function) {
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.function,
received: ctx.parsedType
});
return INVALID;
}
function makeArgsIssue(args, error) {
return makeIssue({
data: args,
path: ctx.path,
errorMaps: [
ctx.common.contextualErrorMap,
ctx.schemaErrorMap,
getErrorMap(),
errorMap
].filter((x) => !!x),
issueData: {
code: ZodIssueCode.invalid_arguments,
argumentsError: error
}
});
}
function makeReturnsIssue(returns, error) {
return makeIssue({
data: returns,
path: ctx.path,
errorMaps: [
ctx.common.contextualErrorMap,
ctx.schemaErrorMap,
getErrorMap(),
errorMap
].filter((x) => !!x),
issueData: {
code: ZodIssueCode.invalid_return_type,
returnTypeError: error
}
});
}
const params = { errorMap: ctx.common.contextualErrorMap };
const fn = ctx.data;
if (this._def.returns instanceof ZodPromise) {
const me = this;
return OK(async function(...args) {
const error = new ZodError([]);
const parsedArgs = await me._def.args.parseAsync(args, params).catch((e) => {
error.addIssue(makeArgsIssue(args, e));
throw error;
});
const result = await Reflect.apply(fn, this, parsedArgs);
const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e) => {
error.addIssue(makeReturnsIssue(result, e));
throw error;
});
return parsedReturns;
});
} else {
const me = this;
return OK(function(...args) {
const parsedArgs = me._def.args.safeParse(args, params);
if (!parsedArgs.success) {
throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
}
const result = Reflect.apply(fn, this, parsedArgs.data);
const parsedReturns = me._def.returns.safeParse(result, params);
if (!parsedReturns.success) {
throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
}
return parsedReturns.data;
});
}
}
parameters() {
return this._def.args;
}
returnType() {
return this._def.returns;
}
args(...items) {
return new _ZodFunction({
...this._def,
args: ZodTuple.create(items).rest(ZodUnknown.create())
});
}
returns(returnType) {
return new _ZodFunction({
...this._def,
returns: returnType
});
}
implement(func) {
const validatedFunc = this.parse(func);
return validatedFunc;
}
strictImplement(func) {
const validatedFunc = this.parse(func);
return validatedFunc;
}
static create(args, returns, params) {
return new _ZodFunction({
args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()),
returns: returns || ZodUnknown.create(),
typeName: ZodFirstPartyTypeKind.ZodFunction,
...processCreateParams(params)
});
}
};
ZodLazy = class extends ZodType {
get schema() {
return this._def.getter();
}
_parse(input) {
const { ctx } = this._processInputParams(input);
const lazySchema = this._def.getter();
return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx });
}
};
ZodLazy.create = (getter, params) => {
return new ZodLazy({
getter,
typeName: ZodFirstPartyTypeKind.ZodLazy,
...processCreateParams(params)
});
};
ZodLiteral = class extends ZodType {
_parse(input) {
if (input.data !== this._def.value) {
const ctx = this._getOrReturnCtx(input);
addIssueToContext(ctx, {
received: ctx.data,
code: ZodIssueCode.invalid_literal,
expected: this._def.value
});
return INVALID;
}
return { status: "valid", value: input.data };
}
get value() {
return this._def.value;
}
};
ZodLiteral.create = (value, params) => {
return new ZodLiteral({
value,
typeName: ZodFirstPartyTypeKind.ZodLiteral,
...processCreateParams(params)
});
};
ZodEnum = class _ZodEnum extends ZodType {
constructor() {
super(...arguments);
_ZodEnum_cache.set(this, void 0);
}
_parse(input) {
if (typeof input.data !== "string") {
const ctx = this._getOrReturnCtx(input);
const expectedValues = this._def.values;
addIssueToContext(ctx, {
expected: util.joinValues(expectedValues),
received: ctx.parsedType,
code: ZodIssueCode.invalid_type
});
return INVALID;
}
if (!__classPrivateFieldGet(this, _ZodEnum_cache, "f")) {
__classPrivateFieldSet(this, _ZodEnum_cache, new Set(this._def.values), "f");
}
if (!__classPrivateFieldGet(this, _ZodEnum_cache, "f").has(input.data)) {
const ctx = this._getOrReturnCtx(input);
const expectedValues = this._def.values;
addIssueToContext(ctx, {
received: ctx.data,
code: ZodIssueCode.invalid_enum_value,
options: expectedValues
});
return INVALID;
}
return OK(input.data);
}
get options() {
return this._def.values;
}
get enum() {
const enumValues = {};
for (const val of this._def.values) {
enumValues[val] = val;
}
return enumValues;
}
get Values() {
const enumValues = {};
for (const val of this._def.values) {
enumValues[val] = val;
}
return enumValues;
}
get Enum() {
const enumValues = {};
for (const val of this._def.values) {
enumValues[val] = val;
}
return enumValues;
}
extract(values, newDef = this._def) {
return _ZodEnum.create(values, {
...this._def,
...newDef
});
}
exclude(values, newDef = this._def) {
return _ZodEnum.create(this.options.filter((opt) => !values.includes(opt)), {
...this._def,
...newDef
});
}
};
_ZodEnum_cache = /* @__PURE__ */ new WeakMap();
ZodEnum.create = createZodEnum;
ZodNativeEnum = class extends ZodType {
constructor() {
super(...arguments);
_ZodNativeEnum_cache.set(this, void 0);
}
_parse(input) {
const nativeEnumValues = util.getValidEnumValues(this._def.values);
const ctx = this._getOrReturnCtx(input);
if (ctx.parsedType !== ZodParsedType.string && ctx.parsedType !== ZodParsedType.number) {
const expectedValues = util.objectValues(nativeEnumValues);
addIssueToContext(ctx, {
expected: util.joinValues(expectedValues),
received: ctx.parsedType,
code: ZodIssueCode.invalid_type
});
return INVALID;
}
if (!__classPrivateFieldGet(this, _ZodNativeEnum_cache, "f")) {
__classPrivateFieldSet(this, _ZodNativeEnum_cache, new Set(util.getValidEnumValues(this._def.values)), "f");
}
if (!__classPrivateFieldGet(this, _ZodNativeEnum_cache, "f").has(input.data)) {
const expectedValues = util.objectValues(nativeEnumValues);
addIssueToContext(ctx, {
received: ctx.data,
code: ZodIssueCode.invalid_enum_value,
options: expectedValues
});
return INVALID;
}
return OK(input.data);
}
get enum() {
return this._def.values;
}
};
_ZodNativeEnum_cache = /* @__PURE__ */ new WeakMap();
ZodNativeEnum.create = (values, params) => {
return new ZodNativeEnum({
values,
typeName: ZodFirstPartyTypeKind.ZodNativeEnum,
...processCreateParams(params)
});
};
ZodPromise = class extends ZodType {
unwrap() {
return this._def.type;
}
_parse(input) {
const { ctx } = this._processInputParams(input);
if (ctx.parsedType !== ZodParsedType.promise && ctx.common.async === false) {
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.promise,
received: ctx.parsedType
});
return INVALID;
}
const promisified = ctx.parsedType === ZodParsedType.promise ? ctx.data : Promise.resolve(ctx.data);
return OK(promisified.then((data) => {
return this._def.type.parseAsync(data, {
path: ctx.path,
errorMap: ctx.common.contextualErrorMap
});
}));
}
};
ZodPromise.create = (schema, params) => {
return new ZodPromise({
type: schema,
typeName: ZodFirstPartyTypeKind.ZodPromise,
...processCreateParams(params)
});
};
ZodEffects = class extends ZodType {
innerType() {
return this._def.schema;
}
sourceType() {
return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects ? this._def.schema.sourceType() : this._def.schema;
}
_parse(input) {
const { status, ctx } = this._processInputParams(input);
const effect = this._def.effect || null;
const checkCtx = {
addIssue: (arg) => {
addIssueToContext(ctx, arg);
if (arg.fatal) {
status.abort();
} else {
status.dirty();
}
},
get path() {
return ctx.path;
}
};
checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx);
if (effect.type === "preprocess") {
const processed = effect.transform(ctx.data, checkCtx);
if (ctx.common.async) {
return Promise.resolve(processed).then(async (processed2) => {
if (status.value === "aborted")
return INVALID;
const result = await this._def.schema._parseAsync({
data: processed2,
path: ctx.path,
parent: ctx
});
if (result.status === "aborted")
return INVALID;
if (result.status === "dirty")
return DIRTY(result.value);
if (status.value === "dirty")
return DIRTY(result.value);
return result;
});
} else {
if (status.value === "aborted")
return INVALID;
const result = this._def.schema._parseSync({
data: processed,
path: ctx.path,
parent: ctx
});
if (result.status === "aborted")
return INVALID;
if (result.status === "dirty")
return DIRTY(result.value);
if (status.value === "dirty")
return DIRTY(result.value);
return result;
}
}
if (effect.type === "refinement") {
const executeRefinement = (acc) => {
const result = effect.refinement(acc, checkCtx);
if (ctx.common.async) {
return Promise.resolve(result);
}
if (result instanceof Promise) {
throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");
}
return acc;
};
if (ctx.common.async === false) {
const inner = this._def.schema._parseSync({
data: ctx.data,
path: ctx.path,
parent: ctx
});
if (inner.status === "aborted")
return INVALID;
if (inner.status === "dirty")
status.dirty();
executeRefinement(inner.value);
return { status: status.value, value: inner.value };
} else {
return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => {
if (inner.status === "aborted")
return INVALID;
if (inner.status === "dirty")
status.dirty();
return executeRefinement(inner.value).then(() => {
return { status: status.value, value: inner.value };
});
});
}
}
if (effect.type === "transform") {
if (ctx.common.async === false) {
const base = this._def.schema._parseSync({
data: ctx.data,
path: ctx.path,
parent: ctx
});
if (!isValid(base))
return base;
const result = effect.transform(base.value, checkCtx);
if (result instanceof Promise) {
throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
}
return { status: status.value, value: result };
} else {
return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => {
if (!isValid(base))
return base;
return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({ status: status.value, value: result }));
});
}
}
util.assertNever(effect);
}
};
ZodEffects.create = (schema, effect, params) => {
return new ZodEffects({
schema,
typeName: ZodFirstPartyTypeKind.ZodEffects,
effect,
...processCreateParams(params)
});
};
ZodEffects.createWithPreprocess = (preprocess, schema, params) => {
return new ZodEffects({
schema,
effect: { type: "preprocess", transform: preprocess },
typeName: ZodFirstPartyTypeKind.ZodEffects,
...processCreateParams(params)
});
};
ZodOptional = class extends ZodType {
_parse(input) {
const parsedType = this._getType(input);
if (parsedType === ZodParsedType.undefined) {
return OK(void 0);
}
return this._def.innerType._parse(input);
}
unwrap() {
return this._def.innerType;
}
};
ZodOptional.create = (type, params) => {
return new ZodOptional({
innerType: type,
typeName: ZodFirstPartyTypeKind.ZodOptional,
...processCreateParams(params)
});
};
ZodNullable = class extends ZodType {
_parse(input) {
const parsedType = this._getType(input);
if (parsedType === ZodParsedType.null) {
return OK(null);
}
return this._def.innerType._parse(input);
}
unwrap() {
return this._def.innerType;
}
};
ZodNullable.create = (type, params) => {
return new ZodNullable({
innerType: type,
typeName: ZodFirstPartyTypeKind.ZodNullable,
...processCreateParams(params)
});
};
ZodDefault = class extends ZodType {
_parse(input) {
const { ctx } = this._processInputParams(input);
let data = ctx.data;
if (ctx.parsedType === ZodParsedType.undefined) {
data = this._def.defaultValue();
}
return this._def.innerType._parse({
data,
path: ctx.path,
parent: ctx
});
}
removeDefault() {
return this._def.innerType;
}
};
ZodDefault.create = (type, params) => {
return new ZodDefault({
innerType: type,
typeName: ZodFirstPartyTypeKind.ZodDefault,
defaultValue: typeof params.default === "function" ? params.default : () => params.default,
...processCreateParams(params)
});
};
ZodCatch = class extends ZodType {
_parse(input) {
const { ctx } = this._processInputParams(input);
const newCtx = {
...ctx,
common: {
...ctx.common,
issues: []
}
};
const result = this._def.innerType._parse({
data: newCtx.data,
path: newCtx.path,
parent: {
...newCtx
}
});
if (isAsync(result)) {
return result.then((result2) => {
return {
status: "valid",
value: result2.status === "valid" ? result2.value : this._def.catchValue({
get error() {
return new ZodError(newCtx.common.issues);
},
input: newCtx.data
})
};
});
} else {
return {
status: "valid",
value: result.status === "valid" ? result.value : this._def.catchValue({
get error() {
return new ZodError(newCtx.common.issues);
},
input: newCtx.data
})
};
}
}
removeCatch() {
return this._def.innerType;
}
};
ZodCatch.create = (type, params) => {
return new ZodCatch({
innerType: type,
typeName: ZodFirstPartyTypeKind.ZodCatch,
catchValue: typeof params.catch === "function" ? params.catch : () => params.catch,
...processCreateParams(params)
});
};
ZodNaN = class extends ZodType {
_parse(input) {
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.nan) {
const ctx = this._getOrReturnCtx(input);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.nan,
received: ctx.parsedType
});
return INVALID;
}
return { status: "valid", value: input.data };
}
};
ZodNaN.create = (params) => {
return new ZodNaN({
typeName: ZodFirstPartyTypeKind.ZodNaN,
...processCreateParams(params)
});
};
BRAND = Symbol("zod_brand");
ZodBranded = class extends ZodType {
_parse(input) {
const { ctx } = this._processInputParams(input);
const data = ctx.data;
return this._def.type._parse({
data,
path: ctx.path,
parent: ctx
});
}
unwrap() {
return this._def.type;
}
};
ZodPipeline = class _ZodPipeline extends ZodType {
_parse(input) {
const { status, ctx } = this._processInputParams(input);
if (ctx.common.async) {
const handleAsync = async () => {
const inResult = await this._def.in._parseAsync({
data: ctx.data,
path: ctx.path,
parent: ctx
});
if (inResult.status === "aborted")
return INVALID;
if (inResult.status === "dirty") {
status.dirty();
return DIRTY(inResult.value);
} else {
return this._def.out._parseAsync({
data: inResult.value,
path: ctx.path,
parent: ctx
});
}
};
return handleAsync();
} else {
const inResult = this._def.in._parseSync({
data: ctx.data,
path: ctx.path,
parent: ctx
});
if (inResult.status === "aborted")
return INVALID;
if (inResult.status === "dirty") {
status.dirty();
return {
status: "dirty",
value: inResult.value
};
} else {
return this._def.out._parseSync({
data: inResult.value,
path: ctx.path,
parent: ctx
});
}
}
}
static create(a, b) {
return new _ZodPipeline({
in: a,
out: b,
typeName: ZodFirstPartyTypeKind.ZodPipeline
});
}
};
ZodReadonly = class extends ZodType {
_parse(input) {
const result = this._def.innerType._parse(input);
const freeze = (data) => {
if (isValid(data)) {
data.value = Object.freeze(data.value);
}
return data;
};
return isAsync(result) ? result.then((data) => freeze(data)) : freeze(result);
}
unwrap() {
return this._def.innerType;
}
};
ZodReadonly.create = (type, params) => {
return new ZodReadonly({
innerType: type,
typeName: ZodFirstPartyTypeKind.ZodReadonly,
...processCreateParams(params)
});
};
late = {
object: ZodObject.lazycreate
};
(function(ZodFirstPartyTypeKind2) {
ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber";
ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN";
ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt";
ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean";
ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate";
ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol";
ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined";
ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull";
ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny";
ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown";
ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever";
ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid";
ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray";
ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject";
ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion";
ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion";
ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection";
ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple";
ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord";
ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap";
ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet";
ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction";
ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy";
ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral";
ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum";
ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects";
ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum";
ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional";
ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable";
ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault";
ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch";
ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise";
ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded";
ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
instanceOfType = (cls, params = {
message: `Input not instance of ${cls.name}`
}) => custom((data) => data instanceof cls, params);
stringType = ZodString.create;
numberType = ZodNumber.create;
nanType = ZodNaN.create;
bigIntType = ZodBigInt.create;
booleanType = ZodBoolean.create;
dateType = ZodDate.create;
symbolType = ZodSymbol.create;
undefinedType = ZodUndefined.create;
nullType = ZodNull.create;
anyType = ZodAny.create;
unknownType = ZodUnknown.create;
neverType = ZodNever.create;
voidType = ZodVoid.create;
arrayType = ZodArray.create;
objectType = ZodObject.create;
strictObjectType = ZodObject.strictCreate;
unionType = ZodUnion.create;
discriminatedUnionType = ZodDiscriminatedUnion.create;
intersectionType = ZodIntersection.create;
tupleType = ZodTuple.create;
recordType = ZodRecord.create;
mapType = ZodMap.create;
setType = ZodSet.create;
functionType = ZodFunction.create;
lazyType = ZodLazy.create;
literalType = ZodLiteral.create;
enumType = ZodEnum.create;
nativeEnumType = ZodNativeEnum.create;
promiseType = ZodPromise.create;
effectsType = ZodEffects.create;
optionalType = ZodOptional.create;
nullableType = ZodNullable.create;
preprocessType = ZodEffects.createWithPreprocess;
pipelineType = ZodPipeline.create;
ostring = () => stringType().optional();
onumber = () => numberType().optional();
oboolean = () => booleanType().optional();
coerce = {
string: (arg) => ZodString.create({ ...arg, coerce: true }),
number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
boolean: (arg) => ZodBoolean.create({
...arg,
coerce: true
}),
bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
date: (arg) => ZodDate.create({ ...arg, coerce: true })
};
NEVER = INVALID;
z = /* @__PURE__ */ Object.freeze({
__proto__: null,
defaultErrorMap: errorMap,
setErrorMap,
getErrorMap,
makeIssue,
EMPTY_PATH,
addIssueToContext,
ParseStatus,
INVALID,
DIRTY,
OK,
isAborted,
isDirty,
isValid,
isAsync,
get util() {
return util;
},
get objectUtil() {
return objectUtil;
},
ZodParsedType,
getParsedType,
ZodType,
datetimeRegex,
ZodString,
ZodNumber,
ZodBigInt,
ZodBoolean,
ZodDate,
ZodSymbol,
ZodUndefined,
ZodNull,
ZodAny,
ZodUnknown,
ZodNever,
ZodVoid,
ZodArray,
ZodObject,
ZodUnion,
ZodDiscriminatedUnion,
ZodIntersection,
ZodTuple,
ZodRecord,
ZodMap,
ZodSet,
ZodFunction,
ZodLazy,
ZodLiteral,
ZodEnum,
ZodNativeEnum,
ZodPromise,
ZodEffects,
ZodTransformer: ZodEffects,
ZodOptional,
ZodNullable,
ZodDefault,
ZodCatch,
ZodNaN,
BRAND,
ZodBranded,
ZodPipeline,
ZodReadonly,
custom,
Schema: ZodType,
ZodSchema: ZodType,
late,
get ZodFirstPartyTypeKind() {
return ZodFirstPartyTypeKind;
},
coerce,
any: anyType,
array: arrayType,
bigint: bigIntType,
boolean: booleanType,
date: dateType,
discriminatedUnion: discriminatedUnionType,
effect: effectsType,
"enum": enumType,
"function": functionType,
"instanceof": instanceOfType,
intersection: intersectionType,
lazy: lazyType,
literal: literalType,
map: mapType,
nan: nanType,
nativeEnum: nativeEnumType,
never: neverType,
"null": nullType,
nullable: nullableType,
number: numberType,
object: objectType,
oboolean,
onumber,
optional: optionalType,
ostring,
pipeline: pipelineType,
preprocess: preprocessType,
promise: promiseType,
record: recordType,
set: setType,
strictObject: strictObjectType,
string: stringType,
symbol: symbolType,
transformer: effectsType,
tuple: tupleType,
"undefined": undefinedType,
union: unionType,
unknown: unknownType,
"void": voidType,
NEVER,
ZodIssueCode,
quotelessJson,
ZodError
});
}
});
// cypher-executor/src/lib/prompt-recipe-schema.ts
var TransformSchema, KBDBBlockFragmentSchema, KVFragmentSchema, FragmentSchema, InputSchema, PromptAssemblySchema, OutputSpecSchema, PromptRecipeSchema;
var init_prompt_recipe_schema = __esm({
"cypher-executor/src/lib/prompt-recipe-schema.ts"() {
"use strict";
init_lib();
TransformSchema = z.string().regex(/^[a-z_]+(:.+)?$/, "transform \u5FC5\u9808\u70BA name \u6216 name:arg \u683C\u5F0F");
KBDBBlockFragmentSchema = z.object({
var: z.string().min(1),
// prompt template 內的變數名
source: z.literal("kbdb_block"),
block_id: z.string().optional(),
// 二擇一
block_page_name: z.string().optional(),
// 比 block_id 穩定
field: z.string().default("content")
// 抓 block 的哪個欄位
});
KVFragmentSchema = z.object({
var: z.string().min(1),
source: z.literal("kv"),
key: z.string().min(1)
});
FragmentSchema = z.discriminatedUnion("source", [
KBDBBlockFragmentSchema,
KVFragmentSchema
]).superRefine((d, ctx) => {
if (d.source === "kbdb_block" && !d.block_id && !d.block_page_name) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "block_id \u6216 block_page_name \u5FC5\u586B\u5176\u4E00"
});
}
});
InputSchema = z.object({
var: z.string().min(1),
from: z.string().min(1),
// JSONPath-lite,如 "ctx.read_drafts.blocks"
transform: TransformSchema.optional(),
default: z.unknown().optional()
// from 取不到時的預設值(避免炸 prompt)
});
PromptAssemblySchema = z.object({
system: z.string().min(1),
// 模板,可含 {{var}}
user: z.string().min(1)
});
OutputSpecSchema = z.object({
format: z.enum(["text", "json"]).default("text"),
// 若 format=json,可選 schema 做 parse 後驗證(簡化版,列必填欄位即可)
required_fields: z.array(z.string()).optional()
});
PromptRecipeSchema = z.object({
kind: z.literal("prompt_recipe"),
name: z.string().min(1).regex(/^[a-z][a-z0-9_]*$/, "name \u70BA lowercase + underscore"),
version: z.number().int().positive().default(1),
description: z.string().optional(),
model: z.enum(["haiku", "sonnet", "opus"]).default("sonnet"),
fragments: z.array(FragmentSchema).default([]),
inputs: z.array(InputSchema).default([]),
prompt_assembly: PromptAssemblySchema,
output: OutputSpecSchema.default({ format: "text" })
});
}
});
// cypher-executor/src/lib/recipe-loader.ts
async function loadPromptRecipe(recipeRef, recipesKv) {
const key = recipeRef.startsWith("prompt_recipe:") ? recipeRef : `prompt_recipe:${recipeRef}`;
const raw2 = await recipesKv.get(key);
if (!raw2) {
throw new RecipeLoadError(`\u627E\u4E0D\u5230 recipe: ${key}`, key);
}
let parsed;
try {
parsed = JSON.parse(raw2);
} catch (e) {
throw new RecipeLoadError(
`recipe ${key} \u4E0D\u662F\u5408\u6CD5 JSON: ${e instanceof Error ? e.message : String(e)}`,
key
);
}
const result = PromptRecipeSchema.safeParse(parsed);
if (!result.success) {
const issues = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
throw new RecipeLoadError(`recipe ${key} schema \u9A57\u8B49\u5931\u6557: ${issues}`, key);
}
return result.data;
}
var RecipeLoadError;
var init_recipe_loader = __esm({
"cypher-executor/src/lib/recipe-loader.ts"() {
"use strict";
init_prompt_recipe_schema();
RecipeLoadError = class extends Error {
constructor(message, recipe) {
super(message);
this.recipe = recipe;
}
};
}
});
// cypher-executor/src/lib/recipe-transforms.ts
function applyTransform(value, spec) {
const colonIdx = spec.indexOf(":");
const name = colonIdx === -1 ? spec : spec.slice(0, colonIdx);
const arg = colonIdx === -1 ? void 0 : spec.slice(colonIdx + 1);
const fn = transforms[name];
if (!fn) throw new Error(`\u672A\u77E5 transform: ${name}`);
return fn(value, arg);
}
var transforms;
var init_recipe_transforms = __esm({
"cypher-executor/src/lib/recipe-transforms.ts"() {
"use strict";
transforms = {
json_array: (v) => JSON.stringify(v ?? []),
to_string: (v) => {
if (v === null || v === void 0) return "";
if (typeof v === "object") return JSON.stringify(v);
return String(v);
},
join: (v, sep) => {
if (!Array.isArray(v)) throw new Error("join: input \u4E0D\u662F array");
return v.map((x) => typeof x === "string" ? x : JSON.stringify(x)).join(sep ?? "\n");
},
markdown_list: (v) => {
if (!Array.isArray(v)) throw new Error("markdown_list: input \u4E0D\u662F array");
return v.map((x) => `- ${typeof x === "string" ? x : JSON.stringify(x)}`).join("\n");
},
extract_field: (v, field) => {
if (!field) throw new Error("extract_field: \u9700\u8981 field \u53C3\u6578\uFF0C\u4F8B\u5982 extract_field:page_name");
if (!Array.isArray(v)) throw new Error("extract_field: input \u4E0D\u662F array");
return v.map((x) => x && typeof x === "object" ? x[field] : void 0);
},
first: (v) => {
if (!Array.isArray(v)) return v;
return v[0];
},
pluck_content: (v) => {
if (!Array.isArray(v)) throw new Error("pluck_content: input \u4E0D\u662F array");
return v.map((b) => b && typeof b === "object" ? String(b.content ?? "") : "").filter((s) => s.length > 0).join("\n\n---\n\n");
}
};
}
});
// cypher-executor/src/lib/recipe-expander.ts
function getByPath(ctx, path) {
const parts = path.split(".");
let cur = ctx;
for (const p of parts) {
if (cur === null || cur === void 0) return void 0;
if (typeof cur !== "object") return void 0;
cur = cur[p];
}
return cur;
}
function interpolate(template, vars) {
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] !== void 0 ? vars[key] : `{{${key}}}`);
}
async function fetchKbdbBlock(env, apiKey, fragment) {
const base = (env.KBDB_BASE_URL ?? "https://kbdb.finally.click").replace(/\/$/, "");
let url;
if (fragment.block_id) {
url = `${base}/blocks/${encodeURIComponent(fragment.block_id)}`;
} else {
url = `${base}/blocks?page_name=${encodeURIComponent(fragment.block_page_name)}&limit=1`;
}
const res = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
if (!res.ok) throw new Error(`KBDB fragment \u6293\u53D6\u5931\u6557 (${res.status}): ${url}`);
const data = await res.json();
const block = fragment.block_id ? data : data.blocks?.[0] ?? {};
if (!block) throw new Error(`KBDB block \u4E0D\u5B58\u5728: ${fragment.block_id ?? fragment.block_page_name}`);
const fieldVal = block[fragment.field];
if (fieldVal === void 0) throw new Error(`block \u7F3A\u6B04\u4F4D "${fragment.field}"`);
return fieldVal;
}
async function resolveFragment(env, apiKey, frag) {
if (frag.source === "kv") {
const val = await env.RECIPES.get(frag.key);
if (val === null) throw new Error(`KV \u627E\u4E0D\u5230 key: ${frag.key}`);
return { var: frag.var, value: val };
}
return { var: frag.var, value: await fetchKbdbBlock(env, apiKey, frag) };
}
function resolveInput(input, ctx) {
let val = getByPath(ctx, input.from);
const beforeDefault = val;
if (val === void 0) val = input.default;
try {
if (input.transform) val = applyTransform(val, input.transform);
return { var: input.var, value: val };
} catch (e) {
const valType = Array.isArray(beforeDefault) ? `array(${beforeDefault.length})` : beforeDefault === void 0 ? "undefined(default applied)" : typeof beforeDefault;
throw new Error(`${e instanceof Error ? e.message : String(e)} [path=${input.from}, type=${valType}]`);
}
}
async function expandPromptRecipe(recipeRef, ctx, env, apiKey) {
const recipe = await loadPromptRecipe(recipeRef, env.RECIPES);
const vars = {};
for (const frag of recipe.fragments) {
const { var: name, value } = await resolveFragment(env, apiKey, frag);
vars[name] = typeof value === "string" ? value : JSON.stringify(value);
}
for (const inp of recipe.inputs) {
const { var: name, value } = resolveInput(inp, ctx);
vars[name] = typeof value === "string" ? value : JSON.stringify(value);
}
const system = interpolate(recipe.prompt_assembly.system, vars);
const user = interpolate(recipe.prompt_assembly.user, vars);
const prompt = `${system}
--- USER ---
${user}`;
return {
prompt,
model: recipe.model,
output_format: recipe.output.format,
output_required_fields: recipe.output.required_fields
};
}
var init_recipe_expander = __esm({
"cypher-executor/src/lib/recipe-expander.ts"() {
"use strict";
init_recipe_loader();
init_recipe_transforms();
}
});
// cypher-executor/src/lib/paused-runs.ts
async function readIndex(kv, apiKey) {
const raw2 = await kv.get(`${IDX_PREFIX}${apiKey}`);
if (!raw2) return [];
try {
const arr = JSON.parse(raw2);
return Array.isArray(arr) ? arr : [];
} catch {
return [];
}
}
async function writeIndex(kv, apiKey, entries) {
const now2 = Date.now();
const fresh = entries.filter((e) => e.expires_at > now2);
await kv.put(`${IDX_PREFIX}${apiKey}`, JSON.stringify(fresh), { expirationTtl: TTL_SECONDS });
}
async function persistPausedRun(kv, taskId, state) {
await kv.put(`${KEY_PREFIX}${taskId}`, JSON.stringify(state), { expirationTtl: TTL_SECONDS });
if (state.api_key) {
const idx = await readIndex(kv, state.api_key);
const filtered = idx.filter((e) => e.task_id !== taskId);
filtered.unshift({
task_id: taskId,
run_id: state.run_id,
paused_node_id: state.paused_node_id,
workflow_name: state.graph.name,
expires_at: state.expires_at,
persisted_at: Date.now()
});
await writeIndex(kv, state.api_key, filtered.slice(0, 100));
}
}
async function loadPausedRun(kv, taskId) {
const raw2 = await kv.get(`${KEY_PREFIX}${taskId}`);
if (!raw2) return null;
try {
return JSON.parse(raw2);
} catch {
return null;
}
}
async function listPausedRunsByApiKey(kv, apiKey, limit = 20) {
const idx = await readIndex(kv, apiKey);
const now2 = Date.now();
return idx.filter((e) => e.expires_at > now2).slice(0, limit);
}
async function consumePausedRun(kv, taskId) {
const state = await loadPausedRun(kv, taskId);
if (!state) return null;
await kv.delete(`${KEY_PREFIX}${taskId}`).catch(() => {
});
if (state.api_key) {
const idx = await readIndex(kv, state.api_key);
const filtered = idx.filter((e) => e.task_id !== taskId);
await writeIndex(kv, state.api_key, filtered).catch(() => {
});
}
return state;
}
function isResumablePending(result) {
if (!result || typeof result !== "object") return null;
const r = result;
if (r.pending !== true) return null;
if (typeof r.task_id !== "string" || !r.task_id) return null;
return { task_id: r.task_id };
}
function parseRecipeOutput(result, format, requiredFields) {
if (format !== "json" || !result || typeof result !== "object") return result;
const r = result;
const text = r.data?.text ?? r.text;
if (typeof text !== "string") return result;
let jsonText = String(text).trim();
const fenceMatch = jsonText.match(/^```(?:json)?\s*\n([\s\S]*?)\n```$/);
if (fenceMatch) jsonText = fenceMatch[1].trim();
try {
const parsed = JSON.parse(jsonText);
if (requiredFields && parsed && typeof parsed === "object") {
const missing = requiredFields.filter((f) => !(f in parsed));
if (missing.length > 0) {
return { success: false, error: `recipe output \u7F3A\u6B04\u4F4D: ${missing.join(", ")}`, raw: parsed };
}
}
return { success: true, data: parsed, ...parsed && typeof parsed === "object" ? parsed : {} };
} catch (e) {
return { success: false, error: `recipe output JSON parse \u5931\u6557: ${e instanceof Error ? e.message : String(e)}`, raw_text: text };
}
}
var KEY_PREFIX, IDX_PREFIX, TTL_SECONDS;
var init_paused_runs = __esm({
"cypher-executor/src/lib/paused-runs.ts"() {
"use strict";
KEY_PREFIX = "paused_run:";
IDX_PREFIX = "paused_idx:";
TTL_SECONDS = 24 * 60 * 60;
}
});
// cypher-executor/src/lib/magic-vars.ts
function isoWeekNumber(d) {
const target = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
const dayNum = (target.getUTCDay() + 6) % 7;
target.setUTCDate(target.getUTCDate() - dayNum + 3);
const firstThursday = new Date(Date.UTC(target.getUTCFullYear(), 0, 4));
const weekNum = 1 + Math.round(
((target.getTime() - firstThursday.getTime()) / 864e5 - 3 + (firstThursday.getUTCDay() + 6) % 7) / 7
);
return { year: target.getUTCFullYear(), week: weekNum };
}
function pad2(n) {
return n.toString().padStart(2, "0");
}
function buildMagicVars(now2 = /* @__PURE__ */ new Date()) {
const iso = now2.toISOString();
const yyyy = now2.getUTCFullYear();
const mm = pad2(now2.getUTCMonth() + 1);
const dd = pad2(now2.getUTCDate());
const hh = pad2(now2.getUTCHours());
const mi = pad2(now2.getUTCMinutes());
const ss = pad2(now2.getUTCSeconds());
const yesterday = new Date(now2.getTime() - 864e5);
const yMm = pad2(yesterday.getUTCMonth() + 1);
const yDd = pad2(yesterday.getUTCDate());
const { year: isoYear, week: isoWeek } = isoWeekNumber(now2);
return {
// 日期 / 時間(UTC)
_today: `${yyyy}-${mm}-${dd}`,
// 2026-05-16
_yesterday: `${yesterday.getUTCFullYear()}-${yMm}-${yDd}`,
// 2026-05-15
_now: iso,
// ISO 8601
_now_unix: now2.getTime(),
// unix ms
_now_unix_s: Math.floor(now2.getTime() / 1e3),
// unix sec
// 個別欄位(給 path / page_name 拼)
_year: yyyy,
_month: mm,
_day: dd,
_hour: hh,
_minute: mi,
_second: ss,
// ISO 週(roadmap weekly archive 必備)
_iso_week: `${isoYear}-W${pad2(isoWeek)}`,
// 2026-W20
_iso_week_num: isoWeek,
_iso_year: isoYear,
// 簡單時間 slot(cron-friendly)
_yyyymm: `${yyyy}${mm}`,
// 202605
_yyyymmdd: `${yyyy}${mm}${dd}`,
// 20260516
// 週幾(0=週日,1=週一 ... 6=週六;ISO 風格在 _iso_weekday)
_weekday: now2.getUTCDay(),
_iso_weekday: (now2.getUTCDay() + 6) % 7 + 1
// 1=Mon...7=Sun
};
}
var init_magic_vars = __esm({
"cypher-executor/src/lib/magic-vars.ts"() {
"use strict";
}
});
// cypher-executor/src/lib/telemetry.ts
async function hashApiKey(apiKey) {
if (!apiKey) return "anon";
const encoder = new TextEncoder();
const data = encoder.encode(apiKey);
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.slice(0, 8).map((b) => b.toString(16).padStart(2, "0")).join("");
}
function kbdbCreateBlockUrl(env) {
const subdomain = env.WORKER_SUBDOMAIN || "uncle6-me";
return `https://arcrun-kbdb-create-block.${subdomain}.workers.dev`;
}
function recordTelemetry(env, apiKey, record, ctx) {
const promise = (async () => {
try {
const api_key_hash = await hashApiKey(apiKey ?? "");
const platformKey = env.PLATFORM_API_KEY || apiKey || "";
if (!platformKey) {
console.warn("[telemetry] no api_key, skipping");
return;
}
const body = {
api_key: platformKey,
type: "agent-telemetry",
source: "cypher-executor",
user_id: "platform_telemetry",
content: JSON.stringify(record),
metadata_json: JSON.stringify({ ...record, api_key_hash }),
tags_json: JSON.stringify([
"agent-telemetry",
`event:${record.event_type}`
])
};
const res = await fetch(kbdbCreateBlockUrl(env), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
});
if (!res.ok) {
console.warn(
"[telemetry] write failed",
res.status,
await res.text().catch(() => "no body")
);
}
} catch (e) {
console.warn("[telemetry] exception", e);
}
})();
if (ctx?.waitUntil) {
ctx.waitUntil(promise);
}
}
var init_telemetry = __esm({
"cypher-executor/src/lib/telemetry.ts"() {
"use strict";
}
});
// cypher-executor/src/graph-executor.ts
function propagateCtx(context, upstreamResult, upstreamNodeId) {
const baseCtx = typeof context === "object" && context !== null ? context : {};
const baseResult = typeof upstreamResult === "object" && upstreamResult !== null ? upstreamResult : {};
return {
...baseCtx,
...baseResult,
[upstreamNodeId]: upstreamResult
};
}
function interpolateString(s, ctx) {
const single = s.match(/^\s*\{\{([\w.]+)\}\}\s*$/);
if (single) {
const val = getNestedValue(ctx, single[1]);
return val === void 0 ? s : val;
}
return s.replace(/\{\{([\w.]+)\}\}/g, (_, key) => {
const val = getNestedValue(ctx, key);
if (val === void 0) return `{{${key}}}`;
if (typeof val === "string") return val;
return JSON.stringify(val);
});
}
function interpolateValue(v, ctx) {
if (typeof v === "string") return interpolateString(v, ctx);
if (Array.isArray(v)) return v.map((item) => interpolateValue(item, ctx));
if (v !== null && typeof v === "object") {
const result = {};
for (const [k, val] of Object.entries(v)) {
result[k] = interpolateValue(val, ctx);
}
return result;
}
return v;
}
function interpolateData(data, ctx) {
if (!data) return {};
return interpolateValue(data, ctx);
}
function getNestedValue(ctx, path) {
const parts = path.split(".");
let cur = ctx;
for (const p of parts) {
if (cur === null || cur === void 0) return void 0;
if (typeof cur !== "object") return void 0;
cur = cur[p];
}
return cur;
}
function readBranch(result) {
if (!result || typeof result !== "object") return void 0;
const r = result;
const data = r.data && typeof r.data === "object" ? r.data : void 0;
const named = data?.branch ?? r.branch;
if (typeof named === "string") return named;
const bool = data?.result ?? r.result;
if (typeof bool === "boolean") return bool ? "true" : "false";
return void 0;
}
function isFailure(result) {
if (!result || typeof result !== "object") return false;
const r = result;
return r["success"] === false || "error" in r;
}
function evaluateCondition(condition, context) {
if (!context || typeof context !== "object") return false;
const ctx = context;
const expr = condition.replace(/result\./g, "").replace(/ctx\./g, "");
const eqMatch = expr.match(/^(\w+)\s*===?\s*(.+)$/);
if (eqMatch) {
const key2 = eqMatch[1];
const rawVal = eqMatch[2].trim();
const expected = rawVal === "true" ? true : rawVal === "false" ? false : rawVal.replace(/['"]/g, "");
return ctx[key2] === expected;
}
const gtMatch = expr.match(/^(\w+)\s*>\s*(\d+)$/);
if (gtMatch) {
return Number(ctx[gtMatch[1]]) > Number(gtMatch[2]);
}
const key = expr.trim();
if (key && key in ctx) return !!ctx[key];
return true;
}
function getIterableFromContext(context, key) {
if (!context || typeof context !== "object") return [];
const variants = [
key + "s",
// paragraph → paragraphs
key.replace(/y$/, "ies"),
// entity → entities
key.replace(/(s|x|z|ch|sh)$/, "$1es"),
// box → boxes
key
// singular fallback
];
const obj = context;
for (const v of variants) {
if (Array.isArray(obj[v])) return obj[v];
}
for (const val of Object.values(obj)) {
if (val !== null && typeof val === "object" && !Array.isArray(val)) {
for (const v of variants) {
const nested = val[v];
if (Array.isArray(nested)) return nested;
}
}
}
return [];
}
var GraphExecutor;
var init_graph_executor = __esm({
"cypher-executor/src/graph-executor.ts"() {
"use strict";
init_types();
init_auth_dispatcher();
init_recipe_expander();
init_recipes();
init_paused_runs();
init_magic_vars();
init_telemetry();
GraphExecutor = class _GraphExecutor {
loader;
workflowLoader;
env;
apiKey;
recordComponentReference;
// kbdb-base §7.1+§7.5.h:本次執行用到的 recipe **key**(uuid 優先,舊資料 fallback canonical_id)。
// 判定單位是「工作流執行」(n8n execution):執行結束後由 executeWebhookGraph 一次性把這組 key
// 各記成功/失敗到 KBDB 市場星數(per-uuid → 能區分同 canonical 的不同作者版本,§7.5.5)。執行中只收集。
usedRecipeKeys = /* @__PURE__ */ new Set();
// resumable workflow(SDD: resumable-workflow/design.md)
// 暫停時持久化 state 用,需在 execute 進入時設定
currentGraph;
currentRunId;
constructor(loader, workflowLoader, env, apiKey) {
this.loader = loader;
this.workflowLoader = workflowLoader;
this.env = env;
this.apiKey = apiKey;
}
async execute(graph, initialContext, kvNamespace) {
const trace = [];
const kvStore = kvNamespace ? { runId: `${graph.id}-${Date.now()}`, kv: kvNamespace } : void 0;
this.currentGraph = graph;
this.currentRunId = kvStore?.runId ?? `${graph.id}-${Date.now()}`;
const ctxWithMagic = {
...initialContext,
...buildMagicVars()
};
const hasIncoming = new Set(graph.edges.map((e) => e.to));
const startNodes = graph.nodes.filter((n) => !hasIncoming.has(n.id));
if (startNodes.length === 0) {
return { data: ctxWithMagic, trace };
}
const fanIn = /* @__PURE__ */ new Map();
for (const node of graph.nodes) {
const inDeg = graph.edges.filter((e) => e.to === node.id).length;
if (inDeg > 1) {
fanIn.set(node.id, { ctx: { ...ctxWithMagic }, remaining: inDeg });
}
}
const results = await Promise.all(
startNodes.map(
(node) => this.executeNode(node, graph, ctxWithMagic, /* @__PURE__ */ new Set(), trace, fanIn, kvStore)
)
);
let mergedResult;
if (results.length === 1) {
mergedResult = results[0];
} else {
mergedResult = results.reduce(
(acc, r) => ({
...acc,
...typeof r === "object" && r !== null ? r : {}
}),
{}
);
}
return { data: mergedResult, trace };
}
/**
* 從 paused state 繼續執行 workflow
* SDD: resumable-workflow/design.md §3.2
*
* 流程:
* 1. 把 paused_node 當已執行(result = callbackResult,注入進 context)
* 2. 找出 paused_node 的所有下游節點當新起點
* 3. 執行下游節點直到結束(或再次 paused)
*/
async resumeFromPaused(args) {
const { graph, paused_node_id, paused_context, prior_trace, kvNamespace } = args;
let { callback_result } = args;
callback_result = parseRecipeOutput(
callback_result,
args.recipe_output_format,
args.recipe_output_required_fields
);
this.currentGraph = graph;
this.currentRunId = `${graph.id}-resume-${Date.now()}`;
const trace = [...prior_trace];
const kvStore = kvNamespace ? { runId: this.currentRunId, kv: kvNamespace } : void 0;
if (kvStore) {
await kvSetNodeOutput(kvStore, paused_node_id, callback_result);
}
const mergedContext = {
...paused_context,
...callback_result && typeof callback_result === "object" ? callback_result : {},
[paused_node_id]: callback_result
};
if (kvStore) {
if (!mergedContext._kv_outputs) mergedContext._kv_outputs = {};
mergedContext._kv_outputs[paused_node_id] = callback_result;
}
const downstreamEdges = graph.edges.filter((e) => e.from === paused_node_id);
if (downstreamEdges.length === 0) {
return { data: callback_result, trace };
}
const fanIn = /* @__PURE__ */ new Map();
for (const node of graph.nodes) {
const inDeg = graph.edges.filter((e) => e.to === node.id).length;
if (inDeg > 1) {
fanIn.set(node.id, { ctx: { ...mergedContext }, remaining: inDeg });
}
}
const visited = /* @__PURE__ */ new Set([`${paused_node_id}:${JSON.stringify(paused_context).slice(0, 50)}`]);
const downstreamNodes = downstreamEdges.map((e) => graph.nodes.find((n) => n.id === e.to)).filter((n) => !!n);
const results = await Promise.all(
downstreamNodes.map(
(node) => this.executeNode(node, graph, mergedContext, visited, trace, fanIn, kvStore)
)
);
let mergedResult;
if (results.length === 1) {
mergedResult = results[0];
} else {
mergedResult = results.reduce(
(acc, r) => ({
...acc,
...typeof r === "object" && r !== null ? r : {}
}),
{}
);
}
return { data: mergedResult, trace };
}
async executeNode(node, graph, context, visited, trace, fanIn, kvStore) {
const nodeKey = `${node.id}:${JSON.stringify(context).slice(0, 50)}`;
if (visited.has(nodeKey)) return context;
visited.add(nodeKey);
const start = Date.now();
let result = context;
let nodeInput = context;
try {
switch (node.type) {
case "Input":
result = node.data ?? context;
nodeInput = result;
break;
case "Component": {
if (!node.componentId) throw new Error(`\u7BC0\u9EDE ${node.id} \u7F3A\u5C11 componentId`);
const runner = await this.loader(node.componentId);
const ctx = context;
const resolvedData = interpolateData(node.data, ctx);
let mergedContext = {
...ctx,
...resolvedData
};
if (this.env && this.apiKey) {
mergedContext = await resolveCredentialRefs(mergedContext, this.env, this.apiKey);
}
if (node.componentId === "claude_api") {
const baseUrl = this.env?.PUBLIC_BASE_URL ?? "https://cypher.arcrun.dev";
mergedContext.callback_url = `${baseUrl.replace(/\/$/, "")}/workflows/resume`;
}
if (typeof resolvedData.recipe === "string" && this.env?.RECIPES) {
try {
const expanded = await expandPromptRecipe(
resolvedData.recipe,
ctx,
this.env,
this.apiKey ?? ""
);
mergedContext = {
...mergedContext,
prompt: expanded.prompt,
model: expanded.model,
_recipe_output_format: expanded.output_format,
_recipe_output_required_fields: expanded.output_required_fields
};
} catch (e) {
throw new Error(`recipe \u5C55\u958B\u5931\u6557 (${resolvedData.recipe}): ${e instanceof Error ? e.message : String(e)}`);
}
}
if (this.env && this.apiKey) {
const dispatched = await tryAuthDispatch(node.componentId, mergedContext, this.env, this.apiKey);
if (dispatched) {
mergedContext = dispatched;
}
}
if (this.env?.RECIPES) {
try {
const apiRecipe = await resolveRecipe(node.componentId, this.env.RECIPES);
if (apiRecipe) this.usedRecipeKeys.add(apiRecipe.uuid ?? apiRecipe.canonical_id);
} catch {
}
}
nodeInput = mergedContext;
result = await runner(mergedContext);
const pending = isResumablePending(result);
if (pending && this.env?.EXEC_CONTEXT && this.currentGraph && this.currentRunId) {
trace.push({
nodeId: node.id,
type: node.type,
input: nodeInput,
output: result,
duration_ms: Date.now() - start
});
await persistPausedRun(this.env.EXEC_CONTEXT, pending.task_id, {
run_id: this.currentRunId,
graph: this.currentGraph,
paused_node_id: node.id,
paused_context: context,
paused_pending_result: result,
trace_so_far: trace,
api_key: this.apiKey,
expires_at: Date.now() + 24 * 60 * 60 * 1e3,
recipe_output_format: mergedContext._recipe_output_format,
recipe_output_required_fields: mergedContext._recipe_output_required_fields
});
throw new WorkflowPaused(pending.task_id, this.currentRunId, node.id, trace);
}
result = parseRecipeOutput(
result,
mergedContext._recipe_output_format,
mergedContext._recipe_output_required_fields
);
if (kvStore && result !== null && result !== void 0 && graph.edges.some((e) => e.from === node.id && e.type === "PIPE")) {
await kvSetNodeOutput(kvStore, node.id, result);
}
void this.recordComponentReference?.(node.componentId, graph.id).catch(() => {
});
break;
}
case "Output":
result = context;
break;
}
} catch (e) {
if (e instanceof WorkflowPaused) throw e;
const errMsg = e.message || String(e);
const duration_ms2 = Date.now() - start;
trace.push({
nodeId: node.id,
type: node.type,
input: nodeInput,
output: null,
error: errMsg,
duration_ms: duration_ms2
});
if (this.env && node.type === "Component") {
recordTelemetry(this.env, this.apiKey, {
event_type: "node_failure",
workflow_name: graph.name,
component_id: node.componentId,
error_code: "node_error",
duration_ms: duration_ms2
});
}
if (e instanceof ExecutionError) throw e;
throw new ExecutionError(
`Node ${node.id} failed: ${errMsg}`,
node.id,
nodeInput,
trace
);
}
const duration_ms = Date.now() - start;
trace.push({
nodeId: node.id,
type: node.type,
input: nodeInput,
output: result,
duration_ms
});
if (this.env && node.type === "Component") {
recordTelemetry(this.env, this.apiKey, {
event_type: "node_success",
workflow_name: graph.name,
component_id: node.componentId,
duration_ms
});
}
const outEdges = graph.edges.filter((e) => e.from === node.id);
for (const edge of outEdges) {
const nextNode = graph.nodes.find((n) => n.id === edge.to);
if (!nextNode) continue;
switch (edge.type) {
case "PIPE": {
const pipeContext = propagateCtx(context, result, node.id);
if (kvStore) {
const kvOutput = await kvGetNodeOutput(kvStore, node.id);
if (kvOutput !== void 0) {
if (!pipeContext._kv_outputs) pipeContext._kv_outputs = {};
pipeContext._kv_outputs[node.id] = kvOutput;
}
}
const fanInState = fanIn.get(nextNode.id);
if (fanInState) {
Object.assign(fanInState.ctx, pipeContext);
fanInState.remaining--;
if (fanInState.remaining === 0) {
result = await this.executeNode(nextNode, graph, fanInState.ctx, visited, trace, fanIn, kvStore);
}
} else {
result = await this.executeNode(nextNode, graph, pipeContext, visited, trace, fanIn, kvStore);
}
break;
}
case "ON_SUCCESS": {
if (!isFailure(result)) {
const mergedCtx = propagateCtx(context, result, node.id);
result = await this.executeNode(nextNode, graph, mergedCtx, visited, trace, fanIn, kvStore);
}
break;
}
case "ON_FAIL": {
if (isFailure(result)) {
const mergedCtx = propagateCtx(context, result, node.id);
result = await this.executeNode(nextNode, graph, mergedCtx, visited, trace, fanIn, kvStore);
}
break;
}
case "IF": {
const passes = evaluateCondition(edge.condition ?? "true", result);
if (passes) {
const mergedCtx = propagateCtx(context, result, node.id);
result = await this.executeNode(nextNode, graph, mergedCtx, visited, trace, fanIn, kvStore);
}
break;
}
// ── 條件邊(SDD workflow-discovery 3.11 / CP arcrun-usable 步驟 5 缺口①)──
// 為什麼要有:`if_control` 回 {result, branch} 卻沒有邊讀得懂它,
// AI 照規矩用了零件仍得寫 code 判斷走哪條 ⇒「全變成 code」的根(Arcrun#5)。
// 讀法對齊零件 output_schema:優先 data.branch(if_control/switch 的正式形狀),
// 相容 top-level branch / result 布林。讀不出分支=不走(誠實,不亂挑一條)。
case "ON_TRUE": {
if (readBranch(result) === "true") {
const mergedCtx = propagateCtx(context, result, node.id);
result = await this.executeNode(nextNode, graph, mergedCtx, visited, trace, fanIn, kvStore);
}
break;
}
case "ON_FALSE": {
if (readBranch(result) === "false") {
const mergedCtx = propagateCtx(context, result, node.id);
result = await this.executeNode(nextNode, graph, mergedCtx, visited, trace, fanIn, kvStore);
}
break;
}
case "ON_BRANCH": {
const actual = readBranch(result);
if (edge.branch !== void 0 && actual !== void 0 && actual === edge.branch) {
const mergedCtx = propagateCtx(context, result, node.id);
result = await this.executeNode(nextNode, graph, mergedCtx, visited, trace, fanIn, kvStore);
}
break;
}
case "FOREACH": {
const iteratorKey = edge.iterator ?? "item";
let items = getIterableFromContext(result, iteratorKey);
if (items.length === 0) {
items = getIterableFromContext(context, iteratorKey);
}
const iterResults = [];
const baseForeachCtx = propagateCtx(context, result, node.id);
for (const item of items) {
const itemContext = {
...baseForeachCtx,
[iteratorKey]: item
};
const itemResult = await this.executeNode(nextNode, graph, itemContext, /* @__PURE__ */ new Set(), trace, fanIn, kvStore);
iterResults.push(itemResult);
}
if (iterResults.length > 0) {
const failures = iterResults.filter(
(r) => r !== null && typeof r === "object" && r.success === false
);
if (failures.length === iterResults.length) {
const first = failures[0];
const errParts = [];
if (first.error) errParts.push(String(first.error));
if (typeof first.status === "number") errParts.push(`HTTP ${first.status}`);
const bodyData = first.data;
if (bodyData && typeof bodyData.body === "string" && bodyData.body) {
errParts.push(bodyData.body.slice(0, 200));
}
throw new Error(
`FOREACH \u6240\u6709 ${iterResults.length} \u9805\u76EE\u5747\u5931\u6557\uFF08\u9996\u9805\uFF1A${errParts.join("\uFF1B") || "\u672A\u77E5\u932F\u8AA4"}\uFF09`
);
}
}
result = { ...result, results: iterResults };
break;
}
case "CALLS_SUBFLOW": {
const subWorkflowId = nextNode.componentId?.replace("workflow://", "") ?? nextNode.id;
if (this.workflowLoader) {
const subGraph = await this.workflowLoader(subWorkflowId);
const subExecutor = new _GraphExecutor(this.loader, this.workflowLoader);
const subResult = await subExecutor.execute(
subGraph,
result,
kvStore?.kv
);
result = {
...result,
...subResult.data
};
}
break;
}
case "ON_CLICK": {
const mergedCtx = propagateCtx(context, result, node.id);
result = await this.executeNode(nextNode, graph, mergedCtx, visited, trace, fanIn, kvStore);
break;
}
case "IS_A": {
break;
}
case "CONTAINS":
case "HAS_STYLE":
case "HAS_BEHAVIOR": {
break;
}
case "CONTINUE":
break;
}
}
return result;
}
};
}
});
// cypher-executor/src/lib/schemas.ts
var graphSchema, executeSchema;
var init_schemas = __esm({
"cypher-executor/src/lib/schemas.ts"() {
"use strict";
init_lib();
graphSchema = z.object({
id: z.string().min(1),
name: z.string().min(1),
nodes: z.array(z.object({
id: z.string(),
type: z.enum(["Input", "Component", "Output"]),
componentId: z.string().optional(),
label: z.string().optional(),
data: z.record(z.unknown()).optional()
})),
edges: z.array(z.object({
from: z.string(),
to: z.string(),
type: z.enum(["PIPE", "IF", "FOREACH", "CONTINUE", "IS_A", "ON_SUCCESS", "ON_FAIL", "ON_TRUE", "ON_FALSE", "ON_BRANCH", "ON_CLICK", "CALLS_SUBFLOW", "CONTAINS", "HAS_STYLE", "HAS_BEHAVIOR"]),
condition: z.string().optional(),
iterator: z.string().optional(),
branch: z.string().optional()
// ON_BRANCH 的具名分支(SDD workflow-discovery 3.11)
}))
});
executeSchema = z.object({
graph: graphSchema,
context: z.record(z.unknown()).default({})
});
}
});
// cypher-executor/src/actions/execution-evaluator.ts
function componentVerdictsFromTrace(nodes, trace) {
const componentByNodeId = /* @__PURE__ */ new Map();
for (const n of nodes) {
if (n.type === "Component" && n.componentId) componentByNodeId.set(n.id, n.componentId);
}
const verdicts = [];
for (const step of trace) {
const componentId = componentByNodeId.get(step.nodeId);
if (!componentId) continue;
const out = step.output;
const outputSaysFailed = typeof out === "object" && out !== null && !Array.isArray(out) && out.success === false;
verdicts.push({
component_id: componentId,
success: !step.error && !outputSaysFailed,
duration_ms: Math.max(0, Number(step.duration_ms) || 0)
});
}
return verdicts;
}
async function recordComponentStats(env, nodes, trace) {
try {
const base = (env.REGISTRY_BASE_URL ?? (env.WORKER_SUBDOMAIN ? wasmWorkerUrl("registry", env.WORKER_SUBDOMAIN) : void 0))?.replace(/\/$/, "");
if (!base) return;
const verdicts = componentVerdictsFromTrace(nodes, trace);
if (verdicts.length === 0) return;
await Promise.all(
verdicts.map(
(v) => fetch(`${base}/analytics/record`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
canonical_id: v.component_id,
success: v.success,
duration_ms: v.duration_ms
})
}).catch(() => void 0)
// 統計失敗不影響執行
)
);
} catch {
}
}
var init_execution_evaluator = __esm({
"cypher-executor/src/actions/execution-evaluator.ts"() {
"use strict";
init_component_loader();
}
});
// cypher-executor/src/actions/webhook-handlers.ts
var webhook_handlers_exports = {};
__export(webhook_handlers_exports, {
executeWebhookGraph: () => executeWebhookGraph,
generateToken: () => generateToken,
validateAndParseWebhook: () => validateAndParseWebhook
});
function recordRecipeStats(env, recipeKeys, ok, at, ctx) {
if (recipeKeys.size === 0) return;
const base = (env.KBDB_BASE_URL ?? "https://kbdb.finally.click").replace(/\/$/, "");
const headers = { "Content-Type": "application/json" };
if (env.KBDB_INTERNAL_TOKEN) headers["Authorization"] = `Bearer ${env.KBDB_INTERNAL_TOKEN}`;
const promise = Promise.all(
[...recipeKeys].map(
(key) => fetch(`${base}/recipe-stats/record`, {
method: "POST",
headers,
body: JSON.stringify({ canonical_id: key, ok, at })
}).catch(() => void 0)
)
).then(() => void 0);
if (ctx?.waitUntil) ctx.waitUntil(promise);
else ;
}
function generateToken() {
const tokenBytes = crypto.getRandomValues(new Uint8Array(16));
return Array.from(tokenBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
}
async function validateAndParseWebhook(raw2) {
try {
return JSON.parse(raw2);
} catch {
return null;
}
}
async function executeWebhookGraph(env, graph, triggerContext, token, apiKey, ctx, userAgent) {
const parsed = graphSchema.safeParse(graph);
if (!parsed.success) {
return { success: false, error: "\u5716\u5B9A\u7FA9\u5DF2\u5931\u6548", duration_ms: 0 };
}
const loader = createComponentLoader(env);
const executor = new GraphExecutor(loader, void 0, env, apiKey);
const start = Date.now();
try {
const result = await executor.execute(
parsed.data,
{ ...triggerContext, _webhook_token: token },
env.EXEC_CONTEXT
);
const duration_ms = Date.now() - start;
recordTelemetry(env, apiKey, {
event_type: "run_success",
workflow_name: token,
duration_ms,
agent_user_agent: userAgent
}, ctx);
recordRecipeStats(env, executor.usedRecipeKeys, true, Date.now(), ctx);
{
const statsPromise = recordComponentStats(
env,
parsed.data.nodes,
result.trace
);
if (ctx?.waitUntil) ctx.waitUntil(statsPromise);
else ;
}
return { success: true, data: result.data, duration_ms };
} catch (err) {
const duration_ms = Date.now() - start;
const errMsg = err instanceof Error ? err.message : String(err);
const isPaused = /workflow paused/i.test(errMsg);
recordTelemetry(env, apiKey, {
event_type: isPaused ? "run_success" : "run_fail",
workflow_name: token,
error_code: isPaused ? "paused_awaiting_resume" : "execution_error",
duration_ms,
agent_user_agent: userAgent
}, ctx);
if (!isPaused) {
recordRecipeStats(env, executor.usedRecipeKeys, false, Date.now(), ctx);
}
if (!isPaused && err instanceof ExecutionError) {
const statsPromise = recordComponentStats(
env,
parsed.data.nodes,
err.trace
);
if (ctx?.waitUntil) ctx.waitUntil(statsPromise);
else ;
}
if (err instanceof ExecutionError) {
const traceFormatted = err.trace.map((s) => ({
node: s.nodeId,
status: s.error ? "failed" : "success",
...s.error ? { error: s.error } : {}
}));
return {
success: false,
error: errMsg,
trace: traceFormatted,
duration_ms
};
}
return { success: false, error: errMsg, duration_ms };
}
}
var init_webhook_handlers = __esm({
"cypher-executor/src/actions/webhook-handlers.ts"() {
"use strict";
init_types();
init_graph_executor();
init_schemas();
init_component_loader();
init_telemetry();
init_execution_evaluator();
}
});
// cypher-executor/src/index.ts
init_dist();
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/middleware/cors/index.js
var cors = (options) => {
const defaults = {
origin: "*",
allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"],
allowHeaders: [],
exposeHeaders: []
};
const opts = {
...defaults,
...options
};
const findAllowOrigin = ((optsOrigin) => {
if (typeof optsOrigin === "string") {
if (optsOrigin === "*") {
if (opts.credentials) {
return (origin) => origin || null;
}
return () => optsOrigin;
} else {
return (origin) => optsOrigin === origin ? origin : null;
}
} else if (typeof optsOrigin === "function") {
return optsOrigin;
} else {
return (origin) => optsOrigin.includes(origin) ? origin : null;
}
})(opts.origin);
const findAllowMethods = ((optsAllowMethods) => {
if (typeof optsAllowMethods === "function") {
return optsAllowMethods;
} else if (Array.isArray(optsAllowMethods)) {
return () => optsAllowMethods;
} else {
return () => [];
}
})(opts.allowMethods);
return async function cors2(c, next) {
function set(key, value) {
c.res.headers.set(key, value);
}
const allowOrigin = await findAllowOrigin(c.req.header("origin") || "", c);
if (allowOrigin) {
set("Access-Control-Allow-Origin", allowOrigin);
}
if (opts.credentials) {
set("Access-Control-Allow-Credentials", "true");
}
if (opts.exposeHeaders?.length) {
set("Access-Control-Expose-Headers", opts.exposeHeaders.join(","));
}
if (c.req.method === "OPTIONS") {
if (opts.origin !== "*" || opts.credentials) {
set("Vary", "Origin");
}
if (opts.maxAge != null) {
set("Access-Control-Max-Age", opts.maxAge.toString());
}
const allowMethods = await findAllowMethods(c.req.header("origin") || "", c);
if (allowMethods.length) {
set("Access-Control-Allow-Methods", allowMethods.join(","));
}
let headers = opts.allowHeaders;
if (!headers?.length) {
const requestHeaders = c.req.header("Access-Control-Request-Headers");
if (requestHeaders) {
headers = requestHeaders.split(/\s*,\s*/);
}
}
if (headers?.length) {
set("Access-Control-Allow-Headers", headers.join(","));
c.res.headers.append("Vary", "Access-Control-Request-Headers");
}
c.res.headers.delete("Content-Length");
c.res.headers.delete("Content-Type");
return new Response(null, {
headers: c.res.headers,
status: 204,
statusText: "No Content"
});
}
await next();
if (opts.origin !== "*" || opts.credentials) {
c.header("Vary", "Origin", { append: true });
}
};
};
// cypher-executor/src/lib/cron-match.ts
function matchField(expr, value, min, max) {
if (expr === "*") return true;
for (const part of expr.split(",")) {
if (matchPart(part.trim(), value, min, max)) return true;
}
return false;
}
function matchPart(part, value, min, max) {
if (part.startsWith("*/")) {
const step = parseInt(part.slice(2), 10);
if (!Number.isFinite(step) || step <= 0) return false;
return (value - min) % step === 0;
}
if (part.includes("-")) {
const [rangePart, stepStr] = part.split("/");
const [aStr, bStr] = rangePart.split("-");
const a = parseInt(aStr, 10);
const b = parseInt(bStr, 10);
if (!Number.isFinite(a) || !Number.isFinite(b)) return false;
if (value < a || value > b) return false;
if (stepStr === void 0) return true;
const step = parseInt(stepStr, 10);
if (!Number.isFinite(step) || step <= 0) return false;
return (value - a) % step === 0;
}
const n = parseInt(part, 10);
if (!Number.isFinite(n)) return false;
if (n < min || n > max) return false;
return value === n;
}
function cronMatch(expr, date) {
const fields = expr.trim().split(/\s+/);
if (fields.length !== 5) return false;
const [m, h, dom, mon, dow] = fields;
return matchField(m, date.getUTCMinutes(), 0, 59) && matchField(h, date.getUTCHours(), 0, 23) && matchField(dom, date.getUTCDate(), 1, 31) && matchField(mon, date.getUTCMonth() + 1, 1, 12) && matchField(dow, date.getUTCDay(), 0, 6);
}
function extractCronExpr(graph) {
if (!graph || typeof graph !== "object") return null;
const nodes = graph.nodes;
if (!Array.isArray(nodes)) return null;
for (const node of nodes) {
if (node.componentId !== "cron") continue;
const expr = node.data?.cron_expr;
if (typeof expr === "string" && expr.trim()) return expr.trim();
}
return null;
}
// cypher-executor/src/lib/cron-index.ts
var CRON_INDEX_KEY = "cron-idx:_all";
function cronEntryKey(apiKey, name) {
return `${apiKey}:${name}`;
}
function parseCronEntryKey(entryKey) {
const idx = entryKey.indexOf(":");
if (idx <= 0) return null;
return { apiKey: entryKey.slice(0, idx), name: entryKey.slice(idx + 1) };
}
async function readCronIndex(kv) {
const raw2 = await kv.get(CRON_INDEX_KEY, "text");
if (!raw2) return {};
try {
const parsed = JSON.parse(raw2);
return parsed && typeof parsed === "object" ? parsed : {};
} catch {
return {};
}
}
async function updateCronIndexEntry(kv, apiKey, name, cronExpr) {
const index = await readCronIndex(kv);
const entryKey = cronEntryKey(apiKey, name);
if (cronExpr) {
if (index[entryKey] === cronExpr) return;
index[entryKey] = cronExpr;
} else {
if (!(entryKey in index)) return;
delete index[entryKey];
}
await kv.put(CRON_INDEX_KEY, JSON.stringify(index));
}
// cypher-executor/src/scheduled.ts
init_webhook_handlers();
init_kbdb_proxy();
async function handleScheduled(controller, env, ctx) {
const now2 = new Date(controller.scheduledTime);
console.log("[scheduled] tick", now2.toISOString(), "controller.cron=", controller.cron);
const index = await readCronIndex(env.WEBHOOKS);
const entries = Object.entries(index);
let triggered = 0;
for (const [entryKey, cronExpr] of entries) {
const parsed = parseCronEntryKey(entryKey);
if (!parsed) continue;
const { apiKey, name } = parsed;
if (!cronExpr) continue;
if (!cronMatch(cronExpr, now2)) continue;
const wfKey = `${apiKey}:wf:${name}`;
const wfRaw = await env.WEBHOOKS.get(wfKey, "text");
if (!wfRaw) {
console.warn("[scheduled] cron-idx \u5C0D\u61C9 workflow \u4E0D\u5B58\u5728", wfKey);
continue;
}
let record;
try {
record = JSON.parse(wfRaw);
} catch {
continue;
}
triggered++;
console.log("[scheduled] trigger", name, "apiKey=", apiKey.slice(0, 12) + "...", "cron=", cronExpr);
const triggerContext = {
api_key: apiKey,
_triggered_by: "cron",
_scheduled_at: now2.toISOString()
};
ctx.waitUntil(
executeWebhookGraph(env, record.graph, triggerContext, name, apiKey).then(
(r) => console.log("[scheduled] done", name, r.success, r.duration_ms + "ms"),
(e) => console.error("[scheduled] fail", name, e)
)
);
}
console.log(`[scheduled] scanned ${entries.length} cron-idx entries, ${triggered} triggered`);
if (now2.getUTCHours() === 2 && now2.getUTCMinutes() === 30) {
const { base, headers } = kbdbBase(env);
ctx.waitUntil(
fetch(`${base}/execution-log/cleanup`, { method: "POST", headers }).then(async (r) => {
const body = await r.json().catch(() => null);
console.log("[scheduled] execution-log cleanup", r.status, JSON.stringify(body));
}).catch((e) => console.error("[scheduled] execution-log cleanup failed", e))
);
}
}
// cypher-executor/src/routes/health.ts
init_dist();
// cypher-executor/src/lib/portal-auth-store.ts
init_credentials();
var AUTH_STORE_PREFIX = "ARCRUN_AUTH_STORE";
var SHARD_MAX_BYTES = 4600;
var AUTH_OVERLAY_TTL_MS = 18e4;
var ACCEL_KEY = "auth_store_recent";
var ACCEL_TTL_SECONDS = 600;
var AUTH_ID_PREFIX = "auth:";
var AuthStoreWriteError = class extends Error {
};
var overlay = null;
var overlayAt = 0;
function emptyStore() {
return { version: 1, console: null, users: [] };
}
function shardNames(env) {
const bag = env;
return Object.keys(bag).filter((k) => k === AUTH_STORE_PREFIX || /^ARCRUN_AUTH_STORE_\d+$/.test(k)).filter((k) => typeof bag[k] === "string" && bag[k].length > 0).sort((a, b) => shardIndex(a) - shardIndex(b));
}
function shardIndex(name) {
if (name === AUTH_STORE_PREFIX) return 0;
return Number.parseInt(name.slice(AUTH_STORE_PREFIX.length + 1), 10) || 0;
}
function shardNameOf(index) {
return index === 0 ? AUTH_STORE_PREFIX : `${AUTH_STORE_PREFIX}_${index}`;
}
function authStorePresent(env) {
return shardNames(env).length > 0 || overlay !== null && Date.now() - overlayAt < AUTH_OVERLAY_TTL_MS;
}
function authStoreWritable(env) {
return Boolean(env.CF_SECRETS_API_TOKEN && env.CF_ACCOUNT_ID);
}
function readAuthStore(env) {
if (overlay && Date.now() - overlayAt < AUTH_OVERLAY_TTL_MS) return overlay;
return readAuthStoreFromEnv(env);
}
function readAuthStoreFromEnv(env) {
const bag = env;
const out = emptyStore();
for (const name of shardNames(env)) {
let parsed = null;
try {
parsed = JSON.parse(bag[name]);
} catch {
continue;
}
if (!parsed || typeof parsed !== "object") continue;
if (parsed.console && !out.console) out.console = parsed.console;
if (Array.isArray(parsed.users)) {
for (const u of parsed.users) {
if (u && typeof u.email === "string" && typeof u.id === "string") out.users.push(u);
}
}
}
return out;
}
function findAuthUserByEmail(env, email) {
const needle = email.trim().toLowerCase();
return readAuthStore(env).users.find((u) => u.email.toLowerCase() === needle) ?? null;
}
function findAuthUserById(env, id) {
return readAuthStore(env).users.find((u) => u.id === id) ?? null;
}
function isAuthStoreId(recordId) {
return recordId.startsWith(AUTH_ID_PREFIX);
}
function newAuthUserId() {
const arr = new Uint8Array(12);
crypto.getRandomValues(arr);
return AUTH_ID_PREFIX + Array.from(arr).map((b) => b.toString(16).padStart(2, "0")).join("");
}
async function writeAuthStore(env, data) {
if (!authStoreWritable(env)) {
throw new AuthStoreWriteError(
"\u9019\u53F0\u5BE6\u4F8B\u9084\u4E0D\u80FD\u5BEB\u5165\u8A8D\u8B49\u5132\u5B58\uFF08\u7F3A CF_SECRETS_API_TOKEN / CF_ACCOUNT_ID\uFF09\u3002\u8A8D\u8B49\u5206\u96E2\u9700\u8981\u9019\u5169\u9805\u624D\u5BEB\u5F97\u9032 Workers Secrets\u2014\u2014\u8ACB\u91CD\u65B0\u57F7\u884C\u5B89\u88DD\uFF0F\u66F4\u65B0\u8B93\u5B83\u5C31\u7DD2\u3002"
);
}
const shards = [];
let current = { v: 1, console: data.console ?? null, users: [] };
for (const u of data.users) {
const trial = { ...current, users: [...current.users ?? [], u] };
const size = new TextEncoder().encode(JSON.stringify(trial)).length;
if (size > SHARD_MAX_BYTES && (current.users ?? []).length > 0) {
shards.push(JSON.stringify(current));
current = { v: 1, users: [u] };
} else {
current = trial;
}
}
shards.push(JSON.stringify(current));
for (const s of shards) {
if (new TextEncoder().encode(s).length > 5e3) {
throw new AuthStoreWriteError("\u55AE\u7B46\u8A8D\u8B49\u8CC7\u6599\u8D85\u904E Cloudflare \u8B8A\u6578 5 KB \u4E0A\u9650\uFF0C\u7121\u6CD5\u5BEB\u5165\u3002");
}
}
const existing = shardNames(env);
for (let i = 0; i < shards.length; i++) {
await putWorkerSecret(env, shardNameOf(i), shards[i]);
}
for (const name of existing) {
if (shardIndex(name) >= shards.length) await deleteWorkerSecret(env, name);
}
overlay = { version: 1, console: data.console ?? null, users: [...data.users] };
overlayAt = Date.now();
try {
await env.SESSIONS_KV.put(
ACCEL_KEY,
JSON.stringify({ written_at: Date.now(), data: overlay }),
{ expirationTtl: ACCEL_TTL_SECONDS }
);
} catch {
}
}
async function hydrateFromAccelerator(env) {
let raw2 = null;
try {
raw2 = await env.SESSIONS_KV.get(ACCEL_KEY);
} catch {
return false;
}
if (!raw2) return false;
try {
const parsed = JSON.parse(raw2);
if (!parsed?.data || !Array.isArray(parsed.data.users)) return false;
if (overlay && overlayAt >= (parsed.written_at ?? 0)) return false;
overlay = { version: 1, console: parsed.data.console ?? null, users: parsed.data.users };
overlayAt = parsed.written_at ?? Date.now();
return true;
} catch {
return false;
}
}
async function authStoreRecentlyWritten(env) {
try {
return Boolean(await env.SESSIONS_KV.get(ACCEL_KEY));
} catch {
return false;
}
}
function unionStores(a, b) {
const byId = /* @__PURE__ */ new Map();
for (const u of [...a.users, ...b.users]) {
const prev = byId.get(u.id);
if (!prev || (u.updated_at ?? "") >= (prev.updated_at ?? "")) byId.set(u.id, u);
}
return { version: 1, console: a.console ?? b.console ?? null, users: [...byId.values()] };
}
async function mutateAuthStore(env, fn) {
await hydrateFromAccelerator(env);
const next = unionStores(readAuthStore(env), readAuthStoreFromEnv(env));
await fn(next);
await writeAuthStore(env, next);
return next;
}
function authStoreStatus(env) {
const data = readAuthStore(env);
return {
present: authStorePresent(env),
writable: authStoreWritable(env),
users: data.users.length,
console_configured: Boolean(data.console),
shards: shardNames(env).length
};
}
// cypher-executor/src/routes/health.ts
var healthRouter = new Hono2();
healthRouter.get("/health", (c) => {
const bundleVersion = c.env.ARCRUN_BUNDLE_VERSION;
return c.json({
ok: true,
...bundleVersion ? { bundle_version: bundleVersion } : {},
auth_store: authStoreStatus(c.env),
// arcrun-rag#38/#69/#25(2026-08-11):安裝器判斷「要不要重推」只比 bundle_version——
// 但這次要修的洞是「installer 從沒注入過 PORTAL_MAIL_RELAY_BASE」,跟 bundle 內容
// 版本無關(同一個 cypher 版本,有的實例有這個 var、有的沒有)。純比版本號的話,
// 已經在最新版的實例(如 leo 自己那台)永遠不會因為「按更新」而重推,這個 var
// 就永遠補不進去。只回布林(有沒有設,不回值本身)——不洩漏郵差網址。
mail_relay_configured: Boolean(String(c.env.PORTAL_MAIL_RELAY_BASE ?? "").trim())
});
});
healthRouter.get(
"/",
(c) => c.json({
service: "arcrun-cypher-executor",
version: "1.0.0",
status: "ok"
})
);
// cypher-executor/src/routes/execute.ts
init_dist();
init_types();
init_graph_executor();
init_schemas();
init_component_loader();
// cypher-executor/src/actions/execution-logger.ts
init_kbdb_proxy();
function extractTarget(input) {
if (!input) return void 0;
const raw2 = input.page_name ?? input.path;
if (raw2 === void 0 || raw2 === null) return void 0;
return typeof raw2 === "string" ? raw2 : JSON.stringify(raw2);
}
async function writeExecutionVerdict(env, workflowId, nodes, verdict, durationMs, message, input, apiKey) {
try {
const { base, headers } = kbdbBase(env);
await fetch(`${base}/execution-log/record`, {
method: "POST",
headers,
body: JSON.stringify({
workflow_id: workflowId,
owner_id: apiKey ?? null,
verdict,
duration_ms: Math.max(0, Math.round(durationMs)),
message: message ?? "",
target: extractTarget(input) ?? null
})
});
} catch {
}
}
// cypher-executor/src/routes/execute.ts
var executeRouter = new Hono2();
executeRouter.post("/execute", async (c) => {
const body = await c.req.json();
const parsed = executeSchema.safeParse(body);
if (!parsed.success) {
return c.json({ error: "\u5716\u5B9A\u7FA9\u9A57\u8B49\u5931\u6557", details: parsed.error.issues }, 400);
}
const { graph, context } = parsed.data;
const apiKey = c.req.header("x-arcrun-api-key") ?? void 0;
const loader = createComponentLoader(c.env);
const executor = new GraphExecutor(loader, void 0, c.env, apiKey);
const start = Date.now();
try {
const result = await executor.execute(graph, context, c.env.EXEC_CONTEXT);
const duration_ms = Date.now() - start;
c.executionCtx.waitUntil(
writeExecutionVerdict(c.env, graph.id, graph.nodes, "success", duration_ms, "\u57F7\u884C\u5B8C\u6210", context, apiKey)
);
return c.json({ success: true, data: result.data, trace: result.trace, duration_ms });
} catch (err) {
const duration_ms = Date.now() - start;
const errMsg = err instanceof Error ? err.message : String(err);
c.executionCtx.waitUntil(
writeExecutionVerdict(c.env, graph.id, graph.nodes, "failed", duration_ms, errMsg.slice(0, 100), context, apiKey)
);
if (err instanceof ExecutionError) {
const traceFormatted = err.trace.map((s) => ({
node: s.nodeId,
status: s.error ? "failed" : "success",
...s.error ? { error: s.error } : {}
}));
return c.json({
success: false,
error: errMsg,
failed_node: err.failed_node,
failed_input: err.failed_input,
trace: traceFormatted,
duration_ms
}, 500);
}
return c.json({ success: false, error: errMsg, failed_node: null, trace: [], duration_ms }, 500);
}
});
// cypher-executor/src/routes/cypher.ts
init_dist();
// cypher-executor/src/actions/cypher-handlers.ts
init_types();
init_graph_executor();
init_schemas();
init_component_loader();
init_execution_evaluator();
// cypher-executor/src/actions/triplet-parser.ts
init_constants3();
function parseTriplets(rawTriplets) {
const edges = [];
const nodeNames = /* @__PURE__ */ new Set();
const fromSet = /* @__PURE__ */ new Set();
const toSet = /* @__PURE__ */ new Set();
for (const line of rawTriplets) {
if (typeof line !== "string") continue;
const parts = line.split(">>").map((s) => s.trim());
if (parts.length !== 3) continue;
const [from, action, to] = parts;
edges.push({ from, to, label: action });
nodeNames.add(from);
nodeNames.add(to);
fromSet.add(from);
toSet.add(to);
}
if (nodeNames.size === 0) return null;
const sourceNodes = new Set([...fromSet].filter((n) => !toSet.has(n)));
const sinkNodes = new Set([...toSet].filter((n) => !fromSet.has(n)));
return { edges, nodeNames, sourceNodes, sinkNodes };
}
var INPUT_NAMES = /* @__PURE__ */ new Set(["input", "trigger", "webhook", "start"]);
var OUTPUT_NAMES = /* @__PURE__ */ new Set(["output", "result", "end", "done"]);
function isVirtualIoName(name) {
const lower = name.toLowerCase();
return INPUT_NAMES.has(lower) || OUTPUT_NAMES.has(lower);
}
function resolveNodeRole(name, parsed) {
if (INPUT_NAMES.has(name.toLowerCase())) return "Input";
if (OUTPUT_NAMES.has(name.toLowerCase())) return "Output";
if (parsed.sourceNodes.has(name)) return "Input";
return "Component";
}
function toEdgeType(label) {
const upper = label.toUpperCase();
if (VALID_EDGE_TYPES.has(upper)) return upper;
return SEMANTIC_EDGE_MAP[label] ?? SEMANTIC_EDGE_MAP[upper] ?? "PIPE";
}
// cypher-executor/src/actions/search-nodes.ts
init_component_loader();
init_recipes();
// cypher-executor/src/lib/branch-hints.ts
var BRANCH_HINTS = {
if_control: {
branch_field: "data.branch",
branches: ["true", "false"],
edge_types: ["ON_TRUE", "ON_FALSE"],
usage: '\u9019\u9846\u7B97\u5B8C\u6703\u8F38\u51FA data.branch\uFF08"true"\uFF0F"false"\uFF09\u3002\u4E0B\u6E38\u63A5\u5169\u689D\u908A\uFF1AON_TRUE \u63A5\u689D\u4EF6\u6210\u7ACB\u8981\u505A\u7684\u4E8B\uFF0CON_FALSE \u63A5\u4E0D\u6210\u7ACB\u8981\u505A\u7684\u4E8B\u3002**\u4E0D\u9700\u8981\u81EA\u5DF1\u5BEB code \u5224\u65B7\u8D70\u54EA\u689D**\u2014\u2014\u5F15\u64CE\u4F9D branch \u81EA\u52D5\u9078\u8DEF\u3002',
example: "\u5224\u65B7\u6709\u6C92\u6709\u65B0\u8CC7\u6599 >> ON_TRUE >> \u50B3\u5230 telegram\n\u5224\u65B7\u6709\u6C92\u6709\u65B0\u8CC7\u6599 >> ON_FALSE >> \u7D50\u675F\n\uFF08\u4E2D\u6587\u8A9E\u610F\u8A5E\u4EA6\u53EF\uFF1A\u300C\u6210\u7ACB\u6642\u300D\uFF1DON_TRUE\u3001\u300C\u5426\u5247\u300D\uFF1DON_FALSE\uFF09"
},
switch: {
branch_field: "data.branch",
branches: "\u7531 input_schema.cases[].branch \u8207 default_branch \u6C7A\u5B9A\uFF08N \u8DEF\uFF0C\u975E\u56FA\u5B9A\u6E05\u55AE\uFF09",
edge_types: ["ON_BRANCH"],
usage: "\u9019\u9846\u4F9D value \u6BD4\u5C0D cases\uFF0C\u8F38\u51FA data.branch\uFF1D\u547D\u4E2D\u90A3\u500B case \u7684 branch \u540D\uFF08\u90FD\u6C92\u4E2D\u5247\u662F default_branch\uFF09\u3002\u4E0B\u6E38**\u6BCF\u689D\u8DEF\u5404\u63A5\u4E00\u689D ON_BRANCH \u908A\uFF0C\u4E26\u5728\u908A\u4E0A\u6A19 branch \u7B49\u65BC\u4F60\u5728 cases \u88E1\u53D6\u7684\u540D\u5B57**\u3002default_branch \u4E0D\u9700\u8981\u7279\u5225\u7684\u908A\u578B\uFF0C\u7167\u6A23\u7528 ON_BRANCH \u6A19\u5B83\u7684\u540D\u5B57\u5373\u53EF\u3002",
example: '{"cases":[{"match":"active","branch":"branch_active"}],"default_branch":"branch_default"}\nedges: [\n {"from":"my_switch","to":"\u8655\u7406\u555F\u7528","type":"ON_BRANCH","branch":"branch_active"},\n {"from":"my_switch","to":"\u8655\u7406\u5176\u4ED6","type":"ON_BRANCH","branch":"branch_default"}\n]'
},
try_catch: {
branch_field: "data.branch",
branches: ["try", "catch"],
edge_types: ["ON_BRANCH"],
usage: '\u9019\u9846\u770B\u4E0A\u6E38 error \u662F\u5426\u975E\u7A7A\uFF0C\u8F38\u51FA data.branch\uFF08"try"\uFF1D\u6C92\u932F\uFF0F"catch"\uFF1D\u6709\u932F\uFF09\u3002\u4E0B\u6E38\u63A5\u5169\u689D ON_BRANCH \u908A\uFF0Cbranch \u5206\u5225\u6A19 "try" \u8207 "catch"\u3002**\u932F\u8AA4\u8655\u7406\u4E0D\u9700\u8981\u5BEB code**\u2014\u2014\u628A\u8981\u88DC\u6551\u7684\u7BC0\u9EDE\u63A5\u5728 catch \u90A3\u689D\u908A\u5F8C\u9762\u5373\u53EF\u3002',
example: 'edges: [\n {"from":"my_try_catch","to":"\u6B63\u5E38\u6D41\u7A0B","type":"ON_BRANCH","branch":"try"},\n {"from":"my_try_catch","to":"\u88DC\u6551\u6D41\u7A0B","type":"ON_BRANCH","branch":"catch"}\n]'
}
};
function branchHintFor(componentId) {
if (!componentId) return void 0;
return BRANCH_HINTS[componentId.toLowerCase()];
}
// cypher-executor/src/actions/search-nodes.ts
async function searchNodes(parsed, config, env, mode = "discover", target) {
const nodeResults = {};
const missingNodes = [];
if (mode === "compile") {
for (const nodeName of parsed.nodeNames) {
const role = resolveNodeRole(nodeName, parsed);
if ((role === "Input" || role === "Output") && isVirtualIoName(nodeName)) {
nodeResults[nodeName] = { status: "found", componentId: nodeName.toLowerCase(), type: role };
continue;
}
const configComponent = config?.[nodeName]?.component;
nodeResults[nodeName] = {
status: configComponent ? "found" : "unchecked",
componentId: configComponent ?? nodeName,
type: role
};
}
return { nodeResults, missingNodes };
}
const sub = env?.WORKER_SUBDOMAIN;
const registryBase = env?.REGISTRY_BASE_URL ?? (sub ? wasmWorkerUrl("registry", sub) : void 0);
const wantComponents = target !== "recipe";
const wantRecipes = target !== "component";
const catalog = !wantComponents ? { status: "ok", entries: [] } : registryBase ? await fetchCatalog(registryBase) : { status: "unreachable", entries: [] };
const recipes = wantRecipes && env?.RECIPES ? await listAllRecipes2(env.RECIPES) : [];
const byId = /* @__PURE__ */ new Map();
for (const e of catalog.entries) {
const prev = byId.get(e.canonical_id);
if (!prev || (e.score ?? 0) > (prev.score ?? 0)) byId.set(e.canonical_id, e);
for (const a of e.aliases ?? []) if (!byId.has(a)) byId.set(a, e);
}
for (const nodeName of parsed.nodeNames) {
const role = resolveNodeRole(nodeName, parsed);
if ((role === "Input" || role === "Output") && isVirtualIoName(nodeName)) {
nodeResults[nodeName] = { status: "found", componentId: nodeName.toLowerCase(), type: role };
continue;
}
const configComponent = config?.[nodeName]?.component;
const componentId = configComponent ?? nodeName;
if (configComponent) {
nodeResults[nodeName] = { status: "found", componentId, type: role };
continue;
}
if (catalog.status === "unreachable") {
nodeResults[nodeName] = { status: "unknown", componentId, type: role };
continue;
}
if (catalog.status === "no_endpoint") {
const legacy = await legacyPerNodeLookup(registryBase, componentId, nodeName, role, env, recipes);
nodeResults[nodeName] = legacy.info;
if (legacy.missing) missingNodes.push(nodeName);
continue;
}
const hit = byId.get(componentId);
if (hit) {
nodeResults[nodeName] = {
status: "found",
componentId,
type: role,
source: "component",
input_schema: hit.input_schema,
success_rate: typeof hit.success_rate === "number" ? hit.success_rate : void 0,
stability: typeof hit.stability === "string" ? hit.stability : void 0,
branch_hint: branchHintFor(componentId)
};
continue;
}
const recipe = recipes.find((r) => r.canonical_id === componentId);
if (recipe) {
nodeResults[nodeName] = {
status: "found",
componentId: recipe.canonical_id,
type: role,
source: "recipe",
description: recipe.description,
endpoint: recipe.endpoint,
payload_hint: buildPayloadHint(recipe)
};
continue;
}
const substituted = trySubstitution(nodeName, catalog.entries, recipes);
if (substituted) {
nodeResults[nodeName] = { ...substituted, type: role };
continue;
}
const similarComponents = similarFromCatalog(catalog.entries, nodeName);
const similarRecipes = similarFromRecipes(recipes, nodeName);
nodeResults[nodeName] = {
status: "not_found",
componentId,
type: role,
suggestion: buildSuggestion(componentId),
...similarComponents.length > 0 ? { similar_components: similarComponents } : {},
...similarRecipes.length > 0 ? { similar_recipes: similarRecipes } : {}
};
missingNodes.push(nodeName);
}
return { nodeResults, missingNodes };
}
async function fetchCatalog(registryBase) {
try {
const res = await fetch(`${registryBase}/components/catalog`, { signal: AbortSignal.timeout(1e4) });
if (res.status === 404) return { status: "no_endpoint", entries: [] };
if (!res.ok) return { status: "unreachable", entries: [] };
const body = await res.json();
return { status: "ok", entries: body.data?.components ?? [] };
} catch {
return { status: "unreachable", entries: [] };
}
}
async function listAllRecipes2(kv) {
try {
const list = await kv.list({ prefix: "recipe:" });
return (await Promise.all(
list.keys.map((k) => kv.get(k.name, "json"))
)).filter(Boolean);
} catch {
return [];
}
}
function similarFromCatalog(entries, nodeName) {
const searchableOf = (e) => [e.canonical_id, e.display_name ?? "", e.description ?? "", ...e.aliases ?? [], ...e.tags ?? []].join(" ").toLowerCase();
const full = nodeName.toLowerCase();
const direct = entries.filter((e) => searchableOf(e).includes(full)).map((e) => e.canonical_id);
if (direct.length > 0) return [...new Set(direct)].slice(0, 3);
const tokens = extractTokens(nodeName);
if (tokens.length === 0) return [];
const count = /* @__PURE__ */ new Map();
for (const e of entries) {
const hay = searchableOf(e);
const hits = tokens.filter((t) => hay.includes(t)).length;
if (hits > 0) count.set(e.canonical_id, Math.max(count.get(e.canonical_id) ?? 0, hits));
}
return [...count.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([id]) => id);
}
function similarFromRecipes(recipes, nodeName) {
const tokens = [nodeName.toLowerCase(), ...extractTokens(nodeName)];
const seen = /* @__PURE__ */ new Set();
const matched = [];
for (const r of recipes) {
if (seen.has(r.canonical_id)) continue;
const hay = `${r.canonical_id} ${r.display_name ?? ""} ${r.description ?? ""}`.toLowerCase();
if (tokens.some((t) => hay.includes(t))) {
seen.add(r.canonical_id);
matched.push(r.canonical_id);
}
}
return matched.slice(0, 3);
}
async function legacyPerNodeLookup(registryBase, componentId, nodeName, role, env, recipes) {
const q = await fetchComponent(registryBase, componentId);
if (!q.ok) return { info: { status: "unknown", componentId, type: role }, missing: false };
if (q.entry) {
return {
info: {
status: "found",
componentId,
type: role,
source: "component",
input_schema: q.entry.input_schema,
success_rate: q.entry.success_rate,
stability: q.entry.stability,
branch_hint: branchHintFor(componentId)
},
missing: false
};
}
const recipe = recipes.find((r) => r.canonical_id === componentId) ?? (env?.RECIPES ? await resolveRecipe(componentId, env.RECIPES) : null);
if (recipe) {
return {
info: {
status: "found",
componentId: recipe.canonical_id,
type: role,
source: "recipe",
description: recipe.description,
endpoint: recipe.endpoint,
payload_hint: buildPayloadHint(recipe)
},
missing: false
};
}
const similarComponents = await searchSimilarComponents(registryBase, nodeName);
const similarRecipes = similarFromRecipes(recipes, nodeName);
return {
info: {
status: "not_found",
componentId,
type: role,
suggestion: buildSuggestion(componentId),
...similarComponents.length > 0 ? { similar_components: similarComponents } : {},
...similarRecipes.length > 0 ? { similar_recipes: similarRecipes } : {}
},
missing: true
};
}
function trySubstitution(nodeName, catalogEntries, recipes) {
const lower = nodeName.toLowerCase();
const serviceHits = SERVICE_HINTS.filter((w) => lower.includes(w));
if (serviceHits.length > 0) {
const matched = /* @__PURE__ */ new Map();
for (const r of recipes) {
const hay = `${r.canonical_id} ${r.display_name ?? ""} ${r.description ?? ""}`.toLowerCase();
if (serviceHits.every((h) => hay.includes(h))) matched.set(r.canonical_id, r);
}
if (matched.size !== 1) return null;
const recipe = [...matched.values()][0];
return {
status: "resolved",
componentId: recipe.canonical_id,
source: "recipe",
description: recipe.description,
endpoint: recipe.endpoint,
substitution: {
from: nodeName,
componentId: "http_request",
// recipe=http_request+參數模板的具名封裝
recipe: recipe.canonical_id,
reason: `\u670D\u52D9\u8A5E\u300C${serviceHits.join("\u3001")}\u300D\u552F\u4E00\u547D\u4E2D recipe\u300C${recipe.canonical_id}\u300D\uFF1Bworkflow config \u5BEB component: ${recipe.canonical_id}\uFF08\u5E95\u5C64\u96F6\u4EF6\uFF1Dhttp_request\uFF09\uFF0C\u53EA\u9700\u586B payload`
}
};
}
const tokens = extractTokens(nodeName);
if (tokens.length === 0) return null;
const byCanonical = /* @__PURE__ */ new Map();
for (const e of catalogEntries) {
const strongHay = [e.canonical_id, e.display_name ?? "", ...e.aliases ?? []].join(" ").toLowerCase();
const weakHay = [e.description ?? "", ...e.tags ?? []].join(" ").toLowerCase();
const strongHits = tokens.filter((t) => strongHay.includes(t));
const weakCount = tokens.filter((t) => weakHay.includes(t)).length;
const score = strongHits.length * 10 + weakCount;
if (score === 0) continue;
const prev = byCanonical.get(e.canonical_id);
if (!prev || score > prev.score) byCanonical.set(e.canonical_id, { entry: e, score, strongHits });
}
const ranked = [...byCanonical.values()].sort((a, b) => b.score - a.score);
const top = ranked[0];
if (!top || top.strongHits.length === 0) return null;
if (ranked[1] && ranked[1].score >= top.score) return null;
return {
status: "resolved",
componentId: top.entry.canonical_id,
source: "component",
input_schema: top.entry.input_schema,
success_rate: typeof top.entry.success_rate === "number" ? top.entry.success_rate : void 0,
stability: typeof top.entry.stability === "string" ? top.entry.stability : void 0,
// 替換成分岔零件時(例「判斷有沒有新資料」→ if_control)一併附分支用法,
// 否則 AI 換到零件卻不知道怎麼接兩條路,仍會退回寫 code。
branch_hint: branchHintFor(top.entry.canonical_id),
substitution: {
from: nodeName,
componentId: top.entry.canonical_id,
reason: `\u65B7\u8A5E\u300C${top.strongHits.join("\u3001")}\u300D\u547D\u4E2D\u96F6\u4EF6\u300C${top.entry.canonical_id}\u300D\uFF08${top.entry.display_name ?? ""}\uFF09\u5F37\u6B04\u4F4D\u4E14\u5206\u6578\u552F\u4E00\u6700\u9AD8\uFF1B\u53EA\u9700\u7167 input_schema \u586B payload`
}
};
}
var SERVICE_HINTS = [
"google",
"gmail",
"sheets",
"slides",
"gdocs",
"drive",
"calendar",
"youtube",
"slack",
"telegram",
"discord",
"line",
"whatsapp",
"twilio",
"notion",
"airtable",
"trello",
"jira",
"asana",
"linear",
"github",
"gitea",
"gitlab",
"bitbucket",
"stripe",
"paypal",
"shopify",
"hubspot",
"salesforce",
"openai",
"anthropic",
"claude",
"gemini",
"groq",
"twitter",
"facebook",
"instagram",
"linkedin",
"dropbox",
"zoom",
"sendgrid",
"mailgun",
"kbdb"
];
var COMPUTE_HINTS = [
"encrypt",
"decrypt",
"cipher",
"aes",
"rsa",
"sha",
"md5",
"hmac",
"hash",
"sign",
"verify",
"encode",
"decode",
"base64",
"hex",
"compress",
"decompress",
"zip",
"gzip",
"uuid",
"random",
"regex",
"math",
"calc",
"sort",
"dedup",
"diff",
"template",
"render",
"convert",
"transform",
"parse",
"format",
"csv",
"xml"
];
function buildSuggestion(componentId) {
const lower = componentId.toLowerCase();
const serviceHit = SERVICE_HINTS.find((w) => lower.includes(w));
const computeHit = COMPUTE_HINTS.find((w) => lower.includes(w));
if (serviceHit) {
return `\u5169\u5EAB\u90FD\u67E5\u904E\uFF0C\u96F6\u4EF6 registry \u8207 recipe \u5EAB\u7686\u7121\u300C${componentId}\u300D\u3002\u540D\u5B57\u542B\u670D\u52D9\u8A5E\u300C${serviceHit}\u300D\uFF1D\u5916\u90E8 API \u6A23\u8C8C \u2192 \u6C92\u6709\u6B64 recipe\uFF0C\u53EF\u81EA\u5DF1\u5BEB\uFF1A\u5BEB\u6CD5\u770B skill\u300Cwrite_recipe\u300D\uFF08arcrun_get_skill('write_recipe')\uFF09\uFF0C\u5BEB\u597D\u7528 acr recipe push \u6216 POST /recipes \u88DD\u4E0A\u5373\u53EF\u7528\uFF0C\u4E0D\u7528\u6539\u5E73\u53F0\u3002`;
}
if (computeHit) {
return `\u5169\u5EAB\u90FD\u67E5\u904E\uFF0C\u96F6\u4EF6 registry \u8207 recipe \u5EAB\u7686\u7121\u300C${componentId}\u300D\u3002\u540D\u5B57\u542B\u8A08\u7B97\u8A5E\u300C${computeHit}\u300D\uFF1D\u8A08\u7B97\u539F\u8A9E\u6A23\u8C8C \u2192 \u6C92\u6709\u6B64\u96F6\u4EF6\uFF0C\u53EF\u6295\u7A3F PR \u65B0\u589E WASM component\uFF1A\u505A\u6CD5\u770B skill\u300Cadd_new_wasm_component\u300D\uFF08arcrun_get_skill('add_new_wasm_component')\uFF09\u3002`;
}
return `\u5169\u5EAB\u90FD\u67E5\u904E\uFF0C\u96F6\u4EF6 registry \u8207 recipe \u5EAB\u7686\u7121\u300C${componentId}\u300D\uFF0C\u4E14\u540D\u5B57\u5224\u4E0D\u51FA\u578B\u3002\u7F3A\u5916\u90E8 API \u2192 \u81EA\u5DF1\u5BEB recipe\uFF08skill\u300Cwrite_recipe\u300D\uFF09\uFF1B\u7F3A\u8A08\u7B97\u80FD\u529B \u2192 \u6295\u7A3F\u96F6\u4EF6 PR\uFF08skill\u300Cadd_new_wasm_component\u300D\uFF0Ccomponent \u9032 WASM \u6C99\u7BB1\uFF09\u3002`;
}
function buildPayloadHint(recipe) {
const parts = [];
if (recipe.body_template) {
parts.push("payload \u5DF2\u6536\u5728 recipe \u7684 body_template \u88E1\uFF0C\u4F60\u53EA\u8981\u628A {{\u8B8A\u6578}} \u5C0D\u61C9\u7684\u503C\u653E\u9032\u7BC0\u9EDE context");
} else if (recipe.body) {
parts.push("payload \u5F62\u72C0\u898B body \u6B04\u4F4D\uFF08{{\u8B8A\u6578}} \u7531\u7BC0\u9EDE context \u586B\uFF09");
} else {
parts.push("\u672A\u5B9A\u7FA9 body_template\uFF1A\u7BC0\u9EDE context \u6703\u6574\u5305\u7576 body \u9001\u51FA\uFF08_ \u958B\u982D\u7684\u5167\u90E8\u6B04\u4F4D\u6703\u88AB\u5254\u9664\uFF09");
}
if (recipe.response_map) {
parts.push("\u56DE\u61C9\u5DF2\u6B63\u898F\u5316\uFF1A\u57F7\u884C\u7D50\u679C\u9664\u4E86\u539F\u59CB data\uFF0C\u53E6\u9644 text\uFF08\u53D6\u503C\u8DEF\u5F91\u7B49\u898F\u5247\u5BEB\u5728 recipe \u88E1\uFF0C\u63DB\u6E90\u4E0D\u5FC5\u6539 workflow\uFF09");
} else {
parts.push("\u672A\u5B9A\u7FA9 response_map\uFF1A\u56DE\u61C9\u539F\u6A23\u653E\u5728 data\uFF0C\u53D6\u503C\u8981\u81EA\u5DF1\u6307\u8DEF\u5F91");
}
if (recipe.auth === "binding") {
parts.push(`\u8A8D\u8B49\uFF1Dbinding\uFF08\u514D\u91D1\u9470\uFF0C\u7528\u5E73\u53F0\u5167\u5EFA ${recipe.binding_name ?? "AI"}\uFF09`);
} else if (recipe.auth_service) {
parts.push(`\u8A8D\u8B49\u8D70 auth recipe\u300C${recipe.auth_service}\u300D\uFF08\u91D1\u9470\u7531\u7CFB\u7D71\u5728\u57F7\u884C\u524D\u6CE8\u5165\uFF0C\u4F60\u4E0D\u5FC5\u4E5F\u4E0D\u8A72\u586B\uFF09`);
}
return {
body_template: recipe.body_template,
response_map: recipe.response_map,
usage: parts.join("\uFF1B") + "\u3002"
};
}
async function fetchComponent(registryBase, id) {
try {
const res = await fetch(`${registryBase}/components/${encodeURIComponent(id)}`, {
signal: AbortSignal.timeout(5e3)
});
if (res.status === 404) return { ok: true };
if (!res.ok) return { ok: false };
const body = await res.json();
if (body.success === false) return { ok: true };
const d = body.data ?? body;
return {
ok: true,
entry: {
input_schema: d.input_schema,
success_rate: typeof d.success_rate === "number" ? d.success_rate : void 0,
stability: typeof d.stability === "string" ? d.stability : void 0
}
};
} catch {
return { ok: false };
}
}
function extractTokens(name) {
const tokens = [];
const ascii = name.toLowerCase().match(/[a-z0-9]{3,}/g) ?? [];
tokens.push(...ascii);
const cjkRuns = name.match(/[一-鿿]+/g) ?? [];
for (const run2 of cjkRuns) {
for (let i = 0; i + 2 <= run2.length; i++) tokens.push(run2.slice(i, i + 2));
}
return [...new Set(tokens)].slice(0, 8);
}
async function searchRegistryIds(registryBase, q) {
try {
const res = await fetch(`${registryBase}/components/search?q=${encodeURIComponent(q)}`, {
signal: AbortSignal.timeout(5e3)
});
if (!res.ok) return [];
const body = await res.json();
return (body.data?.results ?? []).map((r) => r.canonical_id).filter((s) => !!s);
} catch {
return [];
}
}
async function searchSimilarComponents(registryBase, nodeName) {
const direct = await searchRegistryIds(registryBase, nodeName);
if (direct.length > 0) return direct.slice(0, 3);
const tokens = extractTokens(nodeName);
if (tokens.length === 0) return [];
const hits = await Promise.all(tokens.map((t) => searchRegistryIds(registryBase, t)));
const count = /* @__PURE__ */ new Map();
for (const ids of hits) {
for (const id of ids) count.set(id, (count.get(id) ?? 0) + 1);
}
return [...count.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([id]) => id);
}
// cypher-executor/src/actions/graph-builder.ts
function buildExecutionGraph(parsed, nodeResults, graphId, graphName, config) {
const nodes = [...parsed.nodeNames].map((name) => {
const nr = nodeResults[name];
const id = name.toLowerCase().replace(/\s+/g, "-");
const nodeConfig = config?.[name] ?? {};
const componentId = nodeConfig.component ?? nr.componentId;
const { component: _component, ...staticParams } = nodeConfig;
const data = Object.keys(staticParams).length > 0 ? staticParams : void 0;
return { id, type: nr.type, componentId, label: name, data };
});
const edges = parsed.edges.map((e) => {
let iterator;
let label = e.label;
const foreachMatch = label.match(/^(?:對每個|FOREACH)\s+(\w+)$/i);
if (foreachMatch) {
iterator = foreachMatch[1];
label = "\u5C0D\u6BCF\u500B";
}
let branch;
const branchMatch = label.match(/^(?:ON_BRANCH|分支)\s*[((]\s*([\w-]+)\s*[))]$/i);
if (branchMatch) {
branch = branchMatch[1];
label = "ON_BRANCH";
}
const edge = {
from: e.from.toLowerCase().replace(/\s+/g, "-"),
to: e.to.toLowerCase().replace(/\s+/g, "-"),
type: toEdgeType(label)
};
if (iterator) edge.iterator = iterator;
if (branch) edge.branch = branch;
return edge;
});
return { id: graphId, name: graphName, nodes, edges };
}
// cypher-executor/src/actions/cypher-handlers.ts
async function handleCypherSearch(triplets, env, mode = "discover", target) {
const parsed = parseTriplets(triplets);
if (!parsed) {
throw new Error("\u7121\u6CD5\u89E3\u6790\u4EFB\u4F55\u7BC0\u9EDE");
}
const { nodeResults, missingNodes } = await searchNodes(parsed, void 0, env, mode, target);
const graph = buildExecutionGraph(parsed, nodeResults, "cypher-search-result", "Cypher Search Result");
return { nodes: nodeResults, cypher: { nodes: graph.nodes, edges: graph.edges }, missing: missingNodes };
}
async function handleCypherExecute(triplets, context, graphId, graphName, config, env, waitUntil, apiKey) {
const parsed = parseTriplets(triplets);
if (!parsed) {
throw new Error("\u7121\u6CD5\u89E3\u6790\u4EFB\u4F55\u7BC0\u9EDE");
}
const { nodeResults } = await searchNodes(parsed, config, env, "compile");
const graph = buildExecutionGraph(parsed, nodeResults, graphId, graphName, config);
const parseResult = graphSchema.safeParse(graph);
if (!parseResult.success) {
throw new Error("\u5716\u5B9A\u7FA9\u7522\u751F\u5931\u6557");
}
const loader = createComponentLoader(env);
const executor = new GraphExecutor(loader, void 0, env, apiKey);
const start = Date.now();
try {
const result = await executor.execute(parseResult.data, context ?? {}, env.EXEC_CONTEXT);
const duration_ms = Date.now() - start;
waitUntil(recordComponentStats(env, graph.nodes, result.trace));
return { success: true, data: result.data, trace: result.trace, duration_ms, graph };
} catch (err) {
const duration_ms = Date.now() - start;
if (err instanceof WorkflowPaused) {
return {
success: true,
paused: true,
task_id: err.task_id,
run_id: err.run_id,
paused_node_id: err.paused_node_id,
trace: err.trace_so_far,
duration_ms,
graph
};
}
const errMsg = err instanceof Error ? err.message : String(err);
if (err instanceof ExecutionError) {
waitUntil(recordComponentStats(env, graph.nodes, err.trace));
const traceFormatted = err.trace.map((s) => ({
node: s.nodeId,
status: s.error ? "failed" : "success",
...s.error ? { error: s.error } : {}
}));
throw new Error(JSON.stringify({
success: false,
error: errMsg,
failed_node: err.failed_node,
failed_input: err.failed_input,
trace: traceFormatted,
duration_ms
}));
}
throw err;
}
}
// cypher-executor/src/actions/target-search.ts
init_component_loader();
// cypher-executor/src/lib/workflow-search.ts
async function fetchTenantWorkflowSearch(env, apiKey, q, mode = "semantic") {
const base = (env.KBDB_BASE_URL ?? "https://arcrun-kbdb.uncle6-me.workers.dev").replace(/\/$/, "");
const headers = { "Content-Type": "application/json" };
if (env.KBDB_INTERNAL_TOKEN) headers["Authorization"] = `Bearer ${env.KBDB_INTERNAL_TOKEN}`;
const params = new URLSearchParams({
q,
owner_id: apiKey,
// 租戶隔離(只搜本租戶的 workflow)
entry_type: "workflow",
// base 通用 filter(Q4),只回 workflow entry
mode
});
return fetch(`${base}/entries/search?${params.toString()}`, { headers });
}
// cypher-executor/src/actions/target-search.ts
async function searchByTarget(target, query, env, apiKey) {
if (target === "component") {
const sub = env.WORKER_SUBDOMAIN;
const registryBase = env.REGISTRY_BASE_URL ?? (sub ? wasmWorkerUrl("registry", sub) : void 0);
if (!registryBase) return { ok: false, status: 502, error: "registry \u4F4D\u7F6E\u672A\u8A2D\u5B9A\uFF08WORKER_SUBDOMAIN\uFF0FREGISTRY_BASE_URL \u7686\u7F3A\uFF09" };
try {
const res2 = await fetch(
`${registryBase}/components/search?q=${encodeURIComponent(query)}`,
{ signal: AbortSignal.timeout(1e4) }
);
if (!res2.ok) return { ok: false, status: 502, error: `registry \u641C\u5C0B\u5931\u6557\uFF08HTTP ${res2.status}\uFF09` };
const body2 = await res2.json();
const results = (body2.data?.results ?? []).map((r) => {
if (!r || typeof r !== "object") return r;
const rec = r;
const hint = branchHintFor(typeof rec.canonical_id === "string" ? rec.canonical_id : void 0);
return hint ? { ...rec, branch_hint: hint } : rec;
});
return {
ok: true,
body: {
target,
query,
results,
count: body2.data?.count ?? 0
}
};
} catch (e) {
return { ok: false, status: 502, error: `registry \u67E5\u4E0D\u901A\uFF1A${e instanceof Error ? e.message : String(e)}` };
}
}
if (target === "recipe") {
if (!env.RECIPES) return { ok: false, status: 502, error: "RECIPES KV \u672A\u7D81\u5B9A" };
const all = await listAllRecipes2(env.RECIPES);
const q = query.toLowerCase();
const seen = /* @__PURE__ */ new Set();
const results = [];
for (const r of all) {
if (seen.has(r.canonical_id)) continue;
const hay = `${r.canonical_id} ${r.display_name ?? ""} ${r.description ?? ""}`.toLowerCase();
if (!hay.includes(q)) continue;
seen.add(r.canonical_id);
results.push({
canonical_id: r.canonical_id,
display_name: r.display_name,
description: r.description,
endpoint: r.endpoint,
// 3.12:逐顆查 recipe 時也要說得出「payload 怎麼填、回應怎麼取值」
payload_hint: buildPayloadHint(r)
});
}
return {
ok: true,
body: {
target,
query,
results,
count: results.length,
note: "\u641C\u7684\u662F\u672C\u90E8\u7F72\u79C1\u5EAB\uFF08workflow \u53EF\u76F4\u63A5 component: \u5F15\u7528\uFF09\u3002\u516C\u5EAB\uFF08\u591A\u4F5C\u8005\u5E02\u5834\uFF09\u8D70 MCP arcrun_recipe_search\uFF0FGET /public-recipes\u3002"
}
};
}
if (!apiKey) return { ok: false, status: 401, error: "target=workflow \u9700\u8981 X-Arcrun-API-Key header\uFF08workflow \u641C\u5C0B\u9650\u672C\u79DF\u6236\uFF09" };
const res = await fetchTenantWorkflowSearch(env, apiKey, query);
if (!res.ok) return { ok: false, status: 502, error: `workflow \u641C\u5C0B\u5931\u6557\uFF08KBDB HTTP ${res.status}\uFF09` };
const body = await res.json();
return { ok: true, body: { target, query, ...body } };
}
// cypher-executor/src/routes/cypher.ts
var cypherRouter = new Hono2();
var VALID_TARGETS = /* @__PURE__ */ new Set(["component", "recipe", "workflow"]);
cypherRouter.post("/cypher/search", async (c) => {
const body = await c.req.json();
const rawTriplets = body?.triplets;
const target = typeof body?.target === "string" ? body.target : void 0;
if (target !== void 0 && !VALID_TARGETS.has(target)) {
return c.json({ error: `target \u53EA\u63A5\u53D7 component\uFF0Frecipe\uFF0Fworkflow\uFF0C\u6536\u5230\u300C${target}\u300D` }, 400);
}
const query = typeof body?.query === "string" ? body.query.trim() : "";
if (query) {
if (!target) {
return c.json({ error: "\u7D66 query \u5FC5\u9808\u540C\u6642\u7D66 target\uFF08component\uFF0Frecipe\uFF0Fworkflow\uFF09\uFF0C\u6307\u660E\u8981\u641C\u54EA\u500B\u5EAB" }, 400);
}
const apiKey = c.req.header("X-Arcrun-API-Key") ?? void 0;
const r = await searchByTarget(target, query, c.env, apiKey);
if (!r.ok) return c.json({ error: r.error }, r.status);
return c.json(r.body);
}
if (!Array.isArray(rawTriplets) || rawTriplets.length === 0) {
return c.json({ error: "triplets \u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32\u9663\u5217\uFF08\u6216\u7D66 query + target \u505A\u540D\u5B57\u641C\u5C0B\uFF09" }, 400);
}
const mode = body?.mode === "compile" ? "compile" : "discover";
if (target && mode === "compile") {
return c.json({ error: "mode=compile\uFF08\u8907\u88FD\u8DEF\u5F91\uFF09\u4E0D\u67E5\u5EAB\uFF0C\u4E0D\u63A5\u53D7 target\uFF1B\u8981\u6307\u5B9A\u641C\u5C0B\u5C0D\u8C61\u8ACB\u7528 discover\uFF08\u9810\u8A2D\uFF09" }, 400);
}
if (target === "workflow") {
return c.json({ error: 'target=workflow \u662F\u540D\u5B57\u641C\u5C0B\uFF0C\u8ACB\u6539\u5E36 { target: "workflow", query: "..." }\uFF08\u4E0D\u5403 triplets\uFF09' }, 400);
}
try {
const now2 = /* @__PURE__ */ new Date();
const timestamp = now2.toISOString();
const versionId = `search-v1-${now2.getFullYear()}${String(now2.getMonth() + 1).padStart(2, "0")}${String(now2.getDate()).padStart(2, "0")}-${String(now2.getHours()).padStart(2, "0")}${String(now2.getMinutes()).padStart(2, "0")}${String(now2.getSeconds()).padStart(2, "0")}`;
const result = await handleCypherSearch(rawTriplets, c.env, mode, target);
const response = {
version: versionId,
timestamp,
triplets: rawTriplets,
nodes: result.nodes,
cypher: result.cypher,
missing: result.missing
};
return c.json(response);
} catch (err) {
const errMsg = err instanceof Error ? err.message : String(err);
return c.json({ error: errMsg }, 400);
}
});
cypherRouter.post("/cypher/execute", async (c) => {
const body = await c.req.json();
if (!Array.isArray(body?.triplets) || body.triplets.length === 0) {
return c.json({ error: "triplets \u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32\u9663\u5217" }, 400);
}
const graphId = typeof body.graph_id === "string" ? body.graph_id : `triplet-exec-${Date.now()}`;
const graphName = typeof body.graph_name === "string" ? body.graph_name : "Triplet Execution";
const now2 = /* @__PURE__ */ new Date();
const timestamp = now2.toISOString();
const versionId = `execute-v1-${now2.getFullYear()}${String(now2.getMonth() + 1).padStart(2, "0")}${String(now2.getDate()).padStart(2, "0")}-${String(now2.getHours()).padStart(2, "0")}${String(now2.getMinutes()).padStart(2, "0")}${String(now2.getSeconds()).padStart(2, "0")}`;
const apiKey = c.req.header("X-Arcrun-API-Key") ?? void 0;
try {
const result = await handleCypherExecute(
body.triplets,
body.context,
graphId,
graphName,
body.config,
c.env,
(p) => c.executionCtx.waitUntil(p),
apiKey
);
const response = {
version: versionId,
timestamp,
...result
};
return c.json(response);
} catch (err) {
const errMsg = err instanceof Error ? err.message : String(err);
try {
const parsed = JSON.parse(errMsg);
const response = {
version: versionId,
timestamp,
...parsed
};
return c.json(response, 500);
} catch {
return c.json({ version: versionId, timestamp, success: false, error: errMsg, duration_ms: 0 }, 500);
}
}
});
// cypher-executor/src/routes/validate.ts
init_dist();
init_schemas();
init_telemetry();
var validateRouter = new Hono2();
validateRouter.post("/validate", async (c) => {
const start = Date.now();
const apiKey = c.req.header("X-Arcrun-API-Key");
const userAgent = c.req.header("User-Agent") ?? void 0;
const body = await c.req.json();
const parsed = graphSchema.safeParse(body);
if (!parsed.success) {
recordTelemetry(c.env, apiKey, {
event_type: "validation_error",
error_code: "schema_failed",
duration_ms: Date.now() - start,
agent_user_agent: userAgent
}, c.executionCtx);
return c.json({ valid: false, errors: parsed.error.issues }, 400);
}
const nodeIds = new Set(parsed.data.nodes.map((n) => n.id));
const invalidEdges = parsed.data.edges.filter((e) => !nodeIds.has(e.from) || !nodeIds.has(e.to));
if (invalidEdges.length > 0) {
recordTelemetry(c.env, apiKey, {
event_type: "validation_error",
error_code: "edge_node_missing",
duration_ms: Date.now() - start,
agent_user_agent: userAgent
}, c.executionCtx);
return c.json({
valid: false,
errors: invalidEdges.map((e) => `\u908A ${e.from} \u2192 ${e.to} \u6307\u5411\u4E0D\u5B58\u5728\u7684\u7BC0\u9EDE`)
}, 400);
}
return c.json({ valid: true, nodeCount: parsed.data.nodes.length, edgeCount: parsed.data.edges.length });
});
// cypher-executor/src/routes/docs.ts
init_dist();
// cypher-executor/src/lib/openapi.ts
var OPENAPI_SPEC = {
openapi: "3.0.3",
info: {
title: "arcrun cypher-executor API",
description: "AI Workflow Execution Engine \u2014 \u900F\u904E\u4E09\u5143\u7D44 Triplet \u6216\u5716 Graph \u5B9A\u7FA9\u5DE5\u4F5C\u6D41\uFF0C\u7CFB\u7D71\u57F7\u884C\u4E26\u56DE\u50B3\u7D50\u679C",
version: "1.0.0",
contact: {
name: "arcrun",
url: "https://github.com/arcrun/arcrun"
}
},
servers: [
{ url: "https://cypher.arcrun.dev", description: "arcrun.dev Hosted" },
{ url: "http://localhost:8787", description: "Local Development" }
],
paths: {
"/": {
get: {
summary: "Health Check",
tags: ["Health"],
responses: {
"200": {
description: "Service is running",
content: {
"application/json": {
schema: {
type: "object",
properties: {
service: { type: "string" },
version: { type: "string" },
status: { type: "string" }
}
}
}
}
}
}
}
},
"/cypher/search": {
post: {
summary: "\u641C\u5C0B\u5DE5\u4F5C\u6D41\u9700\u8981\u7684\u96F6\u4EF6",
tags: ["Cypher"],
description: "\u7528\u4E09\u5143\u7D44\u63CF\u8FF0\u5DE5\u4F5C\u6D41\uFF0C\u7CFB\u7D71\u89E3\u6790\u4E26\u5F9E Registry \u67E5\u8A62\u5C0D\u61C9\u96F6\u4EF6",
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
properties: {
triplets: {
type: "array",
items: { type: "string" },
example: ["start >> \u5B8C\u6210\u5F8C >> get-data", "get-data >> \u5B8C\u6210\u5F8C >> done"],
description: '\u4E09\u5143\u7D44\u9663\u5217\uFF0C\u683C\u5F0F\uFF1A"FROM >> ACTION >> TO"'
},
auto_publish: {
type: "boolean",
default: true,
description: "\u7F3A\u5931\u7684\u96F6\u4EF6\u662F\u5426\u81EA\u52D5\u7522\u751F\u767C\u4F48"
}
},
required: ["triplets"]
}
}
}
},
responses: {
"200": {
description: "\u96F6\u4EF6\u641C\u5C0B\u6210\u529F\uFF08\u542B\u7248\u672C\u865F\u548C\u6642\u6233\uFF0C\u9069\u5408 Markdown \u6587\u6A94\u8FFD\u8E64\uFF09",
content: {
"application/json": {
schema: {
type: "object",
properties: {
version: { type: "string", example: "search-v1-20260327-143022", description: "\u7248\u672C\u865F\uFF08endpoint-v{major}-{timestamp}\uFF09" },
timestamp: { type: "string", format: "date-time", description: "ISO 8601 \u6642\u6233" },
triplets: { type: "array", items: { type: "string" }, description: "\u56DE\u9001\u7684\u4E09\u5143\u7D44\u5217\u8868" },
nodes: { type: "object", description: "\u641C\u5C0B\u5230\u7684\u96F6\u4EF6\u53CA\u5176\u72C0\u614B" },
cypher: { type: "object", description: "\u5DE5\u4F5C\u6D41\u5716\uFF08null \u82E5\u6709\u7F3A\u5931\u96F6\u4EF6\uFF09" },
missing: { type: "array", items: { type: "string" }, description: "\u7F3A\u5931\u96F6\u4EF6\u5217\u8868" },
auto_published: { type: "object", description: "\u81EA\u52D5\u767C\u4F48\u7684\u96F6\u4EF6\uFF08\u82E5 auto_publish=true\uFF09" }
}
}
}
}
},
"400": { description: "\u7121\u6CD5\u89E3\u6790\u4E09\u5143\u7D44" }
}
}
},
"/cypher/execute": {
post: {
summary: "\u57F7\u884C\u5DE5\u4F5C\u6D41",
tags: ["Cypher"],
description: "\u76F4\u63A5\u57F7\u884C triplets\uFF0C\u56DE\u50B3\u5B8C\u6574\u57F7\u884C\u7D50\u679C\u3002\u652F\u63F4\u81EA\u52D5\u767C\u4F48\u7F3A\u5931\u96F6\u4EF6\u3002",
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
properties: {
triplets: {
type: "array",
items: { type: "string" },
description: '\u4E09\u5143\u7D44\u9663\u5217\uFF0C\u683C\u5F0F\uFF1A"FROM >> ACTION >> TO"'
},
context: {
type: "object",
description: "\u57F7\u884C\u4E0A\u4E0B\u6587\uFF0C\u50B3\u5165\u5404\u7BC0\u9EDE\u4F5C\u70BA\u521D\u59CB\u53C3\u6578"
},
auto_publish: {
type: "boolean",
default: true,
description: "\u7F3A\u5931\u7684\u96F6\u4EF6\u662F\u5426\u81EA\u52D5\u7522\u751F\u81E8\u6642\u5BE6\u4F5C"
}
},
required: ["triplets"]
}
}
}
},
responses: {
"200": {
description: "\u57F7\u884C\u6210\u529F\uFF08\u542B\u7248\u672C\u865F\u548C\u6642\u6233\uFF09",
content: {
"application/json": {
schema: {
type: "object",
properties: {
version: { type: "string", example: "execute-v1-20260327-143022", description: "\u7248\u672C\u865F\uFF08endpoint-v{major}-{timestamp}\uFF09" },
timestamp: { type: "string", format: "date-time", description: "ISO 8601 \u6642\u6233" },
success: { type: "boolean", enum: [true] },
data: { type: "object", description: "\u57F7\u884C\u7D50\u679C" },
trace: { type: "array", description: "\u57F7\u884C\u8DDF\u8E64" },
duration_ms: { type: "number" }
}
}
}
}
},
"500": {
description: "\u57F7\u884C\u5931\u6557\u6216\u90E8\u4EFD\u96F6\u4EF6\u7F3A\u5931\uFF08\u542B\u7248\u672C\u865F\u548C\u6642\u6233\uFF09",
content: {
"application/json": {
schema: {
type: "object",
properties: {
version: { type: "string", example: "execute-v1-20260327-143022", description: "\u7248\u672C\u865F\uFF08endpoint-v{major}-{timestamp}\uFF09" },
timestamp: { type: "string", format: "date-time", description: "ISO 8601 \u6642\u6233" },
success: { type: "boolean", enum: [false] },
error: { type: "string" },
missing: { type: "array", items: { type: "string" }, description: "\u7121\u6CD5\u81EA\u52D5\u767C\u4F48\u7684\u7F3A\u5931\u96F6\u4EF6" },
auto_published: {
type: "object",
description: "\u81EA\u52D5\u767C\u4F48\u7684\u96F6\u4EF6\u8CC7\u8A0A",
additionalProperties: {
type: "object",
properties: {
ok: { type: "boolean" },
componentId: { type: "string" },
temporary_endpoint: { type: "string", format: "uri", description: "\u81E8\u6642\u5BE6\u4F5C\u7684 URL" },
implement_by: { type: "string", format: "date-time", description: "\u5BE6\u4F5C\u622A\u6B62\u6642\u9593" }
}
}
},
duration_ms: { type: "number" }
}
}
}
}
}
}
}
},
"/webhooks": {
post: {
summary: "\u5EFA\u7ACB Webhook",
tags: ["Webhooks"],
description: "\u5C07\u5DE5\u4F5C\u6D41\u8A3B\u518A\u6210 Webhook\uFF0C\u5F97\u5230\u516C\u958B URL",
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
properties: {
triplets: {
type: "array",
items: { type: "string" }
},
description: { type: "string" }
}
}
}
}
},
responses: {
"201": {
description: "Webhook \u5EFA\u7ACB\u6210\u529F",
content: {
"application/json": {
schema: {
type: "object",
properties: {
token: { type: "string" },
webhook_url: { type: "string", format: "uri" },
description: { type: "string" },
created_at: { type: "string", format: "date-time" }
}
}
}
}
}
}
},
get: {
summary: "\u5217\u51FA\u6240\u6709 Webhooks",
tags: ["Webhooks"],
parameters: [
{
name: "Authorization",
in: "header",
required: true,
schema: { type: "string", example: "Bearer u6u_xxxxx" },
description: "API Key \u8A8D\u8B49"
}
],
responses: {
"200": {
description: "Webhooks \u5217\u8868",
content: {
"application/json": {
schema: {
type: "object",
properties: {
webhooks: {
type: "array",
items: {
type: "object",
properties: {
token: { type: "string" },
description: { type: "string" },
created_at: { type: "string", format: "date-time" }
}
}
},
total: { type: "number" }
}
}
}
}
},
"401": { description: "\u672A\u6388\u6B0A" }
}
}
},
"/webhooks/{token}": {
get: {
summary: "\u67E5\u8A62\u55AE\u500B Webhook",
tags: ["Webhooks"],
parameters: [
{
name: "token",
in: "path",
required: true,
schema: { type: "string" }
}
],
responses: {
"200": {
description: "Webhook \u8CC7\u8A0A"
},
"404": { description: "Webhook \u4E0D\u5B58\u5728" }
}
},
delete: {
summary: "\u522A\u9664 Webhook",
tags: ["Webhooks"],
parameters: [
{
name: "token",
in: "path",
required: true,
schema: { type: "string" }
}
],
responses: {
"200": { description: "Webhook \u5DF2\u522A\u9664" },
"404": { description: "Webhook \u4E0D\u5B58\u5728" }
}
}
}
},
components: {
securitySchemes: {
ApiKeyAuth: {
type: "apiKey",
in: "header",
name: "Authorization"
}
}
}
};
// cypher-executor/src/routes/docs.ts
var docsRouter = new Hono2();
docsRouter.get("/openapi.json", (c) => {
return c.json(OPENAPI_SPEC);
});
docsRouter.get("/docs", (c) => {
const specStr = JSON.stringify(OPENAPI_SPEC);
const htmlStr = `
Cypher Executor API Docs