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
package/dist/adapters/auth.d.ts
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
import { Promisify } from "@kizlo/shared";
|
|
1
|
+
import { JsonValue, Promisify } from "@kizlo/shared";
|
|
2
2
|
|
|
3
3
|
//#region src/adapters/auth.d.ts
|
|
4
4
|
interface AuthUser {
|
|
5
|
-
/** The
|
|
6
|
-
id:
|
|
7
|
-
/** The user's email
|
|
8
|
-
email
|
|
9
|
-
/** The user's
|
|
10
|
-
|
|
11
|
-
/** The user's
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
|
|
5
|
+
/** The third-party auth id. This is the identity provider's own id, never a WordPress user id. */
|
|
6
|
+
id: string;
|
|
7
|
+
/** The user's email. The single join key that maps this session to a WordPress user. */
|
|
8
|
+
email: string;
|
|
9
|
+
/** The user's first name, if known. */
|
|
10
|
+
firstName?: string;
|
|
11
|
+
/** The user's last name, if known. */
|
|
12
|
+
lastName?: string;
|
|
13
|
+
/** Arbitrary claims the auth service carries alongside the identity. */
|
|
14
|
+
meta?: Record<string, JsonValue>;
|
|
15
15
|
}
|
|
16
|
-
type
|
|
16
|
+
type AuthGetSession = (request: Request | null) => Promisify<AuthUser | null>;
|
|
17
17
|
interface AuthAdapter {
|
|
18
|
-
/** Resolve the authenticated
|
|
19
|
-
|
|
18
|
+
/** Resolve the authenticated session from the request, or `null` when there is no session. Receives `null` for server-side (non-HTTP) invocations. */
|
|
19
|
+
getSession: AuthGetSession;
|
|
20
20
|
}
|
|
21
21
|
/** Author a custom auth adapter, typed against the {@link AuthAdapter} contract. */
|
|
22
22
|
declare function createAuthAdapter(adapter: AuthAdapter): AuthAdapter;
|
|
23
23
|
declare function authMock(options?: {
|
|
24
|
-
|
|
24
|
+
id?: string;
|
|
25
|
+
email?: string;
|
|
25
26
|
}): AuthAdapter;
|
|
26
27
|
//#endregion
|
|
27
|
-
export { AuthAdapter,
|
|
28
|
+
export { AuthAdapter, AuthGetSession, AuthUser, authMock, createAuthAdapter };
|
package/dist/adapters/auth.js
CHANGED
|
@@ -4,13 +4,12 @@ function createAuthAdapter(adapter) {
|
|
|
4
4
|
return adapter;
|
|
5
5
|
}
|
|
6
6
|
function authMock(options) {
|
|
7
|
-
return {
|
|
7
|
+
return { getSession() {
|
|
8
8
|
return {
|
|
9
|
-
id: options?.
|
|
9
|
+
id: options?.id ?? "auth-mock-user",
|
|
10
|
+
email: options?.email ?? "karan@gmail.com",
|
|
10
11
|
firstName: "Karan",
|
|
11
|
-
lastName: "Gill"
|
|
12
|
-
email: "karan@gmail.con",
|
|
13
|
-
phone: "12345678901"
|
|
12
|
+
lastName: "Gill"
|
|
14
13
|
};
|
|
15
14
|
} };
|
|
16
15
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { aliasWithSlash, resolveModuleImport, writeFileIfAbsent } from "../utils.js";
|
|
2
|
-
import { CONTRACT_BARREL,
|
|
2
|
+
import { CONTRACT_BARREL, INTROSPECTION_STUB } from "../daemon/generate.js";
|
|
3
3
|
import { resolvePatch } from "../presets/template.js";
|
|
4
4
|
import { orCancel } from "./_setup.js";
|
|
5
5
|
import { applyPatchToSource, patchChanged, renderPatchCode, resolvePatchTargetPath } from "../presets/patch.js";
|
|
@@ -8,16 +8,17 @@ import fs from "node:fs";
|
|
|
8
8
|
import * as p from "@clack/prompts";
|
|
9
9
|
|
|
10
10
|
//#region src/cli/commands/_wiring.ts
|
|
11
|
-
/** The kizlo.config.ts a scaffolded project gets: the Kizlo directory, the import-alias preference, and
|
|
12
|
-
* when local WordPress is chosen
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
/** The kizlo.config.ts a scaffolded project gets: the Kizlo directory, the import-alias preference, and,
|
|
12
|
+
* when local WordPress is chosen, `local: true` so `kizlo dev` and `kizlo test` boot the fixed
|
|
13
|
+
* `.kizlo/local` install (both stacks on with defaults; the object form is for configuring them). The
|
|
14
|
+
* alias is always written (`""` for relative imports included) so it records a made decision a later
|
|
15
|
+
* `kizlo init` reads back instead of prompting for it again. The alias is written in its canonical `@/`
|
|
16
|
+
* form (never a bare `@`) so it reads like the imports it produces. */
|
|
16
17
|
function kizloConfigTemplate(dir, alias, localWordPress = false) {
|
|
17
18
|
return `import { defineConfig } from "kizlo/config"
|
|
18
19
|
|
|
19
20
|
export default defineConfig({
|
|
20
|
-
dir: "${dir}",${`\n\talias: "${aliasWithSlash(alias)}",`}${localWordPress ? `\n\
|
|
21
|
+
dir: "${dir}",${`\n\talias: "${aliasWithSlash(alias)}",`}${localWordPress ? `\n\tlocal: true,` : ""}
|
|
21
22
|
})
|
|
22
23
|
`;
|
|
23
24
|
}
|
|
@@ -77,7 +78,7 @@ function writeGeneratedContract(cwd, serverDirRel) {
|
|
|
77
78
|
const generatedDirRel = path.join(serverDirRel, "generated");
|
|
78
79
|
writeFileIfAbsent(path.join(cwd, generatedDirRel, "contract.json"), "{}\n");
|
|
79
80
|
writeFileIfAbsent(path.join(cwd, generatedDirRel, "index.ts"), CONTRACT_BARREL);
|
|
80
|
-
writeFileIfAbsent(path.join(cwd, generatedDirRel, "
|
|
81
|
+
writeFileIfAbsent(path.join(cwd, generatedDirRel, "introspection.ts"), INTROSPECTION_STUB);
|
|
81
82
|
}
|
|
82
83
|
/**
|
|
83
84
|
* Apply every patch a template declares, merging Kizlo wiring into files the project already owns
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { log } from "../daemon/logger.js";
|
|
2
|
-
import { resolveConfig
|
|
3
|
-
import {
|
|
2
|
+
import { resolveConfig } from "../daemon/config.js";
|
|
3
|
+
import { generateIntrospectionSource, reportGenerationError } from "../daemon/generate.js";
|
|
4
4
|
import { testWordPressCredentials } from "./_test-wordpress.js";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import fs from "node:fs";
|
|
@@ -26,14 +26,10 @@ function formatGeneratedFileDiff(cwd, mismatch) {
|
|
|
26
26
|
const file = path.relative(cwd, mismatch.file) || path.basename(mismatch.file);
|
|
27
27
|
return createTwoFilesPatch(file, file, mismatch.actual, mismatch.expected, "committed", "generated", { context: 3 });
|
|
28
28
|
}
|
|
29
|
-
/**
|
|
30
|
-
async function
|
|
31
|
-
const files = /* @__PURE__ */ new Set();
|
|
32
|
-
const workspaceDir = await resolveWordPressClientDir(cwd);
|
|
33
|
-
if (workspaceDir) files.add(path.resolve(cwd, workspaceDir, "wordpress.ts"));
|
|
29
|
+
/** The generated introspection the project configuration owns, if any. */
|
|
30
|
+
async function introspectionFiles(cwd, dir) {
|
|
34
31
|
const config = await resolveConfig(cwd, { dir });
|
|
35
|
-
|
|
36
|
-
return [...files];
|
|
32
|
+
return config ? [path.resolve(cwd, config.introspectionPath)] : [];
|
|
37
33
|
}
|
|
38
34
|
const check = defineCommand({
|
|
39
35
|
meta: {
|
|
@@ -52,9 +48,9 @@ const check = defineCommand({
|
|
|
52
48
|
},
|
|
53
49
|
async run({ args }) {
|
|
54
50
|
const cwd = process.cwd();
|
|
55
|
-
const files = await
|
|
51
|
+
const files = await introspectionFiles(cwd, args.dir);
|
|
56
52
|
if (files.length === 0) {
|
|
57
|
-
log.info("No generated WordPress
|
|
53
|
+
log.info("No generated WordPress introspection configured, nothing to check.");
|
|
58
54
|
return;
|
|
59
55
|
}
|
|
60
56
|
let credentials;
|
|
@@ -67,18 +63,18 @@ const check = defineCommand({
|
|
|
67
63
|
}
|
|
68
64
|
let expected;
|
|
69
65
|
try {
|
|
70
|
-
expected = await
|
|
66
|
+
expected = await generateIntrospectionSource(cwd, {
|
|
71
67
|
credentials,
|
|
72
68
|
strict: true
|
|
73
69
|
});
|
|
74
70
|
} catch (error) {
|
|
75
|
-
reportGenerationError("Failed to check the generated WordPress
|
|
71
|
+
reportGenerationError("Failed to check the generated WordPress introspection:", error);
|
|
76
72
|
process.exitCode = 1;
|
|
77
73
|
return;
|
|
78
74
|
}
|
|
79
75
|
const mismatches = findGeneratedFileMismatches(files, expected);
|
|
80
76
|
if (mismatches.length === 0) {
|
|
81
|
-
log.success(
|
|
77
|
+
log.success("WordPress introspection is current");
|
|
82
78
|
return;
|
|
83
79
|
}
|
|
84
80
|
for (const mismatch of mismatches) {
|
|
@@ -90,7 +86,7 @@ const check = defineCommand({
|
|
|
90
86
|
args.test ? "--test" : "",
|
|
91
87
|
args.dir ? `--dir ${args.dir}` : ""
|
|
92
88
|
].filter(Boolean).join(" ");
|
|
93
|
-
log.info(`Run \`${command}\` and commit the updated
|
|
89
|
+
log.info(`Run \`${command}\` and commit the updated introspection.`);
|
|
94
90
|
process.exitCode = 1;
|
|
95
91
|
}
|
|
96
92
|
});
|
package/dist/cli/commands/dev.js
CHANGED
|
@@ -25,12 +25,12 @@ async function resolve$1(cwd) {
|
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
27
|
* The lifecycle subcommands (`stop`/`reset`) only act on local WordPress. A project pointing at its
|
|
28
|
-
* own WordPress (no `
|
|
28
|
+
* own WordPress (no `local`) has nothing to manage, so print a plain note and exit cleanly. Returns
|
|
29
29
|
* true when local WordPress is configured and the caller may proceed.
|
|
30
30
|
*/
|
|
31
31
|
async function requireLocalWordPress(cwd) {
|
|
32
32
|
if (await usesLocalWordPress(cwd)) return true;
|
|
33
|
-
note("This project doesn't run local WordPress — set `
|
|
33
|
+
note("This project doesn't run local WordPress — set `local: true` in kizlo.config.ts to add it.");
|
|
34
34
|
return false;
|
|
35
35
|
}
|
|
36
36
|
/** A dim, untagged one-liner (no consola timestamp) for the clean dev output. */
|
|
@@ -351,7 +351,7 @@ async function watchOnly(cwd) {
|
|
|
351
351
|
async function bringUp() {
|
|
352
352
|
const cwd = process.cwd();
|
|
353
353
|
if (!await usesLocalWordPress(cwd)) {
|
|
354
|
-
note("This project doesn't run local WordPress — running the contract watcher only. Set `
|
|
354
|
+
note("This project doesn't run local WordPress — running the contract watcher only. Set `local` in kizlo.config.ts to add it.");
|
|
355
355
|
await watchOnly(cwd);
|
|
356
356
|
return;
|
|
357
357
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { log } from "../daemon/logger.js";
|
|
2
|
-
import { resolveConfig
|
|
3
|
-
import { PartialContractError, generateOnce,
|
|
2
|
+
import { resolveConfig } from "../daemon/config.js";
|
|
3
|
+
import { PartialContractError, generateOnce, reportGenerationError } from "../daemon/generate.js";
|
|
4
4
|
import { testWordPressCredentials } from "./_test-wordpress.js";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { defineCommand } from "citty";
|
|
@@ -12,7 +12,7 @@ import { defineCommand } from "citty";
|
|
|
12
12
|
* mode did not do to the files on disk.
|
|
13
13
|
*/
|
|
14
14
|
function fail(message, error) {
|
|
15
|
-
if (error instanceof PartialContractError) log.error(`${error.message} The generated
|
|
15
|
+
if (error instanceof PartialContractError) log.error(`${error.message} The generated introspection on disk is unchanged.`);
|
|
16
16
|
else reportGenerationError(message, error);
|
|
17
17
|
process.exit(1);
|
|
18
18
|
}
|
|
@@ -28,7 +28,7 @@ const generate = defineCommand({
|
|
|
28
28
|
},
|
|
29
29
|
strict: {
|
|
30
30
|
type: "boolean",
|
|
31
|
-
description: "Fail instead of generating
|
|
31
|
+
description: "Fail instead of generating an introspection WordPress had to exclude routes or types from"
|
|
32
32
|
},
|
|
33
33
|
test: {
|
|
34
34
|
type: "boolean",
|
|
@@ -50,29 +50,27 @@ const generate = defineCommand({
|
|
|
50
50
|
credentials,
|
|
51
51
|
strict
|
|
52
52
|
};
|
|
53
|
-
const wordpressClientDir = await resolveWordPressClientDir(cwd);
|
|
54
|
-
if (wordpressClientDir) try {
|
|
55
|
-
const result = await generateWorkspaceClientOnce(cwd, wordpressClientDir, options);
|
|
56
|
-
const file = path.resolve(cwd, wordpressClientDir, "wordpress.ts");
|
|
57
|
-
log.success(`WordPress client ${result === "generated" ? "written to" : "already current at"} ${file}`);
|
|
58
|
-
} catch (error) {
|
|
59
|
-
fail("Failed to generate the WordPress client:", error);
|
|
60
|
-
}
|
|
61
53
|
const cfg = await resolveConfig(cwd, { dir: args.dir });
|
|
62
54
|
if (!cfg) {
|
|
63
|
-
|
|
55
|
+
log.info("Nothing to generate. Set `dir` in kizlo.config.ts or pass --dir.");
|
|
64
56
|
return;
|
|
65
57
|
}
|
|
66
|
-
let
|
|
58
|
+
let result;
|
|
67
59
|
try {
|
|
68
|
-
|
|
60
|
+
result = await generateOnce(cfg, options);
|
|
69
61
|
} catch (error) {
|
|
70
62
|
fail("Failed to generate the Kizlo contract:", error);
|
|
71
63
|
}
|
|
72
|
-
if (
|
|
73
|
-
log.error(`No Kizlo server found in ${cfg.
|
|
64
|
+
if (result.contract === "empty" && cfg.server) {
|
|
65
|
+
log.error(`No Kizlo server found in ${cfg.server.entry}`);
|
|
74
66
|
process.exit(1);
|
|
75
67
|
}
|
|
68
|
+
if (result.contract === "none") {
|
|
69
|
+
const file = path.resolve(cwd, cfg.introspectionPath);
|
|
70
|
+
log.success(`WordPress introspection ${result.introspection === "generated" ? "written to" : "already current at"} ${file}`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (result.introspection === "generated") log.success("WordPress introspection generated");
|
|
76
74
|
log.success("Contract generated");
|
|
77
75
|
}
|
|
78
76
|
});
|
|
@@ -114,9 +114,9 @@ const runArgs = {
|
|
|
114
114
|
}
|
|
115
115
|
};
|
|
116
116
|
/**
|
|
117
|
-
* Run the project's tests. When
|
|
118
|
-
* suite (JS then the plugin's PHPUnit) against it, then leave it
|
|
119
|
-
*
|
|
117
|
+
* Run the project's tests. When the test stack is enabled (`local` with `test.enable !== false`), boot
|
|
118
|
+
* (+ seed) local WordPress first, run the suite (JS then the plugin's PHPUnit) against it, then leave it
|
|
119
|
+
* up (or tear down). Otherwise there's no Docker WordPress to manage — just run the project's own test script.
|
|
120
120
|
*/
|
|
121
121
|
async function runSuite({ args }) {
|
|
122
122
|
const cfg = await resolveTestConfig(process.cwd());
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_WORDPRESS_TAG,
|
|
1
|
+
import { DEFAULT_WORDPRESS_TAG, INTROSPECTION_META_REL, LOCAL_DIR_REL } from "../wp/constants.js";
|
|
2
2
|
import { credentialsPath, findConfigDir } from "../wp/utils.js";
|
|
3
3
|
import { log } from "./logger.js";
|
|
4
4
|
import { detectPackageManager } from "../utils.js";
|
|
@@ -11,39 +11,82 @@ import fs from "node:fs";
|
|
|
11
11
|
//#region src/cli/daemon/config.ts
|
|
12
12
|
const fixtureSchema = z.custom((value) => typeof value === "object" && value !== null && typeof value.name === "string", { message: "must be a fixture object with a string `name` (use defineFixture)" });
|
|
13
13
|
/**
|
|
14
|
-
* `dev.version` / `test.version`. Validated against Docker's tag grammar here rather than
|
|
15
|
-
* so `"wordpress:7.1.0"`, which would resolve to `wordpress:wordpress:7.1.0`, is named as a
|
|
16
|
-
* mistake instead of surfacing as a pull failure seconds into starting a stack.
|
|
14
|
+
* `local.dev.version` / `local.test.version`. Validated against Docker's tag grammar here rather than
|
|
15
|
+
* at boot, so `"wordpress:7.1.0"`, which would resolve to `wordpress:wordpress:7.1.0`, is named as a
|
|
16
|
+
* config mistake instead of surfacing as a pull failure seconds into starting a stack.
|
|
17
17
|
*/
|
|
18
18
|
const versionSchema = z.string().regex(WORDPRESS_TAG_PATTERN, "must be a WordPress image tag, like \"7.1.0\" or \"7.1.0-php8.3-apache\" (no \"wordpress:\" prefix)");
|
|
19
|
+
/**
|
|
20
|
+
* A key that moved in the config redesign. It accepts only `undefined`, so an absent key passes and a
|
|
21
|
+
* present one fails validation with a message naming where it went: a hard cutover with no silent
|
|
22
|
+
* fallback. {@link loadConfigFile} surfaces the message through `z.prettifyError`.
|
|
23
|
+
*/
|
|
24
|
+
const removedKey = (message) => z.undefined({ error: message }).optional();
|
|
25
|
+
const dirSchema = z.union([z.string(), z.object({
|
|
26
|
+
server: z.string().optional(),
|
|
27
|
+
contract: z.string().optional(),
|
|
28
|
+
introspection: z.string().optional()
|
|
29
|
+
})]);
|
|
30
|
+
const devStackSchema = z.object({
|
|
31
|
+
enable: z.boolean().optional(),
|
|
32
|
+
port: z.number().int().positive().optional(),
|
|
33
|
+
version: versionSchema.optional(),
|
|
34
|
+
dbPort: z.number().int().positive().optional(),
|
|
35
|
+
fixtures: z.array(fixtureSchema).optional()
|
|
36
|
+
});
|
|
37
|
+
const testStackSchema = z.object({
|
|
38
|
+
enable: z.boolean().optional(),
|
|
39
|
+
inherit: z.boolean().optional(),
|
|
40
|
+
port: z.number().int().positive().optional(),
|
|
41
|
+
version: versionSchema.optional(),
|
|
42
|
+
fixtures: z.array(fixtureSchema).optional(),
|
|
43
|
+
packageManager: z.enum([
|
|
44
|
+
"npm",
|
|
45
|
+
"pnpm",
|
|
46
|
+
"yarn",
|
|
47
|
+
"bun"
|
|
48
|
+
]).optional(),
|
|
49
|
+
command: z.string().optional()
|
|
50
|
+
});
|
|
51
|
+
const localSchema = z.union([z.boolean(), z.object({
|
|
52
|
+
enable: z.boolean().optional(),
|
|
53
|
+
name: z.string().optional(),
|
|
54
|
+
worktrees: z.boolean().optional(),
|
|
55
|
+
dev: devStackSchema.optional(),
|
|
56
|
+
test: testStackSchema.optional()
|
|
57
|
+
})]);
|
|
19
58
|
/** Runtime shape of `kizlo.config.*` — mirrors {@link KizloGlobalConfig}. */
|
|
20
59
|
const configSchema = z.object({
|
|
21
|
-
dir:
|
|
60
|
+
dir: dirSchema.optional(),
|
|
22
61
|
alias: z.string().optional(),
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
version: versionSchema.optional(),
|
|
30
|
-
dbPort: z.number().int().positive().optional(),
|
|
31
|
-
fixtures: z.array(fixtureSchema).optional()
|
|
32
|
-
}).optional(),
|
|
33
|
-
test: z.object({
|
|
34
|
-
local: z.boolean().optional(),
|
|
35
|
-
port: z.number().int().positive().optional(),
|
|
36
|
-
version: versionSchema.optional(),
|
|
37
|
-
fixtures: z.array(fixtureSchema).optional(),
|
|
38
|
-
packageManager: z.enum([
|
|
39
|
-
"npm",
|
|
40
|
-
"pnpm",
|
|
41
|
-
"yarn",
|
|
42
|
-
"bun"
|
|
43
|
-
]).optional(),
|
|
44
|
-
command: z.string().optional()
|
|
45
|
-
}).optional()
|
|
62
|
+
local: localSchema.optional(),
|
|
63
|
+
wordpressClientDir: removedKey("`wordpressClientDir` has been removed. Use `dir: { introspection }` instead."),
|
|
64
|
+
name: removedKey("`name` is no longer a config root key. Move it under `local.name`."),
|
|
65
|
+
worktrees: removedKey("`worktrees` is no longer a config root key. Move it under `local.worktrees`."),
|
|
66
|
+
dev: removedKey("`dev` is no longer a config root key. Move it under `local.dev`."),
|
|
67
|
+
test: removedKey("`test` is no longer a config root key. Move it under `local.test`.")
|
|
46
68
|
});
|
|
69
|
+
/** Normalize `local` (absent, `true`, `false`, or the object form) into one shape the resolvers read. */
|
|
70
|
+
function resolveLocal(fileConfig) {
|
|
71
|
+
const local = fileConfig?.local;
|
|
72
|
+
if (local === true) return {
|
|
73
|
+
enabled: true,
|
|
74
|
+
dev: {},
|
|
75
|
+
test: {}
|
|
76
|
+
};
|
|
77
|
+
if (!local) return {
|
|
78
|
+
enabled: false,
|
|
79
|
+
dev: {},
|
|
80
|
+
test: {}
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
enabled: local.enable !== false,
|
|
84
|
+
name: local.name,
|
|
85
|
+
worktrees: local.worktrees,
|
|
86
|
+
dev: local.dev ?? {},
|
|
87
|
+
test: local.test ?? {}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
47
90
|
const CONFIG_FILES = [
|
|
48
91
|
"kizlo.config.ts",
|
|
49
92
|
"kizlo.config.js",
|
|
@@ -52,6 +95,12 @@ const CONFIG_FILES = [
|
|
|
52
95
|
const DEFAULT_DEV_PORT = 8080;
|
|
53
96
|
const DEFAULT_DEV_DB_PORT = 3307;
|
|
54
97
|
const DEFAULT_TEST_PORT = 8889;
|
|
98
|
+
/** The generated introspection artifact's filename, written under its resolved directory. */
|
|
99
|
+
const INTROSPECTION_FILE = "introspection.ts";
|
|
100
|
+
/** Strip a leading `./` and trailing slashes so a config path joins cleanly. */
|
|
101
|
+
function normalizeDir(value) {
|
|
102
|
+
return value.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
103
|
+
}
|
|
55
104
|
async function loadConfigFile(cwd) {
|
|
56
105
|
const file = CONFIG_FILES.map((name) => path.join(cwd, name)).find((p) => fs.existsSync(p));
|
|
57
106
|
if (!file) return void 0;
|
|
@@ -71,40 +120,56 @@ async function loadConfigFile(cwd) {
|
|
|
71
120
|
return result.data;
|
|
72
121
|
}
|
|
73
122
|
/**
|
|
74
|
-
* Resolve
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
123
|
+
* Resolve what Kizlo generates and watches from `dir`: the `--dir` flag or `dir` in `kizlo.config.*`.
|
|
124
|
+
* A string is the home Kizlo owns the layout under (sources in `<dir>/server`, generated files under
|
|
125
|
+
* `<dir>/server/generated`); the object form sets `server` / `contract` / `introspection` independently.
|
|
126
|
+
*
|
|
127
|
+
* The introspection is always generated at its resolved path, so a project with no server still gets it.
|
|
128
|
+
* `server` is present only when `dir` resolves a server path (the string form always does, the object
|
|
129
|
+
* form only when `server` is set), and its absence means there is nothing to watch and no contract to
|
|
130
|
+
* build. Returns `undefined` only when nothing at all is configured (no `dir`, or an object naming
|
|
131
|
+
* neither a server nor an introspection path): there is then nothing to generate.
|
|
79
132
|
*/
|
|
80
133
|
async function resolveConfig(cwd, flags) {
|
|
81
134
|
const fileConfig = await loadConfigFile(cwd);
|
|
82
135
|
const raw = flags?.dir ?? fileConfig?.dir;
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
136
|
+
if (raw === void 0) return void 0;
|
|
137
|
+
if (typeof raw === "string") {
|
|
138
|
+
const home = normalizeDir(raw);
|
|
139
|
+
const serverDir$1 = path.join(home, "server");
|
|
140
|
+
const contractDir$1 = path.join(serverDir$1, "generated");
|
|
141
|
+
return {
|
|
142
|
+
cwd,
|
|
143
|
+
server: {
|
|
144
|
+
dir: serverDir$1,
|
|
145
|
+
entry: path.join(serverDir$1, "index.ts"),
|
|
146
|
+
contractDir: contractDir$1,
|
|
147
|
+
contractPath: path.join(contractDir$1, "contract.json"),
|
|
148
|
+
barrelPath: path.join(contractDir$1, "index.ts")
|
|
149
|
+
},
|
|
150
|
+
introspectionPath: path.join(contractDir$1, INTROSPECTION_FILE),
|
|
151
|
+
introspectionMetaPath: INTROSPECTION_META_REL
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const serverDir = raw.server ? normalizeDir(raw.server) : void 0;
|
|
155
|
+
const contractDir = serverDir ? raw.contract ? normalizeDir(raw.contract) : path.join(serverDir, "generated") : void 0;
|
|
156
|
+
const server = serverDir && contractDir ? {
|
|
157
|
+
dir: serverDir,
|
|
158
|
+
entry: path.join(serverDir, "index.ts"),
|
|
159
|
+
contractDir,
|
|
160
|
+
contractPath: path.join(contractDir, "contract.json"),
|
|
161
|
+
barrelPath: path.join(contractDir, "index.ts")
|
|
162
|
+
} : void 0;
|
|
163
|
+
const introspectionDir = raw.introspection ? normalizeDir(raw.introspection) : contractDir;
|
|
164
|
+
if (!introspectionDir) return void 0;
|
|
87
165
|
return {
|
|
88
166
|
cwd,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
generatedDir,
|
|
93
|
-
contractPath: path.join(generatedDir, "contract.json"),
|
|
94
|
-
barrelPath: path.join(generatedDir, "index.ts"),
|
|
95
|
-
wordpressPath: path.join(generatedDir, "wordpress.ts"),
|
|
96
|
-
wordpressMetaPath: WORDPRESS_META_REL
|
|
167
|
+
server,
|
|
168
|
+
introspectionPath: path.join(introspectionDir, INTROSPECTION_FILE),
|
|
169
|
+
introspectionMetaPath: INTROSPECTION_META_REL
|
|
97
170
|
};
|
|
98
171
|
}
|
|
99
172
|
/**
|
|
100
|
-
* The directory `wordpress.ts` is written to for a workspace with no Kizlo server, from `wordpressClientDir`
|
|
101
|
-
* in `kizlo.config.*`. Resolved on its own rather than through {@link resolveConfig}, which describes a
|
|
102
|
-
* server layout and returns nothing without `dir` — the two are independent.
|
|
103
|
-
*/
|
|
104
|
-
async function resolveWordPressClientDir(cwd) {
|
|
105
|
-
return (await loadConfigFile(cwd))?.wordpressClientDir;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
173
|
* Sanitize an arbitrary package/dir name into a valid Docker compose project id:
|
|
109
174
|
* lowercase, `@scope/pkg` → `scope-pkg`, dropping anything outside `[a-z0-9_-]`.
|
|
110
175
|
*/
|
|
@@ -164,61 +229,62 @@ function stackProject(baseName, kind) {
|
|
|
164
229
|
return `kizlo-${baseName}-${kind}`;
|
|
165
230
|
}
|
|
166
231
|
/**
|
|
167
|
-
* Resolve the
|
|
168
|
-
*
|
|
169
|
-
* and
|
|
232
|
+
* Resolve the test stack (`local.test`) into concrete values for the `test` command, applying defaults
|
|
233
|
+
* (port 8889, auto-detected package manager) and anchoring the credentials artifact to the config
|
|
234
|
+
* directory. `version` and `fixtures` fall back to the dev stack unless `test.inherit` is `false`;
|
|
235
|
+
* `port`, `packageManager`, and `command` are never inherited.
|
|
170
236
|
*/
|
|
171
237
|
async function resolveTestConfig(cwd) {
|
|
172
238
|
const configDir = findConfigDir(cwd);
|
|
173
|
-
const
|
|
174
|
-
const
|
|
239
|
+
const local = resolveLocal(await loadConfigFile(configDir));
|
|
240
|
+
const { dev, test } = local;
|
|
241
|
+
const inherit = test.inherit !== false;
|
|
175
242
|
return {
|
|
176
243
|
configDir,
|
|
177
|
-
local:
|
|
244
|
+
local: local.enabled && test.enable !== false,
|
|
178
245
|
project: stackProject(resolveStackName(configDir, {
|
|
179
|
-
name:
|
|
180
|
-
worktrees:
|
|
246
|
+
name: local.name,
|
|
247
|
+
worktrees: local.worktrees
|
|
181
248
|
}), "test"),
|
|
182
249
|
command: test.command,
|
|
183
250
|
port: test.port ?? DEFAULT_TEST_PORT,
|
|
184
|
-
portExplicit: test.port !== void 0 && !
|
|
185
|
-
wordpressTag: test.version ?? DEFAULT_WORDPRESS_TAG,
|
|
186
|
-
fixtures: test.fixtures ?? [],
|
|
251
|
+
portExplicit: test.port !== void 0 && !local.worktrees,
|
|
252
|
+
wordpressTag: test.version ?? (inherit ? dev.version : void 0) ?? DEFAULT_WORDPRESS_TAG,
|
|
253
|
+
fixtures: test.fixtures ?? (inherit ? dev.fixtures : void 0) ?? [],
|
|
187
254
|
credentialsPath: credentialsPath(cwd),
|
|
188
255
|
packageManager: test.packageManager ?? await detectPackageManager(configDir) ?? "npm"
|
|
189
256
|
};
|
|
190
257
|
}
|
|
191
258
|
/**
|
|
192
|
-
* Whether this project runs local WordPress under `kizlo dev
|
|
193
|
-
* `kizlo.config.*`. When false, `kizlo dev` has nothing to boot and
|
|
194
|
-
* the path a project pointing at its own WordPress takes.
|
|
195
|
-
* local WordPress is chosen,
|
|
196
|
-
* survives `kizlo dev reset`.
|
|
259
|
+
* Whether this project runs local WordPress under `kizlo dev`: local is enabled and the dev stack is
|
|
260
|
+
* on (`local.dev.enable !== false`) in `kizlo.config.*`. When false, `kizlo dev` has nothing to boot and
|
|
261
|
+
* runs the contract watcher alone, the path a project pointing at its own WordPress takes. Written by
|
|
262
|
+
* `create`/`init` when local WordPress is chosen, so it's committed and survives `kizlo dev reset`.
|
|
197
263
|
*/
|
|
198
264
|
async function usesLocalWordPress(cwd) {
|
|
199
|
-
const
|
|
200
|
-
return
|
|
265
|
+
const local = resolveLocal(await loadConfigFile(findConfigDir(cwd)));
|
|
266
|
+
return local.enabled && local.dev.enable !== false;
|
|
201
267
|
}
|
|
202
268
|
/**
|
|
203
|
-
* Resolve the
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
269
|
+
* Resolve the dev stack (`local.dev`) into concrete values for the `dev` command, applying defaults
|
|
270
|
+
* (port 8080). The install folder is fixed at `.kizlo/local` (no longer a config choice), so there's
|
|
271
|
+
* nothing required here. Callers gate on {@link usesLocalWordPress} first, since a project without
|
|
272
|
+
* local WordPress runs the watcher alone rather than reaching here.
|
|
207
273
|
*/
|
|
208
274
|
async function resolveDevConfig(cwd) {
|
|
209
275
|
const configDir = findConfigDir(cwd);
|
|
210
|
-
const
|
|
211
|
-
const dev =
|
|
276
|
+
const local = resolveLocal(await loadConfigFile(configDir));
|
|
277
|
+
const { dev } = local;
|
|
212
278
|
return {
|
|
213
279
|
configDir,
|
|
214
280
|
project: stackProject(resolveStackName(configDir, {
|
|
215
|
-
name:
|
|
216
|
-
worktrees:
|
|
281
|
+
name: local.name,
|
|
282
|
+
worktrees: local.worktrees
|
|
217
283
|
}), "dev"),
|
|
218
284
|
port: dev.port ?? DEFAULT_DEV_PORT,
|
|
219
|
-
portExplicit: dev.port !== void 0 && !
|
|
285
|
+
portExplicit: dev.port !== void 0 && !local.worktrees,
|
|
220
286
|
dbPort: dev.dbPort ?? DEFAULT_DEV_DB_PORT,
|
|
221
|
-
dbPortExplicit: dev.dbPort !== void 0 && !
|
|
287
|
+
dbPortExplicit: dev.dbPort !== void 0 && !local.worktrees,
|
|
222
288
|
wordpressTag: dev.version ?? DEFAULT_WORDPRESS_TAG,
|
|
223
289
|
fixtures: dev.fixtures ?? [],
|
|
224
290
|
wordpressPath: LOCAL_DIR_REL,
|
|
@@ -227,4 +293,4 @@ async function resolveDevConfig(cwd) {
|
|
|
227
293
|
}
|
|
228
294
|
|
|
229
295
|
//#endregion
|
|
230
|
-
export { CONFIG_FILES, DEFAULT_DEV_DB_PORT, DEFAULT_DEV_PORT, resolveConfig, resolveDevConfig, resolveStackName, resolveTestConfig,
|
|
296
|
+
export { CONFIG_FILES, DEFAULT_DEV_DB_PORT, DEFAULT_DEV_PORT, resolveConfig, resolveDevConfig, resolveStackName, resolveTestConfig, stackProject, usesLocalWordPress };
|