@wp-typia/project-tools 0.23.0 → 0.24.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 (228) hide show
  1. package/dist/runtime/ai-feature-artifacts.js +4 -1
  2. package/dist/runtime/block-generator-service-spec.js +2 -1
  3. package/dist/runtime/built-in-block-non-ts-basic-artifacts.d.ts +9 -0
  4. package/dist/runtime/built-in-block-non-ts-basic-artifacts.js +84 -0
  5. package/dist/runtime/built-in-block-non-ts-compound-artifacts.d.ts +9 -0
  6. package/dist/runtime/built-in-block-non-ts-compound-artifacts.js +36 -0
  7. package/dist/runtime/built-in-block-non-ts-compound-templates.d.ts +23 -0
  8. package/dist/runtime/built-in-block-non-ts-compound-templates.js +453 -0
  9. package/dist/runtime/built-in-block-non-ts-family-artifacts.d.ts +8 -26
  10. package/dist/runtime/built-in-block-non-ts-family-artifacts.js +8 -1034
  11. package/dist/runtime/built-in-block-non-ts-interactivity-artifacts.d.ts +9 -0
  12. package/dist/runtime/built-in-block-non-ts-interactivity-artifacts.js +83 -0
  13. package/dist/runtime/built-in-block-non-ts-persistence-artifacts.d.ts +9 -0
  14. package/dist/runtime/built-in-block-non-ts-persistence-artifacts.js +33 -0
  15. package/dist/runtime/built-in-block-non-ts-persistence-templates.d.ts +23 -0
  16. package/dist/runtime/built-in-block-non-ts-persistence-templates.js +395 -0
  17. package/dist/runtime/cli-add-block-json.js +5 -1
  18. package/dist/runtime/cli-add-collision.js +8 -0
  19. package/dist/runtime/cli-add-help.js +14 -10
  20. package/dist/runtime/cli-add-kind-ids.d.ts +1 -1
  21. package/dist/runtime/cli-add-kind-ids.js +1 -0
  22. package/dist/runtime/cli-add-types.d.ts +45 -6
  23. package/dist/runtime/cli-add-types.js +2 -0
  24. package/dist/runtime/cli-add-validation.d.ts +7 -0
  25. package/dist/runtime/cli-add-validation.js +9 -0
  26. package/dist/runtime/cli-add-workspace-ability-anchors.d.ts +24 -0
  27. package/dist/runtime/cli-add-workspace-ability-anchors.js +294 -0
  28. package/dist/runtime/cli-add-workspace-ability-registry.d.ts +10 -0
  29. package/dist/runtime/cli-add-workspace-ability-registry.js +51 -0
  30. package/dist/runtime/cli-add-workspace-ability-scaffold.d.ts +1 -1
  31. package/dist/runtime/cli-add-workspace-ability-scaffold.js +5 -308
  32. package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +6 -2
  33. package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.d.ts +34 -0
  34. package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.js +483 -0
  35. package/dist/runtime/cli-add-workspace-admin-view-templates-default.d.ts +30 -0
  36. package/dist/runtime/cli-add-workspace-admin-view-templates-default.js +310 -0
  37. package/dist/runtime/cli-add-workspace-admin-view-templates-rest.d.ts +25 -0
  38. package/dist/runtime/cli-add-workspace-admin-view-templates-rest.js +124 -0
  39. package/dist/runtime/cli-add-workspace-admin-view-templates-settings.d.ts +34 -0
  40. package/dist/runtime/cli-add-workspace-admin-view-templates-settings.js +370 -0
  41. package/dist/runtime/cli-add-workspace-admin-view-templates-shared.d.ts +49 -0
  42. package/dist/runtime/cli-add-workspace-admin-view-templates-shared.js +259 -0
  43. package/dist/runtime/cli-add-workspace-admin-view-templates.d.ts +18 -27
  44. package/dist/runtime/cli-add-workspace-admin-view-templates.js +30 -1326
  45. package/dist/runtime/cli-add-workspace-ai-anchors.d.ts +4 -4
  46. package/dist/runtime/cli-add-workspace-ai-anchors.js +8 -233
  47. package/dist/runtime/cli-add-workspace-ai-scaffold.js +4 -2
  48. package/dist/runtime/cli-add-workspace-ai-source-emitters.d.ts +1 -4
  49. package/dist/runtime/cli-add-workspace-ai-source-emitters.js +1 -129
  50. package/dist/runtime/cli-add-workspace-ai-sync-rest-anchors.d.ts +5 -0
  51. package/dist/runtime/cli-add-workspace-ai-sync-rest-anchors.js +236 -0
  52. package/dist/runtime/cli-add-workspace-ai-sync-script-source.d.ts +4 -0
  53. package/dist/runtime/cli-add-workspace-ai-sync-script-source.js +145 -0
  54. package/dist/runtime/cli-add-workspace-assets.d.ts +6 -63
  55. package/dist/runtime/cli-add-workspace-assets.js +6 -950
  56. package/dist/runtime/cli-add-workspace-binding-source-anchors.d.ts +23 -0
  57. package/dist/runtime/cli-add-workspace-binding-source-anchors.js +112 -0
  58. package/dist/runtime/cli-add-workspace-binding-source-source-emitters.d.ts +33 -0
  59. package/dist/runtime/cli-add-workspace-binding-source-source-emitters.js +436 -0
  60. package/dist/runtime/cli-add-workspace-binding-source-types.d.ts +20 -0
  61. package/dist/runtime/cli-add-workspace-binding-source-types.js +1 -0
  62. package/dist/runtime/cli-add-workspace-binding-source.d.ts +40 -0
  63. package/dist/runtime/cli-add-workspace-binding-source.js +275 -0
  64. package/dist/runtime/cli-add-workspace-block-style.d.ts +22 -0
  65. package/dist/runtime/cli-add-workspace-block-style.js +148 -0
  66. package/dist/runtime/cli-add-workspace-block-transform.d.ts +32 -0
  67. package/dist/runtime/cli-add-workspace-block-transform.js +197 -0
  68. package/dist/runtime/cli-add-workspace-contract.js +1 -1
  69. package/dist/runtime/cli-add-workspace-core-variation.d.ts +20 -0
  70. package/dist/runtime/cli-add-workspace-core-variation.js +322 -0
  71. package/dist/runtime/cli-add-workspace-editor-plugin-anchors.d.ts +37 -0
  72. package/dist/runtime/cli-add-workspace-editor-plugin-anchors.js +206 -0
  73. package/dist/runtime/cli-add-workspace-editor-plugin-source-emitters.d.ts +47 -0
  74. package/dist/runtime/cli-add-workspace-editor-plugin-source-emitters.js +219 -0
  75. package/dist/runtime/cli-add-workspace-editor-plugin.d.ts +22 -0
  76. package/dist/runtime/cli-add-workspace-editor-plugin.js +78 -0
  77. package/dist/runtime/cli-add-workspace-hooked-block.d.ts +23 -0
  78. package/dist/runtime/cli-add-workspace-hooked-block.js +57 -0
  79. package/dist/runtime/cli-add-workspace-integration-env-files.d.ts +33 -0
  80. package/dist/runtime/cli-add-workspace-integration-env-files.js +65 -0
  81. package/dist/runtime/cli-add-workspace-integration-env-package-json.d.ts +38 -0
  82. package/dist/runtime/cli-add-workspace-integration-env-package-json.js +122 -0
  83. package/dist/runtime/cli-add-workspace-integration-env-source-emitters.d.ts +44 -0
  84. package/dist/runtime/cli-add-workspace-integration-env-source-emitters.js +262 -0
  85. package/dist/runtime/cli-add-workspace-integration-env.d.ts +3 -1
  86. package/dist/runtime/cli-add-workspace-integration-env.js +10 -313
  87. package/dist/runtime/cli-add-workspace-pattern-anchors.d.ts +10 -0
  88. package/dist/runtime/cli-add-workspace-pattern-anchors.js +95 -0
  89. package/dist/runtime/cli-add-workspace-pattern-options.d.ts +20 -0
  90. package/dist/runtime/cli-add-workspace-pattern-options.js +113 -0
  91. package/dist/runtime/cli-add-workspace-pattern-source-emitters.d.ts +20 -0
  92. package/dist/runtime/cli-add-workspace-pattern-source-emitters.js +57 -0
  93. package/dist/runtime/cli-add-workspace-pattern.d.ts +42 -0
  94. package/dist/runtime/cli-add-workspace-pattern.js +99 -0
  95. package/dist/runtime/cli-add-workspace-post-meta.js +1 -1
  96. package/dist/runtime/cli-add-workspace-registration-hooks.d.ts +50 -0
  97. package/dist/runtime/cli-add-workspace-registration-hooks.js +162 -0
  98. package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +9 -4
  99. package/dist/runtime/cli-add-workspace-rest-anchors.js +9 -428
  100. package/dist/runtime/cli-add-workspace-rest-bootstrap-anchors.d.ts +17 -0
  101. package/dist/runtime/cli-add-workspace-rest-bootstrap-anchors.js +108 -0
  102. package/dist/runtime/cli-add-workspace-rest-contract-sync-anchors.d.ts +9 -0
  103. package/dist/runtime/cli-add-workspace-rest-contract-sync-anchors.js +142 -0
  104. package/dist/runtime/cli-add-workspace-rest-generated-source-emitters.d.ts +51 -0
  105. package/dist/runtime/cli-add-workspace-rest-generated-source-emitters.js +415 -0
  106. package/dist/runtime/cli-add-workspace-rest-generated.d.ts +9 -0
  107. package/dist/runtime/cli-add-workspace-rest-generated.js +160 -0
  108. package/dist/runtime/cli-add-workspace-rest-manual-source-emitters.d.ts +80 -0
  109. package/dist/runtime/cli-add-workspace-rest-manual-source-emitters.js +238 -0
  110. package/dist/runtime/cli-add-workspace-rest-manual.d.ts +8 -0
  111. package/dist/runtime/cli-add-workspace-rest-manual.js +266 -0
  112. package/dist/runtime/cli-add-workspace-rest-php-templates.d.ts +18 -0
  113. package/dist/runtime/cli-add-workspace-rest-php-templates.js +359 -0
  114. package/dist/runtime/cli-add-workspace-rest-resource-php-routing-template.d.ts +33 -0
  115. package/dist/runtime/cli-add-workspace-rest-resource-php-routing-template.js +145 -0
  116. package/dist/runtime/cli-add-workspace-rest-resource-sync-anchors.d.ts +9 -0
  117. package/dist/runtime/cli-add-workspace-rest-resource-sync-anchors.js +162 -0
  118. package/dist/runtime/cli-add-workspace-rest-schema-helper-php-template.d.ts +7 -0
  119. package/dist/runtime/cli-add-workspace-rest-schema-helper-php-template.js +193 -0
  120. package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +5 -91
  121. package/dist/runtime/cli-add-workspace-rest-source-emitters.js +5 -642
  122. package/dist/runtime/cli-add-workspace-rest-source-utils.d.ts +17 -0
  123. package/dist/runtime/cli-add-workspace-rest-source-utils.js +50 -0
  124. package/dist/runtime/cli-add-workspace-rest-sync-script-shared.d.ts +56 -0
  125. package/dist/runtime/cli-add-workspace-rest-sync-script-shared.js +122 -0
  126. package/dist/runtime/cli-add-workspace-rest-types.d.ts +108 -0
  127. package/dist/runtime/cli-add-workspace-rest-types.js +1 -0
  128. package/dist/runtime/cli-add-workspace-rest.d.ts +3 -20
  129. package/dist/runtime/cli-add-workspace-rest.js +33 -788
  130. package/dist/runtime/cli-add-workspace-variation.d.ts +22 -0
  131. package/dist/runtime/cli-add-workspace-variation.js +162 -0
  132. package/dist/runtime/cli-add-workspace.d.ts +42 -107
  133. package/dist/runtime/cli-add-workspace.js +42 -674
  134. package/dist/runtime/cli-add.d.ts +3 -3
  135. package/dist/runtime/cli-add.js +2 -2
  136. package/dist/runtime/cli-core.d.ts +3 -2
  137. package/dist/runtime/cli-core.js +2 -2
  138. package/dist/runtime/cli-diagnostics.d.ts +3 -1
  139. package/dist/runtime/cli-diagnostics.js +17 -5
  140. package/dist/runtime/cli-doctor-workspace-bindings.js +63 -1
  141. package/dist/runtime/cli-doctor-workspace-block-addons.d.ts +12 -0
  142. package/dist/runtime/cli-doctor-workspace-block-addons.js +162 -0
  143. package/dist/runtime/cli-doctor-workspace-block-iframe.d.ts +9 -0
  144. package/dist/runtime/cli-doctor-workspace-block-iframe.js +228 -0
  145. package/dist/runtime/cli-doctor-workspace-block-metadata.d.ts +11 -0
  146. package/dist/runtime/cli-doctor-workspace-block-metadata.js +111 -0
  147. package/dist/runtime/cli-doctor-workspace-blocks.js +6 -424
  148. package/dist/runtime/cli-doctor-workspace-features-abilities.d.ts +11 -0
  149. package/dist/runtime/cli-doctor-workspace-features-abilities.js +112 -0
  150. package/dist/runtime/cli-doctor-workspace-features-admin-views.d.ts +11 -0
  151. package/dist/runtime/cli-doctor-workspace-features-admin-views.js +128 -0
  152. package/dist/runtime/cli-doctor-workspace-features-ai.d.ts +11 -0
  153. package/dist/runtime/cli-doctor-workspace-features-ai.js +57 -0
  154. package/dist/runtime/cli-doctor-workspace-features-editor-plugins.d.ts +11 -0
  155. package/dist/runtime/cli-doctor-workspace-features-editor-plugins.js +80 -0
  156. package/dist/runtime/cli-doctor-workspace-features-post-meta.d.ts +11 -0
  157. package/dist/runtime/cli-doctor-workspace-features-post-meta.js +77 -0
  158. package/dist/runtime/cli-doctor-workspace-features-rest.d.ts +11 -0
  159. package/dist/runtime/cli-doctor-workspace-features-rest.js +120 -0
  160. package/dist/runtime/cli-doctor-workspace-features.js +14 -487
  161. package/dist/runtime/cli-doctor.d.ts +54 -3
  162. package/dist/runtime/cli-doctor.js +92 -10
  163. package/dist/runtime/cli-help.js +12 -7
  164. package/dist/runtime/cli-init-package-json.js +4 -2
  165. package/dist/runtime/cli-prompt.d.ts +16 -2
  166. package/dist/runtime/cli-prompt.js +29 -12
  167. package/dist/runtime/cli-scaffold.d.ts +2 -1
  168. package/dist/runtime/cli-scaffold.js +19 -10
  169. package/dist/runtime/external-template-guards.js +4 -6
  170. package/dist/runtime/index.d.ts +6 -3
  171. package/dist/runtime/index.js +4 -2
  172. package/dist/runtime/json-utils.d.ts +62 -4
  173. package/dist/runtime/json-utils.js +78 -4
  174. package/dist/runtime/local-dev-presets.js +6 -2
  175. package/dist/runtime/migration-ui-capability.js +4 -1
  176. package/dist/runtime/migration-utils.js +4 -1
  177. package/dist/runtime/package-managers.js +6 -1
  178. package/dist/runtime/package-versions.d.ts +1 -0
  179. package/dist/runtime/package-versions.js +16 -3
  180. package/dist/runtime/pattern-catalog.d.ts +122 -0
  181. package/dist/runtime/pattern-catalog.js +471 -0
  182. package/dist/runtime/post-meta-binding-fields.d.ts +46 -0
  183. package/dist/runtime/post-meta-binding-fields.js +135 -0
  184. package/dist/runtime/scaffold-bootstrap.js +7 -2
  185. package/dist/runtime/scaffold-package-manager-files.js +5 -1
  186. package/dist/runtime/scaffold-repository-reference.js +4 -2
  187. package/dist/runtime/scaffold-template-variables.js +2 -1
  188. package/dist/runtime/scaffold.d.ts +18 -1
  189. package/dist/runtime/scaffold.js +55 -2
  190. package/dist/runtime/temp-roots.js +4 -1
  191. package/dist/runtime/template-layers.js +4 -1
  192. package/dist/runtime/template-registry.js +9 -3
  193. package/dist/runtime/template-source-contracts.d.ts +2 -0
  194. package/dist/runtime/template-source-normalization.js +2 -1
  195. package/dist/runtime/template-source-remote.js +18 -5
  196. package/dist/runtime/template-source-seeds.js +10 -3
  197. package/dist/runtime/typia-llm-json-schema.d.ts +24 -0
  198. package/dist/runtime/typia-llm-json-schema.js +33 -0
  199. package/dist/runtime/typia-llm-openapi-constraints.d.ts +20 -0
  200. package/dist/runtime/typia-llm-openapi-constraints.js +254 -0
  201. package/dist/runtime/typia-llm-projection.d.ts +25 -0
  202. package/dist/runtime/typia-llm-projection.js +58 -0
  203. package/dist/runtime/typia-llm-render.d.ts +21 -0
  204. package/dist/runtime/typia-llm-render.js +252 -0
  205. package/dist/runtime/typia-llm-sync.d.ts +10 -0
  206. package/dist/runtime/typia-llm-sync.js +63 -0
  207. package/dist/runtime/typia-llm-types.d.ts +197 -0
  208. package/dist/runtime/typia-llm-types.js +1 -0
  209. package/dist/runtime/typia-llm.d.ts +9 -255
  210. package/dist/runtime/typia-llm.js +5 -634
  211. package/dist/runtime/workspace-inventory-mutations.js +15 -1
  212. package/dist/runtime/workspace-inventory-parser-entries.d.ts +17 -0
  213. package/dist/runtime/workspace-inventory-parser-entries.js +157 -0
  214. package/dist/runtime/workspace-inventory-parser-validation.d.ts +104 -0
  215. package/dist/runtime/workspace-inventory-parser-validation.js +34 -0
  216. package/dist/runtime/workspace-inventory-parser.d.ts +3 -45
  217. package/dist/runtime/workspace-inventory-parser.js +3 -581
  218. package/dist/runtime/workspace-inventory-section-descriptors.d.ts +19 -0
  219. package/dist/runtime/workspace-inventory-section-descriptors.js +443 -0
  220. package/dist/runtime/workspace-inventory-templates.d.ts +3 -3
  221. package/dist/runtime/workspace-inventory-templates.js +10 -1
  222. package/dist/runtime/workspace-inventory-types.d.ts +10 -1
  223. package/dist/runtime/workspace-project.js +4 -6
  224. package/package.json +8 -3
  225. package/templates/_shared/compound/core/scripts/block-config.ts.mustache +22 -0
  226. package/templates/_shared/compound/core/scripts/sync-types-to-block-json.ts.mustache +103 -2
  227. package/templates/_shared/compound/core/src/inner-blocks-templates.ts.mustache +13 -0
  228. package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +22 -1
