@wp-typia/project-tools 0.23.0 → 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 (109) 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-help.js +4 -3
  5. package/dist/runtime/cli-add-types.d.ts +18 -6
  6. package/dist/runtime/cli-add-validation.d.ts +7 -0
  7. package/dist/runtime/cli-add-validation.js +9 -0
  8. package/dist/runtime/cli-add-workspace-ability-scaffold.js +4 -1
  9. package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +5 -1
  10. package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.d.ts +34 -0
  11. package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.js +483 -0
  12. package/dist/runtime/cli-add-workspace-admin-view-templates-default.d.ts +30 -0
  13. package/dist/runtime/cli-add-workspace-admin-view-templates-default.js +310 -0
  14. package/dist/runtime/cli-add-workspace-admin-view-templates-rest.d.ts +25 -0
  15. package/dist/runtime/cli-add-workspace-admin-view-templates-rest.js +124 -0
  16. package/dist/runtime/cli-add-workspace-admin-view-templates-settings.d.ts +34 -0
  17. package/dist/runtime/cli-add-workspace-admin-view-templates-settings.js +370 -0
  18. package/dist/runtime/cli-add-workspace-admin-view-templates-shared.d.ts +49 -0
  19. package/dist/runtime/cli-add-workspace-admin-view-templates-shared.js +259 -0
  20. package/dist/runtime/cli-add-workspace-admin-view-templates.d.ts +18 -27
  21. package/dist/runtime/cli-add-workspace-admin-view-templates.js +30 -1326
  22. package/dist/runtime/cli-add-workspace-ai-anchors.js +4 -1
  23. package/dist/runtime/cli-add-workspace-ai-source-emitters.js +17 -1
  24. package/dist/runtime/cli-add-workspace-integration-env.d.ts +3 -1
  25. package/dist/runtime/cli-add-workspace-integration-env.js +42 -5
  26. package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +8 -0
  27. package/dist/runtime/cli-add-workspace-rest-anchors.js +41 -0
  28. package/dist/runtime/cli-add-workspace-rest-generated.d.ts +9 -0
  29. package/dist/runtime/cli-add-workspace-rest-generated.js +158 -0
  30. package/dist/runtime/cli-add-workspace-rest-manual.d.ts +8 -0
  31. package/dist/runtime/cli-add-workspace-rest-manual.js +279 -0
  32. package/dist/runtime/cli-add-workspace-rest-php-templates.d.ts +24 -0
  33. package/dist/runtime/cli-add-workspace-rest-php-templates.js +678 -0
  34. package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +8 -0
  35. package/dist/runtime/cli-add-workspace-rest-source-emitters.js +25 -4
  36. package/dist/runtime/cli-add-workspace-rest-types.d.ts +108 -0
  37. package/dist/runtime/cli-add-workspace-rest-types.js +1 -0
  38. package/dist/runtime/cli-add-workspace-rest.d.ts +3 -20
  39. package/dist/runtime/cli-add-workspace-rest.js +33 -788
  40. package/dist/runtime/cli-core.d.ts +1 -1
  41. package/dist/runtime/cli-core.js +1 -1
  42. package/dist/runtime/cli-diagnostics.d.ts +3 -1
  43. package/dist/runtime/cli-diagnostics.js +17 -5
  44. package/dist/runtime/cli-doctor-workspace-bindings.js +4 -1
  45. package/dist/runtime/cli-doctor-workspace-block-addons.d.ts +12 -0
  46. package/dist/runtime/cli-doctor-workspace-block-addons.js +134 -0
  47. package/dist/runtime/cli-doctor-workspace-block-iframe.d.ts +9 -0
  48. package/dist/runtime/cli-doctor-workspace-block-iframe.js +228 -0
  49. package/dist/runtime/cli-doctor-workspace-block-metadata.d.ts +11 -0
  50. package/dist/runtime/cli-doctor-workspace-block-metadata.js +111 -0
  51. package/dist/runtime/cli-doctor-workspace-blocks.js +6 -424
  52. package/dist/runtime/cli-doctor-workspace-features-abilities.d.ts +11 -0
  53. package/dist/runtime/cli-doctor-workspace-features-abilities.js +112 -0
  54. package/dist/runtime/cli-doctor-workspace-features-admin-views.d.ts +11 -0
  55. package/dist/runtime/cli-doctor-workspace-features-admin-views.js +128 -0
  56. package/dist/runtime/cli-doctor-workspace-features-ai.d.ts +11 -0
  57. package/dist/runtime/cli-doctor-workspace-features-ai.js +57 -0
  58. package/dist/runtime/cli-doctor-workspace-features-editor-plugins.d.ts +11 -0
  59. package/dist/runtime/cli-doctor-workspace-features-editor-plugins.js +80 -0
  60. package/dist/runtime/cli-doctor-workspace-features-post-meta.d.ts +11 -0
  61. package/dist/runtime/cli-doctor-workspace-features-post-meta.js +77 -0
  62. package/dist/runtime/cli-doctor-workspace-features-rest.d.ts +11 -0
  63. package/dist/runtime/cli-doctor-workspace-features-rest.js +120 -0
  64. package/dist/runtime/cli-doctor-workspace-features.js +14 -487
  65. package/dist/runtime/cli-doctor.d.ts +52 -3
  66. package/dist/runtime/cli-doctor.js +79 -8
  67. package/dist/runtime/cli-help.js +6 -3
  68. package/dist/runtime/cli-init-package-json.js +4 -2
  69. package/dist/runtime/cli-prompt.d.ts +16 -2
  70. package/dist/runtime/cli-prompt.js +29 -12
  71. package/dist/runtime/cli-scaffold.d.ts +2 -1
  72. package/dist/runtime/cli-scaffold.js +19 -10
  73. package/dist/runtime/external-template-guards.js +4 -6
  74. package/dist/runtime/index.d.ts +2 -2
  75. package/dist/runtime/index.js +1 -1
  76. package/dist/runtime/json-utils.d.ts +62 -4
  77. package/dist/runtime/json-utils.js +78 -4
  78. package/dist/runtime/local-dev-presets.js +4 -1
  79. package/dist/runtime/migration-ui-capability.js +4 -1
  80. package/dist/runtime/migration-utils.js +4 -1
  81. package/dist/runtime/package-managers.js +6 -1
  82. package/dist/runtime/package-versions.js +6 -1
  83. package/dist/runtime/scaffold-bootstrap.js +7 -2
  84. package/dist/runtime/scaffold-package-manager-files.js +5 -1
  85. package/dist/runtime/scaffold-repository-reference.js +4 -2
  86. package/dist/runtime/scaffold-template-variables.js +2 -1
  87. package/dist/runtime/scaffold.d.ts +18 -1
  88. package/dist/runtime/scaffold.js +55 -2
  89. package/dist/runtime/temp-roots.js +4 -1
  90. package/dist/runtime/template-layers.js +4 -1
  91. package/dist/runtime/template-registry.js +9 -3
  92. package/dist/runtime/template-source-contracts.d.ts +2 -0
  93. package/dist/runtime/template-source-normalization.js +2 -1
  94. package/dist/runtime/template-source-remote.js +18 -5
  95. package/dist/runtime/template-source-seeds.js +10 -3
  96. package/dist/runtime/workspace-inventory-mutations.js +2 -1
  97. package/dist/runtime/workspace-inventory-parser-entries.d.ts +17 -0
  98. package/dist/runtime/workspace-inventory-parser-entries.js +157 -0
  99. package/dist/runtime/workspace-inventory-parser-validation.d.ts +104 -0
  100. package/dist/runtime/workspace-inventory-parser-validation.js +34 -0
  101. package/dist/runtime/workspace-inventory-parser.d.ts +3 -45
  102. package/dist/runtime/workspace-inventory-parser.js +3 -581
  103. package/dist/runtime/workspace-inventory-section-descriptors.d.ts +19 -0
  104. package/dist/runtime/workspace-inventory-section-descriptors.js +435 -0
  105. package/dist/runtime/workspace-inventory-templates.d.ts +1 -1
  106. package/dist/runtime/workspace-inventory-templates.js +1 -0
  107. package/dist/runtime/workspace-inventory-types.d.ts +1 -0
  108. package/dist/runtime/workspace-project.js +4 -6
  109. package/package.json +2 -2
