@wp-typia/project-tools 0.22.10 → 0.23.1

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 (155) 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/cli-add-block-json.js +5 -1
  4. package/dist/runtime/cli-add-collision.d.ts +25 -0
  5. package/dist/runtime/cli-add-collision.js +76 -0
  6. package/dist/runtime/cli-add-help.js +12 -2
  7. package/dist/runtime/cli-add-kind-ids.d.ts +1 -1
  8. package/dist/runtime/cli-add-kind-ids.js +3 -0
  9. package/dist/runtime/cli-add-types.d.ts +129 -0
  10. package/dist/runtime/cli-add-types.js +26 -0
  11. package/dist/runtime/cli-add-validation.d.ts +97 -1
  12. package/dist/runtime/cli-add-validation.js +313 -1
  13. package/dist/runtime/cli-add-workspace-ability-scaffold.js +4 -1
  14. package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +79 -20
  15. package/dist/runtime/cli-add-workspace-admin-view-source.js +11 -2
  16. package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.d.ts +34 -0
  17. package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.js +483 -0
  18. package/dist/runtime/cli-add-workspace-admin-view-templates-default.d.ts +30 -0
  19. package/dist/runtime/cli-add-workspace-admin-view-templates-default.js +310 -0
  20. package/dist/runtime/cli-add-workspace-admin-view-templates-rest.d.ts +25 -0
  21. package/dist/runtime/cli-add-workspace-admin-view-templates-rest.js +124 -0
  22. package/dist/runtime/cli-add-workspace-admin-view-templates-settings.d.ts +34 -0
  23. package/dist/runtime/cli-add-workspace-admin-view-templates-settings.js +370 -0
  24. package/dist/runtime/cli-add-workspace-admin-view-templates-shared.d.ts +49 -0
  25. package/dist/runtime/cli-add-workspace-admin-view-templates-shared.js +259 -0
  26. package/dist/runtime/cli-add-workspace-admin-view-templates.d.ts +19 -10
  27. package/dist/runtime/cli-add-workspace-admin-view-templates.js +31 -971
  28. package/dist/runtime/cli-add-workspace-admin-view-types.d.ts +21 -0
  29. package/dist/runtime/cli-add-workspace-admin-view-types.js +22 -0
  30. package/dist/runtime/cli-add-workspace-ai-anchors.js +125 -32
  31. package/dist/runtime/cli-add-workspace-ai-source-emitters.js +17 -1
  32. package/dist/runtime/cli-add-workspace-contract-source-emitters.d.ts +15 -0
  33. package/dist/runtime/cli-add-workspace-contract-source-emitters.js +42 -0
  34. package/dist/runtime/cli-add-workspace-contract.d.ts +15 -0
  35. package/dist/runtime/cli-add-workspace-contract.js +65 -0
  36. package/dist/runtime/cli-add-workspace-integration-env.d.ts +26 -0
  37. package/dist/runtime/cli-add-workspace-integration-env.js +428 -0
  38. package/dist/runtime/cli-add-workspace-post-meta-anchors.d.ts +23 -0
  39. package/dist/runtime/cli-add-workspace-post-meta-anchors.js +244 -0
  40. package/dist/runtime/cli-add-workspace-post-meta-source-emitters.d.ts +63 -0
  41. package/dist/runtime/cli-add-workspace-post-meta-source-emitters.js +179 -0
  42. package/dist/runtime/cli-add-workspace-post-meta.d.ts +15 -0
  43. package/dist/runtime/cli-add-workspace-post-meta.js +107 -0
  44. package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +9 -0
  45. package/dist/runtime/cli-add-workspace-rest-anchors.js +326 -21
  46. package/dist/runtime/cli-add-workspace-rest-generated.d.ts +9 -0
  47. package/dist/runtime/cli-add-workspace-rest-generated.js +158 -0
  48. package/dist/runtime/cli-add-workspace-rest-manual.d.ts +8 -0
  49. package/dist/runtime/cli-add-workspace-rest-manual.js +279 -0
  50. package/dist/runtime/cli-add-workspace-rest-php-templates.d.ts +24 -0
  51. package/dist/runtime/cli-add-workspace-rest-php-templates.js +678 -0
  52. package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +98 -2
  53. package/dist/runtime/cli-add-workspace-rest-source-emitters.js +323 -29
  54. package/dist/runtime/cli-add-workspace-rest-types.d.ts +108 -0
  55. package/dist/runtime/cli-add-workspace-rest-types.js +1 -0
  56. package/dist/runtime/cli-add-workspace-rest.d.ts +3 -7
  57. package/dist/runtime/cli-add-workspace-rest.js +34 -481
  58. package/dist/runtime/cli-add-workspace.d.ts +15 -0
  59. package/dist/runtime/cli-add-workspace.js +15 -0
  60. package/dist/runtime/cli-add.d.ts +1 -1
  61. package/dist/runtime/cli-add.js +1 -1
  62. package/dist/runtime/cli-core.d.ts +3 -2
  63. package/dist/runtime/cli-core.js +3 -2
  64. package/dist/runtime/cli-diagnostics.d.ts +3 -1
  65. package/dist/runtime/cli-diagnostics.js +17 -5
  66. package/dist/runtime/cli-doctor-environment.js +1 -3
  67. package/dist/runtime/cli-doctor-workspace-bindings.js +4 -1
  68. package/dist/runtime/cli-doctor-workspace-block-addons.d.ts +12 -0
  69. package/dist/runtime/cli-doctor-workspace-block-addons.js +134 -0
  70. package/dist/runtime/cli-doctor-workspace-block-iframe.d.ts +9 -0
  71. package/dist/runtime/cli-doctor-workspace-block-iframe.js +228 -0
  72. package/dist/runtime/cli-doctor-workspace-block-metadata.d.ts +11 -0
  73. package/dist/runtime/cli-doctor-workspace-block-metadata.js +111 -0
  74. package/dist/runtime/cli-doctor-workspace-blocks.js +6 -424
  75. package/dist/runtime/cli-doctor-workspace-features-abilities.d.ts +11 -0
  76. package/dist/runtime/cli-doctor-workspace-features-abilities.js +112 -0
  77. package/dist/runtime/cli-doctor-workspace-features-admin-views.d.ts +11 -0
  78. package/dist/runtime/cli-doctor-workspace-features-admin-views.js +128 -0
  79. package/dist/runtime/cli-doctor-workspace-features-ai.d.ts +11 -0
  80. package/dist/runtime/cli-doctor-workspace-features-ai.js +57 -0
  81. package/dist/runtime/cli-doctor-workspace-features-editor-plugins.d.ts +11 -0
  82. package/dist/runtime/cli-doctor-workspace-features-editor-plugins.js +80 -0
  83. package/dist/runtime/cli-doctor-workspace-features-post-meta.d.ts +11 -0
  84. package/dist/runtime/cli-doctor-workspace-features-post-meta.js +77 -0
  85. package/dist/runtime/cli-doctor-workspace-features-rest.d.ts +11 -0
  86. package/dist/runtime/cli-doctor-workspace-features-rest.js +120 -0
  87. package/dist/runtime/cli-doctor-workspace-features.js +14 -369
  88. package/dist/runtime/cli-doctor-workspace-package.d.ts +25 -3
  89. package/dist/runtime/cli-doctor-workspace-package.js +35 -13
  90. package/dist/runtime/cli-doctor-workspace-shared.d.ts +2 -0
  91. package/dist/runtime/cli-doctor-workspace-shared.js +2 -0
  92. package/dist/runtime/cli-doctor-workspace.js +8 -3
  93. package/dist/runtime/cli-doctor.d.ts +52 -3
  94. package/dist/runtime/cli-doctor.js +79 -8
  95. package/dist/runtime/cli-help.js +10 -0
  96. package/dist/runtime/cli-init-package-json.js +4 -2
  97. package/dist/runtime/cli-init-templates.js +11 -1
  98. package/dist/runtime/cli-prompt.d.ts +16 -2
  99. package/dist/runtime/cli-prompt.js +29 -12
  100. package/dist/runtime/cli-scaffold.d.ts +2 -1
  101. package/dist/runtime/cli-scaffold.js +19 -10
  102. package/dist/runtime/contract-artifacts.d.ts +14 -0
  103. package/dist/runtime/contract-artifacts.js +15 -0
  104. package/dist/runtime/external-template-guards.js +4 -6
  105. package/dist/runtime/index.d.ts +2 -2
  106. package/dist/runtime/index.js +1 -1
  107. package/dist/runtime/json-utils.d.ts +62 -4
  108. package/dist/runtime/json-utils.js +78 -4
  109. package/dist/runtime/local-dev-presets.js +4 -1
  110. package/dist/runtime/migration-ui-capability.js +4 -1
  111. package/dist/runtime/migration-utils.js +4 -1
  112. package/dist/runtime/package-managers.js +6 -1
  113. package/dist/runtime/package-versions.js +6 -1
  114. package/dist/runtime/rest-resource-artifacts.d.ts +57 -1
  115. package/dist/runtime/rest-resource-artifacts.js +97 -1
  116. package/dist/runtime/scaffold-bootstrap.js +7 -2
  117. package/dist/runtime/scaffold-package-manager-files.js +5 -1
  118. package/dist/runtime/scaffold-repository-reference.js +4 -2
  119. package/dist/runtime/scaffold-template-variables.js +2 -1
  120. package/dist/runtime/scaffold.d.ts +18 -1
  121. package/dist/runtime/scaffold.js +55 -2
  122. package/dist/runtime/temp-roots.js +4 -1
  123. package/dist/runtime/template-layers.js +4 -1
  124. package/dist/runtime/template-registry.js +9 -3
  125. package/dist/runtime/template-render.d.ts +1 -1
  126. package/dist/runtime/template-render.js +1 -1
  127. package/dist/runtime/template-source-cache-markers.d.ts +37 -0
  128. package/dist/runtime/template-source-cache-markers.js +125 -0
  129. package/dist/runtime/template-source-cache.d.ts +1 -4
  130. package/dist/runtime/template-source-cache.js +16 -122
  131. package/dist/runtime/template-source-contracts.d.ts +2 -0
  132. package/dist/runtime/template-source-external.d.ts +4 -2
  133. package/dist/runtime/template-source-external.js +4 -2
  134. package/dist/runtime/template-source-normalization.js +2 -1
  135. package/dist/runtime/template-source-remote.d.ts +8 -4
  136. package/dist/runtime/template-source-remote.js +26 -9
  137. package/dist/runtime/template-source-seeds.js +10 -3
  138. package/dist/runtime/workspace-inventory-mutations.js +54 -4
  139. package/dist/runtime/workspace-inventory-parser-entries.d.ts +17 -0
  140. package/dist/runtime/workspace-inventory-parser-entries.js +157 -0
  141. package/dist/runtime/workspace-inventory-parser-validation.d.ts +104 -0
  142. package/dist/runtime/workspace-inventory-parser-validation.js +34 -0
  143. package/dist/runtime/workspace-inventory-parser.d.ts +3 -44
  144. package/dist/runtime/workspace-inventory-parser.js +7 -464
  145. package/dist/runtime/workspace-inventory-read.d.ts +9 -2
  146. package/dist/runtime/workspace-inventory-read.js +9 -2
  147. package/dist/runtime/workspace-inventory-section-descriptors.d.ts +19 -0
  148. package/dist/runtime/workspace-inventory-section-descriptors.js +435 -0
  149. package/dist/runtime/workspace-inventory-templates.d.ts +16 -1
  150. package/dist/runtime/workspace-inventory-templates.js +75 -4
  151. package/dist/runtime/workspace-inventory-types.d.ts +52 -2
  152. package/dist/runtime/workspace-inventory.d.ts +2 -2
  153. package/dist/runtime/workspace-inventory.js +1 -1
  154. package/dist/runtime/workspace-project.js +4 -6
  155. package/package.json +2 -2
