kizlo 0.10.0 → 0.11.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.
- package/README.md +4 -4
- package/dist/cli/commands/_setup.js +11 -2
- package/dist/cli/commands/create.js +4 -4
- package/dist/cli/commands/dev.js +113 -20
- package/dist/cli/commands/init.js +5 -1
- package/dist/cli/daemon/config.js +1 -1
- package/dist/cli/daemon/logger.js +1 -1
- package/dist/cli/daemon/reload.js +66 -0
- package/dist/cli/daemon/watch.js +0 -1
- package/dist/cli/presets/template.js +29 -17
- package/dist/cli/wp/settings.js +8 -2
- package/dist/comment/schema.js +2 -2
- package/dist/context.d.ts +9 -0
- package/dist/context.js +20 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/package.js +1 -1
- package/dist/page/schema.js +2 -2
- package/dist/post/schema.js +2 -2
- package/dist/shared/schema.d.ts +1 -2
- package/dist/shared/schema.js +2 -2
- package/dist/wordpress/service.d.ts +4 -0
- package/dist/wordpress/service.js +5 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -23,16 +23,16 @@ Kizlo is an open-source framework for building content-driven applications with
|
|
|
23
23
|
|
|
24
24
|
## Quick start
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Scaffold a new project with Kizlo already wired:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
npx kizlo@latest
|
|
29
|
+
npx kizlo@latest create
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Or add Kizlo to an existing project:
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
npx kizlo
|
|
35
|
+
npx kizlo@latest init
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
See the [docs](https://kizlo.io/docs) for the full guide.
|
|
@@ -7,6 +7,7 @@ import { bootstrapDev } from "../wp/dev.js";
|
|
|
7
7
|
import { removeProjectContainers } from "../wp/session.js";
|
|
8
8
|
import { syncSiteSettings } from "../wp/settings.js";
|
|
9
9
|
import { devStack } from "../wp/stack.js";
|
|
10
|
+
import { isPluginVersionSupported, pluginUpdateMessage } from "@kizlo/shared";
|
|
10
11
|
import z from "zod/v4";
|
|
11
12
|
import path from "node:path";
|
|
12
13
|
import fs from "node:fs";
|
|
@@ -80,7 +81,11 @@ function managedEnv(envKeys, conn) {
|
|
|
80
81
|
};
|
|
81
82
|
}
|
|
82
83
|
const requiredString = z.string().trim().min(1, "Required");
|
|
83
|
-
const urlString = requiredString.pipe(z.url(
|
|
84
|
+
const urlString = requiredString.pipe(z.url({
|
|
85
|
+
protocol: /^https?$/,
|
|
86
|
+
hostname: /^(localhost|(\d{1,3}\.){3}\d{1,3}|([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})$/,
|
|
87
|
+
error: "Must be a valid http(s) URL with a real host (e.g. https://example.com)"
|
|
88
|
+
}));
|
|
84
89
|
const dirPath = requiredString.refine((value) => !value.endsWith(".ts"), "Enter a directory, not a file");
|
|
85
90
|
function validate(schema) {
|
|
86
91
|
return (value) => {
|
|
@@ -382,7 +387,11 @@ async function syncRemote(conn) {
|
|
|
382
387
|
siteUrl: conn.siteUrl,
|
|
383
388
|
backendUrl: conn.baseUrl
|
|
384
389
|
});
|
|
385
|
-
if (!sync.ok)
|
|
390
|
+
if (!sync.ok) {
|
|
391
|
+
p.log.warn(`Could not sync the site settings to WordPress (${sync.error}) — make sure the kizlo plugin is active, then set them from the Kizlo settings.`);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (!isPluginVersionSupported(sync.pluginVersion)) p.log.warn(pluginUpdateMessage(sync.pluginVersion));
|
|
386
395
|
}
|
|
387
396
|
/** The "Next steps" lines. `kizlo dev` is the single entry point for development. */
|
|
388
397
|
function nextStepsLines(conn, prefix = "") {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { availablePackageManagers, detectInvokingPackageManager, ensureGitignored, frameworkCreateArgs, installArgs, runCommandAsync, runCommandCaptured } from "../utils.js";
|
|
2
2
|
import { getPreset } from "../presets/index.js";
|
|
3
3
|
import { fetchTemplate } from "../presets/source.js";
|
|
4
|
-
import { adaptFile, changesFor, fileEntries,
|
|
4
|
+
import { adaptFile, changesFor, fileEntries, minCliError, patchEntries, readManifest, resolveDependencies } from "../presets/template.js";
|
|
5
5
|
import { collectConnectionInteractively, nextStepsLines, orCancel, pickAppPort, readEnvExample, resolveEnvKeys, setupLocalWordPress, syncRemote, validate, withApiPath, writeEnv } from "./_setup.js";
|
|
6
6
|
import { applyLayoutPatches, buildScaffoldContext, kizloConfigTemplate, reportScaffold, scaffoldFile, writeGeneratedContract } from "./_wiring.js";
|
|
7
7
|
import z from "zod/v4";
|
|
@@ -63,8 +63,8 @@ function bootstrapArgs(manifest, pm, name) {
|
|
|
63
63
|
* Layer Kizlo's wiring onto a freshly bootstrapped app in `dir`, from an already-fetched template
|
|
64
64
|
* directory + manifest. The framework CLI has produced the base project (package.json, config,
|
|
65
65
|
* tsconfig, the root layout); this drives the manifest — the same engine `init` uses — to record the
|
|
66
|
-
* `kizlo` dependency, write `kizlo.config.ts`, scaffold the manifest's files (
|
|
67
|
-
*
|
|
66
|
+
* `kizlo` dependency, write `kizlo.config.ts`, scaffold the manifest's files (core wiring from `base`
|
|
67
|
+
* and `create` always, the `example`-flagged demo pages only when `includeExamples`),
|
|
68
68
|
* seed the generated contract, and ignore `.env` and the `.kizlo/` working dir (which holds the local WordPress install). On a fresh app Kizlo owns the layout, so `create`
|
|
69
69
|
* writes it whole (already SEO-wired) rather than patching — `applyLayoutPatches` runs over `create`'s
|
|
70
70
|
* patches, which are none today. The target directory layout comes straight from the manifest's
|
|
@@ -80,7 +80,7 @@ async function applyManifestWiring(dir, templateDir, manifest, opts) {
|
|
|
80
80
|
clientUrl: void 0
|
|
81
81
|
});
|
|
82
82
|
recordDependencies(dir, manifest);
|
|
83
|
-
const changes = changesFor(manifest, "create"
|
|
83
|
+
const changes = changesFor(manifest, "create", { includeExamples: opts.includeExamples });
|
|
84
84
|
const files = [{
|
|
85
85
|
label: "Kizlo config",
|
|
86
86
|
relPath: "kizlo.config.ts",
|
package/dist/cli/commands/dev.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createStack, dockerHint, dockerStatus } from "../wp/docker.js";
|
|
2
|
+
import { findConfigDir } from "../wp/utils.js";
|
|
3
|
+
import { log } from "../daemon/logger.js";
|
|
2
4
|
import { DEFAULT_ENV_KEYS, ensureGitignored, envGroups, groupDefault, mergeEnv, pickStackPort, withSpinner } from "../utils.js";
|
|
3
5
|
import { resolveDevConfig, usesLocalWordPress } from "../daemon/config.js";
|
|
4
6
|
import { bootstrapDev } from "../wp/dev.js";
|
|
@@ -6,6 +8,7 @@ import { reapOrphans, registerSession, removeProjectContainers, spawnWatchdog, u
|
|
|
6
8
|
import { syncSiteSettings } from "../wp/settings.js";
|
|
7
9
|
import { devStack } from "../wp/stack.js";
|
|
8
10
|
import { palette } from "../banner.js";
|
|
11
|
+
import { watchReload } from "../daemon/reload.js";
|
|
9
12
|
import { startWatcher } from "../daemon/watch.js";
|
|
10
13
|
import { join } from "node:path";
|
|
11
14
|
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -166,13 +169,13 @@ function armForegroundTeardown(cfg) {
|
|
|
166
169
|
}
|
|
167
170
|
}
|
|
168
171
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
172
|
+
* Bring the dev stack's containers up for `cfg`: clear any stale containers, resolve the published
|
|
173
|
+
* ports (stepping off a collision unless the port is pinned in config), write the compose files, and
|
|
174
|
+
* note any port that moved. Returns `cfg` with the resolved ports — the shape the rest of the boot
|
|
175
|
+
* and the teardown work against. Shared by the first boot and every reload reboot, so a changed
|
|
176
|
+
* `dev.port` in `kizlo.config.ts` is re-resolved the same way each time.
|
|
174
177
|
*/
|
|
175
|
-
async function
|
|
178
|
+
async function prepareStack(cfg) {
|
|
176
179
|
await removeProjectContainers(cfg.project);
|
|
177
180
|
const port = await pickStackPort(cfg.port, {
|
|
178
181
|
fixed: cfg.portExplicit,
|
|
@@ -191,11 +194,17 @@ async function startForeground(cfg) {
|
|
|
191
194
|
createStack(devStack(ready));
|
|
192
195
|
if (port !== cfg.port) note(`Port ${cfg.port} is in use — serving on ${port} instead.`);
|
|
193
196
|
if (dbPort !== cfg.dbPort) note(`Database port ${cfg.dbPort} is in use — using ${dbPort} instead.`);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
return ready;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* After a boot, sync the fresh credentials (or the current LAN URL) into `.env` and the plugin, print
|
|
201
|
+
* the connection summary, and print the timed ready line. `reloaded` swaps the wording and drops the
|
|
202
|
+
* one-time Ctrl+C hint. `start` is when the boot began, so the timing spans the whole reboot including
|
|
203
|
+
* the spinner. The `.env` writes here are why the caller pauses the reload watcher around a reboot.
|
|
204
|
+
*/
|
|
205
|
+
async function report(cfg, creds, reloaded, start) {
|
|
197
206
|
if (creds.appPassword) {
|
|
198
|
-
const env = updateWpEnv(
|
|
207
|
+
const env = updateWpEnv(cfg, creds);
|
|
199
208
|
note("Updated .env with the new WordPress credentials");
|
|
200
209
|
if (env) {
|
|
201
210
|
const sync = await syncSiteSettings({
|
|
@@ -209,34 +218,118 @@ async function startForeground(cfg) {
|
|
|
209
218
|
});
|
|
210
219
|
if (!sync.ok) note(`Could not sync the site settings to WordPress (${sync.error}) — make sure the kizlo plugin is active, then set them from the Kizlo settings.`);
|
|
211
220
|
}
|
|
212
|
-
} else if (syncLocalUrl(
|
|
221
|
+
} else if (syncLocalUrl(cfg, creds.url)) note("Updated .env WordPress URL to the current network address — restart your app to pick it up");
|
|
213
222
|
printSummary(creds);
|
|
214
223
|
const { green, dim, bold, reset } = palette();
|
|
215
224
|
const ms = Date.now() - start;
|
|
216
225
|
const took = ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
|
|
217
|
-
process.stdout.write(`\n ${dim}Press ${reset}${bold}Ctrl+C${reset}${dim} (or close the terminal) to stop local WordPress${reset}\n`);
|
|
218
|
-
process.stdout.write(`\n ${green}✓${reset} Ready in ${took}\n\n`);
|
|
219
|
-
|
|
220
|
-
|
|
226
|
+
if (!reloaded) process.stdout.write(`\n ${dim}Press ${reset}${bold}Ctrl+C${reset}${dim} (or close the terminal) to stop local WordPress${reset}\n`);
|
|
227
|
+
process.stdout.write(`\n ${green}✓${reset} ${reloaded ? "Reloaded" : "Ready"} in ${took}\n\n`);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Boot local WordPress for an already-prepared `cfg` (ports resolved, teardown armed) behind a spinner,
|
|
231
|
+
* then report. The first-boot path only — reboots go through {@link rebootStack}, which also folds the
|
|
232
|
+
* prepare step under the spinner.
|
|
233
|
+
*/
|
|
234
|
+
async function bootAndReport(cfg) {
|
|
235
|
+
const start = Date.now();
|
|
236
|
+
await report(cfg, await withSpinner("Starting local WordPress", () => bootstrapDev(cfg)), false, start);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Reboot the whole stack in place after a config/env change: re-resolve the config, recreate the
|
|
240
|
+
* containers, and boot WordPress — all behind a single spinner labelled with the file that changed, so
|
|
241
|
+
* there's no loaderless gap between detecting the change and the boot. Teardown is already armed from
|
|
242
|
+
* the first boot, so this never re-arms. Returns the freshly resolved config (ports may have moved) so
|
|
243
|
+
* the caller can re-target its watchers.
|
|
244
|
+
*/
|
|
245
|
+
async function rebootStack(cwd, label) {
|
|
246
|
+
const start = Date.now();
|
|
247
|
+
const { ready, creds } = await withSpinner(label, async () => {
|
|
248
|
+
const ready$1 = await prepareStack(await resolveDevConfig(cwd));
|
|
249
|
+
return {
|
|
250
|
+
ready: ready$1,
|
|
251
|
+
creds: await bootstrapDev(ready$1)
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
await report(ready, creds, true, start);
|
|
255
|
+
return ready;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Boot local WordPress and run in the foreground until exit — the path behind bare `kizlo dev`. Arms
|
|
259
|
+
* teardown first (so a mid-startup cancel still stops partial containers), boots and prints the
|
|
260
|
+
* summary, then parks. While parked it watches `.env` and `kizlo.config.*`: a change reboots the whole
|
|
261
|
+
* stack in place so a new port, connection, or fixture takes effect without rerunning the command. The
|
|
262
|
+
* reboot runs in the same process — the watchdog and session keep watching this PID throughout — and is
|
|
263
|
+
* guarded so the credential/URL writes it makes to `.env` don't trigger another reboot. A referenced
|
|
264
|
+
* timer holds the event loop open — an unresolved promise alone would not, so without it Node would
|
|
265
|
+
* empty its loop and exit straight after printing.
|
|
266
|
+
*/
|
|
267
|
+
async function startForeground(cfg) {
|
|
268
|
+
const cwd = process.cwd();
|
|
269
|
+
let ready = await prepareStack(cfg);
|
|
270
|
+
armForegroundTeardown(ready);
|
|
271
|
+
await bootAndReport(ready);
|
|
272
|
+
let stopContract = await startWatcher(ready.configDir);
|
|
273
|
+
let reloading = false;
|
|
274
|
+
const reload = async (changed) => {
|
|
275
|
+
if (reloading) return;
|
|
276
|
+
reloading = true;
|
|
277
|
+
try {
|
|
278
|
+
stopContract?.();
|
|
279
|
+
ready = await rebootStack(cwd, `${changed} changed — restarting the session`);
|
|
280
|
+
stopContract = await startWatcher(ready.configDir);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
log.error("Failed to reload the dev session:", error);
|
|
283
|
+
} finally {
|
|
284
|
+
setTimeout(() => {
|
|
285
|
+
reloading = false;
|
|
286
|
+
}, 500);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
const stopReload = watchReload(ready.configDir, (changed) => void reload(changed));
|
|
290
|
+
process.on("exit", () => {
|
|
291
|
+
stopContract?.();
|
|
292
|
+
stopReload();
|
|
293
|
+
});
|
|
221
294
|
setInterval(() => {}, 1 << 30);
|
|
222
295
|
await new Promise(() => {});
|
|
223
296
|
}
|
|
224
297
|
/**
|
|
225
298
|
* Run the contract watcher in the foreground until exit — the path bare `kizlo dev` takes when local
|
|
226
299
|
* WordPress isn't configured (a project pointing at its own WordPress). Generates once, then watches
|
|
227
|
-
* the server files and regenerates on save.
|
|
228
|
-
* the
|
|
300
|
+
* the server files and regenerates on save. It also watches `.env` and `kizlo.config.*` and restarts
|
|
301
|
+
* the watcher on a change, so a new server dir or env value is picked up the same way the local path
|
|
302
|
+
* reboots its stack. Returns immediately when another watcher already holds the lock; otherwise the
|
|
303
|
+
* persistent watcher keeps the process alive on its own.
|
|
229
304
|
*/
|
|
230
305
|
async function watchOnly(cwd) {
|
|
231
|
-
|
|
306
|
+
let stop = await startWatcher(cwd);
|
|
232
307
|
if (!stop) return;
|
|
233
|
-
|
|
308
|
+
let reloading = false;
|
|
309
|
+
const reload = async () => {
|
|
310
|
+
if (reloading) return;
|
|
311
|
+
reloading = true;
|
|
312
|
+
try {
|
|
313
|
+
stop?.();
|
|
314
|
+
stop = await startWatcher(cwd);
|
|
315
|
+
} finally {
|
|
316
|
+
setTimeout(() => {
|
|
317
|
+
reloading = false;
|
|
318
|
+
}, 300);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
const stopReload = watchReload(findConfigDir(cwd), () => void reload());
|
|
322
|
+
const shutdown = () => {
|
|
323
|
+
stop?.();
|
|
324
|
+
stopReload();
|
|
325
|
+
};
|
|
326
|
+
process.on("exit", shutdown);
|
|
234
327
|
for (const signal of [
|
|
235
328
|
"SIGINT",
|
|
236
329
|
"SIGTERM",
|
|
237
330
|
"SIGHUP"
|
|
238
331
|
]) process.on(signal, () => {
|
|
239
|
-
|
|
332
|
+
shutdown();
|
|
240
333
|
process.exit(0);
|
|
241
334
|
});
|
|
242
335
|
}
|
|
@@ -185,6 +185,10 @@ const init = defineCommand({
|
|
|
185
185
|
});
|
|
186
186
|
if (args.alias !== void 0) setup.alias = String(args.alias).trim();
|
|
187
187
|
setup.alias = aliasWithSlash(setup.alias);
|
|
188
|
+
const includeExamples = manifest && fetched ? yes ? true : orCancel(await p.confirm({
|
|
189
|
+
message: "Add example pages?",
|
|
190
|
+
initialValue: true
|
|
191
|
+
})) : false;
|
|
188
192
|
if (!hasKizlo) {
|
|
189
193
|
const spec = `kizlo@${manifest?.dependencies?.kizlo ?? `^${getVersion()}`}`;
|
|
190
194
|
const s = p.spinner();
|
|
@@ -217,7 +221,7 @@ const init = defineCommand({
|
|
|
217
221
|
}];
|
|
218
222
|
if (manifest && fetched) {
|
|
219
223
|
await alignDependencies(cwd, pm, pkg, manifest);
|
|
220
|
-
for (const entry of fileEntries(changesFor(manifest, "init"))) files.push(adaptFile(fetched.dir, entry, manifest.conventions, scaffold));
|
|
224
|
+
for (const entry of fileEntries(changesFor(manifest, "init", { includeExamples }))) files.push(adaptFile(fetched.dir, entry, manifest.conventions, scaffold));
|
|
221
225
|
} else if (preset.scaffolds) files.push(...preset.scaffolds(scaffold));
|
|
222
226
|
const scaffolded = [];
|
|
223
227
|
for (const file of files) scaffolded.push({
|
|
@@ -154,4 +154,4 @@ async function resolveDevConfig(cwd) {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
//#endregion
|
|
157
|
-
export { DEFAULT_DEV_DB_PORT, DEFAULT_DEV_PORT, resolveConfig, resolveDevConfig, resolveStackName, resolveTestConfig, usesLocalWordPress };
|
|
157
|
+
export { CONFIG_FILES, DEFAULT_DEV_DB_PORT, DEFAULT_DEV_PORT, resolveConfig, resolveDevConfig, resolveStackName, resolveTestConfig, usesLocalWordPress };
|
|
@@ -2,7 +2,7 @@ import { consola } from "consola";
|
|
|
2
2
|
|
|
3
3
|
//#region src/cli/daemon/logger.ts
|
|
4
4
|
/** Tagged logger for the daemon — every line is prefixed with `kizlo`. */
|
|
5
|
-
const log = consola
|
|
5
|
+
const log = consola;
|
|
6
6
|
log.options.formatOptions.date = false;
|
|
7
7
|
|
|
8
8
|
//#endregion
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { CONFIG_FILES } from "./config.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { FSWatcher } from "chokidar";
|
|
6
|
+
|
|
7
|
+
//#region src/cli/daemon/reload.ts
|
|
8
|
+
/** Env files `loadEnvFiles` reads, in the same order — the runtime values a reload must pick up. */
|
|
9
|
+
const ENV_FILES = [".env", ".env.local"];
|
|
10
|
+
/** A combined hash of the watched files' contents; a missing file counts as empty. */
|
|
11
|
+
function fingerprint(files) {
|
|
12
|
+
const hash = createHash("sha1");
|
|
13
|
+
for (const file of files) {
|
|
14
|
+
hash.update(file);
|
|
15
|
+
hash.update("\0");
|
|
16
|
+
try {
|
|
17
|
+
hash.update(readFileSync(file));
|
|
18
|
+
} catch {}
|
|
19
|
+
hash.update("\0");
|
|
20
|
+
}
|
|
21
|
+
return hash.digest("hex");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Watch the project's `.env` files and `kizlo.config.*` and call `onReload` with the changed file's
|
|
25
|
+
* basename when their *contents* change. `kizlo dev` uses this to restart its whole session — rebooting
|
|
26
|
+
* local WordPress and regenerating the contract — so a changed port, connection, or fixture takes
|
|
27
|
+
* effect without stopping and rerunning the command by hand. Debounced so an editor's multi-write save
|
|
28
|
+
* fires once, and gated on a content hash so a no-op save (`touch`, or writing the file back unchanged)
|
|
29
|
+
* is ignored rather than triggering a pointless reboot. Returns a synchronous `stop()` that closes the
|
|
30
|
+
* watcher.
|
|
31
|
+
*
|
|
32
|
+
* `dev` rewrites `.env` itself while booting (fresh credentials, the current LAN URL); the caller
|
|
33
|
+
* pauses its reload handler around a reboot so that self-write doesn't trigger another reload. The hash
|
|
34
|
+
* is still refreshed for it here, so once the reboot settles the new content is the baseline and won't
|
|
35
|
+
* re-fire.
|
|
36
|
+
*/
|
|
37
|
+
function watchReload(configDir, onReload) {
|
|
38
|
+
const files = [...ENV_FILES, ...CONFIG_FILES].map((name) => path.join(configDir, name));
|
|
39
|
+
const watcher = new FSWatcher({
|
|
40
|
+
persistent: true,
|
|
41
|
+
ignoreInitial: true
|
|
42
|
+
});
|
|
43
|
+
let last = fingerprint(files);
|
|
44
|
+
let timer;
|
|
45
|
+
const schedule = (changed) => {
|
|
46
|
+
clearTimeout(timer);
|
|
47
|
+
timer = setTimeout(() => {
|
|
48
|
+
const next = fingerprint(files);
|
|
49
|
+
if (next === last) return;
|
|
50
|
+
last = next;
|
|
51
|
+
onReload(path.basename(changed));
|
|
52
|
+
}, 300);
|
|
53
|
+
};
|
|
54
|
+
watcher.add(files);
|
|
55
|
+
watcher.on("all", (_event, changed) => schedule(changed));
|
|
56
|
+
let stopped = false;
|
|
57
|
+
return () => {
|
|
58
|
+
if (stopped) return;
|
|
59
|
+
stopped = true;
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
watcher.close();
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
export { watchReload };
|
package/dist/cli/daemon/watch.js
CHANGED
|
@@ -30,7 +30,6 @@ async function watch(cfg) {
|
|
|
30
30
|
});
|
|
31
31
|
const onChange = debounce(() => regenerate(cfg), 300);
|
|
32
32
|
watcher.add(path.resolve(cfg.cwd, cfg.serverDir));
|
|
33
|
-
watcher.on("ready", () => log.start("Watching for contract changes..."));
|
|
34
33
|
watcher.on("all", () => void onChange());
|
|
35
34
|
return watcher;
|
|
36
35
|
}
|
|
@@ -8,17 +8,25 @@ import fs from "node:fs";
|
|
|
8
8
|
/**
|
|
9
9
|
* The template's `template.json`. It is the single declaration of what Kizlo lays onto a project and
|
|
10
10
|
* how — the same engine drives both `create` (onto a fresh app the framework CLI just bootstrapped)
|
|
11
|
-
* and `init` (onto the user's existing project). Changes are grouped into three sections
|
|
12
|
-
*
|
|
11
|
+
* and `init` (onto the user's existing project). Changes are grouped into three sections by the one
|
|
12
|
+
* question that decides whether a command may safely apply them — *where is this safe to land?* — and
|
|
13
|
+
* no command ever reads another command's section:
|
|
13
14
|
*
|
|
14
|
-
* - `base` —
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* - `create` —
|
|
18
|
-
* the root layout (written whole,
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
15
|
+
* - `base` — safe on any project: additive, Kizlo-owned paths that never collide with the user's own
|
|
16
|
+
* files (the server entry, client, routes, and additive demo pages like a new blog route). Applied
|
|
17
|
+
* by both commands.
|
|
18
|
+
* - `create` — safe only on a freshly bootstrapped app `create` may freely overwrite: the files the
|
|
19
|
+
* framework CLI already scaffolded, like the root layout (written whole, SEO-wired) and the homepage
|
|
20
|
+
* showcase. Applied only by `create`, never `init` — overwriting these on a real project would
|
|
21
|
+
* clobber the user's work.
|
|
22
|
+
* - `init` — the non-destructive counterpart for the user's existing project: the root-layout SEO
|
|
23
|
+
* wiring lands here as a `patch` that merges into the layout the user already owns.
|
|
24
|
+
*
|
|
25
|
+
* Cutting across all three is the orthogonal `example` flag: opt-in demo content, applied only when the
|
|
26
|
+
* user answers yes to "Add example pages?". A cross-cutting example (e.g. a blog) files each of its
|
|
27
|
+
* pieces in the section that matches its safety — the additive new route in `base`, the homepage
|
|
28
|
+
* showcase that overwrites an app-owned file in `create` — so `init` picks up only the additive half and
|
|
29
|
+
* never touches a file the user owns, while `create` gets the whole demo. See {@link changesFor}.
|
|
22
30
|
*
|
|
23
31
|
* Each section is a list of changes, every one a member of a small closed set — deliberately no
|
|
24
32
|
* "patch an arbitrary source file" kind, so a dangerous strategy is unrepresentable:
|
|
@@ -162,12 +170,16 @@ function minCliError(manifest) {
|
|
|
162
170
|
return `This template needs the kizlo CLI >= ${manifest.minCli} (you have ${running}). Run \`npx kizlo@latest\` and try again.`;
|
|
163
171
|
}
|
|
164
172
|
/**
|
|
165
|
-
* The changes a command applies: the shared `base` set plus the command's own section
|
|
166
|
-
*
|
|
167
|
-
*
|
|
173
|
+
* The changes a command applies: the shared `base` set plus the command's own section (`base` first, so
|
|
174
|
+
* a later section can override it). Section placement is the *safety* axis — `base` is additive and safe
|
|
175
|
+
* on any project, `create`/`init` carry the fresh-app-vs-existing-project halves — while the `example`
|
|
176
|
+
* flag is the orthogonal *opt-in* axis. Example changes are dropped unless `includeExamples`, so the same
|
|
177
|
+
* call serves both "core wiring only" and "plus the demo pages", and each command only ever sees its own
|
|
178
|
+
* section: init's opt-in examples are the additive ones in `base`, never create's homepage overwrite.
|
|
168
179
|
*/
|
|
169
|
-
function changesFor(manifest, command) {
|
|
170
|
-
|
|
180
|
+
function changesFor(manifest, command, opts = {}) {
|
|
181
|
+
const changes = [...manifest.base, ...manifest[command]];
|
|
182
|
+
return opts.includeExamples ? changes : changes.filter((change) => !isExample(change));
|
|
171
183
|
}
|
|
172
184
|
/** The whole-file changes in a resolved change list. */
|
|
173
185
|
function fileEntries(changes) {
|
|
@@ -177,7 +189,7 @@ function fileEntries(changes) {
|
|
|
177
189
|
function patchEntries(changes) {
|
|
178
190
|
return changes.filter((change) => change.kind === "patch");
|
|
179
191
|
}
|
|
180
|
-
/** Whether a change is opt-in demo content —
|
|
192
|
+
/** Whether a change is opt-in demo content — applied only when example pages are requested. */
|
|
181
193
|
function isExample(change) {
|
|
182
194
|
return change.kind === "file" && change.example === true;
|
|
183
195
|
}
|
|
@@ -267,4 +279,4 @@ function rendersHtml(file) {
|
|
|
267
279
|
}
|
|
268
280
|
|
|
269
281
|
//#endregion
|
|
270
|
-
export { adaptFile, changesFor, fileEntries, findRootLayout,
|
|
282
|
+
export { adaptFile, changesFor, fileEntries, findRootLayout, isOlderVersion, minCliError, patchEntries, readManifest, resolveDependencies, resolvePatch };
|
package/dist/cli/wp/settings.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { WP_KIZLO_BASE } from "../../wordpress/constants.js";
|
|
2
2
|
import { WordPressService } from "../../wordpress/service.js";
|
|
3
|
+
import { PLUGIN_VERSION_HEADER } from "@kizlo/shared";
|
|
3
4
|
|
|
4
5
|
//#region src/cli/wp/settings.ts
|
|
5
6
|
/** Loopback hosts that, from inside the dev Docker stack, must be rewritten to reach the host. */
|
|
@@ -52,11 +53,16 @@ async function syncSiteSettings(credentials, settings) {
|
|
|
52
53
|
base: WP_KIZLO_BASE,
|
|
53
54
|
body
|
|
54
55
|
});
|
|
56
|
+
const pluginVersion = response.headers.get(PLUGIN_VERSION_HEADER);
|
|
55
57
|
if (response.error) return {
|
|
56
58
|
ok: false,
|
|
57
|
-
error: describeError(response.status, response.error)
|
|
59
|
+
error: describeError(response.status, response.error),
|
|
60
|
+
pluginVersion
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
ok: true,
|
|
64
|
+
pluginVersion
|
|
58
65
|
};
|
|
59
|
-
return { ok: true };
|
|
60
66
|
}
|
|
61
67
|
/**
|
|
62
68
|
* A short, log-friendly summary of a failed request. A transport failure has no HTTP status (`0`), so
|
package/dist/comment/schema.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ListMetadata, ListOrder
|
|
2
|
-
import { NumberLike, arrayable } from "@kizlo/shared";
|
|
1
|
+
import { ListMetadata, ListOrder } from "../shared/schema.js";
|
|
2
|
+
import { Media, NumberLike, arrayable } from "@kizlo/shared";
|
|
3
3
|
import z from "zod/v4";
|
|
4
4
|
|
|
5
5
|
//#region src/comment/schema.ts
|
package/dist/context.d.ts
CHANGED
|
@@ -60,6 +60,15 @@ declare class Context {
|
|
|
60
60
|
private readonly settings;
|
|
61
61
|
private readonly email;
|
|
62
62
|
constructor(config: ContextConfig);
|
|
63
|
+
/**
|
|
64
|
+
* Warn when the WordPress plugin is older than {@link isPluginVersionSupported} allows, read from the
|
|
65
|
+
* `X-Kizlo-Version` header the plugin stamps on every response. A version mismatch is a serious
|
|
66
|
+
* contract break, so this fires on every WordPress response rather than once — the header rides
|
|
67
|
+
* responses the runtime already makes, so it costs no extra request. Emitted through `console.warn`
|
|
68
|
+
* rather than the logger adapter on purpose: the adapter is optional (it defaults to a no-op), and
|
|
69
|
+
* this is a setup problem every user must see regardless of whether they've wired up logging.
|
|
70
|
+
*/
|
|
71
|
+
private warnIfPluginOutdated;
|
|
63
72
|
createRestContext(request: Request): {
|
|
64
73
|
headers: Headers;
|
|
65
74
|
request: Request;
|
package/dist/context.js
CHANGED
|
@@ -5,7 +5,7 @@ import { CookiesStorage } from "./cookie.js";
|
|
|
5
5
|
import { WordPressService } from "./wordpress/service.js";
|
|
6
6
|
import { EmailService } from "./email/service.js";
|
|
7
7
|
import { SettingsService } from "./settings/service.js";
|
|
8
|
-
import { base64Decode, tryCatchSync } from "@kizlo/shared";
|
|
8
|
+
import { PLUGIN_VERSION_HEADER, base64Decode, isPluginVersionSupported, pluginUpdateMessage, tryCatchSync } from "@kizlo/shared";
|
|
9
9
|
import { serialize } from "cookie";
|
|
10
10
|
|
|
11
11
|
//#region src/context.ts
|
|
@@ -17,11 +17,29 @@ var Context = class {
|
|
|
17
17
|
email;
|
|
18
18
|
constructor(config) {
|
|
19
19
|
this.config = config;
|
|
20
|
-
this.wordpress = new WordPressService(
|
|
20
|
+
this.wordpress = new WordPressService({
|
|
21
|
+
...config,
|
|
22
|
+
onResponse: (headers) => this.warnIfPluginOutdated(headers)
|
|
23
|
+
});
|
|
21
24
|
this.settings = new SettingsService(this.wordpress);
|
|
22
25
|
this.email = new EmailService(this.wordpress);
|
|
23
26
|
this.logger = this.createLogger();
|
|
24
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Warn when the WordPress plugin is older than {@link isPluginVersionSupported} allows, read from the
|
|
30
|
+
* `X-Kizlo-Version` header the plugin stamps on every response. A version mismatch is a serious
|
|
31
|
+
* contract break, so this fires on every WordPress response rather than once — the header rides
|
|
32
|
+
* responses the runtime already makes, so it costs no extra request. Emitted through `console.warn`
|
|
33
|
+
* rather than the logger adapter on purpose: the adapter is optional (it defaults to a no-op), and
|
|
34
|
+
* this is a setup problem every user must see regardless of whether they've wired up logging.
|
|
35
|
+
*/
|
|
36
|
+
warnIfPluginOutdated(headers) {
|
|
37
|
+
const installed = headers.get(PLUGIN_VERSION_HEADER);
|
|
38
|
+
if (isPluginVersionSupported(installed)) return;
|
|
39
|
+
const inner = ` ${pluginUpdateMessage(installed)} `;
|
|
40
|
+
const border = "-".repeat(inner.length + 2);
|
|
41
|
+
console.warn(`\n\x1b[1m\x1b[33m${border}\n|${inner}|\n${border}\x1b[0m\n`);
|
|
42
|
+
}
|
|
25
43
|
createRestContext(request) {
|
|
26
44
|
const headers = new Headers();
|
|
27
45
|
return {
|
package/dist/index.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ import "./wordpress/index.js";
|
|
|
22
22
|
import { DevPluginSource, Fixture, FixtureCleanupFn, FixtureSeedFn, JsonValue, LocalPlugin, PluginSource, SeedContext, TestAdminUser, TestCredentials, TestUser, defineFixture, isLocalPlugin } from "./cli/wp/types.js";
|
|
23
23
|
import { AuthAdapter, AuthGetUser, AuthUser, authMock, createAuthAdapter } from "./adapters/auth.js";
|
|
24
24
|
import { CaptchaAdapter, altcha, arkose, createCaptchaAdapter, friendlyCaptcha, hcaptcha, recaptcha, turnstile } from "./adapters/captcha.js";
|
|
25
|
-
import { CurrencyFormat, IdentifierInput, LIST_ORDER, ListMetadata, ListOrder,
|
|
25
|
+
import { CurrencyFormat, IdentifierInput, LIST_ORDER, ListMetadata, ListOrder, PreviewTokenData, PreviewTokenPayload } from "./shared/schema.js";
|
|
26
26
|
import { AnyContext, AnyNestedClient, CookiesAdapter, CookiesDeleteAll, CookiesGetAll, CookiesSetAll, HTTPMethod, List, MaybeOptionalOptions, RedirectInput, RedirectStatus } from "./shared/types.js";
|
|
27
27
|
import { createCookiesAdapter } from "./adapters/cookies.js";
|
|
28
28
|
import { ConnInfo, GeoAdapter, GeoGetConnInfo, createGeoAdapter, geoMock } from "./adapters/geo.js";
|
|
@@ -52,4 +52,4 @@ import { ShaAlgorithm, compare, hash, hmac } from "./shared/crypto.js";
|
|
|
52
52
|
import { Identifier, parseIdentifier } from "./shared/identifier.js";
|
|
53
53
|
import { createOrpcRouter } from "./shared/router.js";
|
|
54
54
|
import { deserializeCurrencyFormat, deserializeListMetadata, emptyList, serializeCurrencyFormat } from "./shared/serialize.js";
|
|
55
|
-
export { AnyContext, AnyEventSchema, AnyEvents, AnyExtension, AnyKizloConfig, AnyMiddleware, AnyNestedClient, AnyProcedure, AnyProcedureRouter, AuthAdapter, AuthGetUser, AuthUser, AuthUserFn, AuthorsSettings, AuthorsSettingsInput, BaseHandlerOptions, BrandSettings, BrandSettingsInput, CART_HASH_HEADER_KEY, COMMON_ERRORS, CONTENT_TYPES, CaptchaAdapter, CategoryService, CommentService, CommonErrorCode, CommonErrorLiteral, CommonErrorMap, ConnInfo, ConnInfoFn, ConsoleLogAdapterOptions, ContentType, Context, ContextConfig, Contract, ContractProcedure, CookiesAdapter, CookiesDeleteAll, CookiesGetAll, CookiesSetAll, CookiesStorage, CrawlingSettings, CrawlingSettingsInput, CreateKizloOptions, CreateSitemapRouteOptions, CurrencyFormat, DefinedErrorMapLike, DevPluginSource, Environment, ErrorDefinition, EventHandler, EventHandlerFn, Extension, ExtensionInitFn, ExtensionInitOptions, ExtensionInitResult, ExtractExtensionRouter, ExtractMiddlewareContextOutput, ExtractParams, ExtractProcedureByScope, Fixture, FixtureCleanupFn, FixtureSeedFn, GeoAdapter, GeoGetConnInfo, HTTPMethod, IconDescriptor, Identifier, IdentifierInput, IdentitySettings, IdentitySettingsInput, InferEventUnion, InferExtensionRouter, InferMiddlewares, InvocationScope, JsonValue, Kizlo, KizloClient, KizloClientConfig, KizloConfig, KizloConnect, KizloError, KizloErrorOptions, LIST_ORDER, List, ListMetadata, ListOrder, ListSitemapUrlInput, ListSitemapUrlInputBase, LocalPlugin, LogLevel, LogPayload, Logger, LoggerAdapter, ManifestIcon, MaybeOptionalOptions,
|
|
55
|
+
export { AnyContext, AnyEventSchema, AnyEvents, AnyExtension, AnyKizloConfig, AnyMiddleware, AnyNestedClient, AnyProcedure, AnyProcedureRouter, AuthAdapter, AuthGetUser, AuthUser, AuthUserFn, AuthorsSettings, AuthorsSettingsInput, BaseHandlerOptions, BrandSettings, BrandSettingsInput, CART_HASH_HEADER_KEY, COMMON_ERRORS, CONTENT_TYPES, CaptchaAdapter, CategoryService, CommentService, CommonErrorCode, CommonErrorLiteral, CommonErrorMap, ConnInfo, ConnInfoFn, ConsoleLogAdapterOptions, ContentType, Context, ContextConfig, Contract, ContractProcedure, CookiesAdapter, CookiesDeleteAll, CookiesGetAll, CookiesSetAll, CookiesStorage, CrawlingSettings, CrawlingSettingsInput, CreateKizloOptions, CreateSitemapRouteOptions, CurrencyFormat, DefinedErrorMapLike, DevPluginSource, Environment, ErrorDefinition, EventHandler, EventHandlerFn, Extension, ExtensionInitFn, ExtensionInitOptions, ExtensionInitResult, ExtractExtensionRouter, ExtractMiddlewareContextOutput, ExtractParams, ExtractProcedureByScope, Fixture, FixtureCleanupFn, FixtureSeedFn, GeoAdapter, GeoGetConnInfo, HTTPMethod, IconDescriptor, Identifier, IdentifierInput, IdentitySettings, IdentitySettingsInput, InferEventUnion, InferExtensionRouter, InferMiddlewares, InvocationScope, JsonValue, Kizlo, KizloClient, KizloClientConfig, KizloConfig, KizloConnect, KizloError, KizloErrorOptions, LIST_ORDER, List, ListMetadata, ListOrder, ListSitemapUrlInput, ListSitemapUrlInputBase, LocalPlugin, LogLevel, LogPayload, Logger, LoggerAdapter, ManifestIcon, MaybeOptionalOptions, MenuItemService, MenuService, Middleware, MiddlewareHandler, MiddlewareNextFn, MiddlewareOptions, MiddlewareResult, OpenapiPart, OrganizationFounder, OrganizationSettings, OrganizationSettingsInput, PageService, PersonSettings, PersonSettingsInput, PluginSource, PostService, PostStatusDefinition, PostTypeSettings, PostTypeSettingsInput, PreviewTokenData, PreviewTokenPayload, Procedure, ProcedureContext, ProcedureErrors, ProcedureHandler, ProcedureOptions, ROUTER_MAP, RedirectInput, RedirectStatus, ResolveParamsPart, ResolvedIcons, ResolvedSchemaInput, ResolvedSchemaOutput, Robots, RobotsRule, RootRouter, RouterDepthTuple, RouterMap, S2SClient, SAFE_REQUEST_TIMEOUT, SITEMAP_BASE, SITEMAP_INDEX_SLUG, SchemaIO, SeedContext, Seo, SeoHead, SeoSchema, ServiceAdapters, Settings, SettingsService, ShaAlgorithm, SiteSettings, SiteSettingsInput, Sitemap, SitemapIndex, SitemapIndexEntry, SitemapList, SitemapRouteExtra, SitemapUrl, SitemapUrlImage, SitemapUrlList, SocialProfile, TaxonomySettings, TaxonomySettingsInput, TestAdminUser, TestCredentials, TestUser, ThrowableErrorFn, ThrowableErrorMap, UNEXPECTED_BODY_SNIPPET_LENGTH, UploadsSettings, UploadsSettingsInput, UserService, VerifyCaptchaFn, VerifyPreviewTokenFn, WC_CORE_BASE, WC_STORE_BASE, WEB_MANIFEST_ROUTE, WPK_RobotRule, WPK_Robots, WPK_Seo, WPK_Sitemap, WPK_SitemapContentType, WPK_SitemapIndex, WPK_SitemapUrl, WPK_SitemapUrlImage, WP_AUTH_HEADER_KEY, WP_AUTH_TYPE, WP_CATEGORY_ORDER_BYS, WP_CORE_BASE, WP_Category, WP_CategoryCreateErrorCode, WP_CategoryCreateInput, WP_CategoryDeleteErrorCode, WP_CategoryDeleteInput, WP_CategoryDeleteResponse, WP_CategoryLinks, WP_CategoryListErrorCode, WP_CategoryListInput, WP_CategoryOrderBy, WP_CategoryRetrieveErrorCode, WP_CategoryRetrieveInput, WP_CategoryTaxonomy, WP_CategoryUpdateErrorCode, WP_CategoryUpdateInput, 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_ITEM_ORDER_BYES, WP_MENU_ITEM_TARGETS, WP_MENU_ITEM_TAX_RELATIONS, 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_PAGE_ORDER_BYES, WP_PAGE_STATUSES, WP_POST_FORMATS, WP_POST_ORDER_BYES, WP_POST_STATUSES, WP_POST_TAX_RELATIONS, WP_Page, WP_PageCommentStatus, WP_PageContent, WP_PageContentInput, WP_PageCreateErrorCode, WP_PageCreateInput, WP_PageDeleteErrorCode, WP_PageDeleteInput, WP_PageDeleteResponse, WP_PageExcerpt, WP_PageExcerptInput, WP_PageGuid, WP_PageLinks, WP_PageListErrorCode, WP_PageListInput, WP_PageOrderBy, WP_PageRetrieveErrorCode, WP_PageRetrieveInput, WP_PageStatus, WP_PageStatusFilter, WP_PageTitle, WP_PageTitleInput, WP_PageUpdateErrorCode, WP_PageUpdateInput, 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_TAG_ORDER_BYS, WP_Tag, WP_TagCreateErrorCode, WP_TagCreateInput, WP_TagDeleteErrorCode, WP_TagDeleteInput, WP_TagDeleteResponse, WP_TagLinks, WP_TagListErrorCode, WP_TagListInput, WP_TagOrderBy, WP_TagRetrieveErrorCode, WP_TagRetrieveInput, WP_TagTaxonomy, WP_TagUpdateErrorCode, WP_TagUpdateInput, 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, WebManifest, WebhookOptions, WebhookSettings, WebhookSettingsInput, WithCommonErrorMap, WordPressCredentials, WordPressService, altcha, arkose, authMock, buildProcedure, buildWebManifest, combineDetailed, compare, consoleLog, createAuthAdapter, createCaptchaAdapter, createCookiesAdapter, createEventHandler, createExtension, createGeoAdapter, createKizlo, createKizloClient, createLoggerAdapter, createManifestRoute, createMiddleware, createOrpcRouter, createProcedure, createSitemapRedirectRoute, createSitemapRoute, createThrowableErrorMap, createWebhookRouter, defineErrorMap, defineFixture, deserializeCurrencyFormat, deserializeListMetadata, deserializeSeo, emptyList, friendlyCaptcha, generateContract, geoMock, geoVercelNext, handleUnexpectedError, hash, hcaptcha, hmac, isContractProcedure, isKizloError, isLocalPlugin, isWordPressResourceError, noopAdapter, parseIdentifier, parseSitemapSlug, recaptcha, renderSitemapIndex, renderUrlSet, resolveIcons, resolveKizloConfig, restoreContract, schemaType, serializeCurrencyFormat, sitemapEntryPath, toKizloError, turnstile, xmlResponse };
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { COMMON_ERRORS, KizloError, createThrowableErrorMap, defineErrorMap, isK
|
|
|
12
12
|
import { buildProcedure, combineDetailed, createProcedure, handleUnexpectedError, schemaType } from "./shared/procedure.js";
|
|
13
13
|
import { createOrpcRouter } from "./shared/router.js";
|
|
14
14
|
import { generateContract, isContractProcedure, restoreContract } from "./shared/contract.js";
|
|
15
|
-
import { CurrencyFormat, IdentifierInput, LIST_ORDER, ListMetadata, ListOrder,
|
|
15
|
+
import { CurrencyFormat, IdentifierInput, LIST_ORDER, ListMetadata, ListOrder, PreviewTokenData, PreviewTokenPayload } from "./shared/schema.js";
|
|
16
16
|
import { KizloClient, createKizloClient } from "./client.js";
|
|
17
17
|
import { CookiesStorage } from "./cookie.js";
|
|
18
18
|
import { CART_HASH_HEADER_KEY, SAFE_REQUEST_TIMEOUT, UNEXPECTED_BODY_SNIPPET_LENGTH, WC_CORE_BASE, WC_STORE_BASE, WP_AUTH_HEADER_KEY, WP_AUTH_TYPE, WP_CORE_BASE, WP_KIZLO_BASE } from "./wordpress/constants.js";
|
|
@@ -44,4 +44,4 @@ import { SITEMAP_BASE, SITEMAP_INDEX_SLUG, createSitemapRedirectRoute, createSit
|
|
|
44
44
|
import { createExtension } from "./shared/extension.js";
|
|
45
45
|
import { createMiddleware } from "./shared/middleware.js";
|
|
46
46
|
|
|
47
|
-
export { CART_HASH_HEADER_KEY, COMMON_ERRORS, CONTENT_TYPES, CategoryService, CommentService, ContentType, Context, CookiesStorage, CurrencyFormat, IdentifierInput, Kizlo, KizloClient, KizloError, LIST_ORDER, ListMetadata, ListOrder, ListSitemapUrlInput, ListSitemapUrlInputBase,
|
|
47
|
+
export { CART_HASH_HEADER_KEY, COMMON_ERRORS, CONTENT_TYPES, CategoryService, CommentService, ContentType, Context, CookiesStorage, CurrencyFormat, IdentifierInput, Kizlo, KizloClient, KizloError, LIST_ORDER, ListMetadata, ListOrder, ListSitemapUrlInput, ListSitemapUrlInputBase, MenuItemService, MenuService, PageService, PostService, PreviewTokenData, PreviewTokenPayload, ROUTER_MAP, Robots, RobotsRule, SAFE_REQUEST_TIMEOUT, SITEMAP_BASE, SITEMAP_INDEX_SLUG, Seo, SeoHead, SeoSchema, SettingsService, Sitemap, SitemapIndex, SitemapList, SitemapUrl, SitemapUrlImage, SitemapUrlList, UNEXPECTED_BODY_SNIPPET_LENGTH, UserService, WC_CORE_BASE, WC_STORE_BASE, WEB_MANIFEST_ROUTE, WP_AUTH_HEADER_KEY, WP_AUTH_TYPE, WP_CATEGORY_ORDER_BYS, WP_CORE_BASE, WP_Error, WP_KIZLO_BASE, WP_MENU_ITEM_ORDER_BYES, WP_MENU_ITEM_TARGETS, WP_MENU_ITEM_TAX_RELATIONS, WP_PAGE_ORDER_BYES, WP_PAGE_STATUSES, WP_POST_FORMATS, WP_POST_ORDER_BYES, WP_POST_STATUSES, WP_POST_TAX_RELATIONS, WP_TAG_ORDER_BYS, WordPressService, altcha, arkose, authMock, buildProcedure, buildWebManifest, combineDetailed, compare, consoleLog, createAuthAdapter, createCaptchaAdapter, createCookiesAdapter, createEventHandler, createExtension, createGeoAdapter, createKizlo, createKizloClient, createLoggerAdapter, createManifestRoute, createMiddleware, createOrpcRouter, createProcedure, createSitemapRedirectRoute, createSitemapRoute, createThrowableErrorMap, createWebhookRouter, defineErrorMap, defineFixture, deserializeCurrencyFormat, deserializeListMetadata, deserializeSeo, emptyList, friendlyCaptcha, generateContract, geoMock, geoVercelNext, handleUnexpectedError, hash, hcaptcha, hmac, isContractProcedure, isKizloError, isLocalPlugin, isWordPressResourceError, noopAdapter, parseIdentifier, parseSitemapSlug, recaptcha, renderSitemapIndex, renderUrlSet, resolveIcons, resolveKizloConfig, restoreContract, schemaType, serializeCurrencyFormat, sitemapEntryPath, toKizloError, turnstile, xmlResponse };
|
package/dist/package.js
CHANGED
package/dist/page/schema.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { IdentifierInput, ListMetadata, ListOrder
|
|
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
|
package/dist/post/schema.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { IdentifierInput, ListMetadata, ListOrder
|
|
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
|
package/dist/shared/schema.d.ts
CHANGED
|
@@ -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,
|
|
48
|
+
export { CurrencyFormat, IdentifierInput, LIST_ORDER, ListMetadata, ListOrder, PreviewTokenData, PreviewTokenPayload };
|
package/dist/shared/schema.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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,
|
|
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.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A headless WordPress framework 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.
|
|
79
|
+
"@kizlo/shared": "0.6.0",
|
|
80
80
|
"@orpc/client": "^1.14.3",
|
|
81
81
|
"@orpc/contract": "^1.14.3",
|
|
82
82
|
"@orpc/openapi": "^1.14.3",
|