@wp-typia/project-tools 0.17.0 → 0.18.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 (60) hide show
  1. package/dist/runtime/block-generator-service-core.d.ts +1 -1
  2. package/dist/runtime/block-generator-service-core.js +2 -1
  3. package/dist/runtime/block-generator-service-spec.d.ts +2 -1
  4. package/dist/runtime/built-in-block-artifacts.js +1 -0
  5. package/dist/runtime/built-in-block-code-templates/compound-child.d.ts +2 -2
  6. package/dist/runtime/built-in-block-code-templates/compound-child.js +30 -2
  7. package/dist/runtime/built-in-block-code-templates/compound-parent.d.ts +1 -1
  8. package/dist/runtime/built-in-block-code-templates/compound-parent.js +139 -19
  9. package/dist/runtime/cli-add-block.d.ts +2 -1
  10. package/dist/runtime/cli-add-block.js +19 -1
  11. package/dist/runtime/cli-add-shared.d.ts +55 -1
  12. package/dist/runtime/cli-add-shared.js +101 -2
  13. package/dist/runtime/cli-add-workspace-assets.d.ts +21 -1
  14. package/dist/runtime/cli-add-workspace-assets.js +417 -1
  15. package/dist/runtime/cli-add-workspace-rest.d.ts +14 -0
  16. package/dist/runtime/cli-add-workspace-rest.js +1060 -0
  17. package/dist/runtime/cli-add-workspace.d.ts +10 -1
  18. package/dist/runtime/cli-add-workspace.js +10 -1
  19. package/dist/runtime/cli-add.d.ts +3 -3
  20. package/dist/runtime/cli-add.js +2 -2
  21. package/dist/runtime/cli-core.d.ts +3 -1
  22. package/dist/runtime/cli-core.js +2 -1
  23. package/dist/runtime/cli-doctor-workspace.js +135 -1
  24. package/dist/runtime/cli-help.js +10 -6
  25. package/dist/runtime/cli-scaffold.d.ts +10 -2
  26. package/dist/runtime/cli-scaffold.js +136 -36
  27. package/dist/runtime/cli-templates.d.ts +4 -4
  28. package/dist/runtime/cli-templates.js +79 -39
  29. package/dist/runtime/index.d.ts +4 -3
  30. package/dist/runtime/index.js +3 -2
  31. package/dist/runtime/local-dev-presets.js +7 -2
  32. package/dist/runtime/rest-resource-artifacts.d.ts +35 -0
  33. package/dist/runtime/rest-resource-artifacts.js +158 -0
  34. package/dist/runtime/scaffold-answer-resolution.js +68 -2
  35. package/dist/runtime/scaffold-apply-utils.d.ts +4 -3
  36. package/dist/runtime/scaffold-apply-utils.js +34 -17
  37. package/dist/runtime/scaffold-bootstrap.d.ts +15 -0
  38. package/dist/runtime/scaffold-bootstrap.js +29 -7
  39. package/dist/runtime/scaffold-documents.js +2 -1
  40. package/dist/runtime/scaffold-onboarding.js +7 -3
  41. package/dist/runtime/scaffold-package-manager-files.js +6 -1
  42. package/dist/runtime/scaffold.d.ts +7 -1
  43. package/dist/runtime/scaffold.js +50 -8
  44. package/dist/runtime/template-render.d.ts +5 -2
  45. package/dist/runtime/template-render.js +9 -3
  46. package/dist/runtime/template-source-contracts.d.ts +11 -0
  47. package/dist/runtime/template-source-external.d.ts +1 -1
  48. package/dist/runtime/template-source-external.js +45 -13
  49. package/dist/runtime/template-source-normalization.d.ts +1 -1
  50. package/dist/runtime/template-source-normalization.js +5 -1
  51. package/dist/runtime/template-source-remote.d.ts +5 -0
  52. package/dist/runtime/template-source-remote.js +33 -0
  53. package/dist/runtime/template-source.js +30 -1
  54. package/dist/runtime/workspace-inventory.d.ts +43 -1
  55. package/dist/runtime/workspace-inventory.js +132 -1
  56. package/dist/runtime/workspace-project.d.ts +1 -1
  57. package/dist/runtime/workspace-project.js +2 -2
  58. package/package.json +3 -3
  59. package/templates/_shared/compound/core/scripts/add-compound-child.ts.mustache +428 -49
  60. package/templates/query-loop/src/validator-toolkit.ts.mustache +0 -1
