kizlo 0.20.2 → 0.22.0

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.
Files changed (47) hide show
  1. package/dist/category/index.js +3 -3
  2. package/dist/category/schema.d.ts +1 -1
  3. package/dist/cli/commands/_setup.js +5 -1
  4. package/dist/cli/commands/dev.js +63 -20
  5. package/dist/cli/daemon/config.js +27 -3
  6. package/dist/cli/daemon/generate.js +8 -2
  7. package/dist/cli/daemon/watch.js +11 -3
  8. package/dist/cli/mcp/index.js +46 -0
  9. package/dist/cli/mcp/routes.js +42 -0
  10. package/dist/cli/mcp/schema.js +212 -0
  11. package/dist/cli/mcp/search.js +255 -0
  12. package/dist/cli/mcp/server.js +248 -0
  13. package/dist/cli/mcp/state.js +7 -0
  14. package/dist/cli/mcp/tools.js +116 -0
  15. package/dist/comment/index.js +4 -4
  16. package/dist/config.d.ts +17 -1
  17. package/dist/email/service.js +1 -1
  18. package/dist/index.d.ts +3 -3
  19. package/dist/index.js +2 -2
  20. package/dist/integrations/astro/integration.js +2 -0
  21. package/dist/integrations/nextjs/integration.js +2 -0
  22. package/dist/integrations/node/integration.js +2 -0
  23. package/dist/integrations/tanstack-start/integration.js +2 -0
  24. package/dist/kizlo.js +4 -3
  25. package/dist/menu/utils.js +2 -2
  26. package/dist/package.js +1 -1
  27. package/dist/page/index.js +4 -4
  28. package/dist/page/schema.d.ts +1 -1
  29. package/dist/post/index.js +4 -4
  30. package/dist/post/schema.d.ts +1 -1
  31. package/dist/procedures.d.ts +7 -7
  32. package/dist/seo/index.js +13 -11
  33. package/dist/seo/types.d.ts +4 -4
  34. package/dist/settings/service.d.ts +11 -11
  35. package/dist/settings/service.js +15 -15
  36. package/dist/shared/constants.js +3 -1
  37. package/dist/shared/integration.d.ts +9 -1
  38. package/dist/shared/integration.js +6 -3
  39. package/dist/tag/index.js +3 -3
  40. package/dist/tag/schema.d.ts +1 -1
  41. package/dist/wordpress/endpoint.d.ts +13 -3
  42. package/dist/wordpress/endpoint.js +24 -12
  43. package/dist/wordpress/generate.js +57 -9
  44. package/dist/wordpress/index.d.ts +1 -1
  45. package/dist/wordpress/introspection.js +26 -11
  46. package/dist/wordpress/types.d.ts +16 -2
  47. package/package.json +4 -2
