kizlo 0.10.0 → 0.12.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 (58) hide show
  1. package/README.md +4 -4
  2. package/dist/astro/server.d.ts +9 -0
  3. package/dist/astro/server.js +10 -0
  4. package/dist/astro.d.ts +2 -0
  5. package/dist/astro.js +3 -0
  6. package/dist/cli/commands/_setup.js +19 -10
  7. package/dist/cli/commands/create.js +102 -72
  8. package/dist/cli/commands/dev.js +113 -20
  9. package/dist/cli/commands/generate.js +4 -0
  10. package/dist/cli/commands/init.js +111 -57
  11. package/dist/cli/daemon/config.js +13 -7
  12. package/dist/cli/daemon/generate.js +2 -3
  13. package/dist/cli/daemon/jiti.js +101 -0
  14. package/dist/cli/daemon/logger.js +1 -1
  15. package/dist/cli/daemon/reload.js +66 -0
  16. package/dist/cli/daemon/watch.js +7 -4
  17. package/dist/cli/presets/source.js +122 -24
  18. package/dist/cli/presets/template.js +75 -17
  19. package/dist/cli/wp/settings.js +8 -2
  20. package/dist/comment/schema.js +2 -2
  21. package/dist/context.d.ts +9 -0
  22. package/dist/context.js +20 -2
  23. package/dist/index.d.ts +4 -4
  24. package/dist/index.js +3 -3
  25. package/dist/integrations/astro/kizlo.client.d.ts +15 -0
  26. package/dist/integrations/astro/kizlo.client.js +13 -0
  27. package/dist/integrations/astro/kizlo.server.d.ts +10 -0
  28. package/dist/integrations/astro/kizlo.server.js +12 -0
  29. package/dist/integrations/astro/manifest.d.ts +9 -0
  30. package/dist/integrations/astro/manifest.js +11 -0
  31. package/dist/integrations/astro/metadata.d.ts +64 -0
  32. package/dist/integrations/astro/metadata.js +71 -0
  33. package/dist/integrations/astro/robots.d.ts +8 -0
  34. package/dist/integrations/astro/robots.js +11 -0
  35. package/dist/integrations/astro/sitemap.d.ts +16 -0
  36. package/dist/integrations/astro/sitemap.js +20 -0
  37. package/dist/integrations/astro/utils.d.ts +19 -0
  38. package/dist/integrations/astro/utils.js +17 -0
  39. package/dist/integrations/nextjs/metadata.d.ts +1 -0
  40. package/dist/integrations/nextjs/metadata.js +1 -0
  41. package/dist/kizlo.d.ts +8 -1
  42. package/dist/kizlo.js +16 -13
  43. package/dist/nextjs/server.d.ts +2 -1
  44. package/dist/nextjs/server.js +2 -1
  45. package/dist/package.js +1 -1
  46. package/dist/page/schema.js +2 -2
  47. package/dist/post/schema.js +2 -2
  48. package/dist/seo/utils.d.ts +8 -2
  49. package/dist/seo/utils.js +9 -1
  50. package/dist/shared/constants.js +9 -1
  51. package/dist/shared/schema.d.ts +1 -2
  52. package/dist/shared/schema.js +2 -2
  53. package/dist/wordpress/service.d.ts +4 -0
  54. package/dist/wordpress/service.js +5 -0
  55. package/package.json +18 -2
  56. package/dist/cli/presets/base.js +0 -40
  57. package/dist/cli/presets/index.js +0 -16
  58. package/dist/cli/presets/nextjs.js +0 -16