@@ -1,5 +1,5 @@
1
- import { getBuiltInTemplateLayerDirs } from "./template-builtins.js";
2
- import { getTemplateById, getTemplateSelectOptions, isBuiltInTemplateId, listTemplates, } from "./template-registry.js";
1
+ import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, getTemplateById, getTemplateSelectOptions, isBuiltInTemplateId, listTemplates, } from "./template-registry.js";
2
+ const WORKSPACE_TEMPLATE_ALIAS = "workspace";
3
3
  /**
4
4
  * Format one line of template list output for a built-in template.
5
5
  *
@@ -10,62 +10,102 @@ export function formatTemplateSummary(template) {
10
10
  return `${template.id.padEnd(14)} ${template.description}`;
11
11
  }
12
12
  /**
13
- * Format the feature hint line shown under a template summary.
13
+ * Format the feature and capability hint lines shown under a template summary.
14
14
  *
15
15
  * @param template Template metadata including the `features` list.
16
- * @returns Indented feature text for CLI list output.
16
+ * @returns Indented feature and capability text for CLI list output.
17
17
  */
18
18
  export function formatTemplateFeatures(template) {
19
- return ` ${template.features.join(" • ")}`;
19
+ const lines = [` Features: ${template.features.join(" • ")}`];
20
+ const capabilityHints = getTemplateCapabilityHints(template);
21
+ if (capabilityHints.length > 0) {
22
+ lines.push(` Supports: ${capabilityHints.join(" • ")}`);
23
+ }
24
+ if (template.id === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE) {
25
+ lines.push(` Alias: ${WORKSPACE_TEMPLATE_ALIAS} (\`--template ${WORKSPACE_TEMPLATE_ALIAS}\`)`);
26
+ }
27
+ return lines.join("\n");
20
28
  }
21
29
  /**
22
30
  * Format the detailed template description for `templates inspect`.
23
31
  *
24
32
  * This expands special layer combinations for the `persistence` and `compound`
25
- * templates and returns a multi-line block including category, overlay path,
26
- * resolved layers, and feature labels.
33
+ * templates and returns a multi-line block centered on human-facing identity,
34
+ * capabilities, and logical layer composition.
27
35
  *
28
36
  * @param template Template metadata including `id`, `defaultCategory`,
29
37
  * `templateDir`, and `features`.
30
38
  * @returns Multi-line template details text for CLI output.
31
39
  */
32
40
  export function formatTemplateDetails(template) {
41
+ const detailLines = [
42
+ template.id,
43
+ `Summary: ${template.description}`,
44
+ ...getTemplateIdentityLines(template),
45
+ `Category: ${template.defaultCategory}`,
46
+ ];
47
+ const capabilityHints = getTemplateCapabilityHints(template);
48
+ if (capabilityHints.length > 0) {
49
+ detailLines.push("Capabilities:");
50
+ for (const capabilityHint of capabilityHints) {
51
+ detailLines.push(` - ${capabilityHint}`);
52
+ }
53
+ }
54
+ detailLines.push("Logical layers:");
55
+ for (const logicalLayer of getTemplateLogicalLayerSummaries(template)) {
56
+ detailLines.push(` - ${logicalLayer}`);
57
+ }
58
+ detailLines.push(`Features: ${template.features.join(", ")}`);
59
+ return detailLines.join("\n");
60
+ }
61
+ function getTemplateCapabilityHints(template) {
62
+ if (template.id === "persistence" || template.id === "compound") {
63
+ return ["--data-storage", "--persistence-policy", "external layers"];
64
+ }
65
+ if (template.id === "query-loop") {
66
+ return ["--query-post-type", "external layers"];
67
+ }
68
+ if (isBuiltInTemplateId(template.id)) {
69
+ return ["external layers"];
70
+ }
71
+ return [];
72
+ }
73
+ function getTemplateIdentityLines(template) {
74
+ if (template.id === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE) {
75
+ return [
76
+ "Identity:",
77
+ ` - Official package: ${OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE}`,
78
+ ` - Alias: ${WORKSPACE_TEMPLATE_ALIAS} (\`--template ${WORKSPACE_TEMPLATE_ALIAS}\`)`,
79
+ "Type: official workspace scaffold",
80
+ ];
81
+ }
82
+ return [
83
+ "Identity:",
84
+ ` - Built-in template id: ${template.id}`,
85
+ "Type: built-in block scaffold",
86
+ ];
87
+ }
88
+ function getTemplateLogicalLayerSummaries(template) {
33
89
  if (!isBuiltInTemplateId(template.id)) {
90
+ return ["workspace package scaffold"];
91
+ }
92
+ if (template.id === "persistence") {
93
+ return [
94
+ "authenticated write policy: shared/base -> rest helpers (shared) -> persistence core -> authenticated write policy -> persistence overlay",
95
+ "public write policy: shared/base -> rest helpers (shared) -> persistence core -> public write policy -> persistence overlay",
96
+ ];
97
+ }
98
+ if (template.id === "compound") {
34
99
  return [
35
- template.id,
36
- template.description,
37
- `Category: ${template.defaultCategory}`,
38
- `Overlay path: ${template.templateDir}`,
39
- "Layers: workspace package scaffold",
40
- `Features: ${template.features.join(", ")}`,
41
- ].join("\n");
100
+ "pure block family: shared/base -> compound core -> compound overlay",
101
+ "authenticated persistence: shared/base -> compound core -> rest helpers (shared) -> compound persistence core -> authenticated write policy -> compound overlay",
102
+ "public persistence: shared/base -> compound core -> rest helpers (shared) -> compound persistence core -> public write policy -> compound overlay",
103
+ ];
42
104
  }
