@wp-typia/project-tools 0.22.9 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/runtime/ai-artifacts.js +3 -4
  2. package/dist/runtime/ai-feature-artifacts.js +2 -4
  3. package/dist/runtime/cli-add-collision.d.ts +25 -0
  4. package/dist/runtime/cli-add-collision.js +76 -0
  5. package/dist/runtime/cli-add-filesystem.js +2 -15
  6. package/dist/runtime/cli-add-help.js +11 -2
  7. package/dist/runtime/cli-add-kind-ids.d.ts +1 -1
  8. package/dist/runtime/cli-add-kind-ids.js +3 -0
  9. package/dist/runtime/cli-add-types.d.ts +117 -0
  10. package/dist/runtime/cli-add-types.js +29 -1
  11. package/dist/runtime/cli-add-validation.d.ts +90 -1
  12. package/dist/runtime/cli-add-validation.js +304 -1
  13. package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +74 -19
  14. package/dist/runtime/cli-add-workspace-admin-view-source.js +11 -2
  15. package/dist/runtime/cli-add-workspace-admin-view-templates.d.ts +20 -2
  16. package/dist/runtime/cli-add-workspace-admin-view-templates.js +359 -3
  17. package/dist/runtime/cli-add-workspace-admin-view-types.d.ts +21 -0
  18. package/dist/runtime/cli-add-workspace-admin-view-types.js +22 -0
  19. package/dist/runtime/cli-add-workspace-ai-anchors.js +121 -31
  20. package/dist/runtime/cli-add-workspace-contract-source-emitters.d.ts +15 -0
  21. package/dist/runtime/cli-add-workspace-contract-source-emitters.js +42 -0
  22. package/dist/runtime/cli-add-workspace-contract.d.ts +15 -0
  23. package/dist/runtime/cli-add-workspace-contract.js +65 -0
  24. package/dist/runtime/cli-add-workspace-integration-env.d.ts +24 -0
  25. package/dist/runtime/cli-add-workspace-integration-env.js +391 -0
  26. package/dist/runtime/cli-add-workspace-post-meta-anchors.d.ts +23 -0
  27. package/dist/runtime/cli-add-workspace-post-meta-anchors.js +244 -0
  28. package/dist/runtime/cli-add-workspace-post-meta-source-emitters.d.ts +63 -0
  29. package/dist/runtime/cli-add-workspace-post-meta-source-emitters.js +179 -0
  30. package/dist/runtime/cli-add-workspace-post-meta.d.ts +15 -0
  31. package/dist/runtime/cli-add-workspace-post-meta.js +107 -0
  32. package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +1 -0
  33. package/dist/runtime/cli-add-workspace-rest-anchors.js +285 -21
  34. package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +90 -2
  35. package/dist/runtime/cli-add-workspace-rest-source-emitters.js +302 -29
  36. package/dist/runtime/cli-add-workspace-rest.d.ts +15 -2
  37. package/dist/runtime/cli-add-workspace-rest.js +329 -21
  38. package/dist/runtime/cli-add-workspace.d.ts +15 -0
  39. package/dist/runtime/cli-add-workspace.js +15 -0
  40. package/dist/runtime/cli-add.d.ts +1 -1
  41. package/dist/runtime/cli-add.js +1 -1
  42. package/dist/runtime/cli-core.d.ts +2 -1
  43. package/dist/runtime/cli-core.js +2 -1
  44. package/dist/runtime/cli-doctor-environment.js +1 -3
  45. package/dist/runtime/cli-doctor-workspace-features.js +128 -10
  46. package/dist/runtime/cli-doctor-workspace-package.d.ts +25 -3
  47. package/dist/runtime/cli-doctor-workspace-package.js +35 -13
  48. package/dist/runtime/cli-doctor-workspace-shared.d.ts +2 -0
  49. package/dist/runtime/cli-doctor-workspace-shared.js +5 -0
  50. package/dist/runtime/cli-doctor-workspace.d.ts +1 -1
  51. package/dist/runtime/cli-doctor-workspace.js +16 -8
  52. package/dist/runtime/cli-doctor.js +1 -1
  53. package/dist/runtime/cli-help.js +7 -0
  54. package/dist/runtime/cli-init-templates.js +11 -1
  55. package/dist/runtime/contract-artifacts.d.ts +14 -0
  56. package/dist/runtime/contract-artifacts.js +15 -0
  57. package/dist/runtime/fs-async.d.ts +7 -0
  58. package/dist/runtime/fs-async.js +11 -2
  59. package/dist/runtime/index.d.ts +1 -1
  60. package/dist/runtime/index.js +1 -1
  61. package/dist/runtime/migration-maintenance-verify.js +3 -0
  62. package/dist/runtime/migration-render-generated.js +4 -0
  63. package/dist/runtime/package-versions.js +3 -7
  64. package/dist/runtime/rest-resource-artifacts.d.ts +57 -1
  65. package/dist/runtime/rest-resource-artifacts.js +97 -1
  66. package/dist/runtime/scaffold-repository-reference.js +3 -7
  67. package/dist/runtime/template-render.d.ts +1 -1
  68. package/dist/runtime/template-render.js +1 -1
  69. package/dist/runtime/template-source-cache-markers.d.ts +37 -0
  70. package/dist/runtime/template-source-cache-markers.js +125 -0
  71. package/dist/runtime/template-source-cache.d.ts +1 -4
  72. package/dist/runtime/template-source-cache.js +16 -122
  73. package/dist/runtime/template-source-external.d.ts +4 -2
  74. package/dist/runtime/template-source-external.js +4 -2
  75. package/dist/runtime/template-source-remote.d.ts +8 -4
  76. package/dist/runtime/template-source-remote.js +8 -4
  77. package/dist/runtime/typia-llm.js +3 -4
  78. package/dist/runtime/workspace-inventory-mutations.d.ts +24 -0
  79. package/dist/runtime/workspace-inventory-mutations.js +181 -0
  80. package/dist/runtime/workspace-inventory-parser.d.ts +53 -0
  81. package/dist/runtime/workspace-inventory-parser.js +632 -0
  82. package/dist/runtime/workspace-inventory-read.d.ts +51 -0
  83. package/dist/runtime/workspace-inventory-read.js +98 -0
  84. package/dist/runtime/workspace-inventory-templates.d.ts +50 -0
  85. package/dist/runtime/workspace-inventory-templates.js +268 -0
  86. package/dist/runtime/workspace-inventory-types.d.ts +220 -0
  87. package/dist/runtime/workspace-inventory-types.js +1 -0
  88. package/dist/runtime/workspace-inventory.d.ts +5 -252
  89. package/dist/runtime/workspace-inventory.js +4 -928
  90. package/package.json +2 -2
