kizlo 0.21.0 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/category/index.js +3 -3
- package/dist/category/schema.d.ts +1 -1
- package/dist/cli/commands/dev.js +7 -6
- package/dist/cli/mcp/schema.js +140 -32
- package/dist/cli/mcp/search.js +255 -0
- package/dist/cli/mcp/server.js +111 -20
- package/dist/cli/mcp/tools.js +65 -52
- package/dist/cli/wp/compose/docker-compose.test.ts +21 -0
- package/dist/cli/wp/compose/docker-compose.yml +8 -3
- package/dist/cli/wp/dev.js +33 -0
- package/dist/cli/wp/docker.js +101 -2
- package/dist/comment/index.js +4 -4
- package/dist/email/service.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/menu/utils.js +2 -2
- package/dist/package.js +1 -1
- package/dist/page/index.js +4 -4
- package/dist/page/schema.d.ts +1 -1
- package/dist/post/index.js +4 -4
- package/dist/post/schema.d.ts +5 -5
- package/dist/procedures.d.ts +10 -10
- package/dist/seo/index.js +13 -11
- package/dist/seo/schema.d.ts +5 -5
- package/dist/seo/types.d.ts +4 -4
- package/dist/settings/service.d.ts +11 -11
- package/dist/settings/service.js +15 -15
- package/dist/tag/index.js +3 -3
- package/dist/tag/schema.d.ts +1 -1
- package/dist/wordpress/endpoint.d.ts +13 -3
- package/dist/wordpress/endpoint.js +24 -12
- package/dist/wordpress/generate.js +51 -8
- package/dist/wordpress/index.d.ts +1 -1
- package/dist/wordpress/introspection.js +26 -11
- package/dist/wordpress/types.d.ts +16 -2
- package/package.json +3 -2
package/dist/cli/mcp/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { log } from "../daemon/logger.js";
|
|
2
|
-
import { callRoute, describeRoute,
|
|
2
|
+
import { callRoute, describeRoute, searchRoutes } from "./tools.js";
|
|
3
3
|
import z from "zod/v4";
|
|
4
4
|
import { createServer } from "node:http";
|
|
5
5
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -38,21 +38,92 @@ function isAllowedOrigin(origin) {
|
|
|
38
38
|
return false;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
/**
|
|
42
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Render a tool's answer for both kinds of reader: `structuredContent` for a client that parses, and
|
|
43
|
+
* a text block for one that does not.
|
|
44
|
+
*
|
|
45
|
+
* A failure carries no `structuredContent`. A client validates whatever it finds there against the
|
|
46
|
+
* tool's declared output schema whether or not the result is an error, and a failure is not that
|
|
47
|
+
* shape, so putting it there makes a client reject the answer outright instead of reading it. The
|
|
48
|
+
* detail rides in a second text block, which keeps it machine-readable without breaking the contract.
|
|
49
|
+
*/
|
|
50
|
+
function toolContent(result, summarize = (value) => JSON.stringify(value, null, 2)) {
|
|
43
51
|
if (!result.ok) return {
|
|
44
52
|
content: [{
|
|
45
53
|
type: "text",
|
|
46
|
-
text: result.error
|
|
54
|
+
text: result.error.message
|
|
55
|
+
}, {
|
|
56
|
+
type: "text",
|
|
57
|
+
text: JSON.stringify(result.error, null, 2)
|
|
47
58
|
}],
|
|
48
59
|
isError: true
|
|
49
60
|
};
|
|
50
|
-
return {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
return {
|
|
62
|
+
content: [{
|
|
63
|
+
type: "text",
|
|
64
|
+
text: summarize(result.value)
|
|
65
|
+
}],
|
|
66
|
+
structuredContent: result.value
|
|
67
|
+
};
|
|
54
68
|
}
|
|
55
69
|
/**
|
|
70
|
+
* The one result long enough to be worth compressing. Describe and call put their payload in the
|
|
71
|
+
* text block because the payload is the answer; a ranked list is better read than parsed.
|
|
72
|
+
*/
|
|
73
|
+
function summarizeSearch(value) {
|
|
74
|
+
const outcome = value;
|
|
75
|
+
if (outcome.count === 0) return outcome.message ?? "No route matches that search.";
|
|
76
|
+
const lines = outcome.routes.map((route) => {
|
|
77
|
+
const evidence = route.evidence.map((item) => `${item.field}:${item.term}`).join(", ");
|
|
78
|
+
const summary = route.summary === void 0 ? "" : ` — ${route.summary}`;
|
|
79
|
+
const deprecated = route.deprecated ? " (deprecated)" : "";
|
|
80
|
+
return `${route.name} — ${route.method} ${route.path}${summary}${deprecated}${evidence === "" ? "" : ` [${evidence}]`}`;
|
|
81
|
+
});
|
|
82
|
+
const more = outcome.nextCursor === void 0 ? "" : `\n${outcome.count - outcome.routes.length} more. Pass cursor "${outcome.nextCursor}" for the next page.`;
|
|
83
|
+
return `${outcome.count} route${outcome.count === 1 ? "" : "s"} match:\n${lines.join("\n")}${more}`;
|
|
84
|
+
}
|
|
85
|
+
/** A ranked result, and the named part of the contract that selected it. */
|
|
86
|
+
const SEARCH_OUTPUT = {
|
|
87
|
+
count: z.number().describe("Matches across the whole catalog, not just this page."),
|
|
88
|
+
routes: z.array(z.object({
|
|
89
|
+
name: z.string(),
|
|
90
|
+
namespace: z.string(),
|
|
91
|
+
method: z.string(),
|
|
92
|
+
path: z.string(),
|
|
93
|
+
summary: z.string().optional(),
|
|
94
|
+
deprecated: z.literal(true).optional(),
|
|
95
|
+
evidence: z.array(z.object({
|
|
96
|
+
field: z.string(),
|
|
97
|
+
term: z.string()
|
|
98
|
+
}))
|
|
99
|
+
})),
|
|
100
|
+
namespaces: z.array(z.string()).describe("Every REST namespace this WordPress serves."),
|
|
101
|
+
nextCursor: z.string().optional().describe("Hand back as `cursor` to read the next page."),
|
|
102
|
+
message: z.string().optional().describe("Present only when nothing matched.")
|
|
103
|
+
};
|
|
104
|
+
const DESCRIBE_OUTPUT = {
|
|
105
|
+
name: z.string(),
|
|
106
|
+
namespace: z.string(),
|
|
107
|
+
method: z.string(),
|
|
108
|
+
path: z.string(),
|
|
109
|
+
summary: z.string().optional(),
|
|
110
|
+
description: z.string().optional(),
|
|
111
|
+
deprecated: z.literal(true).optional(),
|
|
112
|
+
pathParameters: z.array(z.string()),
|
|
113
|
+
input: z.record(z.string(), z.unknown()).describe("JSON Schema for `{ params, query, body }`, with its `$defs`."),
|
|
114
|
+
responses: z.record(z.string(), z.unknown()).describe("Keyed by HTTP status."),
|
|
115
|
+
errors: z.array(z.string()).describe("Error codes this route declares.")
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Deliberately permissive. The SDK validates `structuredContent` against this on every successful
|
|
119
|
+
* call, so anything narrower would turn a route whose response does not fit into a protocol error
|
|
120
|
+
* rather than an answer.
|
|
121
|
+
*/
|
|
122
|
+
const CALL_OUTPUT = {
|
|
123
|
+
status: z.number(),
|
|
124
|
+
data: z.unknown().optional()
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
56
127
|
* Three dispatchers rather than a tool per route. The contract a seeded WordPress serves runs to well
|
|
57
128
|
* over a hundred routes, and putting their schemas in front of a model up front would cost tens of
|
|
58
129
|
* thousands of tokens in every message. These cost a few hundred and reach all of them — and because
|
|
@@ -63,24 +134,44 @@ function createMcpServer(state) {
|
|
|
63
134
|
const server = new McpServer({
|
|
64
135
|
name: "kizlo",
|
|
65
136
|
version: "1.0.0"
|
|
66
|
-
}, { instructions:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
137
|
+
}, { instructions: [
|
|
138
|
+
"Read and change the WordPress of the Kizlo project this server was started in.",
|
|
139
|
+
"Every REST route that WordPress exposes is reachable here — core, Kizlo's own, and any plugin's — through three tools rather than one tool per route.",
|
|
140
|
+
"Work in that order: `kizlo_search_routes` to find a route, `kizlo_describe_route` to read its contract, `kizlo_call` to run it.",
|
|
141
|
+
"Describe a route before calling it unless you already know its contract. Arguments are grouped into `params`, `query`, and `body`, and the description says which fields belong where.",
|
|
142
|
+
"API errors are passed through unchanged: the code, message, and data are WordPress's own, not this server's summary of them."
|
|
143
|
+
].join(" ") });
|
|
144
|
+
server.registerTool("kizlo_search_routes", {
|
|
145
|
+
title: "Search WordPress routes",
|
|
146
|
+
description: "Find routes by identity or by meaning: a route name, path, or namespace, or terms from a route's arguments, response fields, description, or error codes. Start here. Omit `query` to browse the whole catalog. Each result names the evidence that selected it, and the route names it returns are what `kizlo_describe_route` and `kizlo_call` take.",
|
|
147
|
+
inputSchema: {
|
|
148
|
+
query: z.string().optional().describe("Terms to rank on. Omit or leave empty to browse every route."),
|
|
149
|
+
namespace: z.string().optional().describe("Only routes in this REST namespace, e.g. \"wp/v2\" or \"kizlo/v1\"."),
|
|
150
|
+
method: z.string().optional().describe("Only routes served with this HTTP method, e.g. \"GET\"."),
|
|
151
|
+
access: z.enum(["read", "write"]).optional().describe("Only reading routes (GET, HEAD, OPTIONS) or only writing ones."),
|
|
152
|
+
limit: z.number().int().optional().describe("Results per page. Defaults to 20, at most 100."),
|
|
153
|
+
cursor: z.string().optional().describe("The `nextCursor` from a previous search, to read the next page.")
|
|
154
|
+
},
|
|
155
|
+
outputSchema: SEARCH_OUTPUT
|
|
156
|
+
}, async (input) => toolContent(searchRoutes(state, input), summarizeSearch));
|
|
72
157
|
server.registerTool("kizlo_describe_route", {
|
|
73
158
|
title: "Describe a WordPress route",
|
|
74
|
-
description: "The
|
|
75
|
-
inputSchema: { route: z.string().describe("A route name from `
|
|
159
|
+
description: "The complete contract for one route: the JSON Schema for its `params`, `query`, and `body`, with the definitions they reference; the response schema for each HTTP status, with content types and headers; which arguments are interpolated into the path; and the error codes the route declares.",
|
|
160
|
+
inputSchema: { route: z.string().describe("A route name from `kizlo_search_routes`, e.g. `kizlo.postTypes.book.list`.") },
|
|
161
|
+
outputSchema: DESCRIBE_OUTPUT
|
|
76
162
|
}, async ({ route }) => toolContent(describeRoute(state, { route })));
|
|
77
163
|
server.registerTool("kizlo_call", {
|
|
78
164
|
title: "Call a WordPress route",
|
|
79
|
-
description: "Run one route against this project's WordPress and return its response.
|
|
165
|
+
description: "Run one route against this project's WordPress and return its response. Arguments go in `input` under the part they belong to: `params` for the values interpolated into the path, `query` for query parameters, and `body` for the payload. `kizlo_describe_route` says which are which.",
|
|
80
166
|
inputSchema: {
|
|
81
|
-
route: z.string().describe("A route name from `
|
|
82
|
-
input: z.
|
|
83
|
-
|
|
167
|
+
route: z.string().describe("A route name from `kizlo_search_routes`."),
|
|
168
|
+
input: z.object({
|
|
169
|
+
params: z.record(z.string(), z.unknown()).optional().describe("Values interpolated into the path."),
|
|
170
|
+
query: z.record(z.string(), z.unknown()).optional().describe("Query parameters."),
|
|
171
|
+
body: z.unknown().optional().describe("The request payload.")
|
|
172
|
+
}).optional().describe("The route's arguments, grouped as `kizlo_describe_route` describes them.")
|
|
173
|
+
},
|
|
174
|
+
outputSchema: CALL_OUTPUT
|
|
84
175
|
}, async ({ route, input }) => toolContent(await callRoute(state, input === void 0 ? { route } : {
|
|
85
176
|
route,
|
|
86
177
|
input
|
package/dist/cli/mcp/tools.js
CHANGED
|
@@ -1,52 +1,54 @@
|
|
|
1
1
|
import { buildWordPressRequest } from "../../wordpress/endpoint.js";
|
|
2
2
|
import { WordPressTransport } from "../../wordpress/transport.js";
|
|
3
|
-
import { routesOf
|
|
4
|
-
import { toJsonSchema } from "./schema.js";
|
|
3
|
+
import { routesOf } from "./routes.js";
|
|
4
|
+
import { inputJsonSchema, toJsonSchema } from "./schema.js";
|
|
5
|
+
import { searchRoutes } from "./search.js";
|
|
5
6
|
|
|
6
7
|
//#region src/cli/mcp/tools.ts
|
|
7
8
|
/** The document is fetched by the watcher, so the only reason it is missing is that none has arrived yet. */
|
|
8
9
|
const NO_DOCUMENT = "The WordPress contract has not been fetched yet. Retry in a moment; `kizlo dev` refreshes it every few seconds.";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (!state.document) return {
|
|
10
|
+
function failed(message) {
|
|
11
|
+
return {
|
|
12
12
|
ok: false,
|
|
13
|
-
error:
|
|
13
|
+
error: {
|
|
14
|
+
kind: "request",
|
|
15
|
+
message
|
|
16
|
+
}
|
|
14
17
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Rank the routes this WordPress serves against a query, or browse them all when there is none.
|
|
21
|
+
*
|
|
22
|
+
* Summaries and match evidence only. The schemas behind these routes are far too large to put in
|
|
23
|
+
* front of a model at once, which is what `kizlo_describe_route` is for.
|
|
24
|
+
*/
|
|
25
|
+
function searchRoutes$1(state, input = {}) {
|
|
26
|
+
if (!state.document) return failed(NO_DOCUMENT);
|
|
27
|
+
const outcome = searchRoutes(state.document, input);
|
|
28
|
+
if (outcome.count === 0) {
|
|
29
|
+
const within = [
|
|
30
|
+
input.namespace !== void 0 ? `namespace "${input.namespace}"` : void 0,
|
|
31
|
+
input.method !== void 0 ? `method ${input.method.toUpperCase()}` : void 0,
|
|
32
|
+
input.access !== void 0 ? `${input.access} routes` : void 0
|
|
33
|
+
].filter((part) => part !== void 0).join(", ");
|
|
19
34
|
return {
|
|
20
|
-
ok:
|
|
21
|
-
|
|
35
|
+
ok: true,
|
|
36
|
+
value: {
|
|
37
|
+
...outcome,
|
|
38
|
+
message: `No route matches that search${within === "" ? "" : ` within ${within}`}. This WordPress serves these namespaces: ${outcome.namespaces.join(", ")}.`
|
|
39
|
+
}
|
|
22
40
|
};
|
|
23
41
|
}
|
|
24
42
|
return {
|
|
25
43
|
ok: true,
|
|
26
|
-
value:
|
|
27
|
-
count: routes.length,
|
|
28
|
-
routes: routes.map((route) => ({
|
|
29
|
-
name: route.name,
|
|
30
|
-
namespace: route.namespace,
|
|
31
|
-
method: route.method,
|
|
32
|
-
path: route.path,
|
|
33
|
-
...route.summary !== void 0 ? { summary: route.summary } : {},
|
|
34
|
-
...route.deprecated ? { deprecated: true } : {}
|
|
35
|
-
}))
|
|
36
|
-
}
|
|
44
|
+
value: outcome
|
|
37
45
|
};
|
|
38
46
|
}
|
|
39
|
-
/** The full
|
|
47
|
+
/** The full contract for one route, which is what a caller needs before it can build a call. */
|
|
40
48
|
function describeRoute(state, input) {
|
|
41
|
-
if (!state.document) return
|
|
42
|
-
ok: false,
|
|
43
|
-
error: NO_DOCUMENT
|
|
44
|
-
};
|
|
49
|
+
if (!state.document) return failed(NO_DOCUMENT);
|
|
45
50
|
const route = routesOf(state.document).get(input.route);
|
|
46
|
-
if (!route) return
|
|
47
|
-
ok: false,
|
|
48
|
-
error: unknownRoute(input.route)
|
|
49
|
-
};
|
|
51
|
+
if (!route) return failed(unknownRoute(input.route));
|
|
50
52
|
return {
|
|
51
53
|
ok: true,
|
|
52
54
|
value: {
|
|
@@ -58,34 +60,36 @@ function describeRoute(state, input) {
|
|
|
58
60
|
...route.description !== void 0 ? { description: route.description } : {},
|
|
59
61
|
...route.deprecated ? { deprecated: true } : {},
|
|
60
62
|
pathParameters: route.definition.pathParameters,
|
|
61
|
-
input:
|
|
63
|
+
input: inputJsonSchema(route.operation.input, state.document),
|
|
64
|
+
responses: responseSchemas(route.operation.responses, state.document),
|
|
65
|
+
errors: route.operation.errors
|
|
62
66
|
}
|
|
63
67
|
};
|
|
64
68
|
}
|
|
65
69
|
/** Run one route against the connected WordPress and hand back what it answered. */
|
|
66
70
|
async function callRoute(state, input) {
|
|
67
|
-
if (!state.document) return
|
|
68
|
-
|
|
69
|
-
error: NO_DOCUMENT
|
|
70
|
-
};
|
|
71
|
-
if (!state.credentials) return {
|
|
72
|
-
ok: false,
|
|
73
|
-
error: "No WordPress connection is configured, so nothing can be called. Check the connection settings in `.env`."
|
|
74
|
-
};
|
|
71
|
+
if (!state.document) return failed(NO_DOCUMENT);
|
|
72
|
+
if (!state.credentials) return failed("No WordPress connection is configured, so nothing can be called. Check the connection settings in `.env`.");
|
|
75
73
|
const route = routesOf(state.document).get(input.route);
|
|
76
|
-
if (!route) return
|
|
77
|
-
ok: false,
|
|
78
|
-
error: unknownRoute(input.route)
|
|
79
|
-
};
|
|
74
|
+
if (!route) return failed(unknownRoute(input.route));
|
|
80
75
|
const built = buildWordPressRequest(route.definition, input.input ?? {});
|
|
81
|
-
if (built.error) return
|
|
82
|
-
ok: false,
|
|
83
|
-
error: built.error.message
|
|
84
|
-
};
|
|
76
|
+
if (built.error) return failed(built.error.message);
|
|
85
77
|
const response = await new WordPressTransport({ credentials: state.credentials }).request(built.request);
|
|
86
78
|
if (response.error) return {
|
|
87
79
|
ok: false,
|
|
88
|
-
error:
|
|
80
|
+
error: {
|
|
81
|
+
kind: "response",
|
|
82
|
+
message: `${route.method} ${route.path} failed (${response.status || "no response"}): ${response.error.code} — ${response.error.message}`,
|
|
83
|
+
route: route.name,
|
|
84
|
+
method: route.method,
|
|
85
|
+
path: route.path,
|
|
86
|
+
status: response.status,
|
|
87
|
+
error: {
|
|
88
|
+
code: response.error.code,
|
|
89
|
+
message: response.error.message,
|
|
90
|
+
data: response.error.data
|
|
91
|
+
}
|
|
92
|
+
}
|
|
89
93
|
};
|
|
90
94
|
return {
|
|
91
95
|
ok: true,
|
|
@@ -95,9 +99,18 @@ async function callRoute(state, input) {
|
|
|
95
99
|
}
|
|
96
100
|
};
|
|
97
101
|
}
|
|
102
|
+
/** The response half of the contract, converted so a caller can read a success or a failure body. */
|
|
103
|
+
function responseSchemas(responses, document) {
|
|
104
|
+
return Object.fromEntries(Object.entries(responses).map(([status, response]) => [status, {
|
|
105
|
+
...response.description !== void 0 ? { description: response.description } : {},
|
|
106
|
+
...response.content_type !== void 0 ? { content_type: response.content_type } : {},
|
|
107
|
+
...response.headers ? { headers: toJsonSchema(response.headers, document) } : {},
|
|
108
|
+
...response.body ? { body: toJsonSchema(response.body, document) } : {}
|
|
109
|
+
}]));
|
|
110
|
+
}
|
|
98
111
|
function unknownRoute(name) {
|
|
99
|
-
return `No route named "${name}" in this WordPress contract. Call \`
|
|
112
|
+
return `No route named "${name}" in this WordPress contract. Call \`kizlo_search_routes\` for the routes it serves.`;
|
|
100
113
|
}
|
|
101
114
|
|
|
102
115
|
//#endregion
|
|
103
|
-
export { callRoute, describeRoute,
|
|
116
|
+
export { callRoute, describeRoute, searchRoutes$1 as searchRoutes };
|
|
@@ -7,6 +7,7 @@ interface Service {
|
|
|
7
7
|
environment?: Record<string, string>
|
|
8
8
|
ports?: string[]
|
|
9
9
|
volumes?: string[]
|
|
10
|
+
healthcheck?: { test: string[] }
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
const wordpress = (): Service => parse(fs.readFileSync(COMPOSE_FILE, "utf8")).services.wordpress
|
|
@@ -41,6 +42,26 @@ describe("base compose wordpress service", () => {
|
|
|
41
42
|
expect(wordpress().ports).toContain("${WP_PORT:-8080}:80")
|
|
42
43
|
})
|
|
43
44
|
|
|
45
|
+
describe("healthcheck", () => {
|
|
46
|
+
const probe = (): string => wordpress().healthcheck?.test?.at(-1) ?? ""
|
|
47
|
+
|
|
48
|
+
test("requires the WordPress install wp-cli depends on", () => {
|
|
49
|
+
expect(probe()).toContain("test -f /var/www/html/wp-includes/version.php")
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test("probes HTTP for liveness without asserting a successful status", () => {
|
|
53
|
+
expect(probe()).toContain("curl ")
|
|
54
|
+
expect(probe()).toContain("http://localhost/")
|
|
55
|
+
// `-f`/`--fail` turns any non-2xx into a probe failure, which is the bug being fixed:
|
|
56
|
+
// Headless Mode 404s pages by design, and a live server answering 404 is still live.
|
|
57
|
+
expect(probe().slice(probe().indexOf("curl "))).not.toMatch(/(?:^|\s)--?\S*f/)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test("references no WordPress application page, so a 404 from one can't fail it", () => {
|
|
61
|
+
expect(probe()).not.toMatch(/wp-login\.php|wp-admin|wp-json|feed|xmlrpc/)
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
44
65
|
test("mounts both local-only config assets read-only", () => {
|
|
45
66
|
const volumes = wordpress().volumes ?? []
|
|
46
67
|
expect(volumes).toContain("./uploads.ini:/usr/local/etc/php/conf.d/zz-kizlo-uploads.ini:ro")
|
|
@@ -67,9 +67,14 @@ services:
|
|
|
67
67
|
- ./uploads.ini:/usr/local/etc/php/conf.d/zz-kizlo-uploads.ini:ro
|
|
68
68
|
- ./apache-listen.conf:/etc/apache2/conf-enabled/zz-kizlo-listen.conf:ro
|
|
69
69
|
healthcheck:
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
|
|
70
|
+
# Liveness, not a status assertion: the install is in place (what wp-cli needs before
|
|
71
|
+
# it can run anything) and the server answers HTTP at all. Deliberately status-agnostic,
|
|
72
|
+
# because Kizlo's own Headless Mode legitimately 404s the front page, the feeds and the
|
|
73
|
+
# login path. Requiring a 2xx from any WordPress page (`curl -f`) makes an install that is
|
|
74
|
+
# working as configured look dead, and every service gated on this one never starts.
|
|
75
|
+
# curl's own limit is a second under `timeout` below, so a server that accepts the connection
|
|
76
|
+
# and then stalls is reported by curl rather than killed by docker with nothing to show.
|
|
77
|
+
test: ["CMD-SHELL", "test -f /var/www/html/wp-includes/version.php && curl -s -o /dev/null -m 4 http://localhost/"]
|
|
73
78
|
interval: 5s
|
|
74
79
|
timeout: 5s
|
|
75
80
|
retries: 60
|
package/dist/cli/wp/dev.js
CHANGED
|
@@ -32,6 +32,37 @@ function devUrl(port) {
|
|
|
32
32
|
return `http://${lanAddress() ?? "localhost"}:${port}`;
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
+
* Where the login screen actually lives, read from the raw `kizlo_settings_headless` option. Mirrors
|
|
36
|
+
* the plugin's `HeadlessSettings::isLoginRenameActive()`: the master switch, the rename toggle and a
|
|
37
|
+
* non-empty slug must all hold, so a half-configured feature still points at `/wp-admin`. Anything
|
|
38
|
+
* unreadable (option unset, plugin absent, output not JSON) falls back the same way, which is what a
|
|
39
|
+
* project that never touches the setting gets.
|
|
40
|
+
*/
|
|
41
|
+
function resolveAdminPath(option) {
|
|
42
|
+
if (!option) return "/wp-admin";
|
|
43
|
+
let settings;
|
|
44
|
+
try {
|
|
45
|
+
settings = JSON.parse(option);
|
|
46
|
+
} catch {
|
|
47
|
+
return "/wp-admin";
|
|
48
|
+
}
|
|
49
|
+
const slug = typeof settings?.login_slug === "string" ? settings.login_slug.trim() : "";
|
|
50
|
+
return settings?.enabled === true && settings.rename_login === true && slug !== "" ? `/${slug}` : "/wp-admin";
|
|
51
|
+
}
|
|
52
|
+
/** The Headless Mode option as wp-cli prints it; undefined when it's unset or wp-cli couldn't answer. */
|
|
53
|
+
async function readHeadlessOption() {
|
|
54
|
+
try {
|
|
55
|
+
return await wpCli([
|
|
56
|
+
"option",
|
|
57
|
+
"get",
|
|
58
|
+
"kizlo_settings_headless",
|
|
59
|
+
"--format=json"
|
|
60
|
+
]);
|
|
61
|
+
} catch {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
35
66
|
* Seed `dev.fixtures` into freshly installed local WordPress, reusing the test seeding
|
|
36
67
|
* primitives: seed the default subscriber so `ctx.userId` exists, then run each `seed`
|
|
37
68
|
* over REST. Plugins are already active (`bootstrapDev` ensures them before seeding).
|
|
@@ -106,12 +137,14 @@ async function bootstrapDev(cfg) {
|
|
|
106
137
|
});
|
|
107
138
|
const seeded = !installed && cfg.fixtures.length ? await seedDevFixtures(cfg, url) : 0;
|
|
108
139
|
const appPassword = !installed ? await createAdminAppPassword("kizlo-dev") : void 0;
|
|
140
|
+
const adminPath = resolveAdminPath(await readHeadlessOption());
|
|
109
141
|
return {
|
|
110
142
|
url,
|
|
111
143
|
username: TEST_ADMIN.username,
|
|
112
144
|
dbPort: cfg.dbPort,
|
|
113
145
|
seeded,
|
|
114
146
|
appPassword,
|
|
147
|
+
adminPath,
|
|
115
148
|
adminPassword: !installed ? TEST_ADMIN.password : void 0
|
|
116
149
|
};
|
|
117
150
|
}
|
package/dist/cli/wp/docker.js
CHANGED
|
@@ -90,6 +90,50 @@ async function stackStatus(project) {
|
|
|
90
90
|
return await dockerStatus() === "running" ? "unknown" : "stopped";
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* How long `composeUp` waits for every service to report healthy. The `wordpress` probe's own
|
|
95
|
+
* verdict lands anywhere from ~330s (a 30s start period, then 60 retries 5s apart) to ~630s when
|
|
96
|
+
* every attempt burns its full 5s timeout, so this cap sits inside that range on purpose: a stack
|
|
97
|
+
* that is still coming up is never cut off, while one that will never converge is reported at
|
|
98
|
+
* roughly six minutes rather than eleven. Past the cap the error names each service's state at that
|
|
99
|
+
* moment instead of the healthcheck's final verdict, which is the trade this bound accepts.
|
|
100
|
+
*/
|
|
101
|
+
const WAIT_TIMEOUT_SECONDS = 360;
|
|
102
|
+
/** Cap on a healthcheck's reported output; docker keeps up to 4 KB per probe, far more than reads. */
|
|
103
|
+
const PROBE_OUTPUT_LIMIT = 500;
|
|
104
|
+
/**
|
|
105
|
+
* Compose's per-resource progress chatter, which it writes to stderr even when the command works:
|
|
106
|
+
* `Container x Created`, `Volume y Removed`. A failed `up --wait` buries its real message under a
|
|
107
|
+
* dozen of these, so they are dropped and the lines that say what went wrong survive: `Container x
|
|
108
|
+
* Error …`, `dependency failed to start: …`, a denied mount. An unrecognised verb just costs one
|
|
109
|
+
* noise line, and anything carrying trailing text is kept, so this can only under-filter.
|
|
110
|
+
*/
|
|
111
|
+
const COMPOSE_PROGRESS_LINE = /^(?:Container|Network|Volume|Image)\s+\S+\s+(?:Creating|Created|Recreate|Recreated|Starting|Started|Stopping|Stopped|Removing|Removed|Waiting|Healthy|Running|Pulling|Pulled|Building|Built|Skipped)$/;
|
|
112
|
+
/** Docker's own message for a failed command, with its progress chatter removed. */
|
|
113
|
+
function dockerMessage(res) {
|
|
114
|
+
return (res.stderr || res.stdout).split("\n").map((line) => line.trim()).filter((line) => line !== "" && !COMPOSE_PROGRESS_LINE.test(line)).join("\n");
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Whether compose reports a container's healthcheck as passing, or as having none to report. The
|
|
118
|
+
* field is empty for a service without a healthcheck and absent altogether on some compose builds,
|
|
119
|
+
* and neither is a failure, so both read as healthy here.
|
|
120
|
+
*/
|
|
121
|
+
function isHealthy(health) {
|
|
122
|
+
return !health || health === "healthy";
|
|
123
|
+
}
|
|
124
|
+
/** Containers from `compose ps --format json`: one object per line, or a single array on older builds. */
|
|
125
|
+
function parseComposePs(stdout) {
|
|
126
|
+
const trimmed = stdout.trim();
|
|
127
|
+
if (!trimmed) return [];
|
|
128
|
+
return (trimmed.startsWith("[") ? [trimmed] : trimmed.split("\n")).flatMap((chunk) => {
|
|
129
|
+
try {
|
|
130
|
+
const parsed = JSON.parse(chunk);
|
|
131
|
+
return Array.isArray(parsed) ? parsed : [parsed];
|
|
132
|
+
} catch {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
93
137
|
function bind(stack) {
|
|
94
138
|
const base = [
|
|
95
139
|
"compose",
|
|
@@ -104,13 +148,68 @@ function bind(stack) {
|
|
|
104
148
|
};
|
|
105
149
|
const compose$1 = (args, opts) => run("docker", [...base, ...args], env, opts);
|
|
106
150
|
const composePull$1 = (services = []) => compose$1(["pull", ...services]);
|
|
151
|
+
/**
|
|
152
|
+
* The last line a container's healthcheck printed, as `exit <code>: <output>`. Undefined when
|
|
153
|
+
* the container declares no healthcheck, has not run one yet, or docker could not answer. In all
|
|
154
|
+
* of those the caller has nothing to add beyond the service's state.
|
|
155
|
+
*/
|
|
156
|
+
const lastProbe = async (container) => {
|
|
157
|
+
const res = await run("docker", [
|
|
158
|
+
"inspect",
|
|
159
|
+
"--format",
|
|
160
|
+
"{{json .State.Health}}",
|
|
161
|
+
container
|
|
162
|
+
], env);
|
|
163
|
+
if (res.code !== 0) return void 0;
|
|
164
|
+
let health;
|
|
165
|
+
try {
|
|
166
|
+
health = JSON.parse(res.stdout.trim());
|
|
167
|
+
} catch {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const last = health?.Log?.at(-1);
|
|
171
|
+
if (!last) return void 0;
|
|
172
|
+
const output = (last.Output ?? "").trim().replace(/\s*\n\s*/g, " ").slice(0, PROBE_OUTPUT_LIMIT);
|
|
173
|
+
return output ? `exit ${last.ExitCode}: ${output}` : `exit ${last.ExitCode}`;
|
|
174
|
+
};
|
|
175
|
+
const describe = async (container) => {
|
|
176
|
+
const state = container.Health ? `${container.State} (${container.Health})` : container.State;
|
|
177
|
+
const probe = await lastProbe(container.Name);
|
|
178
|
+
return ` ${container.Service}: ${state}${probe ? `, healthcheck ${probe}` : ""}`;
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Why the stack did not come up, in terms the user can act on: every service that isn't running
|
|
182
|
+
* and healthy, with the last thing its healthcheck reported. `compose up --wait` only says that
|
|
183
|
+
* waiting failed, and a probe failing quietly (a 404, a refused connection) leaves no trace in
|
|
184
|
+
* its stderr, so the container's own health log is the only place that answer exists.
|
|
185
|
+
*
|
|
186
|
+
* Never throws. This runs while a failure is already being reported, so a second fault here
|
|
187
|
+
* (docker gone, a health log shaped differently) must cost the explanation, not replace it.
|
|
188
|
+
*/
|
|
189
|
+
const diagnose = async () => {
|
|
190
|
+
try {
|
|
191
|
+
const failing = parseComposePs((await compose$1([
|
|
192
|
+
"ps",
|
|
193
|
+
"--all",
|
|
194
|
+
"--format",
|
|
195
|
+
"json"
|
|
196
|
+
])).stdout).filter((container) => container.State !== "running" || !isHealthy(container.Health));
|
|
197
|
+
return (await Promise.all(failing.map(describe))).join("\n");
|
|
198
|
+
} catch {
|
|
199
|
+
return "";
|
|
200
|
+
}
|
|
201
|
+
};
|
|
107
202
|
const composeUp$1 = async () => {
|
|
108
203
|
const res = await compose$1([
|
|
109
204
|
"up",
|
|
110
205
|
"-d",
|
|
111
|
-
"--wait"
|
|
206
|
+
"--wait",
|
|
207
|
+
"--wait-timeout",
|
|
208
|
+
String(WAIT_TIMEOUT_SECONDS)
|
|
112
209
|
]);
|
|
113
|
-
if (res.code
|
|
210
|
+
if (res.code === 0) return;
|
|
211
|
+
const detail = [dockerMessage(res), await diagnose()].filter(Boolean).join("\n");
|
|
212
|
+
throw new Error(`docker compose up failed (waited up to ${WAIT_TIMEOUT_SECONDS}s for healthy services):\n${detail}`);
|
|
114
213
|
};
|
|
115
214
|
const composeDown$1 = async (opts) => {
|
|
116
215
|
const args = ["down"];
|
package/dist/comment/index.js
CHANGED
|
@@ -17,8 +17,8 @@ const COMMENT_PROCEDURES = {
|
|
|
17
17
|
output: Comment
|
|
18
18
|
}, async ({ context, errors, input }) => {
|
|
19
19
|
const response = await context.wordpress.comments.retrieve({
|
|
20
|
-
id: input.params.id,
|
|
21
|
-
password: input.query?.password
|
|
20
|
+
params: { id: input.params.id },
|
|
21
|
+
query: { password: input.query?.password }
|
|
22
22
|
});
|
|
23
23
|
if (response.error) switch (response.error.code) {
|
|
24
24
|
case "rest_comment_invalid_id":
|
|
@@ -63,7 +63,7 @@ const COMMENT_PROCEDURES = {
|
|
|
63
63
|
post: listed(input.query?.post),
|
|
64
64
|
search: input.query?.search
|
|
65
65
|
};
|
|
66
|
-
const response = await context.wordpress.comments.list(searchParams);
|
|
66
|
+
const response = await context.wordpress.comments.list({ query: searchParams });
|
|
67
67
|
if (response.error) switch (response.error.code) {
|
|
68
68
|
case "rest_cannot_read":
|
|
69
69
|
case "rest_cannot_read_post":
|
|
@@ -114,7 +114,7 @@ const COMMENT_PROCEDURES = {
|
|
|
114
114
|
author_url: input.body.authorUrl,
|
|
115
115
|
parent: input.body.parentId
|
|
116
116
|
};
|
|
117
|
-
const response = await context.wordpress.kizlo.comments.create(submission);
|
|
117
|
+
const response = await context.wordpress.kizlo.comments.create({ body: submission });
|
|
118
118
|
if (response.error) switch (response.error.code) {
|
|
119
119
|
case "require_name_email": throw errors.COMMENT_NAME_EMAIL_REQUIRED();
|
|
120
120
|
case "require_valid_email": throw errors.COMMENT_INVALID_EMAIL();
|
package/dist/email/service.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WP_Error } from "./wordpress/error.js";
|
|
2
|
-
import { ActiveWordPressClient, WP_CallOptions, WP_Client, WP_CommonErrorCode, WP_CurrencyFormat, WP_CustomFields, WP_CustomFieldsPath, WP_Endpoint, WP_EndpointData, WP_EndpointDefinition, WP_EndpointError, WP_EndpointInput, WP_EndpointPath, WP_EndpointResult, WP_ErrorData, WP_Failure, WP_List, WP_ListMetadata, WP_RequestBuild, WP_RequestContentType, WP_RequestInput, WP_RequestMethod, WP_ResponseContentType, WP_Result, WP_Success, WP_TransportFailure, WP_TypedHeaders, WordPressClientRegistry, WordPressCredentials, WordPressCustomFieldsRegistry, WordPressEndpointRegistry } from "./wordpress/types.js";
|
|
2
|
+
import { ActiveWordPressClient, WP_CallOptions, WP_Client, WP_CommonErrorCode, WP_CurrencyFormat, WP_CustomFields, WP_CustomFieldsPath, WP_Endpoint, WP_EndpointData, WP_EndpointDefinition, WP_EndpointError, WP_EndpointInput, WP_EndpointPath, WP_EndpointResult, WP_ErrorData, WP_Failure, WP_List, WP_ListMetadata, WP_RequestBuild, WP_RequestContentType, WP_RequestInput, WP_RequestMethod, WP_RequestParts, WP_ResponseContentType, WP_Result, WP_Success, WP_TransportFailure, WP_TypedHeaders, WordPressClientRegistry, WordPressCredentials, WordPressCustomFieldsRegistry, WordPressEndpointRegistry } from "./wordpress/types.js";
|
|
3
3
|
import { WordPressTransport, WordPressTransportOptions, WordPressTransportResult } from "./wordpress/transport.js";
|
|
4
4
|
import { buildWordPressRequest, createWordPressClient, wpEndpoint } from "./wordpress/endpoint.js";
|
|
5
5
|
import { isWordPressResourceError } from "./wordpress/utils.js";
|
|
@@ -43,4 +43,4 @@ import { ShaAlgorithm, compare, hash, hmac } from "./shared/crypto.js";
|
|
|
43
43
|
import { Identifier, parseIdentifier } from "./shared/identifier.js";
|
|
44
44
|
import { createOrpcRouter } from "./shared/router.js";
|
|
45
45
|
import { deserializeCurrencyFormat, deserializeListMetadata, emptyList, serializeCurrencyFormat } from "./shared/serialize.js";
|
|
46
|
-
export { ActiveWordPressClient, AnyContext, AnyEventSchema, AnyEvents, AnyIntegration, AnyKizloConfig, AnyMiddleware, AnyNestedClient, AnyProcedure, AnyProcedureTree, AuthAdapter, AuthGetSession, AuthUser, AuthorsSettings, AuthorsSettingsInput, BaseHandlerOptions, BrandSettings, BrandSettingsInput, COMMON_ERRORS, CONTENT_TYPES, CORE_PROCEDURES, CaptchaAdapter, type Category, type CategoryCustomFields, CategoryCustomFieldsSchema, CommonErrorCode, CommonErrorLiteral, CommonErrorMap, ConnInfo, ConnInfoFn, ConsoleLogAdapterOptions, ContentType, Context, ContextConfig, Contract, ContractProcedure, CookiesAdapter, CookiesDeleteAll, CookiesGetAll, CookiesSetAll, CookiesStorage, CoreProcedures, CrawlingSettings, CrawlingSettingsInput, CreateKizloOptions, CreateSitemapRouteOptions, CurrencyFormat, DefinedErrorMapLike, DevPluginSource, EnvReader, EnvRecord, EnvSource, ErrorDefinition, EventHandler, EventHandlerFn, ExtractIntegrationProcedures, ExtractMiddlewareContextOutput, ExtractParams, ExtractProcedureByScope, Fixture, FixtureCleanupFn, FixtureSeedFn, FixtureSettleFn, GeoAdapter, GeoGetConnInfo, HTTPMethod, HeadlessSettings, HeadlessSettingsInput, IconDescriptor, Identifier, IdentifierInput, IdentitySettings, IdentitySettingsInput, InferEventUnion, InferIntegrationProcedures, InferMiddlewares, InferProcedureData, InferProcedureError, InferProcedureInput, InferProcedureResult, Integration, IntegrationRequirements, InvocationScope, JsonValue, Kizlo, KizloClient, KizloClientConfig, KizloConfig, KizloEnv, KizloError, KizloErrorOptions, KizloErrorUnion, KizloResult, LIST_ORDER, List, ListMetadata, ListOrder, ListSitemapUrlInput, ListSitemapUrlInputBase, LocalPlugin, LogLevel, LogPayload, Logger, LoggerAdapter, ManifestIcon, MaybeCallerOptions, MaybeOptionalOptions, Middleware, MiddlewareHandler, MiddlewareNextFn, MiddlewareOptions, MiddlewareResult, OpenapiPart, OrganizationFounder, OrganizationSettings, OrganizationSettingsInput, type Page, type PageCustomFields, PageCustomFieldsSchema, PersonSettings, PersonSettingsInput, PluginSource, type Post, type PostCustomFields, PostCustomFieldsSchema, PostStatusDefinition, PostTypeSettings, PostTypeSettingsInput, PreviewTokenData, PreviewTokenPayload, Procedure, ProcedureContext, ProcedureErrors, ProcedureHandler, ProcedureMethod, ProcedureOptions, ProcedureTreeDepthTuple, RedirectInput, RedirectStatus, ResolveParamsPart, ResolvedIcons, ResolvedSchemaInput, ResolvedSchemaOutput, ResultClient, Robots, RobotsRule, RootProcedures, S2SClient, SITEMAP_BASE, SITEMAP_INDEX_SLUG, SchemaIO, SeedContext, Seo, SeoHead, SeoSchema, ServiceAdapters, SessionFn, Settings, SettingsService, SettleContext, ShaAlgorithm, SiteSettings, SiteSettingsInput, Sitemap, SitemapIndex, SitemapIndexEntry, SitemapList, SitemapRouteExtra, SitemapUrl, SitemapUrlImage, SitemapUrlList, SocialProfile, type Tag, type TagCustomFields, TagCustomFieldsSchema, TaxonomySettings, TaxonomySettingsInput, TestAdminUser, TestCredentials, TestUser, ThrowableErrorFn, ThrowableErrorMap, UploadsSettings, UploadsSettingsInput, VerifyCaptchaFn, VerifyPreviewTokenFn, WEB_MANIFEST_ROUTE, WPK_RobotRule, WPK_Robots, WPK_Seo, WPK_Sitemap, WPK_SitemapUrl, WPK_SitemapUrlImage, WP_CallOptions, WP_Client, WP_CommonErrorCode, WP_CurrencyFormat, WP_CustomFields, WP_CustomFieldsPath, WP_Endpoint, WP_EndpointData, WP_EndpointDefinition, WP_EndpointError, WP_EndpointInput, WP_EndpointPath, WP_EndpointResult, WP_Error, WP_ErrorData, WP_Failure, WP_List, WP_ListMetadata, WP_RequestBuild, WP_RequestContentType, WP_RequestInput, WP_RequestMethod, WP_ResponseContentType, WP_Result, WP_Success, WP_TransportFailure, WP_TypedHeaders, WebManifest, WebhookOptions, WebhookSettings, WebhookSettingsInput, WithCommonErrorMap, WordPressClientRegistry, WordPressCredentials, WordPressCustomFieldsRegistry, WordPressEndpointRegistry, WordPressEnv, WordPressTransport, WordPressTransportOptions, WordPressTransportResult, altcha, arkose, assertIntegrationEndpoints, assertIntegrationEnv, authMock, buildProcedure, buildWebManifest, buildWordPressRequest, combineDetailed, compare, consoleLog, createAuthAdapter, createCaptchaAdapter, createCookiesAdapter, createEventHandler, createGeoAdapter, createIntegration, createKizlo, createKizloClient, createLoggerAdapter, createManifestRoute, createMiddleware, createOrpcRouter, createProcedure, createResultClient, createSitemapRedirectRoute, createSitemapRoute, createThrowableErrorMap, createWebhookRouter, createWordPressClient, customFieldsSchema, defineErrorMap, defineFixture, deserializeCurrencyFormat, deserializeListMetadata, deserializeSeo, emptyList, friendlyCaptcha, generateContract, geoMock, geoVercelNext, handleUnexpectedError, hash, hcaptcha, hmac, integrationEnv, isContractProcedure, isKizloError, isLocalPlugin, isWordPressResourceError, mergeServiceAdapters, missingEndpoints, missingEnv, noopAdapter, parseIdentifier, parseSitemapSlug, readEnv, recaptcha, renderJsonLd, renderSitemapIndex, renderUrlSet, resolveIcons, resolveKizloConfig, resolveWordPressConnection, restoreContract, schemaType, serializeCurrencyFormat, sitemapEntryPath, sortIntegrations, toKizloError, turnstile, wordPressConnectionComplete, wpEndpoint, xmlResponse };
|
|
46
|
+
export { ActiveWordPressClient, AnyContext, AnyEventSchema, AnyEvents, AnyIntegration, AnyKizloConfig, AnyMiddleware, AnyNestedClient, AnyProcedure, AnyProcedureTree, AuthAdapter, AuthGetSession, AuthUser, AuthorsSettings, AuthorsSettingsInput, BaseHandlerOptions, BrandSettings, BrandSettingsInput, COMMON_ERRORS, CONTENT_TYPES, CORE_PROCEDURES, CaptchaAdapter, type Category, type CategoryCustomFields, CategoryCustomFieldsSchema, CommonErrorCode, CommonErrorLiteral, CommonErrorMap, ConnInfo, ConnInfoFn, ConsoleLogAdapterOptions, ContentType, Context, ContextConfig, Contract, ContractProcedure, CookiesAdapter, CookiesDeleteAll, CookiesGetAll, CookiesSetAll, CookiesStorage, CoreProcedures, CrawlingSettings, CrawlingSettingsInput, CreateKizloOptions, CreateSitemapRouteOptions, CurrencyFormat, DefinedErrorMapLike, DevPluginSource, EnvReader, EnvRecord, EnvSource, ErrorDefinition, EventHandler, EventHandlerFn, ExtractIntegrationProcedures, ExtractMiddlewareContextOutput, ExtractParams, ExtractProcedureByScope, Fixture, FixtureCleanupFn, FixtureSeedFn, FixtureSettleFn, GeoAdapter, GeoGetConnInfo, HTTPMethod, HeadlessSettings, HeadlessSettingsInput, IconDescriptor, Identifier, IdentifierInput, IdentitySettings, IdentitySettingsInput, InferEventUnion, InferIntegrationProcedures, InferMiddlewares, InferProcedureData, InferProcedureError, InferProcedureInput, InferProcedureResult, Integration, IntegrationRequirements, InvocationScope, JsonValue, Kizlo, KizloClient, KizloClientConfig, KizloConfig, KizloEnv, KizloError, KizloErrorOptions, KizloErrorUnion, KizloResult, LIST_ORDER, List, ListMetadata, ListOrder, ListSitemapUrlInput, ListSitemapUrlInputBase, LocalPlugin, LogLevel, LogPayload, Logger, LoggerAdapter, ManifestIcon, MaybeCallerOptions, MaybeOptionalOptions, Middleware, MiddlewareHandler, MiddlewareNextFn, MiddlewareOptions, MiddlewareResult, OpenapiPart, OrganizationFounder, OrganizationSettings, OrganizationSettingsInput, type Page, type PageCustomFields, PageCustomFieldsSchema, PersonSettings, PersonSettingsInput, PluginSource, type Post, type PostCustomFields, PostCustomFieldsSchema, PostStatusDefinition, PostTypeSettings, PostTypeSettingsInput, PreviewTokenData, PreviewTokenPayload, Procedure, ProcedureContext, ProcedureErrors, ProcedureHandler, ProcedureMethod, ProcedureOptions, ProcedureTreeDepthTuple, RedirectInput, RedirectStatus, ResolveParamsPart, ResolvedIcons, ResolvedSchemaInput, ResolvedSchemaOutput, ResultClient, Robots, RobotsRule, RootProcedures, S2SClient, SITEMAP_BASE, SITEMAP_INDEX_SLUG, SchemaIO, SeedContext, Seo, SeoHead, SeoSchema, ServiceAdapters, SessionFn, Settings, SettingsService, SettleContext, ShaAlgorithm, SiteSettings, SiteSettingsInput, Sitemap, SitemapIndex, SitemapIndexEntry, SitemapList, SitemapRouteExtra, SitemapUrl, SitemapUrlImage, SitemapUrlList, SocialProfile, type Tag, type TagCustomFields, TagCustomFieldsSchema, TaxonomySettings, TaxonomySettingsInput, TestAdminUser, TestCredentials, TestUser, ThrowableErrorFn, ThrowableErrorMap, UploadsSettings, UploadsSettingsInput, VerifyCaptchaFn, VerifyPreviewTokenFn, WEB_MANIFEST_ROUTE, WPK_RobotRule, WPK_Robots, WPK_Seo, WPK_Sitemap, WPK_SitemapUrl, WPK_SitemapUrlImage, WP_CallOptions, WP_Client, WP_CommonErrorCode, WP_CurrencyFormat, WP_CustomFields, WP_CustomFieldsPath, WP_Endpoint, WP_EndpointData, WP_EndpointDefinition, WP_EndpointError, WP_EndpointInput, WP_EndpointPath, WP_EndpointResult, WP_Error, WP_ErrorData, WP_Failure, WP_List, WP_ListMetadata, WP_RequestBuild, WP_RequestContentType, WP_RequestInput, WP_RequestMethod, WP_RequestParts, WP_ResponseContentType, WP_Result, WP_Success, WP_TransportFailure, WP_TypedHeaders, WebManifest, WebhookOptions, WebhookSettings, WebhookSettingsInput, WithCommonErrorMap, WordPressClientRegistry, WordPressCredentials, WordPressCustomFieldsRegistry, WordPressEndpointRegistry, WordPressEnv, WordPressTransport, WordPressTransportOptions, WordPressTransportResult, altcha, arkose, assertIntegrationEndpoints, assertIntegrationEnv, authMock, buildProcedure, buildWebManifest, buildWordPressRequest, combineDetailed, compare, consoleLog, createAuthAdapter, createCaptchaAdapter, createCookiesAdapter, createEventHandler, createGeoAdapter, createIntegration, createKizlo, createKizloClient, createLoggerAdapter, createManifestRoute, createMiddleware, createOrpcRouter, createProcedure, createResultClient, createSitemapRedirectRoute, createSitemapRoute, createThrowableErrorMap, createWebhookRouter, createWordPressClient, customFieldsSchema, defineErrorMap, defineFixture, deserializeCurrencyFormat, deserializeListMetadata, deserializeSeo, emptyList, friendlyCaptcha, generateContract, geoMock, geoVercelNext, handleUnexpectedError, hash, hcaptcha, hmac, integrationEnv, isContractProcedure, isKizloError, isLocalPlugin, isWordPressResourceError, mergeServiceAdapters, missingEndpoints, missingEnv, noopAdapter, parseIdentifier, parseSitemapSlug, readEnv, recaptcha, renderJsonLd, renderSitemapIndex, renderUrlSet, resolveIcons, resolveKizloConfig, resolveWordPressConnection, restoreContract, schemaType, serializeCurrencyFormat, sitemapEntryPath, sortIntegrations, toKizloError, turnstile, wordPressConnectionComplete, wpEndpoint, xmlResponse };
|
package/dist/menu/utils.js
CHANGED
|
@@ -3,7 +3,7 @@ import { listed, stringifiedMetaRecord } from "@kizlo/shared";
|
|
|
3
3
|
//#region src/menu/utils.ts
|
|
4
4
|
function deserializeListMenuInput(input) {
|
|
5
5
|
const orderby = input?.orderby === "relevance" && !input?.search || input?.orderby === "include" && input?.include === void 0 || input?.orderby === "include_slugs" && input?.slug === void 0 ? void 0 : input?.orderby;
|
|
6
|
-
return {
|
|
6
|
+
return { query: {
|
|
7
7
|
after: input?.after,
|
|
8
8
|
before: input?.before,
|
|
9
9
|
exclude: listed(input?.exclude),
|
|
@@ -21,7 +21,7 @@ function deserializeListMenuInput(input) {
|
|
|
21
21
|
tax_relation: input?.taxRelation,
|
|
22
22
|
search_columns: input?.searchColumns,
|
|
23
23
|
status: ["publish"]
|
|
24
|
-
};
|
|
24
|
+
} };
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
27
|
* The `href` a headless frontend links to. WP builds a non-custom item's `url` as an absolute same-site
|