43
- const layers = template.id === "persistence"
44
- ? [
45
- `authenticated: ${getBuiltInTemplateLayerDirs(template.id, { persistencePolicy: "authenticated" }).join(" -> ")}`,
46
- `public: ${getBuiltInTemplateLayerDirs(template.id, { persistencePolicy: "public" }).join(" -> ")}`,
47
- ]
48
- : template.id === "compound"
49
- ? [
50
- `pure: ${getBuiltInTemplateLayerDirs(template.id).join(" -> ")}`,
51
- `authenticated+persistence: ${getBuiltInTemplateLayerDirs(template.id, {
52
- persistenceEnabled: true,
53
- persistencePolicy: "authenticated",
54
- }).join(" -> ")}`,
55
- `public+persistence: ${getBuiltInTemplateLayerDirs(template.id, {
56
- persistenceEnabled: true,
57
- persistencePolicy: "public",
58
- }).join(" -> ")}`,
59
- ]
60
- : [getBuiltInTemplateLayerDirs(template.id).join(" -> ")];
105
+ const overlayName = template.id === "query-loop" ? "query-loop overlay" : `${template.id} overlay`;
61
106
  return [
62
- template.id,
63
- template.description,
64
- `Category: ${template.defaultCategory}`,
65
- `Overlay path: ${template.templateDir}`,
66
- `Layers: ${layers.join("\n")}`,
67
- `Features: ${template.features.join(", ")}`,
68
- ].join("\n");
107
+ `shared/base -> ${overlayName}`,
108
+ ];
69
109
  }
70
110
  export { getTemplateById, getTemplateSelectOptions, listTemplates };
71
111
  export { isBuiltInTemplateId };
@@ -6,7 +6,8 @@
6
6
  * Consumers should prefer these exports for scaffold, add, migrate, doctor,
7
7
  * and workspace-aware helpers such as `getWorkspaceBlockSelectOptions`,
8
8
  * `runAddBlockCommand`, `runAddVariationCommand`, `runAddPatternCommand`,
9
- * `runAddBindingSourceCommand`, `runAddHookedBlockCommand`,
9
+ * `runAddBindingSourceCommand`, `runAddEditorPluginCommand`,
10
+ * `runAddHookedBlockCommand`,
10
11
  * `HOOKED_BLOCK_POSITION_IDS`, and `runDoctor`.
11
12
  */
12
13
  export { scaffoldProject, collectScaffoldAnswers, getDefaultAnswers, getTemplateVariables, resolvePackageManagerId, resolveTemplateId, } from "./scaffold.js";