@@ -1,7 +1,81 @@
1
+ import fs from "node:fs";
2
+ import { promises as fsp } from "node:fs";
1
3
  /**
2
- * Re-exports JSON cloning helpers from `@wp-typia/block-runtime`.
3
- * This adapter keeps the existing project-tools module path stable while the
4
- * runtime implementation now lives in block-runtime.
4
+ * JSON helpers shared by project-tools runtime modules.
5
+ *
6
+ * File-backed runtime JSON readers should use `safeJsonParse`,
7
+ * `readJsonFileSync`, or `readJsonFile` so malformed JSON reports the file path
8
+ * and operation context. Raw `JSON.parse` remains intentional for trusted
9
+ * in-memory clones, subprocess output, test fixtures, generated workspace
10
+ * script templates that embed their own path-aware parse handling,
11
+ * package-version manifest cache probes with colocated path-aware wrappers,
12
+ * and cache/discovery probes that immediately catch malformed documents to
13
+ * continue with fallback behavior.
14
+ *
15
+ * This module keeps `cloneJsonValue` local instead of re-exporting the
16
+ * block-runtime helper so Bunli CLI bundles that only need project-tools JSON
17
+ * readers do not need to resolve the block-runtime subpath at runtime.
18
+ *
5
19
  * @module
6
20
  */
7
- export * from "@wp-typia/block-runtime/json-utils";
21
+ /**
22
+ * Create a deep clone of a JSON-serializable value.
23
+ *
24
+ * @remarks
25
+ * Values that are not JSON-serializable, such as functions, `undefined`,
26
+ * `BigInt`, class instances, and `Date` objects, are not preserved faithfully.
27
+ *
28
+ * @param value JSON-compatible data to clone.
29
+ * @returns A deep-cloned copy created with `JSON.parse(JSON.stringify(...))`.
30
+ */
31
+ export function cloneJsonValue(value) {
32
+ return JSON.parse(JSON.stringify(value));
33
+ }
34
+ function formatJsonParseTarget({ context, filePath }) {
35
+ const operation = context?.trim() || "JSON";
36
+ return filePath ? `${operation} at ${filePath}` : operation;
37
+ }
38
+ /**
39
+ * Parse JSON and include operation/file context when decoding fails.
40
+ *
41
+ * @param source Raw JSON source text.
42
+ * @param options Optional file path and human-readable operation context.
43
+ * @returns Parsed JSON value cast to the caller-specified type.
44
+ * @throws {Error} When the source is malformed JSON.
45
+ */
46
+ export function safeJsonParse(source, options = {}) {
47
+ try {
48
+ return JSON.parse(source);
49
+ }
50
+ catch (error) {
51
+ throw new Error(`Failed to parse ${formatJsonParseTarget(options)}: ${error instanceof Error ? error.message : String(error)}`);
52
+ }
53
+ }
54
+ /**
55
+ * Read and parse a JSON file synchronously with path-aware parse errors.
56
+ *
57
+ * @param filePath JSON file path to read.
58
+ * @param options Optional parse context. `filePath` is always set from the
59
+ * reader argument.
60
+ * @returns Parsed JSON value cast to the caller-specified type.
61
+ */
62
+ export function readJsonFileSync(filePath, options = {}) {
63
+ return safeJsonParse(fs.readFileSync(filePath, "utf8"), {
64
+ ...options,
65
+ filePath,
66
+ });
67
+ }
68
+ /**
69
+ * Read and parse a JSON file asynchronously with path-aware parse errors.
70
+ *
71
+ * @param filePath JSON file path to read.
72
+ * @param options Optional parse context. `filePath` is always set from the
73
+ * reader argument.
74
+ * @returns Parsed JSON value cast to the caller-specified type.
75
+ */
76
+ export async function readJsonFile(filePath, options = {}) {
77
+ return safeJsonParse(await fsp.readFile(filePath, "utf8"), {
78
+ ...options,
79
+ filePath,
80
+ });
81
+ }
@@ -8,6 +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 { readJsonFile } from "./json-utils.js";
11
12
  import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, SHARED_TEST_PRESET_TEMPLATE_ROOT, SHARED_WP_ENV_PRESET_TEMPLATE_ROOT, } from "./template-registry.js";
