kizlo 0.2.1 → 0.3.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/README.md +7 -11
  2. package/dist/adapters/captcha.js +13 -5
  3. package/dist/cli/commands/dev.js +1 -1
  4. package/dist/cli/commands/init.js +51 -36
  5. package/dist/cli/commands/test.js +4 -1
  6. package/dist/cli/presets/base.js +17 -9
  7. package/dist/cli/presets/nextjs.js +61 -14
  8. package/dist/cli/wp/dev.js +1 -1
  9. package/dist/cli/wp/phpunit.js +104 -0
  10. package/dist/context.js +2 -2
  11. package/dist/index.d.ts +6 -4
  12. package/dist/index.js +4 -3
  13. package/dist/integrations/nextjs/kizlo.server.d.ts +5 -6
  14. package/dist/integrations/nextjs/kizlo.server.js +7 -5
  15. package/dist/integrations/nextjs/revalidate.d.ts +28 -0
  16. package/dist/integrations/nextjs/revalidate.js +49 -0
  17. package/dist/integrations/nextjs/robots.d.ts +7 -0
  18. package/dist/integrations/nextjs/robots.js +23 -0
  19. package/dist/integrations/nextjs/seo.d.ts +7 -0
  20. package/dist/integrations/nextjs/seo.js +41 -0
  21. package/dist/integrations/nextjs/sitemap.d.ts +33 -0
  22. package/dist/integrations/nextjs/sitemap.js +31 -0
  23. package/dist/nextjs/server.d.ts +3 -1
  24. package/dist/nextjs/server.js +3 -1
  25. package/dist/nextjs.d.ts +2 -1
  26. package/dist/nextjs.js +2 -1
  27. package/dist/package.js +1 -1
  28. package/dist/post/index.js +2 -2
  29. package/dist/router.d.ts +71 -13
  30. package/dist/seo/index.js +71 -6
  31. package/dist/seo/robots.js +18 -0
  32. package/dist/seo/schema.d.ts +15 -1
  33. package/dist/seo/schema.js +5 -1
  34. package/dist/seo/sitemap.d.ts +39 -0
  35. package/dist/seo/sitemap.js +64 -0
  36. package/dist/seo/types.d.ts +5 -1
  37. package/dist/seo/utils.d.ts +6 -0
  38. package/dist/seo/utils.js +33 -1
  39. package/dist/settings/service.interface.d.ts +1 -0
  40. package/dist/shared/crypto.d.ts +11 -8
  41. package/dist/shared/crypto.js +26 -9
  42. package/dist/shared/error.d.ts +3 -3
  43. package/dist/shared/procedure.d.ts +10 -10
  44. package/dist/webhook/schema.d.ts +2 -9
  45. package/dist/webhook/schema.js +5 -16
  46. package/dist/wordpress/service.js +4 -1
  47. package/package.json +2 -2
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,7 +34,9 @@
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
 
@@ -44,23 +46,17 @@ Set up Kizlo in your project:
44
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
@@ -1,3 +1,5 @@
1
+ import { hash, hmac } from "../shared/crypto.js";
2
+
1
3
  //#region src/adapters/captcha.ts
2
4
  /** Author a custom captcha adapter — types your verify function against the {@link CaptchaAdapter} contract. */
3
5
  function createCaptchaAdapter(adapter) {
@@ -72,14 +74,20 @@ function arkose(opts) {
72
74
  })).json()).session_details?.solved === true;
73
75
  };
74
76
  }