@@ -21,5 +22,5 @@ export { buildCompoundChildStarterManifestDocument, getStarterManifestFiles, str
21
22
  export type { EndpointAuthIntent, EndpointOpenApiAuthMode, EndpointOpenApiContractDocument, EndpointOpenApiDocumentOptions, EndpointOpenApiEndpointDefinition, EndpointOpenApiMethod, EndpointWordPressAuthDefinition, EndpointWordPressAuthMechanism, JsonSchemaDocument, JsonSchemaProjectionProfile, JsonSchemaObject, NormalizedEndpointAuthDefinition, OpenApiDocument, OpenApiInfo, OpenApiOperation, OpenApiParameter, OpenApiPathItem, OpenApiSecurityScheme, } from "./schema-core.js";
22
23
  export { PACKAGE_MANAGER_IDS, PACKAGE_MANAGERS, formatPackageExecCommand, formatInstallCommand, formatRunScript, getPackageManager, getPackageManagerSelectOptions, transformPackageManagerText, } from "./package-managers.js";
23
24
  export { TEMPLATE_IDS, TEMPLATE_REGISTRY, getTemplateById, getTemplateSelectOptions, listTemplates, } from "./template-registry.js";
24
- export { createReadlinePrompt, createCliCommandError, CliDiagnosticError, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, HOOKED_BLOCK_POSITION_IDS, isCliDiagnosticError, runAddBindingSourceCommand, runAddBlockCommand, runAddHookedBlockCommand, runAddPatternCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
25
- export type { CliDiagnosticMessage, DoctorCheck, HookedBlockPositionId, ReadlinePrompt, } from "./cli-core.js";
25
+ export { createReadlinePrompt, createCliCommandError, CliDiagnosticError, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, HOOKED_BLOCK_POSITION_IDS, EDITOR_PLUGIN_SLOT_IDS, isCliDiagnosticError, runAddBindingSourceCommand, runAddBlockCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
26
+ export type { CliDiagnosticMessage, DoctorCheck, EditorPluginSlotId, HookedBlockPositionId, ReadlinePrompt, } from "./cli-core.js";
@@ -6,7 +6,8 @@
6
6
  * Consumers should prefer these exports for scaffold, add, migrate, doctor,
7
7
  * and workspace-aware helpers such as `getWorkspaceBlockSelectOptions`,
8
8
  * `runAddBlockCommand`, `runAddVariationCommand`, `runAddPatternCommand`,
9
- * `runAddBindingSourceCommand`, `runAddHookedBlockCommand`,
9
+ * `runAddBindingSourceCommand`, `runAddEditorPluginCommand`,
10
+ * `runAddHookedBlockCommand`,
10
11
  * `HOOKED_BLOCK_POSITION_IDS`, and `runDoctor`.
11
12
  */
12
13
  export { scaffoldProject, collectScaffoldAnswers, getDefaultAnswers, getTemplateVariables, resolvePackageManagerId, resolveTemplateId, } from "./scaffold.js";
@@ -18,4 +19,4 @@ export { manifestAttributeToJsonSchema, projectJsonSchemaDocument, manifestToJso
18
19
  export { buildCompoundChildStarterManifestDocument, getStarterManifestFiles, stringifyStarterManifest, } from "./starter-manifests.js";
19
20
  export { PACKAGE_MANAGER_IDS, PACKAGE_MANAGERS, formatPackageExecCommand, formatInstallCommand, formatRunScript, getPackageManager, getPackageManagerSelectOptions, transformPackageManagerText, } from "./package-managers.js";
20
21
  export { TEMPLATE_IDS, TEMPLATE_REGISTRY, getTemplateById, getTemplateSelectOptions, listTemplates, } from "./template-registry.js";
21
- export { createReadlinePrompt, createCliCommandError, CliDiagnosticError, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, HOOKED_BLOCK_POSITION_IDS, isCliDiagnosticError, runAddBindingSourceCommand, runAddBlockCommand, runAddHookedBlockCommand, runAddPatternCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
22
+ export { createReadlinePrompt, createCliCommandError, CliDiagnosticError, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, HOOKED_BLOCK_POSITION_IDS, EDITOR_PLUGIN_SLOT_IDS, isCliDiagnosticError, runAddBindingSourceCommand, runAddBlockCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
@@ -8,7 +8,7 @@ import fs from "node:fs";
8
8
  import { promises as fsp } from "node:fs";
9
9
  import path from "node:path";
10
10
  import { formatRunScript, } from "./package-managers.js";
11
- import { SHARED_TEST_PRESET_TEMPLATE_ROOT, SHARED_WP_ENV_PRESET_TEMPLATE_ROOT, } from "./template-registry.js";
11
+ import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, SHARED_TEST_PRESET_TEMPLATE_ROOT, SHARED_WP_ENV_PRESET_TEMPLATE_ROOT, } from "./template-registry.js";
12
12
  import { copyInterpolatedDirectory } from "./template-render.js";
13
13
  function templateHasPersistenceSync(templateId, compoundPersistenceEnabled) {
14
14
  return templateId === "persistence" || (templateId === "compound" && compoundPersistenceEnabled);
@@ -19,6 +19,11 @@ function templateSupportsGeneratedSyncWatchers(templateId) {
19
19
  templateId === "persistence" ||
20
20
  templateId === "compound");
21
21
  }
22
+ function templateUsesDevAsPrimaryEntrypoint(templateId) {
23
+ return (templateSupportsGeneratedSyncWatchers(templateId) ||
24
+ templateId === "query-loop" ||
25
+ templateId === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE);
26
+ }
22
27
  function getWatchSyncTypesScript(packageManager, templateId) {
23
28
  if (templateId === "compound") {
24
29
  return `chokidar "src/blocks/**/types.ts" "scripts/block-config.ts" --debounce 200 -c "${formatRunScript(packageManager, "sync-types")}"`;
@@ -138,5 +143,5 @@ export async function applyGeneratedProjectDxPackageJson({ compoundPersistenceEn
138
143
  * scaffolded template.
139
144
  */
140
145
  export function getPrimaryDevelopmentScript(templateId) {
141
- return templateSupportsGeneratedSyncWatchers(templateId) ? "dev" : "start";
146
+ return templateUsesDevAsPrimaryEntrypoint(templateId) ? "dev" : "start";
142
147
  }
@@ -0,0 +1,35 @@
1
+ import type { RestResourceMethodId } from "./cli-add-shared.js";
2
+ interface RestResourceTemplateVariablesLike {
3
+ namespace: string;
4
+ pascalCase: string;
5
+ slugKebabCase: string;
6
+ title: string;
7
+ }
8
+ interface SyncRestResourceArtifactsOptions {
9
+ clientFile: string;
10
+ methods: RestResourceMethodId[];
11
+ outputDir: string;
12
+ projectDir: string;
13
+ typesFile: string;
14
+ validatorsFile: string;
15
+ variables: RestResourceTemplateVariablesLike;
16
+ }
17
+ /**
18
+ * Build the endpoint manifest for a workspace-level REST resource scaffold.
19
+ *
20
+ * @param variables Template naming data used for contract names, routes, and OpenAPI info.
21
+ * @param methods Enabled REST methods for the generated resource.
22
+ * @returns Endpoint manifest consumed by schema, OpenAPI, and client generators.
23
+ */
24
+ export declare function buildRestResourceEndpointManifest(variables: RestResourceTemplateVariablesLike, methods: RestResourceMethodId[]): import("@wp-typia/block-runtime/metadata-core").EndpointManifestDefinition<Record<string, {
25
+ sourceTypeName: string;
26
+ }>, import("@wp-typia/block-runtime/schema-core").EndpointOpenApiEndpointDefinition[]>;
27
+ /**
28
+ * Synchronize generated schemas, OpenAPI output, and endpoint client code for
29
+ * a workspace-level REST resource scaffold.
30
+ *
31
+ * @param options Resource file paths, enabled methods, and naming variables.
32
+ * @returns A promise that resolves after every generated REST artifact has been refreshed.
33
+ */
34
+ export declare function syncRestResourceArtifacts({ clientFile, methods, outputDir, projectDir, typesFile, validatorsFile, variables, }: SyncRestResourceArtifactsOptions): Promise<void>;
35
+ export {};
@@ -0,0 +1,158 @@
1
+ import path from "node:path";
2
+ import { defineEndpointManifest, syncEndpointClient, syncRestOpenApi, syncTypeSchemas, } from "@wp-typia/block-runtime/metadata-core";
3
+ /**
4
+ * Build the endpoint manifest for a workspace-level REST resource scaffold.
5
+ *
6
+ * @param variables Template naming data used for contract names, routes, and OpenAPI info.
7
+ * @param methods Enabled REST methods for the generated resource.
8
+ * @returns Endpoint manifest consumed by schema, OpenAPI, and client generators.
9
+ */
10
+ export function buildRestResourceEndpointManifest(variables, methods) {
11
+ const basePath = `/${variables.namespace}/${variables.slugKebabCase}`;
12
+ const itemPath = `${basePath}/item`;
13
+ const contracts = {};
14
+ const endpoints = [];
15
+ if (methods.includes("list")) {
16
+ contracts["list-query"] = {
17
+ sourceTypeName: `${variables.pascalCase}ListQuery`,
18
+ };
19
+ contracts["list-response"] = {
20
+ sourceTypeName: `${variables.pascalCase}ListResponse`,
21
+ };
22
+ endpoints.push({
23
+ auth: "public",
24
+ method: "GET",
25
+ operationId: `list${variables.pascalCase}Resources`,
26
+ path: basePath,
27
+ queryContract: "list-query",
28
+ responseContract: "list-response",
29
+ summary: `List ${variables.title} resources.`,
30
+ tags: [variables.title],
31
+ });
32
+ }
33
+ if (methods.includes("read")) {
34
+ contracts["read-query"] = {
35
+ sourceTypeName: `${variables.pascalCase}ReadQuery`,
36
+ };
37
+ contracts["read-response"] = {
38
+ sourceTypeName: `${variables.pascalCase}ReadResponse`,
39
+ };
40
+ endpoints.push({
41
+ auth: "public",
42
+ method: "GET",
43
+ operationId: `read${variables.pascalCase}Resource`,
44
+ path: itemPath,
45
+ queryContract: "read-query",
46
+ responseContract: "read-response",
47
+ summary: `Read one ${variables.title} resource.`,
48
+ tags: [variables.title],
49
+ });
50
+ }
51
+ if (methods.includes("create")) {
52
+ contracts["create-request"] = {
53
+ sourceTypeName: `${variables.pascalCase}CreateRequest`,
54
+ };
55
+ contracts["create-response"] = {
56
+ sourceTypeName: `${variables.pascalCase}CreateResponse`,
57
+ };
58
+ endpoints.push({
59
+ auth: "authenticated",
60
+ bodyContract: "create-request",
61
+ method: "POST",
62
+ operationId: `create${variables.pascalCase}Resource`,
63
+ path: basePath,
64
+ responseContract: "create-response",
65
+ summary: `Create one ${variables.title} resource.`,
66
+ tags: [variables.title],
67
+ wordpressAuth: {
68
+ mechanism: "rest-nonce",
69
+ },
70
+ });
71
+ }
72
+ if (methods.includes("update")) {
73
+ contracts["update-query"] = {
74
+ sourceTypeName: `${variables.pascalCase}UpdateQuery`,
75
+ };
76
+ contracts["update-request"] = {
77
+ sourceTypeName: `${variables.pascalCase}UpdateRequest`,
78
+ };
79
+ contracts["update-response"] = {
80
+ sourceTypeName: `${variables.pascalCase}UpdateResponse`,
81
+ };
82
+ endpoints.push({
83
+ auth: "authenticated",
84
+ bodyContract: "update-request",
85
+ method: "POST",
86
+ operationId: `update${variables.pascalCase}Resource`,
87
+ path: itemPath,
88
+ queryContract: "update-query",
89
+ responseContract: "update-response",
90
+ summary: `Update one ${variables.title} resource.`,
91
+ tags: [variables.title],
92
+ wordpressAuth: {
93
+ mechanism: "rest-nonce",
94
+ },
95
+ });
96
+ }
97
+ if (methods.includes("delete")) {
98
+ contracts["delete-query"] = {
99
+ sourceTypeName: `${variables.pascalCase}DeleteQuery`,
100
+ };
101
+ contracts["delete-response"] = {
102
+ sourceTypeName: `${variables.pascalCase}DeleteResponse`,
103
+ };
104
+ endpoints.push({
105
+ auth: "authenticated",
106
+ method: "DELETE",
107
+ operationId: `delete${variables.pascalCase}Resource`,
108
+ path: itemPath,
109
+ queryContract: "delete-query",
110
+ responseContract: "delete-response",
111
+ summary: `Delete one ${variables.title} resource.`,
112
+ tags: [variables.title],
113
+ wordpressAuth: {
114
+ mechanism: "rest-nonce",
115
+ },
116
+ });
117
+ }
118
+ return defineEndpointManifest({
119
+ contracts,
120
+ endpoints,
121
+ info: {
122
+ title: `${variables.title} REST API`,
123
+ version: "1.0.0",
124
+ },
125
+ });
126
+ }
127
+ /**
128
+ * Synchronize generated schemas, OpenAPI output, and endpoint client code for
129
+ * a workspace-level REST resource scaffold.
130
+ *
131
+ * @param options Resource file paths, enabled methods, and naming variables.
132
+ * @returns A promise that resolves after every generated REST artifact has been refreshed.
133
+ */
134
+ export async function syncRestResourceArtifacts({ clientFile, methods, outputDir, projectDir, typesFile, validatorsFile, variables, }) {
135
+ const manifest = buildRestResourceEndpointManifest(variables, methods);
136
+ for (const [baseName, contract] of Object.entries(manifest.contracts)) {
137
+ await syncTypeSchemas({
138
+ jsonSchemaFile: path.join(outputDir, "api-schemas", `${baseName}.schema.json`),
139
+ openApiFile: path.join(outputDir, "api-schemas", `${baseName}.openapi.json`),
140
+ projectRoot: projectDir,
141
+ sourceTypeName: contract.sourceTypeName,
142
+ typesFile,
143
+ });
144
+ }
145
+ await syncRestOpenApi({
146
+ manifest,
147
+ openApiFile: path.join(outputDir, "api.openapi.json"),
148
+ projectRoot: projectDir,
149
+ typesFile,
150
+ });
151
+ await syncEndpointClient({
152
+ clientFile,
153
+ manifest,
154
+ projectRoot: projectDir,
155
+ typesFile,
156
+ validatorsFile,
157
+ });
158
+ }
@@ -1,9 +1,15 @@
1
+ import { execSync } from 'node:child_process';
1
2
  import { PACKAGE_MANAGER_IDS, getPackageManager, } from './package-managers.js';
2
3
  import { normalizeBlockSlug, resolveScaffoldIdentifiers, validateBlockSlug, validateNamespace, } from './scaffold-identifiers.js';
3
4
  import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, TEMPLATE_IDS, getTemplateById, isBuiltInTemplateId, } from './template-registry.js';
4
5
  import { getRemovedBuiltInTemplateMessage, isRemovedBuiltInTemplateId, } from './template-defaults.js';
5
6
  import { toSnakeCase, toTitleCase, } from './string-case.js';
6
7
  const WORKSPACE_TEMPLATE_ALIAS = 'workspace';
8
+ const TEMPLATE_SELECTION_HINT = `--template <${[
9
+ ...TEMPLATE_IDS,
10
+ WORKSPACE_TEMPLATE_ALIAS,
11
+ ].join('|')}|./path|github:owner/repo/path[#ref]|npm-package>`;
12
+ const TEMPLATE_SUGGESTION_IDS = [...TEMPLATE_IDS, WORKSPACE_TEMPLATE_ALIAS];
7
13
  /**
8
14
  * Detect the current author name from local Git config.
9
15
  *
@@ -68,6 +74,60 @@ function normalizeTemplateSelection(templateId) {
68
74
  ? OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE
69
75
  : templateId;
70
76
  }
77
+ function looksLikeExplicitExternalTemplateLocator(templateId) {
78
+ return (templateId.startsWith('./') ||
79
+ templateId.startsWith('../') ||
80
+ templateId.startsWith('/') ||
81
+ templateId.startsWith('@') ||
82
+ templateId.startsWith('github:') ||
83
+ templateId.includes('/'));
84
+ }
85
+ function getEditDistance(left, right) {
86
+ const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
87
+ const current = new Array(right.length + 1);
88
+ for (let leftIndex = 0; leftIndex < left.length; leftIndex += 1) {
89
+ current[0] = leftIndex + 1;
90
+ for (let rightIndex = 0; rightIndex < right.length; rightIndex += 1) {
91
+ const substitutionCost = left[leftIndex] === right[rightIndex] ? 0 : 1;
92
+ current[rightIndex + 1] = Math.min(current[rightIndex] + 1, previous[rightIndex + 1] + 1, previous[rightIndex] + substitutionCost);
93
+ }
94
+ for (let index = 0; index < current.length; index += 1) {
95
+ previous[index] = current[index];
96
+ }
97
+ }
98
+ return previous[right.length];
99
+ }
100
+ function findMistypedBuiltInTemplateSuggestion(templateId) {
101
+ const normalizedTemplateId = templateId.trim().toLowerCase();
102
+ if (normalizedTemplateId.length === 0 ||
103
+ looksLikeExplicitExternalTemplateLocator(normalizedTemplateId)) {
104
+ return null;
105
+ }
106
+ let bestCandidate = null;
107
+ for (const candidateId of TEMPLATE_SUGGESTION_IDS) {
108
+ const distance = getEditDistance(normalizedTemplateId, candidateId);
109
+ if (bestCandidate === null ||
110
+ distance < bestCandidate.distance) {
111
+ bestCandidate = {
112
+ distance,
113
+ id: candidateId,
114
+ };
115
+ }
116
+ }
117
+ return bestCandidate && bestCandidate.distance <= 2
118
+ ? bestCandidate.id
119
+ : null;
120
+ }
121
+ function getMistypedBuiltInTemplateMessage(templateId) {
122
+ const suggestion = findMistypedBuiltInTemplateSuggestion(templateId);
123
+ if (!suggestion) {
124
+ return null;
125
+ }
126
+ const suggestionDescription = suggestion === WORKSPACE_TEMPLATE_ALIAS
127
+ ? 'official workspace scaffold'
128
+ : 'built-in scaffold';
129
+ return `Unknown template "${templateId}". Did you mean "${suggestion}"? Use \`--template ${suggestion}\` for the ${suggestionDescription}, or pass a local path, \`github:owner/repo/path[#ref]\`, or an npm package spec for an external template.`;
130
+ }
71
131
  /**
72
132
  * Resolve the scaffold template id from flags, defaults, and interactive selection.
73
133
  *
@@ -80,16 +140,23 @@ export async function resolveTemplateId({ templateId, yes = false, isInteractive
80
140
  if (isRemovedBuiltInTemplateId(templateId)) {
81
141
  throw new Error(getRemovedBuiltInTemplateMessage(templateId));
82
142
  }
143
+ if (normalizedTemplateId === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE) {
144
+ return normalizedTemplateId;
145
+ }
83
146
  if (isBuiltInTemplateId(normalizedTemplateId)) {
84
147
  return getTemplateById(normalizedTemplateId).id;
85
148
  }
149
+ const mistypedBuiltInTemplateMessage = getMistypedBuiltInTemplateMessage(templateId);
150
+ if (mistypedBuiltInTemplateMessage) {
151
+ throw new Error(mistypedBuiltInTemplateMessage);
152
+ }
86
153
  return normalizedTemplateId;
87
154
  }
88
155
  if (yes) {
89
156
  return 'basic';
90
157
  }
91
158
  if (!isInteractive || !selectTemplate) {
92
- throw new Error(`Template is required in non-interactive mode. Use --template <${TEMPLATE_IDS.join('|')}|./path|github:owner/repo/path[#ref]|npm-package>.`);
159
+ throw new Error(`Template is required in non-interactive mode. Use ${TEMPLATE_SELECTION_HINT}.`);
93
160
  }
94
161
  return normalizeTemplateSelection(await selectTemplate());
95
162
  }
@@ -160,4 +227,3 @@ export async function collectScaffoldAnswers({ projectName, templateId, yes = fa
160
227
  title: await promptText('Block title', toTitleCase(identifiers.slug)),
161
228
  };
162
229
  }
163
- import { execSync } from 'node:child_process';
@@ -2,7 +2,7 @@ import { type BuiltInBlockArtifact } from "./built-in-block-artifacts.js";
2
2
  import type { BuiltInCodeArtifact } from "./built-in-block-code-artifacts.js";
3
3
  import { type BuiltInTemplateId } from "./template-registry.js";
4
4
  import type { PackageManagerId } from "./package-managers.js";
5
- import type { ScaffoldTemplateVariables } from "./scaffold.js";
5
+ import type { ScaffoldProgressEvent, ScaffoldTemplateVariables } from "./scaffold.js";
6
6
  export { buildGitignore, buildReadme, mergeTextLines, } from "./scaffold-documents.js";
7
7
  export interface InstallDependenciesOptions {
8
8
  packageManager: PackageManagerId;
@@ -16,7 +16,7 @@ export declare function writeStarterManifestFiles(targetDir: string, templateId:
16
16
  */
17
17
  export declare function seedBuiltInPersistenceArtifacts(targetDir: string, templateId: BuiltInTemplateId, variables: ScaffoldTemplateVariables): Promise<void>;
18
18
  export declare function normalizePackageManagerFiles(targetDir: string, packageManagerId: PackageManagerId): Promise<void>;
19
- export declare function normalizePackageJson(targetDir: string, packageManagerId: PackageManagerId): Promise<void>;
19
+ export declare function removeQueryLoopPlaceholderFiles(projectDir: string, templateId: string): Promise<void>;
20
20
  export declare function removeUnexpectedLockfiles(targetDir: string, packageManagerId: PackageManagerId): Promise<void>;
21
21
  /**
22
22
  * Recursively normalizes generated text files for the selected package manager
@@ -33,7 +33,7 @@ export declare function applyWorkspaceMigrationCapability(projectDir: string, pa
33
33
  * Applies a built-in scaffold into the target directory, including generated
34
34
  * code artifacts, starter manifests, preset files, and placeholder rewrites.
35
35
  */
36
- export declare function applyBuiltInScaffoldProjectFiles({ projectDir, templateDir, templateId, variables, artifacts, codeArtifacts, readmeContent, gitignoreContent, allowExistingDir, packageManager, withMigrationUi, withTestPreset, withWpEnv, noInstall, installDependencies, repositoryReference, }: {
36
+ export declare function applyBuiltInScaffoldProjectFiles({ projectDir, templateDir, templateId, variables, artifacts, codeArtifacts, readmeContent, gitignoreContent, allowExistingDir, packageManager, withMigrationUi, withTestPreset, withWpEnv, noInstall, installDependencies, repositoryReference, onProgress, }: {
37
37
  projectDir: string;
38
38
  templateDir: string;
39
39
  templateId: BuiltInTemplateId;
@@ -50,4 +50,5 @@ export declare function applyBuiltInScaffoldProjectFiles({ projectDir, templateD
50
50
  noInstall?: boolean;
51
51
  installDependencies?: ((options: InstallDependenciesOptions) => Promise<void>) | undefined;
52
52
  repositoryReference?: string;
53
+ onProgress?: ((event: ScaffoldProgressEvent) => void | Promise<void>) | undefined;
53
54
  }): Promise<void>;