@@ -0,0 +1,244 @@
1
+ import path from "node:path";
2
+ import { getWorkspaceBootstrapPath, patchFile, } from "./cli-add-shared.js";
3
+ import { appendPhpSnippetBeforeClosingTag, insertPhpSnippetBeforeWorkspaceAnchors, } from "./cli-add-workspace-mutation.js";
4
+ import { hasPhpFunctionDefinition } from "./php-utils.js";
5
+ const POST_META_SERVER_GLOB = "/inc/post-meta/*.php";
6
+ const NO_RESOURCES_GUARD_PATTERN = /if \(\s*restBlocks\.length === 0(?:\s*&&\s*standaloneContracts\.length === 0)?(?:\s*&&\s*postMetaContracts\.length === 0)?(?:\s*&&\s*restResources\.length === 0)?(?:\s*&&\s*aiFeatures\.length === 0)?\s*\) \{[\s\S]*?\n\t\treturn;\n\t\}/u;
7
+ /**
8
+ * Ensure the workspace bootstrap loads generated post-meta PHP modules.
9
+ *
10
+ * Inserts the generated loader function, appends its `init` hook, and rejects
11
+ * incompatible existing loader functions that omit the generated post-meta glob.
12
+ *
13
+ * @param workspace Resolved official workspace metadata and paths.
14
+ * @returns A promise that resolves after the bootstrap has been patched.
15
+ * @throws {Error} When the bootstrap cannot be read, written, or safely patched.
16
+ */
17
+ export async function ensurePostMetaBootstrapAnchors(workspace) {
18
+ const bootstrapPath = getWorkspaceBootstrapPath(workspace);
19
+ await patchFile(bootstrapPath, (source) => {
20
+ let nextSource = source;
21
+ const registerFunctionName = `${workspace.workspace.phpPrefix}_register_post_meta_contracts`;
22
+ const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
23
+ const registerFunction = `
24
+
25
+ function ${registerFunctionName}() {
26
+ \tforeach ( glob( __DIR__ . '${POST_META_SERVER_GLOB}' ) ?: array() as $post_meta_module ) {
27
+ \t\trequire_once $post_meta_module;
28
+ \t}
29
+ }
30
+ `;
31
+ if (!hasPhpFunctionDefinition(nextSource, registerFunctionName)) {
32
+ nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, registerFunction);
33
+ }
34
+ else if (!nextSource.includes(POST_META_SERVER_GLOB)) {
35
+ throw new Error([
36
+ `Unable to patch ${path.basename(bootstrapPath)} in ensurePostMetaBootstrapAnchors.`,
37
+ `The existing ${registerFunctionName}() definition does not include ${POST_META_SERVER_GLOB}.`,
38
+ "Restore the generated bootstrap shape or wire the post-meta loader manually before retrying.",
39
+ ].join(" "));
40
+ }
41
+ if (!nextSource.includes(registerHook)) {
42
+ nextSource = appendPhpSnippetBeforeClosingTag(nextSource, registerHook);
43
+ }
44
+ return nextSource;
45
+ });
46
+ }
47
+ function getSyncRestPatchErrorMessage(syncRestScriptPath, anchorDescription) {
48
+ return [
49
+ `ensurePostMetaSyncScriptAnchors could not patch ${path.basename(syncRestScriptPath)}.`,
50
+ `Missing expected ${anchorDescription} anchor in scripts/sync-rest-contracts.ts.`,
51
+ "Restore the generated template or add the POST_META wiring manually before retrying.",
52
+ ].join(" ");
53
+ }
54
+ function replaceBlockConfigImportForPostMeta(nextSource, syncRestScriptPath) {
55
+ const importPatterns = [
56
+ /^import\s*\{\n(?:\t[^\n]*\n)+\} from ["']\.\/block-config["'];?$/mu,
57
+ /^import\s*\{[^\n]*\}\s*from\s*["']\.\/block-config["'];?$/mu,
58
+ ];
59
+ const importMatch = importPatterns.map((pattern) => pattern.exec(nextSource)).find(Boolean) ??
60
+ null;
61
+ if (!importMatch) {
62
+ throw new Error(getSyncRestPatchErrorMessage(syncRestScriptPath, "block-config import"));
63
+ }
64
+ const importSource = importMatch[0];
65
+ if (importSource.includes("POST_META") &&
66
+ importSource.includes("WorkspacePostMetaConfig")) {
67
+ return nextSource;
68
+ }
69
+ if (!importSource.includes("BLOCKS") ||
70
+ !importSource.includes("WorkspaceBlockConfig")) {
71
+ throw new Error(getSyncRestPatchErrorMessage(syncRestScriptPath, "BLOCKS import"));
72
+ }
73
+ const hasAiFeatures = importSource.includes("AI_FEATURES");
74
+ const hasAiFeatureConfig = importSource.includes("WorkspaceAiFeatureConfig");
75
+ const hasContracts = importSource.includes("CONTRACTS");
76
+ const hasContractConfig = importSource.includes("WorkspaceContractConfig");
77
+ const hasRestResources = importSource.includes("REST_RESOURCES");
78
+ const hasRestResourceConfig = importSource.includes("WorkspaceRestResourceConfig");
79
+ const replacement = [
80
+ "import {",
81
+ ...(hasAiFeatures ? ["\tAI_FEATURES,"] : []),
82
+ "\tBLOCKS,",
83
+ ...(hasContracts ? ["\tCONTRACTS,"] : []),
84
+ "\tPOST_META,",
85
+ ...(hasRestResources ? ["\tREST_RESOURCES,"] : []),
86
+ ...(hasAiFeatureConfig ? ["\ttype WorkspaceAiFeatureConfig,"] : []),
87
+ "\ttype WorkspaceBlockConfig,",
88
+ ...(hasContractConfig ? ["\ttype WorkspaceContractConfig,"] : []),
89
+ "\ttype WorkspacePostMetaConfig,",
90
+ ...(hasRestResourceConfig ? ["\ttype WorkspaceRestResourceConfig,"] : []),
91
+ "} from './block-config';",
92
+ ].join("\n");
93
+ return nextSource.replace(importSource, replacement);
94
+ }
95
+ function replaceRequiredSyncRestSource(nextSource, target, anchor, replacement, anchorDescription, syncRestScriptPath) {
96
+ if (nextSource.includes(target)) {
97
+ return nextSource;
98
+ }
99
+ const hasAnchor = typeof anchor === "string" ? nextSource.includes(anchor) : anchor.test(nextSource);
100
+ if (!hasAnchor) {
101
+ throw new Error(getSyncRestPatchErrorMessage(syncRestScriptPath, anchorDescription));
102
+ }
103
+ return nextSource.replace(anchor, replacement);
104
+ }
105
+ function replaceAllOccurrences(source, searchValue, replacement) {
106
+ return source.split(searchValue).join(replacement);
107
+ }
108
+ function buildPostMetaNoResourcesGuard({ hasAiFeatures, hasRestResources, }) {
109
+ const condition = [
110
+ "restBlocks.length === 0 &&",
111
+ "standaloneContracts.length === 0 &&",
112
+ "postMetaContracts.length === 0",
113
+ ];
114
+ if (hasRestResources) {
115
+ condition[condition.length - 1] = `${condition[condition.length - 1]} &&`;
116
+ condition.push("restResources.length === 0");
117
+ }
118
+ if (hasAiFeatures) {
119
+ condition[condition.length - 1] = `${condition[condition.length - 1]} &&`;
120
+ condition.push("aiFeatures.length === 0");
121
+ }
122
+ const noResourcesSubject = [
123
+ "REST-enabled workspace blocks",
124
+ "standalone contracts",
125
+ "post meta contracts",
126
+ ...(hasRestResources ? ["plugin-level REST resources"] : []),
127
+ ...(hasAiFeatures ? ["AI features"] : []),
128
+ ].join(", ");
129
+ return [
130
+ "if (",
131
+ ...condition.map((line) => `\t\t${line}`),
132
+ "\t) {",
133
+ "\t\tconsole.log(",
134
+ "\t\t\toptions.check",
135
+ `\t\t\t\t? 'ℹ️ No ${noResourcesSubject} are registered yet. \`sync-rest --check\` is already clean.'`,
136
+ `\t\t\t\t: 'ℹ️ No ${noResourcesSubject} are registered yet.'`,
137
+ "\t\t);",
138
+ "\t\treturn;",
139
+ "\t}",
140
+ ].join("\n");
141
+ }
142
+ function replaceNoResourcesGuard(nextSource, replacement, syncRestScriptPath) {
143
+ if (!NO_RESOURCES_GUARD_PATTERN.test(nextSource)) {
144
+ throw new Error(getSyncRestPatchErrorMessage(syncRestScriptPath, "no-resources guard"));
145
+ }
146
+ return nextSource.replace(NO_RESOURCES_GUARD_PATTERN, replacement);
147
+ }
148
+ function insertPostMetaFilter(nextSource, syncRestScriptPath) {
149
+ if (nextSource.includes("const postMetaContracts = POST_META.filter")) {
150
+ return nextSource;
151
+ }
152
+ const restResourcesFilter = "const restResources = REST_RESOURCES.filter( isWorkspaceRestResource );";
153
+ if (nextSource.includes(restResourcesFilter)) {
154
+ return nextSource.replace(restResourcesFilter, [
155
+ "const postMetaContracts = POST_META.filter( isWorkspacePostMetaContract );",
156
+ restResourcesFilter,
157
+ ].join("\n\t"));
158
+ }
159
+ const standaloneContractsFilter = "const standaloneContracts = CONTRACTS.filter( isWorkspaceStandaloneContract );";
160
+ return replaceRequiredSyncRestSource(nextSource, "const postMetaContracts = POST_META.filter", standaloneContractsFilter, [
161
+ standaloneContractsFilter,
162
+ "const postMetaContracts = POST_META.filter( isWorkspacePostMetaContract );",
163
+ ].join("\n\t"), "standaloneContracts filter", syncRestScriptPath);
164
+ }
165
+ function insertPostMetaNoResourcesGuard(nextSource, syncRestScriptPath) {
166
+ const hasRestResources = nextSource.includes("const restResources = REST_RESOURCES.filter( isWorkspaceRestResource );");
167
+ const hasAiFeatures = nextSource.includes("const aiFeatures = AI_FEATURES.filter( isWorkspaceAiFeature );");
168
+ return replaceNoResourcesGuard(nextSource, buildPostMetaNoResourcesGuard({
169
+ hasAiFeatures,
170
+ hasRestResources,
171
+ }), syncRestScriptPath);
172
+ }
173
+ function insertPostMetaSyncLoop(nextSource, syncRestScriptPath) {
174
+ if (nextSource.includes("for ( const postMeta of postMetaContracts )")) {
175
+ return nextSource;
176
+ }
177
+ const loopSource = [
178
+ "\tfor ( const postMeta of postMetaContracts ) {",
179
+ "\t\tawait syncTypeSchemas(",
180
+ "\t\t\t{",
181
+ "\t\t\t\tjsonSchemaFile: postMeta.schemaFile,",
182
+ "\t\t\t\tsourceTypeName: postMeta.sourceTypeName,",
183
+ "\t\t\t\ttypesFile: postMeta.typesFile,",
184
+ "\t\t\t},",
185
+ "\t\t\t{",
186
+ "\t\t\t\tcheck: options.check,",
187
+ "\t\t\t}",
188
+ "\t\t);",
189
+ "\t}",
190
+ ].join("\n");
191
+ const resourceLoopAnchor = "\n\tfor ( const resource of restResources ) {";
192
+ if (nextSource.includes(resourceLoopAnchor)) {
193
+ return nextSource.replace(resourceLoopAnchor, `\n${loopSource}\n${resourceLoopAnchor}`);
194
+ }
195
+ const consoleLogPattern = /\n\tconsole\.log\(\n\t\toptions\.check/u;
196
+ return replaceRequiredSyncRestSource(nextSource, "for ( const postMeta of postMetaContracts )", consoleLogPattern, [
197
+ "",
198
+ loopSource,
199
+ "",
200
+ "\tconsole.log(",
201
+ "\t\toptions.check",
202
+ ].join("\n"), "success log insertion point", syncRestScriptPath);
203
+ }
204
+ /**
205
+ * Ensure `scripts/sync-rest-contracts.ts` handles post-meta schema contracts.
206
+ *
207
+ * Patches inventory imports, type guards, no-resource checks, sync loops, and
208
+ * success copy so generated post-meta schemas participate in REST contract sync.
209
+ *
210
+ * @param workspace Resolved official workspace metadata and paths.
211
+ * @returns A promise that resolves after the sync script has been patched.
212
+ * @throws {Error} When expected anchors are missing or file IO fails.
213
+ */
214
+ export async function ensurePostMetaSyncScriptAnchors(workspace) {
215
+ const syncRestScriptPath = path.join(workspace.projectDir, "scripts", "sync-rest-contracts.ts");
216
+ await patchFile(syncRestScriptPath, (source) => {
217
+ let nextSource = replaceBlockConfigImportForPostMeta(source, syncRestScriptPath);
218
+ const helperInsertionAnchor = "async function assertTypeArtifactsCurrent";
219
+ nextSource = replaceRequiredSyncRestSource(nextSource, "function isWorkspacePostMetaContract(", helperInsertionAnchor, [
220
+ "function isWorkspacePostMetaContract(",
221
+ "\tpostMeta: WorkspacePostMetaConfig",
222
+ "): postMeta is WorkspacePostMetaConfig & {",
223
+ "\tschemaFile: string;",
224
+ "\tsourceTypeName: string;",
225
+ "\ttypesFile: string;",
226
+ "} {",
227
+ "\treturn (",
228
+ "\t\ttypeof postMeta.schemaFile === 'string' &&",
229
+ "\t\ttypeof postMeta.sourceTypeName === 'string' &&",
230
+ "\t\ttypeof postMeta.typesFile === 'string'",
231
+ "\t);",
232
+ "}",
233
+ "",
234
+ "async function assertTypeArtifactsCurrent",
235
+ ].join("\n"), "type artifact assertion helper", syncRestScriptPath);
236
+ nextSource = insertPostMetaFilter(nextSource, syncRestScriptPath);
237
+ nextSource = insertPostMetaNoResourcesGuard(nextSource, syncRestScriptPath);
238
+ nextSource = insertPostMetaSyncLoop(nextSource, syncRestScriptPath);
239
+ nextSource = replaceAllOccurrences(nextSource, "REST contract schemas, standalone schemas, portable API clients, and endpoint-aware OpenAPI documents", "REST contract schemas, standalone schemas, post meta schemas, portable API clients, and endpoint-aware OpenAPI documents");
240
+ nextSource = replaceAllOccurrences(nextSource, "workspace blocks, standalone contracts, and plugin-level resources", "workspace blocks, standalone contracts, post meta contracts, and plugin-level resources");
241
+ nextSource = replaceAllOccurrences(nextSource, "workspace blocks, standalone contracts, or plugin-level REST resources", "workspace blocks, standalone contracts, post meta contracts, or plugin-level REST resources");
242
+ return nextSource;
243
+ });
244
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Render one `POST_META` inventory entry for `scripts/block-config.ts`.
3
+ *
4
+ * @param options Metadata used to render the inventory entry.
5
+ * @param options.metaKey WordPress meta key registered by the generated PHP.
6
+ * @param options.postMetaSlug Stable post-meta contract slug.
7
+ * @param options.postType WordPress post type scoped to the meta key.
8
+ * @param options.showInRest Whether the meta key is exposed to REST/editor APIs.
9
+ * @param options.sourceTypeName Exported TypeScript interface name.
10
+ * @returns Formatted TypeScript object literal for the `POST_META` array.
11
+ */
12
+ export declare function buildPostMetaConfigEntry(options: {
13
+ metaKey: string;
14
+ postMetaSlug: string;
15
+ postType: string;
16
+ showInRest: boolean;
17
+ sourceTypeName: string;
18
+ }): string;
19
+ /**
20
+ * Render a starter TypeScript post-meta contract.
21
+ *
22
+ * @param postMetaSlug Stable post-meta contract slug used to build the title.
23
+ * @param sourceTypeName Exported TypeScript interface name to emit.
24
+ * @returns Generated TypeScript source for the post-meta contract.
25
+ */
26
+ export declare function buildPostMetaTypesSource(postMetaSlug: string, sourceTypeName: string): string;
27
+ /**
28
+ * Render smoke-test guidance beside the generated TypeScript contract.
29
+ *
30
+ * @param options Metadata used to render the README content.
31
+ * @param options.metaKey WordPress meta key registered by the generated PHP.
32
+ * @param options.postMetaSlug Stable post-meta contract slug.
33
+ * @param options.postType WordPress post type scoped to the meta key.
34
+ * @param options.sourceTypeName Exported TypeScript interface name.
35
+ * @returns Generated README content for the post-meta contract directory.
36
+ */
37
+ export declare function buildPostMetaReadmeSource(options: {
38
+ metaKey: string;
39
+ postMetaSlug: string;
40
+ postType: string;
41
+ sourceTypeName: string;
42
+ }): string;
43
+ /**
44
+ * Render the PHP module loaded by the workspace bootstrap to register the post
45
+ * meta key with the schema generated from TypeScript.
46
+ *
47
+ * @param options Metadata used to render the PHP module.
48
+ * @param options.metaKey WordPress meta key to register.
49
+ * @param options.phpPrefix Workspace PHP function prefix.
50
+ * @param options.postMetaSlug Stable post-meta contract slug.
51
+ * @param options.postType WordPress post type scoped to the meta key.
52
+ * @param options.showInRest Whether `register_post_meta()` exposes REST schema.
53
+ * @param options.textDomain Workspace text domain used for descriptions.
54
+ * @returns Generated PHP source for registering the post-meta contract.
55
+ */
56
+ export declare function buildPostMetaPhpSource(options: {
57
+ metaKey: string;
58
+ phpPrefix: string;
59
+ postMetaSlug: string;
60
+ postType: string;
61
+ showInRest: boolean;
62
+ textDomain: string;
63
+ }): string;
@@ -0,0 +1,179 @@
1
+ import { quoteTsString } from "./cli-add-shared.js";
2
+ import { quotePhpString } from "./php-utils.js";
3
+ import { toTitleCase } from "./string-case.js";
4
+ /**
5
+ * Render one `POST_META` inventory entry for `scripts/block-config.ts`.
6
+ *
7
+ * @param options Metadata used to render the inventory entry.
8
+ * @param options.metaKey WordPress meta key registered by the generated PHP.
9
+ * @param options.postMetaSlug Stable post-meta contract slug.
10
+ * @param options.postType WordPress post type scoped to the meta key.
11
+ * @param options.showInRest Whether the meta key is exposed to REST/editor APIs.
12
+ * @param options.sourceTypeName Exported TypeScript interface name.
13
+ * @returns Formatted TypeScript object literal for the `POST_META` array.
14
+ */
15
+ export function buildPostMetaConfigEntry(options) {
16
+ return [
17
+ "\t{",
18
+ `\t\tmetaKey: ${quoteTsString(options.metaKey)},`,
19
+ `\t\tphpFile: ${quoteTsString(`inc/post-meta/${options.postMetaSlug}.php`)},`,
20
+ `\t\tpostType: ${quoteTsString(options.postType)},`,
21
+ `\t\tschemaFile: ${quoteTsString(`src/post-meta/${options.postMetaSlug}/meta.schema.json`)},`,
22
+ `\t\tshowInRest: ${options.showInRest ? "true" : "false"},`,
23
+ `\t\tslug: ${quoteTsString(options.postMetaSlug)},`,
24
+ `\t\tsourceTypeName: ${quoteTsString(options.sourceTypeName)},`,
25
+ `\t\ttypesFile: ${quoteTsString(`src/post-meta/${options.postMetaSlug}/types.ts`)},`,
26
+ "\t},",
27
+ ].join("\n");
28
+ }
29
+ /**
30
+ * Render a starter TypeScript post-meta contract.
31
+ *
32
+ * @param postMetaSlug Stable post-meta contract slug used to build the title.
33
+ * @param sourceTypeName Exported TypeScript interface name to emit.
34
+ * @returns Generated TypeScript source for the post-meta contract.
35
+ */
36
+ export function buildPostMetaTypesSource(postMetaSlug, sourceTypeName) {
37
+ const title = toTitleCase(postMetaSlug);
38
+ return `/**
39
+ * ${title} is the source of truth for a generated WordPress post meta contract.
40
+ *
41
+ * Edit this interface, then run \`wp-typia sync-rest --check\` to verify the
42
+ * JSON Schema artifact and PHP registration stay aligned.
43
+ */
44
+ export interface ${sourceTypeName} {
45
+ \tenabled: boolean;
46
+ \tstatus: 'draft' | 'ready';
47
+ \tupdatedAt: string;
48
+ \tnotes?: string;
49
+ }
50
+ `;
51
+ }
52
+ /**
53
+ * Render smoke-test guidance beside the generated TypeScript contract.
54
+ *
55
+ * @param options Metadata used to render the README content.
56
+ * @param options.metaKey WordPress meta key registered by the generated PHP.
57
+ * @param options.postMetaSlug Stable post-meta contract slug.
58
+ * @param options.postType WordPress post type scoped to the meta key.
59
+ * @param options.sourceTypeName Exported TypeScript interface name.
60
+ * @returns Generated README content for the post-meta contract directory.
61
+ */
62
+ export function buildPostMetaReadmeSource(options) {
63
+ const title = toTitleCase(options.postMetaSlug);
64
+ return `# ${title} Post Meta Contract
65
+
66
+ \`${options.sourceTypeName}\` in \`types.ts\` defines the shape for
67
+ \`${options.metaKey}\` on the \`${options.postType}\` post type.
68
+
69
+ After editing the TypeScript interface, run:
70
+
71
+ \`\`\`bash
72
+ wp-typia sync-rest --check
73
+ \`\`\`
74
+
75
+ If the check reports stale artifacts, run \`wp-typia sync-rest\` and commit the
76
+ updated \`meta.schema.json\`. For an end-to-end smoke test in WordPress, create
77
+ or update a \`${options.postType}\` post and confirm the REST response exposes
78
+ the meta key only when \`showInRest\` is enabled in \`scripts/block-config.ts\`.
79
+ `;
80
+ }
81
+ /**
82
+ * Render the PHP module loaded by the workspace bootstrap to register the post
83
+ * meta key with the schema generated from TypeScript.
84
+ *
85
+ * @param options Metadata used to render the PHP module.
86
+ * @param options.metaKey WordPress meta key to register.
87
+ * @param options.phpPrefix Workspace PHP function prefix.
88
+ * @param options.postMetaSlug Stable post-meta contract slug.
89
+ * @param options.postType WordPress post type scoped to the meta key.
90
+ * @param options.showInRest Whether `register_post_meta()` exposes REST schema.
91
+ * @param options.textDomain Workspace text domain used for descriptions.
92
+ * @returns Generated PHP source for registering the post-meta contract.
93
+ */
94
+ export function buildPostMetaPhpSource(options) {
95
+ const postMetaTitle = toTitleCase(options.postMetaSlug);
96
+ const postMetaPhpId = options.postMetaSlug.replace(/-/gu, "_");
97
+ const functionPrefix = `${options.phpPrefix}_${postMetaPhpId}`;
98
+ const loadSchemaFunctionName = `${functionPrefix}_load_post_meta_schema`;
99
+ const normalizeSchemaFunctionName = `${functionPrefix}_normalize_post_meta_schema`;
100
+ const authFunctionName = `${functionPrefix}_can_edit_post_meta`;
101
+ const registerFunctionName = `${functionPrefix}_register_post_meta`;
102
+ const showInRestSource = options.showInRest
103
+ ? `array(
104
+ \t\t\t'schema' => ${loadSchemaFunctionName}(),
105
+ \t\t)`
106
+ : "false";
107
+ return `<?php
108
+ /**
109
+ * Registers the ${postMetaTitle} post meta contract.
110
+ *
111
+ * The REST schema is generated from src/post-meta/${options.postMetaSlug}/types.ts.
112
+ *
113
+ * @package ${options.phpPrefix}
114
+ */
115
+
116
+ if ( ! defined( 'ABSPATH' ) ) {
117
+ \texit;
118
+ }
119
+
120
+ if ( ! function_exists( '${normalizeSchemaFunctionName}' ) ) {
121
+ \tfunction ${normalizeSchemaFunctionName}( $schema ) {
122
+ \t\tif ( ! is_array( $schema ) ) {
123
+ \t\t\treturn array(
124
+ \t\t\t\t'type' => 'object',
125
+ \t\t\t\t'properties' => array(),
126
+ \t\t\t);
127
+ \t\t}
128
+
129
+ \t\tunset( $schema['$schema'], $schema['$id'], $schema['title'] );
130
+
131
+ \t\tif ( empty( $schema['type'] ) ) {
132
+ \t\t\t$schema['type'] = 'object';
133
+ \t\t}
134
+
135
+ \t\treturn $schema;
136
+ \t}
137
+ }
138
+
139
+ if ( ! function_exists( '${loadSchemaFunctionName}' ) ) {
140
+ \tfunction ${loadSchemaFunctionName}() {
141
+ \t\t$schema_file = dirname( __DIR__, 2 ) . '/src/post-meta/${options.postMetaSlug}/meta.schema.json';
142
+
143
+ \t\tif ( ! file_exists( $schema_file ) ) {
144
+ \t\t\treturn ${normalizeSchemaFunctionName}( array() );
145
+ \t\t}
146
+
147
+ \t\t$schema = json_decode( (string) file_get_contents( $schema_file ), true );
148
+
149
+ \t\treturn ${normalizeSchemaFunctionName}( $schema );
150
+ \t}
151
+ }
152
+
153
+ if ( ! function_exists( '${authFunctionName}' ) ) {
154
+ \tfunction ${authFunctionName}( $allowed, $meta_key, $post_id, $user_id, $cap, $caps ) {
155
+ \t\tunset( $allowed, $meta_key, $cap, $caps );
156
+
157
+ \t\treturn user_can( $user_id, 'edit_post', $post_id );
158
+ \t}
159
+ }
160
+
161
+ if ( ! function_exists( '${registerFunctionName}' ) ) {
162
+ \tfunction ${registerFunctionName}() {
163
+ \t\tregister_post_meta(
164
+ \t\t\t${quotePhpString(options.postType)},
165
+ \t\t\t${quotePhpString(options.metaKey)},
166
+ \t\t\tarray(
167
+ \t\t\t\t'auth_callback' => ${quotePhpString(authFunctionName)},
168
+ \t\t\t\t'description' => __( ${quotePhpString(`${postMetaTitle} typed post meta contract.`)}, ${quotePhpString(options.textDomain)} ),
169
+ \t\t\t\t'show_in_rest' => ${showInRestSource},
170
+ \t\t\t\t'single' => true,
171
+ \t\t\t\t'type' => 'object',
172
+ \t\t\t)
173
+ \t\t);
174
+ \t}
175
+ }
176
+
177
+ ${registerFunctionName}();
178
+ `;
179
+ }
@@ -0,0 +1,15 @@
1
+ import { type RunAddPostMetaCommandOptions } from "./cli-add-shared.js";
2
+ /**
3
+ * Scaffold a typed WordPress post-meta contract and matching PHP registration.
4
+ */
5
+ export declare function runAddPostMetaCommand({ cwd, hideFromRest, metaKey, postMetaName, postType, typeName, }: RunAddPostMetaCommandOptions): Promise<{
6
+ metaKey: string;
7
+ phpFile: string;
8
+ postMetaSlug: string;
9
+ postType: string;
10
+ projectDir: string;
11
+ schemaFile: string;
12
+ showInRest: boolean;
13
+ sourceTypeName: string;
14
+ typesFile: string;
15
+ }>;
@@ -0,0 +1,107 @@
1
+ import { promises as fsp } from "node:fs";
2
+ import path from "node:path";
3
+ import { pathExists } from "./fs-async.js";
4
+ import { assertPostMetaDoesNotExist, assertValidGeneratedSlug, assertValidPostMetaPostType, assertValidTypeScriptIdentifier, getWorkspaceBootstrapPath, normalizeBlockSlug, resolvePostMetaKey, } from "./cli-add-shared.js";
5
+ import { ensureContractSyncScriptAnchors } from "./cli-add-workspace-rest-anchors.js";
6
+ import { ensurePostMetaBootstrapAnchors, ensurePostMetaSyncScriptAnchors, } from "./cli-add-workspace-post-meta-anchors.js";
7
+ import { buildPostMetaConfigEntry, buildPostMetaPhpSource, buildPostMetaReadmeSource, buildPostMetaTypesSource, } from "./cli-add-workspace-post-meta-source-emitters.js";
8
+ import { executeWorkspaceMutationPlan } from "./cli-add-workspace-mutation.js";
9
+ import { syncStandaloneContractArtifacts } from "./contract-artifacts.js";
10
+ import { appendWorkspaceInventoryEntries, readWorkspaceInventoryAsync, } from "./workspace-inventory.js";
11
+ import { resolveWorkspaceProject } from "./workspace-project.js";
12
+ import { toPascalCase } from "./string-case.js";
13
+ const ADD_POST_META_USAGE = "wp-typia add post-meta <name> --post-type <post-type> [--type <ExportedTypeName>] [--meta-key <meta-key>] [--hide-from-rest]";
14
+ /**
15
+ * Scaffold a typed WordPress post-meta contract and matching PHP registration.
16
+ */
17
+ export async function runAddPostMetaCommand({ cwd = process.cwd(), hideFromRest, metaKey, postMetaName, postType, typeName, }) {
18
+ const workspace = resolveWorkspaceProject(cwd);
19
+ const postMetaSlug = assertValidGeneratedSlug("Post meta name", normalizeBlockSlug(postMetaName), ADD_POST_META_USAGE);
20
+ const sourceTypeName = assertValidTypeScriptIdentifier("Post meta type", typeName ?? `${toPascalCase(postMetaSlug)}Meta`, ADD_POST_META_USAGE);
21
+ const resolvedPostType = assertValidPostMetaPostType(postType);
22
+ const resolvedMetaKey = resolvePostMetaKey({
23
+ metaKey,
24
+ phpPrefix: workspace.workspace.phpPrefix,
25
+ slug: postMetaSlug,
26
+ });
27
+ const showInRest = !hideFromRest;
28
+ const inventory = await readWorkspaceInventoryAsync(workspace.projectDir);
29
+ assertPostMetaDoesNotExist(workspace.projectDir, postMetaSlug, resolvedPostType, resolvedMetaKey, inventory);
30
+ const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
31
+ const syncRestScriptPath = path.join(workspace.projectDir, "scripts", "sync-rest-contracts.ts");
32
+ const bootstrapPath = getWorkspaceBootstrapPath(workspace);
33
+ const postMetaRoot = path.join(workspace.projectDir, "src", "post-meta");
34
+ const postMetaDir = path.join(postMetaRoot, postMetaSlug);
35
+ const postMetaIncRoot = path.join(workspace.projectDir, "inc", "post-meta");
36
+ const typesFile = `src/post-meta/${postMetaSlug}/types.ts`;
37
+ const schemaFile = `src/post-meta/${postMetaSlug}/meta.schema.json`;
38
+ const phpFile = `inc/post-meta/${postMetaSlug}.php`;
39
+ const readmeFile = `src/post-meta/${postMetaSlug}/README.md`;
40
+ const typesFilePath = path.join(workspace.projectDir, typesFile);
41
+ const schemaFilePath = path.join(workspace.projectDir, schemaFile);
42
+ const phpFilePath = path.join(workspace.projectDir, phpFile);
43
+ const readmeFilePath = path.join(workspace.projectDir, readmeFile);
44
+ const postMetaRootExisted = await pathExists(postMetaRoot);
45
+ const postMetaIncRootExisted = await pathExists(postMetaIncRoot);
46
+ return executeWorkspaceMutationPlan({
47
+ filePaths: [blockConfigPath, bootstrapPath, syncRestScriptPath],
48
+ targetPaths: [
49
+ typesFilePath,
50
+ schemaFilePath,
51
+ phpFilePath,
52
+ readmeFilePath,
53
+ ...(postMetaRootExisted ? [] : [postMetaRoot]),
54
+ ...(postMetaIncRootExisted ? [] : [postMetaIncRoot]),
55
+ ],
56
+ run: async () => {
57
+ await fsp.mkdir(postMetaDir, { recursive: true });
58
+ await fsp.mkdir(postMetaIncRoot, { recursive: true });
59
+ await ensureContractSyncScriptAnchors(workspace);
60
+ await ensurePostMetaSyncScriptAnchors(workspace);
61
+ await ensurePostMetaBootstrapAnchors(workspace);
62
+ await fsp.writeFile(typesFilePath, buildPostMetaTypesSource(postMetaSlug, sourceTypeName), "utf8");
63
+ await syncStandaloneContractArtifacts({
64
+ projectDir: workspace.projectDir,
65
+ schemaFile,
66
+ sourceTypeName,
67
+ typesFile,
68
+ });
69
+ await fsp.writeFile(phpFilePath, buildPostMetaPhpSource({
70
+ metaKey: resolvedMetaKey,
71
+ phpPrefix: workspace.workspace.phpPrefix,
72
+ postMetaSlug,
73
+ postType: resolvedPostType,
74
+ showInRest,
75
+ textDomain: workspace.workspace.textDomain,
76
+ }), "utf8");
77
+ await fsp.writeFile(readmeFilePath, buildPostMetaReadmeSource({
78
+ metaKey: resolvedMetaKey,
79
+ postMetaSlug,
80
+ postType: resolvedPostType,
81
+ sourceTypeName,
82
+ }), "utf8");
83
+ await appendWorkspaceInventoryEntries(workspace.projectDir, {
84
+ postMetaEntries: [
85
+ buildPostMetaConfigEntry({
86
+ metaKey: resolvedMetaKey,
87
+ postMetaSlug,
88
+ postType: resolvedPostType,
89
+ showInRest,
90
+ sourceTypeName,
91
+ }),
92
+ ],
93
+ });
94
+ return {
95
+ metaKey: resolvedMetaKey,
96
+ phpFile,
97
+ postMetaSlug,
98
+ postType: resolvedPostType,
99
+ projectDir: workspace.projectDir,
100
+ schemaFile,
101
+ showInRest,
102
+ sourceTypeName,
103
+ typesFile,
104
+ };
105
+ },
106
+ });
107
+ }
@@ -1,3 +1,12 @@
1
1
  import type { WorkspaceProject } from "./workspace-project.js";
2
+ /**
3
+ * Ensure the workspace bootstrap loads the shared REST schema helper file.
4
+ *
5
+ * @param workspace Resolved workspace project metadata and PHP prefix.
6
+ * @returns A promise that resolves after the bootstrap is patched.
7
+ * @throws When an existing loader does not reference `inc/rest-schema.php`.
8
+ */
9
+ export declare function ensureRestSchemaHelperBootstrapAnchors(workspace: WorkspaceProject): Promise<void>;
2
10
  export declare function ensureRestResourceBootstrapAnchors(workspace: WorkspaceProject): Promise<void>;
11
+ export declare function ensureContractSyncScriptAnchors(workspace: WorkspaceProject): Promise<void>;
3
12
  export declare function ensureRestResourceSyncScriptAnchors(workspace: WorkspaceProject): Promise<void>;