@@ -0,0 +1,112 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_ABILITY_EDITOR_ASSET, WORKSPACE_ABILITY_EDITOR_SCRIPT, WORKSPACE_ABILITY_GLOB, } from "./cli-doctor-workspace-shared.js";
4
+ import { readJsonFileSync } from "./json-utils.js";
5
+ import { escapeRegex } from "./php-utils.js";
6
+ function getWorkspaceAbilityRequiredFiles(ability) {
7
+ return Array.from(new Set([
8
+ ability.clientFile,
9
+ ability.configFile,
10
+ ability.dataFile,
11
+ ability.inputSchemaFile,
12
+ ability.outputSchemaFile,
13
+ ability.phpFile,
14
+ ability.typesFile,
15
+ ]));
16
+ }
17
+ function checkWorkspaceAbilityConfig(projectDir, ability) {
18
+ const configPath = path.join(projectDir, ability.configFile);
19
+ if (!fs.existsSync(configPath)) {
20
+ return createDoctorCheck(`Ability config ${ability.slug}`, "fail", `Missing ${ability.configFile}`);
21
+ }
22
+ try {
23
+ const config = readJsonFileSync(configPath, {
24
+ context: "workspace ability config",
25
+ });
26
+ const abilityId = typeof config.abilityId === "string" ? config.abilityId.trim() : "";
27
+ const categorySlug = typeof config.category?.slug === "string"
28
+ ? config.category.slug.trim()
29
+ : "";
30
+ const hasValidAbilityId = /^[a-z0-9-]+\/[a-z0-9-]+$/u.test(abilityId);
31
+ const hasValidCategorySlug = /^[a-z0-9-]+$/u.test(categorySlug);
32
+ return createDoctorCheck(`Ability config ${ability.slug}`, hasValidAbilityId && hasValidCategorySlug ? "pass" : "fail", hasValidAbilityId && hasValidCategorySlug
33
+ ? `Ability id ${abilityId} in category ${categorySlug} is valid`
34
+ : "Ability config must define a valid abilityId (`namespace/ability-name`) and category.slug.");
35
+ }
36
+ catch (error) {
37
+ return createDoctorCheck(`Ability config ${ability.slug}`, "fail", error instanceof Error ? error.message : String(error));
38
+ }
39
+ }
40
+ function checkWorkspaceAbilityBootstrap(projectDir, packageName, phpPrefix) {
41
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
42
+ if (!fs.existsSync(bootstrapPath)) {
43
+ return createDoctorCheck("Ability bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
44
+ }
45
+ const source = fs.readFileSync(bootstrapPath, "utf8");
46
+ const loadFunctionName = `${phpPrefix}_load_workflow_abilities`;
47
+ const enqueueFunctionName = `${phpPrefix}_enqueue_workflow_abilities`;
48
+ const loadHook = `add_action( 'plugins_loaded', '${loadFunctionName}' );`;
49
+ const adminEnqueueHook = `add_action( 'admin_enqueue_scripts', '${enqueueFunctionName}' );`;
50
+ const editorEnqueueHook = `add_action( 'enqueue_block_editor_assets', '${enqueueFunctionName}' );`;
51
+ const hasLoaderHook = source.includes(loadHook);
52
+ const hasAdminEnqueueHook = source.includes(adminEnqueueHook);
53
+ const hasEditorEnqueueHook = source.includes(editorEnqueueHook);
54
+ const hasServerGlob = source.includes(WORKSPACE_ABILITY_GLOB);
55
+ const hasEditorScript = source.includes(WORKSPACE_ABILITY_EDITOR_SCRIPT);
56
+ const hasEditorAsset = source.includes(WORKSPACE_ABILITY_EDITOR_ASSET);
57
+ const hasScriptModuleEnqueue = source.includes("wp_enqueue_script_module");
58
+ return createDoctorCheck("Ability bootstrap", hasLoaderHook &&
59
+ hasAdminEnqueueHook &&
60
+ hasEditorEnqueueHook &&
61
+ hasServerGlob &&
62
+ hasEditorScript &&
63
+ hasEditorAsset &&
64
+ hasScriptModuleEnqueue
65
+ ? "pass"
66
+ : "fail", hasLoaderHook &&
67
+ hasAdminEnqueueHook &&
68
+ hasEditorEnqueueHook &&
69
+ hasServerGlob &&
70
+ hasEditorScript &&
71
+ hasEditorAsset &&
72
+ hasScriptModuleEnqueue
73
+ ? "Ability loader and admin/editor script-module bootstrap hooks are present"
74
+ : "Missing ability loader hook, script-module enqueue, or build/abilities asset references");
75
+ }
76
+ function checkWorkspaceAbilityIndex(projectDir, abilities) {
77
+ const indexRelativePath = [
78
+ path.join("src", "abilities", "index.ts"),
79
+ path.join("src", "abilities", "index.js"),
80
+ ].find((relativePath) => fs.existsSync(path.join(projectDir, relativePath)));
81
+ if (!indexRelativePath) {
82
+ return createDoctorCheck("Abilities index", "fail", "Missing src/abilities/index.ts or src/abilities/index.js");
83
+ }
84
+ const indexPath = path.join(projectDir, indexRelativePath);
85
+ const source = fs.readFileSync(indexPath, "utf8");
86
+ const missingExports = abilities.filter((ability) => {
87
+ const exportPattern = new RegExp(`^\\s*export\\s+(?:\\*\\s+from|\\{[^}]+\\}\\s+from)\\s+['"\`]\\./${escapeRegex(ability.slug)}\\/client['"\`]`, "mu");
88
+ return !exportPattern.test(source);
89
+ });
90
+ return createDoctorCheck("Abilities index", missingExports.length === 0 ? "pass" : "fail", missingExports.length === 0
91
+ ? "Ability client helpers are aggregated"
92
+ : `Missing ability exports for: ${missingExports.map((entry) => entry.slug).join(", ")}`);
93
+ }
94
+ /**
95
+ * Collect ability workspace doctor checks while preserving existing row order.
96
+ *
97
+ * @param workspace Resolved workspace metadata and filesystem paths.
98
+ * @param abilities Ability entries parsed from the workspace inventory.
99
+ * @returns Ordered ability doctor checks.
100
+ */
101
+ export function getWorkspaceAbilityDoctorChecks(workspace, abilities) {
102
+ const checks = [];
103
+ if (abilities.length > 0) {
104
+ checks.push(checkWorkspaceAbilityBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
105
+ checks.push(checkWorkspaceAbilityIndex(workspace.projectDir, abilities));
106
+ }
107
+ for (const ability of abilities) {
108
+ checks.push(checkWorkspaceAbilityConfig(workspace.projectDir, ability));
109
+ checks.push(checkExistingFiles(workspace.projectDir, `Ability ${ability.slug}`, getWorkspaceAbilityRequiredFiles(ability)));
110
+ }
111
+ return checks;
112
+ }
@@ -0,0 +1,11 @@
1
+ import type { DoctorCheck } from "./cli-doctor.js";
2
+ import type { WorkspaceInventory } from "./workspace-inventory.js";
3
+ import type { WorkspaceProject } from "./workspace-project.js";
4
+ /**
5
+ * Collect admin view workspace doctor checks while preserving existing row order.
6
+ *
7
+ * @param workspace Resolved workspace metadata and filesystem paths.
8
+ * @param inventory Parsed workspace inventory from `scripts/block-config.ts`.
9
+ * @returns Ordered admin view doctor checks.
10
+ */
11
+ export declare function getWorkspaceAdminViewDoctorChecks(workspace: WorkspaceProject, inventory: WorkspaceInventory): DoctorCheck[];
@@ -0,0 +1,128 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { hasAdminViewManualSettingsRouteParameters, isAdminViewManualSettingsRestResource, } from "./cli-add-workspace-admin-view-types.js";
4
+ import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_ADMIN_VIEW_ASSET, WORKSPACE_ADMIN_VIEW_GLOB, WORKSPACE_ADMIN_VIEW_SCRIPT, WORKSPACE_ADMIN_VIEW_STYLE, } from "./cli-doctor-workspace-shared.js";
5
+ import { escapeRegex } from "./php-utils.js";
6
+ function getWorkspaceAdminViewRequiredFiles(adminView) {
7
+ const adminViewDir = path.join("src", "admin-views", adminView.slug);
8
+ return Array.from(new Set([
9
+ adminView.file,
10
+ adminView.phpFile,
11
+ path.join(adminViewDir, "Screen.tsx"),
12
+ path.join(adminViewDir, "config.ts"),
13
+ path.join(adminViewDir, "data.ts"),
14
+ path.join(adminViewDir, "style.scss"),
15
+ path.join(adminViewDir, "types.ts"),
16
+ ]));
17
+ }
18
+ function checkWorkspaceAdminViewConfig(adminView, inventory) {
19
+ if (adminView.source === undefined) {
20
+ return createDoctorCheck(`Admin view config ${adminView.slug}`, "pass", "Admin view uses a replaceable local fetcher");
21
+ }
22
+ const source = adminView.source.trim();
23
+ const restSourceMatch = /^rest-resource:([a-z][a-z0-9-]*)$/u.exec(source);
24
+ const coreDataSourceMatch = /^core-data:(postType|taxonomy)\/([a-z0-9][a-z0-9_-]*)$/u.exec(source);
25
+ const restResourceSlug = restSourceMatch?.[1];
26
+ const restResource = restResourceSlug
27
+ ? inventory.restResources.find((entry) => entry.slug === restResourceSlug)
28
+ : undefined;
29
+ const isListCapableRestResource = Boolean(restResource?.methods.includes("list"));
30
+ const isManualSettingsRestResource = isAdminViewManualSettingsRestResource(restResource);
31
+ const hasManualSettingsRouteParameters = isManualSettingsRestResource &&
32
+ hasAdminViewManualSettingsRouteParameters(restResource);
33
+ const isValid = isListCapableRestResource ||
34
+ (isManualSettingsRestResource && !hasManualSettingsRouteParameters) ||
35
+ Boolean(coreDataSourceMatch);
36
+ const failDetail = hasManualSettingsRouteParameters
37
+ ? `Admin view source ${source} uses route parameters or regex groups and cannot scaffold a singleton settings form`
38
+ : "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>";
39
+ return createDoctorCheck(`Admin view config ${adminView.slug}`, isValid ? "pass" : "fail", isValid
40
+ ? `Admin view source ${source} is ${isManualSettingsRestResource
41
+ ? "settings-form capable"
42
+ : coreDataSourceMatch
43
+ ? "core-data capable"
44
+ : "list-capable"}`
45
+ : failDetail);
46
+ }
47
+ function checkWorkspaceAdminViewBootstrap(projectDir, packageName, phpPrefix) {
48
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
49
+ if (!fs.existsSync(bootstrapPath)) {
50
+ return createDoctorCheck("Admin view bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
51
+ }
52
+ const source = fs.readFileSync(bootstrapPath, "utf8");
53
+ const loadFunctionName = `${phpPrefix}_load_admin_views`;
54
+ const loadHook = `add_action( 'plugins_loaded', '${loadFunctionName}' );`;
55
+ const hasLoaderHook = source.includes(loadHook);
56
+ const hasServerGlob = source.includes(WORKSPACE_ADMIN_VIEW_GLOB);
57
+ return createDoctorCheck("Admin view bootstrap", hasLoaderHook && hasServerGlob ? "pass" : "fail", hasLoaderHook && hasServerGlob
58
+ ? "Admin view PHP loader hook is present"
59
+ : "Missing admin view PHP require glob or plugins_loaded hook");
60
+ }
61
+ function checkWorkspaceAdminViewIndex(projectDir, adminViews) {
62
+ const indexRelativePath = [
63
+ path.join("src", "admin-views", "index.ts"),
64
+ path.join("src", "admin-views", "index.js"),
65
+ ].find((relativePath) => fs.existsSync(path.join(projectDir, relativePath)));
66
+ if (!indexRelativePath) {
67
+ return createDoctorCheck("Admin views index", "fail", "Missing src/admin-views/index.ts or src/admin-views/index.js");
68
+ }
69
+ const indexPath = path.join(projectDir, indexRelativePath);
70
+ const source = fs.readFileSync(indexPath, "utf8");
71
+ const missingImports = adminViews.filter((adminView) => {
72
+ const importPattern = new RegExp(`['"\`]\\./${escapeRegex(adminView.slug)}(?:/[^'"\`]*)?['"\`]`, "u");
73
+ return !importPattern.test(source);
74
+ });
75
+ return createDoctorCheck("Admin views index", missingImports.length === 0 ? "pass" : "fail", missingImports.length === 0
76
+ ? "Admin view registrations are aggregated"
77
+ : `Missing admin view imports for: ${missingImports
78
+ .map((entry) => entry.slug)
79
+ .join(", ")}`);
80
+ }
81
+ function checkWorkspaceAdminViewPhp(projectDir, adminView) {
82
+ const phpPath = path.join(projectDir, adminView.phpFile);
83
+ if (!fs.existsSync(phpPath)) {
84
+ return createDoctorCheck(`Admin view PHP ${adminView.slug}`, "fail", `Missing ${adminView.phpFile}`);
85
+ }
86
+ const source = fs.readFileSync(phpPath, "utf8");
87
+ const hasAdminMenu = source.includes("add_submenu_page");
88
+ const hasAdminEnqueue = source.includes("admin_enqueue_scripts");
89
+ const hasScript = source.includes(WORKSPACE_ADMIN_VIEW_SCRIPT);
90
+ const hasAsset = source.includes(WORKSPACE_ADMIN_VIEW_ASSET);
91
+ const hasStyle = source.includes(WORKSPACE_ADMIN_VIEW_STYLE);
92
+ const hasComponentsStyleDependency = /['"]wp-components['"]/u.test(source);
93
+ return createDoctorCheck(`Admin view PHP ${adminView.slug}`, hasAdminMenu &&
94
+ hasAdminEnqueue &&
95
+ hasScript &&
96
+ hasAsset &&
97
+ hasStyle &&
98
+ hasComponentsStyleDependency
99
+ ? "pass"
100
+ : "fail", hasAdminMenu &&
101
+ hasAdminEnqueue &&
102
+ hasScript &&
103
+ hasAsset &&
104
+ hasStyle &&
105
+ hasComponentsStyleDependency
106
+ ? "Admin menu, script, style, and wp-components style dependency are wired"
107
+ : "Missing admin menu, enqueue hook, build/admin-views asset reference, or wp-components style dependency");
108
+ }
109
+ /**
110
+ * Collect admin view workspace doctor checks while preserving existing row order.
111
+ *
112
+ * @param workspace Resolved workspace metadata and filesystem paths.
113
+ * @param inventory Parsed workspace inventory from `scripts/block-config.ts`.
114
+ * @returns Ordered admin view doctor checks.
115
+ */
116
+ export function getWorkspaceAdminViewDoctorChecks(workspace, inventory) {
117
+ const checks = [];
118
+ if (inventory.adminViews.length > 0) {
119
+ checks.push(checkWorkspaceAdminViewBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
120
+ checks.push(checkWorkspaceAdminViewIndex(workspace.projectDir, inventory.adminViews));
121
+ }
122
+ for (const adminView of inventory.adminViews) {
123
+ checks.push(checkWorkspaceAdminViewConfig(adminView, inventory));
124
+ checks.push(checkExistingFiles(workspace.projectDir, `Admin view ${adminView.slug}`, getWorkspaceAdminViewRequiredFiles(adminView)));
125
+ checks.push(checkWorkspaceAdminViewPhp(workspace.projectDir, adminView));
126
+ }
127
+ return checks;
128
+ }
@@ -0,0 +1,11 @@
1
+ import type { DoctorCheck } from "./cli-doctor.js";
2
+ import type { WorkspaceInventory } from "./workspace-inventory.js";
3
+ import type { WorkspaceProject } from "./workspace-project.js";
4
+ /**
5
+ * Collect AI feature workspace doctor checks while preserving existing row order.
6
+ *
7
+ * @param workspace Resolved workspace metadata and filesystem paths.
8
+ * @param aiFeatures AI feature entries parsed from the workspace inventory.
9
+ * @returns Ordered AI feature doctor checks.
10
+ */
11
+ export declare function getWorkspaceAiFeatureDoctorChecks(workspace: WorkspaceProject, aiFeatures: WorkspaceInventory["aiFeatures"]): DoctorCheck[];
@@ -0,0 +1,57 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { REST_RESOURCE_NAMESPACE_PATTERN } from "./cli-add-shared.js";
4
+ import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_AI_FEATURE_GLOB, } from "./cli-doctor-workspace-shared.js";
5
+ function getWorkspaceAiFeatureRequiredFiles(aiFeature) {
6
+ return Array.from(new Set([
7
+ aiFeature.aiSchemaFile,
8
+ aiFeature.apiFile,
9
+ path.join(path.dirname(aiFeature.typesFile), "api-schemas", "feature-request.schema.json"),
10
+ path.join(path.dirname(aiFeature.typesFile), "api-schemas", "feature-response.schema.json"),
11
+ path.join(path.dirname(aiFeature.typesFile), "api-schemas", "feature-result.schema.json"),
12
+ aiFeature.clientFile,
13
+ aiFeature.dataFile,
14
+ aiFeature.openApiFile,
15
+ aiFeature.phpFile,
16
+ aiFeature.typesFile,
17
+ aiFeature.validatorsFile,
18
+ ]));
19
+ }
20
+ function checkWorkspaceAiFeatureConfig(aiFeature) {
21
+ const hasNamespace = REST_RESOURCE_NAMESPACE_PATTERN.test(aiFeature.namespace);
22
+ return createDoctorCheck(`AI feature config ${aiFeature.slug}`, hasNamespace ? "pass" : "fail", hasNamespace
23
+ ? `AI feature namespace ${aiFeature.namespace} is valid`
24
+ : "AI feature namespace is invalid");
25
+ }
26
+ function checkWorkspaceAiFeatureBootstrap(projectDir, packageName, phpPrefix) {
27
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
28
+ if (!fs.existsSync(bootstrapPath)) {
29
+ return createDoctorCheck("AI feature bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
30
+ }
31
+ const source = fs.readFileSync(bootstrapPath, "utf8");
32
+ const registerFunctionName = `${phpPrefix}_register_ai_features`;
33
+ const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
34
+ const hasServerGlob = source.includes(WORKSPACE_AI_FEATURE_GLOB);
35
+ const hasRegisterHook = source.includes(registerHook);
36
+ return createDoctorCheck("AI feature bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook
37
+ ? "AI feature PHP loader hook is present"
38
+ : "Missing AI feature PHP require glob or init hook");
39
+ }
40
+ /**
41
+ * Collect AI feature workspace doctor checks while preserving existing row order.
42
+ *
43
+ * @param workspace Resolved workspace metadata and filesystem paths.
44
+ * @param aiFeatures AI feature entries parsed from the workspace inventory.
45
+ * @returns Ordered AI feature doctor checks.
46
+ */
47
+ export function getWorkspaceAiFeatureDoctorChecks(workspace, aiFeatures) {
48
+ const checks = [];
49
+ if (aiFeatures.length > 0) {
50
+ checks.push(checkWorkspaceAiFeatureBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
51
+ }
52
+ for (const aiFeature of aiFeatures) {
53
+ checks.push(checkWorkspaceAiFeatureConfig(aiFeature));
54
+ checks.push(checkExistingFiles(workspace.projectDir, `AI feature ${aiFeature.slug}`, getWorkspaceAiFeatureRequiredFiles(aiFeature)));
55
+ }
56
+ return checks;
57
+ }
@@ -0,0 +1,11 @@
1
+ import type { DoctorCheck } from "./cli-doctor.js";
2
+ import type { WorkspaceInventory } from "./workspace-inventory.js";
3
+ import type { WorkspaceProject } from "./workspace-project.js";
4
+ /**
5
+ * Collect editor plugin workspace doctor checks while preserving existing row order.
6
+ *
7
+ * @param workspace Resolved workspace metadata and filesystem paths.
8
+ * @param editorPlugins Editor plugin entries parsed from the workspace inventory.
9
+ * @returns Ordered editor plugin doctor checks.
10
+ */
11
+ export declare function getWorkspaceEditorPluginDoctorChecks(workspace: WorkspaceProject, editorPlugins: WorkspaceInventory["editorPlugins"]): DoctorCheck[];
@@ -0,0 +1,80 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { EDITOR_PLUGIN_SLOT_IDS, resolveEditorPluginSlotAlias, } from "./cli-add-shared.js";
4
+ import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_EDITOR_PLUGIN_EDITOR_ASSET, WORKSPACE_EDITOR_PLUGIN_EDITOR_SCRIPT, WORKSPACE_EDITOR_PLUGIN_EDITOR_STYLE, } from "./cli-doctor-workspace-shared.js";
5
+ import { escapeRegex } from "./php-utils.js";
6
+ function getWorkspaceEditorPluginRequiredFiles(editorPlugin) {
7
+ const editorPluginDir = path.join("src", "editor-plugins", editorPlugin.slug);
8
+ const surfaceFile = editorPlugin.slot === "PluginSidebar"
9
+ ? path.join(editorPluginDir, "Sidebar.tsx")
10
+ : path.join(editorPluginDir, "Surface.tsx");
11
+ return Array.from(new Set([
12
+ editorPlugin.file,
13
+ surfaceFile,
14
+ path.join(editorPluginDir, "data.ts"),
15
+ path.join(editorPluginDir, "types.ts"),
16
+ path.join(editorPluginDir, "style.scss"),
17
+ ]));
18
+ }
19
+ function checkWorkspaceEditorPluginConfig(editorPlugin) {
20
+ const normalizedSlot = resolveEditorPluginSlotAlias(editorPlugin.slot);
21
+ const isValidSlot = Boolean(normalizedSlot);
22
+ return createDoctorCheck(`Editor plugin config ${editorPlugin.slug}`, isValidSlot ? "pass" : "fail", isValidSlot
23
+ ? `Editor plugin slot ${editorPlugin.slot} is supported as ${normalizedSlot}`
24
+ : `Unsupported editor plugin slot "${editorPlugin.slot}". Expected one of: ${EDITOR_PLUGIN_SLOT_IDS.join(", ")} or legacy aliases PluginSidebar, PluginDocumentSettingPanel.`);
25
+ }
26
+ function checkWorkspaceEditorPluginBootstrap(projectDir, packageName, phpPrefix) {
27
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
28
+ if (!fs.existsSync(bootstrapPath)) {
29
+ return createDoctorCheck("Editor plugin bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
30
+ }
31
+ const source = fs.readFileSync(bootstrapPath, "utf8");
32
+ const enqueueFunctionName = `${phpPrefix}_enqueue_editor_plugins_editor`;
33
+ const enqueueHook = `add_action( 'enqueue_block_editor_assets', '${enqueueFunctionName}' );`;
34
+ const hasEditorEnqueueHook = source.includes(enqueueHook);
35
+ const hasEditorScript = source.includes(WORKSPACE_EDITOR_PLUGIN_EDITOR_SCRIPT);
36
+ const hasEditorAsset = source.includes(WORKSPACE_EDITOR_PLUGIN_EDITOR_ASSET);
37
+ const hasEditorStyle = source.includes(WORKSPACE_EDITOR_PLUGIN_EDITOR_STYLE);
38
+ return createDoctorCheck("Editor plugin bootstrap", hasEditorEnqueueHook && hasEditorScript && hasEditorAsset && hasEditorStyle ? "pass" : "fail", hasEditorEnqueueHook && hasEditorScript && hasEditorAsset && hasEditorStyle
39
+ ? "Editor plugin enqueue hook is present"
40
+ : "Missing editor plugin enqueue hook or build/editor-plugins script/style asset references");
41
+ }
42
+ function checkWorkspaceEditorPluginIndex(projectDir, editorPlugins) {
43
+ const indexRelativePath = [
44
+ path.join("src", "editor-plugins", "index.ts"),
45
+ path.join("src", "editor-plugins", "index.js"),
46
+ ].find((relativePath) => fs.existsSync(path.join(projectDir, relativePath)));
47
+ if (!indexRelativePath) {
48
+ return createDoctorCheck("Editor plugins index", "fail", "Missing src/editor-plugins/index.ts or src/editor-plugins/index.js");
49
+ }
50
+ const indexPath = path.join(projectDir, indexRelativePath);
51
+ const source = fs.readFileSync(indexPath, "utf8");
52
+ const missingImports = editorPlugins.filter((editorPlugin) => {
53
+ const importPattern = new RegExp(`['"\`]\\./${escapeRegex(editorPlugin.slug)}(?:/[^'"\`]*)?['"\`]`, "u");
54
+ return !importPattern.test(source);
55
+ });
56
+ return createDoctorCheck("Editor plugins index", missingImports.length === 0 ? "pass" : "fail", missingImports.length === 0
57
+ ? "Editor plugin registrations are aggregated"
58
+ : `Missing editor plugin imports for: ${missingImports
59
+ .map((entry) => entry.slug)
60
+ .join(", ")}`);
61
+ }
62
+ /**
63
+ * Collect editor plugin workspace doctor checks while preserving existing row order.
64
+ *
65
+ * @param workspace Resolved workspace metadata and filesystem paths.
66
+ * @param editorPlugins Editor plugin entries parsed from the workspace inventory.
67
+ * @returns Ordered editor plugin doctor checks.
68
+ */
69
+ export function getWorkspaceEditorPluginDoctorChecks(workspace, editorPlugins) {
70
+ const checks = [];
71
+ if (editorPlugins.length > 0) {
72
+ checks.push(checkWorkspaceEditorPluginBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
73
+ checks.push(checkWorkspaceEditorPluginIndex(workspace.projectDir, editorPlugins));
74
+ }
75
+ for (const editorPlugin of editorPlugins) {
76
+ checks.push(checkExistingFiles(workspace.projectDir, `Editor plugin ${editorPlugin.slug}`, getWorkspaceEditorPluginRequiredFiles(editorPlugin)));
77
+ checks.push(checkWorkspaceEditorPluginConfig(editorPlugin));
78
+ }
79
+ return checks;
80
+ }
@@ -0,0 +1,11 @@
1
+ import type { DoctorCheck } from "./cli-doctor.js";
2
+ import type { WorkspaceInventory } from "./workspace-inventory.js";
3
+ import type { WorkspaceProject } from "./workspace-project.js";
4
+ /**
5
+ * Collect post meta workspace doctor checks while preserving existing row order.
6
+ *
7
+ * @param workspace Resolved workspace metadata and filesystem paths.
8
+ * @param postMetaEntries Post meta entries parsed from the workspace inventory.
9
+ * @returns Ordered post meta doctor checks.
10
+ */
11
+ export declare function getWorkspacePostMetaDoctorChecks(workspace: WorkspaceProject, postMetaEntries: WorkspaceInventory["postMeta"]): DoctorCheck[];
@@ -0,0 +1,77 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { assertValidPostMetaPostType } from "./cli-add-shared.js";
4
+ import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_POST_META_GLOB, } from "./cli-doctor-workspace-shared.js";
5
+ function getWorkspacePostMetaRequiredFiles(postMeta) {
6
+ return Array.from(new Set([
7
+ postMeta.phpFile,
8
+ postMeta.schemaFile,
9
+ postMeta.typesFile,
10
+ ]));
11
+ }
12
+ function checkWorkspacePostMetaConfig(postMeta) {
13
+ let hasPostType = false;
14
+ try {
15
+ hasPostType = assertValidPostMetaPostType(postMeta.postType) === postMeta.postType;
16
+ }
17
+ catch {
18
+ hasPostType = false;
19
+ }
20
+ const hasMetaKey = typeof postMeta.metaKey === "string" &&
21
+ postMeta.metaKey.trim().length > 0 &&
22
+ !/\s/u.test(postMeta.metaKey);
23
+ const hasRestExposure = typeof postMeta.showInRest === "boolean";
24
+ return createDoctorCheck(`Post meta config ${postMeta.slug}`, hasPostType && hasMetaKey && hasRestExposure ? "pass" : "fail", hasPostType && hasMetaKey && hasRestExposure
25
+ ? `Post meta ${postMeta.metaKey} targets ${postMeta.postType}`
26
+ : "Post meta postType, metaKey, or showInRest configuration is invalid");
27
+ }
28
+ function checkWorkspacePostMetaBootstrap(projectDir, packageName, phpPrefix) {
29
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
30
+ if (!fs.existsSync(bootstrapPath)) {
31
+ return createDoctorCheck("Post meta bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
32
+ }
33
+ const source = fs.readFileSync(bootstrapPath, "utf8");
34
+ const registerFunctionName = `${phpPrefix}_register_post_meta_contracts`;
35
+ const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
36
+ const hasServerGlob = source.includes(WORKSPACE_POST_META_GLOB);
37
+ const hasRegisterHook = source.includes(registerHook);
38
+ return createDoctorCheck("Post meta bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook
39
+ ? "Post meta PHP loader hook is present"
40
+ : "Missing post meta PHP require glob or init hook");
41
+ }
42
+ function checkWorkspacePostMetaPhp(projectDir, postMeta) {
43
+ const phpPath = path.join(projectDir, postMeta.phpFile);
44
+ if (!fs.existsSync(phpPath)) {
45
+ return createDoctorCheck(`Post meta PHP ${postMeta.slug}`, "fail", `Missing ${postMeta.phpFile}`);
46
+ }
47
+ const source = fs.readFileSync(phpPath, "utf8");
48
+ const hasRegisterPostMeta = source.includes("register_post_meta");
49
+ const hasPostType = source.includes(postMeta.postType);
50
+ const hasMetaKey = source.includes(postMeta.metaKey);
51
+ const hasSchemaFile = source.includes(postMeta.schemaFile);
52
+ const hasRestExposure = source.includes("'show_in_rest'");
53
+ return createDoctorCheck(`Post meta PHP ${postMeta.slug}`, hasRegisterPostMeta && hasPostType && hasMetaKey && hasSchemaFile && hasRestExposure
54
+ ? "pass"
55
+ : "fail", hasRegisterPostMeta && hasPostType && hasMetaKey && hasSchemaFile && hasRestExposure
56
+ ? "Post meta registration, schema path, and REST exposure flag are wired"
57
+ : "Missing register_post_meta, post type, meta key, schema path, or show_in_rest wiring");
58
+ }
59
+ /**
60
+ * Collect post meta workspace doctor checks while preserving existing row order.
61
+ *
62
+ * @param workspace Resolved workspace metadata and filesystem paths.
63
+ * @param postMetaEntries Post meta entries parsed from the workspace inventory.
64
+ * @returns Ordered post meta doctor checks.
65
+ */
66
+ export function getWorkspacePostMetaDoctorChecks(workspace, postMetaEntries) {
67
+ const checks = [];
68
+ if (postMetaEntries.length > 0) {
69
+ checks.push(checkWorkspacePostMetaBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
70
+ }
71
+ for (const postMeta of postMetaEntries) {
72
+ checks.push(checkWorkspacePostMetaConfig(postMeta));
73
+ checks.push(checkExistingFiles(workspace.projectDir, `Post meta ${postMeta.slug}`, getWorkspacePostMetaRequiredFiles(postMeta)));
74
+ checks.push(checkWorkspacePostMetaPhp(workspace.projectDir, postMeta));
75
+ }
76
+ return checks;
77
+ }
@@ -0,0 +1,11 @@
1
+ import type { DoctorCheck } from "./cli-doctor.js";
2
+ import type { WorkspaceInventory } from "./workspace-inventory.js";
3
+ import type { WorkspaceProject } from "./workspace-project.js";
4
+ /**
5
+ * Collect REST resource workspace doctor checks while preserving existing row order.
6
+ *
7
+ * @param workspace Resolved workspace metadata and filesystem paths.
8
+ * @param restResources REST resources parsed from the workspace inventory.
9
+ * @returns Ordered REST resource doctor checks.
10
+ */
11
+ export declare function getWorkspaceRestResourceDoctorChecks(workspace: WorkspaceProject, restResources: WorkspaceInventory["restResources"]): DoctorCheck[];
@@ -0,0 +1,120 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { MANUAL_REST_CONTRACT_AUTH_IDS, MANUAL_REST_CONTRACT_HTTP_METHOD_IDS, REST_RESOURCE_METHOD_IDS, REST_RESOURCE_NAMESPACE_PATTERN, isGeneratedRestResourceRoutePatternCompatible, } from "./cli-add-shared.js";
4
+ import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_REST_RESOURCE_GLOB, } from "./cli-doctor-workspace-shared.js";
5
+ function isManualRestResource(restResource) {
6
+ return restResource.mode === "manual";
7
+ }
8
+ function getWorkspaceRestResourceRequiredFiles(restResource) {
9
+ const schemaNames = new Set();
10
+ if (isManualRestResource(restResource)) {
11
+ schemaNames.add("query");
12
+ if (restResource.bodyTypeName) {
13
+ schemaNames.add("request");
14
+ }
15
+ schemaNames.add("response");
16
+ return Array.from(new Set([
17
+ restResource.apiFile,
18
+ ...Array.from(schemaNames, (schemaName) => path.join(path.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
19
+ restResource.clientFile,
20
+ restResource.openApiFile,
21
+ restResource.typesFile,
22
+ restResource.validatorsFile,
23
+ ]));
24
+ }
25
+ if (restResource.methods.includes("list")) {
26
+ schemaNames.add("list-query");
27
+ schemaNames.add("list-response");
28
+ }
29
+ if (restResource.methods.includes("read")) {
30
+ schemaNames.add("read-query");
31
+ schemaNames.add("read-response");
32
+ }
33
+ if (restResource.methods.includes("create")) {
34
+ schemaNames.add("create-request");
35
+ schemaNames.add("create-response");
36
+ }
37
+ if (restResource.methods.includes("update")) {
38
+ schemaNames.add("update-query");
39
+ schemaNames.add("update-request");
40
+ schemaNames.add("update-response");
41
+ }
42
+ if (restResource.methods.includes("delete")) {
43
+ schemaNames.add("delete-query");
44
+ schemaNames.add("delete-response");
45
+ }
46
+ return Array.from(new Set([
47
+ restResource.apiFile,
48
+ ...Array.from(schemaNames, (schemaName) => path.join(path.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
49
+ restResource.clientFile,
50
+ ...(restResource.dataFile ? [restResource.dataFile] : []),
51
+ restResource.openApiFile,
52
+ ...(restResource.phpFile ? [restResource.phpFile] : []),
53
+ restResource.typesFile,
54
+ restResource.validatorsFile,
55
+ ]));
56
+ }
57
+ function checkWorkspaceRestResourceConfig(restResource) {
58
+ const hasNamespace = REST_RESOURCE_NAMESPACE_PATTERN.test(restResource.namespace);
59
+ if (isManualRestResource(restResource)) {
60
+ const hasAuth = restResource.auth == null ||
61
+ MANUAL_REST_CONTRACT_AUTH_IDS.includes(restResource.auth);
62
+ const hasMethod = typeof restResource.method === "string" &&
63
+ MANUAL_REST_CONTRACT_HTTP_METHOD_IDS.includes(restResource.method);
64
+ const hasPathPattern = typeof restResource.pathPattern === "string" &&
65
+ restResource.pathPattern.startsWith("/") &&
66
+ restResource.pathPattern.length > 1;
67
+ return createDoctorCheck(`REST resource config ${restResource.slug}`, hasNamespace && hasAuth && hasMethod && hasPathPattern ? "pass" : "fail", hasNamespace && hasAuth && hasMethod && hasPathPattern
68
+ ? `Manual REST contract ${restResource.method} /${restResource.namespace}${restResource.pathPattern}`
69
+ : "Manual REST contract namespace, auth, method, or path pattern is invalid");
70
+ }
71
+ const hasMethods = restResource.methods.length > 0 &&
72
+ restResource.methods.every((method) => REST_RESOURCE_METHOD_IDS.includes(method));
73
+ const hasGeneratedFiles = typeof restResource.dataFile === "string" &&
74
+ restResource.dataFile.length > 0 &&
75
+ typeof restResource.phpFile === "string" &&
76
+ restResource.phpFile.length > 0;
77
+ const hasRoutePattern = restResource.routePattern == null ||
78
+ (typeof restResource.routePattern === "string" &&
79
+ restResource.routePattern.startsWith("/") &&
80
+ restResource.routePattern.length > 1 &&
81
+ !/\s/u.test(restResource.routePattern) &&
82
+ isGeneratedRestResourceRoutePatternCompatible(restResource.routePattern));
83
+ return createDoctorCheck(`REST resource config ${restResource.slug}`, hasNamespace && hasMethods && hasGeneratedFiles && hasRoutePattern
84
+ ? "pass"
85
+ : "fail", hasNamespace && hasMethods && hasGeneratedFiles && hasRoutePattern
86
+ ? `REST resource namespace ${restResource.namespace} with methods ${restResource.methods.join(", ")}`
87
+ : "REST resource namespace, methods, dataFile, phpFile, or routePattern are invalid");
88
+ }
89
+ function checkWorkspaceRestResourceBootstrap(projectDir, packageName, phpPrefix) {
90
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
91
+ if (!fs.existsSync(bootstrapPath)) {
92
+ return createDoctorCheck("REST resource bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
93
+ }
94
+ const source = fs.readFileSync(bootstrapPath, "utf8");
95
+ const registerFunctionName = `${phpPrefix}_register_rest_resources`;
96
+ const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
97
+ const hasServerGlob = source.includes(WORKSPACE_REST_RESOURCE_GLOB);
98
+ const hasRegisterHook = source.includes(registerHook);
99
+ return createDoctorCheck("REST resource bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook
100
+ ? "REST resource PHP loader hook is present"
101
+ : "Missing REST resource PHP require glob or init hook");
102
+ }
103
+ /**
104
+ * Collect REST resource workspace doctor checks while preserving existing row order.
105
+ *
106
+ * @param workspace Resolved workspace metadata and filesystem paths.
107
+ * @param restResources REST resources parsed from the workspace inventory.
108
+ * @returns Ordered REST resource doctor checks.
109
+ */
110
+ export function getWorkspaceRestResourceDoctorChecks(workspace, restResources) {
111
+ const checks = [];
112
+ if (restResources.some((restResource) => !isManualRestResource(restResource))) {
113
+ checks.push(checkWorkspaceRestResourceBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
114
+ }
115
+ for (const restResource of restResources) {
116
+ checks.push(checkWorkspaceRestResourceConfig(restResource));
117
+ checks.push(checkExistingFiles(workspace.projectDir, `REST resource ${restResource.slug}`, getWorkspaceRestResourceRequiredFiles(restResource)));
118
+ }
119
+ return checks;
120
+ }