77
+ const ALTCHA_ALGORITHMS = {
78
+ "SHA-256": "SHA-256",
79
+ "SHA-384": "SHA-384",
80
+ "SHA-512": "SHA-512"
81
+ };
75
82
  function altcha(opts) {
76
83
  return async ({ token }) => {
77
84
  try {
78
- const payload = JSON.parse(Buffer.from(token, "base64").toString("utf-8"));
79
- const { createHmac, createHash } = await import("node:crypto");
80
- const algo = payload.algorithm.toLowerCase().replace("-", "");
81
- if (createHash(algo).update(`${payload.salt}${payload.number}`).digest("hex") !== payload.challenge) return false;
82
- return createHmac(algo, opts.hmacKey).update(payload.challenge).digest("hex") === payload.signature;
85
+ const json = new TextDecoder().decode(Uint8Array.from(atob(token), (c) => c.charCodeAt(0)));
86
+ const payload = JSON.parse(json);
87
+ const algo = ALTCHA_ALGORITHMS[payload.algorithm.toUpperCase()];
88
+ if (!algo) return false;
89
+ if (await hash(`${payload.salt}${payload.number}`, algo) !== payload.challenge) return false;
90
+ return await hmac(opts.hmacKey, payload.challenge, algo) === payload.signature;
83
91
  } catch {
84
92
  return false;
85
93
  }
@@ -7,10 +7,10 @@ import { bootstrapDev } from "../wp/dev.js";
7
7
  import { reapOrphans, registerSession, removeProjectContainers, spawnWatchdog, unregisterSession } from "../wp/session.js";
8
8
  import { syncSiteSettings } from "../wp/settings.js";
9
9
  import { devStack } from "../wp/stack.js";
10
- import { randomBytes } from "node:crypto";
11
10
  import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
12
11
  import { join } from "node:path";
13
12
  import { defineCommand } from "citty";
13
+ import { randomBytes } from "node:crypto";
14
14
  import { networkInterfaces } from "node:os";
15
15
 
16
16
  //#region src/cli/commands/dev.ts
@@ -10,11 +10,11 @@ import { syncSiteSettings } from "../wp/settings.js";
10
10
  import { devStack } from "../wp/stack.js";
11
11
  import { PRESETS, detectPreset, getPreset } from "../presets/index.js";
12
12
  import z from "zod/v4";
13
- import { randomBytes } from "node:crypto";
14
13
  import fs from "node:fs";
15
14
  import path from "node:path";
16
15
  import { defineCommand } from "citty";
17
16
  import * as p from "@clack/prompts";
17
+ import { randomBytes } from "node:crypto";
18
18
 
19
19
  //#region src/cli/commands/init.ts
20
20
  /** Production connection keys — what a real deploy needs, and what `.env.example` always lists. */
@@ -116,6 +116,35 @@ function orCancel(value) {
116
116
  }
117
117
  return value;
118
118
  }
119
+ /**
120
+ * The single overwrite policy for every scaffolded file: create it when absent, overwrite on
121
+ * `--force`, and otherwise ask before clobbering an existing file (keeping it when the user
122
+ * declines or when running non-interactively with `--yes`). Every scaffolded file routes through
123
+ * here rather than deciding for itself, so new files inherit the same behavior for free.
124
+ */
125
+ async function scaffoldFile(cwd, file, opts) {
126
+ const absPath = path.join(cwd, file.relPath);
127
+ const existed = fs.existsSync(absPath);
128
+ if (existed) {
129
+ let overwrite = opts.force;
130
+ if (!opts.force && !opts.yes) {
131
+ p.log.warn(`${file.label} already exists at ${file.relPath}`);
132
+ overwrite = orCancel(await p.confirm({
133
+ message: "Overwrite it?",
134
+ initialValue: true
135
+ }));
136
+ }
137
+ if (!overwrite) return "kept";
138
+ }
139
+ fs.mkdirSync(path.dirname(absPath), { recursive: true });
140
+ fs.writeFileSync(absPath, file.contents);
141
+ return existed ? "overwritten" : "created";
142
+ }
143
+ /** Report a {@link scaffoldFile} outcome in init's usual voice. */
144
+ function reportScaffold(file, result, yes) {
145
+ if (result === "kept") p.log.info(`Kept existing ${file.label} (${file.relPath})${yes ? " — pass --force to overwrite" : ""}`);
146
+ else p.log.success(`${result === "overwritten" ? "Overwrote" : "Created"} ${file.label} (${file.relPath})`);
147
+ }
119
148
  async function collectInteractively(ctx) {
120
149
  let siteUrl;
121
150
  let baseUrl;
@@ -193,7 +222,7 @@ async function collectInteractively(ctx) {
193
222
  validate: validate(dirPath)
194
223
  }));