@@ -0,0 +1,64 @@
1
+ import { IconDescriptor } from "../../brand/icons.js";
2
+ import { S2SClient } from "../../kizlo.js";
3
+ import { SeoHead } from "../../seo/schema.js";
4
+ import { renderJsonLd } from "../../seo/utils.js";
5
+
6
+ //#region src/integrations/astro/metadata.d.ts
7
+ /** A `<meta name="theme-color">` entry, optionally scoped to a `prefers-color-scheme` media query. */
8
+ interface ThemeColor {
9
+ media?: string;
10
+ color: string;
11
+ }
12
+ /**
13
+ * Brand + site data for the document `<head>`, resolved from WordPress settings. The `.astro`
14
+ * head component maps this to `<title>`/`<link>`/`<meta>` tags (Astro has no `generateMetadata`,
15
+ * and this package can't compile `.astro`, so the rendering lives in the template).
16
+ */
17
+ interface RootHead {
18
+ /** Web manifest route to link with `rel="manifest"`. */
19
+ manifest: string;
20
+ icons: {
21
+ icon: IconDescriptor[];
22
+ appleTouch: IconDescriptor[];
23
+ };
24
+ /** Site origin used to resolve relative URLs, when set in WordPress. */
25
+ metadataBase?: string;
26
+ /** Browser-chrome theme color(s); a light/dark pair carries `prefers-color-scheme` media queries. */
27
+ themeColor: ThemeColor[];
28
+ }
29
+ /** Per-page SEO tags, mapped from a post's SEO head. Consumed by the template's head component. */
30
+ interface PageHead {
31
+ title: string;
32
+ description?: string;
33
+ canonical: string;
34
+ robots: string;
35
+ og: {
36
+ type: string;
37
+ title: string;
38
+ description?: string;
39
+ url: string;
40
+ siteName: string;
41
+ locale: string;
42
+ image?: {
43
+ url: string;
44
+ width?: number;
45
+ height?: number;
46
+ alt?: string;
47
+ };
48
+ };
49
+ twitter: {
50
+ card: string;
51
+ title: string;
52
+ description?: string;
53
+ site?: string;
54
+ creator?: string;
55
+ image?: string;
56
+ imageAlt?: string;
57
+ };
58
+ }
59
+ /** Resolve the root brand/site head from WordPress settings (icons, manifest, theme color, base URL). */
60
+ declare function resolveRootHead(client: S2SClient<[]>): Promise<RootHead>;
61
+ /** Map a post's SEO head to the flat descriptors the template's head component renders. */
62
+ declare function resolvePageHead(head: SeoHead): PageHead;
63
+ //#endregion
64
+ export { PageHead, RootHead, ThemeColor, resolvePageHead, resolveRootHead };
@@ -0,0 +1,71 @@
1
+ import { resolveIcons } from "../../brand/icons.js";
2
+ import { WEB_MANIFEST_ROUTE } from "../../brand/manifest.js";
3
+ import { renderJsonLd } from "../../seo/utils.js";
4
+
5
+ //#region src/integrations/astro/metadata.ts
6
+ /** Resolve the root brand/site head from WordPress settings (icons, manifest, theme color, base URL). */
7
+ async function resolveRootHead(client) {
8
+ const settings = await client.settings.get.call();
9
+ const { icon, appleTouch } = resolveIcons(settings.brand);
10
+ const light = settings.brand.theme_color;
11
+ const dark = settings.brand.theme_color_dark;
12
+ const themeColor = [];
13
+ if (light) themeColor.push(dark ? {
14
+ media: "(prefers-color-scheme: light)",
15
+ color: light
16
+ } : { color: light });
17
+ if (dark) themeColor.push({
18
+ media: "(prefers-color-scheme: dark)",
19
+ color: dark
20
+ });
21
+ return {
22
+ manifest: WEB_MANIFEST_ROUTE,
23
+ icons: {
24
+ icon,
25
+ appleTouch
26
+ },
27
+ metadataBase: settings.site.url || void 0,
28
+ themeColor
29
+ };
30
+ }
31
+ /** Map a post's SEO head to the flat descriptors the template's head component renders. */
32
+ function resolvePageHead(head) {
33
+ return {
34
+ title: head.title,
35
+ description: head.og.description || void 0,
36
+ canonical: head.canonical,
37
+ robots: [
38
+ head.robots.index,
39
+ head.robots.follow,
40
+ head.robots.maxSnippet,
41
+ head.robots.maxImagePreview,
42
+ head.robots.maxVideoPreview
43
+ ].join(", "),
44
+ og: {
45
+ type: head.og.type,
46
+ title: head.og.title,
47
+ description: head.og.description || void 0,
48
+ url: head.og.url,
49
+ siteName: head.og.siteName,
50
+ locale: head.og.locale,
51
+ image: head.og.image ? {
52
+ url: head.og.image.url,
53
+ width: head.og.image.width ?? void 0,
54
+ height: head.og.image.height ?? void 0,
55
+ alt: head.og.image.alt ?? void 0
56
+ } : void 0
57
+ },
58
+ twitter: {
59
+ card: head.twitter.card,
60
+ title: head.twitter.title,
61
+ description: head.twitter.description || void 0,
62
+ site: head.twitter.site ?? void 0,
63
+ creator: head.twitter.creator ?? void 0,
64
+ image: head.twitter.image ?? void 0,
65
+ imageAlt: head.twitter.imageAlt ?? void 0
66
+ }
67
+ };
68
+ }
69
+
70
+ //#endregion
71
+ export { resolvePageHead, resolveRootHead };
@@ -0,0 +1,8 @@
1
+ import { S2SClient } from "../../kizlo.js";
2
+ import { AstroRoute } from "./utils.js";
3
+
4
+ //#region src/integrations/astro/robots.d.ts
5
+ /** Astro endpoint for `src/pages/robots.txt.ts`. SSR refetches live on each request. */
6
+ declare function createRobotsRoute(client: S2SClient<[]>): AstroRoute;
7
+ //#endregion
8
+ export { createRobotsRoute };
@@ -0,0 +1,11 @@
1
+ import { createRobotsRoute } from "../../seo/robots.js";
2
+
3
+ //#region src/integrations/astro/robots.ts
4
+ /** Astro endpoint for `src/pages/robots.txt.ts`. SSR refetches live on each request. */
5
+ function createRobotsRoute$1(client) {
6
+ const handler = createRobotsRoute(client);
7
+ return (context) => handler(context.request);
8
+ }
9
+
10
+ //#endregion
11
+ export { createRobotsRoute$1 as createRobotsRoute };
@@ -0,0 +1,16 @@
1
+ import { S2SClient } from "../../kizlo.js";
2
+ import { CreateSitemapRouteOptions } from "../../seo/sitemap.js";
3
+ import { AstroRoute } from "./utils.js";
4
+
5
+ //#region src/integrations/astro/sitemap.d.ts
6
+
7
+ /**
8
+ * Astro endpoint for `src/pages/sitemaps/[sitemap].xml.ts`. The core handler reads the slug from the
9
+ * request URL's last segment (`/sitemaps/posts.xml` → `posts.xml`), so forwarding `context.request`
10
+ * needs no `params` plumbing.
11
+ */
12
+ declare function createSitemapRoute(client: S2SClient<[]>, options?: CreateSitemapRouteOptions): AstroRoute;
13
+ /** Astro endpoint for `src/pages/sitemap.xml.ts`: 308-redirects the well-known path to the index. */
14
+ declare function createSitemapRedirectRoute(): AstroRoute;
15
+ //#endregion
16
+ export { createSitemapRedirectRoute, createSitemapRoute };
@@ -0,0 +1,20 @@
1
+ import { createSitemapRedirectRoute, createSitemapRoute } from "../../seo/sitemap.js";
2
+
3
+ //#region src/integrations/astro/sitemap.ts
4
+ /**
5
+ * Astro endpoint for `src/pages/sitemaps/[sitemap].xml.ts`. The core handler reads the slug from the
6
+ * request URL's last segment (`/sitemaps/posts.xml` → `posts.xml`), so forwarding `context.request`
7
+ * needs no `params` plumbing.
8
+ */
9
+ function createSitemapRoute$1(client, options) {
10
+ const handler = createSitemapRoute(client, options);
11
+ return (context) => handler(context.request);
12
+ }
13
+ /** Astro endpoint for `src/pages/sitemap.xml.ts`: 308-redirects the well-known path to the index. */
14
+ function createSitemapRedirectRoute$1() {
15
+ const handler = createSitemapRedirectRoute();
16
+ return (context) => handler(context.request);
17
+ }
18
+
19
+ //#endregion
20
+ export { createSitemapRedirectRoute$1 as createSitemapRedirectRoute, createSitemapRoute$1 as createSitemapRoute };
@@ -0,0 +1,19 @@
1
+ //#region src/integrations/astro/utils.d.ts
2
+ /**
3
+ * Minimal shape of Astro's `APIContext` this integration needs — just the standard web `Request`.
4
+ * Typed structurally so the package never has to depend on `astro` (which stays an optional peer).
5
+ */
6
+ interface AstroRouteContext {
7
+ request: Request;
8
+ }
9
+ /** An Astro endpoint handler (`export const GET = …`): takes the route context, returns a `Response`. */
10
+ type AstroRoute = (context: AstroRouteContext) => Promise<Response> | Response;
11
+ /**
12
+ * The public Kizlo API base URL, read from `process.env` as a last-resort fallback for a client built
13
+ * without an explicit `url`. The server factory (`createKizlo`) reads env the Astro-native way through
14
+ * `astro:env/server`, and client-side code passes `import.meta.env.PUBLIC_KIZLO_API_URL` at the call
15
+ * site (see the template's `client.ts`); this fallback only matters if neither is supplied.
16
+ */
17
+ declare function getServerBaseUrl(): string;
18
+ //#endregion
19
+ export { AstroRoute, AstroRouteContext, getServerBaseUrl };
@@ -0,0 +1,17 @@
1
+ import { KizloError } from "../../shared/error.js";
2
+
3
+ //#region src/integrations/astro/utils.ts
4
+ /**
5
+ * The public Kizlo API base URL, read from `process.env` as a last-resort fallback for a client built
6
+ * without an explicit `url`. The server factory (`createKizlo`) reads env the Astro-native way through
7
+ * `astro:env/server`, and client-side code passes `import.meta.env.PUBLIC_KIZLO_API_URL` at the call
8
+ * site (see the template's `client.ts`); this fallback only matters if neither is supplied.
9
+ */
10
+ function getServerBaseUrl() {
11
+ const baseUrl = process.env.PUBLIC_KIZLO_API_URL?.trim();
12
+ if (!baseUrl) throw new KizloError("MISSING_ENV_VARIABLE", { message: "Please define PUBLIC_KIZLO_API_URL in your .env file." });
13
+ return baseUrl;
14
+ }
15
+
16
+ //#endregion
17
+ export { getServerBaseUrl };
@@ -1,5 +1,6 @@
1
1
  import { S2SClient } from "../../kizlo.js";
