kizlo 0.2.0 → 0.2.2

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/README.md CHANGED
@@ -11,10 +11,10 @@
11
11
  </a>
12
12
  </p>
13
13
 
14
- <h3 align="center">Headless WordPress Toolkit</h3>
14
+ <h3 align="center">Headless WordPress Toolkit for TypeScript</h3>
15
15
 
16
16
  <p align="center">
17
- Your backend, your frontend — Kizlo handles the plumbing.
17
+ Develop, test, and ship from your machine with a fully typed RPC layer and WordPress API client.
18
18
  </p>
19
19
 
20
20
  <p align="center">
@@ -34,33 +34,29 @@
34
34
 
35
35
  ## What is Kizlo?
36
36
 
37
- Kizlo is a framework-agnostic toolkit for headless WordPress, built for a stack you host and own from top to bottom. Use it to build websites, mobile apps, agentic applications, and more — Kizlo takes care of the plumbing so you can focus on the design.
37
+ Kizlo is an open-source, typesafe toolkit for headless WordPress, built to sit behind any JavaScript framework as your backend or CMS. Its CLI spins up a local, Docker-based environment scoped to your project, giving you a fully typed RPC layer and WordPress API client to develop, test, and ship from your machine.
38
+
39
+ > Kizlo extends through integrations, whether that's a WordPress plugin like WooCommerce or an external service for auth, analytics, and more. Browse all available [integrations](https://kizlo.io/integrations).
38
40
 
39
41
  ## Quick start
40
42
 
41
43
  Set up Kizlo in your project:
42
44
 
43
45
  ```bash
44
- npx kizlo init
46
+ npx kizlo@latest init
45
47
  ```
46
48
 
47
- Watch your extensions and regenerate the contract during development:
49
+ Start the local WordPress stack and regenerate the contract as you work:
48
50
 
49
51
  ```bash
50
52
  npx kizlo dev
51
53
  ```
52
54
 
