@wp-typia/project-tools 0.18.0 → 0.19.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 (62) hide show
  1. package/dist/runtime/alternate-render-targets.d.ts +5 -0
  2. package/dist/runtime/alternate-render-targets.js +29 -0
  3. package/dist/runtime/block-generator-service-core.d.ts +1 -1
  4. package/dist/runtime/block-generator-service-core.js +11 -7
  5. package/dist/runtime/block-generator-service-spec.d.ts +8 -1
  6. package/dist/runtime/block-generator-service-spec.js +248 -2
  7. package/dist/runtime/built-in-block-artifacts.js +3 -1
  8. package/dist/runtime/built-in-block-code-artifacts.js +3 -1
  9. package/dist/runtime/built-in-block-code-templates/compound-child.d.ts +1 -1
  10. package/dist/runtime/built-in-block-code-templates/compound-child.js +14 -9
  11. package/dist/runtime/built-in-block-code-templates/compound-parent.d.ts +2 -2
  12. package/dist/runtime/built-in-block-code-templates/compound-parent.js +100 -43
  13. package/dist/runtime/built-in-block-code-templates/compound-persistence.d.ts +1 -1
  14. package/dist/runtime/built-in-block-code-templates/compound-persistence.js +11 -8
  15. package/dist/runtime/built-in-block-non-ts-artifacts.js +505 -2
  16. package/dist/runtime/cli-add-block.d.ts +4 -1
  17. package/dist/runtime/cli-add-block.js +66 -31
  18. package/dist/runtime/cli-add-shared.d.ts +3 -1
  19. package/dist/runtime/cli-add-shared.js +12 -12
  20. package/dist/runtime/cli-core.d.ts +2 -0
  21. package/dist/runtime/cli-core.js +1 -0
  22. package/dist/runtime/cli-diagnostics.d.ts +26 -0
  23. package/dist/runtime/cli-diagnostics.js +107 -0
  24. package/dist/runtime/cli-doctor-workspace.js +4 -4
  25. package/dist/runtime/cli-help.js +4 -3
  26. package/dist/runtime/cli-scaffold.d.ts +3 -1
  27. package/dist/runtime/cli-scaffold.js +91 -15
  28. package/dist/runtime/cli-templates.js +26 -1
  29. package/dist/runtime/cli-validation.d.ts +66 -0
  30. package/dist/runtime/cli-validation.js +92 -0
  31. package/dist/runtime/compound-inner-blocks.d.ts +78 -0
  32. package/dist/runtime/compound-inner-blocks.js +88 -0
  33. package/dist/runtime/index.d.ts +5 -1
  34. package/dist/runtime/index.js +3 -1
  35. package/dist/runtime/migration-command-surface.js +2 -0
  36. package/dist/runtime/package-versions.d.ts +1 -0
  37. package/dist/runtime/package-versions.js +12 -0
  38. package/dist/runtime/scaffold-answer-resolution.js +10 -6
  39. package/dist/runtime/scaffold-bootstrap.js +5 -1
  40. package/dist/runtime/scaffold-documents.js +29 -6
  41. package/dist/runtime/scaffold-identifiers.d.ts +17 -0
  42. package/dist/runtime/scaffold-identifiers.js +22 -0
  43. package/dist/runtime/scaffold-onboarding.js +21 -13
  44. package/dist/runtime/scaffold-template-variable-groups.d.ts +154 -0
  45. package/dist/runtime/scaffold-template-variable-groups.js +13 -0
  46. package/dist/runtime/scaffold-template-variables.js +80 -1
  47. package/dist/runtime/scaffold.d.ts +21 -2
  48. package/dist/runtime/scaffold.js +12 -5
  49. package/dist/runtime/temp-roots.d.ts +44 -0
  50. package/dist/runtime/temp-roots.js +129 -0
  51. package/dist/runtime/template-builtins.js +4 -6
  52. package/dist/runtime/template-registry.d.ts +8 -0
  53. package/dist/runtime/template-registry.js +34 -1
  54. package/dist/runtime/template-source-external.d.ts +1 -0
  55. package/dist/runtime/template-source-external.js +4 -7
  56. package/dist/runtime/template-source-remote.js +44 -23
  57. package/dist/runtime/template-source-seeds.js +3 -9
  58. package/dist/runtime/template-source.d.ts +2 -3
  59. package/dist/runtime/template-source.js +13 -5
  60. package/dist/runtime/workspace-project.js +1 -1
  61. package/package.json +12 -2
  62. package/templates/_shared/compound/core/scripts/add-compound-child.ts.mustache +318 -18
@@ -1,6 +1,5 @@
1
1
  import fs from "node:fs";
2
2
  import { promises as fsp } from "node:fs";
3
- import os from "node:os";
4
3
  import path from "node:path";
5
4
  import { syncBlockMetadata, } from "@wp-typia/block-runtime/metadata-core";
6
5
  import { ensureMigrationDirectories, parseMigrationConfig, writeInitialMigrationScaffold, writeMigrationConfig, } from "./migration-project.js";
@@ -9,14 +8,19 @@ import { snapshotProjectVersion } from "./migrations.js";
9
8
  import { getDefaultAnswers, scaffoldProject } from "./scaffold.js";
10
9
  import { copyInterpolatedDirectory, listInterpolatedDirectoryOutputs, } from "./template-render.js";