2
2
  import { SeoHead } from "../../seo/schema.js";
3
+ import { renderJsonLd } from "../../seo/utils.js";
3
4
  import { Metadata, Viewport } from "next";
4
5
 
5
6
  //#region src/integrations/nextjs/metadata.d.ts
@@ -1,5 +1,6 @@
1
1
  import { resolveIcons } from "../../brand/icons.js";
2
2
  import { WEB_MANIFEST_ROUTE } from "../../brand/manifest.js";
3
+ import { renderJsonLd } from "../../seo/utils.js";
3
4
 
4
5
  //#region src/integrations/nextjs/metadata.ts
5
6
  function createRootMetadata(client) {
package/dist/kizlo.d.ts CHANGED
@@ -75,6 +75,12 @@ interface CreateKizloOptions<TExts extends readonly AnyExtension[] = []> {
75
75
  credentials?: Partial<WordPressCredentials>;
76
76
  };
77
77
  }
78
+ /**
79
+ * Reads a single environment variable by name. Defaults to `process.env`, but framework factories can
80
+ * pass their own source: the Astro factory hands in `getSecret` from `astro:env/server`, so `.env` is
81
+ * resolved the Astro-native way (through the dev server / adapter) rather than through `process.env`.
82
+ */
83
+ type EnvReader = (name: string) => string | undefined;
78
84
  /**
79
85
  * Resolves a full `KizloConfig` from options and the environment. Shared by the
80
86
  * base `createKizlo` and each framework factory — the single place env values
@@ -83,6 +89,7 @@ interface CreateKizloOptions<TExts extends readonly AnyExtension[] = []> {
83
89
  declare function resolveKizloConfig<TExts extends readonly AnyExtension[]>(options: CreateKizloOptions<TExts> | undefined, defaults: {
84
90
  baseUrlEnvKey: string;
85
91
  adapters?: ServiceAdapters;
92
+ env?: EnvReader;
86
93
  }): KizloConfig<TExts>;
87
94
  /**
88
95
  * Creates a Kizlo server from the environment (`KIZLO_API_URL`, `KIZLO_WP_SECRET`,
@@ -91,4 +98,4 @@ declare function resolveKizloConfig<TExts extends readonly AnyExtension[]>(optio
91
98
  */