12
13
  import { copyInterpolatedDirectory } from "./template-render.js";
13
14
  function templateHasPersistenceSync(templateId, compoundPersistenceEnabled) {
@@ -54,7 +55,9 @@ function getDevScript(packageManager, compoundPersistenceEnabled, templateId) {
54
55
  }
55
56
  async function mutatePackageJson(projectDir, mutate) {
56
57
  const packageJsonPath = path.join(projectDir, "package.json");
57
- const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, "utf8"));
58
+ const packageJson = await readJsonFile(packageJsonPath, {
59
+ context: "local dev package manifest",
60
+ });
58
61
  mutate(packageJson);
59
62
  await fsp.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, "\t")}\n`, "utf8");
60
63
  }
@@ -2,6 +2,7 @@ import { promises as fsp } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { getPackageVersions } from "./package-versions.js";
4
4
  import { formatPackageExecCommand } from "./package-managers.js";
5
+ import { readJsonFile } from "./json-utils.js";
5
6
  import { seedProjectMigrations } from "./migrations.js";
6
7
  import { copyInterpolatedDirectory } from "./template-render.js";
7
8
  import { SHARED_MIGRATION_UI_TEMPLATE_ROOT, } from "./template-registry.js";
@@ -10,7 +11,9 @@ const BLOCK_METADATA_IMPORT_LINE = "import metadata from './block-metadata';";
10
11
  const LEGACY_BLOCK_JSON_IMPORT_LINE = "import metadata from './block.json';";
11
12
  async function mutatePackageJson(projectDir, mutate) {
12
13
  const packageJsonPath = path.join(projectDir, "package.json");
13
- const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, "utf8"));
14
+ const packageJson = await readJsonFile(packageJsonPath, {
15
+ context: "migration UI package manifest",
16
+ });
14
17
  mutate(packageJson);
15
18
  await fsp.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, "\t")}\n`, "utf8");
16
19
  }
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
  import { execSync } from 'node:child_process';
4
4
  import { formatRunScript, inferPackageManagerId, parsePackageManagerField, } from './package-managers.js';
5
5
  import { isPlainObject } from './object-utils.js';
6
+ import { readJsonFileSync } from './json-utils.js';
6
7
  export { cloneJsonValue } from './json-utils.js';
7
8
  const MIGRATION_VERSION_LABEL_PATTERN = /^v([1-9]\d*)$/;
8
9
  const LEGACY_SEMVER_MIGRATION_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
@@ -66,7 +67,9 @@ export function createTransformFixtureValue(attribute, pathLabel) {
66
67
  }
67
68
  }
68
69
  export function readJson(filePath) {
69
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
70
+ return readJsonFileSync(filePath, {
71
+ context: 'migration JSON file',
72
+ });
70
73
  }