@@ -24,7 +24,7 @@ const PAGE_PROCEDURES = {
24
24
  if (input.query?.previewToken) {
25
25
  const result = await context.verifyPreviewToken(input.query.previewToken);
26
26
  if (!result) throw errors.PAGE_NOT_FOUND();
27
- const response$1 = await context.wordpress.postTypes.page.retrieve({ identifier: String(result.id) });
27
+ const response$1 = await context.wordpress.kizlo.postTypes.page.retrieve({ params: { identifier: String(result.id) } });
28
28
  if (response$1.error) switch (response$1.error.code) {
29
29
  case "invalid_post_type":
30
30
  case "post_type_not_found":
@@ -44,7 +44,7 @@ const PAGE_PROCEDURES = {
44
44
  }
45
45
  const identifier = parseIdentifier(input.params.identifier);
46
46
  if (!identifier) throw errors.PAGE_NOT_FOUND();
47
- const response = await context.wordpress.postTypes.page.retrieve({ identifier: String(identifier.value) });
47
+ const response = await context.wordpress.kizlo.postTypes.page.retrieve({ params: { identifier: String(identifier.value) } });
48
48
  if (response.error) switch (response.error.code) {
49
49
  case "invalid_post_type":
50
50
  case "post_type_not_found":
@@ -78,7 +78,7 @@ const PAGE_PROCEDURES = {
78
78
  }, async ({ input, context, errors }) => {
79
79
  const q = input.query;
80
80
  const orderby = q?.orderby === "relevance" && !q?.search || q?.orderby === "include" && q?.include === void 0 ? void 0 : q?.orderby;
81
- const response = await context.wordpress.postTypes.page.list({
81
+ const response = await context.wordpress.kizlo.postTypes.page.list({ query: {
82
82
  status: ["publish"],
83
83
  after: q?.after,
84
84
  author: normalizeArrayableValue(q?.author),
@@ -99,7 +99,7 @@ const PAGE_PROCEDURES = {
99
99
  search: q?.search,
100
100
  search_columns: q?.searchColumns,
101
101
  slug: normalizeArrayableValue(q?.slug)
102
- });
102
+ } });
103
103
  if (response.error) switch (response.error.code) {
104
104
  case "rest_post_invalid_page_number": throw errors.PAGE_INVALID_PAGE();
105
105
  case "rest_no_route": throw errors.PAGE_NOT_FOUND();
@@ -5,7 +5,7 @@ import z from "zod/v4";
5
5
 
6
6
  //#region src/page/schema.d.ts
7
7
 
8
- type PageCustomFields = WP_CustomFields<"postTypes.page">;
8
+ type PageCustomFields = WP_CustomFields<"kizlo.postTypes.page">;
9
9
  declare const PageCustomFieldsSchema: z.ZodType<PageCustomFields, PageCustomFields>;
10
10
  declare const Page: z.ZodObject<{
11
11
  id: z.ZodNumber;
@@ -24,7 +24,7 @@ const POST_PROCEDURES = {
24
24
  if (input.query?.previewToken) {
25
25
  const result = await context.verifyPreviewToken(input.query.previewToken);
26
26
  if (!result) throw errors.POST_NOT_FOUND();
27
- const response$1 = await context.wordpress.postTypes.post.retrieve({ identifier: String(result.id) });
27
+ const response$1 = await context.wordpress.kizlo.postTypes.post.retrieve({ params: { identifier: String(result.id) } });
28
28
  if (response$1.error) switch (response$1.error.code) {
29
29
  case "invalid_post_type":
30
30
  case "post_type_not_found":
@@ -44,7 +44,7 @@ const POST_PROCEDURES = {
44
44
  }
45
45
  const identifier = parseIdentifier(input.params.identifier);
46
46
  if (!identifier) throw errors.POST_NOT_FOUND();
47
- const response = await context.wordpress.postTypes.post.retrieve({ identifier: String(identifier.value) });
47
+ const response = await context.wordpress.kizlo.postTypes.post.retrieve({ params: { identifier: String(identifier.value) } });
48
48
  if (response.error) switch (response.error.code) {
49
49
  case "invalid_post_type":
50
50
  case "post_type_not_found":
@@ -78,7 +78,7 @@ const POST_PROCEDURES = {
78
78
  }, async ({ input, context, errors }) => {
79
79
  const q = input.query;
80
80
  const orderby = q?.orderby === "relevance" && !q?.search || q?.orderby === "include" && q?.include === void 0 ? void 0 : q?.orderby;
81
- const response = await context.wordpress.postTypes.post.list({
81
+ const response = await context.wordpress.kizlo.postTypes.post.list({ query: {
82
82
  status: ["publish"],
83
83
  after: q?.after,
84
84
  author: normalizeArrayableValue(q?.author),
@@ -102,7 +102,7 @@ const POST_PROCEDURES = {
102
102
  tags: normalizeArrayableValue(q?.tags),
103
103
  tags_exclude: normalizeArrayableValue(q?.tagsExclude),
104
104
  tax_relation: q?.taxRelation
105
- });
105
+ } });
106
106
  if (response.error) switch (response.error.code) {
107
107
  case "rest_post_invalid_page_number": throw errors.POST_INVALID_PAGE();
108
108
  case "rest_no_route": throw errors.POST_NOT_FOUND();
@@ -5,7 +5,7 @@ import z from "zod/v4";
5
5
 
6
6
  //#region src/post/schema.d.ts
7
7
 
8
- type PostCustomFields = WP_CustomFields<"postTypes.post">;
8
+ type PostCustomFields = WP_CustomFields<"kizlo.postTypes.post">;
9
9
  declare const PostCustomFieldsSchema: z.ZodType<PostCustomFields, PostCustomFields>;
10
10
  declare const Post: z.ZodObject<{
11
11
  id: z.ZodNumber;
@@ -44,7 +44,7 @@ declare const CORE_PROCEDURES: {
44
44
  include?: string | number | (string | number)[] | undefined;
45
45
  offset?: string | number | undefined;
46
46
  order?: "asc" | "desc" | undefined;
47
- orderby?: "date" | "id" | "slug" | "parent" | "title" | "author" | "include" | "modified" | "relevance" | "include_slugs" | undefined;
47
+ orderby?: "id" | "date" | "slug" | "parent" | "title" | "author" | "include" | "modified" | "relevance" | "include_slugs" | undefined;
48
48
  searchColumns?: ("post_title" | "post_content" | "post_excerpt")[] | undefined;
49
49
  slug?: string | string[] | undefined;
50
50
  taxRelation?: "AND" | "OR" | undefined;
@@ -99,7 +99,7 @@ declare const CORE_PROCEDURES: {
99
99
  include?: string | number | (string | number)[] | undefined;
100
100
  offset?: string | number | undefined;
101
101
  order?: "asc" | "desc" | undefined;
102
- orderby?: "date" | "id" | "slug" | "parent" | "title" | "author" | "include" | "modified" | "relevance" | "include_slugs" | "menu_order" | undefined;
102
+ orderby?: "id" | "date" | "slug" | "parent" | "title" | "author" | "include" | "modified" | "relevance" | "include_slugs" | "menu_order" | undefined;
103
103
  searchColumns?: ("post_title" | "post_content" | "post_excerpt")[] | undefined;
104
104
  slug?: string | string[] | undefined;
105
105
  parent?: string | number | (string | number)[] | undefined;
@@ -136,7 +136,7 @@ declare const CORE_PROCEDURES: {
136
136
  exclude?: string | number | (string | number)[] | undefined;
137
137
  include?: string | number | (string | number)[] | undefined;
138
138
  order?: "asc" | "desc" | undefined;
139
- orderBy?: "count" | "id" | "slug" | "name" | "description" | "include" | "include_slugs" | "term_group" | undefined;
139
+ orderBy?: "id" | "count" | "slug" | "name" | "description" | "include" | "include_slugs" | "term_group" | undefined;
140
140
  hideEmpty?: string | boolean | undefined;
141
141
  parent?: string | number | undefined;
142
142
  post?: string | number | undefined;
@@ -172,7 +172,7 @@ declare const CORE_PROCEDURES: {
172
172
  exclude?: string | number | (string | number)[] | undefined;
173
173
  include?: string | number | (string | number)[] | undefined;
174
174
  order?: "asc" | "desc" | undefined;
175
- orderBy?: "count" | "id" | "slug" | "name" | "description" | "include" | "include_slugs" | "term_group" | undefined;
175
+ orderBy?: "id" | "count" | "slug" | "name" | "description" | "include" | "include_slugs" | "term_group" | undefined;
176
176
  hideEmpty?: string | boolean | undefined;
177
177
  post?: string | number | undefined;
178
178
  slug?: string | string[] | undefined;
@@ -292,7 +292,7 @@ declare const CORE_PROCEDURES: {
292
292
  include?: string | number | (string | number)[] | undefined;
293
293
  offset?: string | number | undefined;
294
294
  order?: "asc" | "desc" | undefined;
295
- orderby?: "date" | "id" | "slug" | "parent" | "title" | "author" | "include" | "relevance" | "include_slugs" | "menu_order" | undefined;
295
+ orderby?: "id" | "date" | "slug" | "parent" | "title" | "author" | "include" | "relevance" | "include_slugs" | "menu_order" | undefined;
296
296
  searchColumns?: ("post_title" | "post_content" | "post_excerpt")[] | undefined;
297
297
  slug?: string | string[] | undefined;
298
298
  taxRelation?: "AND" | "OR" | undefined;
@@ -371,7 +371,7 @@ declare const CORE_PROCEDURES: {
371
371
  include?: string | number | (string | number)[] | undefined;
372
372
  offset?: string | number | undefined;
373
373
  order?: "asc" | "desc" | undefined;
374
- orderby?: "date" | "id" | "slug" | "parent" | "title" | "author" | "include" | "relevance" | "include_slugs" | "menu_order" | undefined;
374
+ orderby?: "id" | "date" | "slug" | "parent" | "title" | "author" | "include" | "relevance" | "include_slugs" | "menu_order" | undefined;
375
375
  searchColumns?: ("post_title" | "post_content" | "post_excerpt")[] | undefined;
376
376
  slug?: string | string[] | undefined;
377
377
  taxRelation?: "AND" | "OR" | undefined;
@@ -507,7 +507,7 @@ declare const CORE_PROCEDURES: {
507
507
  include?: string | number | (string | number)[] | undefined;
508
508
  offset?: string | number | undefined;
509
509
  order?: "asc" | "desc" | undefined;
510
- orderby?: "date" | "id" | "parent" | "include" | "post" | undefined;
510
+ orderby?: "id" | "date" | "parent" | "include" | "post" | undefined;
511
511
  parent?: string | number | (string | number)[] | undefined;
512
512
  parentExclude?: string | number | (string | number)[] | undefined;
513
513
  post?: string | number | (string | number)[] | undefined;
package/dist/seo/index.js CHANGED
@@ -28,7 +28,7 @@ const SEO_PROCEDURES = {
28
28
  scope: "internal",
29
29
  output: Seo
30
30
  }, async ({ context, errors }) => {
31
- const response = await context.wordpress.seo.homepage.retrieve();
31
+ const response = await context.wordpress.kizlo.seo.homepage.retrieve();
32
32
  if (response.error) {
33
33
  context.logger.error("Get homepage seo unhandled error", response.error);
34
34
  throw errors.INTERNAL_SERVER_ERROR();
@@ -40,7 +40,7 @@ const SEO_PROCEDURES = {
40
40
  scope: "internal",
41
41
  output: SitemapList
42
42
  }, async ({ context, errors }) => {
43
- const response = await context.wordpress.seo.sitemaps.list();
43
+ const response = await context.wordpress.kizlo.seo.sitemaps.list();
44
44
  if (response.error) {
45
45
  context.logger.error("List sitemaps unhandled error", response.error);
46
46
  throw errors.INTERNAL_SERVER_ERROR();
@@ -51,7 +51,7 @@ const SEO_PROCEDURES = {
51
51
  scope: "internal",
52
52
  output: SitemapIndex
53
53
  }, async ({ context, errors }) => {
54
- const response = await context.wordpress.seo.sitemaps.retrieve({ type: "index" });
54
+ const response = await context.wordpress.kizlo.seo.sitemaps.retrieve({ params: { type: "index" } });
55
55
  if (response.error) {
56
56
  context.logger.error("Get sitemap index unhandled error", response.error);
57
57
  throw errors.INTERNAL_SERVER_ERROR();
@@ -70,7 +70,7 @@ const SEO_PROCEDURES = {
70
70
  scope: "internal",
71
71
  output: Robots
72
72
  }, async ({ context, errors }) => {
73
- const response = await context.wordpress.seo.robots.retrieve();
73
+ const response = await context.wordpress.kizlo.seo.robots.retrieve();
74
74
  if (response.error) {
75
75
  context.logger.error("List robots unhandled error", response.error);
76
76
  throw errors.INTERNAL_SERVER_ERROR();
@@ -90,13 +90,15 @@ const SEO_PROCEDURES = {
90
90
  input: ListSitemapUrlInput
91
91
  }, async ({ context, input, errors }) => {
92
92
  const page = input.page ?? 1;
93
- const response = input.type === "author" ? await context.wordpress.seo.sitemaps.retrieve({
94
- type: "author",
95
- page
96
- }) : await context.wordpress.seo.sitemaps.listUrls({
97
- type: input.type,
98
- key: input.key,
99
- page
93
+ const response = input.type === "author" ? await context.wordpress.kizlo.seo.sitemaps.retrieve({
94
+ params: { type: "author" },
95
+ query: { page }
96
+ }) : await context.wordpress.kizlo.seo.sitemaps.listUrls({
97
+ params: {
98
+ type: input.type,
99
+ key: input.key
100
+ },
101
+ query: { page }
100
102
  });
101
103
  if (response.error) {
102
104
  context.logger.error("List sitemap urls unhandled error", response.error);
@@ -3,11 +3,11 @@ import "../wordpress/index.js";
3
3
 
4
4
  //#region src/seo/types.d.ts
5
5
  /** The resolved SEO block, exactly as the project's generated WordPress client describes it. */
6
- type WPK_Seo = WP_EndpointData<"seo.homepage.retrieve">;
7
- type WPK_Robots = WP_EndpointData<"seo.robots.retrieve">;
6
+ type WPK_Seo = WP_EndpointData<"kizlo.seo.homepage.retrieve">;
7
+ type WPK_Robots = WP_EndpointData<"kizlo.seo.robots.retrieve">;
8
8
  type WPK_RobotRule = WPK_Robots["rules"][number];
9
- type WPK_Sitemap = WP_EndpointData<"seo.sitemaps.list">[number];
10
- type WPK_SitemapUrl = WP_EndpointData<"seo.sitemaps.listUrls">[number];
9
+ type WPK_Sitemap = WP_EndpointData<"kizlo.seo.sitemaps.list">[number];
10
+ type WPK_SitemapUrl = WP_EndpointData<"kizlo.seo.sitemaps.listUrls">[number];
11
11
  type WPK_SitemapUrlImage = WPK_SitemapUrl["images"][number];
12
12
  //#endregion
13
13
  export { WPK_RobotRule, WPK_Robots, WPK_Seo, WPK_Sitemap, WPK_SitemapUrl, WPK_SitemapUrlImage };
@@ -7,17 +7,17 @@ declare class SettingsService {
7
7
  private readonly wordpress;
8
8
  constructor(wordpress: ActiveWordPressClient);
9
9
  /** Fetch every Kizlo settings section in one response. */
10
- get(): Promise<WP_EndpointResult<"settings.retrieve">>;
11
- updateSite(input: SiteSettingsInput): Promise<WP_EndpointResult<"settings.site.update">>;
12
- updateBrand(input: BrandSettingsInput): Promise<WP_EndpointResult<"settings.brand.update">>;
13
- updateWebhook(input: WebhookSettingsInput): Promise<WP_EndpointResult<"settings.webhook.update">>;
14
- updateIdentity(input: IdentitySettingsInput): Promise<WP_EndpointResult<"settings.identity.update">>;
15
- updateAuthors(input: AuthorsSettingsInput): Promise<WP_EndpointResult<"settings.authors.update">>;
16
- updateCrawling(input: CrawlingSettingsInput): Promise<WP_EndpointResult<"settings.crawling.update">>;
17
- updateUploads(input: UploadsSettingsInput): Promise<WP_EndpointResult<"settings.uploads.update">>;
18
- updateHeadless(input: HeadlessSettingsInput): Promise<WP_EndpointResult<"settings.headless.update">>;
19
- updatePostType(key: string, input: PostTypeSettingsInput): Promise<WP_EndpointResult<"settings.postTypes.update">>;
20
- updateTaxonomy(key: string, input: TaxonomySettingsInput): Promise<WP_EndpointResult<"settings.taxonomies.update">>;
10
+ get(): Promise<WP_EndpointResult<"kizlo.settings.retrieve">>;
11
+ updateSite(input: SiteSettingsInput): Promise<WP_EndpointResult<"kizlo.settings.site.update">>;
12
+ updateBrand(input: BrandSettingsInput): Promise<WP_EndpointResult<"kizlo.settings.brand.update">>;
13
+ updateWebhook(input: WebhookSettingsInput): Promise<WP_EndpointResult<"kizlo.settings.webhook.update">>;
14
+ updateIdentity(input: IdentitySettingsInput): Promise<WP_EndpointResult<"kizlo.settings.identity.update">>;
15
+ updateAuthors(input: AuthorsSettingsInput): Promise<WP_EndpointResult<"kizlo.settings.authors.update">>;
16
+ updateCrawling(input: CrawlingSettingsInput): Promise<WP_EndpointResult<"kizlo.settings.crawling.update">>;
17
+ updateUploads(input: UploadsSettingsInput): Promise<WP_EndpointResult<"kizlo.settings.uploads.update">>;
18
+ updateHeadless(input: HeadlessSettingsInput): Promise<WP_EndpointResult<"kizlo.settings.headless.update">>;
19
+ updatePostType(key: string, input: PostTypeSettingsInput): Promise<WP_EndpointResult<"kizlo.settings.postTypes.update">>;
20
+ updateTaxonomy(key: string, input: TaxonomySettingsInput): Promise<WP_EndpointResult<"kizlo.settings.taxonomies.update">>;
21
21
  }
22
22
  //#endregion
23
23
  export { SettingsService };
@@ -6,42 +6,42 @@ var SettingsService = class {
6
6
  }
7
7
  /** Fetch every Kizlo settings section in one response. */
8
8
  async get() {
9
- return this.wordpress.settings.retrieve();
9
+ return this.wordpress.kizlo.settings.retrieve();
10
10
  }
11
11
  async updateSite(input) {
12
- return this.wordpress.settings.site.update(input);
12
+ return this.wordpress.kizlo.settings.site.update({ body: input });
13
13
  }
14
14
  async updateBrand(input) {
15
- return this.wordpress.settings.brand.update(input);
15
+ return this.wordpress.kizlo.settings.brand.update({ body: input });
16
16
  }
17
17
  async updateWebhook(input) {
18
- return this.wordpress.settings.webhook.update(input);
18
+ return this.wordpress.kizlo.settings.webhook.update({ body: input });
19
19
  }
20
20
  async updateIdentity(input) {
21
- return this.wordpress.settings.identity.update(input);
21
+ return this.wordpress.kizlo.settings.identity.update({ body: input });
22
22
  }
23
23
  async updateAuthors(input) {
24
- return this.wordpress.settings.authors.update(input);
24
+ return this.wordpress.kizlo.settings.authors.update({ body: input });
25
25
  }
26
26
  async updateCrawling(input) {
27
- return this.wordpress.settings.crawling.update(input);
27
+ return this.wordpress.kizlo.settings.crawling.update({ body: input });
28
28
  }
29
29
  async updateUploads(input) {
30
- return this.wordpress.settings.uploads.update(input);
30
+ return this.wordpress.kizlo.settings.uploads.update({ body: input });
31
31
  }
32
32
  async updateHeadless(input) {
33
- return this.wordpress.settings.headless.update(input);
33
+ return this.wordpress.kizlo.settings.headless.update({ body: input });
34
34
  }
35
35
  async updatePostType(key, input) {
36
- return this.wordpress.settings.postTypes.update({
37
- ...input,
38
- slug: key
36
+ return this.wordpress.kizlo.settings.postTypes.update({
37
+ params: { slug: key },
38
+ body: input
39
39
  });
40
40
  }
41
41
  async updateTaxonomy(key, input) {
42
- return this.wordpress.settings.taxonomies.update({
43
- ...input,
44
- slug: key
42
+ return this.wordpress.kizlo.settings.taxonomies.update({
43
+ params: { slug: key },
44
+ body: input
45
45
  });
46
46
  }
47
47
  };
@@ -5,6 +5,8 @@ import { milliseconds } from "@kizlo/shared";
5
5
  const VERSION = version;
6
6
  const RPC_PROTOCOL_HEADER = "x-rpc-protocol";
7
7
  const SESSION_EXPIRY_MARGIN_MS = milliseconds("5 minutes");
8
+ /** Mount order for the built-in framework integrations, ahead of app integrations that leave `order` unset. */
9
+ const FRAMEWORK_INTEGRATION_ORDER = -100;
8
10
 
9
11
  //#endregion
10
- export { RPC_PROTOCOL_HEADER };
12
+ export { FRAMEWORK_INTEGRATION_ORDER, RPC_PROTOCOL_HEADER };
@@ -69,11 +69,19 @@ interface Integration<TId extends string, TProcedures extends AnyProcedureTree =
69
69
  env?: EnvSource;
70
70
  /** Environment, endpoint, and WordPress plugin requirements checked at their documented lifecycle points. */
71
71
  requires?: IntegrationRequirements;
72
+ /**
73
+ * Mount position, ascending, defaulting to `0`. Mounting first means supplying the baseline a later
74
+ * integration overrides, so a framework integration declares a negative order to land ahead of app
75
+ * integrations however the `integrations` array is written.
76
+ */
77
+ order?: number;
72
78
  }
73
79
  type AnyIntegration = Integration<any, AnyProcedureTree>;
74
80
  type ExtractIntegrationProcedures<TIntegration extends AnyIntegration> = TIntegration extends Integration<infer Name, infer Procedures> ? [keyof Procedures] extends [never] ? never : string extends keyof Procedures ? never : Record<Name, Procedures> : never;
75
81
  type InferIntegrationProcedures<TIntegrations extends readonly AnyIntegration[]> = TIntegrations extends readonly [] ? Record<never, never> : ExtractIntegrationProcedures<TIntegrations[number]> extends infer R ? [R] extends [never] ? Record<never, never> : UnionToIntersection<R> : never;
76
82
  declare function createIntegration<TId extends string, TProcedures extends AnyProcedureTree = Record<never, never>>(integration: Integration<TId, TProcedures>): Integration<TId, TProcedures>;
83
+ /** Integrations in mount order. `sort` is stable, so integrations sharing an `order` keep their declared order. */
84
+ declare function sortIntegrations<TIntegration extends AnyIntegration>(integrations: readonly TIntegration[]): TIntegration[];
77
85
  /** The declared subtrees the generated tree does not have. */
78
86
  declare function missingEndpoints(endpoints: object, required: readonly string[]): string[];
79
87
  /**
@@ -88,4 +96,4 @@ declare function assertIntegrationEndpoints(integration: AnyIntegration, endpoin
88
96
  /** Fail at startup when an integration's required environment values are absent. */
89
97
  declare function assertIntegrationEnv(integration: AnyIntegration, env: EnvSource): void;
90
98
  //#endregion
91
- export { AnyIntegration, EnvReader, EnvRecord, EnvSource, ExtractIntegrationProcedures, InferIntegrationProcedures, Integration, IntegrationRequirements, KizloEnv, WordPressEnv, assertIntegrationEndpoints, assertIntegrationEnv, createIntegration, missingEndpoints, missingEnv, readEnv };
99
+ export { AnyIntegration, EnvReader, EnvRecord, EnvSource, ExtractIntegrationProcedures, InferIntegrationProcedures, Integration, IntegrationRequirements, KizloEnv, WordPressEnv, assertIntegrationEndpoints, assertIntegrationEnv, createIntegration, missingEndpoints, missingEnv, readEnv, sortIntegrations };
@@ -18,6 +18,10 @@ function missingEnv(source, required) {
18
18
  function createIntegration(integration) {
19
19
  return integration;
20
20
  }
21
+ /** Integrations in mount order. `sort` is stable, so integrations sharing an `order` keep their declared order. */
22
+ function sortIntegrations(integrations) {
23
+ return [...integrations].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
24
+ }
21
25
  /** The declared subtrees the generated tree does not have. */
22
26
  function missingEndpoints(endpoints, required) {
23
27
  return required.filter((path) => {
@@ -43,8 +47,7 @@ function assertIntegrationEndpoints(integration, endpoints) {
43
47
  const missing = missingEndpoints(endpoints, required);
44
48
  if (missing.length === 0) return;
45
49
  const plugins = integration.requires?.plugins;
46
- const remedy = plugins?.length ? `Install or update the required WordPress plugins (${plugins.map((plugin) => `${plugin.name} ${plugin.version}+`).join(", ")}), then run \`kizlo generate\`.` : "Run `kizlo generate` against a WordPress that serves them.";
47
- throw new KizloError("MISSING_WORDPRESS_ENDPOINTS", { message: `The "${integration.id}" integration needs WordPress endpoints your generated client does not have: ${missing.join(", ")}. ${remedy}` });
50
+ throw new KizloError("MISSING_WORDPRESS_ENDPOINTS", { message: `${plugins?.length ? `The "${integration.id}" integration plugin is outdated. Update ${plugins.map((plugin) => `${plugin.name} to ${plugin.version} or newer`).join(" and ")}, then run \`kizlo generate\` to refresh introspection.ts.` : `The "${integration.id}" integration is missing routes in your introspection. Run \`kizlo generate\` against a WordPress that serves them to refresh introspection.ts.`}\n\nMissing routes:\n${missing.map((route) => ` - ${route}`).join("\n")}\n\n${`Download the latest version: https://kizlo.io/integrations/${integration.id}`}` });
48
51
  }
49
52
  /** Fail at startup when an integration's required environment values are absent. */
50
53
  function assertIntegrationEnv(integration, env) {
@@ -56,4 +59,4 @@ function assertIntegrationEnv(integration, env) {
56
59
  }
57
60
 
58
61
  //#endregion
59
- export { assertIntegrationEndpoints, assertIntegrationEnv, createIntegration, missingEndpoints, missingEnv, readEnv };
62
+ export { assertIntegrationEndpoints, assertIntegrationEnv, createIntegration, missingEndpoints, missingEnv, readEnv, sortIntegrations };
package/dist/tag/index.js CHANGED
@@ -18,7 +18,7 @@ const TAG_PROCEDURES = {
18
18
  }, async ({ input, context, errors }) => {
19
19
  const identifier = parseIdentifier(input.params.identifier);
20
20
  if (!identifier) throw errors.TAG_NOT_FOUND();
21
- const response = await context.wordpress.taxonomies.postTag.retrieve({ identifier: String(identifier.value) });
21
+ const response = await context.wordpress.kizlo.taxonomies.postTag.retrieve({ params: { identifier: String(identifier.value) } });
22
22
  if (response.error) switch (response.error.code) {
23
23
  case "invalid_taxonomy":
24
24
  case "term_not_found":
@@ -41,7 +41,7 @@ const TAG_PROCEDURES = {
41
41
  output: schemaType(TagList),
42
42
  errors: LIST_TAG_ERROR_MAP
43
43
  }, async ({ input, context, errors }) => {
44
- const response = await context.wordpress.taxonomies.postTag.list({
44
+ const response = await context.wordpress.kizlo.taxonomies.postTag.list({ query: {
45
45
  page: input.query?.page,
46
46
  per_page: input.query?.perPage,
47
47
  search: input.query?.search,
@@ -52,7 +52,7 @@ const TAG_PROCEDURES = {
52
52
  hide_empty: input.query?.hideEmpty,
53
53
  post: input.query?.post,
54
54
  slug: normalizeArrayableValue(input.query?.slug)
55
- });
55
+ } });
56
56
  if (response.error) switch (response.error.code) {
57
57
  case "rest_post_invalid_page_number": throw errors.TAG_INVALID_PAGE();
58
58
  case "rest_no_route": throw errors.TAG_NOT_FOUND();
@@ -4,7 +4,7 @@ import * as _kizlo_shared24 from "@kizlo/shared";
4
4
  import z from "zod/v4";
5
5
 
6
6
  //#region src/tag/schema.d.ts
7
- type TagCustomFields = WP_CustomFields<"taxonomies.postTag">;
7
+ type TagCustomFields = WP_CustomFields<"kizlo.taxonomies.postTag">;
8
8
  declare const TagCustomFieldsSchema: z.ZodType<TagCustomFields, TagCustomFields>;
9
9
  declare const Tag: z.ZodObject<{
10
10
  id: z.ZodNumber;
@@ -1,4 +1,4 @@
1
- import { WP_Client, WP_Endpoint, WP_EndpointDefinition, WP_RequestBuild } from "./types.js";
1
+ import { WP_Client, WP_Endpoint, WP_EndpointDefinition, WP_RequestBuild, WP_RequestParts } from "./types.js";
2
2
  import { WordPressTransport } from "./transport.js";
3
3
 
4
4
  //#region src/wordpress/endpoint.d.ts
@@ -10,14 +10,24 @@ import { WordPressTransport } from "./transport.js";
10
10
  */
11
11
  declare function wpEndpoint<TInput, TResult>(definition: WP_EndpointDefinition): WP_Endpoint<TInput, TResult>;
12
12
  /**
13
- * Turn a definition and its input into a request: interpolate path parameters, then split the rest.
13
+ * Turn a definition and its input into a request: interpolate `params` into the path, put `query` on
14
+ * the URL, and forward `body` as it stands.
15
+ *
16
+ * Each part goes exactly where the caller put it, so nothing has to be inferred from the input's own
17
+ * keys. That matters because a field can legitimately be named `body` — `kizlo.email.send` takes one
18
+ * — and it is what lets a body method also carry query parameters, which the old method-based split
19
+ * could not express.
20
+ *
21
+ * Whether the request has a body at all is the definition's to answer: `requestContentType` is set
22
+ * for exactly those operations that declare one. A declared body left unsupplied is sent as an empty
23
+ * object rather than dropped, which is the shape a call with no fields has always sent.
14
24
  *
15
25
  * A path parameter that cannot be interpolated comes back as an error rather than throwing. The
16
26
  * generated input marks these required, so TypeScript catches the ones it can see, and what reaches
17
27
  * here is the value that was present but unusable — typically an identifier read off a request the
18
28
  * caller never validated.
19
29
  */
20
- declare function buildWordPressRequest(definition: WP_EndpointDefinition, input: object): WP_RequestBuild;
30
+ declare function buildWordPressRequest(definition: WP_EndpointDefinition, input: WP_RequestParts): WP_RequestBuild;
21
31
  declare function createWordPressClient<TEndpoints extends object>(transport: WordPressTransport, endpoints: TEndpoints): WP_Client<TEndpoints> & WordPressTransport;
22
32
  //#endregion
23
33
  export { buildWordPressRequest, createWordPressClient, wpEndpoint };
@@ -28,7 +28,17 @@ function describeValue(value) {
28
28
  return typeof value;
29
29
  }
30
30
  /**
31
- * Turn a definition and its input into a request: interpolate path parameters, then split the rest.
31
+ * Turn a definition and its input into a request: interpolate `params` into the path, put `query` on
32
+ * the URL, and forward `body` as it stands.
33
+ *
34
+ * Each part goes exactly where the caller put it, so nothing has to be inferred from the input's own
35
+ * keys. That matters because a field can legitimately be named `body` — `kizlo.email.send` takes one
36
+ * — and it is what lets a body method also carry query parameters, which the old method-based split
37
+ * could not express.
38
+ *
39
+ * Whether the request has a body at all is the definition's to answer: `requestContentType` is set
40
+ * for exactly those operations that declare one. A declared body left unsupplied is sent as an empty
41
+ * object rather than dropped, which is the shape a call with no fields has always sent.
32
42
  *
33
43
  * A path parameter that cannot be interpolated comes back as an error rather than throwing. The
34
44
  * generated input marks these required, so TypeScript catches the ones it can see, and what reaches
@@ -37,9 +47,9 @@ function describeValue(value) {
37
47
  */
38
48
  function buildWordPressRequest(definition, input) {
39
49
  let path = definition.path;
40
- const rest = { ...input };
50
+ const params = input.params ?? {};
41
51
  for (const name of definition.pathParameters) {
42
- const value = rest[name];
52
+ const value = params[name];
43
53
  if (!isUsablePathParameter(value)) return {
44
54
  request: null,
45
55
  error: new WP_Error({
@@ -50,22 +60,18 @@ function buildWordPressRequest(definition, input) {
50
60
  const encoded = encodeURIComponent(String(value));
51
61
  const marker = `{${name}}`;
52
62
  while (path.includes(marker)) path = path.replace(marker, encoded);
53
- delete rest[name];
54
63
  }
55
- const hasBody = [
56
- "POST",
57
- "PUT",
58
- "PATCH"
59
- ].includes(definition.method);
64
+ const hasBody = definition.requestContentType !== void 0;
60
65
  return {
61
66
  request: {
62
67
  base: `/wp-json/${definition.namespace}`,
63
68
  path,
64
69
  method: definition.method,
65
70
  ...hasBody ? {
66
- body: rest,
71
+ body: input.body ?? {},
67
72
  requestContentType: definition.requestContentType
68
- } : { searchParams: rest },
73
+ } : {},
74
+ ...input.query ? { searchParams: input.query } : {},
69
75
  responseContentTypes: definition.responseContentTypes,
70
76
  dataResponseStatuses: definition.dataResponseStatuses
71
77
  },
@@ -91,9 +97,15 @@ function createCaller(definition, transport) {
91
97
  headers: new Headers(),
92
98
  error: built.error
93
99
  };
100
+ const { searchParams,...rest } = options ?? {};
101
+ const query = searchParams ? {
102
+ ...built.request.searchParams,
103
+ ...searchParams
104
+ } : built.request.searchParams;
94
105
  return transport.request({
95
106
  ...built.request,
96
- ...options
107
+ ...rest,
108
+ ...query ? { searchParams: query } : {}
97
109
  });
98
110
  };
99
111
  }
@@ -215,10 +215,51 @@ function renderResult(apiId, operationId, operation, context) {
215
215
  * described.
216
216
  */
217
217
  const NO_INPUT = "Record<string, never>";
218
+ /**
219
+ * Whether anything inside an object schema has to be supplied, following `$extends` the way
220
+ * {@link inheritedPropertyTypes} does, since an inherited property is as required as an own one.
221
+ */
222
+ function hasRequiredMember(schema, context, seen = /* @__PURE__ */ new Set()) {
223
+ if (Object.values(schema.properties ?? {}).some((child) => child.required)) return true;
224
+ const parents = schema.$extends ? Array.isArray(schema.$extends) ? schema.$extends : [schema.$extends] : [];
225
+ for (const parent of parents) {
226
+ if (seen.has(parent)) continue;
227
+ seen.add(parent);
228
+ const parentSchema = context.schemas.get(parent);
229
+ if (parentSchema && hasRequiredMember(parentSchema, context, seen)) return true;
230
+ }
231
+ return false;
232
+ }
233
+ /**
234
+ * Whether a part must be passed. An object part is demanded only when something in it is required,
235
+ * so a list's `query` stays optional and a retrieve's `params` does not. Anything else — an array
236
+ * body, a scalar body — is the value itself rather than a container of fields, and has no way to be
237
+ * half-supplied, so passing it is the only way to send one.
238
+ */
239
+ function isRequiredPart(schema, context) {
240
+ return schema.type === "object" ? hasRequiredMember(schema, context) : schema.required !== false;
241
+ }
242
+ /**
243
+ * The parts an operation takes, each rendered under its own key. A part the operation does not
244
+ * declare is absent, and one whose contents are undescribed is dropped rather than rendered as an
245
+ * open record: an operation left with no part at all takes nothing, which is what {@link NO_INPUT}
246
+ * says.
247
+ */
218
248
  function renderInput(apiId, operationId, operation, context) {
219
249
  const name = endpointName(apiId, operationId, "EndpointInput");
220
- const input = renderType(operation.input, context);
221
- const declaration = input === UNDESCRIBED_OBJECT ? NO_INPUT : input;
250
+ const members = [];
251
+ for (const part of [
252
+ "params",
253
+ "query",
254
+ "body"
255
+ ]) {
256
+ const schema = operation.input[part];
257
+ if (!schema) continue;
258
+ const rendered = renderType(schema, context, " ");
259
+ if (rendered === UNDESCRIBED_OBJECT) continue;
260
+ members.push(`\t${part}${isRequiredPart(schema, context) ? "" : "?"}: ${rendered}`);
261
+ }
262
+ const declaration = members.length === 0 ? NO_INPUT : `{\n${members.join("\n")}\n}`;
222
263
  return `${jsdoc(operation.description ?? operation.summary, operation.deprecated)}export type ${name} = ${declaration}`;
223
264
  }
224
265
  function endpointTree(document) {
@@ -257,8 +298,13 @@ function sortedChildren(node) {
257
298
  function sortedEndpoints(node) {
258
299
  return [...node.endpoints].sort((left, right) => left.member.localeCompare(right.member));
259
300
  }
301
+ /**
302
+ * The runtime descriptor for one operation: what the generated client emits, and what anything calling
303
+ * a route straight off the introspection document needs to build its request. Exported because the
304
+ * CLI's MCP server dispatches routes the client never generated a member for.
305
+ */
260
306
  function endpointDefinition(entry) {
261
- const pathParameters = sortedEntries(entry.operation.input.properties ?? {}).filter(([, schema]) => schema.in === "path").map(([name]) => name);
307
+ const pathParameters = sortedEntries(entry.operation.input.params?.properties ?? {}).map(([name]) => name);
262
308
  const responseContentTypes = Object.fromEntries(sortedEntries(entry.operation.responses).map(([status, response]) => [status, response.content_type]));
263
309
  const dataResponseStatuses = sortedEntries(entry.operation.responses).filter(([status, response]) => !isSuccessStatus(status) && !isWordPressErrorResponse(response)).map(([status]) => status);
264
310
  return {
@@ -266,7 +312,7 @@ function endpointDefinition(entry) {
266
312
  path: entry.path,
267
313
  method: entry.operation.method,
268
314
  pathParameters,
269
- ...entry.operation.input.content_type ? { requestContentType: entry.operation.input.content_type } : {},
315
+ ...entry.operation.input.body?.content_type ? { requestContentType: entry.operation.input.body.content_type } : {},
270
316
  responseContentTypes,
271
317
  ...dataResponseStatuses.length > 0 ? { dataResponseStatuses } : {}
272
318
  };
@@ -319,12 +365,14 @@ function renderEndpointRegistry(node, path = []) {
319
365
  function renderCustomFieldsRegistry(document) {
320
366
  const entries = [];
321
367
  for (const id of Object.keys(document.schemas).sort((left, right) => left.localeCompare(right))) {
322
- const match = /^(post-types|taxonomies)\.([^.]+)\.item$/.exec(id);
368
+ const match = /^((?:[^.]+\.)*)(post-types|taxonomies)\.([^.]+)\.item$/.exec(id);
323
369
  if (!match) continue;
324
- const namespace = match[1] === "post-types" ? "postTypes" : "taxonomies";
325
- const slug = match[2];
370
+ if (!document.schemas[id]?.properties?.kizlo?.properties?.custom) continue;
371
+ const prefix = (match[1] ?? "").split(".").filter(Boolean).map((segment) => `${camel(segment)}.`).join("");
372
+ const namespace = match[2] === "post-types" ? "postTypes" : "taxonomies";
373
+ const slug = match[3];
326
374
  if (!slug) continue;
327
- entries.push(`\t\t${JSON.stringify(`${namespace}.${camel(slug)}`)}: ${schemaName(id)}["kizlo"]["custom"]`);
375
+ entries.push(`\t\t${JSON.stringify(`${prefix}${namespace}.${camel(slug)}`)}: ${schemaName(id)}["kizlo"]["custom"]`);
328
376
  }
329
377
  return entries;
330
378
  }
@@ -454,4 +502,4 @@ function generateWordPressModule(document) {
454
502
  }
455
503
 
456
504
  //#endregion
457
- export { generateWordPressModule };
505
+ export { camel, endpointDefinition, generateWordPressModule };