92
99
  declare function createKizlo<TExts extends readonly AnyExtension[] = []>(options?: CreateKizloOptions<TExts>): Kizlo<TExts>;
93
100
  //#endregion
94
- export { AnyKizloConfig, CreateKizloOptions, Kizlo, KizloConfig, KizloConnect, RootRouter, S2SClient, ServiceAdapters, createKizlo, resolveKizloConfig };
101
+ export { AnyKizloConfig, CreateKizloOptions, EnvReader, Kizlo, KizloConfig, KizloConnect, RootRouter, S2SClient, ServiceAdapters, createKizlo, resolveKizloConfig };
package/dist/kizlo.js CHANGED
@@ -1,4 +1,4 @@
1
- import { RPC_PROTOCOL_HEADER } from "./shared/constants.js";
1
+ import { CONTRACT_GENERATION_ENV, RPC_PROTOCOL_HEADER } from "./shared/constants.js";
2
2
  import { KizloError } from "./shared/error.js";
3
3
  import { createOrpcRouter } from "./shared/router.js";
4
4
  import { createResultClient } from "./shared/result.js";
@@ -100,14 +100,16 @@ var Kizlo = class {
100
100
  };
101
101
  }
102
102
  };
103
- function requireEnv(name) {
104
- const value = process.env[name]?.trim();
105
- if (!value) throw new KizloError("MISSING_ENV_VARIABLE", { message: `Please define ${name} in your .env file.` });
106
- return value;
103
+ const processEnvReader = (name) => process.env[name];
104
+ function requireEnv(name, env) {
105
+ const value = env(name)?.trim();
106
+ if (value) return value;
107
+ if (process.env[CONTRACT_GENERATION_ENV]) return `__kizlo_contract_generation__:${name}`;
108
+ throw new KizloError("MISSING_ENV_VARIABLE", { message: `Please define ${name} in your .env file.` });
107
109
  }
108
- function resolveConnect(option) {
110
+ function resolveConnect(option, env) {
109
111
  if (option) return option;
110
- const value = process.env.KIZLO_CONNECT?.trim();
112
+ const value = env("KIZLO_CONNECT")?.trim();
111
113
  if (!value) return "remote";
112
114
  if (value !== "local" && value !== "remote") throw new KizloError("INVALID_ENV_VARIABLE", { message: `KIZLO_CONNECT must be "local" or "remote", got "${value}".` });
113
115
  return value;
@@ -128,12 +130,13 @@ function connectEnvKeys(connect) {
128
130
  * and credentials are read. `baseUrlEnvKey` is the only framework-varying part.
129
131
  */
130
132
  function resolveKizloConfig(options, defaults) {
133
+ const env = defaults.env ?? processEnvReader;
131
134
  const credentials = options?.wordpress?.credentials;
132
- const connect = resolveConnect(options?.connect);
135
+ const connect = resolveConnect(options?.connect, env);
133
136
  const keys = connectEnvKeys(connect);
134
137
  return {
135
- baseUrl: options?.baseUrl ?? requireEnv(defaults.baseUrlEnvKey),
136
- siteSecret: options?.siteSecret ?? requireEnv(keys.siteSecret),
138
+ baseUrl: options?.baseUrl ?? requireEnv(defaults.baseUrlEnvKey, env),
139
+ siteSecret: options?.siteSecret ?? requireEnv(keys.siteSecret, env),
137
140
  environment: options?.environment ?? process.env.NODE_ENV ?? "development",
138
141
  connect,
139
142
  extensions: options?.extensions,
@@ -142,9 +145,9 @@ function resolveKizloConfig(options, defaults) {
142
145
  ...options?.adapters
143
146
  },
144
147
  credentials: {
145
- url: credentials?.url ?? requireEnv(keys.url),
146
- username: credentials?.username ?? requireEnv(keys.username),
147
- password: credentials?.password ?? requireEnv(keys.password)
148
+ url: credentials?.url ?? requireEnv(keys.url, env),
149
+ username: credentials?.username ?? requireEnv(keys.username, env),
150
+ password: credentials?.password ?? requireEnv(keys.password, env)
148
151
  }
149
152
  };
150
153
  }
@@ -1,4 +1,5 @@
1
1
  import { WEB_MANIFEST_ROUTE } from "../brand/manifest.js";
2
+ import { renderJsonLd } from "../seo/utils.js";
2
3
  import { createSitemapRedirectRoute } from "../seo/sitemap.js";
3
4
  import { NextRevalidateOptions, RevalidatePathFn, RevalidateTagFn, RevalidateTarget, nextRevalidation } from "../integrations/nextjs/revalidate.js";
4
5
  import { KizloServerOptions, createKizlo } from "../integrations/nextjs/kizlo.server.js";
@@ -7,4 +8,4 @@ import { createPageMetadata, createRootMetadata, createRootViewport } from "../i
7
8
  import { ROBOTS_CACHE_TAG, createRobotsRoute } from "../integrations/nextjs/robots.js";
8
9
  import { SITEMAP_CACHE_TAG, SITEMAP_ROUTE, createSitemapRoute } from "../integrations/nextjs/sitemap.js";
9
10
  import { createCachedTextRoute, createNextCookiesInterface, getServerBaseUrl } from "../integrations/nextjs/utils.js";
10
- export { KizloServerOptions, MANIFEST_CACHE_TAG, NextRevalidateOptions, ROBOTS_CACHE_TAG, RevalidatePathFn, RevalidateTagFn, RevalidateTarget, SITEMAP_CACHE_TAG, SITEMAP_ROUTE, WEB_MANIFEST_ROUTE, createCachedTextRoute, createKizlo, createManifestRoute, createNextCookiesInterface, createPageMetadata, createRobotsRoute, createRootMetadata, createRootViewport, createSitemapRedirectRoute, createSitemapRoute, getServerBaseUrl, nextRevalidation };
11
+ export { KizloServerOptions, MANIFEST_CACHE_TAG, NextRevalidateOptions, ROBOTS_CACHE_TAG, RevalidatePathFn, RevalidateTagFn, RevalidateTarget, SITEMAP_CACHE_TAG, SITEMAP_ROUTE, WEB_MANIFEST_ROUTE, createCachedTextRoute, createKizlo, createManifestRoute, createNextCookiesInterface, createPageMetadata, createRobotsRoute, createRootMetadata, createRootViewport, createSitemapRedirectRoute, createSitemapRoute, getServerBaseUrl, nextRevalidation, renderJsonLd };
@@ -1,4 +1,5 @@
1
1
  import { WEB_MANIFEST_ROUTE } from "../brand/manifest.js";
2
+ import { renderJsonLd } from "../seo/utils.js";
2
3
  import { createSitemapRedirectRoute } from "../seo/sitemap.js";
3
4
  import { createCachedTextRoute, createNextCookiesInterface, getServerBaseUrl } from "../integrations/nextjs/utils.js";
4
5
  import { ROBOTS_CACHE_TAG, createRobotsRoute } from "../integrations/nextjs/robots.js";
@@ -8,4 +9,4 @@ import { createKizlo } from "../integrations/nextjs/kizlo.server.js";
8
9
  import { MANIFEST_CACHE_TAG, createManifestRoute } from "../integrations/nextjs/manifest.js";
9
10
  import { createPageMetadata, createRootMetadata, createRootViewport } from "../integrations/nextjs/metadata.js";
10
11
 
11
- export { MANIFEST_CACHE_TAG, ROBOTS_CACHE_TAG, SITEMAP_CACHE_TAG, SITEMAP_ROUTE, WEB_MANIFEST_ROUTE, createCachedTextRoute, createKizlo, createManifestRoute, createNextCookiesInterface, createPageMetadata, createRobotsRoute, createRootMetadata, createRootViewport, createSitemapRedirectRoute, createSitemapRoute, getServerBaseUrl, nextRevalidation };
12
+ export { MANIFEST_CACHE_TAG, ROBOTS_CACHE_TAG, SITEMAP_CACHE_TAG, SITEMAP_ROUTE, WEB_MANIFEST_ROUTE, createCachedTextRoute, createKizlo, createManifestRoute, createNextCookiesInterface, createPageMetadata, createRobotsRoute, createRootMetadata, createRootViewport, createSitemapRedirectRoute, createSitemapRoute, getServerBaseUrl, nextRevalidation, renderJsonLd };
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.10.0";
2
+ var version = "0.12.0";
3
3
 
4
4
  //#endregion
5
5
  export { version };
@@ -1,7 +1,7 @@
1
- import { IdentifierInput, ListMetadata, ListOrder, Media } from "../shared/schema.js";
1
+ import { IdentifierInput, ListMetadata, ListOrder } from "../shared/schema.js";
2
2
  import { WP_PAGE_ORDER_BYES, WP_PAGE_STATUSES } from "../wordpress/page/types.js";
3
3
  import { Seo } from "../seo/schema.js";
4
- import { Metadata, NumberLike, arrayable, lenient } from "@kizlo/shared";
4
+ import { Media, Metadata, NumberLike, arrayable, lenient } from "@kizlo/shared";
5
5
  import z from "zod/v4";
6
6
 
7
7
  //#region src/page/schema.ts
@@ -1,7 +1,7 @@
1
- import { IdentifierInput, ListMetadata, ListOrder, Media } from "../shared/schema.js";
1
+ import { IdentifierInput, ListMetadata, ListOrder } from "../shared/schema.js";
2
2
  import { WP_POST_FORMATS, WP_POST_ORDER_BYES, WP_POST_STATUSES, WP_POST_TAX_RELATIONS } from "../wordpress/post/types.js";
3
3
  import { Seo } from "../seo/schema.js";
4
- import { BooleanLike, Metadata, NumberLike, arrayable, lenient } from "@kizlo/shared";
4
+ import { BooleanLike, Media, Metadata, NumberLike, arrayable, lenient } from "@kizlo/shared";
5
5
  import z from "zod/v4";
6
6
 
7
7
  //#region src/post/schema.ts
@@ -1,8 +1,14 @@
1
- import { Seo, Sitemap, SitemapUrl } from "./schema.js";
1
+ import { Seo, SeoSchema, Sitemap, SitemapUrl } from "./schema.js";
2
2
  import { WPK_Seo } from "./types.js";
3
3
 
4
4
  //#region src/seo/utils.d.ts
5
5
  declare function deserializeSeo(data: WPK_Seo): Seo;
6
+ /**
7
+ * Serialize a post's JSON-LD (`data.seo.schema`) for a `<script type="application/ld+json">` tag.
8
+ * Returns `null` when the `@graph` is empty so the template can skip the tag entirely. The result
9
+ * is JSON, safe to inject as script-element text content.
10
+ */
11
+ declare function renderJsonLd(schema: SeoSchema): string | null;
6
12
  type SitemapIndexEntry = Pick<Sitemap, "key" | "pages" | "lastmod">;
7
13
  declare function sitemapEntryPath(key: string, page: number): string;
8
14
  declare function parseSitemapSlug(slug: string): {
@@ -13,4 +19,4 @@ declare function renderSitemapIndex(sitemaps: readonly SitemapIndexEntry[], orig
13
19
  declare function renderUrlSet(urls: SitemapUrl[]): string;
14
20
  declare function xmlResponse(body: string): Response;
15
21
  //#endregion
16
- export { SitemapIndexEntry, deserializeSeo, parseSitemapSlug, renderSitemapIndex, renderUrlSet, sitemapEntryPath, xmlResponse };
22
+ export { SitemapIndexEntry, deserializeSeo, parseSitemapSlug, renderJsonLd, renderSitemapIndex, renderUrlSet, sitemapEntryPath, xmlResponse };
package/dist/seo/utils.js CHANGED
@@ -41,6 +41,14 @@ function deserializeSeo(data) {
41
41
  schema: data.schema
42
42
  };
43
43
  }
44
+ /**
45
+ * Serialize a post's JSON-LD (`data.seo.schema`) for a `<script type="application/ld+json">` tag.
46
+ * Returns `null` when the `@graph` is empty so the template can skip the tag entirely. The result
47
+ * is JSON, safe to inject as script-element text content.
48
+ */
49
+ function renderJsonLd(schema) {
50
+ return schema["@graph"].length ? JSON.stringify(schema) : null;
51
+ }
44
52
  function sitemapEntryPath(key, page) {
45
53
  return `/sitemaps/${key}${page > 1 ? `-${page}` : ""}.xml`;
46
54
  }
@@ -75,4 +83,4 @@ function xmlResponse(body) {
75
83
  }
76
84
 
77
85
  //#endregion
78
- export { deserializeSeo, parseSitemapSlug, renderSitemapIndex, renderUrlSet, sitemapEntryPath, xmlResponse };
86
+ export { deserializeSeo, parseSitemapSlug, renderJsonLd, renderSitemapIndex, renderUrlSet, sitemapEntryPath, xmlResponse };
@@ -5,6 +5,14 @@ import { milliseconds } from "@kizlo/shared";
5
5
  const VERSION = version;
6
6
  const RPC_PROTOCOL_HEADER = "x-rpc-protocol";
7
7
  const SESSION_EXPIRY_MARGIN_MS = milliseconds("5 minutes");
8
+ /**
9
+ * Set in `process.env` by the CLI daemon while it imports the server module solely to read the
10
+ * router's *shape* for contract generation. No request is served in that mode, so `requireEnv`
11
+ * returns a placeholder instead of aborting the import when connection env vars are absent — the
12
+ * router shape doesn't depend on any env value, and framework virtual modules (e.g. Astro's
13
+ * `getSecret`) are stubbed to `undefined` during the shape-only import anyway.
14
+ */
15
+ const CONTRACT_GENERATION_ENV = "KIZLO_CONTRACT_GENERATION";
8
16
 
9
17
  //#endregion
10
- export { RPC_PROTOCOL_HEADER };
18
+ export { CONTRACT_GENERATION_ENV, RPC_PROTOCOL_HEADER };
@@ -1,4 +1,3 @@
1
- import { Media } from "@kizlo/shared";
2
1
  import z from "zod/v4";
3
2
 
4
3
  //#region src/shared/schema.d.ts
@@ -46,4 +45,4 @@ declare const PreviewTokenData: z.ZodObject<{
46
45
  }, z.core.$strip>;
47
46
  type PreviewTokenData = z.infer<typeof PreviewTokenData>;
48
47
  //#endregion
49
- export { CurrencyFormat, IdentifierInput, LIST_ORDER, ListMetadata, ListOrder, Media, PreviewTokenData, PreviewTokenPayload };
48
+ export { CurrencyFormat, IdentifierInput, LIST_ORDER, ListMetadata, ListOrder, PreviewTokenData, PreviewTokenPayload };
@@ -1,4 +1,4 @@
1
- import { Media, NumberLike } from "@kizlo/shared";
1
+ import { NumberLike } from "@kizlo/shared";
2
2
  import z from "zod/v4";
3
3
 
4
4
  //#region src/shared/schema.ts
@@ -34,4 +34,4 @@ const PreviewTokenData = z.object({
34
34
  });
35
35
 
36
36
  //#endregion
37
- export { CurrencyFormat, IdentifierInput, LIST_ORDER, ListMetadata, ListOrder, Media, PreviewTokenData, PreviewTokenPayload };
37
+ export { CurrencyFormat, IdentifierInput, LIST_ORDER, ListMetadata, ListOrder, PreviewTokenData, PreviewTokenPayload };
@@ -20,9 +20,13 @@ declare class WordPressService {
20
20
  private readonly siteBase;
21
21
  private readonly authHeader;
22
22
  private readonly defaultTimeout;
23
+ /** Called with the response headers of every non-transport-error response, so callers can observe
24
+ * per-response signals (e.g. the plugin version header) without threading them through every method. */
25
+ private readonly onResponse?;
23
26
  constructor(context: {
24
27
  credentials: WordPressCredentials;
25
28
  timeout?: Duration;
29
+ onResponse?: (headers: Headers) => void;
26
30
  });
27
31
  post<TData, TCode extends string = never>(path: string, input: {
28
32
  body?: unknown;
@@ -22,6 +22,9 @@ var WordPressService = class {
22
22
  siteBase;
23
23
  authHeader;
24
24
  defaultTimeout;
25
+ /** Called with the response headers of every non-transport-error response, so callers can observe
26
+ * per-response signals (e.g. the plugin version header) without threading them through every method. */
27
+ onResponse;
25
28
  constructor(context) {
26
29
  const credentialBytes = new TextEncoder().encode(`${context.credentials.username}:${context.credentials.password}`);
27
30
  let binary = "";
@@ -29,6 +32,7 @@ var WordPressService = class {
29
32
  this.authHeader = `${WP_AUTH_TYPE} ${btoa(binary)}`;
30
33
  this.siteBase = context.credentials.url.endsWith("/") ? context.credentials.url : `${context.credentials.url}/`;
31
34
  this.defaultTimeout = context.timeout ?? SAFE_REQUEST_TIMEOUT;
35
+ this.onResponse = context.onResponse;
32
36
  }
33
37
  async post(path, input) {
34
38
  return this.request({
@@ -103,6 +107,7 @@ var WordPressService = class {
103
107
  message: transportErr.message
104
108
  })
105
109
  };
110
+ this.onResponse?.(response.headers);
106
111
  const [readErr, text] = await tryCatch(response.text());
107
112
  if (readErr) return {
108
113
  data: null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kizlo",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "description": "A headless WordPress framework for TypeScript.",
6
6
  "keywords": [
@@ -63,6 +63,18 @@
63
63
  "types": "./dist/nextjs/server.d.ts",
64
64
  "default": "./dist/nextjs/server.js"
65
65
  }
66
+ },
67
+ "./astro": {
68
+ "import": {
69
+ "types": "./dist/astro.d.ts",
70
+ "default": "./dist/astro.js"
71
+ }
72
+ },
73
+ "./astro/server": {
74
+ "import": {
75
+ "types": "./dist/astro/server.d.ts",
76
+ "default": "./dist/astro/server.js"
77
+ }
66
78
  }
67
79
  },
68
80
  "files": [
@@ -76,7 +88,7 @@
76
88
  },
77
89
  "dependencies": {
78
90
  "@clack/prompts": "^1.5.1",
79
- "@kizlo/shared": "0.5.1",
91
+ "@kizlo/shared": "0.6.0",
80
92
  "@orpc/client": "^1.14.3",
81
93
  "@orpc/contract": "^1.14.3",
82
94
  "@orpc/openapi": "^1.14.3",
@@ -93,11 +105,15 @@
93
105
  "zod": "^4.3.6"
94
106
  },
95
107
  "peerDependencies": {
108
+ "astro": "^7.0.0",
96
109
  "next": "^14.0.0 || ^15.0.0 || ^16.0.0",
97
110
  "react": "^18.0.0 || ^19.0.0",
98
111
  "react-dom": "^18.0.0 || ^19.0.0"
99
112
  },
100
113
  "peerDependenciesMeta": {
114
+ "astro": {
115
+ "optional": true
116
+ },
101
117
  "next": {
102
118
  "optional": true
103
119
  },
@@ -1,40 +0,0 @@
1
- import { DEFAULT_ENV_KEYS } from "../utils.js";
2
-
3
- //#region src/cli/presets/base.ts
4
- const base = {
5
- id: "base",
6
- label: "Generic (no framework detected)",
7
- detect: () => 1,
8
- envKeys: DEFAULT_ENV_KEYS,
9
- scaffolds(ctx) {
10
- return [{
11
- label: "Kizlo server instance",
12
- relPath: ctx.serverEntryPath,
13
- contents: `import { createKizlo } from "kizlo"
14
-
15
- export const { router, client, context, handler } = createKizlo()
16
- `
17
- }, {
18
- label: "Browser client",
19
- relPath: ctx.clientPath,
20
- contents: clientEntry(ctx)
21
- }];
22
- }
23
- };
24
- function clientEntry(ctx) {
25
- if (ctx.clientUrl) return `import { createKizloClient } from "kizlo"
26
- import { contract } from "./${ctx.serverDirName}/generated"
27
-
28
- // Your Kizlo backend runs on a different origin than this app. Swap in your bundler's
29
- // public env var (e.g. import.meta.env.VITE_KIZLO_API_URL) to set it per environment.
30
- export const client = createKizloClient(contract, { url: "${ctx.clientUrl}" })
31
- `;
32
- return `import { createKizloClient } from "kizlo"
33
- import { contract } from "./${ctx.serverDirName}/generated"
34
-
35
- export const client = createKizloClient(contract)
36
- `;
37
- }
38
-
39
- //#endregion
40
- export { base };
@@ -1,16 +0,0 @@
1
- import { base } from "./base.js";
2
- import { nextjs } from "./nextjs.js";
3
-
4
- //#region src/cli/presets/index.ts
5
- /** Framework presets, in declaration order. `base` always matches last. */
6
- const PRESETS = [nextjs, base];
7
- function getPreset(id) {
8
- return PRESETS.find((preset) => preset.id === id);
9
- }
10
- /** Highest-scoring preset for the project; falls back to `base`. */
11
- function detectPreset(ctx) {
12
- return PRESETS.reduce((best, preset) => preset.detect(ctx) > best.detect(ctx) ? preset : best);
13
- }
14
-
15
- //#endregion
16
- export { PRESETS, detectPreset, getPreset };
@@ -1,16 +0,0 @@
1
- //#region src/cli/presets/nextjs.ts
2
- const nextjs = {
3
- id: "nextjs",
4
- label: "Next.js",
5
- detect(ctx) {
6
- return {
7
- ...ctx.pkg.dependencies,
8
- ...ctx.pkg.devDependencies
9
- }.next ? 100 : 0;
10
- },
11
- apiPath: "/api/kizlo",
12
- template: "nextjs"
13
- };
14
-
15
- //#endregion
16
- export { nextjs };