@@ -1,10 +1,29 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { EDITOR_PLUGIN_SLOT_IDS, REST_RESOURCE_METHOD_IDS, REST_RESOURCE_NAMESPACE_PATTERN, resolveEditorPluginSlotAlias, } from "./cli-add-shared.js";
4
- import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_ABILITY_EDITOR_ASSET, WORKSPACE_ABILITY_EDITOR_SCRIPT, WORKSPACE_ABILITY_GLOB, WORKSPACE_ADMIN_VIEW_ASSET, WORKSPACE_ADMIN_VIEW_GLOB, WORKSPACE_ADMIN_VIEW_SCRIPT, WORKSPACE_ADMIN_VIEW_STYLE, WORKSPACE_AI_FEATURE_GLOB, WORKSPACE_EDITOR_PLUGIN_EDITOR_ASSET, WORKSPACE_EDITOR_PLUGIN_EDITOR_SCRIPT, WORKSPACE_EDITOR_PLUGIN_EDITOR_STYLE, WORKSPACE_REST_RESOURCE_GLOB, } from "./cli-doctor-workspace-shared.js";
3
+ import { EDITOR_PLUGIN_SLOT_IDS, MANUAL_REST_CONTRACT_AUTH_IDS, MANUAL_REST_CONTRACT_HTTP_METHOD_IDS, REST_RESOURCE_METHOD_IDS, REST_RESOURCE_NAMESPACE_PATTERN, assertValidPostMetaPostType, isGeneratedRestResourceRoutePatternCompatible, resolveEditorPluginSlotAlias, } from "./cli-add-shared.js";
4
+ import { hasAdminViewManualSettingsRouteParameters, isAdminViewManualSettingsRestResource, } from "./cli-add-workspace-admin-view-types.js";
5
+ import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_ABILITY_EDITOR_ASSET, WORKSPACE_ABILITY_EDITOR_SCRIPT, WORKSPACE_ABILITY_GLOB, WORKSPACE_ADMIN_VIEW_ASSET, WORKSPACE_ADMIN_VIEW_GLOB, WORKSPACE_ADMIN_VIEW_SCRIPT, WORKSPACE_ADMIN_VIEW_STYLE, WORKSPACE_AI_FEATURE_GLOB, WORKSPACE_EDITOR_PLUGIN_EDITOR_ASSET, WORKSPACE_EDITOR_PLUGIN_EDITOR_SCRIPT, WORKSPACE_EDITOR_PLUGIN_EDITOR_STYLE, WORKSPACE_POST_META_GLOB, WORKSPACE_REST_RESOURCE_GLOB, } from "./cli-doctor-workspace-shared.js";
5
6
  import { escapeRegex } from "./php-utils.js";