11
10
  import { appendWorkspaceInventoryEntries, } from "./workspace-inventory.js";
11
+ import { createManagedTempRoot } from "./temp-roots.js";
12
12
  import { resolveWorkspaceProject, } from "./workspace-project.js";
13
- import { ADD_BLOCK_TEMPLATE_IDS, buildWorkspacePhpPrefix, isAddBlockTemplateId, normalizeBlockSlug, patchFile, readOptionalFile, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
13
+ import { ADD_BLOCK_TEMPLATE_IDS, buildWorkspacePhpPrefix, isAddBlockTemplateId, patchFile, readOptionalFile, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
14
+ import { resolveNonEmptyNormalizedBlockSlug, } from "./scaffold-identifiers.js";
14
15
  import { buildConfigEntries, buildMigrationBlocks, buildServerTemplateRoot, } from "./cli-add-block-config.js";
15
16
  import { COMPOUND_SHARED_SUPPORT_FILES, collectLegacyCompoundValidatorPaths, ensureBlockConfigCanAddRestManifests, ensureCompoundWorkspaceSupportFiles, } from "./cli-add-block-legacy-validator.js";
16
17
  import { parseTemplateLocator, resolveTemplateSeed, } from "./template-source.js";
17
18
  import { resolveExternalTemplateLayers, } from "./template-layers.js";
18
19
  import { formatInstallCommand, } from "./package-managers.js";
20
+ import { parseCompoundInnerBlocksPreset } from "./compound-inner-blocks.js";
19
21
  import { resolveOptionalInteractiveExternalLayerId, } from "./external-layer-selection.js";
22
+ import { parseAlternateRenderTargets } from "./alternate-render-targets.js";
23
+ import { assertExternalLayerCompositionOptions, normalizeOptionalCliString, resolveLocalCliPathOption, } from "./cli-validation.js";
20
24
  const COLLECTION_IMPORT_LINE = "import '../../collection';";
21
25
  // This is a lightweight preflight heuristic for the common install layouts:
22
26
  // node_modules for npm/pnpm/bun/yarn-classic and Yarn PnP marker files.
@@ -61,21 +65,6 @@ async function renderWorkspacePersistenceServerModule(projectDir, variables) {
61
65
  const templateDir = buildServerTemplateRoot(variables.persistencePolicy);
62
66
  await copyInterpolatedDirectory(templateDir, targetDir, variables);
63
67
  }
64
- function normalizeExternalLayerOption(value) {
65
- if (typeof value !== "string") {
66
- return undefined;
67
- }
68
- const trimmed = value.trim();
69
- return trimmed.length > 0 ? trimmed : undefined;
70
- }
71
- function resolveExternalLayerSourceFromCaller(source, callerCwd) {
72
- if (typeof source !== "string" ||
73
- source.length === 0 ||
74
- !(path.isAbsolute(source) || source.startsWith("./") || source.startsWith("../"))) {
75
- return source;
76
- }
77
- return path.resolve(callerCwd, source);
78
- }
79
68
  function hasInstalledWorkspaceDependencies(projectDir) {
80
69
  return WORKSPACE_INSTALL_MARKERS.some((marker) => fs.existsSync(path.join(projectDir, marker)));
81
70
  }
@@ -207,12 +196,14 @@ async function syncWorkspaceAddedBlockArtifacts(projectDir, templateId, variable
207
196
  }
208
197
  }
209
198
  function assertPersistenceFlagsAllowed(templateId, options) {
210
- const hasPersistenceFlags = typeof options.dataStorageMode === "string" ||
199
+ const hasPersistenceFlags = typeof options.alternateRenderTargets === "string" ||
200
+ typeof options.dataStorageMode === "string" ||
211
201
  typeof options.persistencePolicy === "string";
212
202
  if (!hasPersistenceFlags) {
213
203
  return;
214
204
  }
215
205
  if (templateId === "persistence" || templateId === "compound") {
206
+ parseAlternateRenderTargets(options.alternateRenderTargets);
216
207
  if (typeof options.dataStorageMode === "string" &&
217
208
  options.dataStorageMode !== "custom-table" &&
218
209
  options.dataStorageMode !== "post-meta") {
@@ -223,9 +214,24 @@ function assertPersistenceFlagsAllowed(templateId, options) {
223
214
  options.persistencePolicy !== "public") {
224
215
  throw new Error(`Unsupported persistence policy "${options.persistencePolicy}". Expected one of: authenticated, public.`);
225
216
  }
217
+ if (templateId === "compound" &&
218
+ typeof options.alternateRenderTargets === "string" &&
219
+ !options.dataStorageMode &&
220
+ !options.persistencePolicy) {
221
+ throw new Error("`--alternate-render-targets` on `wp-typia add block --template compound` requires the persistence-enabled server render path. Add `--data-storage <post-meta|custom-table>` or `--persistence-policy <authenticated|public>` first.");
222
+ }
226
223
  return;
227
224
  }
228
- throw new Error(`--data-storage and --persistence-policy are supported only for \`wp-typia add block --template persistence\` or \`--template compound\`.`);
225
+ throw new Error(`--data-storage, --persistence-policy, and --alternate-render-targets are supported only for \`wp-typia add block --template persistence\` or persistence-enabled \`--template compound\`.`);
226
+ }
227
+ function assertCompoundInnerBlocksPresetAllowed(templateId, innerBlocksPreset) {
228
+ if (!innerBlocksPreset) {
229
+ return;
230
+ }
231
+ if (templateId !== "compound") {
232
+ throw new Error("`--inner-blocks-preset` is supported only for `wp-typia add block --template compound`.");
233
+ }
234
+ parseCompoundInnerBlocksPreset(innerBlocksPreset);
229
235
  }
230
236
  /**
231
237
  * Seeds an empty official workspace migration project before any blocks are added.
@@ -255,6 +261,9 @@ export async function seedWorkspaceMigrationProject(projectDir, currentMigration
255
261
  * workspace. Defaults to `process.cwd()`.
256
262
  * @param options.dataStorageMode Optional storage mode for persistence-capable
257
263
  * templates.
264
+ * @param options.innerBlocksPreset Optional compound-only InnerBlocks preset
265
+ * (`freeform`, `ordered`, `horizontal`, or `locked-structure`) that controls
266
+ * the generated authoring defaults for nested compound containers.
258
267
  * @param options.persistencePolicy Optional persistence policy for
259
268
  * persistence-capable templates.
260
269
  * @param options.templateId Built-in block family to scaffold. Defaults to
@@ -267,16 +276,33 @@ export async function seedWorkspaceMigrationProject(projectDir, currentMigration
267
276
  * workspace dependencies have not been installed yet, or target block paths
268
277
  * already exist.
269
278
  */
270
- export async function runAddBlockCommand({ blockName, cwd = process.cwd(), dataStorageMode, externalLayerId, externalLayerSource, persistencePolicy, selectExternalLayerId, templateId = "basic", }) {
279
+ export async function runAddBlockCommand({ alternateRenderTargets, blockName, cwd = process.cwd(), dataStorageMode, externalLayerId, externalLayerSource, innerBlocksPreset, persistencePolicy, selectExternalLayerId, templateId = "basic", }) {
280
+ if (templateId === "query-loop") {
281
+ throw new Error("`wp-typia add block --template query-loop` is not supported. Query Loop is a create-time `core/query` variation scaffold, so use `wp-typia create <project-dir> --template query-loop` instead.");
282
+ }
271
283
  if (!isAddBlockTemplateId(templateId)) {
272
284
  throw new Error(`Unknown add-block template "${templateId}". Expected one of: ${ADD_BLOCK_TEMPLATE_IDS.join(", ")}`);
273
285
  }
274
286
  const resolvedTemplateId = templateId;
275
- assertPersistenceFlagsAllowed(resolvedTemplateId, { dataStorageMode, persistencePolicy });
287
+ assertPersistenceFlagsAllowed(resolvedTemplateId, {
288
+ alternateRenderTargets,
289
+ dataStorageMode,
290
+ persistencePolicy,
291
+ });
292
+ assertCompoundInnerBlocksPresetAllowed(resolvedTemplateId, innerBlocksPreset);
293
+ const resolvedInnerBlocksPreset = parseCompoundInnerBlocksPreset(innerBlocksPreset);
276
294
  const workspace = resolveWorkspaceProject(cwd);
277
295
  assertWorkspaceDependenciesInstalled(workspace);
278
- const normalizedExternalLayerId = normalizeExternalLayerOption(externalLayerId);
279
- const normalizedExternalLayerSource = resolveExternalLayerSourceFromCaller(normalizeExternalLayerOption(externalLayerSource), cwd);
296
+ const normalizedExternalLayerId = normalizeOptionalCliString(externalLayerId);
297
+ const normalizedExternalLayerSource = resolveLocalCliPathOption({
298
+ cwd,
299
+ label: "--external-layer-source",
300
+ value: externalLayerSource,
301
+ });
302
+ assertExternalLayerCompositionOptions({
303
+ externalLayerId: normalizedExternalLayerId,
304
+ externalLayerSource: normalizedExternalLayerSource,
305
+ });
280
306
  const resolvedExternalLayerSelection = await resolveOptionalInteractiveExternalLayerId({
281
307
  callerCwd: cwd,
282
308
  externalLayerId: normalizedExternalLayerId,
@@ -284,13 +310,18 @@ export async function runAddBlockCommand({ blockName, cwd = process.cwd(), dataS
284
310
  selectExternalLayerId,
285
311
  });
286
312
  let tempRoot = "";
313
+ let cleanupTempRoot;
287
314
  try {
288
- const normalizedSlug = normalizeBlockSlug(blockName);
289
- if (!normalizedSlug) {
290
- throw new Error("Block name is required. Use `wp-typia add block <name> --template <family>`.");
291
- }
315
+ const normalizedSlug = resolveNonEmptyNormalizedBlockSlug({
316
+ input: blockName,
317
+ label: "Block name",
318
+ usage: "wp-typia add block <name> --template <family>",
319
+ });
292
320
  const defaults = getDefaultAnswers(normalizedSlug, resolvedTemplateId);
293
- tempRoot = await fsp.mkdtemp(path.join(os.tmpdir(), "wp-typia-add-block-"));
321
+ ({
322
+ path: tempRoot,
323
+ cleanup: cleanupTempRoot,
324
+ } = await createManagedTempRoot("wp-typia-add-block-"));
294
325
  const tempProjectDir = path.join(tempRoot, normalizedSlug);
295
326
  const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
296
327
  const migrationConfigPath = path.join(workspace.projectDir, "src", "migrations", "config.ts");
@@ -305,9 +336,11 @@ export async function runAddBlockCommand({ blockName, cwd = process.cwd(), dataS
305
336
  : [];
306
337
  const result = await (async () => {
307
338
  const scaffoldResult = await scaffoldProject({
339
+ alternateRenderTargets,
308
340
  answers: {
309
341
  ...defaults,
310
342
  author: workspace.author,
343
+ compoundInnerBlocksPreset: resolvedInnerBlocksPreset,
311
344
  namespace: workspace.workspace.namespace,
312
345
  phpPrefix: blockPhpPrefix,
313
346
  slug: normalizedSlug,
@@ -368,9 +401,11 @@ export async function runAddBlockCommand({ blockName, cwd = process.cwd(), dataS
368
401
  }
369
402
  }
370
403
  finally {
371
- await resolvedExternalLayerSelection.cleanup?.();
372
- if (tempRoot) {
373
- await fsp.rm(tempRoot, { force: true, recursive: true });
404
+ try {
405
+ await resolvedExternalLayerSelection.cleanup?.();
406
+ }
407
+ finally {
408
+ await cleanupTempRoot?.();
374
409
  }
375
410
  }
376
411
  }
@@ -1,6 +1,7 @@
1
1
  import { type HookedBlockPositionId } from "./hooked-blocks.js";
2
2
  import { type WorkspaceInventory } from "./workspace-inventory.js";
3
3
  import { type WorkspaceProject } from "./workspace-project.js";
4
+ export { normalizeBlockSlug, } from "./scaffold-identifiers.js";
4
5
  /**
5
6
  * Supported top-level `wp-typia add` kinds exposed by the canonical CLI.
6
7
  */
@@ -63,11 +64,13 @@ export interface RunAddEditorPluginCommandOptions {
63
64
  slot?: string;
64
65
  }
65
66
  export interface RunAddBlockCommandOptions {
67
+ alternateRenderTargets?: string;
66
68
  blockName: string;
67
69
  cwd?: string;
68
70
  dataStorageMode?: string;
69
71
  externalLayerId?: string;
70
72
  externalLayerSource?: string;
73
+ innerBlocksPreset?: string;
71
74
  persistencePolicy?: string;
72
75
  selectExternalLayerId?: (options: Array<{
73
76
  description?: string;
@@ -89,7 +92,6 @@ export interface WorkspaceMutationSnapshot {
89
92
  /** Files or directories created by the mutation that should be removed on rollback. */
90
93
  targetPaths: string[];
91
94
  }
92
- export declare function normalizeBlockSlug(input: string): string;
93
95
  export declare function assertValidGeneratedSlug(label: string, slug: string, usage: string): string;
94
96
  export declare function assertValidRestResourceNamespace(namespace: string): string;
95
97
  export declare function resolveRestResourceNamespace(workspaceNamespace: string, namespace?: string): string;
@@ -3,8 +3,9 @@ import { promises as fsp } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
5
5
  import { HOOKED_BLOCK_ANCHOR_PATTERN, HOOKED_BLOCK_POSITION_IDS, } from "./hooked-blocks.js";
6
- import { toKebabCase, toSnakeCase, } from "./string-case.js";
6
+ import { toSnakeCase, } from "./string-case.js";
7
7
  import { WORKSPACE_TEMPLATE_PACKAGE, } from "./workspace-project.js";
8
+ export { normalizeBlockSlug, } from "./scaffold-identifiers.js";
8
9
  /**
9
10
  * Supported top-level `wp-typia add` kinds exposed by the canonical CLI.
10
11
  */
@@ -43,9 +44,6 @@ export const ADD_BLOCK_TEMPLATE_IDS = [
43
44
  ];
44
45
  const WORKSPACE_GENERATED_SLUG_PATTERN = /^[a-z][a-z0-9-]*$/;
45
46
  export const REST_RESOURCE_NAMESPACE_PATTERN = /^[a-z][a-z0-9-]*(?:\/[a-z0-9-]+)+$/u;
46
- export function normalizeBlockSlug(input) {
47
- return toKebabCase(input);
48
- }
49
47
  export function assertValidGeneratedSlug(label, slug, usage) {
50
48
  if (!slug) {
51
49
  throw new Error(`${label} is required. Use \`${usage}\`.`);
@@ -279,16 +277,18 @@ export function assertEditorPluginDoesNotExist(projectDir, editorPluginSlug, inv
279
277
  */
280
278
  export function formatAddHelpText() {
281
279
  return `Usage:
282
- wp-typia add block <name> --template <${ADD_BLOCK_TEMPLATE_IDS.join("|")}> [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>]
283
- wp-typia add variation <name> --block <block-slug>
284
- wp-typia add pattern <name>
285
- wp-typia add binding-source <name>
286
- wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <list,read,create,update,delete>]
287
- wp-typia add hooked-block <block-slug> --anchor <anchor-block-name> --position <${HOOKED_BLOCK_POSITION_IDS.join("|")}>
288
- wp-typia add editor-plugin <name> [--slot <${EDITOR_PLUGIN_SLOT_IDS.join("|")}>]
280
+ wp-typia add block <name> --template <${ADD_BLOCK_TEMPLATE_IDS.join("|")}> [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--inner-blocks-preset <freeform|ordered|horizontal|locked-structure>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--dry-run]
281
+ wp-typia add variation <name> --block <block-slug> [--dry-run]
282
+ wp-typia add pattern <name> [--dry-run]
283
+ wp-typia add binding-source <name> [--dry-run]
284
+ wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <list,read,create,update,delete>] [--dry-run]
285
+ wp-typia add hooked-block <block-slug> --anchor <anchor-block-name> --position <${HOOKED_BLOCK_POSITION_IDS.join("|")}> [--dry-run]
286
+ wp-typia add editor-plugin <name> [--slot <${EDITOR_PLUGIN_SLOT_IDS.join("|")}>] [--dry-run]
289
287
 
290
288
  Notes:
291
- \`wp-typia add\` runs only inside official ${WORKSPACE_TEMPLATE_PACKAGE} workspaces.
289
+ \`wp-typia add\` runs only inside official ${WORKSPACE_TEMPLATE_PACKAGE} workspaces scaffolded via \`wp-typia create <project-dir> --template workspace\`.
290
+ Pass \`--dry-run\` to preview the workspace files that would change without writing them.
291
+ \`query-loop\` is a create-time scaffold family. Use \`wp-typia create <project-dir> --template query-loop\` instead of \`wp-typia add block\`.
292
292
  \`add variation\` targets an existing block slug from \`scripts/block-config.ts\`.
293
293
  \`add pattern\` scaffolds a namespaced PHP pattern shell under \`src/patterns/\`.
294
294
  \`add binding-source\` scaffolds shared PHP and editor registration under \`src/bindings/\`.
@@ -27,6 +27,8 @@ export { getDoctorChecks, runDoctor, type DoctorCheck } from "./cli-doctor.js";
27
27
  export { createCliCommandError, CliDiagnosticError, formatCliDiagnosticError, formatDoctorCheckLine, formatDoctorSummaryLine, getDoctorFailureDetailLines, getFailingDoctorChecks, isCliDiagnosticError, } from "./cli-diagnostics.js";
28
28
  export type { CliDiagnosticMessage } from "./cli-diagnostics.js";
29
29
  export { EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, getWorkspaceBlockSelectOptions, runAddBindingSourceCommand, runAddBlockCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "./cli-add.js";
30
+ export { COMPOUND_INNER_BLOCKS_PRESET_IDS, getCompoundInnerBlocksPresetDefinition, } from "./compound-inner-blocks.js";
31
+ export type { CompoundInnerBlocksPresetId } from "./compound-inner-blocks.js";
30
32
  export { HOOKED_BLOCK_POSITION_IDS } from "./hooked-blocks.js";
31
33
  export type { EditorPluginSlotId } from "./cli-add.js";
32
34
  export type { HookedBlockPositionId } from "./hooked-blocks.js";
@@ -26,6 +26,7 @@
26
26
  export { getDoctorChecks, runDoctor } from "./cli-doctor.js";
27
27
  export { createCliCommandError, CliDiagnosticError, formatCliDiagnosticError, formatDoctorCheckLine, formatDoctorSummaryLine, getDoctorFailureDetailLines, getFailingDoctorChecks, isCliDiagnosticError, } from "./cli-diagnostics.js";
28
28
  export { EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, getWorkspaceBlockSelectOptions, runAddBindingSourceCommand, runAddBlockCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "./cli-add.js";
29
+ export { COMPOUND_INNER_BLOCKS_PRESET_IDS, getCompoundInnerBlocksPresetDefinition, } from "./compound-inner-blocks.js";
29
30
  export { HOOKED_BLOCK_POSITION_IDS } from "./hooked-blocks.js";
30
31
  export { formatHelpText } from "./cli-help.js";
31
32
  export { getNextSteps, getOptionalOnboarding, runScaffoldFlow, } from "./cli-scaffold.js";
@@ -2,10 +2,24 @@
2
2
  * Shared human-readable diagnostics for non-interactive `wp-typia` CLI flows.
3
3
  */
4
4
  export interface CliDiagnosticMessage {
5
+ code: CliDiagnosticCode;
5
6
  command: string;
6
7
  detailLines: string[];
7
8
  summary: string;
8
9
  }
10
+ export declare const CLI_DIAGNOSTIC_CODES: {
11
+ readonly COMMAND_EXECUTION: "command-execution";
12
+ readonly CONFIGURATION_MISSING: "configuration-missing";
13
+ readonly DEPENDENCIES_NOT_INSTALLED: "dependencies-not-installed";
14
+ readonly DOCTOR_CHECK_FAILED: "doctor-check-failed";
15
+ readonly INVALID_ARGUMENT: "invalid-argument";
16
+ readonly INVALID_COMMAND: "invalid-command";
17
+ readonly MISSING_ARGUMENT: "missing-argument";
18
+ readonly MISSING_BUILD_ARTIFACT: "missing-build-artifact";
19
+ readonly OUTSIDE_PROJECT_ROOT: "outside-project-root";
20
+ readonly UNSUPPORTED_COMMAND: "unsupported-command";
21
+ };
22
+ export type CliDiagnosticCode = (typeof CLI_DIAGNOSTIC_CODES)[keyof typeof CLI_DIAGNOSTIC_CODES];
9
23
  type DoctorCheckLike = {
10
24
  detail: string;
11
25
  label: string;
@@ -15,6 +29,7 @@ type DoctorCheckLike = {
15
29
  * Structured CLI failure carrying a stable summary/detail layout.
16
30
  */
17
31
  export declare class CliDiagnosticError extends Error {
32
+ readonly code: CliDiagnosticCode;
18
33
  readonly command: string;
19
34
  readonly detailLines: string[];
20
35
  readonly summary: string;
@@ -29,6 +44,7 @@ export declare function isCliDiagnosticError(error: unknown): error is CliDiagno
29
44
  */
30
45
  export declare function createCliCommandError(options: {
31
46
  command: string;
47
+ code?: CliDiagnosticCode;
32
48
  detailLines?: string[];
33
49
  error?: unknown;
34
50
  summary?: string;
@@ -38,6 +54,16 @@ export declare function createCliCommandError(options: {
38
54
  * plain message so existing non-command failures keep working.
39
55
  */
40
56
  export declare function formatCliDiagnosticError(error: unknown): string;
57
+ export declare function serializeCliDiagnosticError(error: unknown): {
58
+ code: CliDiagnosticCode;
59
+ command?: string;
60
+ detailLines?: string[];
61
+ kind: "command-execution";
62
+ message: string;
63
+ name: string;
64
+ summary?: string;
65
+ tag: "CommandExecutionError";
66
+ };
41
67
  /**
42
68
  * Format one human-readable doctor check row.
43
69
  */
@@ -1,8 +1,23 @@
1
+ export const CLI_DIAGNOSTIC_CODES = {
2
+ COMMAND_EXECUTION: "command-execution",
3
+ CONFIGURATION_MISSING: "configuration-missing",
4
+ DEPENDENCIES_NOT_INSTALLED: "dependencies-not-installed",
5
+ DOCTOR_CHECK_FAILED: "doctor-check-failed",
6
+ INVALID_ARGUMENT: "invalid-argument",
7
+ INVALID_COMMAND: "invalid-command",
8
+ MISSING_ARGUMENT: "missing-argument",
9
+ MISSING_BUILD_ARTIFACT: "missing-build-artifact",
10
+ OUTSIDE_PROJECT_ROOT: "outside-project-root",
11
+ UNSUPPORTED_COMMAND: "unsupported-command",
12
+ };
1
13
  const DEFAULT_CLI_FAILURE_SUMMARIES = {
2
14
  add: "Unable to complete the requested add workflow.",
3
15
  create: "Unable to complete the requested create workflow.",
4
16
  doctor: "One or more doctor checks failed.",
17
+ mcp: "Unable to inspect or sync MCP metadata.",
5
18
  migrate: "Unable to complete the requested migration command.",
19
+ sync: "Unable to complete the requested sync workflow.",
20
+ templates: "Unable to inspect scaffold templates.",
6
21
  };
7
22
  const MIN_CLI_WRAP_COLUMNS = 32;
8
23
  function parseCliColumns(value) {
@@ -104,6 +119,7 @@ function normalizeDetailLines(detailLines) {
104
119
  export class CliDiagnosticError extends Error {
105
120
  constructor(message, options) {
106
121
  super(formatCliDiagnosticBlock(message), options);
122
+ this.code = message.code;
107
123
  this.command = message.command;
108
124
  this.detailLines = [...message.detailLines];
109
125
  this.name = "CliDiagnosticError";
@@ -116,6 +132,59 @@ export class CliDiagnosticError extends Error {
116
132
  export function isCliDiagnosticError(error) {
117
133
  return error instanceof CliDiagnosticError;
118
134
  }
135
+ function isCliDiagnosticCode(value) {
136
+ return Object.values(CLI_DIAGNOSTIC_CODES).includes(value);
137
+ }
138
+ function readCliDiagnosticCode(error) {
139
+ if (isCliDiagnosticError(error)) {
140
+ return error.code;
141
+ }
142
+ if (typeof error === "object" && error !== null && "code" in error) {
143
+ const { code } = error;
144
+ if (isCliDiagnosticCode(code)) {
145
+ return code;
146
+ }
147
+ }
148
+ return null;
149
+ }
150
+ function inferCliDiagnosticCode(options) {
151
+ const inheritedCode = readCliDiagnosticCode(options.error);
152
+ if (inheritedCode) {
153
+ return inheritedCode;
154
+ }
155
+ const haystack = normalizeDetailLines([
156
+ ...options.detailLines,
157
+ options.error === undefined ? undefined : getErrorMessage(options.error),
158
+ ]).join("\n");
159
+ if (/No MCP schema sources are configured\./u.test(haystack)) {
160
+ return CLI_DIAGNOSTIC_CODES.CONFIGURATION_MISSING;
161
+ }
162
+ if (/Missing bundled build artifacts/u.test(haystack)) {
163
+ return CLI_DIAGNOSTIC_CODES.MISSING_BUILD_ARTIFACT;
164
+ }
165
+ if (/No generated wp-typia project root was found/u.test(haystack)) {
166
+ return CLI_DIAGNOSTIC_CODES.OUTSIDE_PROJECT_ROOT;
167
+ }
168
+ if (/dependencies have not been installed yet/u.test(haystack)) {
169
+ return CLI_DIAGNOSTIC_CODES.DEPENDENCIES_NOT_INSTALLED;
170
+ }
171
+ if (options.command === "doctor") {
172
+ return CLI_DIAGNOSTIC_CODES.DOCTOR_CHECK_FAILED;
173
+ }
174
+ if (/requires <|requires --/u.test(haystack)) {
175
+ return CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT;
176
+ }
177
+ if (/Unknown .*subcommand|Unknown add kind|Unknown template|removed in favor|does not support|The Bun-free fallback runtime does not support|The positional alias only accepts/u.test(haystack)) {
178
+ return haystack.includes("does not support") ||
179
+ haystack.includes("The Bun-free fallback runtime does not support")
180
+ ? CLI_DIAGNOSTIC_CODES.UNSUPPORTED_COMMAND
181
+ : CLI_DIAGNOSTIC_CODES.INVALID_COMMAND;
182
+ }
183
+ if (/Invalid |must start with|cannot hook|cannot nest|cannot use|cannot be|already defines|already exists|Expected one of/u.test(haystack)) {
184
+ return CLI_DIAGNOSTIC_CODES.INVALID_ARGUMENT;
185
+ }
186
+ return CLI_DIAGNOSTIC_CODES.COMMAND_EXECUTION;
187
+ }
119
188
  /**
120
189
  * Build a shared diagnostic error for one CLI command failure.
121
190
  */
@@ -125,7 +194,14 @@ export function createCliCommandError(options) {
125
194
  }
126
195
  const summary = options.summary ?? DEFAULT_CLI_FAILURE_SUMMARIES[options.command] ?? "Command failed.";
127
196
  const detailLines = normalizeDetailLines(options.detailLines ?? [options.error === undefined ? undefined : getErrorMessage(options.error)]);
197
+ const code = options.code ??
198
+ inferCliDiagnosticCode({
199
+ command: options.command,
200
+ detailLines,
201
+ error: options.error,
202
+ });
128
203
  return new CliDiagnosticError({
204
+ code,
129
205
  command: options.command,
130
206
  detailLines,
131
207
  summary,
@@ -140,11 +216,42 @@ export function formatCliDiagnosticError(error) {
140
216
  return getErrorMessage(error);
141
217
  }
142
218
  return formatCliDiagnosticBlock({
219
+ code: error.code,
143
220
  command: error.command,
144
221
  detailLines: error.detailLines,
145
222
  summary: error.summary,
146
223
  });
147
224
  }
225
+ export function serializeCliDiagnosticError(error) {
226
+ if (isCliDiagnosticError(error)) {
227
+ return {
228
+ code: error.code,
229
+ command: error.command,
230
+ detailLines: [...error.detailLines],
231
+ kind: "command-execution",
232
+ message: formatCliDiagnosticBlock({
233
+ code: error.code,
234
+ command: error.command,
235
+ detailLines: error.detailLines,
236
+ summary: error.summary,
237
+ }),
238
+ name: error.name,
239
+ summary: error.summary,
240
+ tag: "CommandExecutionError",
241
+ };
242
+ }
243
+ return {
244
+ code: inferCliDiagnosticCode({
245
+ command: "unknown",
246
+ detailLines: [],
247
+ error,
248
+ }),
249
+ kind: "command-execution",
250
+ message: getErrorMessage(error),
251
+ name: error instanceof Error ? error.name : "Error",
252
+ tag: "CommandExecutionError",
253
+ };
254
+ }
148
255
  /**
149
256
  * Format one human-readable doctor check row.
150
257
  */
@@ -351,21 +351,21 @@ export function getWorkspaceDoctorChecks(cwd) {
351
351
  workspace = tryResolveWorkspaceProject(cwd);
352
352
  }
353
353
  catch (error) {
354
- checks.push(createDoctorScopeCheck("fail", "Environment checks ran, but workspace discovery could not continue. Fix the nearby workspace package metadata and rerun `wp-typia doctor`."));
354
+ checks.push(createDoctorScopeCheck("fail", "Scope: blocked before workspace checks. Environment checks ran, but workspace discovery could not continue. Fix the nearby workspace package metadata and rerun `wp-typia doctor`."));
355
355
  checks.push(createDoctorCheck("Workspace package metadata", "fail", error instanceof Error ? error.message : String(error)));
356
356
  return checks;
357
357
  }
358
358
  if (!workspace) {
359
359
  if (invalidWorkspaceReason) {
360
- checks.push(createDoctorScopeCheck("fail", "Environment checks ran, but workspace diagnostics could not continue because a nearby wp-typia workspace candidate is invalid. Fix the workspace package metadata and rerun `wp-typia doctor`."));
360
+ checks.push(createDoctorScopeCheck("fail", "Scope: blocked before workspace checks. Environment checks ran, but workspace diagnostics could not continue because a nearby wp-typia workspace candidate is invalid. Fix the workspace package metadata and rerun `wp-typia doctor`."));
361
361
  checks.push(createDoctorCheck("Workspace package metadata", "fail", invalidWorkspaceReason));
362
362
  }
363
363
  else {
364
- checks.push(createDoctorScopeCheck("pass", "No official wp-typia workspace root was detected, so this run only covered environment readiness. Re-run `wp-typia doctor` from a workspace root if you expected package metadata, inventory, or generated artifact checks."));
364
+ checks.push(createDoctorScopeCheck("pass", "Scope: environment-only. No official wp-typia workspace root was detected, so this run only covered environment readiness. Re-run `wp-typia doctor` from a workspace root if you expected package metadata, inventory, or generated artifact checks."));
365
365
  }
366
366
  return checks;
367
367
  }
368
- checks.push(createDoctorScopeCheck("pass", `Official workspace detected for ${workspace.workspace.namespace}; environment readiness checks ran and workspace-scoped diagnostics are enabled for the package metadata, inventory, source-tree drift, and any configured migration hint rows below.`));
368
+ checks.push(createDoctorScopeCheck("pass", `Scope: full workspace diagnostics for ${workspace.workspace.namespace}. Environment readiness checks ran and workspace-scoped diagnostics are enabled for the package metadata, inventory, source-tree drift, and any configured migration hint rows below.`));
369
369
  let workspacePackageJson;
370
370
  try {
371
371
  workspacePackageJson = parseWorkspacePackageJson(workspace.projectDir);
@@ -14,10 +14,10 @@ export function formatHelpText() {
14
14
  wp-typia create <project-dir> [--template query-loop] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--query-post-type <post-type>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
15
15
  wp-typia create <project-dir> [--template <./path|github:owner/repo/path[#ref]>] [--variant <name>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
16
16
  wp-typia create <project-dir> [--template <npm-package>] [--variant <name>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
17
- wp-typia create <project-dir> [--template persistence] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
18
- wp-typia create <project-dir> [--template compound] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
17
+ wp-typia create <project-dir> [--template persistence] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
18
+ wp-typia create <project-dir> [--template compound] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--inner-blocks-preset <freeform|ordered|horizontal|locked-structure>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
19
19
  wp-typia <project-dir> [create flags...]
20
- wp-typia add block <name> --template <basic|interactivity|persistence|compound> [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>]
20
+ wp-typia add block <name> --template <basic|interactivity|persistence|compound> [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--inner-blocks-preset <freeform|ordered|horizontal|locked-structure>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>]
21
21
  wp-typia add variation <name> --block <block-slug>
22
22
  wp-typia add pattern <name>
23
23
  wp-typia add binding-source <name>
@@ -38,6 +38,7 @@ Notes:
38
38
  \`wp-typia create\` is the canonical scaffold command.
39
39
  \`wp-typia <project-dir>\` remains a backward-compatible alias to \`create\` when \`<project-dir>\` is the only positional argument.
40
40
  Use \`--template workspace\` as shorthand for \`@wp-typia/create-workspace-template\`, the official empty workspace scaffold behind \`wp-typia add ...\`.
41
+ \`query-loop\` is create-only. Use \`wp-typia create <project-dir> --template query-loop\`; \`wp-typia add block\` accepts only basic, interactivity, persistence, and compound families.
41
42
  \`add variation\` uses an existing workspace block from \`scripts/block-config.ts\`.
42
43
  \`add pattern\` scaffolds a namespaced PHP pattern shell under \`src/patterns/\`.
43
44
  \`add binding-source\` scaffolds shared PHP and editor registration under \`src/bindings/\`.
@@ -26,12 +26,14 @@ export interface ScaffoldDryRunPlan {
26
26
  }
27
27
  interface RunScaffoldFlowOptions {
28
28
  allowExistingDir?: boolean;
29
+ alternateRenderTargets?: string;
29
30
  cwd?: string;
30
31
  dataStorageMode?: string;
31
32
  dryRun?: boolean;
32
33
  externalLayerId?: string;
33
34
  externalLayerSource?: string;
34
35
  installDependencies?: Parameters<typeof scaffoldProject>[0]["installDependencies"];
36
+ innerBlocksPreset?: string;
35
37
  isInteractive?: boolean;
36
38
  namespace?: string;
37
39
  noInstall?: boolean;
@@ -80,7 +82,7 @@ export declare function getOptionalOnboarding({ availableScripts, packageManager
80
82
  * project.
81
83
  * @returns The scaffold result together with next-step guidance.
82
84
  */
83
- export declare function runScaffoldFlow({ projectInput, cwd, templateId, dataStorageMode, dryRun, externalLayerId, externalLayerSource, persistencePolicy, packageManager, namespace, textDomain, phpPrefix, queryPostType, yes, noInstall, onProgress, isInteractive, allowExistingDir, selectTemplate, selectDataStorage, selectExternalLayerId, selectPersistencePolicy, selectPackageManager, promptText, installDependencies, variant, selectWithTestPreset, selectWithWpEnv, selectWithMigrationUi, withMigrationUi, withTestPreset, withWpEnv, }: RunScaffoldFlowOptions): Promise<{
85
+ export declare function runScaffoldFlow({ projectInput, cwd, templateId, alternateRenderTargets, dataStorageMode, dryRun, externalLayerId, externalLayerSource, innerBlocksPreset, persistencePolicy, packageManager, namespace, textDomain, phpPrefix, queryPostType, yes, noInstall, onProgress, isInteractive, allowExistingDir, selectTemplate, selectDataStorage, selectExternalLayerId, selectPersistencePolicy, selectPackageManager, promptText, installDependencies, variant, selectWithTestPreset, selectWithWpEnv, selectWithMigrationUi, withMigrationUi, withTestPreset, withWpEnv, }: RunScaffoldFlowOptions): Promise<{
84
86
  dryRun: boolean;
85
87
  optionalOnboarding: OptionalOnboardingGuidance;
86
88
  plan: ScaffoldDryRunPlan | undefined;