@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
@@ -1,529 +1,8 @@
1
- import { promises as fsp } from "node:fs";
2
- import path from "node:path";
3
- import { ensureBlockConfigCanAddRestManifests } from "./cli-add-block-legacy-validator.js";
4
- import { assertValidManualRestContractAuth, assertValidManualRestContractHttpMethod, assertRestResourceDoesNotExist, assertValidGeneratedSlug, assertValidRestResourceMethods, assertValidTypeScriptIdentifier, getWorkspaceBootstrapPath, normalizeBlockSlug, resolveGeneratedRestResourceRoutePattern, resolveManualRestContractPathPattern, resolveOptionalPhpCallbackReference, resolveOptionalPhpClassReference, resolveRestResourceNamespace, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
5
- import { ensureRestResourceBootstrapAnchors, ensureRestResourceSyncScriptAnchors, } from "./cli-add-workspace-rest-anchors.js";
6
- import { buildManualRestContractApiSource, buildManualRestContractConfigEntry, buildManualRestContractTypesSource, buildManualRestContractValidatorsSource, buildRestResourceApiSource, buildRestResourceConfigEntry, buildRestResourceDataSource, buildRestResourceTypesSource, buildRestResourceValidatorsSource, } from "./cli-add-workspace-rest-source-emitters.js";
7
- import { quotePhpString } from "./php-utils.js";
8
- import { syncManualRestContractArtifacts, syncRestResourceArtifacts, } from "./rest-resource-artifacts.js";
9
- import { toPascalCase, toTitleCase } from "./string-case.js";
10
- import { appendWorkspaceInventoryEntries, readWorkspaceInventoryAsync, } from "./workspace-inventory.js";
1
+ import { assertRestResourceDoesNotExist, assertValidGeneratedSlug, normalizeBlockSlug, resolveRestResourceNamespace, } from "./cli-add-shared.js";
2
+ import { scaffoldGeneratedRestResource } from "./cli-add-workspace-rest-generated.js";
3
+ import { scaffoldManualRestContract } from "./cli-add-workspace-rest-manual.js";
4
+ import { readWorkspaceInventoryAsync } from "./workspace-inventory.js";
11
5
  import { resolveWorkspaceProject } from "./workspace-project.js";
12
- const MANUAL_REST_REQUEST_BODY_FIELD_NAMES = new Set(["payload", "comment"]);
13
- const MANUAL_REST_RESPONSE_FIELD_NAMES = new Set([
14
- "id",
15
- "status",
16
- "message",
17
- "updatedAt",
18
- ]);
19
- function buildRestResourceRouteRegistrations(restResourceSlug, methods, functions, options) {
20
- const collectionRoutes = [];
21
- const itemRoutes = [];
22
- const readPermissionCallback = options.permissionCallback
23
- ? quotePhpString(options.permissionCallback)
24
- : quotePhpString("__return_true");
25
- const writePermissionCallback = options.permissionCallback
26
- ? quotePhpString(options.permissionCallback)
27
- : options.controllerVariableName
28
- ? `array( ${options.controllerVariableName}, 'can_manage_rest_resource' )`
29
- : quotePhpString(functions.canWriteFunctionName);
30
- const buildRouteCallback = (functionName, methodName) => options.controllerVariableName
31
- ? `array( ${options.controllerVariableName}, '${methodName}' )`
32
- : quotePhpString(functionName);
33
- if (methods.includes("list")) {
34
- collectionRoutes.push(`\t\tarray(
35
- \t\t\t'methods' => WP_REST_Server::READABLE,
36
- \t\t\t'callback' => ${buildRouteCallback(functions.listHandlerName, "list_items")},
37
- \t\t\t'permission_callback' => ${readPermissionCallback},
38
- \t\t)`);
39
- }
40
- if (methods.includes("create")) {
41
- collectionRoutes.push(`\t\tarray(
42
- \t\t\t'methods' => WP_REST_Server::CREATABLE,
43
- \t\t\t'callback' => ${buildRouteCallback(functions.createHandlerName, "create_item")},
44
- \t\t\t'permission_callback' => ${writePermissionCallback},
45
- \t\t)`);
46
- }
47
- if (methods.includes("read")) {
48
- itemRoutes.push(`\t\tarray(
49
- \t\t\t'methods' => WP_REST_Server::READABLE,
50
- \t\t\t'callback' => ${buildRouteCallback(functions.readHandlerName, "read_item")},
51
- \t\t\t'permission_callback' => ${readPermissionCallback},
52
- \t\t)`);
53
- }
54
- if (methods.includes("update")) {
55
- itemRoutes.push(`\t\tarray(
56
- \t\t\t'methods' => WP_REST_Server::EDITABLE,
57
- \t\t\t'callback' => ${buildRouteCallback(functions.updateHandlerName, "update_item")},
58
- \t\t\t'permission_callback' => ${writePermissionCallback},
59
- \t\t)`);
60
- }
61
- if (methods.includes("delete")) {
62
- itemRoutes.push(`\t\tarray(
63
- \t\t\t'methods' => WP_REST_Server::DELETABLE,
64
- \t\t\t'callback' => ${buildRouteCallback(functions.deleteHandlerName, "delete_item")},
65
- \t\t\t'permission_callback' => ${writePermissionCallback},
66
- \t\t)`);
67
- }
68
- const registrations = [];
69
- if (collectionRoutes.length > 0) {
70
- registrations.push(`\tregister_rest_route(
71
- \t\t$namespace,
72
- \t\t'/${restResourceSlug}',
73
- \t\tarray(
74
- ${collectionRoutes.join(",\n")}
75
- \t\t)
76
- \t);`);
77
- }
78
- if (itemRoutes.length > 0) {
79
- registrations.push(`\tregister_rest_route(
80
- \t\t$namespace,
81
- \t\t${quotePhpString(options.routePattern)},
82
- \t\tarray(
83
- ${itemRoutes.join(",\n")}
84
- \t\t)
85
- \t);`);
86
- }
87
- return registrations.join("\n\n");
88
- }
89
- function normalizeGlobalPhpClassName(classReference) {
90
- const normalized = classReference.startsWith("\\")
91
- ? classReference.slice(1)
92
- : classReference;
93
- return /^[A-Za-z_][A-Za-z0-9_]*$/u.test(normalized) ? normalized : undefined;
94
- }
95
- function toPhpClassConstantReference(classReference) {
96
- const normalized = classReference.startsWith("\\")
97
- ? classReference
98
- : `\\${classReference}`;
99
- return `${normalized}::class`;
100
- }
101
- function buildRestResourceControllerClassSource(options) {
102
- const controllerClassName = normalizeGlobalPhpClassName(options.controllerClass);
103
- if (!controllerClassName) {
104
- return "";
105
- }
106
- const extendsClause = options.controllerExtends
107
- ? ` extends ${options.controllerExtends.startsWith("\\") ? options.controllerExtends : `\\${options.controllerExtends}`}`
108
- : "";
109
- return `
110
- if ( ! class_exists( ${quotePhpString(controllerClassName)} ) ) {
111
- \tclass ${controllerClassName}${extendsClause} {
112
- \t\tpublic function can_manage_rest_resource() {
113
- \t\t\treturn ${options.functions.canWriteFunctionName}();
114
- \t\t}
115
-
116
- \t\tpublic function list_items( WP_REST_Request $request ) {
117
- \t\t\treturn ${options.functions.listHandlerName}( $request );
118
- \t\t}
119
-
120
- \t\tpublic function read_item( WP_REST_Request $request ) {
121
- \t\t\treturn ${options.functions.readHandlerName}( $request );
122
- \t\t}
123
-
124
- \t\tpublic function create_item( WP_REST_Request $request ) {
125
- \t\t\treturn ${options.functions.createHandlerName}( $request );
126
- \t\t}
127
-
128
- \t\tpublic function update_item( WP_REST_Request $request ) {
129
- \t\t\treturn ${options.functions.updateHandlerName}( $request );
130
- \t\t}
131
-
132
- \t\tpublic function delete_item( WP_REST_Request $request ) {
133
- \t\t\treturn ${options.functions.deleteHandlerName}( $request );
134
- \t\t}
135
- \t}
136
- }
137
- `;
138
- }
139
- function buildRestResourcePhpSource(restResourceSlug, namespace, phpPrefix, textDomain, methods, options) {
140
- const restResourceTitle = toTitleCase(restResourceSlug);
141
- const restResourcePhpId = restResourceSlug.replace(/-/g, "_");
142
- const canWriteFunctionName = `${phpPrefix}_${restResourcePhpId}_can_manage_rest_resource`;
143
- const getItemsFunctionName = `${phpPrefix}_${restResourcePhpId}_get_rest_resource_items`;
144
- const loadSchemaFunctionName = `${phpPrefix}_${restResourcePhpId}_load_rest_resource_schema`;
145
- const normalizeSchemaFunctionName = `${phpPrefix}_${restResourcePhpId}_sanitize_rest_resource_schema`;
146
- const validatePayloadFunctionName = `${phpPrefix}_${restResourcePhpId}_validate_rest_resource_payload`;
147
- const normalizeItemFunctionName = `${phpPrefix}_${restResourcePhpId}_normalize_rest_resource_item`;
148
- const saveItemsFunctionName = `${phpPrefix}_${restResourcePhpId}_save_rest_resource_items`;
149
- const getOptionNameFunctionName = `${phpPrefix}_${restResourcePhpId}_get_rest_resource_option_name`;
150
- const listHandlerName = `${phpPrefix}_${restResourcePhpId}_handle_list_rest_resource`;
151
- const readHandlerName = `${phpPrefix}_${restResourcePhpId}_handle_read_rest_resource`;
152
- const createHandlerName = `${phpPrefix}_${restResourcePhpId}_handle_create_rest_resource`;
153
- const updateHandlerName = `${phpPrefix}_${restResourcePhpId}_handle_update_rest_resource`;
154
- const deleteHandlerName = `${phpPrefix}_${restResourcePhpId}_handle_delete_rest_resource`;
155
- const registerRoutesFunctionName = `${phpPrefix}_${restResourcePhpId}_register_rest_routes`;
156
- const controllerVariableName = options.controllerClass ? "$controller" : undefined;
157
- const routeRegistrations = buildRestResourceRouteRegistrations(restResourceSlug, methods, {
158
- canWriteFunctionName,
159
- createHandlerName,
160
- deleteHandlerName,
161
- listHandlerName,
162
- readHandlerName,
163
- updateHandlerName,
164
- }, {
165
- ...(controllerVariableName ? { controllerVariableName } : {}),
166
- ...(options.permissionCallback
167
- ? { permissionCallback: options.permissionCallback }
168
- : {}),
169
- routePattern: options.routePattern,
170
- });
171
- const controllerClassSource = options.controllerClass
172
- ? buildRestResourceControllerClassSource({
173
- controllerClass: options.controllerClass,
174
- ...(options.controllerExtends
175
- ? { controllerExtends: options.controllerExtends }
176
- : {}),
177
- functions: {
178
- canWriteFunctionName,
179
- createHandlerName,
180
- deleteHandlerName,
181
- listHandlerName,
182
- readHandlerName,
183
- updateHandlerName,
184
- },
185
- })
186
- : "";
187
- const controllerBootstrapSource = options.controllerClass
188
- ? `\t\t$controller_class = ${toPhpClassConstantReference(options.controllerClass)};
189
- \t\tif ( ! class_exists( $controller_class ) ) {
190
- \t\t\treturn;
191
- \t\t}
192
- \t\t$controller = new $controller_class();
193
-
194
- `
195
- : "";
196
- return `<?php
197
- if ( ! defined( 'ABSPATH' ) ) {
198
- \treturn;
199
- }
200
-
201
- if ( ! function_exists( '${getOptionNameFunctionName}' ) ) {
202
- \tfunction ${getOptionNameFunctionName}() {
203
- \t\treturn ${quotePhpString(`${phpPrefix}_${restResourcePhpId}_rest_resource_items`)};
204
- \t}
205
- }
206
-
207
- if ( ! function_exists( '${normalizeItemFunctionName}' ) ) {
208
- \tfunction ${normalizeItemFunctionName}( array $item ) {
209
- \t\treturn array(
210
- \t\t\t'id' => isset( $item['id'] ) ? (int) $item['id'] : 0,
211
- \t\t\t'title' => isset( $item['title'] ) ? (string) $item['title'] : '',
212
- \t\t\t'content' => isset( $item['content'] ) ? (string) $item['content'] : '',
213
- \t\t\t'status' => isset( $item['status'] ) && 'published' === $item['status'] ? 'published' : 'draft',
214
- \t\t\t'updatedAt' => isset( $item['updatedAt'] ) ? (string) $item['updatedAt'] : gmdate( 'c' ),
215
- \t\t);
216
- \t}
217
- }
218
-
219
- if ( ! function_exists( '${getItemsFunctionName}' ) ) {
220
- \tfunction ${getItemsFunctionName}() {
221
- \t\t$seed_items = array(
222
- \t\t\tarray(
223
- \t\t\t\t'id' => 1,
224
- \t\t\t\t'title' => ${quotePhpString(`${restResourceTitle} Starter`)},
225
- \t\t\t\t'content' => ${quotePhpString(`Replace this seeded ${restResourceTitle.toLowerCase()} content with your plugin data source.`)},
226
- \t\t\t\t'status' => 'draft',
227
- \t\t\t\t'updatedAt' => '2026-01-01T00:00:00Z',
228
- \t\t\t),
229
- \t\t);
230
- \t\t$items = get_option( ${getOptionNameFunctionName}(), $seed_items );
231
-
232
- \t\tif ( ! is_array( $items ) ) {
233
- \t\t\t$items = $seed_items;
234
- \t\t}
235
-
236
- \t\treturn array_values(
237
- \t\t\tarray_map(
238
- \t\t\t\t'${normalizeItemFunctionName}',
239
- \t\t\t\tarray_filter(
240
- \t\t\t\t\t$items,
241
- \t\t\t\t\t'is_array'
242
- \t\t\t\t)
243
- \t\t\t)
244
- \t\t);
245
- \t}
246
- }
247
-
248
- if ( ! function_exists( '${saveItemsFunctionName}' ) ) {
249
- \tfunction ${saveItemsFunctionName}( array $items ) {
250
- \t\tupdate_option(
251
- \t\t\t${getOptionNameFunctionName}(),
252
- \t\t\tarray_values(
253
- \t\t\t\tarray_map(
254
- \t\t\t\t\t'${normalizeItemFunctionName}',
255
- \t\t\t\t\t$items
256
- \t\t\t\t)
257
- \t\t\t),
258
- \t\t\tfalse
259
- \t\t);
260
- \t}
261
- }
262
-
263
- if ( ! function_exists( '${loadSchemaFunctionName}' ) ) {
264
- \tfunction ${loadSchemaFunctionName}( $schema_name ) {
265
- \t\t$project_root = dirname( __DIR__, 2 );
266
- \t\t$schema_path = $project_root . '/src/rest/${restResourceSlug}/api-schemas/' . $schema_name . '.schema.json';
267
- \t\tif ( ! file_exists( $schema_path ) ) {
268
- \t\t\treturn null;
269
- \t\t}
270
-
271
- \t\t$decoded = json_decode( file_get_contents( $schema_path ), true );
272
- \t\treturn is_array( $decoded ) ? $decoded : null;
273
- \t}
274
- }
275
-
276
- if ( ! function_exists( '${normalizeSchemaFunctionName}' ) ) {
277
- \tfunction ${normalizeSchemaFunctionName}( $schema ) {
278
- \t\tif ( ! is_array( $schema ) ) {
279
- \t\t\treturn $schema;
280
- \t\t}
281
-
282
- \t\tunset( $schema['$schema'], $schema['title'] );
283
-
284
- \t\tif ( isset( $schema['properties'] ) && is_array( $schema['properties'] ) ) {
285
- \t\t\tforeach ( $schema['properties'] as $key => $property_schema ) {
286
- \t\t\t\t$schema['properties'][ $key ] = ${normalizeSchemaFunctionName}( $property_schema );
287
- \t\t\t}
288
- \t\t}
289
-
290
- \t\tif ( isset( $schema['items'] ) && is_array( $schema['items'] ) ) {
291
- \t\t\t$schema['items'] = ${normalizeSchemaFunctionName}( $schema['items'] );
292
- \t\t}
293
-
294
- \t\treturn $schema;
295
- \t}
296
- }
297
-
298
- if ( ! function_exists( '${validatePayloadFunctionName}' ) ) {
299
- \tfunction ${validatePayloadFunctionName}( $value, $schema_name, $param_name ) {
300
- \t\t$schema = ${loadSchemaFunctionName}( $schema_name );
301
- \t\tif ( ! is_array( $schema ) ) {
302
- \t\t\treturn new WP_Error( 'missing_schema', 'Missing REST schema.', array( 'status' => 500 ) );
303
- \t\t}
304
-
305
- \t\t$rest_schema = ${normalizeSchemaFunctionName}( $schema );
306
- \t\t$validation = rest_validate_value_from_schema( $value, $rest_schema, $param_name );
307
- \t\tif ( is_wp_error( $validation ) ) {
308
- \t\t\treturn $validation;
309
- \t\t}
310
-
311
- \t\treturn rest_sanitize_value_from_schema( $value, $rest_schema, $param_name );
312
- \t}
313
- }
314
-
315
- if ( ! function_exists( '${canWriteFunctionName}' ) ) {
316
- \tfunction ${canWriteFunctionName}() {
317
- \t\treturn current_user_can( 'edit_posts' );
318
- \t}
319
- }
320
-
321
- if ( ! function_exists( '${listHandlerName}' ) ) {
322
- \tfunction ${listHandlerName}( WP_REST_Request $request ) {
323
- \t\t$payload_input = array();
324
- \t\t$page = $request->get_param( 'page' );
325
- \t\t$per_page = $request->get_param( 'perPage' );
326
- \t\t$search = $request->get_param( 'search' );
327
-
328
- \t\tif ( null !== $page ) {
329
- \t\t\t$payload_input['page'] = $page;
330
- \t\t}
331
- \t\tif ( null !== $per_page ) {
332
- \t\t\t$payload_input['perPage'] = $per_page;
333
- \t\t}
334
- \t\tif ( null !== $search ) {
335
- \t\t\t$payload_input['search'] = $search;
336
- \t\t}
337
-
338
- \t\t$payload = ${validatePayloadFunctionName}(
339
- \t\t\t$payload_input,
340
- \t\t\t'list-query',
341
- \t\t\t'query'
342
- \t\t);
343
-
344
- \t\tif ( is_wp_error( $payload ) ) {
345
- \t\t\treturn $payload;
346
- \t\t}
347
-
348
- \t\t$page = isset( $payload['page'] ) ? max( 1, (int) $payload['page'] ) : 1;
349
- \t\t$per_page = isset( $payload['perPage'] ) ? min( 50, max( 1, (int) $payload['perPage'] ) ) : 10;
350
- \t\t$search = isset( $payload['search'] ) ? strtolower( (string) $payload['search'] ) : '';
351
- \t\t$items = ${getItemsFunctionName}();
352
-
353
- \t\tif ( '' !== $search ) {
354
- \t\t\t$items = array_values(
355
- \t\t\t\tarray_filter(
356
- \t\t\t\t\t$items,
357
- \t\t\t\t\tstatic function ( $item ) use ( $search ) {
358
- \t\t\t\t\t\treturn false !== strpos( strtolower( (string) ( $item['title'] ?? '' ) ), $search ) ||
359
- \t\t\t\t\t\t\tfalse !== strpos( strtolower( (string) ( $item['content'] ?? '' ) ), $search );
360
- \t\t\t\t\t}
361
- \t\t\t\t)
362
- \t\t\t);
363
- \t\t}
364
-
365
- \t\t$total = count( $items );
366
- \t\t$items = array_slice( $items, ( $page - 1 ) * $per_page, $per_page );
367
-
368
- \t\treturn rest_ensure_response(
369
- \t\t\tarray(
370
- \t\t\t\t'items' => $items,
371
- \t\t\t\t'page' => $page,
372
- \t\t\t\t'perPage' => $per_page,
373
- \t\t\t\t'total' => $total,
374
- \t\t\t)
375
- \t\t);
376
- \t}
377
- }
378
-
379
- if ( ! function_exists( '${readHandlerName}' ) ) {
380
- \tfunction ${readHandlerName}( WP_REST_Request $request ) {
381
- \t\t$payload = ${validatePayloadFunctionName}(
382
- \t\t\tarray(
383
- \t\t\t\t'id' => $request->get_param( 'id' ),
384
- \t\t\t),
385
- \t\t\t'read-query',
386
- \t\t\t'query'
387
- \t\t);
388
-
389
- \t\tif ( is_wp_error( $payload ) ) {
390
- \t\t\treturn $payload;
391
- \t\t}
392
-
393
- \t\tforeach ( ${getItemsFunctionName}() as $item ) {
394
- \t\t\tif ( (int) $item['id'] === (int) $payload['id'] ) {
395
- \t\t\t\treturn rest_ensure_response( $item );
396
- \t\t\t}
397
- \t\t}
398
-
399
- \t\treturn new WP_Error( 'rest_not_found', 'Resource not found.', array( 'status' => 404 ) );
400
- \t}
401
- }
402
-
403
- if ( ! function_exists( '${createHandlerName}' ) ) {
404
- \tfunction ${createHandlerName}( WP_REST_Request $request ) {
405
- \t\t$payload = ${validatePayloadFunctionName}( $request->get_json_params(), 'create-request', 'body' );
406
- \t\tif ( is_wp_error( $payload ) ) {
407
- \t\t\treturn $payload;
408
- \t\t}
409
-
410
- \t\t$items = ${getItemsFunctionName}();
411
- \t\t$next_id = 1;
412
- \t\tforeach ( $items as $item ) {
413
- \t\t\t$next_id = max( $next_id, (int) $item['id'] + 1 );
414
- \t\t}
415
-
416
- \t\t$record = ${normalizeItemFunctionName}(
417
- \t\t\tarray(
418
- \t\t\t\t'id' => $next_id,
419
- \t\t\t\t'title' => (string) $payload['title'],
420
- \t\t\t\t'content' => isset( $payload['content'] ) ? (string) $payload['content'] : '',
421
- \t\t\t\t'status' => isset( $payload['status'] ) ? (string) $payload['status'] : 'draft',
422
- \t\t\t\t'updatedAt' => gmdate( 'c' ),
423
- \t\t\t)
424
- \t\t);
425
-
426
- \t\t$items[] = $record;
427
- \t\t${saveItemsFunctionName}( $items );
428
-
429
- \t\treturn rest_ensure_response( $record );
430
- \t}
431
- }
432
-
433
- if ( ! function_exists( '${updateHandlerName}' ) ) {
434
- \tfunction ${updateHandlerName}( WP_REST_Request $request ) {
435
- \t\t$query = ${validatePayloadFunctionName}(
436
- \t\t\tarray(
437
- \t\t\t\t'id' => $request->get_param( 'id' ),
438
- \t\t\t),
439
- \t\t\t'update-query',
440
- \t\t\t'query'
441
- \t\t);
442
- \t\tif ( is_wp_error( $query ) ) {
443
- \t\t\treturn $query;
444
- \t\t}
445
-
446
- \t\t$payload = ${validatePayloadFunctionName}( $request->get_json_params(), 'update-request', 'body' );
447
- \t\tif ( is_wp_error( $payload ) ) {
448
- \t\t\treturn $payload;
449
- \t\t}
450
-
451
- \t\t$items = ${getItemsFunctionName}();
452
- \t\tforeach ( $items as $index => $item ) {
453
- \t\t\tif ( (int) $item['id'] !== (int) $query['id'] ) {
454
- \t\t\t\tcontinue;
455
- \t\t\t}
456
-
457
- \t\t\t$items[ $index ] = ${normalizeItemFunctionName}(
458
- \t\t\t\tarray(
459
- \t\t\t\t\t'id' => $item['id'],
460
- \t\t\t\t\t'title' => isset( $payload['title'] ) ? (string) $payload['title'] : (string) $item['title'],
461
- \t\t\t\t\t'content' => array_key_exists( 'content', $payload ) ? (string) $payload['content'] : (string) $item['content'],
462
- \t\t\t\t\t'status' => isset( $payload['status'] ) ? (string) $payload['status'] : (string) $item['status'],
463
- \t\t\t\t\t'updatedAt' => gmdate( 'c' ),
464
- \t\t\t\t)
465
- \t\t\t);
466
-
467
- \t\t\t${saveItemsFunctionName}( $items );
468
- \t\t\treturn rest_ensure_response( $items[ $index ] );
469
- \t\t}
470
-
471
- \t\treturn new WP_Error( 'rest_not_found', 'Resource not found.', array( 'status' => 404 ) );
472
- \t}
473
- }
474
-
475
- if ( ! function_exists( '${deleteHandlerName}' ) ) {
476
- \tfunction ${deleteHandlerName}( WP_REST_Request $request ) {
477
- \t\t$query = ${validatePayloadFunctionName}(
478
- \t\t\tarray(
479
- \t\t\t\t'id' => $request->get_param( 'id' ),
480
- \t\t\t),
481
- \t\t\t'delete-query',
482
- \t\t\t'query'
483
- \t\t);
484
- \t\tif ( is_wp_error( $query ) ) {
485
- \t\t\treturn $query;
486
- \t\t}
487
-
488
- \t\t$items = ${getItemsFunctionName}();
489
- \t\t$filtered = array_values(
490
- \t\t\tarray_filter(
491
- \t\t\t\t$items,
492
- \t\t\t\tstatic function ( $item ) use ( $query ) {
493
- \t\t\t\t\treturn (int) $item['id'] !== (int) $query['id'];
494
- \t\t\t\t}
495
- \t\t\t)
496
- \t\t);
497
- \t\t$was_deleted = count( $filtered ) !== count( $items );
498
-
499
- \t\tif ( ! $was_deleted ) {
500
- \t\t\treturn new WP_Error( 'rest_not_found', 'Resource not found.', array( 'status' => 404 ) );
501
- \t\t}
502
-
503
- \t\t${saveItemsFunctionName}( $filtered );
504
-
505
- \t\treturn rest_ensure_response(
506
- \t\t\tarray(
507
- \t\t\t\t'deleted' => true,
508
- \t\t\t\t'id' => (int) $query['id'],
509
- \t\t\t)
510
- \t\t);
511
- \t}
512
- }
513
-
514
- ${controllerClassSource}
515
- if ( ! function_exists( '${registerRoutesFunctionName}' ) ) {
516
- \tfunction ${registerRoutesFunctionName}() {
517
- \t\t$namespace = ${quotePhpString(namespace)};
518
-
519
- ${controllerBootstrapSource}
520
- ${routeRegistrations}
521
- \t}
522
- }
523
-
524
- add_action( 'rest_api_init', '${registerRoutesFunctionName}' );
525
- `;
526
- }
527
6
  /**
528
7
  * Scaffold a workspace-level REST resource and synchronize its generated
529
8
  * TypeScript and PHP artifacts.
@@ -531,276 +10,42 @@ add_action( 'rest_api_init', '${registerRoutesFunctionName}' );
531
10
  * @param options Command options for the REST resource scaffold workflow.
532
11
  * @returns Resolved scaffold metadata for the created REST resource.
533
12
  */
534
- export async function runAddRestResourceCommand({ auth, bodyTypeName, controllerClass, controllerExtends, cwd = process.cwd(), manual, method, methods, namespace, permissionCallback, pathPattern, queryTypeName, restResourceName, responseTypeName, routePattern, secretFieldName, secretStateFieldName, }) {
13
+ export async function runAddRestResourceCommand({ auth, bodyTypeName, controllerClass, controllerExtends, cwd = process.cwd(), manual, method, methods, namespace, permissionCallback, pathPattern, queryTypeName, restResourceName, responseTypeName, routePattern, secretFieldName, secretHasValueFieldName, secretMaskedResponseFieldName, secretPreserveOnEmpty, secretStateFieldName, }) {
535
14
  const workspace = resolveWorkspaceProject(cwd);
536
15
  const restResourceSlug = assertValidGeneratedSlug("REST resource name", normalizeBlockSlug(restResourceName), "wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <list,read,create>]");
537
16
  const resolvedNamespace = resolveRestResourceNamespace(workspace.workspace.namespace, namespace);
538
17
  const inventory = await readWorkspaceInventoryAsync(workspace.projectDir);
539
18
  assertRestResourceDoesNotExist(workspace.projectDir, restResourceSlug, inventory);
540
- const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
541
- const syncRestScriptPath = path.join(workspace.projectDir, "scripts", "sync-rest-contracts.ts");
542
- const restResourceDir = path.join(workspace.projectDir, "src", "rest", restResourceSlug);
543
- const typesFilePath = path.join(restResourceDir, "api-types.ts");
544
- const validatorsFilePath = path.join(restResourceDir, "api-validators.ts");
545
- const apiFilePath = path.join(restResourceDir, "api.ts");
546
19
  if (manual) {
547
- if (controllerClass || controllerExtends || permissionCallback || routePattern) {
548
- throw new Error("Manual REST contracts do not generate PHP route glue. Use generated rest-resource mode for --route-pattern, --permission-callback, --controller-class, or --controller-extends.");
549
- }
550
- const pascalCase = toPascalCase(restResourceSlug);
551
- const resolvedAuth = assertValidManualRestContractAuth(auth);
552
- const resolvedMethod = assertValidManualRestContractHttpMethod(method);
553
- const resolvedPathPattern = resolveManualRestContractPathPattern(restResourceSlug, pathPattern);
554
- const resolvedQueryTypeName = assertValidTypeScriptIdentifier("Manual REST contract query type", queryTypeName ?? `${pascalCase}Query`, "wp-typia add rest-resource <name> --manual [--query-type <ExportedQueryType>]");
555
- const resolvedResponseTypeName = assertValidTypeScriptIdentifier("Manual REST contract response type", responseTypeName ?? `${pascalCase}Response`, "wp-typia add rest-resource <name> --manual [--response-type <ExportedResponseType>]");
556
- const defaultsToBody = bodyTypeName == null && ["PATCH", "POST", "PUT"].includes(resolvedMethod);
557
- const resolvedBodyTypeName = bodyTypeName != null || defaultsToBody
558
- ? assertValidTypeScriptIdentifier("Manual REST contract body type", bodyTypeName ?? `${pascalCase}Request`, "wp-typia add rest-resource <name> --manual [--body-type <ExportedBodyType>]")
559
- : undefined;
560
- if (resolvedMethod === "GET" && resolvedBodyTypeName) {
561
- throw new Error("Manual REST contract GET routes cannot define a body type. Remove --body-type or use POST, PUT, or PATCH.");
562
- }
563
- if (secretStateFieldName && !secretFieldName) {
564
- throw new Error("Manual REST contract --secret-state-field requires --secret-field.");
565
- }
566
- if (secretFieldName && !resolvedBodyTypeName) {
567
- throw new Error("Manual REST contract secret fields require a request body. Use POST, PUT, or PATCH so a request body is generated.");
568
- }
569
- const resolvedSecretFieldName = secretFieldName
570
- ? assertValidTypeScriptIdentifier("Manual REST contract secret field", secretFieldName, "wp-typia add rest-resource <name> --manual --method POST --secret-field <field>")
571
- : undefined;
572
- const resolvedSecretStateFieldName = resolvedSecretFieldName
573
- ? assertValidTypeScriptIdentifier("Manual REST contract secret state field", secretStateFieldName ?? `has${toPascalCase(resolvedSecretFieldName)}`, "wp-typia add rest-resource <name> --manual --method POST --secret-state-field <field>")
574
- : undefined;
575
- if (resolvedSecretFieldName &&
576
- MANUAL_REST_REQUEST_BODY_FIELD_NAMES.has(resolvedSecretFieldName)) {
577
- throw new Error(`Manual REST contract secret field must not reuse scaffolded request body fields: ${Array.from(MANUAL_REST_REQUEST_BODY_FIELD_NAMES).join(", ")}.`);
578
- }
579
- if (resolvedSecretStateFieldName &&
580
- MANUAL_REST_RESPONSE_FIELD_NAMES.has(resolvedSecretStateFieldName)) {
581
- throw new Error(`Manual REST contract secret state field must not reuse scaffolded response fields: ${Array.from(MANUAL_REST_RESPONSE_FIELD_NAMES).join(", ")}.`);
582
- }
583
- if (resolvedSecretFieldName &&
584
- resolvedSecretStateFieldName &&
585
- resolvedSecretFieldName === resolvedSecretStateFieldName) {
586
- throw new Error("Manual REST contract secret state field must be different from the raw secret field.");
587
- }
588
- const manualTypeNames = [
589
- resolvedQueryTypeName,
590
- resolvedResponseTypeName,
591
- resolvedBodyTypeName,
592
- ].filter((value) => value != null);
593
- const duplicateManualTypeNames = manualTypeNames.filter((name, index) => manualTypeNames.indexOf(name) !== index);
594
- if (duplicateManualTypeNames.length > 0) {
595
- throw new Error(`Manual REST contract type names must be unique: ${Array.from(new Set(duplicateManualTypeNames)).join(", ")}. Use distinct --query-type, --body-type, and --response-type values.`);
596
- }
597
- const mutationSnapshot = {
598
- fileSources: await snapshotWorkspaceFiles([
599
- blockConfigPath,
600
- syncRestScriptPath,
601
- ]),
602
- snapshotDirs: [],
603
- targetPaths: [restResourceDir],
604
- };
605
- try {
606
- await fsp.mkdir(restResourceDir, { recursive: true });
607
- await ensureRestResourceSyncScriptAnchors(workspace);
608
- await fsp.writeFile(typesFilePath, buildManualRestContractTypesSource({
609
- ...(resolvedBodyTypeName
610
- ? { bodyTypeName: resolvedBodyTypeName }
611
- : {}),
612
- queryTypeName: resolvedQueryTypeName,
613
- responseTypeName: resolvedResponseTypeName,
614
- restResourceSlug,
615
- ...(resolvedSecretFieldName
616
- ? { secretFieldName: resolvedSecretFieldName }
617
- : {}),
618
- ...(resolvedSecretStateFieldName
619
- ? { secretStateFieldName: resolvedSecretStateFieldName }
620
- : {}),
621
- }), "utf8");
622
- await fsp.writeFile(validatorsFilePath, buildManualRestContractValidatorsSource({
623
- ...(resolvedBodyTypeName
624
- ? { bodyTypeName: resolvedBodyTypeName }
625
- : {}),
626
- queryTypeName: resolvedQueryTypeName,
627
- responseTypeName: resolvedResponseTypeName,
628
- }), "utf8");
629
- await fsp.writeFile(apiFilePath, buildManualRestContractApiSource({
630
- ...(resolvedBodyTypeName
631
- ? { bodyTypeName: resolvedBodyTypeName }
632
- : {}),
633
- queryTypeName: resolvedQueryTypeName,
634
- restResourceSlug,
635
- }), "utf8");
636
- await syncManualRestContractArtifacts({
637
- clientFile: `src/rest/${restResourceSlug}/api-client.ts`,
638
- outputDir: restResourceDir,
639
- projectDir: workspace.projectDir,
640
- typesFile: `src/rest/${restResourceSlug}/api-types.ts`,
641
- validatorsFile: `src/rest/${restResourceSlug}/api-validators.ts`,
642
- variables: {
643
- auth: resolvedAuth,
644
- ...(resolvedBodyTypeName
645
- ? { bodyTypeName: resolvedBodyTypeName }
646
- : {}),
647
- method: resolvedMethod,
648
- namespace: resolvedNamespace,
649
- pascalCase,
650
- pathPattern: resolvedPathPattern,
651
- queryTypeName: resolvedQueryTypeName,
652
- responseTypeName: resolvedResponseTypeName,
653
- slugKebabCase: restResourceSlug,
654
- title: toTitleCase(restResourceSlug),
655
- },
656
- });
657
- await appendWorkspaceInventoryEntries(workspace.projectDir, {
658
- restResourceEntries: [
659
- buildManualRestContractConfigEntry({
660
- auth: resolvedAuth,
661
- ...(resolvedBodyTypeName
662
- ? { bodyTypeName: resolvedBodyTypeName }
663
- : {}),
664
- method: resolvedMethod,
665
- namespace: resolvedNamespace,
666
- pathPattern: resolvedPathPattern,
667
- queryTypeName: resolvedQueryTypeName,
668
- responseTypeName: resolvedResponseTypeName,
669
- restResourceSlug,
670
- ...(resolvedSecretFieldName
671
- ? { secretFieldName: resolvedSecretFieldName }
672
- : {}),
673
- ...(resolvedSecretStateFieldName
674
- ? { secretStateFieldName: resolvedSecretStateFieldName }
675
- : {}),
676
- }),
677
- ],
678
- transformSource: ensureBlockConfigCanAddRestManifests,
679
- });
680
- return {
681
- auth: resolvedAuth,
682
- ...(resolvedBodyTypeName
683
- ? { bodyTypeName: resolvedBodyTypeName }
684
- : {}),
685
- method: resolvedMethod,
686
- methods: [],
687
- mode: "manual",
688
- namespace: resolvedNamespace,
689
- pathPattern: resolvedPathPattern,
690
- projectDir: workspace.projectDir,
691
- queryTypeName: resolvedQueryTypeName,
692
- restResourceSlug,
693
- responseTypeName: resolvedResponseTypeName,
694
- ...(resolvedSecretFieldName
695
- ? { secretFieldName: resolvedSecretFieldName }
696
- : {}),
697
- ...(resolvedSecretStateFieldName
698
- ? { secretStateFieldName: resolvedSecretStateFieldName }
699
- : {}),
700
- };
701
- }
702
- catch (error) {
703
- await rollbackWorkspaceMutation(mutationSnapshot);
704
- throw error;
705
- }
706
- }
707
- const resolvedMethods = assertValidRestResourceMethods(methods);
708
- const resolvedRoutePattern = resolveGeneratedRestResourceRoutePattern(restResourceSlug, routePattern);
709
- const hasCustomRoutePattern = typeof routePattern === "string" && routePattern.trim().length > 0;
710
- const resolvedPermissionCallback = resolveOptionalPhpCallbackReference("Generated REST resource permission callback", permissionCallback);
711
- const resolvedControllerClass = resolveOptionalPhpClassReference("Generated REST resource controller class", controllerClass);
712
- const resolvedControllerExtends = resolveOptionalPhpClassReference("Generated REST resource controller base class", controllerExtends);
713
- if (resolvedControllerExtends && !resolvedControllerClass) {
714
- throw new Error("Generated REST resource controller base class requires --controller-class.");
715
- }
716
- const bootstrapPath = getWorkspaceBootstrapPath(workspace);
717
- const dataFilePath = path.join(restResourceDir, "data.ts");
718
- const phpFilePath = path.join(workspace.projectDir, "inc", "rest", `${restResourceSlug}.php`);
719
- const mutationSnapshot = {
720
- fileSources: await snapshotWorkspaceFiles([
721
- blockConfigPath,
722
- bootstrapPath,
723
- syncRestScriptPath,
724
- ]),
725
- snapshotDirs: [],
726
- targetPaths: [restResourceDir, phpFilePath],
727
- };
728
- try {
729
- await fsp.mkdir(restResourceDir, { recursive: true });
730
- await fsp.mkdir(path.dirname(phpFilePath), { recursive: true });
731
- await ensureRestResourceBootstrapAnchors(workspace);
732
- await ensureRestResourceSyncScriptAnchors(workspace);
733
- await fsp.writeFile(typesFilePath, buildRestResourceTypesSource(restResourceSlug, resolvedMethods), "utf8");
734
- await fsp.writeFile(validatorsFilePath, buildRestResourceValidatorsSource(restResourceSlug, resolvedMethods), "utf8");
735
- await fsp.writeFile(apiFilePath, buildRestResourceApiSource(restResourceSlug, resolvedMethods), "utf8");
736
- await fsp.writeFile(dataFilePath, buildRestResourceDataSource(restResourceSlug, resolvedMethods), "utf8");
737
- await fsp.writeFile(phpFilePath, buildRestResourcePhpSource(restResourceSlug, resolvedNamespace, workspace.workspace.phpPrefix, workspace.workspace.textDomain, resolvedMethods, {
738
- ...(resolvedControllerClass
739
- ? { controllerClass: resolvedControllerClass }
740
- : {}),
741
- ...(resolvedControllerExtends
742
- ? { controllerExtends: resolvedControllerExtends }
743
- : {}),
744
- ...(resolvedPermissionCallback
745
- ? { permissionCallback: resolvedPermissionCallback }
746
- : {}),
747
- routePattern: resolvedRoutePattern,
748
- }), "utf8");
749
- await syncRestResourceArtifacts({
750
- clientFile: `src/rest/${restResourceSlug}/api-client.ts`,
751
- methods: resolvedMethods,
752
- outputDir: restResourceDir,
753
- projectDir: workspace.projectDir,
754
- typesFile: `src/rest/${restResourceSlug}/api-types.ts`,
755
- validatorsFile: `src/rest/${restResourceSlug}/api-validators.ts`,
756
- variables: {
757
- namespace: resolvedNamespace,
758
- pascalCase: toPascalCase(restResourceSlug),
759
- ...(hasCustomRoutePattern ? { routePattern: resolvedRoutePattern } : {}),
760
- slugKebabCase: restResourceSlug,
761
- title: toTitleCase(restResourceSlug),
762
- },
763
- });
764
- await appendWorkspaceInventoryEntries(workspace.projectDir, {
765
- restResourceEntries: [
766
- buildRestResourceConfigEntry({
767
- ...(resolvedControllerClass
768
- ? { controllerClass: resolvedControllerClass }
769
- : {}),
770
- ...(resolvedControllerExtends
771
- ? { controllerExtends: resolvedControllerExtends }
772
- : {}),
773
- methods: resolvedMethods,
774
- namespace: resolvedNamespace,
775
- ...(resolvedPermissionCallback
776
- ? { permissionCallback: resolvedPermissionCallback }
777
- : {}),
778
- restResourceSlug,
779
- ...(hasCustomRoutePattern ? { routePattern: resolvedRoutePattern } : {}),
780
- }),
781
- ],
782
- transformSource: ensureBlockConfigCanAddRestManifests,
783
- });
784
- return {
785
- ...(resolvedControllerClass
786
- ? { controllerClass: resolvedControllerClass }
787
- : {}),
788
- ...(resolvedControllerExtends
789
- ? { controllerExtends: resolvedControllerExtends }
790
- : {}),
791
- methods: resolvedMethods,
792
- mode: "generated",
20
+ return scaffoldManualRestContract({
21
+ auth,
22
+ bodyTypeName,
23
+ controllerClass,
24
+ controllerExtends,
25
+ method,
793
26
  namespace: resolvedNamespace,
794
- ...(resolvedPermissionCallback
795
- ? { permissionCallback: resolvedPermissionCallback }
796
- : {}),
797
- projectDir: workspace.projectDir,
27
+ pathPattern,
28
+ permissionCallback,
29
+ queryTypeName,
30
+ responseTypeName,
798
31
  restResourceSlug,
799
- ...(hasCustomRoutePattern ? { routePattern: resolvedRoutePattern } : {}),
800
- };
801
- }
802
- catch (error) {
803
- await rollbackWorkspaceMutation(mutationSnapshot);
804
- throw error;
32
+ routePattern,
33
+ secretFieldName,
34
+ secretHasValueFieldName,
35
+ secretMaskedResponseFieldName,
36
+ secretPreserveOnEmpty,
37
+ secretStateFieldName,
38
+ workspace,
39
+ });
805
40
  }
41
+ return scaffoldGeneratedRestResource({
42
+ controllerClass,
43
+ controllerExtends,
44
+ methods,
45
+ namespace: resolvedNamespace,
46
+ permissionCallback,
47
+ restResourceSlug,
48
+ routePattern,
49
+ workspace,
50
+ });
806
51
  }