7
+ function isManualRestResource(restResource) {
8
+ return restResource.mode === "manual";
9
+ }
6
10
  function getWorkspaceRestResourceRequiredFiles(restResource) {
7
11
  const schemaNames = new Set();
12
+ if (isManualRestResource(restResource)) {
13
+ schemaNames.add("query");
14
+ if (restResource.bodyTypeName) {
15
+ schemaNames.add("request");
16
+ }
17
+ schemaNames.add("response");
18
+ return Array.from(new Set([
19
+ restResource.apiFile,
20
+ ...Array.from(schemaNames, (schemaName) => path.join(path.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
21
+ restResource.clientFile,
22
+ restResource.openApiFile,
23
+ restResource.typesFile,
24
+ restResource.validatorsFile,
25
+ ]));
26
+ }
8
27
  if (restResource.methods.includes("list")) {
9
28
  schemaNames.add("list-query");
10
29
  schemaNames.add("list-response");
@@ -30,20 +49,44 @@ function getWorkspaceRestResourceRequiredFiles(restResource) {
30
49
  restResource.apiFile,
31
50
  ...Array.from(schemaNames, (schemaName) => path.join(path.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
32
51
  restResource.clientFile,
33
- restResource.dataFile,
52
+ ...(restResource.dataFile ? [restResource.dataFile] : []),
34
53
  restResource.openApiFile,
35
- restResource.phpFile,
54
+ ...(restResource.phpFile ? [restResource.phpFile] : []),
36
55
  restResource.typesFile,
37
56
  restResource.validatorsFile,
38
57
  ]));
39
58
  }
40
59
  function checkWorkspaceRestResourceConfig(restResource) {
41
60
  const hasNamespace = REST_RESOURCE_NAMESPACE_PATTERN.test(restResource.namespace);
61
+ if (isManualRestResource(restResource)) {
62
+ const hasAuth = restResource.auth == null ||
63
+ MANUAL_REST_CONTRACT_AUTH_IDS.includes(restResource.auth);
64
+ const hasMethod = typeof restResource.method === "string" &&
65
+ MANUAL_REST_CONTRACT_HTTP_METHOD_IDS.includes(restResource.method);
66
+ const hasPathPattern = typeof restResource.pathPattern === "string" &&
67
+ restResource.pathPattern.startsWith("/") &&
68
+ restResource.pathPattern.length > 1;
69
+ return createDoctorCheck(`REST resource config ${restResource.slug}`, hasNamespace && hasAuth && hasMethod && hasPathPattern ? "pass" : "fail", hasNamespace && hasAuth && hasMethod && hasPathPattern
70
+ ? `Manual REST contract ${restResource.method} /${restResource.namespace}${restResource.pathPattern}`
71
+ : "Manual REST contract namespace, auth, method, or path pattern is invalid");
72
+ }
42
73
  const hasMethods = restResource.methods.length > 0 &&
43
74
  restResource.methods.every((method) => REST_RESOURCE_METHOD_IDS.includes(method));
44
- return createDoctorCheck(`REST resource config ${restResource.slug}`, hasNamespace && hasMethods ? "pass" : "fail", hasNamespace && hasMethods
75
+ const hasGeneratedFiles = typeof restResource.dataFile === "string" &&
76
+ restResource.dataFile.length > 0 &&
77
+ typeof restResource.phpFile === "string" &&
78
+ restResource.phpFile.length > 0;
79
+ const hasRoutePattern = restResource.routePattern == null ||
80
+ (typeof restResource.routePattern === "string" &&
81
+ restResource.routePattern.startsWith("/") &&
82
+ restResource.routePattern.length > 1 &&
83
+ !/\s/u.test(restResource.routePattern) &&
84
+ isGeneratedRestResourceRoutePatternCompatible(restResource.routePattern));
85
+ return createDoctorCheck(`REST resource config ${restResource.slug}`, hasNamespace && hasMethods && hasGeneratedFiles && hasRoutePattern
86
+ ? "pass"
87
+ : "fail", hasNamespace && hasMethods && hasGeneratedFiles && hasRoutePattern
45
88
  ? `REST resource namespace ${restResource.namespace} with methods ${restResource.methods.join(", ")}`
46
- : "REST resource namespace or methods are invalid");
89
+ : "REST resource namespace, methods, dataFile, phpFile, or routePattern are invalid");
47
90
  }
48
91
  function checkWorkspaceRestResourceBootstrap(projectDir, packageName, phpPrefix) {
49
92
  const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
@@ -59,6 +102,60 @@ function checkWorkspaceRestResourceBootstrap(projectDir, packageName, phpPrefix)
59
102
  ? "REST resource PHP loader hook is present"
60
103
  : "Missing REST resource PHP require glob or init hook");
61
104
  }
105
+ function getWorkspacePostMetaRequiredFiles(postMeta) {
106
+ return Array.from(new Set([
107
+ postMeta.phpFile,
108
+ postMeta.schemaFile,
109
+ postMeta.typesFile,
110
+ ]));
111
+ }
112
+ function checkWorkspacePostMetaConfig(postMeta) {
113
+ let hasPostType = false;
114
+ try {
115
+ hasPostType = assertValidPostMetaPostType(postMeta.postType) === postMeta.postType;
116
+ }
117
+ catch {
118
+ hasPostType = false;
119
+ }
120
+ const hasMetaKey = typeof postMeta.metaKey === "string" &&
121
+ postMeta.metaKey.trim().length > 0 &&
122
+ !/\s/u.test(postMeta.metaKey);
123
+ const hasRestExposure = typeof postMeta.showInRest === "boolean";
124
+ return createDoctorCheck(`Post meta config ${postMeta.slug}`, hasPostType && hasMetaKey && hasRestExposure ? "pass" : "fail", hasPostType && hasMetaKey && hasRestExposure
125
+ ? `Post meta ${postMeta.metaKey} targets ${postMeta.postType}`
126
+ : "Post meta postType, metaKey, or showInRest configuration is invalid");
127
+ }
128
+ function checkWorkspacePostMetaBootstrap(projectDir, packageName, phpPrefix) {
129
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
130
+ if (!fs.existsSync(bootstrapPath)) {
131
+ return createDoctorCheck("Post meta bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
132
+ }
133
+ const source = fs.readFileSync(bootstrapPath, "utf8");
134
+ const registerFunctionName = `${phpPrefix}_register_post_meta_contracts`;
135
+ const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
136
+ const hasServerGlob = source.includes(WORKSPACE_POST_META_GLOB);
137
+ const hasRegisterHook = source.includes(registerHook);
138
+ return createDoctorCheck("Post meta bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook
139
+ ? "Post meta PHP loader hook is present"
140
+ : "Missing post meta PHP require glob or init hook");
141
+ }
142
+ function checkWorkspacePostMetaPhp(projectDir, postMeta) {
143
+ const phpPath = path.join(projectDir, postMeta.phpFile);
144
+ if (!fs.existsSync(phpPath)) {
145
+ return createDoctorCheck(`Post meta PHP ${postMeta.slug}`, "fail", `Missing ${postMeta.phpFile}`);
146
+ }
147
+ const source = fs.readFileSync(phpPath, "utf8");
148
+ const hasRegisterPostMeta = source.includes("register_post_meta");
149
+ const hasPostType = source.includes(postMeta.postType);
150
+ const hasMetaKey = source.includes(postMeta.metaKey);
151
+ const hasSchemaFile = source.includes(postMeta.schemaFile);
152
+ const hasRestExposure = source.includes("'show_in_rest'");
153
+ return createDoctorCheck(`Post meta PHP ${postMeta.slug}`, hasRegisterPostMeta && hasPostType && hasMetaKey && hasSchemaFile && hasRestExposure
154
+ ? "pass"
155
+ : "fail", hasRegisterPostMeta && hasPostType && hasMetaKey && hasSchemaFile && hasRestExposure
156
+ ? "Post meta registration, schema path, and REST exposure flag are wired"
157
+ : "Missing register_post_meta, post type, meta key, schema path, or show_in_rest wiring");
158
+ }
62
159
  function getWorkspaceAbilityRequiredFiles(ability) {
63
160
  return Array.from(new Set([
64
161
  ability.clientFile,
@@ -259,10 +356,23 @@ function checkWorkspaceAdminViewConfig(adminView, inventory) {
259
356
  const restResource = restResourceSlug
260
357
  ? inventory.restResources.find((entry) => entry.slug === restResourceSlug)
261
358
  : undefined;
262
- const isValid = Boolean(restResource?.methods.includes("list")) || Boolean(coreDataSourceMatch);
359
+ const isListCapableRestResource = Boolean(restResource?.methods.includes("list"));
360
+ const isManualSettingsRestResource = isAdminViewManualSettingsRestResource(restResource);
361
+ const hasManualSettingsRouteParameters = isManualSettingsRestResource &&
362
+ hasAdminViewManualSettingsRouteParameters(restResource);
363
+ const isValid = isListCapableRestResource ||
364
+ (isManualSettingsRestResource && !hasManualSettingsRouteParameters) ||
365
+ Boolean(coreDataSourceMatch);
366
+ const failDetail = hasManualSettingsRouteParameters
367
+ ? `Admin view source ${source} uses route parameters or regex groups and cannot scaffold a singleton settings form`
368
+ : "Admin view source must use rest-resource:<slug> with a list-capable REST resource, a manual settings contract with a body type, or core-data:<postType|taxonomy>/<name>";
263
369
  return createDoctorCheck(`Admin view config ${adminView.slug}`, isValid ? "pass" : "fail", isValid
264
- ? `Admin view source ${source} is list-capable`
265
- : "Admin view source must use rest-resource:<slug> with a list-capable REST resource or core-data:<postType|taxonomy>/<name>");
370
+ ? `Admin view source ${source} is ${isManualSettingsRestResource
371
+ ? "settings-form capable"
372
+ : coreDataSourceMatch
373
+ ? "core-data capable"
374
+ : "list-capable"}`
375
+ : failDetail);
266
376
  }
267
377
  function checkWorkspaceAdminViewBootstrap(projectDir, packageName, phpPrefix) {
268
378
  const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
@@ -335,13 +445,21 @@ function checkWorkspaceAdminViewPhp(projectDir, adminView) {
335
445
  */
336
446
  export function getWorkspaceFeatureDoctorChecks(workspace, inventory) {
337
447
  const checks = [];
338
- if (inventory.restResources.length > 0) {
448
+ if (inventory.restResources.some((restResource) => !isManualRestResource(restResource))) {
339
449
  checks.push(checkWorkspaceRestResourceBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
340
450
  }
341
451
  for (const restResource of inventory.restResources) {
342
452
  checks.push(checkWorkspaceRestResourceConfig(restResource));
343
453
  checks.push(checkExistingFiles(workspace.projectDir, `REST resource ${restResource.slug}`, getWorkspaceRestResourceRequiredFiles(restResource)));
344
454
  }
455
+ if (inventory.postMeta.length > 0) {
456
+ checks.push(checkWorkspacePostMetaBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
457
+ }
458
+ for (const postMeta of inventory.postMeta) {
459
+ checks.push(checkWorkspacePostMetaConfig(postMeta));
460
+ checks.push(checkExistingFiles(workspace.projectDir, `Post meta ${postMeta.slug}`, getWorkspacePostMetaRequiredFiles(postMeta)));
461
+ checks.push(checkWorkspacePostMetaPhp(workspace.projectDir, postMeta));
462
+ }
345
463
  if (inventory.abilities.length > 0) {
346
464
  checks.push(checkWorkspaceAbilityBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
347
465
  checks.push(checkWorkspaceAbilityIndex(workspace.projectDir, inventory.abilities));
@@ -1,18 +1,40 @@
1
1
  import type { DoctorCheck } from "./cli-doctor.js";
2
2
  import type { WorkspacePackageJson, WorkspaceProject } from "./workspace-project.js";
3
+ /**
4
+ * Snapshot of package-level filesystem doctor inputs prepared asynchronously.
5
+ */
6
+ export interface WorkspacePackageDoctorSnapshot {
7
+ /** Whether the expected workspace bootstrap PHP file exists. */
8
+ bootstrapExists: boolean;
9
+ /** Relative path to the expected workspace bootstrap PHP file. */
10
+ bootstrapRelativePath: string;
11
+ /** Whether the migration config file exists. */
12
+ migrationConfigExists: boolean;
13
+ /** Relative path to the migration config file. */
14
+ migrationConfigRelativePath: string;
15
+ }
16
+ /**
17
+ * Prepare package-level workspace doctor inputs without blocking the event loop.
18
+ *
19
+ * @param workspace Resolved workspace metadata and filesystem paths.
20
+ * @param packageJson Parsed workspace package manifest.
21
+ * @returns Snapshot values consumed by synchronous doctor row mappers.
22
+ */
23
+ export declare function prepareWorkspacePackageDoctorSnapshot(workspace: WorkspaceProject, packageJson: WorkspacePackageJson): Promise<WorkspacePackageDoctorSnapshot>;
3
24
  /**
4
25
  * Validate the package metadata that makes a project an official workspace.
5
26
  *
6
27
  * @param workspace Resolved workspace metadata and filesystem paths.
7
28
  * @param packageJson Parsed workspace package manifest.
29
+ * @param snapshot Async filesystem snapshot for package-level doctor inputs.
8
30
  * @returns A `DoctorCheck` describing whether package metadata matches the workspace contract.
9
31
  */
10
- export declare function getWorkspacePackageMetadataCheck(workspace: WorkspaceProject, packageJson: WorkspacePackageJson): DoctorCheck;
32
+ export declare function getWorkspacePackageMetadataCheck(workspace: WorkspaceProject, packageJson: WorkspacePackageJson, snapshot: WorkspacePackageDoctorSnapshot): DoctorCheck;
11
33
  /**
12
34
  * Report whether a workspace configured for migrations exposes the expected doctor inputs.
13
35
  *
14
- * @param workspace Resolved workspace metadata and filesystem paths.
15
36
  * @param packageJson Parsed workspace package manifest.
37
+ * @param snapshot Async filesystem snapshot for package-level doctor inputs.
16
38
  * @returns A migration hint row when the workspace uses migrations, otherwise `null`.
17
39
  */
18
- export declare function getMigrationWorkspaceHintCheck(workspace: WorkspaceProject, packageJson: WorkspacePackageJson): DoctorCheck | null;
40
+ export declare function getMigrationWorkspaceHintCheck(packageJson: WorkspacePackageJson, snapshot: WorkspacePackageDoctorSnapshot): DoctorCheck | null;
@@ -1,18 +1,42 @@
1
- import fs from "node:fs";
2
1
  import path from "node:path";
3
2
  import { createDoctorCheck, getWorkspaceBootstrapRelativePath, } from "./cli-doctor-workspace-shared.js";
3
+ import { pathExists } from "./fs-async.js";
4
4
  import { WORKSPACE_TEMPLATE_PACKAGE } from "./workspace-project.js";
5
+ /**
6
+ * Prepare package-level workspace doctor inputs without blocking the event loop.
7
+ *
8
+ * @param workspace Resolved workspace metadata and filesystem paths.
9
+ * @param packageJson Parsed workspace package manifest.
10
+ * @returns Snapshot values consumed by synchronous doctor row mappers.
11
+ */
12
+ export async function prepareWorkspacePackageDoctorSnapshot(workspace, packageJson) {
13
+ const packageName = packageJson.name;
14
+ const bootstrapRelativePath = getWorkspaceBootstrapRelativePath(typeof packageName === "string" && packageName.length > 0
15
+ ? packageName
16
+ : workspace.packageName);
17
+ const migrationConfigRelativePath = path.join("src", "migrations", "config.ts");
18
+ const [bootstrapExists, migrationConfigExists] = await Promise.all([
19
+ pathExists(path.join(workspace.projectDir, bootstrapRelativePath)),
20
+ pathExists(path.join(workspace.projectDir, migrationConfigRelativePath)),
21
+ ]);
22
+ return {
23
+ bootstrapExists,
24
+ bootstrapRelativePath,
25
+ migrationConfigExists,
26
+ migrationConfigRelativePath,
27
+ };
28
+ }
5
29
  /**
6
30
  * Validate the package metadata that makes a project an official workspace.
7
31
  *
8
32
  * @param workspace Resolved workspace metadata and filesystem paths.
9
33
  * @param packageJson Parsed workspace package manifest.
34
+ * @param snapshot Async filesystem snapshot for package-level doctor inputs.
10
35
  * @returns A `DoctorCheck` describing whether package metadata matches the workspace contract.
11
36
  */
12
- export function getWorkspacePackageMetadataCheck(workspace, packageJson) {
37
+ export function getWorkspacePackageMetadataCheck(workspace, packageJson, snapshot) {
13
38
  const issues = [];
14
39
  const packageName = packageJson.name;
15
- const bootstrapRelativePath = getWorkspaceBootstrapRelativePath(typeof packageName === "string" && packageName.length > 0 ? packageName : workspace.packageName);
16
40
  const wpTypia = packageJson.wpTypia;
17
41
  if (typeof packageName !== "string" || packageName.length === 0) {
18
42
  issues.push("package.json must define a string name for workspace bootstrap resolution");
@@ -32,28 +56,26 @@ export function getWorkspacePackageMetadataCheck(workspace, packageJson) {
32
56
  if (wpTypia?.phpPrefix !== workspace.workspace.phpPrefix) {
33
57
  issues.push(`wpTypia.phpPrefix must equal "${workspace.workspace.phpPrefix}"`);
34
58
  }
35
- if (!fs.existsSync(path.join(workspace.projectDir, bootstrapRelativePath))) {
36
- issues.push(`Missing bootstrap file ${bootstrapRelativePath}`);
59
+ if (!snapshot.bootstrapExists) {
60
+ issues.push(`Missing bootstrap file ${snapshot.bootstrapRelativePath}`);
37
61
  }
38
62
  return createDoctorCheck("Workspace package metadata", issues.length === 0 ? "pass" : "fail", issues.length === 0
39
- ? `package.json metadata aligns with ${workspace.packageName} and ${bootstrapRelativePath}`
63
+ ? `package.json metadata aligns with ${workspace.packageName} and ${snapshot.bootstrapRelativePath}`
40
64
  : issues.join("; "));
41
65
  }
42
66
  /**
43
67
  * Report whether a workspace configured for migrations exposes the expected doctor inputs.
44
68
  *
45
- * @param workspace Resolved workspace metadata and filesystem paths.
46
69
  * @param packageJson Parsed workspace package manifest.
70
+ * @param snapshot Async filesystem snapshot for package-level doctor inputs.
47
71
  * @returns A migration hint row when the workspace uses migrations, otherwise `null`.
48
72
  */
49
- export function getMigrationWorkspaceHintCheck(workspace, packageJson) {
73
+ export function getMigrationWorkspaceHintCheck(packageJson, snapshot) {
50
74
  const hasMigrationScript = typeof packageJson.scripts?.["migration:doctor"] === "string";
51
- const migrationConfigRelativePath = path.join("src", "migrations", "config.ts");
52
- const hasMigrationConfig = fs.existsSync(path.join(workspace.projectDir, migrationConfigRelativePath));
53
- if (!hasMigrationScript && !hasMigrationConfig) {
75
+ if (!hasMigrationScript && !snapshot.migrationConfigExists) {
54
76
  return null;
55
77
  }
56
- return createDoctorCheck("Migration workspace", hasMigrationConfig ? "pass" : "fail", hasMigrationConfig
78
+ return createDoctorCheck("Migration workspace", snapshot.migrationConfigExists ? "pass" : "fail", snapshot.migrationConfigExists
57
79
  ? "Run `wp-typia migrate doctor --all` for migration target, snapshot, fixture, and generated artifact checks"
58
- : `Missing ${migrationConfigRelativePath} for the configured migration workspace`);
80
+ : `Missing ${snapshot.migrationConfigRelativePath} for the configured migration workspace`);
59
81
  }
@@ -7,6 +7,8 @@ export declare const WORKSPACE_BINDING_EDITOR_SCRIPT = "build/bindings/index.js"
7
7
  export declare const WORKSPACE_BINDING_EDITOR_ASSET = "build/bindings/index.asset.php";
8
8
  /** Glob pattern for generated REST resource PHP entrypoints. */
9
9
  export declare const WORKSPACE_REST_RESOURCE_GLOB = "/inc/rest/*.php";
10
+ /** Glob pattern for generated post-meta PHP entrypoints. */
11
+ export declare const WORKSPACE_POST_META_GLOB = "/inc/post-meta/*.php";
10
12
  /** Glob pattern for generated ability PHP entrypoints. */
11
13
  export declare const WORKSPACE_ABILITY_GLOB = "/inc/abilities/*.php";
12
14
  /** Relative path to the generated ability editor bundle. */
@@ -8,6 +8,8 @@ export const WORKSPACE_BINDING_EDITOR_SCRIPT = "build/bindings/index.js";
8
8
  export const WORKSPACE_BINDING_EDITOR_ASSET = "build/bindings/index.asset.php";
9
9
  /** Glob pattern for generated REST resource PHP entrypoints. */
10
10
  export const WORKSPACE_REST_RESOURCE_GLOB = "/inc/rest/*.php";
11
+ /** Glob pattern for generated post-meta PHP entrypoints. */
12
+ export const WORKSPACE_POST_META_GLOB = "/inc/post-meta/*.php";
11
13
  /** Glob pattern for generated ability PHP entrypoints. */
12
14
  export const WORKSPACE_ABILITY_GLOB = "/inc/abilities/*.php";
13
15
  /** Relative path to the generated ability editor bundle. */
@@ -90,6 +92,9 @@ export function resolveWorkspaceBootstrapPath(projectDir, packageName) {
90
92
  * @returns A passing or failing `DoctorCheck` describing any missing files.
91
93
  */
92
94
  export function checkExistingFiles(projectDir, label, filePaths) {
95
+ // Workspace category collectors remain synchronous pure mappers after the
96
+ // async inventory snapshot is loaded, so these small existence probes stay
97
+ // sync to preserve their current non-Promise APIs and output ordering.
93
98
  const missing = filePaths
94
99
  .filter((filePath) => typeof filePath === "string")
95
100
  .filter((filePath) => !fs.existsSync(path.join(projectDir, filePath)));
@@ -15,4 +15,4 @@ import type { DoctorCheck } from "./cli-doctor.js";
15
15
  * @param cwd Working directory expected to host an official workspace.
16
16
  * @returns Ordered workspace check rows ready for CLI rendering.
17
17
  */
18
- export declare function getWorkspaceDoctorChecks(cwd: string): DoctorCheck[];
18
+ export declare function getWorkspaceDoctorChecks(cwd: string): Promise<DoctorCheck[]>;
@@ -1,9 +1,9 @@
1
1
  import { getWorkspaceBindingDoctorChecks, } from "./cli-doctor-workspace-bindings.js";
2
2
  import { getWorkspaceBlockDoctorChecks, } from "./cli-doctor-workspace-blocks.js";
3
3
  import { getWorkspaceFeatureDoctorChecks, } from "./cli-doctor-workspace-features.js";
4
- import { getMigrationWorkspaceHintCheck, getWorkspacePackageMetadataCheck, } from "./cli-doctor-workspace-package.js";
4
+ import { getMigrationWorkspaceHintCheck, getWorkspacePackageMetadataCheck, prepareWorkspacePackageDoctorSnapshot, } from "./cli-doctor-workspace-package.js";
5
5
  import { createDoctorCheck, createDoctorScopeCheck, } from "./cli-doctor-workspace-shared.js";
6
- import { readWorkspaceInventory } from "./workspace-inventory.js";
6
+ import { readWorkspaceInventoryAsync, } from "./workspace-inventory.js";
7
7
  import { getInvalidWorkspaceProjectReason, parseWorkspacePackageJson, tryResolveWorkspaceProject, } from "./workspace-project.js";
8
8
  function formatWorkspaceInventorySummary(inventory) {
9
9
  return [
@@ -14,6 +14,7 @@ function formatWorkspaceInventorySummary(inventory) {
14
14
  `${inventory.patterns.length} pattern(s)`,
15
15
  `${inventory.bindingSources.length} binding source(s)`,
16
16
  `${inventory.restResources.length} REST resource(s)`,
17
+ `${inventory.postMeta.length} post meta contract(s)`,
17
18
  `${inventory.abilities.length} ability scaffold(s)`,
18
19
  `${inventory.aiFeatures.length} AI feature(s)`,
19
20
  `${inventory.editorPlugins.length} editor plugin(s)`,
@@ -36,11 +37,13 @@ function formatWorkspaceInventorySummary(inventory) {
36
37
  * @param cwd Working directory expected to host an official workspace.
37
38
  * @returns Ordered workspace check rows ready for CLI rendering.
38
39
  */
39
- export function getWorkspaceDoctorChecks(cwd) {
40
+ export async function getWorkspaceDoctorChecks(cwd) {
40
41
  const checks = [];
41
42
  let workspace = null;
42
43
  let invalidWorkspaceReason = null;
43
44
  try {
45
+ // Workspace discovery and package parsing intentionally stay synchronous
46
+ // because they share compatibility helpers with sync add/migration callers.
44
47
  invalidWorkspaceReason = getInvalidWorkspaceProjectReason(cwd);
45
48
  workspace = tryResolveWorkspaceProject(cwd);
46
49
  }
@@ -62,22 +65,27 @@ export function getWorkspaceDoctorChecks(cwd) {
62
65
  checks.push(createDoctorScopeCheck("pass", `Scope: full workspace diagnostics for ${workspace.workspace.namespace}. Environment readiness checks ran and workspace-scoped diagnostics are enabled for the package metadata, inventory, source-tree drift, and any configured migration hint rows below.`));
63
66
  let workspacePackageJson;
64
67
  try {
68
+ // Keep package metadata parsing sync until workspace-project exposes an
69
+ // async companion; the surrounding doctor orchestration already awaits
70
+ // async inventory reads below.
65
71
  workspacePackageJson = parseWorkspacePackageJson(workspace.projectDir);
66
72
  }
67
73
  catch (error) {
68
74
  checks.push(createDoctorCheck("Workspace package metadata", "fail", error instanceof Error ? error.message : String(error)));
69
75
  return checks;
70
76
  }
71
- checks.push(getWorkspacePackageMetadataCheck(workspace, workspacePackageJson));
77
+ const packageDoctorSnapshot = await prepareWorkspacePackageDoctorSnapshot(workspace, workspacePackageJson);
78
+ checks.push(getWorkspacePackageMetadataCheck(workspace, workspacePackageJson, packageDoctorSnapshot));
72
79
  try {
73
- // Doctor checks expose a synchronous API so callers can collect a stable
74
- // snapshot without mixing async inventory reads into check aggregation.
75
- const inventory = readWorkspaceInventory(workspace.projectDir);
80
+ const inventory = await readWorkspaceInventoryAsync(workspace.projectDir);
76
81
  checks.push(createDoctorCheck("Workspace inventory", "pass", formatWorkspaceInventorySummary(inventory)));
82
+ // The highest-impact remaining probes live in block, binding, and feature
83
+ // categories; keep them synchronous until broader path/content snapshots
84
+ // can preserve their current row ordering and diagnostics.
77
85
  checks.push(...getWorkspaceBlockDoctorChecks(workspace, inventory));
78
86
  checks.push(...getWorkspaceBindingDoctorChecks(workspace, inventory));
79
87
  checks.push(...getWorkspaceFeatureDoctorChecks(workspace, inventory));
80
- const migrationWorkspaceCheck = getMigrationWorkspaceHintCheck(workspace, workspacePackageJson);
88
+ const migrationWorkspaceCheck = getMigrationWorkspaceHintCheck(workspacePackageJson, packageDoctorSnapshot);
81
89
  if (migrationWorkspaceCheck) {
82
90
  checks.push(migrationWorkspaceCheck);
83
91
  }
@@ -15,7 +15,7 @@ import { getWorkspaceDoctorChecks } from "./cli-doctor-workspace.js";
15
15
  export async function getDoctorChecks(cwd) {
16
16
  return [
17
17
  ...(await getEnvironmentDoctorChecks(cwd)),
18
- ...getWorkspaceDoctorChecks(cwd),
18
+ ...(await getWorkspaceDoctorChecks(cwd)),
19
19
  ];
20
20
  }
21
21
  /**
@@ -20,12 +20,15 @@ export function formatHelpText() {
20
20
  wp-typia init [project-dir] [--apply] [--package-manager <id>]
21
21
  wp-typia add admin-view <name> [--source <rest-resource:slug|core-data:kind/name>]
22
22
  wp-typia add block <name> [--template <basic|interactivity|persistence|compound>] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--inner-blocks-preset <freeform|ordered|horizontal|locked-structure>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>]
23
+ wp-typia add integration-env <name> [--wp-env] [--service <none|docker-compose>]
23
24
  wp-typia add variation <name> --block <block-slug>
24
25
  wp-typia add style <name> --block <block-slug>
25
26
  wp-typia add transform <name> --from <namespace/block> --to <block-slug|namespace/block-slug>
26
27
  wp-typia add pattern <name>
27
28
  wp-typia add binding-source <name> [--block <block-slug|namespace/block-slug> --attribute <attribute>]
28
29
  wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <method[,method...]>]
30
+ wp-typia add rest-resource <name> --manual [--namespace <vendor/v1>] [--method <GET|POST|PUT|PATCH|DELETE>] [--auth <public|authenticated|public-write-protected>] [--path <route-pattern>] [--query-type <Type>] [--body-type <Type>] [--response-type <Type>]
31
+ wp-typia add post-meta <name> --post-type <post-type> [--type <ExportedTypeName>] [--meta-key <meta-key>] [--hide-from-rest]
29
32
  wp-typia add ability <name>
30
33
  wp-typia add ai-feature <name> [--namespace <vendor/v1>]
31
34
  wp-typia add editor-plugin <name> [--slot <sidebar|document-setting-panel>]
@@ -53,6 +56,8 @@ Notes:
53
56
  Pass \`--source rest-resource:<slug>\` to reuse a list-capable REST resource.
54
57
  Pass \`--source core-data:postType/post\` or \`--source core-data:taxonomy/category\` to bind a WordPress-owned entity collection.
55
58
  Generated admin-view workspaces add \`@wp-typia/dataviews\` and the needed WordPress DataViews packages as opt-in dependencies.
59
+ \`add integration-env\` generates an opt-in local smoke starter under \`scripts/integration-smoke/\`, updates \`.env.example\`, and can add \`@wordpress/env\` plus \`.wp-env.json\` when \`--wp-env\` is passed.
60
+ Pass \`--service docker-compose\` to include a placeholder local service stack that can be adapted to project-specific dependencies.
56
61
  \`query-loop\` is create-only. Use \`wp-typia create <project-dir> --template query-loop\`; \`wp-typia add block\` accepts only basic, interactivity, persistence, and compound families.
57
62
  \`add variation\` uses an existing workspace block from \`scripts/block-config.ts\`.
58
63
  \`add style\` registers a Block Styles option for an existing generated block.
@@ -60,6 +65,8 @@ Notes:
60
65
  \`add pattern\` scaffolds a namespaced PHP pattern shell under \`src/patterns/\`.
61
66
  \`add binding-source\` scaffolds shared PHP and editor registration under \`src/bindings/\`; pass \`--block\` and \`--attribute\` together to declare a bindable generated-block attribute.
62
67
  \`add rest-resource\` scaffolds plugin-level TypeScript REST contracts under \`src/rest/\` and PHP route glue under \`inc/rest/\`.
68
+ \`add rest-resource --manual\` tracks an external REST route with typed schemas, OpenAPI, clients, and drift checks without generating PHP route/controller files.
69
+ \`add post-meta\` scaffolds typed post meta contracts under \`src/post-meta/\`, emits schema artifacts, and wires generated \`register_post_meta()\` helpers under \`inc/post-meta/\`.
63
70
  \`add ability\` scaffolds typed workflow abilities under \`src/abilities/\` and server registration under \`inc/abilities/\`.
64
71
  \`add ai-feature\` scaffolds server-owned AI feature endpoints under \`src/ai-features/\` and PHP route glue under \`inc/ai-features/\`.
65
72
  \`add editor-plugin\` scaffolds a document-level editor extension under \`src/editor-plugins/\`; legacy aliases \`PluginSidebar\` and \`PluginDocumentSettingPanel\` resolve to \`sidebar\` and \`document-setting-panel\`.
@@ -182,6 +182,16 @@ function getSyncScriptEnv() {
182
182
  \treturn env;
183
183
  }
184
184
 
185
+ function getOptionalNodeErrorCode( error: unknown ): string | undefined {
186
+ \treturn typeof error === 'object' && error !== null && 'code' in error
187
+ \t\t? String( ( error as { code: unknown } ).code )
188
+ \t\t: undefined;
189
+ }
190
+
191
+ function isFileNotFoundError( error: unknown ): boolean {
192
+ \treturn getOptionalNodeErrorCode( error ) === 'ENOENT';
193
+ }
194
+
185
195
  function runSyncScript( scriptPath: string, options: SyncCliOptions ) {
186
196
  \tconst args = [ scriptPath ];
187
197
  \tif ( options.check ) {
@@ -196,7 +206,7 @@ function runSyncScript( scriptPath: string, options: SyncCliOptions ) {
196
206
  \t} );
197
207
 
198
208
  \tif ( result.error ) {
199
- \t\tif ( ( result.error as NodeJS.ErrnoException ).code === 'ENOENT' ) {
209
+ \t\tif ( isFileNotFoundError( result.error ) ) {
200
210
  \t\t\tthrow new Error(
201
211
  \t\t\t\t'Unable to resolve \`tsx\` for project sync. Install project dependencies or rerun the command through your package manager.'
202
212
  \t\t\t);
@@ -0,0 +1,14 @@
1
+ interface SyncStandaloneContractArtifactsOptions {
2
+ projectDir: string;
3
+ schemaFile: string;
4
+ sourceTypeName: string;
5
+ typesFile: string;
6
+ }
7
+ /**
8
+ * Generate the JSON Schema artifact for a standalone TypeScript contract.
9
+ *
10
+ * @param options Workspace-relative type/schema paths plus the exported source
11
+ * type name.
12
+ */
13
+ export declare function syncStandaloneContractArtifacts({ projectDir, schemaFile, sourceTypeName, typesFile, }: SyncStandaloneContractArtifactsOptions): Promise<void>;
14
+ export {};
@@ -0,0 +1,15 @@
1
+ import { syncTypeSchemas, } from "@wp-typia/block-runtime/metadata-core";
2
+ /**
3
+ * Generate the JSON Schema artifact for a standalone TypeScript contract.
4
+ *
5
+ * @param options Workspace-relative type/schema paths plus the exported source
6
+ * type name.
7
+ */
8
+ export async function syncStandaloneContractArtifacts({ projectDir, schemaFile, sourceTypeName, typesFile, }) {
9
+ await syncTypeSchemas({
10
+ jsonSchemaFile: schemaFile,
11
+ projectRoot: projectDir,
12
+ sourceTypeName,
13
+ typesFile,
14
+ });
15
+ }
@@ -19,6 +19,13 @@ export declare function readOptionalUtf8File(filePath: string): Promise<string |
19
19
  * @returns The string error code, or an empty string when unavailable.
20
20
  */
21
21
  export declare function getNodeErrorCode(error: unknown): string;
22
+ /**
23
+ * Extract a Node.js error code from an unknown thrown value when available.
24
+ *
25
+ * @param error Unknown error value.
26
+ * @returns The string error code, or `undefined` when unavailable.
27
+ */
28
+ export declare function getOptionalNodeErrorCode(error: unknown): string | undefined;
22
29
  /**
23
30
  * Return whether an unknown error represents a missing filesystem path.
24
31
  *
@@ -38,9 +38,18 @@ export async function readOptionalUtf8File(filePath) {
38
38
  * @returns The string error code, or an empty string when unavailable.
39
39
  */
40
40
  export function getNodeErrorCode(error) {
41
+ return getOptionalNodeErrorCode(error) ?? "";
42
+ }
43
+ /**
44
+ * Extract a Node.js error code from an unknown thrown value when available.
45
+ *
46
+ * @param error Unknown error value.
47
+ * @returns The string error code, or `undefined` when unavailable.
48
+ */
49
+ export function getOptionalNodeErrorCode(error) {
41
50
  return typeof error === "object" && error !== null && "code" in error
42
51
  ? String(error.code)
43
- : "";
52
+ : undefined;
44
53
  }
45
54
  /**
46
55
  * Return whether an unknown error represents a missing filesystem path.
@@ -49,5 +58,5 @@ export function getNodeErrorCode(error) {
49
58
  * @returns `true` when the error has Node.js code `ENOENT`.
50
59
  */
51
60
  export function isFileNotFoundError(error) {
52
- return getNodeErrorCode(error) === "ENOENT";
61
+ return getOptionalNodeErrorCode(error) === "ENOENT";
53
62
  }
@@ -38,5 +38,5 @@ export { TEMPLATE_IDS, TEMPLATE_REGISTRY, getTemplateById, getTemplateSelectOpti
38
38
  export { EXTERNAL_TEMPLATE_CACHE_TTL_DAYS_ENV, pruneExternalTemplateCache, } from "./template-source-cache.js";
39
39
  export type { ExternalTemplateCachePruneOptions, ExternalTemplateCachePruneResult, } from "./template-source-cache.js";
40
40
  export { STALE_TEMP_ROOT_MAX_AGE_MS, WP_TYPIA_TEMP_ROOT_PREFIX, cleanupManagedTempRoot, cleanupStaleTempRoots, createManagedTempRoot, getTrackedTempRoots, } from "./temp-roots.js";
41
- export { createReadlinePrompt, createCliCommandError, createCliDiagnosticCodeError, CliDiagnosticError, CLI_DIAGNOSTIC_CODES, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, HOOKED_BLOCK_POSITION_IDS, EDITOR_PLUGIN_SLOT_IDS, isCliDiagnosticError, runAddAdminViewCommand, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
41
+ export { createReadlinePrompt, createCliCommandError, createCliDiagnosticCodeError, CliDiagnosticError, CLI_DIAGNOSTIC_CODES, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, HOOKED_BLOCK_POSITION_IDS, EDITOR_PLUGIN_SLOT_IDS, isCliDiagnosticError, runAddAdminViewCommand, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddPostMetaCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
42
42
  export type { CliDiagnosticCode, CliDiagnosticCodeError, CliDiagnosticMessage, DoctorCheck, EditorPluginSlotId, HookedBlockPositionId, ReadlinePrompt, WorkspaceBlockSelectOption, } from "./cli-core.js";
@@ -29,4 +29,4 @@ export { clearPackageVersionsCache, getPackageVersions, invalidatePackageVersion
29
29
  export { TEMPLATE_IDS, TEMPLATE_REGISTRY, getTemplateById, getTemplateSelectOptions, listTemplates, } from "./template-registry.js";
30
30
  export { EXTERNAL_TEMPLATE_CACHE_TTL_DAYS_ENV, pruneExternalTemplateCache, } from "./template-source-cache.js";
31
31
  export { STALE_TEMP_ROOT_MAX_AGE_MS, WP_TYPIA_TEMP_ROOT_PREFIX, cleanupManagedTempRoot, cleanupStaleTempRoots, createManagedTempRoot, getTrackedTempRoots, } from "./temp-roots.js";
32
- export { createReadlinePrompt, createCliCommandError, createCliDiagnosticCodeError, CliDiagnosticError, CLI_DIAGNOSTIC_CODES, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, HOOKED_BLOCK_POSITION_IDS, EDITOR_PLUGIN_SLOT_IDS, isCliDiagnosticError, runAddAdminViewCommand, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
32
+ export { createReadlinePrompt, createCliCommandError, createCliDiagnosticCodeError, CliDiagnosticError, CLI_DIAGNOSTIC_CODES, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, HOOKED_BLOCK_POSITION_IDS, EDITOR_PLUGIN_SLOT_IDS, isCliDiagnosticError, runAddAdminViewCommand, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddPostMetaCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
@@ -59,6 +59,9 @@ export function verifyProjectMigrations(projectDir, { all = false, fromMigration
59
59
  return { verifiedVersions: targetVersions };
60
60
  }
61
61
  function recordWorkspaceMigrationTargetAlignment(projectDir, state, recordCheck) {
62
+ // `migrate doctor` is still a synchronous maintenance command: it shares
63
+ // sync migration project loading, generated artifact comparisons, and
64
+ // execFileSync verification with the rest of migration maintenance.
62
65
  let invalidWorkspaceReason = null;
63
66
  let workspace;
64
67
  try {
@@ -217,6 +217,10 @@ export function renderPhpMigrationRegistryFile(state, entries) {
217
217
  return `<?php
218
218
  declare(strict_types=1);
219
219
 
220
+ if ( ! defined( 'ABSPATH' ) ) {
221
+ \texit;
222
+ }
223
+
220
224
  /**
221
225
  * Generated from advanced migration snapshots. Do not edit manually.
222
226
  */