195
224
  let alias = "";
196
- if (ctx.preset.routeHandler) {
225
+ if (ctx.preset.apiPath) {
197
226
  const serverDir = path.join(dir.replace(/^\.\//, "").replace(/\/+$/, ""), "server");
198
227
  const detected = detectImportAlias(ctx.cwd, serverDir)?.prefix;
199
228
  alias = orCancel(await p.text({
@@ -230,7 +259,7 @@ function collectFromEnv(ctx) {
230
259
  wpUsername: process.env.KIZLO_WORDPRESS_USERNAME?.trim() ?? "",
231
260
  wpPassword: process.env.KIZLO_WORDPRESS_APPLICATION_PASSWORD?.trim() ?? "",
232
261
  dir,
233
- alias: ctx.preset.routeHandler ? aliasWithSlash(detectImportAlias(ctx.cwd, path.join(dir, "server"))?.prefix) : ""
262
+ alias: ctx.preset.apiPath ? aliasWithSlash(detectImportAlias(ctx.cwd, path.join(dir, "server"))?.prefix) : ""
234
263
  };
235
264
  }
236
265
  function readPkg(pkgPath) {
@@ -434,52 +463,38 @@ const init = defineCommand({
434
463
  const exampleBody = `${exampleEnv}\n# Point the app at a local dev stack (managed by \`kizlo dev\`) instead of the keys above:\n# KIZLO_TARGET=dev\n`;
435
464
  const exampleCreated = writeFileIfAbsent(path.join(cwd, ".env.example"), exampleBody);
436
465
  const dirRel = setup.dir.replace(/^\.\//, "").replace(/\/+$/, "");
437
- const configCreated = writeFileIfAbsent(path.join(cwd, "kizlo.config.ts"), kizloConfigTemplate(dirRel, setup.alias, setup.devPath));
438
466
  const serverDirRel = path.join(dirRel, "server");
439
- const serverEntryRel = path.join(serverDirRel, "index.ts");
440
- const serverPath = path.join(cwd, serverEntryRel);
441
- const serverExisted = fs.existsSync(serverPath);
442
- let wroteServer = !serverExisted || force;
443
- if (serverExisted && !force && !yes) {
444
- p.log.warn(`A Kizlo server instance already exists at ${serverEntryRel}`);
445
- wroteServer = orCancel(await p.confirm({
446
- message: "Overwrite it?",
447
- initialValue: true
448
- }));
449
- }
450
- if (wroteServer) {
451
- fs.mkdirSync(path.dirname(serverPath), { recursive: true });
452
- fs.writeFileSync(serverPath, preset.serverEntry());
453
- }
454
- const generatedDirRel = path.join(serverDirRel, "generated");
455
- writeFileIfAbsent(path.join(cwd, generatedDirRel, "contract.json"), "{}\n");
456
- writeFileIfAbsent(path.join(cwd, generatedDirRel, "index.ts"), CONTRACT_BARREL);
457
467
  const clientUrl = setup.siteUrl && !sameOrigin(setup.siteUrl, setup.baseUrl) ? setup.baseUrl : void 0;
458
468
  const scaffold = {
459
469
  serverDirName: path.basename(serverDirRel),
470
+ serverEntryPath: path.join(serverDirRel, "index.ts"),
471
+ clientPath: path.join(dirRel, "client.ts"),
460
472
  appDir: detectAppDir(cwd, hasSrcDir),
461
473
  serverImport: (fromDir) => resolveModuleImport(cwd, serverDirRel, fromDir, setup.alias),
462
474
  clientUrl
463
475
  };
464
- const clientRel = path.join(dirRel, "client.ts");
465
- const clientCreated = writeFileIfAbsent(path.join(cwd, clientRel), preset.clientEntry(scaffold));
466
- let routeRel;
467
- let routeCreated = false;
468
- if (preset.routeHandler) {
469
- const route = preset.routeHandler(scaffold);
470
- routeRel = route.path;
471
- routeCreated = writeFileIfAbsent(path.join(cwd, route.path), route.contents);
472
- }
476
+ const files = [{
477
+ label: "Kizlo config",
478
+ relPath: "kizlo.config.ts",
479
+ contents: kizloConfigTemplate(dirRel, setup.alias, setup.devPath)
480
+ }, ...preset.scaffolds(scaffold)];
481
+ const scaffolded = [];
482
+ for (const file of files) scaffolded.push({
483
+ file,
484
+ result: await scaffoldFile(cwd, file, {
485
+ force,
486
+ yes
487
+ })
488
+ });
489
+ const generatedDirRel = path.join(serverDirRel, "generated");
490
+ writeFileIfAbsent(path.join(cwd, generatedDirRel, "contract.json"), "{}\n");
491
+ writeFileIfAbsent(path.join(cwd, generatedDirRel, "index.ts"), CONTRACT_BARREL);
473
492
  const gitignore = ensureGitignored(cwd, ".env");
474
493
  if (!envExisted) p.log.success("Created .env");
475
494
  else if (merge.updated.length || merge.added.length) p.log.success("Updated .env");
476
495
  else p.log.info("Left .env unchanged");
477
496
  p.log.success(exampleCreated ? "Created .env.example" : "Skipped .env.example (exists)");
478
- p.log.success(configCreated ? "Created kizlo.config.ts" : "Skipped kizlo.config.ts (exists)");
479
- if (wroteServer) p.log.success(`${serverExisted ? "Overwrote" : "Created"} Kizlo server instance (${serverEntryRel})`);
480
- else p.log.info(`Kept existing Kizlo server instance (${serverEntryRel})${yes ? " — pass --force to overwrite" : ""}`);
481
- p.log.success(clientCreated ? `Created browser client (${clientRel})` : "Skipped browser client (exists)");
482
- if (routeRel) p.log.success(routeCreated ? `Created API route (${routeRel})` : "Skipped API route (exists)");
497
+ for (const { file, result } of scaffolded) reportScaffold(file, result, yes);
483
498
  if (gitignore !== "present") p.log.success(`${gitignore === "created" ? "Created" : "Updated"} .gitignore (ignoring .env)`);
484
499
  if (setup.mode === "local") {
485
500
  p.log.success(`Local WordPress configured (${setup.devPath}) — .env points at ${setup.wpUrl}`);
@@ -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.");
@@ -4,27 +4,35 @@ const base = {
4
4
  label: "Generic (no framework detected)",
5
5
  detect: () => 1,
6
6
  baseUrlEnvKey: "KIZLO_BACKEND_URL",
7
- serverEntry() {
8
- return `import { createKizlo } from "kizlo"
7
+ scaffolds(ctx) {
8
+ return [{
9
+ label: "Kizlo server instance",
10
+ relPath: ctx.serverEntryPath,
11
+ contents: `import { createKizlo } from "kizlo"
9
12
 
10
13
  export const { router, client, context, handler } = createKizlo()
11
- `;
12
- },
13
- clientEntry(ctx) {
14
- if (ctx.clientUrl) return `import { createKizloClient } from "kizlo"
14
+ `
15
+ }, {
16
+ label: "Browser client",
17
+ relPath: ctx.clientPath,
18
+ contents: clientEntry(ctx)
19
+ }];
20
+ }
21
+ };
22
+ function clientEntry(ctx) {
23
+ if (ctx.clientUrl) return `import { createKizloClient } from "kizlo"
15
24
  import { contract } from "./${ctx.serverDirName}/generated"
16
25
 
17
26
  // Your Kizlo backend runs on a different origin than this app. Swap in your bundler's
18
27
  // public env var (e.g. import.meta.env.VITE_KIZLO_BACKEND_URL) to set it per environment.
19
28
  export const client = createKizloClient(contract, { url: "${ctx.clientUrl}" })
20
29
  `;
21
- return `import { createKizloClient } from "kizlo"
30
+ return `import { createKizloClient } from "kizlo"
22
31
  import { contract } from "./${ctx.serverDirName}/generated"
23
32
 
24
33
  export const client = createKizloClient(contract)
25
34
  `;
26
- }
27
- };
35
+ }
28
36
 
29
37
  //#endregion
30
38
  export { base };
@@ -10,28 +10,75 @@ const nextjs = {
10
10
  },
11
11
  baseUrlEnvKey: "NEXT_PUBLIC_KIZLO_BACKEND_URL",
12
12
  apiPath: "/api/kizlo",
13
- serverEntry() {
14
- return `import { createKizlo } from "kizlo/nextjs/server"
13
+ scaffolds(ctx) {
14
+ const apiDir = `${ctx.appDir}/api/kizlo/[[...rest]]`;
15
+ const robotsDir = `${ctx.appDir}/robots.txt`;
16
+ const sitemapDir = `${ctx.appDir}/sitemaps/[sitemap]`;
17
+ return [
18
+ {
19
+ label: "Kizlo server instance",
20
+ relPath: ctx.serverEntryPath,
21
+ contents: `import { createKizlo } from "kizlo/nextjs/server"
15
22
 
16
23
  export const { router, client, context, handler } = createKizlo()
17
- `;
18
- },
19
- clientEntry(ctx) {
20
- return `import { createKizloClient } from "kizlo/nextjs"
24
+ `
25
+ },
26
+ {
27
+ label: "Browser client",
28
+ relPath: ctx.clientPath,
29
+ contents: `import { createKizloClient } from "kizlo/nextjs"
21
30
  import { contract } from "./${ctx.serverDirName}/generated"
22
31
 
23
32
  export const client = createKizloClient(contract)
24
- `;
25
- },
26
- routeHandler(ctx) {
27
- const dir = `${ctx.appDir}/api/kizlo/[[...rest]]`;
28
- return {
29
- path: `${dir}/route.ts`,
30
- contents: `import { handler } from "${ctx.serverImport(dir)}"
33
+ `
34
+ },
35
+ {
36
+ label: "API route",
37
+ relPath: `${apiDir}/route.ts`,
38
+ contents: `import { handler } from "${ctx.serverImport(apiDir)}"
31
39
 
32
40
  export { handler as GET, handler as POST, handler as PUT, handler as PATCH, handler as DELETE, handler as OPTIONS }
33
41
  `
34
- };
42
+ },
43
+ {
44
+ label: "robots.txt route",
45
+ relPath: `${robotsDir}/route.ts`,
46
+ contents: `import { createRobotsRoute } from "kizlo/nextjs/server"
47
+ import { client } from "${ctx.serverImport(robotsDir)}"
48
+
49
+ // Run on the edge: cheaper and faster than Node for a response this small.
50
+ export const runtime = "edge"
51
+
52
+ // Vercel treats special metadata files like robots.txt and sitemap.xml as completely static
53
+ // files at build time, bypassing normal Incremental Static Regeneration (ISR) and on-demand
54
+ // revalidatePath rules.
55
+ //
56
+ // While your revalidation logic executes perfectly in a local Node.js server, Vercel uploads
57
+ // these routes directly to its Edge CDN as immutable assets. Subsequent on-demand revalidation
58
+ // triggers will successfully clear the Data Cache but fail to purge the CDN-level Edge cache
59
+ // for that file.
60
+ //
61
+ // force-dynamic opts out of that static treatment so CMS edits show up; the WordPress call is
62
+ // cached, so requests stay cheap.
63
+ export const dynamic = "force-dynamic"
64
+
65
+ export const GET = createRobotsRoute(client)
66
+ `
67
+ },
68
+ {
69
+ label: "sitemap route",
70
+ relPath: `${sitemapDir}/route.ts`,
71
+ contents: `import { createSitemapRoute } from "kizlo/nextjs/server"
72
+ import { client } from "${ctx.serverImport(sitemapDir)}"
73
+
74
+ // Unlike robots.txt, Vercel revalidates this route on demand, so it can stay static. It's
75
+ // served from the CDN at no per-request cost and still refreshes when content changes.
76
+ export const dynamic = "force-static"
77
+
78
+ export const GET = createSitemapRoute(client)
79
+ `
80
+ }
81
+ ];
35
82
  }
36
83
  };
37
84
 
@@ -4,9 +4,9 @@ import { DEFAULT_PLUGINS, TEST_ADMIN } from "./constants.js";
4
4
  import { ensurePlugins } from "./utils.js";
5
5
  import { createAdminAppPassword, seedUsers } from "./bootstrap.js";
6
6
  import { prepareByo } from "./byo.js";
7
- import { randomBytes } from "node:crypto";
8
7
  import { existsSync, rmSync } from "node:fs";
9
8
  import { join } from "node:path";
9
+ import { randomBytes } from "node:crypto";
10
10
 
11
11
  //#region src/cli/wp/dev.ts
12
12
  /** A strong random admin password (144 bits, URL/JSON/CLI-safe characters). */
@@ -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/context.js CHANGED
@@ -1,8 +1,8 @@
1
+ import { compare, hmac } from "./shared/crypto.js";
1
2
  import { noopAdapter } from "./adapters/logger.js";
2
3
  import { PreviewTokenData } from "./shared/schema.js";
3
4
  import { CookiesStorage } from "./cookie.js";
4
5
  import { Service } from "./service.js";
5
- import { compare, hmac } from "./shared/crypto.js";
6
6
  import { base64Decode, tryCatchSync } from "@kizlo/shared";
7
7
  import { serialize } from "cookie";
8
8
 
@@ -106,7 +106,7 @@ var Context = class {
106
106
  if (error) return null;
107
107
  if (Date.now() > data.payload.expires * 1e3) return null;
108
108
  const payloadStr = `${data.payload.id}.${data.payload.parent}.${data.payload.expires}`;
109
- if (!compare(hmac(this.config.siteSecret ?? "", payloadStr), data.hash)) return null;
109
+ if (!await compare(await hmac(this.config.siteSecret ?? "", payloadStr), data.hash)) return null;
110
110
  return data.payload;
111
111
  };
112
112
  }
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, SitemapIndex, SitemapList, SitemapUrl, SitemapUrlImage, SitemapUrlList } from "./seo/schema.js";
21
+ import { WPK_ListSitemapUrlInput, WPK_RobotRule, WPK_Robots, WPK_Seo, WPK_Sitemap, WPK_SitemapContentType, WPK_SitemapIndex, 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
- import { compare, hash, hmac } from "./shared/crypto.js";
42
+ import { ShaAlgorithm, 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, ShaAlgorithm, Sitemap, SitemapIndex, 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_SitemapIndex, 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
@@ -1,4 +1,5 @@
1
1
  import { authMock, createAuthAdapter } from "./adapters/auth.js";
2
+ import { compare, hash, hmac } from "./shared/crypto.js";
2
3
  import { altcha, arkose, createCaptchaAdapter, friendlyCaptcha, hcaptcha, recaptcha, turnstile } from "./adapters/captcha.js";
3
4
  import { createCookiesAdapter } from "./adapters/cookies.js";
4
5
  import { createGeoAdapter, geoMock } from "./adapters/geo.js";
@@ -22,14 +23,14 @@ import { isWordPressResourceError } from "./wordpress/utils.js";
22
23
  import { WordPressService } from "./wordpress/service.js";
23
24
  import { KizloSettingsService } from "./settings/service.js";
24
25
  import { Service } from "./service.js";
25
- import { compare, hash, hmac } from "./shared/crypto.js";
26
26
  import { Context } from "./context.js";
27
27
  import { deserializeCurrencyFormat, deserializeListMetadata, emptyList, serializeCurrencyFormat } from "./shared/serialize.js";
28
28
  import { parseIdentifier } from "./shared/identifier.js";
29
- import { CONTENT_TYPES, ContentType, ListSitemapUrlInput, ListSitemapUrlInputBase, Robots, RobotsRule, Seo, SeoHead, SeoSchema, Sitemap, SitemapList, SitemapUrl, SitemapUrlImage, SitemapUrlList } from "./seo/schema.js";
29
+ import { CONTENT_TYPES, ContentType, ListSitemapUrlInput, ListSitemapUrlInputBase, Robots, RobotsRule, Seo, SeoHead, SeoSchema, Sitemap, SitemapIndex, 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, SitemapIndex, 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 };