kizlo 0.18.0 → 0.20.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/dist/adapters/auth.d.ts +17 -16
- package/dist/adapters/auth.js +4 -5
- package/dist/cli/commands/_wiring.js +9 -8
- package/dist/cli/commands/check.js +11 -15
- package/dist/cli/commands/dev.js +3 -3
- package/dist/cli/commands/generate.js +15 -17
- package/dist/cli/commands/test.js +3 -3
- package/dist/cli/daemon/config.js +148 -82
- package/dist/cli/daemon/generate.js +55 -61
- package/dist/cli/daemon/watch.js +42 -55
- package/dist/cli/wp/constants.d.ts +2 -4
- package/dist/cli/wp/constants.js +2 -4
- package/dist/cli/wp/types.d.ts +6 -3
- package/dist/cli/wp/utils.d.ts +3 -3
- package/dist/cli/wp/utils.js +9 -3
- package/dist/comment/errors.js +0 -1
- package/dist/comment/index.js +6 -6
- package/dist/config.d.ts +61 -47
- package/dist/context.d.ts +8 -8
- package/dist/context.js +5 -5
- package/dist/index.d.ts +3 -3
- package/dist/kizlo.d.ts +6 -9
- package/dist/kizlo.js +8 -9
- package/dist/package.js +1 -1
- package/dist/procedures.d.ts +0 -1
- package/dist/test/auth.js +2 -3
- package/dist/test/users.d.ts +2 -4
- package/dist/test/users.js +4 -9
- package/dist/test.d.ts +2 -2
- package/dist/test.js +2 -2
- package/dist/wordpress/endpoint.js +2 -1
- package/dist/wordpress/error.d.ts +1 -0
- package/dist/wordpress/error.js +2 -0
- package/dist/wordpress/fetch-introspection.js +1 -1
- package/dist/wordpress/generate.js +13 -7
- package/dist/wordpress/transport.js +5 -3
- package/dist/wordpress/types.d.ts +5 -1
- package/dist/wordpress/utils.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { generateContract } from "../../shared/contract.js";
|
|
2
2
|
import { integrationEnv, resolveWordPressConnection } from "../../kizlo.js";
|
|
3
|
-
import { WORDPRESS_CLIENT_META_REL } from "../wp/constants.js";
|
|
4
3
|
import { node } from "../../integrations/node/integration.js";
|
|
5
4
|
import { log } from "./logger.js";
|
|
6
5
|
import { loadEnvFiles } from "../utils.js";
|
|
@@ -15,16 +14,16 @@ import fs from "node:fs";
|
|
|
15
14
|
|
|
16
15
|
//#region src/cli/daemon/generate.ts
|
|
17
16
|
/**
|
|
18
|
-
* The generated barrel is also written as a stub by `kizlo init`. It re-exports `
|
|
19
|
-
* than pre-wrapping it: a server entry passes `
|
|
20
|
-
*
|
|
17
|
+
* The generated barrel is also written as a stub by `kizlo init`. It re-exports `introspection`
|
|
18
|
+
* rather than pre-wrapping it: a server entry passes `createKizlo({ introspection })` itself, so the
|
|
19
|
+
* rest of the options stay visible at the call site instead of hiding inside a generated object.
|
|
21
20
|
*/
|
|
22
21
|
const CONTRACT_BARREL = [
|
|
23
22
|
`import type { procedures } from ".."`,
|
|
24
23
|
`import contractJson from "./contract.json"`,
|
|
25
24
|
``,
|
|
26
25
|
`export const contract = contractJson as unknown as typeof procedures`,
|
|
27
|
-
`export {
|
|
26
|
+
`export { introspection, type WordPressClient } from "./introspection"`,
|
|
28
27
|
``
|
|
29
28
|
].join("\n");
|
|
30
29
|
var LegacyRouterExportError = class extends Error {
|
|
@@ -34,22 +33,22 @@ var LegacyRouterExportError = class extends Error {
|
|
|
34
33
|
}
|
|
35
34
|
};
|
|
36
35
|
/**
|
|
37
|
-
* `
|
|
36
|
+
* `introspection.ts` before the first generation. WordPress is reachable but untyped until then, so a
|
|
38
37
|
* freshly scaffolded project compiles, including Kizlo's own procedures, before it has ever
|
|
39
|
-
* connected. `kizlo dev` / `kizlo generate` replaces this with the real
|
|
40
|
-
* empty tree on purpose: every endpoint a procedure names has to resolve to something until the
|
|
38
|
+
* connected. `kizlo dev` / `kizlo generate` replaces this with the real introspection. `any` rather than
|
|
39
|
+
* an empty tree on purpose: every endpoint a procedure names has to resolve to something until the
|
|
41
40
|
* real shapes arrive, which `WP_Client` preserves by passing `any` through.
|
|
42
41
|
*/
|
|
43
|
-
const
|
|
42
|
+
const INTROSPECTION_STUB = [
|
|
44
43
|
`import type { WP_Client } from "kizlo"`,
|
|
45
44
|
``,
|
|
46
|
-
`export const
|
|
45
|
+
`export const introspection = {} as any`,
|
|
47
46
|
``,
|
|
48
|
-
`export type WordPressClient = WP_Client<typeof
|
|
47
|
+
`export type WordPressClient = WP_Client<typeof introspection>`,
|
|
49
48
|
``,
|
|
50
49
|
`declare module "kizlo" {`,
|
|
51
50
|
`\tinterface WordPressClientRegistry {`,
|
|
52
|
-
`\t\
|
|
51
|
+
`\t\tintrospection: typeof introspection`,
|
|
53
52
|
`\t}`,
|
|
54
53
|
`\tinterface WordPressEndpointRegistry {`,
|
|
55
54
|
`\t\t[path: string]: any`,
|
|
@@ -123,8 +122,8 @@ function formatDiagnostic(diagnostic) {
|
|
|
123
122
|
* partial contract is worth is the generator's call rather than the transport's. Taking it is the
|
|
124
123
|
* default: a third-party plugin's broken declaration then costs its own routes and nothing else,
|
|
125
124
|
* where refusing the document costs the project every route that still works, for a mistake in code
|
|
126
|
-
* it does not own. Strict generation makes the opposite trade for CI, where
|
|
127
|
-
* of routes typechecks clean and ships the gap.
|
|
125
|
+
* it does not own. Strict generation makes the opposite trade for CI, where an introspection silently
|
|
126
|
+
* short of routes typechecks clean and ships the gap.
|
|
128
127
|
*/
|
|
129
128
|
var PartialContractError = class extends Error {
|
|
130
129
|
excluded;
|
|
@@ -155,7 +154,7 @@ function reportDiagnostics(document, strict) {
|
|
|
155
154
|
const excluded = new Set(document.diagnostics.filter((diagnostic) => diagnostic.type === "error").map(diagnosticLocation));
|
|
156
155
|
if (!excluded.size) return;
|
|
157
156
|
if (strict) throw new PartialContractError(excluded.size);
|
|
158
|
-
log.warn(`Generating without the ${excluded.size} excluded ${excluded.size === 1 ? "contribution" : "contributions"} above. Their routes and types are missing from the
|
|
157
|
+
log.warn(`Generating without the ${excluded.size} excluded ${excluded.size === 1 ? "contribution" : "contributions"} above. Their routes and types are missing from the introspection; \`kizlo generate --strict\` refuses the contract instead.`);
|
|
159
158
|
}
|
|
160
159
|
/**
|
|
161
160
|
* Plugin versions already named as outdated. `kizlo dev` refetches every few seconds, and the answer
|
|
@@ -178,7 +177,7 @@ function reportPluginVersion(installed) {
|
|
|
178
177
|
/** Fetch `/introspect`, reporting its diagnostics in the CLI's voice. Returns undefined on a 304. */
|
|
179
178
|
async function fetchDocument(cwd, options, etag) {
|
|
180
179
|
loadEnvFiles(cwd);
|
|
181
|
-
const result = await fetchIntrospection(options.credentials ?? resolveWordPressConnection(
|
|
180
|
+
const result = await fetchIntrospection(options.credentials ?? resolveWordPressConnection(integrationEnv([node()])).credentials, {
|
|
182
181
|
etag: options.strict ? void 0 : etag,
|
|
183
182
|
fetch: options.fetch
|
|
184
183
|
});
|
|
@@ -189,74 +188,69 @@ async function fetchDocument(cwd, options, etag) {
|
|
|
189
188
|
return result;
|
|
190
189
|
}
|
|
191
190
|
/**
|
|
192
|
-
* Produce the
|
|
191
|
+
* Produce the introspection source without writing it or its ETag cache. Health checks use this to ask
|
|
193
192
|
* the same generator as `kizlo generate` what should be on disk, while leaving a contributor's
|
|
194
193
|
* checkout untouched. Callers choose strictness just as generation does.
|
|
195
194
|
*/
|
|
196
|
-
async function
|
|
195
|
+
async function generateIntrospectionSource(cwd, options = {}) {
|
|
197
196
|
const result = await fetchDocument(cwd, options);
|
|
198
197
|
if (!result?.document) throw new Error("WordPress introspection returned no document.");
|
|
199
198
|
const generated = generateWordPressModule(result.document);
|
|
200
199
|
await assertGeneratedClientCompiles(generated);
|
|
201
200
|
return generated.source;
|
|
202
201
|
}
|
|
203
|
-
async function generateWordPressOnce(cfg, options = {}) {
|
|
204
|
-
const etag = readWordPressMeta(path.resolve(cfg.cwd, cfg.wordpressMetaPath), path.resolve(cfg.cwd, cfg.wordpressPath))?.etag;
|
|
205
|
-
const result = await fetchDocument(cfg.cwd, options, etag);
|
|
206
|
-
if (!result?.document) return "unchanged";
|
|
207
|
-
const generated = generateWordPressModule(result.document);
|
|
208
|
-
await assertGeneratedClientCompiles(generated);
|
|
209
|
-
writeGeneratedWordPress(cfg, generated.source, wordPressMeta(result.etag, result.document.hash));
|
|
210
|
-
return "generated";
|
|
211
|
-
}
|
|
212
202
|
/**
|
|
213
|
-
* Write
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
* class, so reaching for the runtime later needs no regeneration.
|
|
203
|
+
* Write `introspection.ts` at its resolved path, revalidating against the ETag cache. The one place
|
|
204
|
+
* the introspection reaches disk, whether or not the project has a server: a server-backed app writes
|
|
205
|
+
* it under `generated/`, and a package that only ships procedures writes it at its own `dir.introspection`.
|
|
217
206
|
*/
|
|
218
|
-
async function
|
|
219
|
-
const file = path.resolve(cwd,
|
|
220
|
-
const metaPath = path.resolve(cwd,
|
|
221
|
-
const result = await fetchDocument(cwd, options, readWordPressMeta(metaPath, file)?.etag);
|
|
207
|
+
async function generateIntrospectionOnce(cfg, options = {}) {
|
|
208
|
+
const file = path.resolve(cfg.cwd, cfg.introspectionPath);
|
|
209
|
+
const metaPath = path.resolve(cfg.cwd, cfg.introspectionMetaPath);
|
|
210
|
+
const result = await fetchDocument(cfg.cwd, options, readWordPressMeta(metaPath, file)?.etag);
|
|
222
211
|
if (!result?.document) return "unchanged";
|
|
223
212
|
const generated = generateWordPressModule(result.document);
|
|
224
213
|
await assertGeneratedClientCompiles(generated);
|
|
225
|
-
|
|
214
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
226
215
|
fs.mkdirSync(path.dirname(metaPath), { recursive: true });
|
|
216
|
+
atomicWrite(file, generated.source);
|
|
227
217
|
atomicWrite(metaPath, wordPressMeta(result.etag, result.document.hash));
|
|
228
|
-
if (fs.existsSync(file) && fs.readFileSync(file, "utf8") === contents) return "unchanged";
|
|
229
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
230
|
-
atomicWrite(file, contents);
|
|
231
218
|
return "generated";
|
|
232
219
|
}
|
|
233
|
-
function writeGeneratedWordPress(cfg, client, meta) {
|
|
234
|
-
const metaPath = path.resolve(cfg.cwd, cfg.wordpressMetaPath);
|
|
235
|
-
fs.mkdirSync(path.resolve(cfg.cwd, cfg.generatedDir), { recursive: true });
|
|
236
|
-
fs.mkdirSync(path.dirname(metaPath), { recursive: true });
|
|
237
|
-
atomicWrite(path.resolve(cfg.cwd, cfg.wordpressPath), client);
|
|
238
|
-
atomicWrite(metaPath, meta);
|
|
239
|
-
}
|
|
240
220
|
/**
|
|
241
|
-
*
|
|
242
|
-
*
|
|
221
|
+
* Generate everything `dir` resolves: the introspection always, plus the contract and barrel when a
|
|
222
|
+
* server is present. The server is loaded before the introspection is fetched, so a legacy `router`
|
|
223
|
+
* export is named as a migration error even with WordPress unreachable; a server whose entry exports no
|
|
224
|
+
* `procedures` still gets its introspection, and reports `contract: "empty"`.
|
|
243
225
|
*/
|
|
244
226
|
async function generateOnce(cfg, options = {}) {
|
|
245
227
|
loadEnvFiles(cfg.cwd);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
228
|
+
let procedures;
|
|
229
|
+
if (cfg.server) {
|
|
230
|
+
const entry = path.resolve(cfg.cwd, cfg.server.entry);
|
|
231
|
+
const server = await importIgnoringVirtualModules(cfg.cwd, entry);
|
|
232
|
+
if (!server.procedures && server.router) throw new LegacyRouterExportError(cfg.server.entry);
|
|
233
|
+
procedures = server.procedures;
|
|
251
234
|
}
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (
|
|
258
|
-
|
|
235
|
+
const introspection = await generateIntrospectionOnce(cfg, options);
|
|
236
|
+
if (!cfg.server) return {
|
|
237
|
+
introspection,
|
|
238
|
+
contract: "none"
|
|
239
|
+
};
|
|
240
|
+
if (!procedures) return {
|
|
241
|
+
introspection,
|
|
242
|
+
contract: "empty"
|
|
243
|
+
};
|
|
244
|
+
const contract = JSON.stringify(await generateContract(procedures));
|
|
245
|
+
fs.mkdirSync(path.resolve(cfg.cwd, cfg.server.contractDir), { recursive: true });
|
|
246
|
+
atomicWrite(path.resolve(cfg.cwd, cfg.server.contractPath), contract);
|
|
247
|
+
atomicWrite(path.resolve(cfg.cwd, cfg.server.barrelPath), CONTRACT_BARREL);
|
|
248
|
+
if (introspection === "generated") log.success("WordPress introspection generated");
|
|
249
|
+
return {
|
|
250
|
+
introspection,
|
|
251
|
+
contract: "built"
|
|
252
|
+
};
|
|
259
253
|
}
|
|
260
254
|
|
|
261
255
|
//#endregion
|
|
262
|
-
export { CONTRACT_BARREL,
|
|
256
|
+
export { CONTRACT_BARREL, INTROSPECTION_STUB, PartialContractError, generateIntrospectionOnce, generateIntrospectionSource, generateOnce, reportGenerationError };
|
package/dist/cli/daemon/watch.js
CHANGED
|
@@ -2,9 +2,9 @@ import { integrationEnv, resolveWordPressConnection } from "../../kizlo.js";
|
|
|
2
2
|
import { node } from "../../integrations/node/integration.js";
|
|
3
3
|
import { log } from "./logger.js";
|
|
4
4
|
import { loadEnvFiles } from "../utils.js";
|
|
5
|
-
import { resolveConfig
|
|
5
|
+
import { resolveConfig } from "./config.js";
|
|
6
6
|
import { IntrospectionFetchError } from "../../wordpress/fetch-introspection.js";
|
|
7
|
-
import {
|
|
7
|
+
import { generateIntrospectionOnce, generateOnce, reportGenerationError } from "./generate.js";
|
|
8
8
|
import { acquire, lockPath, release } from "./lock.js";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { FSWatcher } from "chokidar";
|
|
@@ -19,51 +19,49 @@ function debounce(fn, delay) {
|
|
|
19
19
|
}
|
|
20
20
|
async function regenerate(cfg, credentials) {
|
|
21
21
|
try {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const result = await generateOnce(cfg, { credentials });
|
|
23
|
+
if (result.contract === "built") log.success("Contract updated");
|
|
24
|
+
else if (result.contract === "empty" && cfg.server) log.warn(`No Kizlo server found in ${cfg.server.entry}`);
|
|
24
25
|
} catch (error) {
|
|
25
26
|
reportGenerationError("Failed to update the Kizlo contract:", error);
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
|
-
/** Watches the server directory and regenerates the contract on change. */
|
|
29
|
-
async function watch(cfg, credentials) {
|
|
29
|
+
/** Watches the server directory and regenerates the contract on change. Only called when a server is present. */
|
|
30
|
+
async function watch(cfg, server, credentials) {
|
|
30
31
|
const watcher = new FSWatcher({
|
|
31
32
|
persistent: true,
|
|
32
33
|
ignoreInitial: true,
|
|
33
|
-
ignored: path.resolve(cfg.cwd,
|
|
34
|
+
ignored: path.resolve(cfg.cwd, server.contractDir)
|
|
34
35
|
});
|
|
35
36
|
const onChange = debounce(() => regenerate(cfg, credentials), 300);
|
|
36
|
-
watcher.add(path.resolve(cfg.cwd,
|
|
37
|
+
watcher.add(path.resolve(cfg.cwd, server.dir));
|
|
37
38
|
watcher.on("all", () => void onChange());
|
|
38
39
|
return watcher;
|
|
39
40
|
}
|
|
40
41
|
/**
|
|
41
|
-
* Report
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* nothing the first one did not. A failure that reads differently is a different answer and is reported.
|
|
48
|
-
* The state is per reporter rather than per process, so restarting the watcher reports afresh, and one
|
|
49
|
-
* generation failing never silences the other.
|
|
42
|
+
* Report the introspection poll's pass. It carries the last failure it reported, because most of what
|
|
43
|
+
* can fail here cannot clear without the user acting (WordPress down, credentials wrong, a document
|
|
44
|
+
* that will not parse, an introspection version this package does not speak), and repeating the same
|
|
45
|
+
* line every few seconds says nothing the first one did not. A failure that reads differently is a
|
|
46
|
+
* different answer and is reported. The state is per reporter rather than per process, so restarting the
|
|
47
|
+
* watcher reports afresh.
|
|
50
48
|
*/
|
|
51
|
-
function reportGeneration(
|
|
49
|
+
function reportGeneration(stack) {
|
|
52
50
|
let reported;
|
|
53
51
|
return async (run) => {
|
|
54
52
|
try {
|
|
55
|
-
if (await run() === "generated") log.success(
|
|
53
|
+
if (await run() === "generated") log.success("WordPress introspection updated");
|
|
56
54
|
stack?.answered();
|
|
57
55
|
if (reported !== void 0) {
|
|
58
56
|
reported = void 0;
|
|
59
|
-
log.success(
|
|
57
|
+
log.success("Updating the WordPress introspection again");
|
|
60
58
|
}
|
|
61
59
|
} catch (error) {
|
|
62
60
|
if (error instanceof IntrospectionFetchError && error.unreachable && await stack?.ended()) return;
|
|
63
61
|
const message = error instanceof Error ? error.message : String(error);
|
|
64
62
|
if (message === reported) return;
|
|
65
63
|
reported = message;
|
|
66
|
-
reportGenerationError(
|
|
64
|
+
reportGenerationError("Failed to update the WordPress introspection:", error);
|
|
67
65
|
}
|
|
68
66
|
};
|
|
69
67
|
}
|
|
@@ -96,26 +94,20 @@ function endOnStoppedStack(stack, now = Date.now) {
|
|
|
96
94
|
};
|
|
97
95
|
}
|
|
98
96
|
/**
|
|
99
|
-
* One pass of the
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* Both reporters share one stack guard, so a stopped stack ends the session once rather than once per
|
|
105
|
-
* generation the pass refreshes.
|
|
97
|
+
* One pass of the introspection poll: refetch and rewrite `introspection.ts` when WordPress has changed.
|
|
98
|
+
* A server-backed app and a package that ships only procedures both take this same pass, since each has
|
|
99
|
+
* exactly one introspection to keep current; the plugin's PHP changing is picked up either way. The
|
|
100
|
+
* contract is not refreshed here; the file watcher rebuilds it on a server source change.
|
|
106
101
|
*/
|
|
107
|
-
function createWordPressRefresh(
|
|
108
|
-
const
|
|
109
|
-
const service = reportGeneration("service", guard);
|
|
110
|
-
const client = reportGeneration("client", guard);
|
|
102
|
+
function createWordPressRefresh(cfg, options, stack) {
|
|
103
|
+
const introspection = reportGeneration(stack ? endOnStoppedStack(stack) : void 0);
|
|
111
104
|
return async () => {
|
|
112
|
-
|
|
113
|
-
if (wordpressClientDir) await client(() => generateWorkspaceClientOnce(cwd, wordpressClientDir, options));
|
|
105
|
+
await introspection(() => generateIntrospectionOnce(cfg, options));
|
|
114
106
|
};
|
|
115
107
|
}
|
|
116
108
|
/** Run {@link createWordPressRefresh} on a timer, skipping a tick while the previous one is still going. */
|
|
117
|
-
function refreshWordPress(
|
|
118
|
-
const refresh = createWordPressRefresh(
|
|
109
|
+
function refreshWordPress(cfg, credentials, stack) {
|
|
110
|
+
const refresh = createWordPressRefresh(cfg, { credentials }, stack);
|
|
119
111
|
let refreshing = false;
|
|
120
112
|
const timer = setInterval(() => {
|
|
121
113
|
if (refreshing) return;
|
|
@@ -128,12 +120,11 @@ function refreshWordPress(cwd, cfg, wordpressClientDir, credentials, stack) {
|
|
|
128
120
|
return timer;
|
|
129
121
|
}
|
|
130
122
|
/**
|
|
131
|
-
* Acquire the single-instance lock, generate
|
|
132
|
-
*
|
|
133
|
-
* lock
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* stack and when it runs the watcher alone, so a single terminal covers the whole dev loop.
|
|
123
|
+
* Acquire the single-instance lock, generate once, and start the file watcher. Returns a synchronous
|
|
124
|
+
* `stop()` that closes the watcher and releases the lock, or `undefined` when another watcher already
|
|
125
|
+
* holds the lock (a framework dev script, or a second `kizlo dev`), or when nothing is configured to
|
|
126
|
+
* generate, in which case the caller carries on without watching. Used by `kizlo dev`, both when it
|
|
127
|
+
* boots a local stack and when it runs the watcher alone, so a single terminal covers the whole dev loop.
|
|
137
128
|
*/
|
|
138
129
|
async function startWatcher(cwd, opts) {
|
|
139
130
|
const lock = lockPath(cwd);
|
|
@@ -144,26 +135,22 @@ async function startWatcher(cwd, opts) {
|
|
|
144
135
|
let handedOver = false;
|
|
145
136
|
try {
|
|
146
137
|
const cfg = await resolveConfig(cwd, { dir: opts?.dir });
|
|
147
|
-
|
|
148
|
-
if (!cfg && !wordpressClientDir) {
|
|
138
|
+
if (!cfg) {
|
|
149
139
|
log.info("skipping the contract generation.");
|
|
150
140
|
return;
|
|
151
141
|
}
|
|
152
142
|
loadEnvFiles(cwd);
|
|
153
|
-
const { credentials } = resolveWordPressConnection(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (cfg) try {
|
|
160
|
-
if (await generateOnce(cfg, { credentials })) log.success("Contract generated");
|
|
161
|
-
else log.warn(`No Kizlo server found in ${cfg.serverEntry}`);
|
|
143
|
+
const { credentials } = resolveWordPressConnection(integrationEnv([node()]));
|
|
144
|
+
try {
|
|
145
|
+
const result = await generateOnce(cfg, { credentials });
|
|
146
|
+
if (result.contract === "built") log.success("Contract generated");
|
|
147
|
+
else if (result.contract === "empty" && cfg.server) log.warn(`No Kizlo server found in ${cfg.server.entry}`);
|
|
148
|
+
else if (result.introspection === "generated") log.success("WordPress introspection generated");
|
|
162
149
|
} catch (error) {
|
|
163
150
|
reportGenerationError("Failed to generate the Kizlo contract:", error);
|
|
164
151
|
}
|
|
165
|
-
const watcher = cfg ? await watch(cfg, credentials) : void 0;
|
|
166
|
-
const wordpressRefresh = refreshWordPress(
|
|
152
|
+
const watcher = cfg.server ? await watch(cfg, cfg.server, credentials) : void 0;
|
|
153
|
+
const wordpressRefresh = refreshWordPress(cfg, credentials, opts?.stack);
|
|
167
154
|
let stopped = false;
|
|
168
155
|
handedOver = true;
|
|
169
156
|
return () => {
|
|
@@ -34,9 +34,7 @@ declare const CREDENTIALS_REL = ".kizlo/test.json";
|
|
|
34
34
|
* working dir rather than next to the generated files on purpose: it describes one machine's last
|
|
35
35
|
* fetch, so a fresh clone has to regenerate rather than trust a cache it never populated.
|
|
36
36
|
*/
|
|
37
|
-
declare const
|
|
38
|
-
/** Same, for the standalone client written by `wordpressClientDir`. Separate so the two never share one ETag. */
|
|
39
|
-
declare const WORDPRESS_CLIENT_META_REL = ".kizlo/wordpress.client.meta.json";
|
|
37
|
+
declare const INTROSPECTION_META_REL = ".kizlo/introspection.meta.json";
|
|
40
38
|
/**
|
|
41
39
|
* Fixed folder the local WordPress install lives in, relative to the config root. The whole install
|
|
42
40
|
* (core, themes, uploads, plugins) is bind-mounted here; `kizlo dev reset` wipes it. It sits under
|
|
@@ -56,4 +54,4 @@ declare const DEFAULT_PLUGINS: PluginSource[];
|
|
|
56
54
|
declare const TEST_ADMIN: Omit<TestAdminUser, "id" | "applicationPassword">;
|
|
57
55
|
declare const TEST_USER: Omit<TestUser, "id">;
|
|
58
56
|
//#endregion
|
|
59
|
-
export { COMPOSE_FILE, CONFIG_FILES, CREDENTIALS_REL, DEFAULT_PLUGINS, DEFAULT_PORT, DEFAULT_WORDPRESS_TAG, LOCAL_DIR_REL, OPCACHE_INI, REMAP_ENTRYPOINT, SEED_MARKER_OPTION, SEED_VERSION, TEST_ADMIN, TEST_USER,
|
|
57
|
+
export { COMPOSE_FILE, CONFIG_FILES, CREDENTIALS_REL, DEFAULT_PLUGINS, DEFAULT_PORT, DEFAULT_WORDPRESS_TAG, INTROSPECTION_META_REL, LOCAL_DIR_REL, OPCACHE_INI, REMAP_ENTRYPOINT, SEED_MARKER_OPTION, SEED_VERSION, TEST_ADMIN, TEST_USER, githubRelease, kizloRelease };
|
package/dist/cli/wp/constants.js
CHANGED
|
@@ -44,9 +44,7 @@ const CREDENTIALS_REL = ".kizlo/test.json";
|
|
|
44
44
|
* working dir rather than next to the generated files on purpose: it describes one machine's last
|
|
45
45
|
* fetch, so a fresh clone has to regenerate rather than trust a cache it never populated.
|
|
46
46
|
*/
|
|
47
|
-
const
|
|
48
|
-
/** Same, for the standalone client written by `wordpressClientDir`. Separate so the two never share one ETag. */
|
|
49
|
-
const WORDPRESS_CLIENT_META_REL = ".kizlo/wordpress.client.meta.json";
|
|
47
|
+
const INTROSPECTION_META_REL = ".kizlo/introspection.meta.json";
|
|
50
48
|
/**
|
|
51
49
|
* Fixed folder the local WordPress install lives in, relative to the config root. The whole install
|
|
52
50
|
* (core, themes, uploads, plugins) is bind-mounted here; `kizlo dev reset` wipes it. It sits under
|
|
@@ -83,4 +81,4 @@ const TEST_USER = {
|
|
|
83
81
|
};
|
|
84
82
|
|
|
85
83
|
//#endregion
|
|
86
|
-
export { COMPOSE_FILE, CONFIG_FILES, CREDENTIALS_REL, DEFAULT_PLUGINS, DEFAULT_PORT, DEFAULT_WORDPRESS_TAG, LOCAL_DIR_REL, OPCACHE_INI, REMAP_ENTRYPOINT, SEED_MARKER_OPTION, SEED_VERSION, TEST_ADMIN, TEST_USER,
|
|
84
|
+
export { COMPOSE_FILE, CONFIG_FILES, CREDENTIALS_REL, DEFAULT_PLUGINS, DEFAULT_PORT, DEFAULT_WORDPRESS_TAG, INTROSPECTION_META_REL, LOCAL_DIR_REL, OPCACHE_INI, REMAP_ENTRYPOINT, SEED_MARKER_OPTION, SEED_VERSION, TEST_ADMIN, TEST_USER, githubRelease, kizloRelease };
|
package/dist/cli/wp/types.d.ts
CHANGED
|
@@ -53,13 +53,16 @@ interface SeedContext {
|
|
|
53
53
|
/**
|
|
54
54
|
* A plugin to ensure installed + active during bootstrap. A bare string is a
|
|
55
55
|
* wp.org slug (the installed name and the install source are the same). The
|
|
56
|
-
* object form
|
|
57
|
-
* installed slug (`name`, used for activation checks)
|
|
58
|
-
* source.
|
|
56
|
+
* object form names an install source explicitly, either a wp.org slug or a zip
|
|
57
|
+
* URL, when the installed slug (`name`, used for activation checks) cannot be
|
|
58
|
+
* derived from the source or the wp.org release must be pinned. `version` is
|
|
59
|
+
* passed to `wp plugin install --version` when present. `source` is passed
|
|
60
|
+
* straight to `wp plugin install`.
|
|
59
61
|
*/
|
|
60
62
|
type PluginSource = string | {
|
|
61
63
|
name: string;
|
|
62
64
|
source: string;
|
|
65
|
+
version?: string;
|
|
63
66
|
};
|
|
64
67
|
/**
|
|
65
68
|
* A local plugin directory to bind-mount into the dev stack (`kizlo dev`) instead
|
package/dist/cli/wp/utils.d.ts
CHANGED
|
@@ -14,15 +14,15 @@ import { DevPluginSource, Fixture, PluginSource, SettleContext, TestCredentials
|
|
|
14
14
|
* Bumping `SEED_VERSION` invalidates older marks and forces a reseed.
|
|
15
15
|
*/
|
|
16
16
|
declare function isSeeded(): Promise<boolean>;
|
|
17
|
-
/** Resolve a `PluginSource` to the
|
|
18
|
-
declare function resolvePluginSource(plugin: PluginSource): [name: string, source: string];
|
|
17
|
+
/** Resolve a `PluginSource` to the arguments `ensurePlugin` needs. */
|
|
18
|
+
declare function resolvePluginSource(plugin: PluginSource): [name: string, source: string, version?: string];
|
|
19
19
|
/**
|
|
20
20
|
* Ensure a plugin is active without ever re-downloading. Plugin files persist in
|
|
21
21
|
* the `wp_data` volume, so an already-installed plugin is just activated; the
|
|
22
22
|
* download (`source` = release zip URL or wp.org slug) happens only when the files
|
|
23
23
|
* are absent (i.e. after `wp reset` wipes the volume).
|
|
24
24
|
*/
|
|
25
|
-
declare function ensurePlugin(name: string, source: string): Promise<void>;
|
|
25
|
+
declare function ensurePlugin(name: string, source: string, version?: string): Promise<void>;
|
|
26
26
|
/**
|
|
27
27
|
* Activate a bind-mounted local plugin by its directory basename (the slug the
|
|
28
28
|
* mount lands at under `wp-content/plugins`). Idempotent: the files arrive via the
|
package/dist/cli/wp/utils.js
CHANGED
|
@@ -29,9 +29,14 @@ async function isSeeded() {
|
|
|
29
29
|
]);
|
|
30
30
|
return marker.code === 0 && marker.stdout.trim() === SEED_VERSION;
|
|
31
31
|
}
|
|
32
|
-
/** Resolve a `PluginSource` to the
|
|
32
|
+
/** Resolve a `PluginSource` to the arguments `ensurePlugin` needs. */
|
|
33
33
|
function resolvePluginSource(plugin) {
|
|
34
|
-
|
|
34
|
+
if (typeof plugin === "string") return [plugin, plugin];
|
|
35
|
+
return plugin.version === void 0 ? [plugin.name, plugin.source] : [
|
|
36
|
+
plugin.name,
|
|
37
|
+
plugin.source,
|
|
38
|
+
plugin.version
|
|
39
|
+
];
|
|
35
40
|
}
|
|
36
41
|
/**
|
|
37
42
|
* Ensure a plugin is active without ever re-downloading. Plugin files persist in
|
|
@@ -39,7 +44,7 @@ function resolvePluginSource(plugin) {
|
|
|
39
44
|
* download (`source` = release zip URL or wp.org slug) happens only when the files
|
|
40
45
|
* are absent (i.e. after `wp reset` wipes the volume).
|
|
41
46
|
*/
|
|
42
|
-
async function ensurePlugin(name, source) {
|
|
47
|
+
async function ensurePlugin(name, source, version) {
|
|
43
48
|
if ((await compose([
|
|
44
49
|
"exec",
|
|
45
50
|
"-T",
|
|
@@ -69,6 +74,7 @@ async function ensurePlugin(name, source) {
|
|
|
69
74
|
"plugin",
|
|
70
75
|
"install",
|
|
71
76
|
source,
|
|
77
|
+
...version === void 0 ? [] : [`--version=${version}`],
|
|
72
78
|
"--activate"
|
|
73
79
|
]);
|
|
74
80
|
}
|
package/dist/comment/errors.js
CHANGED
package/dist/comment/index.js
CHANGED
|
@@ -98,26 +98,26 @@ const COMMENT_PROCEDURES = {
|
|
|
98
98
|
const connInfo = await context.getConnInfo();
|
|
99
99
|
if (!connInfo?.ip) throw new Error("Connection IP is required.");
|
|
100
100
|
if (!connInfo?.userAgent) throw new Error("Connection user agent is required.");
|
|
101
|
-
const
|
|
102
|
-
if (!
|
|
101
|
+
const session = await context.getSession();
|
|
102
|
+
if (!session) {
|
|
103
103
|
if (!input.body.captchaToken) throw errors.COMMENT_CAPTCHA_REQUIRED();
|
|
104
104
|
if (!await context.verifyCaptcha(input.body.captchaToken)) throw errors.COMMENT_CAPTCHA_INVALID();
|
|
105
105
|
}
|
|
106
|
-
const
|
|
107
|
-
user_id: user?.id,
|
|
106
|
+
const submission = {
|
|
108
107
|
author_ip: connInfo.ip,
|
|
109
108
|
user_agent: connInfo.userAgent,
|
|
110
109
|
content: input.body.content,
|
|
111
110
|
post_id: input.body.postId,
|
|
111
|
+
user_email: session?.email,
|
|
112
112
|
author_email: input.body.authorEmail,
|
|
113
113
|
author_name: input.body.authorName,
|
|
114
114
|
author_url: input.body.authorUrl,
|
|
115
115
|
parent: input.body.parentId
|
|
116
|
-
}
|
|
116
|
+
};
|
|
117
|
+
const response = await context.wordpress.kizlo.comments.create(submission);
|
|
117
118
|
if (response.error) switch (response.error.code) {
|
|
118
119
|
case "require_name_email": throw errors.COMMENT_NAME_EMAIL_REQUIRED();
|
|
119
120
|
case "require_valid_email": throw errors.COMMENT_INVALID_EMAIL();
|
|
120
|
-
case "kizlo_invalid_user": throw errors.COMMENT_USER_NOT_FOUND();
|
|
121
121
|
case "require_valid_comment": throw errors.COMMENT_EMPTY_CONTENT();
|
|
122
122
|
case "not_logged_in": throw errors.COMMENT_LOGIN_REQUIRED();
|
|
123
123
|
case "comment_closed": throw errors.COMMENT_CLOSED();
|