53
- Generate the contract once for production builds:
54
-
55
- ```bash
56
- npx kizlo generate
57
- ```
58
-
59
55
  See the [docs](https://kizlo.io/docs) for the full guide.
60
56
 
61
57
  ## Contributing
62
58
 
63
- Contributions are welcome — see [CONTRIBUTING.md](https://github.com/kizlo-io/kizlo/blob/main/CONTRIBUTING.md) for setup,
59
+ Contributions are welcome. See [CONTRIBUTING.md](https://github.com/kizlo-io/kizlo/blob/main/CONTRIBUTING.md) for setup,
64
60
  workflow, and PR guidelines.
65
61
 
66
62
  ## Security
@@ -2,6 +2,7 @@ import { createStack } from "../wp/docker.js";
2
2
  import { ensureGitignored, envGroups, getVersion, groupDefault, mergeEnv, pickStackPort, withSpinner } from "../utils.js";
3
3
  import { palette, printBanner } from "../banner.js";
4
4
  import { resolveDevConfig } from "../daemon/config.js";
5
+ import { startWatcher } from "../daemon/watch.js";
5
6
  import { bootstrapDev } from "../wp/dev.js";
6
7
  import { reapOrphans, registerSession, removeProjectContainers, spawnWatchdog, unregisterSession } from "../wp/session.js";
7
8
  import { syncSiteSettings } from "../wp/settings.js";
@@ -195,6 +196,8 @@ async function startForeground(cfg) {
195
196
  const took = ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
196
197
  process.stdout.write(`\n ${dim}Press ${reset}${bold}Ctrl+C${reset}${dim} (or close the terminal) to stop the development stack${reset}\n`);
197
198
  process.stdout.write(`\n ${green}✓${reset} Ready in ${took}\n\n`);
199
+ const stopWatcher = await startWatcher(ready.configDir);
200
+ if (stopWatcher) process.on("exit", stopWatcher);
198
201
  setInterval(() => {}, 1 << 30);
199
202
  await new Promise(() => {});
200
203
  }
@@ -2,12 +2,12 @@ import { composeStop, createStack, dockerAvailable } from "../wp/docker.js";
2
2
  import { addDependencyArgs, detectImportAlias, detectPackageManager, ensureGitignored, envGroups, envKeysPresent, getVersion, loadEnvFiles, mergeEnv, pickStackPort, resolveModuleImport, runCommand, writeFileIfAbsent } from "../utils.js";
3
3
  import { printBanner } from "../banner.js";
4
4
  import { DEFAULT_DEV_DB_PORT, DEFAULT_DEV_PORT, resolveStackName } from "../daemon/config.js";
5
+ import { CONTRACT_BARREL } from "../daemon/generate.js";
5
6
  import { createAdminAppPassword } from "../wp/bootstrap.js";
6
7
  import { bootstrapDev } from "../wp/dev.js";
7
8
  import { removeProjectContainers } from "../wp/session.js";
8
9
  import { syncSiteSettings } from "../wp/settings.js";
9
10
  import { devStack } from "../wp/stack.js";
10
- import { CONTRACT_BARREL } from "../daemon/generate.js";
11
11
  import { PRESETS, detectPreset, getPreset } from "../presets/index.js";
12
12
  import z from "zod/v4";
13
13
  import { randomBytes } from "node:crypto";
@@ -375,9 +375,9 @@ const init = defineCommand({
375
375
  if (!hasKizlo) {
376
376
  const s = p.spinner();
377
377
  s.start(`Installing kizlo with ${pm}`);
378
- const ok = runCommand(addDependencyArgs(pm, "kizlo"), cwd, "ignore");
378
+ const ok = runCommand(addDependencyArgs(pm, "kizlo@latest"), cwd, "ignore");
379
379
  s.stop(ok ? "Installed kizlo" : "Could not install kizlo automatically");
380
- if (!ok) p.log.warn(`Install it yourself: ${addDependencyArgs(pm, "kizlo").join(" ")}`);
380
+ if (!ok) p.log.warn(`Install it yourself: ${addDependencyArgs(pm, "kizlo@latest").join(" ")}`);
381
381
  }
382
382
  if (preset.apiPath && setup.baseUrl) setup.baseUrl = withApiPath(setup.baseUrl, preset.apiPath);
383
383
  let adminPassword;
@@ -487,13 +487,14 @@ const init = defineCommand({
487
487
  }
488
488
  p.note([
489
489
  ...setup.mode === "local" ? [
490
- `Start your local WordPress dev stack:`,
490
+ `Start your local WordPress dev stack (also watches your extensions):`,
491
491
  ` npx kizlo dev`,
492
492
  ``
493
- ] : [],
494
- `Watch your extensions and regenerate the contract during development:`,
495
- ` npx kizlo watch`,
496
- ``,
493
+ ] : [
494
+ `Watch your extensions and regenerate the contract during development:`,
495
+ ` npx kizlo watch`,
496
+ ``
497
+ ],
497
498
  `Generate the contract once for production builds:`,
498
499
  ` npx kizlo generate`
499
500
  ].join("\n"), "Next steps");
@@ -5,6 +5,7 @@ import { isFree } from "../wp/ports.js";
5
5
  import { groupDefault, pickStackPort, withSpinner } from "../utils.js";
6
6
  import { resolveTestConfig } from "../daemon/config.js";
7
7
  import { testStack } from "../wp/stack.js";
8
+ import { runPluginPhpunit } from "../wp/phpunit.js";
8
9
  import { runSeeds } from "../wp/setup.js";
9
10
  import { spawn } from "node:child_process";
10
11
  import { defineCommand } from "citty";
@@ -107,7 +108,9 @@ async function runSuite({ args }) {
107
108
  log.info(cfg.command ? `Running: ${cfg.command}` : `Running: ${testCommand(cfg.packageManager).join(" ")}`);
108
109
  let code = 1;
109
110
  try {
110
- code = await spawnTest(cfg.command, cfg.packageManager);
111
+ const jsCode = await spawnTest(cfg.command, cfg.packageManager);
112
+ const phpCode = await runPluginPhpunit(cfg);
113
+ code = jsCode !== 0 ? jsCode : phpCode;
111
114
  } finally {
112
115
  if (args.teardown) await withSpinner("Tearing down WordPress test stack", () => stack.composeDown(), "WordPress test stack stopped");
113
116
  else log.info("Stack left running for fast reruns — `kizlo test stop` to stop it, or rerun with --teardown.");
@@ -1,10 +1,6 @@
1
- import { log } from "../daemon/logger.js";
2
1
  import { getVersion } from "../utils.js";
3
2
  import { printBanner } from "../banner.js";
4
- import { resolveConfig } from "../daemon/config.js";
5
- import { generateOnce } from "../daemon/generate.js";
6
- import { acquire, isLocked, lockPath, release } from "../daemon/lock.js";
7
- import { watch as watch$1 } from "../daemon/watch.js";
3
+ import { startWatcher } from "../daemon/watch.js";
8
4
  import { defineCommand } from "citty";
9
5
 
10
6
  //#region src/cli/commands/watch.ts
@@ -13,32 +9,18 @@ const args = { dir: {
13
9
  description: "Override the Kizlo directory (defaults to kizlo.config.ts)"
14
10
  } };
15
11
  async function run({ args: args$1 }) {
16
- const cwd = process.cwd();
17
- const lock = lockPath(cwd);
18
- if (isLocked(lock)) {
19
- log.info("Watcher already running — nothing to do.");
20
- return;
21
- }
22
12
  printBanner(getVersion());
23
- const cfg = await resolveConfig(cwd, { dir: args$1.dir });
24
- acquire(lock);
25
- const cleanup = () => release(lock);
26
- process.on("exit", cleanup);
13
+ const stop = await startWatcher(process.cwd(), { dir: args$1.dir });
14
+ if (!stop) return;
15
+ process.on("exit", stop);
27
16
  for (const signal of [
28
17
  "SIGINT",
29
18
  "SIGTERM",
30
19
  "SIGHUP"
31
20
  ]) process.on(signal, () => {
32
- cleanup();
21
+ stop();
33
22
  process.exit(0);
34
23
  });
35
- try {
36
- if (await generateOnce(cfg)) log.success("Contract generated");
37
- else log.warn(`No Kizlo server found in ${cfg.serverEntry}`);
38
- } catch (error) {
39
- log.error("Failed to generate the Kizlo contract:", error);
40
- }
41
- await watch$1(cfg);
42
24
  }
43
25
  const watch = defineCommand({
44
26
  meta: {
@@ -3,6 +3,7 @@ import { consola } from "consola";
3
3
  //#region src/cli/daemon/logger.ts
4
4
  /** Tagged logger for the daemon — every line is prefixed with `kizlo`. */
5
5
  const log = consola.withTag("kizlo");
6
+ log.options.formatOptions.date = false;
6
7
 
7
8
  //#endregion
8
9
  export { log };
@@ -1,5 +1,7 @@
1
1
  import { log } from "./logger.js";
2
+ import { resolveConfig } from "./config.js";
2
3
  import { generateOnce } from "./generate.js";
4
+ import { acquire, isLocked, lockPath, release } from "./lock.js";
3
5
  import path from "node:path";
4
6
  import { FSWatcher } from "chokidar";
5
7
 
@@ -28,10 +30,41 @@ async function watch(cfg) {
28
30
  });
29
31
  const onChange = debounce(() => regenerate(cfg), 300);
30
32
  watcher.add(path.resolve(cfg.cwd, cfg.serverDir));
31
- watcher.on("ready", () => log.start("Watching for changes..."));
33
+ watcher.on("ready", () => log.start("Watching for contract changes..."));
32
34
  watcher.on("all", () => void onChange());
33
35
  return watcher;
34
36
  }
37
+ /**
38
+ * Acquire the single-instance lock, generate the contract once, and start the file
39
+ * watcher. Returns a synchronous `stop()` that closes the watcher and releases the
40
+ * lock — or `undefined` when another watcher already holds the lock (a standalone
41
+ * `kizlo watch`, or a framework dev script), in which case the caller carries on
42
+ * without watching. Shared by the standalone `kizlo watch` command and the
43
+ * folded-in watcher of `kizlo dev`, so a single terminal covers both.
44
+ */
45
+ async function startWatcher(cwd, opts) {
46
+ const lock = lockPath(cwd);
47
+ if (isLocked(lock)) {
48
+ log.info("Watcher already running — skipping the contract watcher.");
49
+ return;
50
+ }
51
+ const cfg = await resolveConfig(cwd, { dir: opts?.dir });
52
+ acquire(lock);
53
+ try {
54
+ if (await generateOnce(cfg)) log.success("Contract generated");
55
+ else log.warn(`No Kizlo server found in ${cfg.serverEntry}`);
56
+ } catch (error) {
57
+ log.error("Failed to generate the Kizlo contract:", error);
58
+ }
59
+ const watcher = await watch(cfg);
60
+ let stopped = false;
61
+ return () => {
62
+ if (stopped) return;
63
+ stopped = true;
64
+ release(lock);
65
+ watcher.close();
66
+ };
67
+ }
35
68
 
36
69
  //#endregion
37
- export { watch };
70
+ export { startWatcher };
@@ -0,0 +1,104 @@
1
+ import { isLocalPlugin } from "./types.js";
2
+ import { compose } from "./docker.js";
3
+ import { log } from "../daemon/logger.js";
4
+ import { existsSync } from "node:fs";
5
+ import { basename, isAbsolute, resolve } from "node:path";
6
+
7
+ //#region src/cli/wp/phpunit.ts
8
+ const PLUGINS_DIR = `/var/www/html/wp-content/plugins`;
9
+ /** Isolated database the PHP suite runs against — never the served site's DB. */
10
+ const TEST_DB = "wordpress_test";
11
+ /** Root creds from the compose file, used only to create the DB and grant access. */
12
+ const MYSQL_ROOT_PASSWORD = "rootpass";
13
+ /** The app user the served site already connects as; the suite reuses it. */
14
+ const MYSQL_USER = "wordpress";
15
+ /**
16
+ * The bind-mounted local plugins that carry a `phpunit.xml` (or `.dist`), resolved
17
+ * to their in-container plugin dir. Checked on the host (where the source lives),
18
+ * run in the container (where WordPress lives) — so this works identically for a
19
+ * consumer whose own plugin is mounted while kizlo comes from a release.
20
+ */
21
+ function phpunitTargets(cfg) {
22
+ const seen = /* @__PURE__ */ new Set();
23
+ const targets = [];
24
+ for (const plugin of cfg.fixtures.flatMap((fixture) => fixture.plugins ?? [])) {
25
+ if (!isLocalPlugin(plugin)) continue;
26
+ const hostDir = isAbsolute(plugin.path) ? plugin.path : resolve(cfg.configDir, plugin.path);
27
+ const slug = basename(hostDir);
28
+ if (seen.has(slug)) continue;
29
+ seen.add(slug);
30
+ if (!(existsSync(resolve(hostDir, "phpunit.xml")) || existsSync(resolve(hostDir, "phpunit.xml.dist")))) continue;
31
+ if (!existsSync(resolve(hostDir, "vendor/bin/phpunit"))) {
32
+ log.warn(`${slug} has a phpunit config but no vendor/bin/phpunit — run \`composer install\` with dev dependencies to run its PHP tests. Skipping.`);
33
+ continue;
34
+ }
35
+ targets.push({
36
+ slug,
37
+ containerDir: `${PLUGINS_DIR}/${slug}`
38
+ });
39
+ }
40
+ return targets;
41
+ }
42
+ /**
43
+ * Create the isolated `wordpress_test` database in the `mysql` service and grant the
44
+ * app user full access to it. Idempotent (`IF NOT EXISTS` + a re-runnable grant), so
45
+ * it can run on every `kizlo test` next to seeding without a guard.
46
+ */
47
+ async function provisionTestDb() {
48
+ const sql = [
49
+ `CREATE DATABASE IF NOT EXISTS ${TEST_DB};`,
50
+ `GRANT ALL PRIVILEGES ON ${TEST_DB}.* TO '${MYSQL_USER}'@'%';`,
51
+ "FLUSH PRIVILEGES;"
52
+ ].join(" ");
53
+ const res = await compose([
54
+ "exec",
55
+ "-T",
56
+ "mysql",
57
+ "mysql",
58
+ `-uroot`,
59
+ `-p${MYSQL_ROOT_PASSWORD}`,
60
+ "-e",
61
+ sql
62
+ ]);
63
+ if (res.code !== 0) throw new Error(`Failed to provision the ${TEST_DB} database:\n${res.stderr || res.stdout}`);
64
+ }
65
+ /** Run one plugin's phpunit suite inside the `wordpress` container; resolve its exit code. */
66
+ async function runTarget(target) {
67
+ const wpPhpunitDir = `${target.containerDir}/vendor/wp-phpunit/wp-phpunit`;
68
+ const res = await compose([
69
+ "exec",
70
+ "-T",
71
+ "-w",
72
+ target.containerDir,
73
+ "-e",
74
+ `WP_PHPUNIT__DIR=${wpPhpunitDir}`,
75
+ "wordpress",
76
+ "php",
77
+ "vendor/bin/phpunit",
78
+ "--colors=always"
79
+ ]);
80
+ process.stdout.write(res.stdout);
81
+ if (res.stderr) process.stderr.write(res.stderr);
82
+ return res.code;
83
+ }
84
+ /**
85
+ * Provision the test DB, then run phpunit for every bind-mounted local plugin that
86
+ * ships a phpunit config. Returns the worst exit code (0 when nothing to run), so the
87
+ * caller can fold PHP results into the overall `kizlo test` outcome. Plugin-agnostic:
88
+ * it never hardcodes `plugins/kizlo`.
89
+ */
90
+ async function runPluginPhpunit(cfg) {
91
+ const targets = phpunitTargets(cfg);
92
+ if (targets.length === 0) return 0;
93
+ await provisionTestDb();
94
+ let worst = 0;
95
+ for (const target of targets) {
96
+ log.info(`Running PHP tests for ${target.slug}…`);
97
+ const code = await runTarget(target);
98
+ if (code !== 0) worst = code;
99
+ }
100
+ return worst;
101
+ }
102
+
103
+ //#endregion
104
+ export { runPluginPhpunit };
package/dist/index.d.ts CHANGED
@@ -17,6 +17,9 @@ import "./wordpress/index.js";
17
17
  import { DevPluginSource, Fixture, FixtureCleanupFn, FixtureSeedFn, JsonValue, LocalPlugin, PluginSource, SeedContext, TestAdminUser, TestCredentials, TestUser, defineFixture, isLocalPlugin } from "./cli/wp/types.js";
18
18
  import { AuthAdapter, AuthGetUser, AuthUser, authMock, createAuthAdapter } from "./adapters/auth.js";
19
19
  import { CaptchaAdapter, altcha, arkose, createCaptchaAdapter, friendlyCaptcha, hcaptcha, recaptcha, turnstile } from "./adapters/captcha.js";
20
+ import { CONTENT_TYPES, ContentType, ListSitemapUrlInput, ListSitemapUrlInputBase, Robots, RobotsRule, Seo, SeoHead, SeoSchema, Sitemap, SitemapList, SitemapUrl, SitemapUrlImage, SitemapUrlList } from "./seo/schema.js";
21
+ import { WPK_ListSitemapUrlInput, WPK_RobotRule, WPK_Robots, WPK_Seo, WPK_Sitemap, WPK_SitemapContentType, WPK_SitemapUrl, WPK_SitemapUrlImage } from "./seo/types.js";
22
+ import { SitemapIndexEntry } from "./seo/utils.js";
20
23
  import { CurrencyFormat, IdentifierInput, LIST_ORDER, ListMetadata, ListOrder, Media, PreviewTokenData, PreviewTokenPayload } from "./shared/schema.js";
21
24
  import { AnyContext, AnyNestedClient, CookiesAdapter, CookiesDeleteAll, CookiesGetAll, CookiesSetAll, HTTPMethod, List, MaybeOptionalOptions, RedirectInput, RedirectStatus } from "./shared/types.js";
22
25
  import { createCookiesAdapter } from "./adapters/cookies.js";
@@ -34,11 +37,10 @@ import { AuthUserFn, ConnInfoFn, Context, ContextConfig, ServerContext, VerifyCa
34
37
  import { AnyMiddleware, ExtractMiddlewareContextOutput, InferUses, Middleware, MiddlewareHandler, MiddlewareNextFn, MiddlewareOptions, MiddlewareResult, createMiddleware } from "./shared/middleware.js";
35
38
  import { AnyProcedure, AnyProcedureRouter, BaseHandlerOptions, ExtractParams, ExtractProcedureByScope, InvocationScope, OpenapiPart, Procedure, ProcedureContext, ProcedureErrors, ProcedureHandler, ProcedureOptions, ResolveParamsPart, ResolvedSchemaInput, ResolvedSchemaOutput, RouterDepthTuple, SchemaIO, buildProcedure, combineDetailed, createProcedure, handleUnexpectedError, schemaType } from "./shared/procedure.js";
36
39
  import { KizloClient, KizloClientConfig, createKizloClient } from "./client.js";
37
- import { CONTENT_TYPES, ContentType, ListSitemapUrlInput, ListSitemapUrlInputBase, Robots, RobotsRule, Seo, SeoHead, SeoSchema, Sitemap, SitemapList, SitemapUrl, SitemapUrlImage, SitemapUrlList } from "./seo/schema.js";
38
- import { WPK_ListSitemapUrlInput, WPK_RobotRule, WPK_Robots, WPK_Seo, WPK_Sitemap, WPK_SitemapContentType, WPK_SitemapUrl, WPK_SitemapUrlImage } from "./seo/types.js";
40
+ import { CreateSitemapRouteOptions, SITEMAP_BASE, SITEMAP_INDEX_SLUG, SitemapRouteExtra, createSitemapRoute } from "./seo/sitemap.js";
39
41
  import { Contract, ContractProcedure, generateContract, isContractProcedure, restoreContract } from "./shared/contract.js";
40
42
  import { compare, hash, hmac } from "./shared/crypto.js";
41
43
  import { Identifier, parseIdentifier } from "./shared/identifier.js";
42
44
  import { createOrpcRouter } from "./shared/router.js";
43
45
  import { deserializeCurrencyFormat, deserializeListMetadata, emptyList, serializeCurrencyFormat } from "./shared/serialize.js";
44
- export { AnyContext, AnyEventSchema, AnyEvents, AnyExtension, AnyKizloConfig, AnyMiddleware, AnyNestedClient, AnyProcedure, AnyProcedureRouter, AuthAdapter, AuthGetUser, AuthUser, AuthUserFn, BaseHandlerOptions, CART_HASH_HEADER_KEY, COMMON_ERRORS, CONTENT_TYPES, CaptchaAdapter, CommentService, CommonErrorCode, CommonErrorLiteral, CommonErrorMap, ConnInfo, ConnInfoFn, ConsoleLogAdapterOptions, ContentType, Context, ContextConfig, Contract, ContractProcedure, CookiesAdapter, CookiesDeleteAll, CookiesGetAll, CookiesSetAll, CreateKizloOptions, CurrencyFormat, DefinedErrorMapLike, DevPluginSource, Environment, ErrorDefinition, EventHandler, EventHandlerFn, Extension, ExtensionInitFn, ExtensionInitOptions, ExtensionInitResult, ExtractExtensionRouter, ExtractMiddlewareContextOutput, ExtractParams, ExtractProcedureByScope, Fixture, FixtureCleanupFn, FixtureSeedFn, GeoAdapter, GeoGetConnInfo, HTTPMethod, Identifier, IdentifierInput, InferEventUnion, InferExtensionRouter, InferUses, InvocationScope, JsonValue, Kizlo, KizloAuthorsSettings, KizloAuthorsSettingsInput, KizloClient, KizloClientConfig, KizloConfig, KizloCrawlingSettings, KizloCrawlingSettingsInput, KizloError, KizloErrorOptions, KizloIdentitySettings, KizloIdentitySettingsInput, KizloMedia, KizloOrganizationFounder, KizloOrganizationSettings, KizloOrganizationSettingsInput, KizloPersonSettings, KizloPersonSettingsInput, KizloPostStatus, KizloPostTypeSettings, KizloPostTypeSettingsInput, KizloSettings, KizloSettingsService, KizloSiteSettings, KizloSiteSettingsInput, KizloSocialProfile, KizloTarget, KizloTaxonomySettings, KizloTaxonomySettingsInput, KizloWebhookSettings, KizloWebhookSettingsInput, LIST_ORDER, List, ListMetadata, ListOrder, ListSitemapUrlInput, ListSitemapUrlInputBase, LocalPlugin, LogLevel, LogPayload, Logger, LoggerAdapter, MaybeOptionalOptions, Media, MenuItemService, MenuService, Middleware, MiddlewareHandler, MiddlewareNextFn, MiddlewareOptions, MiddlewareResult, OpenapiPart, PluginSource, PostService, PreviewTokenData, PreviewTokenPayload, Procedure, ProcedureContext, ProcedureErrors, ProcedureHandler, ProcedureOptions, RedirectInput, RedirectStatus, ResolveParamsPart, ResolvedSchemaInput, ResolvedSchemaOutput, Robots, RobotsRule, RootRouter, RouterDepthTuple, S2SClient, SAFE_REQUEST_TIMEOUT, SchemaIO, SeedContext, Seo, SeoHead, SeoSchema, ServerContext, Service, ServiceAdapters, ServiceConfig, Sitemap, SitemapList, SitemapUrl, SitemapUrlImage, SitemapUrlList, TestAdminUser, TestCredentials, TestUser, ThrowableErrorFn, ThrowableErrorMap, UNEXPECTED_BODY_SNIPPET_LENGTH, UserService, VerifyCaptchaFn, VerifyPreviewTokenFn, WC_CORE_BASE, WC_STORE_BASE, WPK_ListSitemapUrlInput, WPK_RobotRule, WPK_Robots, WPK_Seo, WPK_Sitemap, WPK_SitemapContentType, WPK_SitemapUrl, WPK_SitemapUrlImage, WP_AUTH_HEADER_KEY, WP_AUTH_TYPE, WP_CORE_BASE, WP_Comment, WP_CommentAvatarUrls, WP_CommentContent, WP_CommentContentInput, WP_CommentCreateErrorCode, WP_CommentCreateInput, WP_CommentDeleteErrorCode, WP_CommentDeleteInput, WP_CommentDeleteResponse, WP_CommentLinks, WP_CommentListErrorCode, WP_CommentListInput, WP_CommentOrderBy, WP_CommentRetrieveErrorCode, WP_CommentRetrieveInput, WP_CommentStatus, WP_CommentStatusFilter, WP_CommentStatusInput, WP_CommentType, WP_CommentUpdateErrorCode, WP_CommentUpdateInput, WP_CommonErrorCode, WP_Context, WP_CurrencyFormat, WP_Error, WP_ErrorData, WP_KIZLO_BASE, WP_Link, WP_List, WP_ListMetadata, WP_ListOrder, WP_Menu, WP_MenuCreateErrorCode, WP_MenuCreateInput, WP_MenuDeleteErrorCode, WP_MenuDeleteInput, WP_MenuDeleteResponse, WP_MenuItem, WP_MenuItemCreateErrorCode, WP_MenuItemCreateInput, WP_MenuItemDeleteErrorCode, WP_MenuItemDeleteInput, WP_MenuItemDeleteResponse, WP_MenuItemLinks, WP_MenuItemListErrorCode, WP_MenuItemListInput, WP_MenuItemOrderBy, WP_MenuItemRetrieveErrorCode, WP_MenuItemRetrieveInput, WP_MenuItemStatus, WP_MenuItemStatusFilter, WP_MenuItemTarget, WP_MenuItemTaxRelation, WP_MenuItemTitle, WP_MenuItemTitleInput, WP_MenuItemType, WP_MenuItemUpdateErrorCode, WP_MenuItemUpdateInput, WP_MenuLinks, WP_MenuListErrorCode, WP_MenuListInput, WP_MenuOrderBy, WP_MenuRetrieveErrorCode, WP_MenuRetrieveInput, WP_MenuUpdateErrorCode, WP_MenuUpdateInput, WP_Post, WP_PostCommentStatus, WP_PostContent, WP_PostContentInput, WP_PostCreateErrorCode, WP_PostCreateInput, WP_PostDeleteErrorCode, WP_PostDeleteInput, WP_PostDeleteResponse, WP_PostExcerpt, WP_PostExcerptInput, WP_PostFormat, WP_PostGuid, WP_PostLinks, WP_PostListErrorCode, WP_PostListInput, WP_PostOrderBy, WP_PostRetrieveErrorCode, WP_PostRetrieveInput, WP_PostStatus, WP_PostStatusFilter, WP_PostTaxRelation, WP_PostTitle, WP_PostTitleInput, WP_PostUpdateErrorCode, WP_PostUpdateInput, WP_RequestInput, WP_Result, WP_User, WP_UserAvatarUrls, WP_UserCapabilities, WP_UserCreateErrorCode, WP_UserCreateInput, WP_UserDeleteErrorCode, WP_UserDeleteInput, WP_UserDeleteMeErrorCode, WP_UserDeleteMeInput, WP_UserDeleteMeResponse, WP_UserDeleteResponse, WP_UserLinks, WP_UserListErrorCode, WP_UserListInput, WP_UserOrderBy, WP_UserRetrieveErrorCode, WP_UserRetrieveInput, WP_UserRetrieveMeErrorCode, WP_UserRetrieveMeInput, WP_UserUpdateErrorCode, WP_UserUpdateInput, WP_UserUpdateMeErrorCode, WP_UserUpdateMeInput, WP_UserWho, WebhookOptions, WithCommonErrorMap, WordPressCredentials, WordPressService, altcha, arkose, authMock, buildProcedure, combineDetailed, compare, consoleLog, createAuthAdapter, createCaptchaAdapter, createCookiesAdapter, createEventHandler, createExtension, createGeoAdapter, createKizlo, createKizloClient, createLoggerAdapter, createMiddleware, createOrpcRouter, createProcedure, createThrowableErrorMap, createWebhookRouter, defineErrorMap, defineFixture, deserializeCurrencyFormat, deserializeListMetadata, emptyList, friendlyCaptcha, generateContract, geoMock, geoVercelNext, handleUnexpectedError, hash, hcaptcha, hmac, isContractProcedure, isKizloError, isLocalPlugin, isWordPressResourceError, noopAdapter, parseIdentifier, recaptcha, resolveKizloConfig, restoreContract, schemaType, serializeCurrencyFormat, toKizloError, turnstile };
46
+ export { AnyContext, AnyEventSchema, AnyEvents, AnyExtension, AnyKizloConfig, AnyMiddleware, AnyNestedClient, AnyProcedure, AnyProcedureRouter, AuthAdapter, AuthGetUser, AuthUser, AuthUserFn, BaseHandlerOptions, CART_HASH_HEADER_KEY, COMMON_ERRORS, CONTENT_TYPES, CaptchaAdapter, CommentService, CommonErrorCode, CommonErrorLiteral, CommonErrorMap, ConnInfo, ConnInfoFn, ConsoleLogAdapterOptions, ContentType, Context, ContextConfig, Contract, ContractProcedure, CookiesAdapter, CookiesDeleteAll, CookiesGetAll, CookiesSetAll, CreateKizloOptions, CreateSitemapRouteOptions, CurrencyFormat, DefinedErrorMapLike, DevPluginSource, Environment, ErrorDefinition, EventHandler, EventHandlerFn, Extension, ExtensionInitFn, ExtensionInitOptions, ExtensionInitResult, ExtractExtensionRouter, ExtractMiddlewareContextOutput, ExtractParams, ExtractProcedureByScope, Fixture, FixtureCleanupFn, FixtureSeedFn, GeoAdapter, GeoGetConnInfo, HTTPMethod, Identifier, IdentifierInput, InferEventUnion, InferExtensionRouter, InferUses, InvocationScope, JsonValue, Kizlo, KizloAuthorsSettings, KizloAuthorsSettingsInput, KizloClient, KizloClientConfig, KizloConfig, KizloCrawlingSettings, KizloCrawlingSettingsInput, KizloError, KizloErrorOptions, KizloIdentitySettings, KizloIdentitySettingsInput, KizloMedia, KizloOrganizationFounder, KizloOrganizationSettings, KizloOrganizationSettingsInput, KizloPersonSettings, KizloPersonSettingsInput, KizloPostStatus, KizloPostTypeSettings, KizloPostTypeSettingsInput, KizloSettings, KizloSettingsService, KizloSiteSettings, KizloSiteSettingsInput, KizloSocialProfile, KizloTarget, KizloTaxonomySettings, KizloTaxonomySettingsInput, KizloWebhookSettings, KizloWebhookSettingsInput, LIST_ORDER, List, ListMetadata, ListOrder, ListSitemapUrlInput, ListSitemapUrlInputBase, LocalPlugin, LogLevel, LogPayload, Logger, LoggerAdapter, MaybeOptionalOptions, Media, MenuItemService, MenuService, Middleware, MiddlewareHandler, MiddlewareNextFn, MiddlewareOptions, MiddlewareResult, OpenapiPart, PluginSource, PostService, PreviewTokenData, PreviewTokenPayload, Procedure, ProcedureContext, ProcedureErrors, ProcedureHandler, ProcedureOptions, RedirectInput, RedirectStatus, ResolveParamsPart, ResolvedSchemaInput, ResolvedSchemaOutput, Robots, RobotsRule, RootRouter, RouterDepthTuple, S2SClient, SAFE_REQUEST_TIMEOUT, SITEMAP_BASE, SITEMAP_INDEX_SLUG, SchemaIO, SeedContext, Seo, SeoHead, SeoSchema, ServerContext, Service, ServiceAdapters, ServiceConfig, Sitemap, type SitemapIndexEntry, SitemapList, SitemapRouteExtra, SitemapUrl, SitemapUrlImage, SitemapUrlList, TestAdminUser, TestCredentials, TestUser, ThrowableErrorFn, ThrowableErrorMap, UNEXPECTED_BODY_SNIPPET_LENGTH, UserService, VerifyCaptchaFn, VerifyPreviewTokenFn, WC_CORE_BASE, WC_STORE_BASE, WPK_ListSitemapUrlInput, WPK_RobotRule, WPK_Robots, WPK_Seo, WPK_Sitemap, WPK_SitemapContentType, WPK_SitemapUrl, WPK_SitemapUrlImage, WP_AUTH_HEADER_KEY, WP_AUTH_TYPE, WP_CORE_BASE, WP_Comment, WP_CommentAvatarUrls, WP_CommentContent, WP_CommentContentInput, WP_CommentCreateErrorCode, WP_CommentCreateInput, WP_CommentDeleteErrorCode, WP_CommentDeleteInput, WP_CommentDeleteResponse, WP_CommentLinks, WP_CommentListErrorCode, WP_CommentListInput, WP_CommentOrderBy, WP_CommentRetrieveErrorCode, WP_CommentRetrieveInput, WP_CommentStatus, WP_CommentStatusFilter, WP_CommentStatusInput, WP_CommentType, WP_CommentUpdateErrorCode, WP_CommentUpdateInput, WP_CommonErrorCode, WP_Context, WP_CurrencyFormat, WP_Error, WP_ErrorData, WP_KIZLO_BASE, WP_Link, WP_List, WP_ListMetadata, WP_ListOrder, WP_Menu, WP_MenuCreateErrorCode, WP_MenuCreateInput, WP_MenuDeleteErrorCode, WP_MenuDeleteInput, WP_MenuDeleteResponse, WP_MenuItem, WP_MenuItemCreateErrorCode, WP_MenuItemCreateInput, WP_MenuItemDeleteErrorCode, WP_MenuItemDeleteInput, WP_MenuItemDeleteResponse, WP_MenuItemLinks, WP_MenuItemListErrorCode, WP_MenuItemListInput, WP_MenuItemOrderBy, WP_MenuItemRetrieveErrorCode, WP_MenuItemRetrieveInput, WP_MenuItemStatus, WP_MenuItemStatusFilter, WP_MenuItemTarget, WP_MenuItemTaxRelation, WP_MenuItemTitle, WP_MenuItemTitleInput, WP_MenuItemType, WP_MenuItemUpdateErrorCode, WP_MenuItemUpdateInput, WP_MenuLinks, WP_MenuListErrorCode, WP_MenuListInput, WP_MenuOrderBy, WP_MenuRetrieveErrorCode, WP_MenuRetrieveInput, WP_MenuUpdateErrorCode, WP_MenuUpdateInput, WP_Post, WP_PostCommentStatus, WP_PostContent, WP_PostContentInput, WP_PostCreateErrorCode, WP_PostCreateInput, WP_PostDeleteErrorCode, WP_PostDeleteInput, WP_PostDeleteResponse, WP_PostExcerpt, WP_PostExcerptInput, WP_PostFormat, WP_PostGuid, WP_PostLinks, WP_PostListErrorCode, WP_PostListInput, WP_PostOrderBy, WP_PostRetrieveErrorCode, WP_PostRetrieveInput, WP_PostStatus, WP_PostStatusFilter, WP_PostTaxRelation, WP_PostTitle, WP_PostTitleInput, WP_PostUpdateErrorCode, WP_PostUpdateInput, WP_RequestInput, WP_Result, WP_User, WP_UserAvatarUrls, WP_UserCapabilities, WP_UserCreateErrorCode, WP_UserCreateInput, WP_UserDeleteErrorCode, WP_UserDeleteInput, WP_UserDeleteMeErrorCode, WP_UserDeleteMeInput, WP_UserDeleteMeResponse, WP_UserDeleteResponse, WP_UserLinks, WP_UserListErrorCode, WP_UserListInput, WP_UserOrderBy, WP_UserRetrieveErrorCode, WP_UserRetrieveInput, WP_UserRetrieveMeErrorCode, WP_UserRetrieveMeInput, WP_UserUpdateErrorCode, WP_UserUpdateInput, WP_UserUpdateMeErrorCode, WP_UserUpdateMeInput, WP_UserWho, WebhookOptions, WithCommonErrorMap, WordPressCredentials, WordPressService, altcha, arkose, authMock, buildProcedure, combineDetailed, compare, consoleLog, createAuthAdapter, createCaptchaAdapter, createCookiesAdapter, createEventHandler, createExtension, createGeoAdapter, createKizlo, createKizloClient, createLoggerAdapter, createMiddleware, createOrpcRouter, createProcedure, createSitemapRoute, createThrowableErrorMap, createWebhookRouter, defineErrorMap, defineFixture, deserializeCurrencyFormat, deserializeListMetadata, emptyList, friendlyCaptcha, generateContract, geoMock, geoVercelNext, handleUnexpectedError, hash, hcaptcha, hmac, isContractProcedure, isKizloError, isLocalPlugin, isWordPressResourceError, noopAdapter, parseIdentifier, recaptcha, resolveKizloConfig, restoreContract, schemaType, serializeCurrencyFormat, toKizloError, turnstile };
package/dist/index.js CHANGED
@@ -29,7 +29,8 @@ import { parseIdentifier } from "./shared/identifier.js";
29
29
  import { CONTENT_TYPES, ContentType, ListSitemapUrlInput, ListSitemapUrlInputBase, Robots, RobotsRule, Seo, SeoHead, SeoSchema, Sitemap, SitemapList, SitemapUrl, SitemapUrlImage, SitemapUrlList } from "./seo/schema.js";
30
30
  import { createEventHandler, createWebhookRouter } from "./webhook/index.js";
31
31
  import { Kizlo, createKizlo, resolveKizloConfig } from "./kizlo.js";
32
+ import { SITEMAP_BASE, SITEMAP_INDEX_SLUG, createSitemapRoute } from "./seo/sitemap.js";
32
33
  import { createExtension } from "./shared/extension.js";
33
34
  import { createMiddleware } from "./shared/middleware.js";
34
35
 
35
- export { CART_HASH_HEADER_KEY, COMMON_ERRORS, CONTENT_TYPES, CommentService, ContentType, Context, CurrencyFormat, IdentifierInput, Kizlo, KizloClient, KizloError, KizloSettingsService, LIST_ORDER, ListMetadata, ListOrder, ListSitemapUrlInput, ListSitemapUrlInputBase, Media, MenuItemService, MenuService, PostService, PreviewTokenData, PreviewTokenPayload, Robots, RobotsRule, SAFE_REQUEST_TIMEOUT, Seo, SeoHead, SeoSchema, Service, Sitemap, SitemapList, SitemapUrl, SitemapUrlImage, SitemapUrlList, UNEXPECTED_BODY_SNIPPET_LENGTH, UserService, WC_CORE_BASE, WC_STORE_BASE, WP_AUTH_HEADER_KEY, WP_AUTH_TYPE, WP_CORE_BASE, WP_Error, WP_KIZLO_BASE, WordPressService, altcha, arkose, authMock, buildProcedure, combineDetailed, compare, consoleLog, createAuthAdapter, createCaptchaAdapter, createCookiesAdapter, createEventHandler, createExtension, createGeoAdapter, createKizlo, createKizloClient, createLoggerAdapter, createMiddleware, createOrpcRouter, createProcedure, createThrowableErrorMap, createWebhookRouter, defineErrorMap, defineFixture, deserializeCurrencyFormat, deserializeListMetadata, emptyList, friendlyCaptcha, generateContract, geoMock, geoVercelNext, handleUnexpectedError, hash, hcaptcha, hmac, isContractProcedure, isKizloError, isLocalPlugin, isWordPressResourceError, noopAdapter, parseIdentifier, recaptcha, resolveKizloConfig, restoreContract, schemaType, serializeCurrencyFormat, toKizloError, turnstile };
36
+ export { CART_HASH_HEADER_KEY, COMMON_ERRORS, CONTENT_TYPES, CommentService, ContentType, Context, CurrencyFormat, IdentifierInput, Kizlo, KizloClient, KizloError, KizloSettingsService, LIST_ORDER, ListMetadata, ListOrder, ListSitemapUrlInput, ListSitemapUrlInputBase, Media, MenuItemService, MenuService, PostService, PreviewTokenData, PreviewTokenPayload, Robots, RobotsRule, SAFE_REQUEST_TIMEOUT, SITEMAP_BASE, SITEMAP_INDEX_SLUG, Seo, SeoHead, SeoSchema, Service, Sitemap, SitemapList, SitemapUrl, SitemapUrlImage, SitemapUrlList, UNEXPECTED_BODY_SNIPPET_LENGTH, UserService, WC_CORE_BASE, WC_STORE_BASE, WP_AUTH_HEADER_KEY, WP_AUTH_TYPE, WP_CORE_BASE, WP_Error, WP_KIZLO_BASE, WordPressService, altcha, arkose, authMock, buildProcedure, combineDetailed, compare, consoleLog, createAuthAdapter, createCaptchaAdapter, createCookiesAdapter, createEventHandler, createExtension, createGeoAdapter, createKizlo, createKizloClient, createLoggerAdapter, createMiddleware, createOrpcRouter, createProcedure, createSitemapRoute, createThrowableErrorMap, createWebhookRouter, defineErrorMap, defineFixture, deserializeCurrencyFormat, deserializeListMetadata, emptyList, friendlyCaptcha, generateContract, geoMock, geoVercelNext, handleUnexpectedError, hash, hcaptcha, hmac, isContractProcedure, isKizloError, isLocalPlugin, isWordPressResourceError, noopAdapter, parseIdentifier, recaptcha, resolveKizloConfig, restoreContract, schemaType, serializeCurrencyFormat, toKizloError, turnstile };
@@ -1,12 +1,11 @@
1
1
  import { AnyExtension } from "../../shared/extension.js";
2
2
  import { CreateKizloOptions, Kizlo } from "../../kizlo.js";
3
+ import { NextRevalidateOptions } from "./revalidate.js";
3
4
 
4
5
  //#region src/integrations/nextjs/kizlo.server.d.ts
5
- type KizloServerOptions<TExts extends readonly AnyExtension[] = []> = CreateKizloOptions<TExts>;
6
- /**
7
- * Creates a Kizlo server for Next.js — same as the base `createKizlo`, but reads
8
- * the public base URL from `NEXT_PUBLIC_KIZLO_BACKEND_URL`.
9
- */
10
- declare function createKizlo<TExts extends readonly AnyExtension[] = []>(options?: CreateKizloOptions<TExts>): Kizlo<TExts>;
6
+ type KizloServerOptions<TExts extends readonly AnyExtension[] = []> = CreateKizloOptions<TExts> & {
7
+ revalidate?: boolean | NextRevalidateOptions;
8
+ };
9
+ declare function createKizlo<TExts extends readonly AnyExtension[] = []>(options?: KizloServerOptions<TExts>): Kizlo<TExts>;
11
10
  //#endregion
12
11
  export { KizloServerOptions, createKizlo };
@@ -1,12 +1,14 @@
1
1
  import { Kizlo, resolveKizloConfig } from "../../kizlo.js";
2
+ import { nextRevalidation } from "./revalidate.js";
2
3
 
3
4
  //#region src/integrations/nextjs/kizlo.server.ts
4
- /**
5
- * Creates a Kizlo server for Next.js — same as the base `createKizlo`, but reads
6
- * the public base URL from `NEXT_PUBLIC_KIZLO_BACKEND_URL`.
7
- */
8
5
  function createKizlo(options) {
9
- return new Kizlo(resolveKizloConfig(options, { baseUrlEnvKey: "NEXT_PUBLIC_KIZLO_BACKEND_URL" }));
6
+ const revalidate = options?.revalidate;
7
+ const extensions = [...revalidate === false ? [] : [nextRevalidation(typeof revalidate === "object" ? revalidate : void 0)], ...options?.extensions ?? []];
8
+ return new Kizlo(resolveKizloConfig({
9
+ ...options,
10
+ extensions
11
+ }, { baseUrlEnvKey: "NEXT_PUBLIC_KIZLO_BACKEND_URL" }));
10
12
  }
11
13
 
12
14
  //#endregion
@@ -0,0 +1,25 @@
1
+ import { KizloEvent } from "../../webhook/schema.js";
2
+ import "../../index.js";
3
+ import { Pathname } from "@kizlo/shared";
4
+
5
+ //#region src/integrations/nextjs/revalidate.d.ts
6
+ type RevalidatePathFn = (path: string, type?: "layout" | "page") => void;
7
+ /** A path to revalidate, optionally with the Next `type` a dynamic route needs (e.g. `"page"`). */
8
+ type RevalidateTarget = Pathname | {
9
+ path: Pathname;
10
+ type?: "layout" | "page";
11
+ };
12
+ interface NextRevalidateOptions {
13
+ revalidatePath?: RevalidatePathFn;
14
+ /** Return extra paths to revalidate for a given event, on top of the event's own URL. */
15
+ paths?: (event: KizloEvent) => Pathname[] | Promise<Pathname[]>;
16
+ /**
17
+ * How to revalidate the sitemap. Defaults to the route `createSitemapRoute` serves
18
+ * (`/sitemaps/[sitemap]`, revalidated as a dynamic page). Override this only if you
19
+ * serve sitemaps from your own route(s) instead of `createSitemapRoute`; pass `false`
20
+ * to skip sitemap revalidation.
21
+ */
22
+ sitemap?: false | RevalidateTarget | RevalidateTarget[];
23
+ }
24
+ //#endregion
25
+ export { NextRevalidateOptions };
@@ -0,0 +1,46 @@
1
+ import { createEventHandler } from "../../webhook/index.js";
2
+ import { createExtension } from "../../shared/extension.js";
3
+ import { SITEMAP_ROUTE } from "./sitemap.js";
4
+
5
+ //#region src/integrations/nextjs/revalidate.ts
6
+ const ROBOTS_PATH = "/robots.txt";
7
+ const DEFAULT_SITEMAP_TARGET = {
8
+ path: SITEMAP_ROUTE,
9
+ type: "page"
10
+ };
11
+ function nextRevalidation(options) {
12
+ return createExtension({
13
+ id: "nextjs-revalidation",
14
+ init: () => ({ events: [createEventHandler(async (event) => {
15
+ if (!event) return;
16
+ if (event.type.startsWith("settings")) return;
17
+ const revalidatePath = options?.revalidatePath ?? (await import("next/cache")).revalidatePath;
18
+ const paths = [
19
+ ...await Promise.resolve(options?.paths?.(event) ?? []),
20
+ ...event.data?.url ? [event.data.url] : [],
21
+ ROBOTS_PATH
22
+ ];
23
+ for (const path of normalizePaths(paths)) revalidatePath(path);
24
+ for (const target of resolveSitemapTargets(options?.sitemap)) revalidatePath(normalizePath(target.path), target.type);
25
+ })] })
26
+ });
27
+ }
28
+ function resolveSitemapTargets(option) {
29
+ if (option === false) return [];
30
+ return (option === void 0 ? [DEFAULT_SITEMAP_TARGET] : Array.isArray(option) ? option : [option]).map((target) => typeof target === "string" ? { path: target } : target);
31
+ }
32
+ function normalizePaths(paths) {
33
+ return Array.from(new Set((Array.isArray(paths) ? paths : paths ? [paths] : []).map(normalizePath).filter(Boolean)));
34
+ }
35
+ function normalizePath(path) {
36
+ try {
37
+ return normalizePath(new URL(path).pathname);
38
+ } catch {
39
+ const trimmed = path.trim();
40
+ if (!trimmed) return "";
41
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
42
+ }
43
+ }
44
+
45
+ //#endregion
46
+ export { nextRevalidation };
@@ -0,0 +1,7 @@
1
+ import { SeoHead } from "../../seo/schema.js";
2
+ import { Metadata } from "next";
3
+
4
+ //#region src/integrations/nextjs/seo.d.ts
5
+ declare function toMetadata(head: SeoHead): Metadata;
6
+ //#endregion
7
+ export { toMetadata };
@@ -0,0 +1,41 @@
1
+ //#region src/integrations/nextjs/seo.ts
2
+ function toMetadata(head) {
3
+ const robots = [
4
+ head.robots.index,
5
+ head.robots.follow,
6
+ head.robots.maxSnippet,
7
+ head.robots.maxImagePreview,
8
+ head.robots.maxVideoPreview
9
+ ].join(", ");
10
+ return {
11
+ title: { absolute: head.title },
12
+ description: head.og.description || void 0,
13
+ alternates: { canonical: head.canonical },
14
+ robots,
15
+ openGraph: {
16
+ type: head.og.type,
17
+ title: head.og.title,
18
+ description: head.og.description || void 0,
19
+ url: head.og.url,
20
+ siteName: head.og.siteName,
21
+ locale: head.og.locale,
22
+ images: head.og.image ? [{
23
+ url: head.og.image.url,
24
+ width: head.og.image.width,
25
+ height: head.og.image.height,
26
+ alt: head.og.image.alt ?? void 0
27
+ }] : void 0
28
+ },
29
+ twitter: {
30
+ card: head.twitter.card,
31
+ title: head.twitter.title,
32
+ description: head.twitter.description || void 0,
33
+ site: head.twitter.site ?? void 0,
34
+ creator: head.twitter.creator ?? void 0,
35
+ images: head.twitter.image ? [head.twitter.image] : void 0
36
+ }
37
+ };
38
+ }
39
+
40
+ //#endregion
41
+ export { toMetadata };
@@ -0,0 +1,30 @@
1
+ import { S2SClient } from "../../kizlo.js";
2
+ import { CreateSitemapRouteOptions } from "../../seo/sitemap.js";
3
+
4
+ //#region src/integrations/nextjs/sitemap.d.ts
5
+
6
+ /**
7
+ * The Next route the sitemap handler is mounted at: a single dynamic segment named
8
+ * `[sitemap]`. It is fixed (not configurable) so that one `revalidatePath(SITEMAP_ROUTE,
9
+ * "page")` refreshes the index and every generated sitemap at once. `[sitemap]` is Next's
10
+ * file-routing convention, which is why it lives in this integration and not in core.
11
+ */
12
+ declare const SITEMAP_ROUTE: "/sitemaps/[sitemap]";
13
+ /** Next's dynamic-route context. `params` is a promise in the App Router. */
14
+ interface NextRouteContext {
15
+ params?: {
16
+ sitemap?: string;
17
+ } | Promise<{
18
+ sitemap?: string;
19
+ }>;
20
+ }
21
+ /**
22
+ * Next wrapper around the framework-agnostic {@link createCoreSitemapRoute}. It serves the
23
+ * same sitemap index and collection pages, and additionally asserts the route is mounted at
24
+ * {@link SITEMAP_ROUTE}: if the dynamic segment is not named `[sitemap]`, `params.sitemap`
25
+ * is missing and a wrong mount would silently break `revalidatePath(SITEMAP_ROUTE, "page")`,
26
+ * so it throws loudly on the first request instead.
27
+ */
28
+ declare function createSitemapRoute(client: S2SClient<[]>, options?: CreateSitemapRouteOptions): (request: Request, ctx?: NextRouteContext) => Promise<Response>;
29
+ //#endregion
30
+ export { SITEMAP_ROUTE, createSitemapRoute };
@@ -0,0 +1,28 @@
1
+ import { SITEMAP_BASE, createSitemapRoute } from "../../seo/sitemap.js";
2
+
3
+ //#region src/integrations/nextjs/sitemap.ts
4
+ /**
5
+ * The Next route the sitemap handler is mounted at: a single dynamic segment named
6
+ * `[sitemap]`. It is fixed (not configurable) so that one `revalidatePath(SITEMAP_ROUTE,
7
+ * "page")` refreshes the index and every generated sitemap at once. `[sitemap]` is Next's
8
+ * file-routing convention, which is why it lives in this integration and not in core.
9
+ */
10
+ const SITEMAP_ROUTE = `${SITEMAP_BASE}/[sitemap]`;
11
+ /**
12
+ * Next wrapper around the framework-agnostic {@link createCoreSitemapRoute}. It serves the
13
+ * same sitemap index and collection pages, and additionally asserts the route is mounted at
14
+ * {@link SITEMAP_ROUTE}: if the dynamic segment is not named `[sitemap]`, `params.sitemap`
15
+ * is missing and a wrong mount would silently break `revalidatePath(SITEMAP_ROUTE, "page")`,
16
+ * so it throws loudly on the first request instead.
17
+ */
18
+ function createSitemapRoute$1(client, options) {
19
+ const handler = createSitemapRoute(client, options);
20
+ return async function GET(request, ctx) {
21
+ const params = ctx?.params ? await ctx.params : void 0;
22
+ if (params && params.sitemap === void 0) throw new Error(`createSitemapRoute must be mounted at "${SITEMAP_ROUTE}". The dynamic segment must be named [sitemap].`);
23
+ return handler(request);
24
+ };
25
+ }
26
+
27
+ //#endregion
28
+ export { SITEMAP_ROUTE, createSitemapRoute$1 as createSitemapRoute };
@@ -1,2 +1,3 @@
1
1
  import { KizloServerOptions, createKizlo } from "../integrations/nextjs/kizlo.server.js";
2
- export { KizloServerOptions, createKizlo };
2
+ import { SITEMAP_ROUTE, createSitemapRoute } from "../integrations/nextjs/sitemap.js";
3
+ export { KizloServerOptions, SITEMAP_ROUTE, createKizlo, createSitemapRoute };
@@ -1,3 +1,4 @@
1
+ import { SITEMAP_ROUTE, createSitemapRoute } from "../integrations/nextjs/sitemap.js";
1
2
  import { createKizlo } from "../integrations/nextjs/kizlo.server.js";
2
3
 
3
- export { createKizlo };
4
+ export { SITEMAP_ROUTE, createKizlo, createSitemapRoute };
package/dist/nextjs.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  import { KizloClientOptions, createKizloClient } from "./integrations/nextjs/kizlo.client.js";
2
- export { KizloClientOptions, createKizloClient };
2
+ import { toMetadata } from "./integrations/nextjs/seo.js";
3
+ export { KizloClientOptions, createKizloClient, toMetadata };
package/dist/nextjs.js CHANGED
@@ -1,3 +1,4 @@
1
1
  import { createKizloClient } from "./integrations/nextjs/kizlo.client.js";
2
+ import { toMetadata } from "./integrations/nextjs/seo.js";
2
3
 
3
- export { createKizloClient };
4
+ export { createKizloClient, toMetadata };
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.2.0";
2
+ var version = "0.2.2";
3
3
 
4
4
  //#endregion
5
5
  export { version };
package/dist/router.d.ts CHANGED
@@ -299,6 +299,53 @@ declare const ROUTER_MAP: {
299
299
  }>;
300
300
  };
301
301
  seo: {
302
+ homepage: Procedure<"internal", {}, {
303
+ head: {
304
+ title: string;
305
+ canonical: string;
306
+ robots: {
307
+ index: string;
308
+ follow: string;
309
+ maxSnippet: string;
310
+ maxImagePreview: string;
311
+ maxVideoPreview: string;
312
+ };
313
+ og: {
314
+ locale: string;
315
+ type: string;
316
+ title: string;
317
+ url: string;
318
+ siteName: string;
319
+ description: string;
320
+ image: {
321
+ url: string;
322
+ width: string;
323
+ height: string;
324
+ type: string;
325
+ alt: string | null;
326
+ } | null;
327
+ };
328
+ twitter: {
329
+ card: "summary" | "summary_large_image";
330
+ title: string;
331
+ site: string | null;
332
+ creator: string | null;
333
+ description: string;
334
+ image: string | null;
335
+ };
336
+ article: {
337
+ publishedTime: string;
338
+ modifiedTime: string;
339
+ author: string;
340
+ authorUrl: string;
341
+ section: string;
342
+ } | null;
343
+ };
344
+ schema: {
345
+ "@context": "https://schema.org";
346
+ "@graph": Record<string, unknown>[];
347
+ };
348
+ }, DefinedErrorMapLike>;
302
349
  sitemaps: Procedure<"internal", {}, {
303
350
  key: string;
304
351
  type: "author" | "post_type" | "taxonomy";
package/dist/seo/index.js CHANGED
@@ -1,9 +1,58 @@
1
1
  import { createProcedure } from "../shared/procedure.js";
2
2
  import { WP_KIZLO_BASE } from "../wordpress/constants.js";
3
- import { ListSitemapUrlInput, Robots, SitemapList, SitemapUrlList } from "./schema.js";
3
+ import { ListSitemapUrlInput, Robots, Seo, SitemapList, SitemapUrlList } from "./schema.js";
4
4
 
5
5
  //#region src/seo/index.ts
6
6
  const SEO_ROUTER_MAP = {
7
+ homepage: createProcedure({
8
+ scope: "internal",
9
+ output: Seo
10
+ }, async ({ context, errors }) => {
11
+ const response = await context.service.wordpress.get("/seo/homepage", { base: WP_KIZLO_BASE });
12
+ if (response.error) {
13
+ context.logger.error("Get homepage seo unhandled error", response.error);
14
+ throw errors.INTERNAL_SERVER_ERROR();
15
+ }
16
+ const { head } = response.data;
17
+ return {
18
+ head: {
19
+ title: head.title,
20
+ canonical: head.canonical,
21
+ robots: {
22
+ index: head.robots.index,
23
+ follow: head.robots.follow,
24
+ maxSnippet: head.robots["max-snippet"],
25
+ maxImagePreview: head.robots["max-image-preview"],
26
+ maxVideoPreview: head.robots["max-video-preview"]
27
+ },
28
+ og: {
29
+ locale: head.og.locale,
30
+ type: head.og.type,
31
+ title: head.og.title,
32
+ url: head.og.url,
33
+ siteName: head.og.site_name,
34
+ description: head.og.description ?? "",
35
+ image: head.og.image
36
+ },
37
+ twitter: {
38
+ card: head.twitter.card,
39
+ title: head.twitter.title,
40
+ site: head.twitter.site,
41
+ creator: head.twitter.creator,
42
+ description: head.twitter.description ?? "",
43
+ image: head.twitter.image
44
+ },
45
+ article: head.article && {
46
+ publishedTime: head.article.published_time,
47
+ modifiedTime: head.article.modified_time,
48
+ author: head.article.author,
49
+ authorUrl: head.article.author_url,
50
+ section: head.article.section
51
+ }
52
+ },
53
+ schema: response.data.schema
54
+ };
55
+ }),
7
56
  sitemaps: createProcedure({
8
57
  scope: "internal",
9
58
  output: SitemapList
@@ -0,0 +1,39 @@
1
+ import { SitemapUrl } from "./schema.js";
2
+ import { SitemapIndexEntry } from "./utils.js";
3
+ import { S2SClient } from "../kizlo.js";
4
+
5
+ //#region src/seo/sitemap.d.ts
6
+
7
+ /**
8
+ * Every generated sitemap is served from `/sitemaps`: the index at `/sitemaps/index.xml`
9
+ * and each collection page at `/sitemaps/{key}.xml` (later pages `-{n}`). The base is fixed
10
+ * because the index `<loc>` entries (built by `sitemapEntryPath`) point at it, so the route
11
+ * must be mounted there for the links to resolve. How the route is mounted is a
12
+ * framework concern; this handler only cares about the trailing slug.
13
+ */
14
+ declare const SITEMAP_BASE = "/sitemaps";
15
+ /** Slug that serves the sitemap index (`/sitemaps/index.xml`) rather than a collection. */
16
+ declare const SITEMAP_INDEX_SLUG = "index.xml";
17
+ /**
18
+ * A sitemap not backed by WordPress (e.g. docs pages living in the app). It joins the
19
+ * same index/urlset pipeline: `entry` adds it to the index, `urls` serves its pages.
20
+ */
21
+ interface SitemapRouteExtra {
22
+ entry: SitemapIndexEntry;
23
+ urls: (origin: string, page: number) => SitemapUrl[] | Promise<SitemapUrl[]>;
24
+ }
25
+ interface CreateSitemapRouteOptions {
26
+ /** Extra, non-WordPress sitemaps to fold into the index and serve. */
27
+ extra?: SitemapRouteExtra[];
28
+ }
29
+ /**
30
+ * Builds a Web `Request` -> `Response` handler that serves the sitemap index
31
+ * (`/sitemaps/index.xml`) and every collection page, merging any `extra` sitemaps in.
32
+ * Framework-agnostic: the slug is the last path segment of the request URL, so this runs
33
+ * as-is on any web-standard server (Hono, a plain fetch handler, a Next route handler).
34
+ * Mounting it at the right path is the caller's job; framework wrappers (e.g. the Next
35
+ * integration) add mount assertions on top.
36
+ */
37
+ declare function createSitemapRoute(client: S2SClient<[]>, options?: CreateSitemapRouteOptions): (request: Request) => Promise<Response>;
38
+ //#endregion
39
+ export { CreateSitemapRouteOptions, SITEMAP_BASE, SITEMAP_INDEX_SLUG, SitemapRouteExtra, createSitemapRoute };
@@ -0,0 +1,64 @@
1
+ import { parseSitemapSlug, renderSitemapIndex, renderUrlSet, xmlResponse } from "./utils.js";
2
+
3
+ //#region src/seo/sitemap.ts
4
+ /**
5
+ * Every generated sitemap is served from `/sitemaps`: the index at `/sitemaps/index.xml`
6
+ * and each collection page at `/sitemaps/{key}.xml` (later pages `-{n}`). The base is fixed
7
+ * because the index `<loc>` entries (built by `sitemapEntryPath`) point at it, so the route
8
+ * must be mounted there for the links to resolve. How the route is mounted is a
9
+ * framework concern; this handler only cares about the trailing slug.
10
+ */
11
+ const SITEMAP_BASE = "/sitemaps";
12
+ /** Slug that serves the sitemap index (`/sitemaps/index.xml`) rather than a collection. */
13
+ const SITEMAP_INDEX_SLUG = "index.xml";
14
+ /**
15
+ * Builds a Web `Request` -> `Response` handler that serves the sitemap index
16
+ * (`/sitemaps/index.xml`) and every collection page, merging any `extra` sitemaps in.
17
+ * Framework-agnostic: the slug is the last path segment of the request URL, so this runs
18
+ * as-is on any web-standard server (Hono, a plain fetch handler, a Next route handler).
19
+ * Mounting it at the right path is the caller's job; framework wrappers (e.g. the Next
20
+ * integration) add mount assertions on top.
21
+ */
22
+ function createSitemapRoute(client, options) {
23
+ const extra = options?.extra ?? [];
24
+ return async function GET(request) {
25
+ const url = new URL(request.url);
26
+ const slug = lastPathSegment(url);
27
+ if (!slug) return notFound();
28
+ const { origin } = url;
29
+ if (slug === SITEMAP_INDEX_SLUG) {
30
+ const { data: data$1 } = await client.seo.sitemaps();
31
+ return xmlResponse(renderSitemapIndex([...data$1 ?? [], ...extra.map((item) => item.entry)], origin));
32
+ }
33
+ const parsed = parseSitemapSlug(slug);
34
+ if (!parsed) return notFound();
35
+ const custom = extra.find((item) => item.entry.key === parsed.key);
36
+ if (custom) {
37
+ if (parsed.page > custom.entry.pages) return notFound();
38
+ return xmlResponse(renderUrlSet(await custom.urls(origin, parsed.page)));
39
+ }
40
+ const { data: sitemaps } = await client.seo.sitemaps();
41
+ if (!sitemaps) return notFound();
42
+ const entry = sitemaps.find((item) => item.key === parsed.key);
43
+ if (!entry || parsed.page > entry.pages) return notFound();
44
+ const { data } = await client.seo.urls(entry.type === "author" ? {
45
+ type: "author",
46
+ page: parsed.page
47
+ } : {
48
+ type: entry.type,
49
+ key: entry.key,
50
+ page: parsed.page
51
+ });
52
+ if (!data) return notFound();
53
+ return xmlResponse(renderUrlSet(data));
54
+ };
55
+ }
56
+ function lastPathSegment(url) {
57
+ return decodeURIComponent(url.pathname.split("/").filter(Boolean).pop() ?? "");
58
+ }
59
+ function notFound() {
60
+ return new Response("Not Found", { status: 404 });
61
+ }
62
+
63
+ //#endregion
64
+ export { SITEMAP_BASE, SITEMAP_INDEX_SLUG, createSitemapRoute };
@@ -0,0 +1,6 @@
1
+ import { Sitemap } from "./schema.js";
2
+
3
+ //#region src/seo/utils.d.ts
4
+ type SitemapIndexEntry = Pick<Sitemap, "key" | "pages" | "lastmod">;
5
+ //#endregion
6
+ export { SitemapIndexEntry };
package/dist/seo/utils.js CHANGED
@@ -39,6 +39,41 @@ function deserializeSeo(data) {
39
39
  schema: data.schema
40
40
  };
41
41
  }
42
+ function sitemapEntryPath(key, page) {
43
+ return `/sitemaps/${key}${page > 1 ? `-${page}` : ""}.xml`;
44
+ }
45
+ function parseSitemapSlug(slug) {
46
+ const match = /^(.+?)(?:-(\d+))?\.xml$/.exec(slug);
47
+ if (!match) return null;
48
+ const [, key, rawPage] = match;
49
+ const page = rawPage ? Number(rawPage) : 1;
50
+ if (!key || !Number.isInteger(page) || page < 1) return null;
51
+ return {
52
+ key,
53
+ page
54
+ };
55
+ }
56
+ function escapeXml(value) {
57
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
58
+ }
59
+ function renderSitemapIndex(sitemaps, origin) {
60
+ return `<?xml version="1.0" encoding="UTF-8"?>\n<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${sitemaps.flatMap((sitemap) => Array.from({ length: sitemap.pages }, (_, index) => {
61
+ return `\t<sitemap>\n\t\t<loc>${escapeXml(`${origin}${sitemapEntryPath(sitemap.key, index + 1)}`)}</loc>\n\t\t<lastmod>${escapeXml(sitemap.lastmod)}</lastmod>\n\t</sitemap>`;
62
+ })).join("\n")}\n</sitemapindex>\n`;
63
+ }
64
+ function renderUrlSet(urls) {
65
+ return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">\n${urls.map((url) => {
66
+ const images = url.images.map((image) => `\t\t<image:image>\n\t\t\t<image:loc>${escapeXml(image.loc)}</image:loc>\n\t\t</image:image>`).join("\n");
67
+ const imageBlock = images ? `\n${images}` : "";
68
+ return `\t<url>\n\t\t<loc>${escapeXml(url.loc)}</loc>\n\t\t<lastmod>${escapeXml(url.lastmod)}</lastmod>${imageBlock}\n\t</url>`;
69
+ }).join("\n")}\n</urlset>\n`;
70
+ }
71
+ function xmlResponse(body) {
72
+ return new Response(body, { headers: {
73
+ "Content-Type": "application/xml; charset=utf-8",
74
+ "Cache-Control": "public, max-age=0, s-maxage=3600, stale-while-revalidate=86400"
75
+ } });
76
+ }
42
77
 
43
78
  //#endregion
44
- export { deserializeSeo };
79
+ export { deserializeSeo, parseSitemapSlug, renderSitemapIndex, renderUrlSet, xmlResponse };
@@ -17,6 +17,7 @@ interface KizloSiteSettings {
17
17
  title_separator: string;
18
18
  fallback_image: KizloMedia | null;
19
19
  search_action_structure: string | null;
20
+ discourage_search_engines: boolean;
20
21
  }
21
22
  interface KizloPersonSettings {
22
23
  name: string;
@@ -1,4 +1,4 @@
1
- import { AnySchema, LiteralUnion, Metadata, SchemaInput, SchemaOutput } from "@kizlo/shared";
1
+ import { LiteralUnion, Metadata, Schema, SchemaInput, SchemaOutput } from "@kizlo/shared";
2
2
 
3
3
  //#region src/shared/error.d.ts
4
4
  declare const COMMON_ERRORS: {
@@ -116,11 +116,11 @@ type ErrorDefinition = {
116
116
  /** Human-readable message. Overridable per throw via `errors.CODE({ message })`. */
117
117
  message?: string;
118
118
  /** Standard Schema for the error's `data` payload — validated and typed when you throw `errors.CODE({ data })`. */
119
- data?: AnySchema;
119
+ data?: Schema;
120
120
  };
121
121
  declare function defineErrorMap<const T extends Partial<Record<CommonErrorLiteral, ErrorDefinition>>>(map: T): T;
122
122
  type DefinedErrorMapLike = Record<string, ErrorDefinition>;
123
- type ThrowableErrorFn<TCode extends string, T extends ErrorDefinition> = T["data"] extends AnySchema ? undefined extends SchemaInput<T["data"]> ? (options?: {
123
+ type ThrowableErrorFn<TCode extends string, T extends ErrorDefinition> = T["data"] extends Schema ? undefined extends SchemaInput<T["data"]> ? (options?: {
124
124
  status?: number;
125
125
  message?: string;
126
126
  data?: SchemaInput<T["data"]>;
@@ -2,7 +2,7 @@ import { DefinedErrorMapLike, ThrowableErrorMap, WithCommonErrorMap } from "./er
2
2
  import { ServerContext } from "../context.js";
3
3
  import { AnyMiddleware, InferUses, Middleware } from "./middleware.js";
4
4
  import * as _orpc_contract0 from "@orpc/contract";
5
- import { AnySchema, DeepMerge, Pathname, Prettify, Promisify, Schema, SchemaInput, SchemaOutput } from "@kizlo/shared";
5
+ import { DeepMerge, Pathname, Prettify, Promisify, Schema, SchemaInput, SchemaOutput } from "@kizlo/shared";
6
6
  import * as _orpc_server0 from "@orpc/server";
7
7
  import { HTTPMethod } from "@orpc/server";
8
8
  import { JsonifiedValue } from "@orpc/openapi-client";
@@ -33,25 +33,25 @@ interface Procedure<TScope extends InvocationScope, TInput, TOutput, TError exte
33
33
  options: {
34
34
  scope: TScope;
35
35
  errors?: TError;
36
- output: AnySchema;
36
+ output: Schema;
37
37
  inputValidation?: boolean;
38
38
  outputValidation?: boolean;
39
39
  middlewares?: AnyMiddleware[];
40
40
  } & ({
41
41
  scope: "internal";
42
- input?: AnySchema;
42
+ input?: Schema;
43
43
  } | {
44
44
  scope: "remote";
45
- input?: AnySchema;
45
+ input?: Schema;
46
46
  method?: HTTPMethod;
47
47
  } | {
48
48
  scope: "api";
49
49
  method?: HTTPMethod;
50
50
  path?: Pathname;
51
- body?: AnySchema;
52
- query?: AnySchema;
53
- params?: AnySchema;
54
- headers?: AnySchema;
51
+ body?: Schema;
52
+ query?: Schema;
53
+ params?: Schema;
54
+ headers?: Schema;
55
55
  });
56
56
  };
57
57
  }
@@ -112,9 +112,9 @@ type ProcedureOptions<TScope extends InvocationScope, TPathname extends Pathname
112
112
  input?: TInput;
113
113
  });
114
114
  declare function createProcedure<TScope extends InvocationScope, TPathname extends Pathname, TInput extends Schema | undefined = undefined, TBody extends Schema | undefined = undefined, TParams extends Schema | undefined = undefined, TQuery extends Schema | undefined = undefined, THeaders extends Schema | undefined = undefined, TOutput extends Schema = Schema, TError extends DefinedErrorMapLike = DefinedErrorMapLike, TMiddlewares extends Middleware<ResolvedSchemaOutput<TScope, TPathname, TInput, TBody, TParams, TQuery, THeaders>, SchemaOutput<TOutput>, TError>[] = Middleware<ResolvedSchemaOutput<TScope, TPathname, TInput, TBody, TParams, TQuery, THeaders>, SchemaOutput<TOutput>, TError>[], TContext extends ProcedureContext<TMiddlewares> = ProcedureContext<TMiddlewares>>(options: ProcedureOptions<TScope, TPathname, TInput, TBody, TParams, TQuery, THeaders, TOutput, TError, TMiddlewares>, handler: ProcedureHandler<ResolvedSchemaOutput<TScope, TPathname, TInput, TBody, TParams, TQuery, THeaders>, SchemaOutput<TOutput>, TError, TContext>): Procedure<TScope, ResolvedSchemaInput<TScope, TPathname, TInput, TBody, TParams, TQuery, THeaders>, TScope extends "api" ? JsonifiedValue<SchemaOutput<TOutput>> : SchemaOutput<TOutput>, TError>;
115
- declare function schemaType<TInput, TOutput = TInput>(schema?: AnySchema): Schema<TInput, TOutput>;
115
+ declare function schemaType<TInput, TOutput = TInput>(schema?: Schema): Schema<TInput, TOutput>;
116
116
  declare function buildProcedure(procedure: AnyProcedure): _orpc_server0.DecoratedProcedure<_orpc_server0.Context & Record<never, never>, _orpc_server0.Context, _orpc_contract0.Schema<unknown, unknown>, _orpc_contract0.Schema<any, any>, Record<never, never>, Record<never, never>>;
117
117
  declare function handleUnexpectedError(error: unknown, errors?: DefinedErrorMapLike): never;
118
- declare function combineDetailed(parts: Record<string, AnySchema>): AnySchema;
118
+ declare function combineDetailed(parts: Record<string, Schema>): Schema;
119
119
  //#endregion
120
120
  export { AnyProcedure, AnyProcedureRouter, BaseHandlerOptions, ExtractParams, ExtractProcedureByScope, InvocationScope, OpenapiPart, Procedure, ProcedureContext, ProcedureErrors, ProcedureHandler, ProcedureOptions, ResolveParamsPart, ResolvedSchemaInput, ResolvedSchemaOutput, RouterDepthTuple, SchemaIO, buildProcedure, combineDetailed, createProcedure, handleUnexpectedError, schemaType };
@@ -12,6 +12,7 @@ declare const KizloEvent: z.ZodUnion<readonly [z.ZodObject<{
12
12
  data: z.ZodObject<{
13
13
  post_id: z.ZodNumber;
14
14
  post_type: z.ZodString;
15
+ url: z.ZodOptional<z.ZodString>;
15
16
  }, z.core.$strip>;
16
17
  }, z.core.$strip>, z.ZodObject<{
17
18
  type: z.ZodEnum<{
@@ -24,15 +25,7 @@ declare const KizloEvent: z.ZodUnion<readonly [z.ZodObject<{
24
25
  taxonomy: z.ZodString;
25
26
  post_types: z.ZodArray<z.ZodString>;
26
27
  count: z.ZodNumber;
27
- }, z.core.$strip>;
28
- }, z.core.$strip>, z.ZodObject<{
29
- type: z.ZodEnum<{
30
- "payment.status_requested": "payment.status_requested";
31
- }>;
32
- data: z.ZodObject<{
33
- transaction_id: z.ZodNullable<z.ZodString>;
34
- payment_method_id: z.ZodString;
35
- payment_session_id: z.ZodString;
28
+ url: z.ZodOptional<z.ZodString>;
36
29
  }, z.core.$strip>;
37
30
  }, z.core.$strip>, z.ZodObject<{
38
31
  type: z.ZodEnum<{
@@ -27,7 +27,8 @@ const EVENT_TYPES = [
27
27
  const EventType = z.enum(EVENT_TYPES);
28
28
  const PostEventData = z.object({
29
29
  post_id: z.number(),
30
- post_type: z.string()
30
+ post_type: z.string(),
31
+ url: z.string().optional()
31
32
  });
32
33
  const PostEvent = z.object({
33
34
  type: PostEventType,
@@ -37,21 +38,13 @@ const TermEventData = z.object({
37
38
  term_id: z.number(),
38
39
  taxonomy: z.string(),
39
40
  post_types: z.array(z.string()),
40
- count: z.number().nonnegative()
41
+ count: z.number().nonnegative(),
42
+ url: z.string().optional()
41
43
  });
42
44
  const TermEvent = z.object({
43
45
  type: TermEventType,
44
46
  data: TermEventData
45
47
  });
46
- const PaymentEventData = z.object({
47
- transaction_id: z.nullable(z.string()),
48
- payment_method_id: z.string(),
49
- payment_session_id: z.string()
50
- });
51
- const PaymentEvent = z.object({
52
- type: PaymentEventType,
53
- data: PaymentEventData
54
- });
55
48
  const SettingsEventData = z.null();
56
49
  const SettingsEvent = z.object({
57
50
  type: SettingsEventType,
@@ -60,7 +53,6 @@ const SettingsEvent = z.object({
60
53
  const KizloEvent = z.union([
61
54
  PostEvent,
62
55
  TermEvent,
63
- PaymentEvent,
64
56
  SettingsEvent
65
57
  ]);
66
58
  const AnyEvent = z.object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kizlo",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "description": "A headless WordPress toolkit for TypeScript.",
6
6
  "keywords": [
@@ -76,7 +76,7 @@
76
76
  },
77
77
  "dependencies": {
78
78
  "@clack/prompts": "^1.5.1",
79
- "@kizlo/shared": "0.1.1",
79
+ "@kizlo/shared": "0.1.2",
80
80
  "@orpc/client": "^1.14.3",
81
81
  "@orpc/contract": "^1.14.3",
82
82
  "@orpc/openapi": "^1.14.3",