71
74
  export function renderPhpValue(value, indentLevel) {
72
75
  const indent = '\t'.repeat(indentLevel);
@@ -1,5 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { readJsonFileSync } from "./json-utils.js";
3
4
  const PACKAGE_MANAGER_DATA = [
4
5
  {
5
6
  id: "bun",
@@ -73,12 +74,16 @@ function readPackageManagerField(projectDir) {
73
74
  if (!fs.existsSync(packageJsonPath)) {
74
75
  return undefined;
75
76
  }
76
- const manifest = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
77
+ const manifest = readJsonFileSync(packageJsonPath, {
78
+ context: "package manager manifest",
79
+ });
77
80
  return typeof manifest.packageManager === "string"
78
81
  ? manifest.packageManager
79
82
  : undefined;
80
83
  }
81
84
  catch {
85
+ // Package manager inference intentionally falls back to lockfile signals
86
+ // when package.json is absent or malformed.
82
87
  return undefined;
83
88
  }
84
89
  }
@@ -92,7 +92,12 @@ function readPackageManifest(location) {
92
92
  if (!location.packageJsonPath || location.source === null) {
93
93
  return null;
94
94
  }
95
- return JSON.parse(location.source);
95
+ try {
96
+ return JSON.parse(location.source);
97
+ }
98
+ catch (error) {
99
+ throw new Error(`Failed to parse package version manifest at ${location.packageJsonPath}: ${error instanceof Error ? error.message : String(error)}`);
100
+ }
96
101
  }
97
102
  function tryReadPackageManifest(location) {
98
103
  if (!location) {
@@ -9,6 +9,7 @@ import { getStarterManifestFiles, stringifyStarterManifest } from "./starter-man
9
9
  import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, PROJECT_TOOLS_PACKAGE_ROOT, } from "./template-registry.js";
10
10
  import { getScaffoldTemplateVariableGroups } from "./scaffold-template-variable-groups.js";
11
11
  import { pathExists } from "./fs-async.js";
12
+ import { readJsonFile, readJsonFileSync } from "./json-utils.js";
12
13
  const EPHEMERAL_NODE_MODULES_LINK_TYPE = process.platform === "win32" ? "junction" : "dir";
13
14
  /**
14
15
  * Ensures the scaffold target directory exists and is empty unless explicitly allowed.
@@ -32,7 +33,9 @@ function readGeneratedPackageJson(projectDir) {
32
33
  if (!fs.existsSync(packageJsonPath)) {
33
34
  return null;
34
35
  }
35
- return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
36
+ return readJsonFileSync(packageJsonPath, {
37
+ context: "generated package manifest",
38
+ });
36
39
  }
37
40
  /**
38
41
  * Format the actionable error message used when a scaffold target directory
@@ -125,7 +128,9 @@ export function isOfficialWorkspaceProject(projectDir) {
125
128
  */
126
129
  export async function applyWorkspaceMigrationCapability(projectDir, packageManager) {
127
130
  const packageJsonPath = path.join(projectDir, "package.json");
128
- const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, "utf8"));
131
+ const packageJson = await readJsonFile(packageJsonPath, {
132
+ context: "workspace package manifest",
133
+ });
129
134
  const wpTypiaPackageVersion = getPackageVersions().wpTypiaPackageVersion;
130
135
  const canonicalCliSpecifier = wpTypiaPackageVersion === "^0.0.0"
131
136
  ? "wp-typia"
@@ -3,6 +3,7 @@ import { execSync } from "node:child_process";
3
3
  import path from "node:path";
4
4
  import { formatInstallCommand, getPackageManager, transformPackageManagerText, } from "./package-managers.js";
5
5
  import { readOptionalUtf8File } from "./fs-async.js";
6
+ import { safeJsonParse } from "./json-utils.js";
6
7
  const LOCKFILES = {
7
8
  bun: ["bun.lock", "bun.lockb"],
8
9
  npm: ["package-lock.json"],
@@ -38,7 +39,10 @@ export async function normalizePackageJson(targetDir, packageManagerId) {
38
39
  return;
39
40
  }
40
41
  const packageManager = getPackageManager(packageManagerId);
41
- const packageJson = JSON.parse(packageJsonSource);
42
+ const packageJson = safeJsonParse(packageJsonSource, {
43
+ context: "generated package manifest",
44
+ filePath: packageJsonPath,
45
+ });
42
46
  if (packageManagerId === "npm") {
43
47
  delete packageJson.packageManager;
44
48
  }
@@ -1,7 +1,7 @@
1
- import fs from "node:fs";
2
1
  import { createRequire } from "node:module";
3
2
  import path from "node:path";
4
3
  import { getOptionalNodeErrorCode } from "./fs-async.js";
4
+ import { readJsonFileSync } from "./json-utils.js";
5
5
  import { PROJECT_TOOLS_PACKAGE_ROOT } from "./template-registry.js";
6
6
  const require = createRequire(import.meta.url);
7
7
  /**
@@ -11,7 +11,9 @@ const require = createRequire(import.meta.url);
11
11
  export const DEFAULT_SCAFFOLD_REPOSITORY_REFERENCE = "imjlk/wp-typia";
12
12
  function readRepositoryPackageManifest(packageJsonPath) {
13
13
  try {
14
- return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
14
+ return readJsonFileSync(packageJsonPath, {
15
+ context: "repository package manifest",
16
+ });
15
17
  }
16
18
  catch (error) {
17
19
  if (getOptionalNodeErrorCode(error) === "ENOENT") {
@@ -23,7 +23,7 @@ export function getTemplateVariables(templateId, answers) {
23
23
  templateId,
24
24
  }));
25
25
  }
26
- const { apiClientPackageVersion, blockRuntimePackageVersion, blockTypesPackageVersion, projectToolsPackageVersion, restPackageVersion, } = getPackageVersions();
26
+ const { apiClientPackageVersion, blockRuntimePackageVersion, blockTypesPackageVersion, projectToolsPackageVersion, restPackageVersion, wpTypiaPackageVersion, } = getPackageVersions();
27
27
  const template = isBuiltInTemplateId(templateId) ? getTemplateById(templateId) : null;
28
28
  const metadataDefaults = isBuiltInTemplateId(templateId)
29
29
  ? getBuiltInTemplateMetadataDefaults(templateId)
@@ -94,6 +94,7 @@ export function getTemplateVariables(templateId, answers) {
94
94
  hasAlternatePlainTextRenderTarget: 'false',
95
95
  hasAlternateRenderTargets: 'false',
96
96
  projectToolsPackageVersion,
97
+ wpTypiaPackageVersion,
97
98
  requiresAtLeast: compatibility.pluginHeader.requiresAtLeast,
98
99
  requiresPhp: compatibility.pluginHeader.requiresPhp,
99
100
  cssClassName,
@@ -27,6 +27,10 @@ export declare const DATA_STORAGE_MODES: readonly ["post-meta", "custom-table"];
27
27
  export type DataStorageMode = (typeof DATA_STORAGE_MODES)[number];
28
28
  export declare const PERSISTENCE_POLICIES: readonly ["authenticated", "public"];
29
29
  export type PersistencePolicy = (typeof PERSISTENCE_POLICIES)[number];
30
+ /** Supported create profile ids for opt-in workspace scaffold presets. */
31
+ export declare const CREATE_PROFILE_IDS: readonly ["plugin-qa"];
32
+ /** Union of supported `wp-typia create --profile` id strings. */
33
+ export type CreateProfileId = (typeof CREATE_PROFILE_IDS)[number];
30
34
  /**
31
35
  * Normalized template variables shared by built-in and remote scaffold flows.
32
36
  */
@@ -58,6 +62,7 @@ export interface FlatScaffoldTemplateVariables extends Record<string, string> {
58
62
  hasAlternatePlainTextRenderTarget: "false" | "true";
59
63
  hasAlternateRenderTargets: "false" | "true";
60
64
  projectToolsPackageVersion: string;
65
+ wpTypiaPackageVersion: string;
61
66
  requiresAtLeast: string;
62
67
  requiresPhp: string;
63
68
  cssClassName: string;
@@ -163,6 +168,7 @@ interface ScaffoldProjectOptions {
163
168
  onProgress?: ((event: ScaffoldProgressEvent) => void | Promise<void>) | undefined;
164
169
  packageManager: PackageManagerId;
165
170
  persistencePolicy?: PersistencePolicy;
171
+ profile?: CreateProfileId;
166
172
  projectDir: string;
167
173
  repositoryReference?: string;
168
174
  templateId: string;
@@ -185,4 +191,15 @@ export { getTemplateVariables } from "./scaffold-template-variables.js";
185
191
  export { getScaffoldTemplateVariableGroups, type BasicScaffoldTemplateVariableGroups, type CompoundScaffoldTemplateVariableGroups, type ExternalScaffoldTemplateVariableGroups, type InteractivityScaffoldTemplateVariableGroups, type PersistenceScaffoldTemplateVariableGroups, type QueryLoopScaffoldTemplateVariableGroups, type ScaffoldTemplateFamily, type ScaffoldTemplateVariableGroups, type ScaffoldTemplateVariableGroupsCarrier, } from "./scaffold-template-variable-groups.js";
186
192
  export declare function isDataStorageMode(value: string): value is DataStorageMode;
187
193
  export declare function isPersistencePolicy(value: string): value is PersistencePolicy;
188
- export declare function scaffoldProject({ projectDir, templateId, answers, alternateRenderTargets, dataStorageMode, persistencePolicy, packageManager, externalLayerId, externalLayerSource, externalLayerSourceLabel, repositoryReference, cwd, allowExistingDir, noInstall, installDependencies, onProgress, variant, withMigrationUi, withTestPreset, withWpEnv, }: ScaffoldProjectOptions): Promise<ScaffoldProjectResult>;
194
+ /**
195
+ * Return whether a raw string is a supported create profile id.
196
+ */
197
+ export declare function isCreateProfileId(value: string): value is CreateProfileId;
198
+ /**
199
+ * Resolve an optional create profile flag into a validated profile id.
200
+ *
201
+ * Empty input disables profile application; unknown ids throw with the allowed
202
+ * profile list for CLI diagnostics.
203
+ */
204
+ export declare function resolveCreateProfileId(profile?: string): CreateProfileId | undefined;
205
+ export declare function scaffoldProject({ projectDir, templateId, answers, alternateRenderTargets, dataStorageMode, persistencePolicy, packageManager, externalLayerId, externalLayerSource, externalLayerSourceLabel, profile, repositoryReference, cwd, allowExistingDir, noInstall, installDependencies, onProgress, variant, withMigrationUi, withTestPreset, withWpEnv, }: ScaffoldProjectOptions): Promise<ScaffoldProjectResult>;
@@ -3,11 +3,12 @@ import path from "node:path";
3
3
  import { getPackageManager } from "./package-managers.js";
4
4
  import { buildGitignore, buildReadme, mergeTextLines, removeQueryLoopPlaceholderFiles, replaceTextRecursively, } from "./scaffold-apply-utils.js";
5
5
  import { applyGeneratedProjectDxPackageJson, applyLocalDevPresetFiles, } from "./local-dev-presets.js";
6
+ import { runAddIntegrationEnvCommand } from "./cli-add-workspace-integration-env.js";
6
7
  import { applyMigrationUiCapability } from "./migration-ui-capability.js";
7
8
  import { applyWorkspaceMigrationCapability, ensureScaffoldDirectory, isWorkspaceProject, seedBuiltInPersistenceArtifacts, writeStarterManifestFiles, } from "./scaffold-bootstrap.js";
8
9
  import { defaultInstallDependencies, normalizePackageJson, normalizePackageManagerFiles, removeUnexpectedLockfiles, } from "./scaffold-package-manager-files.js";
9
10
  import { copyInterpolatedDirectory } from "./template-render.js";
10
- import { isBuiltInTemplateId, normalizeTemplateLookupId, } from "./template-registry.js";
11
+ import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, isBuiltInTemplateId, normalizeTemplateLookupId, } from "./template-registry.js";
11
12
  import { resolveTemplateSource } from "./template-source.js";
12
13
  import { BlockGeneratorService, } from "./block-generator-service.js";
13
14
  import { getTemplateVariables } from "./scaffold-template-variables.js";
@@ -16,6 +17,8 @@ import { assertExternalLayerCompositionOptions, } from "./cli-validation.js";
16
17
  import { pathExists } from "./fs-async.js";
17
18
  export const DATA_STORAGE_MODES = ["post-meta", "custom-table"];
18
19
  export const PERSISTENCE_POLICIES = ["authenticated", "public"];
20
+ /** Supported create profile ids for opt-in workspace scaffold presets. */
21
+ export const CREATE_PROFILE_IDS = ["plugin-qa"];
19
22
  export { buildBlockCssClassName } from "./scaffold-identifiers.js";
20
23
  export { collectScaffoldAnswers, detectAuthor, getDefaultAnswers, resolvePackageManagerId, resolveTemplateId, } from "./scaffold-answer-resolution.js";
21
24
  export { getTemplateVariables } from "./scaffold-template-variables.js";
@@ -26,16 +29,62 @@ export function isDataStorageMode(value) {
26
29
  export function isPersistencePolicy(value) {
27
30
  return PERSISTENCE_POLICIES.includes(value);
28
31
  }
32
+ /**
33
+ * Return whether a raw string is a supported create profile id.
34
+ */
35
+ export function isCreateProfileId(value) {
36
+ return CREATE_PROFILE_IDS.includes(value);
37
+ }
38
+ /**
39
+ * Resolve an optional create profile flag into a validated profile id.
40
+ *
41
+ * Empty input disables profile application; unknown ids throw with the allowed
42
+ * profile list for CLI diagnostics.
43
+ */
44
+ export function resolveCreateProfileId(profile) {
45
+ if (profile === undefined || profile.trim().length === 0) {
46
+ return undefined;
47
+ }
48
+ const normalizedProfile = profile.trim();
49
+ if (isCreateProfileId(normalizedProfile)) {
50
+ return normalizedProfile;
51
+ }
52
+ throw new Error(`Unknown create profile "${profile}". Expected one of: ${CREATE_PROFILE_IDS.join(", ")}.`);
53
+ }
29
54
  function normalizeTemplateSelection(templateId) {
30
55
  return normalizeTemplateLookupId(templateId);
31
56
  }
57
+ function assertCreateProfileTemplateCompatibility({ profile, templateId, }) {
58
+ if (!profile) {
59
+ return;
60
+ }
61
+ if (profile === "plugin-qa" && templateId !== OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE) {
62
+ throw new Error("`--profile plugin-qa` currently supports only the official workspace template. Use `--template workspace` or add QA files later with `wp-typia add integration-env <name> --wp-env --release-zip`.");
63
+ }
64
+ }
65
+ async function applyCreateProfile({ profile, projectDir, }) {
66
+ if (profile !== "plugin-qa") {
67
+ return;
68
+ }
69
+ await runAddIntegrationEnvCommand({
70
+ cwd: projectDir,
71
+ integrationEnvName: "local-smoke",
72
+ service: "none",
73
+ withReleaseZip: true,
74
+ withWpEnv: true,
75
+ });
76
+ }
32
77
  async function reportScaffoldProgress(onProgress, event) {
33
78
  await onProgress?.(event);
34
79
  }
35
- export async function scaffoldProject({ projectDir, templateId, answers, alternateRenderTargets, dataStorageMode, persistencePolicy, packageManager, externalLayerId, externalLayerSource, externalLayerSourceLabel, repositoryReference, cwd = process.cwd(), allowExistingDir = false, noInstall = false, installDependencies = undefined, onProgress = undefined, variant, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
80
+ export async function scaffoldProject({ projectDir, templateId, answers, alternateRenderTargets, dataStorageMode, persistencePolicy, packageManager, externalLayerId, externalLayerSource, externalLayerSourceLabel, profile, repositoryReference, cwd = process.cwd(), allowExistingDir = false, noInstall = false, installDependencies = undefined, onProgress = undefined, variant, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
36
81
  const resolvedTemplateId = normalizeTemplateSelection(templateId);
37
82
  const resolvedPackageManager = getPackageManager(packageManager).id;
38
83
  const isBuiltInTemplate = isBuiltInTemplateId(resolvedTemplateId);
84
+ assertCreateProfileTemplateCompatibility({
85
+ profile,
86
+ templateId: resolvedTemplateId,
87
+ });
39
88
  assertExternalLayerCompositionOptions({
40
89
  externalLayerId,
41
90
  externalLayerSource,
@@ -174,6 +223,10 @@ export async function scaffoldProject({ projectDir, templateId, answers, alterna
174
223
  withWpEnv,
175
224
  });
176
225
  }
226
+ await applyCreateProfile({
227
+ profile,
228
+ projectDir,
229
+ });
177
230
  await normalizePackageManagerFiles(projectDir, resolvedPackageManager);
178
231
  await removeUnexpectedLockfiles(projectDir, resolvedPackageManager);
179
232
  await replaceTextRecursively(projectDir, resolvedPackageManager, {
@@ -23,7 +23,10 @@ function cleanupTrackedTempRootsSync() {
23
23
  try {
24
24
  fs.rmSync(tempRoot, { force: true, recursive: true });
25
25
  }
26
- catch { }
26
+ catch {
27
+ // Process-exit cleanup is best-effort: shutdown-time rm failures
28
+ // such as EPERM are intentionally ignored rather than made fatal.
29
+ }
27
30
  }
28
31
  }
29
32
  function installCleanupHandlers() {
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { promises as fsp } from "node:fs";
3
3
  import { pathExists } from "./fs-async.js";
4
+ import { readJsonFile } from "./json-utils.js";
4
5
  import { isPlainObject } from "./object-utils.js";
5
6
  import { getBuiltInSharedTemplateLayerDir, isBuiltInSharedTemplateLayerId, } from "./template-builtins.js";
6
7
  import { listInterpolatedDirectoryOutputs } from "./template-render.js";
@@ -59,7 +60,9 @@ export async function loadExternalTemplateLayerManifest(sourceRoot) {
59
60
  if (!(await pathExists(manifestPath))) {
60
61
  return null;
61
62
  }
62
- const raw = JSON.parse(await fsp.readFile(manifestPath, "utf8"));
63
+ const raw = await readJsonFile(manifestPath, {
64
+ context: "template layer manifest",
65
+ });
63
66
  if (!isPlainObject(raw)) {
64
67
  throw new Error(`${TEMPLATE_LAYER_MANIFEST_FILENAME} must export a JSON object.`);
65
68
  }
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { readJsonFileSync } from "./json-utils.js";
4
5
  import { getBuiltInTemplateMetadataDefaults } from "./template-defaults.js";
5
6
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
7
  const PROJECT_TOOLS_PACKAGE_ROOT_ENV = "WP_TYPIA_PROJECT_TOOLS_PACKAGE_ROOT";
@@ -11,7 +12,9 @@ function resolveValidProjectToolsPackageRoot(candidateRoot) {
11
12
  return undefined;
12
13
  }
13
14
  try {
14
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
15
+ const packageJson = readJsonFileSync(packageJsonPath, {
16
+ context: "project-tools package manifest override",
17
+ });
15
18
  return packageJson.name === PROJECT_TOOLS_PACKAGE_NAME
16
19
  ? candidateRoot
17
20
  : undefined;
@@ -42,13 +45,16 @@ export function resolvePackageRoot(startDir) {
42
45
  const packageJsonPath = path.join(currentDir, "package.json");
43
46
  if (fs.existsSync(packageJsonPath)) {
44
47
  try {
45
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
48
+ const packageJson = readJsonFileSync(packageJsonPath, {
49
+ context: "project-tools package root discovery manifest",
50
+ });
46
51
  if (packageJson.name === PROJECT_TOOLS_PACKAGE_NAME) {
47
52
  return currentDir;
48
53
  }
49
54
  }
50
55
  catch {
51
- // Ignore malformed package.json while walking upward.
56
+ // Ignore malformed package.json while walking upward; discovery should
57
+ // keep searching parent directories instead of failing on unrelated files.
52
58
  }
53
59
  }
54
60
  const parentDir = path.dirname(currentDir);
@@ -11,6 +11,8 @@ export interface TemplateVariableContext extends UnknownRecord {
11
11
  blockRuntimePackageVersion: string;
12
12
  /** Version string for `@wp-typia/block-types` used in generated dependencies. */
13
13
  blockTypesPackageVersion: string;
14
+ /** Version string for `wp-typia` used when templates include the CLI binary locally. */
15
+ wpTypiaPackageVersion: string;
14
16
  /** PascalCase block type name derived from the scaffold slug. */
15
17
  pascalCase: string;
16
18
  /** Snake_case PHP symbol prefix used for generated functions, constants, and keys. */
@@ -8,13 +8,14 @@ import { pathExists } from './fs-async.js';
8
8
  export { renderCreateBlockExternalTemplate } from './template-source-external.js';
9
9
  export { getTemplateProjectType, getTemplateProjectTypeAsync, getDefaultCategory, getDefaultCategoryAsync, normalizeCreateBlockSubset, normalizeWpTypiaTemplateSeed, } from './template-source-remote.js';
10
10
  export function getTemplateVariableContext(variables) {
11
- const { apiClientPackageVersion, blockRuntimePackageVersion, blockTypesPackageVersion, projectToolsPackageVersion, restPackageVersion, } = getPackageVersions();
11
+ const { apiClientPackageVersion, blockRuntimePackageVersion, blockTypesPackageVersion, projectToolsPackageVersion, restPackageVersion, wpTypiaPackageVersion, } = getPackageVersions();
12
12
  return {
13
13
  ...variables,
14
14
  apiClientPackageVersion: variables.apiClientPackageVersion ?? apiClientPackageVersion,
15
15
  blockRuntimePackageVersion: variables.blockRuntimePackageVersion ?? blockRuntimePackageVersion,
16
16
  blockTypesPackageVersion: variables.blockTypesPackageVersion ?? blockTypesPackageVersion,
17
17
  projectToolsPackageVersion: variables.projectToolsPackageVersion ?? projectToolsPackageVersion,
18
+ wpTypiaPackageVersion: variables.wpTypiaPackageVersion ?? wpTypiaPackageVersion,
18
19
  description: variables.description,
19
20
  keyword: variables.keyword,
20
21
  namespace: variables.namespace,
@@ -6,6 +6,7 @@ import { getBuiltInTemplateLayerDirs, isOmittableBuiltInTemplateLayerDir, } from
6
6
  import { copyRawDirectory } from './template-render.js';
7
7
  import { createManagedTempRoot } from './temp-roots.js';
8
8
  import { pathExists } from './fs-async.js';
9
+ import { readJsonFile, readJsonFileSync, safeJsonParse, } from './json-utils.js';
9
10
  async function cleanupSeedRootPair(cleanup, seedCleanup) {
10
11
  let cleanupError;
11
12
  try {
@@ -39,7 +40,9 @@ function readRemoteBlockJson(blockDir) {
39
40
  path.join(sourceRoot, 'block.json'),
40
41
  ]) {
41
42
  if (fs.existsSync(candidate)) {
42
- return JSON.parse(fs.readFileSync(candidate, 'utf8'));
43
+ return readJsonFileSync(candidate, {
44
+ context: 'remote block metadata',
45
+ });
43
46
  }
44
47
  }
45
48
  throw new Error(`Unable to locate block.json in ${blockDir}`);
@@ -51,7 +54,9 @@ async function readRemoteBlockJsonAsync(blockDir) {
51
54
  path.join(sourceRoot, 'block.json'),
52
55
  ]) {
53
56
  if (await pathExists(candidate)) {
54
- return JSON.parse(await fsp.readFile(candidate, 'utf8'));
57
+ return readJsonFile(candidate, {
58
+ context: 'remote block metadata',
59
+ });
55
60
  }
56
61
  }
57
62
  throw new Error(`Unable to locate block.json in ${blockDir}`);
@@ -105,7 +110,10 @@ function readTemplatePackageJson(sourceDir) {
105
110
  maxBytes: getExternalTemplatePackageJsonMaxBytes(),
106
111
  });
107
112
  return {
108
- packageJson: JSON.parse(fs.readFileSync(candidate, 'utf8')),
113
+ packageJson: safeJsonParse(fs.readFileSync(candidate, 'utf8'), {
114
+ context: 'template metadata file',
115
+ filePath: candidate,
116
+ }),
109
117
  sourcePath: candidate,
110
118
  };
111
119
  }
@@ -130,7 +138,10 @@ async function readTemplatePackageJsonAsync(sourceDir) {
130
138
  maxBytes: getExternalTemplatePackageJsonMaxBytes(),
131
139
  });
132
140
  return {
133
- packageJson: JSON.parse(await fsp.readFile(candidate, 'utf8')),
141
+ packageJson: safeJsonParse(await fsp.readFile(candidate, 'utf8'), {
142
+ context: 'template metadata file',
143
+ filePath: candidate,
144
+ }),
134
145
  sourcePath: candidate,
135
146
  };
136
147
  }
@@ -327,7 +338,9 @@ async function rewriteBlockJsonImports(directory) {
327
338
  }
328
339
  async function patchRemotePackageJson(templateDir, needsInteractivity) {
329
340
  const packageJsonPath = path.join(templateDir, 'package.json.mustache');
330
- const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, 'utf8'));
341
+ const packageJson = await readJsonFile(packageJsonPath, {
342
+ context: 'remote package template manifest',
343
+ });
331
344
  const existingDependencies = { ...(packageJson.dependencies ?? {}) };
332
345
  const existingDevDependencies = { ...(packageJson.devDependencies ?? {}) };
333
346
  delete existingDependencies['@wp-typia/project-tools'];
@@ -9,6 +9,7 @@ import { createExternalTemplateTimeoutError, fetchWithExternalTemplateTimeout, g
9
9
  import { findReusableExternalTemplateSourceCache, isExternalTemplateCacheEnabled, resolveExternalTemplateSourceCache, } from './template-source-cache.js';
10
10
  import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from './cli-diagnostics.js';
11
11
  import { pathExists } from './fs-async.js';
12
+ import { readJsonFile, readJsonFileSync } from './json-utils.js';
12
13
  import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, OFFICIAL_WORKSPACE_TEMPLATE_ALIAS, PROJECT_TOOLS_PACKAGE_ROOT, TEMPLATE_IDS, } from './template-registry.js';
13
14
  import { isPlainObject } from './object-utils.js';
14
15
  import { createManagedTempRoot } from './temp-roots.js';
@@ -206,7 +207,9 @@ function resolveInstalledNpmTemplateSource(locator, cwd) {
206
207
  if (!fs.existsSync(packageJsonPath)) {
207
208
  continue;
208
209
  }
209
- const manifest = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
210
+ const manifest = readJsonFileSync(packageJsonPath, {
211
+ context: 'workspace template package manifest',
212
+ });
210
213
  if (manifest.name === locator.name) {
211
214
  return {
212
215
  blockDir: packageDir,
@@ -259,7 +262,9 @@ export function isOfficialWorkspaceTemplateSeed(seed) {
259
262
  return false;
260
263
  }
261
264
  try {
262
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
265
+ const packageJson = readJsonFileSync(packageJsonPath, {
266
+ context: 'workspace template seed manifest',
267
+ });
263
268
  return packageJson.name === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE;
264
269
  }
265
270
  catch {
@@ -278,7 +283,9 @@ export async function isOfficialWorkspaceTemplateSeedAsync(seed) {
278
283
  return false;
279
284
  }
280
285
  try {
281
- const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, 'utf8'));
286
+ const packageJson = await readJsonFile(packageJsonPath, {
287
+ context: 'workspace template seed manifest',
288
+ });
282
289
  return packageJson.name === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE;
283
290
  }
284
291
  catch {
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { readFile, writeFile } from "node:fs/promises";
3
3
  import { escapeRegex } from "./php-utils.js";
4
- import { BLOCK_INVENTORY_SECTION, INVENTORY_SECTIONS, } from "./workspace-inventory-parser.js";
4
+ import { BLOCK_INVENTORY_SECTION, INVENTORY_SECTIONS, } from "./workspace-inventory-section-descriptors.js";
5
5
  import { WORKSPACE_COMPATIBILITY_CONFIG_FIELD } from "./workspace-inventory-templates.js";
6
6
  function ensureWorkspaceInventorySections(source) {
7
7
  let nextSource = source.trimEnd();
@@ -157,6 +157,7 @@ export function updateWorkspaceInventorySource(source, options = {}) {
157
157
  ["responseTypeName", "\tresponseTypeName?: string;"],
158
158
  ["routePattern", "\troutePattern?: string;"],
159
159
  ["secretFieldName", "\tsecretFieldName?: string;"],
160
+ ["secretPreserveOnEmpty", "\tsecretPreserveOnEmpty?: boolean;"],
160
161
  ["secretStateFieldName", "\tsecretStateFieldName?: string;"],
161
162
  ]) {
162
163
  nextSource = upsertInterfaceField(nextSource, "WorkspaceRestResourceConfig", fieldName, fieldSource);