@wp-typia/project-tools 0.21.0 → 0.22.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 (34) hide show
  1. package/dist/runtime/ai-feature-capability.js +2 -33
  2. package/dist/runtime/built-in-block-artifact-types.js +11 -0
  3. package/dist/runtime/built-in-block-code-artifacts.js +5 -1
  4. package/dist/runtime/built-in-block-code-templates/interactivity.d.ts +4 -3
  5. package/dist/runtime/built-in-block-code-templates/interactivity.js +259 -100
  6. package/dist/runtime/built-in-block-code-templates.d.ts +1 -1
  7. package/dist/runtime/built-in-block-code-templates.js +1 -1
  8. package/dist/runtime/cli-add-shared.d.ts +4 -3
  9. package/dist/runtime/cli-add-shared.js +5 -2
  10. package/dist/runtime/cli-add-workspace-ability.js +3 -4
  11. package/dist/runtime/cli-add-workspace-admin-view-scaffold.d.ts +9 -0
  12. package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +257 -0
  13. package/dist/runtime/cli-add-workspace-admin-view-source.d.ts +5 -0
  14. package/dist/runtime/cli-add-workspace-admin-view-source.js +86 -0
  15. package/dist/runtime/cli-add-workspace-admin-view-templates.d.ts +23 -0
  16. package/dist/runtime/cli-add-workspace-admin-view-templates.js +991 -0
  17. package/dist/runtime/cli-add-workspace-admin-view-types.d.ts +29 -0
  18. package/dist/runtime/cli-add-workspace-admin-view-types.js +29 -0
  19. package/dist/runtime/cli-add-workspace-admin-view.d.ts +1 -14
  20. package/dist/runtime/cli-add-workspace-admin-view.js +23 -860
  21. package/dist/runtime/cli-doctor-workspace.js +5 -4
  22. package/dist/runtime/cli-help.js +5 -2
  23. package/dist/runtime/external-layer-selection.d.ts +8 -2
  24. package/dist/runtime/external-layer-selection.js +3 -4
  25. package/dist/runtime/package-versions.d.ts +28 -0
  26. package/dist/runtime/package-versions.js +79 -36
  27. package/dist/runtime/scaffold-compatibility.d.ts +2 -2
  28. package/dist/runtime/scaffold-compatibility.js +22 -48
  29. package/dist/runtime/string-case.d.ts +7 -0
  30. package/dist/runtime/string-case.js +21 -11
  31. package/dist/runtime/version-floor.d.ts +26 -0
  32. package/dist/runtime/version-floor.js +56 -0
  33. package/dist/runtime/workspace-inventory.d.ts +2 -1
  34. package/package.json +3 -2
@@ -814,15 +814,16 @@ function checkWorkspaceAdminViewConfig(adminView, inventory) {
814
814
  return createDoctorCheck(`Admin view config ${adminView.slug}`, "pass", "Admin view uses a replaceable local fetcher");
815
815
  }
816
816
  const source = adminView.source.trim();
817
- const sourceMatch = /^rest-resource:([a-z][a-z0-9-]*)$/u.exec(source);
818
- const restResourceSlug = sourceMatch?.[1];
817
+ const restSourceMatch = /^rest-resource:([a-z][a-z0-9-]*)$/u.exec(source);
818
+ const coreDataSourceMatch = /^core-data:(postType|taxonomy)\/([a-z0-9][a-z0-9_-]*)$/u.exec(source);
819
+ const restResourceSlug = restSourceMatch?.[1];
819
820
  const restResource = restResourceSlug
820
821
  ? inventory.restResources.find((entry) => entry.slug === restResourceSlug)
821
822
  : undefined;
822
- const isValid = Boolean(restResource?.methods.includes("list"));
823
+ const isValid = Boolean(restResource?.methods.includes("list")) || Boolean(coreDataSourceMatch);
823
824
  return createDoctorCheck(`Admin view config ${adminView.slug}`, isValid ? "pass" : "fail", isValid
824
825
  ? `Admin view source ${source} is list-capable`
825
- : "Admin view source must use rest-resource:<slug> and reference a list-capable REST resource");
826
+ : "Admin view source must use rest-resource:<slug> with a list-capable REST resource or core-data:<postType|taxonomy>/<name>");
826
827
  }
827
828
  function checkWorkspaceAdminViewBootstrap(projectDir, packageName, phpPrefix) {
828
829
  const packageBaseName = packageName.split("/").pop() ?? packageName;
@@ -18,7 +18,7 @@ export function formatHelpText() {
18
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
20
  wp-typia init [project-dir] [--apply] [--package-manager <id>]
21
- wp-typia add admin-view <name> [--source <rest-resource:slug>]
21
+ wp-typia add admin-view <name> [--source <rest-resource:slug|core-data:kind/name>]
22
22
  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>]
23
23
  wp-typia add variation <name> --block <block-slug>
24
24
  wp-typia add style <name> --block <block-slug>
@@ -49,7 +49,10 @@ Notes:
49
49
  \`wp-typia init\` previews the minimum retrofit sync surface by default; rerun with \`--apply\` to write package.json updates and generated helper scripts.
50
50
  Use \`--template workspace\` as shorthand for \`@wp-typia/create-workspace-template\`, the official empty workspace scaffold behind \`wp-typia add ...\`.
51
51
  Interactive add flows let you choose a template when \`--template\` is omitted; non-interactive runs default to \`basic\`.
52
- \`add admin-view\` scaffolds an opt-in DataViews-powered WordPress admin screen under \`src/admin-views/\`; pass \`--source rest-resource:<slug>\` to reuse a list-capable REST resource.
52
+ \`add admin-view\` scaffolds an opt-in DataViews-powered WordPress admin screen under \`src/admin-views/\`.
53
+ Pass \`--source rest-resource:<slug>\` to reuse a list-capable REST resource.
54
+ Pass \`--source core-data:postType/post\` or \`--source core-data:taxonomy/category\` to bind a WordPress-owned entity collection.
55
+ Public installs currently gate this workflow until \`@wp-typia/dataviews\` is published to npm.
53
56
  \`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.
54
57
  \`add variation\` uses an existing workspace block from \`scripts/block-config.ts\`.
55
58
  \`add style\` registers a Block Styles option for an existing generated block.
@@ -1,4 +1,5 @@
1
- import { type SelectableExternalTemplateLayer } from "./template-layers.js";
1
+ import { resolveTemplateSeed } from "./template-source.js";
2
+ import { listSelectableExternalTemplateLayers, type SelectableExternalTemplateLayer } from "./template-layers.js";
2
3
  export interface ExternalLayerSelectionOption extends SelectableExternalTemplateLayer {
3
4
  }
4
5
  export interface ResolvedExternalLayerSelection {
@@ -6,9 +7,14 @@ export interface ResolvedExternalLayerSelection {
6
7
  externalLayerId?: string;
7
8
  externalLayerSource?: string;
8
9
  }
9
- export declare function resolveOptionalInteractiveExternalLayerId({ callerCwd, externalLayerId, externalLayerSource, selectExternalLayerId, }: {
10
+ type ResolveExternalTemplateSeed = typeof resolveTemplateSeed;
11
+ type ListExternalTemplateLayers = typeof listSelectableExternalTemplateLayers;
12
+ export declare function resolveOptionalInteractiveExternalLayerId({ callerCwd, externalLayerId, externalLayerSource, listExternalTemplateLayers, resolveExternalTemplateSeed, selectExternalLayerId, }: {
10
13
  callerCwd: string;
11
14
  externalLayerId?: string;
12
15
  externalLayerSource?: string;
16
+ listExternalTemplateLayers?: ListExternalTemplateLayers;
17
+ resolveExternalTemplateSeed?: ResolveExternalTemplateSeed;
13
18
  selectExternalLayerId?: (options: ExternalLayerSelectionOption[]) => Promise<string>;
14
19
  }): Promise<ResolvedExternalLayerSelection>;
20
+ export {};
@@ -1,15 +1,15 @@
1
1
  import { parseTemplateLocator, resolveTemplateSeed, } from "./template-source.js";
2
2
  import { listSelectableExternalTemplateLayers, } from "./template-layers.js";
3
- export async function resolveOptionalInteractiveExternalLayerId({ callerCwd, externalLayerId, externalLayerSource, selectExternalLayerId, }) {
3
+ export async function resolveOptionalInteractiveExternalLayerId({ callerCwd, externalLayerId, externalLayerSource, listExternalTemplateLayers = listSelectableExternalTemplateLayers, resolveExternalTemplateSeed = resolveTemplateSeed, selectExternalLayerId, }) {
4
4
  if (!externalLayerSource || externalLayerId || !selectExternalLayerId) {
5
5
  return {
6
6
  externalLayerId,
7
7
  externalLayerSource,
8
8
  };
9
9
  }
10
- const layerSeed = await resolveTemplateSeed(parseTemplateLocator(externalLayerSource), callerCwd);
10
+ const layerSeed = await resolveExternalTemplateSeed(parseTemplateLocator(externalLayerSource), callerCwd);
11
11
  try {
12
- const selectableLayers = await listSelectableExternalTemplateLayers(layerSeed.rootDir);
12
+ const selectableLayers = await listExternalTemplateLayers(layerSeed.rootDir);
13
13
  if (selectableLayers.length <= 1) {
14
14
  await layerSeed.cleanup?.();
15
15
  return {
@@ -25,7 +25,6 @@ export async function resolveOptionalInteractiveExternalLayerId({ callerCwd, ext
25
25
  externalLayerSource: layerSeed.rootDir,
26
26
  };
27
27
  }
28
- await layerSeed.cleanup?.();
29
28
  throw new Error(`Unknown external layer "${selectedLayerId}". Expected one of: ${selectableLayers.map((layer) => layer.id).join(", ")}`);
30
29
  }
31
30
  catch (error) {
@@ -11,6 +11,34 @@ export interface PackageVersions {
11
11
  wpTypiaPackageExactVersion: string;
12
12
  wpTypiaPackageVersion: string;
13
13
  }
14
+ /**
15
+ * Explicit fallback ranges for managed WordPress-facing workspace dependencies.
16
+ *
17
+ * These remain centralized here even when individual scaffold flows resolve a
18
+ * fresher local or installed manifest version first, so add-command defaults do
19
+ * not drift across runtime modules.
20
+ */
21
+ export declare const DEFAULT_WORDPRESS_ABILITIES_VERSION = "^0.10.0";
22
+ export declare const DEFAULT_WORDPRESS_CORE_ABILITIES_VERSION = "^0.9.0";
23
+ export declare const DEFAULT_WORDPRESS_CORE_DATA_VERSION = "^7.44.0";
24
+ export declare const DEFAULT_WORDPRESS_DATA_VERSION = "^9.28.0";
25
+ export declare const DEFAULT_WORDPRESS_DATAVIEWS_VERSION = "^14.1.0";
26
+ export declare const DEFAULT_WP_TYPIA_DATAVIEWS_VERSION = "^0.1.0";
27
+ /**
28
+ * Resolve a managed package version range from linked workspace packages first,
29
+ * then installed package manifests, while preserving the shared normalization
30
+ * and manifest fingerprinting rules used by `getPackageVersions()`.
31
+ *
32
+ * @param packageName npm package whose manifest version should be consulted.
33
+ * @param fallback Canonical range to use when no usable manifest version exists.
34
+ * @param workspacePackageDirName Optional sibling monorepo package directory name.
35
+ * @returns A normalized semver range suitable for generated dependency entries.
36
+ */
37
+ export declare function resolveManagedPackageVersionRange(options: {
38
+ fallback: string;
39
+ packageName: string;
40
+ workspacePackageDirName?: string;
41
+ }): string;
14
42
  /**
15
43
  * Clears the in-memory cache used by `getPackageVersions()`.
16
44
  *
@@ -1,15 +1,28 @@
1
- import fs from "node:fs";
2
- import { createRequire } from "node:module";
3
- import path from "node:path";
4
- import { PROJECT_TOOLS_PACKAGE_ROOT } from "./template-registry.js";
1
+ import fs from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import path from 'node:path';
4
+ import { PROJECT_TOOLS_PACKAGE_ROOT } from './template-registry.js';
5
5
  const require = createRequire(import.meta.url);
6
- const DEFAULT_VERSION_RANGE = "^0.0.0";
7
- const DEFAULT_EXACT_VERSION = "0.0.0";
8
- const DEFAULT_TSX_PACKAGE_VERSION = "^4.20.5";
9
- const DEFAULT_TYPIA_UNPLUGIN_PACKAGE_VERSION = "^12.0.1";
6
+ const DEFAULT_VERSION_RANGE = '^0.0.0';
7
+ const DEFAULT_EXACT_VERSION = '0.0.0';
8
+ const DEFAULT_TSX_PACKAGE_VERSION = '^4.20.5';
9
+ const DEFAULT_TYPIA_UNPLUGIN_PACKAGE_VERSION = '^12.0.1';
10
+ /**
11
+ * Explicit fallback ranges for managed WordPress-facing workspace dependencies.
12
+ *
13
+ * These remain centralized here even when individual scaffold flows resolve a
14
+ * fresher local or installed manifest version first, so add-command defaults do
15
+ * not drift across runtime modules.
16
+ */
17
+ export const DEFAULT_WORDPRESS_ABILITIES_VERSION = '^0.10.0';
18
+ export const DEFAULT_WORDPRESS_CORE_ABILITIES_VERSION = '^0.9.0';
19
+ export const DEFAULT_WORDPRESS_CORE_DATA_VERSION = '^7.44.0';
20
+ export const DEFAULT_WORDPRESS_DATA_VERSION = '^9.28.0';
21
+ export const DEFAULT_WORDPRESS_DATAVIEWS_VERSION = '^14.1.0';
22
+ export const DEFAULT_WP_TYPIA_DATAVIEWS_VERSION = '^0.1.0';
10
23
  let cachedPackageVersions = null;
11
24
  function getErrorCode(error) {
12
- return typeof error === "object" && error !== null && "code" in error
25
+ return typeof error === 'object' && error !== null && 'code' in error
13
26
  ? String(error.code)
14
27
  : undefined;
15
28
  }
@@ -18,7 +31,7 @@ function normalizeVersionRange(value) {
18
31
  if (!trimmed) {
19
32
  return DEFAULT_VERSION_RANGE;
20
33
  }
21
- if (trimmed.startsWith("workspace:")) {
34
+ if (trimmed.startsWith('workspace:')) {
22
35
  return DEFAULT_VERSION_RANGE;
23
36
  }
24
37
  return /^[~^<>=]/.test(trimmed) ? trimmed : `^${trimmed}`;
@@ -28,10 +41,10 @@ function normalizeExactVersion(value) {
28
41
  if (!trimmed) {
29
42
  return DEFAULT_EXACT_VERSION;
30
43
  }
31
- if (trimmed.startsWith("workspace:")) {
44
+ if (trimmed.startsWith('workspace:')) {
32
45
  return DEFAULT_EXACT_VERSION;
33
46
  }
34
- return trimmed.replace(/^[~^<>=]+/, "");
47
+ return trimmed.replace(/^[~^<>=]+/, '');
35
48
  }
36
49
  function normalizeVersionRangeWithFallback(value, fallback) {
37
50
  const normalized = normalizeVersionRange(value);
@@ -48,7 +61,7 @@ function createContentFingerprint(source) {
48
61
  function resolvePackageManifestLocation(packageJsonPath) {
49
62
  try {
50
63
  const stats = fs.statSync(packageJsonPath);
51
- const source = fs.readFileSync(packageJsonPath, "utf8");
64
+ const source = fs.readFileSync(packageJsonPath, 'utf8');
52
65
  return {
53
66
  cacheKey: `file:${packageJsonPath}:${stats.ino}:${stats.mtimeMs}:${stats.ctimeMs}:${stats.size}:${createContentFingerprint(source)}`,
54
67
  packageJsonPath,
@@ -56,7 +69,7 @@ function resolvePackageManifestLocation(packageJsonPath) {
56
69
  };
57
70
  }
58
71
  catch (error) {
59
- if (getErrorCode(error) === "ENOENT") {
72
+ if (getErrorCode(error) === 'ENOENT') {
60
73
  return {
61
74
  cacheKey: `missing-file:${packageJsonPath}`,
62
75
  packageJsonPath: null,
@@ -72,12 +85,23 @@ function readPackageManifest(location) {
72
85
  }
73
86
  return JSON.parse(location.source);
74
87
  }
88
+ function tryReadPackageManifest(location) {
89
+ if (!location) {
90
+ return null;
91
+ }
92
+ try {
93
+ return readPackageManifest(location);
94
+ }
95
+ catch {
96
+ return null;
97
+ }
98
+ }
75
99
  function resolveInstalledPackageManifestLocation(packageName) {
76
100
  try {
77
101
  return resolvePackageManifestLocation(require.resolve(`${packageName}/package.json`));
78
102
  }
79
103
  catch (error) {
80
- if (getErrorCode(error) === "MODULE_NOT_FOUND") {
104
+ if (getErrorCode(error) === 'MODULE_NOT_FOUND') {
81
105
  return {
82
106
  cacheKey: `missing-module:${packageName}`,
83
107
  packageJsonPath: null,
@@ -88,7 +112,26 @@ function resolveInstalledPackageManifestLocation(packageName) {
88
112
  }
89
113
  }
90
114
  function composePackageVersionsCacheKey(locations) {
91
- return locations.map((location) => location.cacheKey).join("|");
115
+ return locations.map((location) => location.cacheKey).join('|');
116
+ }
117
+ /**
118
+ * Resolve a managed package version range from linked workspace packages first,
119
+ * then installed package manifests, while preserving the shared normalization
120
+ * and manifest fingerprinting rules used by `getPackageVersions()`.
121
+ *
122
+ * @param packageName npm package whose manifest version should be consulted.
123
+ * @param fallback Canonical range to use when no usable manifest version exists.
124
+ * @param workspacePackageDirName Optional sibling monorepo package directory name.
125
+ * @returns A normalized semver range suitable for generated dependency entries.
126
+ */
127
+ export function resolveManagedPackageVersionRange(options) {
128
+ const workspaceManifestLocation = options.workspacePackageDirName
129
+ ? resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, '..', options.workspacePackageDirName, 'package.json'))
130
+ : null;
131
+ const installedManifestLocation = resolveInstalledPackageManifestLocation(options.packageName);
132
+ const workspaceManifest = tryReadPackageManifest(workspaceManifestLocation);
133
+ const installedManifest = tryReadPackageManifest(installedManifestLocation);
134
+ return normalizeVersionRangeWithFallback(workspaceManifest?.version ?? installedManifest?.version, options.fallback);
92
135
  }
93
136
  /**
94
137
  * Clears the in-memory cache used by `getPackageVersions()`.
@@ -115,20 +158,20 @@ export function invalidatePackageVersionsCache() {
115
158
  * disk, the cache key changes and the returned version object is refreshed.
116
159
  */
117
160
  export function getPackageVersions() {
118
- const createManifestLocation = resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, "package.json"));
119
- const monorepoManifestLocation = resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, "..", "..", "package.json"));
120
- const blockRuntimeManifestLocation = resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, "..", "wp-typia-block-runtime", "package.json"));
121
- const wpTypiaManifestLocation = resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, "..", "wp-typia", "package.json"));
122
- const installedProjectToolsManifestLocation = resolveInstalledPackageManifestLocation("@wp-typia/project-tools");
123
- const installedApiClientManifestLocation = resolveInstalledPackageManifestLocation("@wp-typia/api-client");
124
- const installedBlockRuntimeManifestLocation = resolveInstalledPackageManifestLocation("@wp-typia/block-runtime");
125
- const installedBlockTypesManifestLocation = resolveInstalledPackageManifestLocation("@wp-typia/block-types");
126
- const installedRestManifestLocation = resolveInstalledPackageManifestLocation("@wp-typia/rest");
127
- const installedTsxManifestLocation = resolveInstalledPackageManifestLocation("tsx");
128
- const installedTypiaManifestLocation = resolveInstalledPackageManifestLocation("typia");
129
- const installedTypiaUnpluginManifestLocation = resolveInstalledPackageManifestLocation("@typia/unplugin");
130
- const installedTypescriptManifestLocation = resolveInstalledPackageManifestLocation("typescript");
131
- const installedWpTypiaManifestLocation = resolveInstalledPackageManifestLocation("wp-typia");
161
+ const createManifestLocation = resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, 'package.json'));
162
+ const monorepoManifestLocation = resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, '..', '..', 'package.json'));
163
+ const blockRuntimeManifestLocation = resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, '..', 'wp-typia-block-runtime', 'package.json'));
164
+ const wpTypiaManifestLocation = resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, '..', 'wp-typia', 'package.json'));
165
+ const installedProjectToolsManifestLocation = resolveInstalledPackageManifestLocation('@wp-typia/project-tools');
166
+ const installedApiClientManifestLocation = resolveInstalledPackageManifestLocation('@wp-typia/api-client');
167
+ const installedBlockRuntimeManifestLocation = resolveInstalledPackageManifestLocation('@wp-typia/block-runtime');
168
+ const installedBlockTypesManifestLocation = resolveInstalledPackageManifestLocation('@wp-typia/block-types');
169
+ const installedRestManifestLocation = resolveInstalledPackageManifestLocation('@wp-typia/rest');
170
+ const installedTsxManifestLocation = resolveInstalledPackageManifestLocation('tsx');
171
+ const installedTypiaManifestLocation = resolveInstalledPackageManifestLocation('typia');
172
+ const installedTypiaUnpluginManifestLocation = resolveInstalledPackageManifestLocation('@typia/unplugin');
173
+ const installedTypescriptManifestLocation = resolveInstalledPackageManifestLocation('typescript');
174
+ const installedWpTypiaManifestLocation = resolveInstalledPackageManifestLocation('wp-typia');
132
175
  const cacheKey = composePackageVersionsCacheKey([
133
176
  createManifestLocation,
134
177
  monorepoManifestLocation,
@@ -158,17 +201,17 @@ export function getPackageVersions() {
158
201
  const wpTypiaManifest = readPackageManifest(wpTypiaManifestLocation) ??
159
202
  readPackageManifest(installedWpTypiaManifestLocation) ??
160
203
  {};
161
- const blockRuntimeDependencyVersion = normalizeVersionRange(createManifest.dependencies?.["@wp-typia/block-runtime"]);
204
+ const blockRuntimeDependencyVersion = normalizeVersionRange(createManifest.dependencies?.['@wp-typia/block-runtime']);
162
205
  const versions = {
163
- apiClientPackageVersion: normalizeVersionRange(createManifest.dependencies?.["@wp-typia/api-client"] ??
206
+ apiClientPackageVersion: normalizeVersionRange(createManifest.dependencies?.['@wp-typia/api-client'] ??
164
207
  readPackageManifest(installedApiClientManifestLocation)?.version),
165
208
  blockRuntimePackageVersion: blockRuntimeDependencyVersion !== DEFAULT_VERSION_RANGE
166
209
  ? blockRuntimeDependencyVersion
167
210
  : normalizeVersionRange(blockRuntimeManifest.version),
168
- blockTypesPackageVersion: normalizeVersionRange(createManifest.dependencies?.["@wp-typia/block-types"] ??
211
+ blockTypesPackageVersion: normalizeVersionRange(createManifest.dependencies?.['@wp-typia/block-types'] ??
169
212
  readPackageManifest(installedBlockTypesManifestLocation)?.version),
170
213
  projectToolsPackageVersion: normalizeVersionRange(createManifest.version),
171
- restPackageVersion: normalizeVersionRange(createManifest.dependencies?.["@wp-typia/rest"] ??
214
+ restPackageVersion: normalizeVersionRange(createManifest.dependencies?.['@wp-typia/rest'] ??
172
215
  readPackageManifest(installedRestManifestLocation)?.version),
173
216
  tsxPackageVersion: normalizeVersionRangeWithFallback(monorepoManifest.dependencies?.tsx ??
174
217
  monorepoManifest.devDependencies?.tsx ??
@@ -177,8 +220,8 @@ export function getPackageVersions() {
177
220
  monorepoManifest.devDependencies?.typia ??
178
221
  createManifest.dependencies?.typia ??
179
222
  readPackageManifest(installedTypiaManifestLocation)?.version, DEFAULT_VERSION_RANGE),
180
- typiaUnpluginPackageVersion: normalizeVersionRangeWithFallback(monorepoManifest.dependencies?.["@typia/unplugin"] ??
181
- monorepoManifest.devDependencies?.["@typia/unplugin"] ??
223
+ typiaUnpluginPackageVersion: normalizeVersionRangeWithFallback(monorepoManifest.dependencies?.['@typia/unplugin'] ??
224
+ monorepoManifest.devDependencies?.['@typia/unplugin'] ??
182
225
  readPackageManifest(installedTypiaUnpluginManifestLocation)?.version, DEFAULT_TYPIA_UNPLUGIN_PACKAGE_VERSION),
183
226
  typescriptPackageVersion: normalizeVersionRangeWithFallback(monorepoManifest.dependencies?.typescript ??
184
227
  monorepoManifest.devDependencies?.typescript ??
@@ -4,7 +4,7 @@
4
4
  * The policy keeps plugin headers, runtime gates, and workspace inventory
5
5
  * metadata aligned when optional or required AI-capable features are added.
6
6
  */
7
- import { type AiFeatureCapabilitySelection, type AiFeatureCompatibilityFloor, type ResolvedAiFeatureCapabilityPlan } from "./ai-feature-capability.js";
7
+ import { type AiFeatureCapabilitySelection, type AiFeatureCompatibilityFloor, type ResolvedAiFeatureCapabilityPlan } from './ai-feature-capability.js';
8
8
  /**
9
9
  * WordPress plugin header version floors emitted by scaffold templates.
10
10
  */
@@ -25,7 +25,7 @@ export interface ScaffoldCompatibilityPolicy {
25
25
  */
26
26
  export interface ScaffoldCompatibilityConfig {
27
27
  hardMinimums: AiFeatureCompatibilityFloor;
28
- mode: "baseline" | "optional" | "required";
28
+ mode: 'baseline' | 'optional' | 'required';
29
29
  optionalFeatures: string[];
30
30
  requiredFeatures: string[];
31
31
  runtimeGates: string[];
@@ -4,14 +4,15 @@
4
4
  * The policy keeps plugin headers, runtime gates, and workspace inventory
5
5
  * metadata aligned when optional or required AI-capable features are added.
6
6
  */
7
- import { AI_FEATURE_DEFINITIONS, resolveAiFeatureCapabilityPlan, } from "./ai-feature-capability.js";
7
+ import { AI_FEATURE_DEFINITIONS, resolveAiFeatureCapabilityPlan, } from './ai-feature-capability.js';
8
+ import { pickHigherVersionFloor } from './version-floor.js';
8
9
  /**
9
10
  * Baseline headers used by scaffold output before optional features are added.
10
11
  */
11
12
  export const DEFAULT_SCAFFOLD_COMPATIBILITY = {
12
- requiresAtLeast: "6.7",
13
- requiresPhp: "8.0",
14
- testedUpTo: "6.9",
13
+ requiresAtLeast: '6.7',
14
+ requiresPhp: '8.0',
15
+ testedUpTo: '6.9',
15
16
  };
16
17
  /**
17
18
  * Optional WordPress AI Client surface used by server-only AI feature scaffold.
@@ -19,7 +20,7 @@ export const DEFAULT_SCAFFOLD_COMPATIBILITY = {
19
20
  export const OPTIONAL_WORDPRESS_AI_CLIENT_COMPATIBILITY = [
20
21
  {
21
22
  featureId: AI_FEATURE_DEFINITIONS.wordpressAiClient.id,
22
- mode: "optional",
23
+ mode: 'optional',
23
24
  },
24
25
  ];
25
26
  /**
@@ -28,42 +29,15 @@ export const OPTIONAL_WORDPRESS_AI_CLIENT_COMPATIBILITY = [
28
29
  export const REQUIRED_WORKSPACE_ABILITY_COMPATIBILITY = [
29
30
  {
30
31
  featureId: AI_FEATURE_DEFINITIONS.wordpressServerAbilities.id,
31
- mode: "required",
32
+ mode: 'required',
32
33
  },
33
34
  {
34
35
  featureId: AI_FEATURE_DEFINITIONS.wordpressCoreAbilities.id,
35
- mode: "required",
36
+ mode: 'required',
36
37
  },
37
38
  ];
38
- function parseVersionFloorParts(value) {
39
- return value.split(".").map((part, index) => {
40
- if (!/^\d+$/u.test(part)) {
41
- throw new Error(`parseVersionFloorParts received an invalid version floor "${value}" at segment ${index + 1}.`);
42
- }
43
- return Number.parseInt(part, 10);
44
- });
45
- }
46
- function compareVersionFloors(left, right) {
47
- const leftParts = parseVersionFloorParts(left);
48
- const rightParts = parseVersionFloorParts(right);
49
- const length = Math.max(leftParts.length, rightParts.length);
50
- for (let index = 0; index < length; index += 1) {
51
- const leftValue = leftParts[index] ?? 0;
52
- const rightValue = rightParts[index] ?? 0;
53
- if (leftValue > rightValue) {
54
- return 1;
55
- }
56
- if (leftValue < rightValue) {
57
- return -1;
58
- }
59
- }
60
- return 0;
61
- }
62
- function pickHigherVersionFloor(current, candidate) {
63
- if (!candidate) {
64
- return current;
65
- }
66
- return compareVersionFloors(current, candidate) >= 0 ? current : candidate;
39
+ function pickHigherScaffoldVersionFloor(current, candidate) {
40
+ return pickHigherVersionFloor(current, candidate) ?? current;
67
41
  }
68
42
  function pickHigherHeaderVersionFloor(policyValue, currentValue) {
69
43
  const normalizedCurrentValue = currentValue.trim();
@@ -71,7 +45,7 @@ function pickHigherHeaderVersionFloor(policyValue, currentValue) {
71
45
  return policyValue;
72
46
  }
73
47
  try {
74
- return pickHigherVersionFloor(policyValue, normalizedCurrentValue);
48
+ return pickHigherScaffoldVersionFloor(policyValue, normalizedCurrentValue);
75
49
  }
76
50
  catch {
77
51
  return policyValue;
@@ -82,21 +56,21 @@ function formatRuntimeGate(feature) {
82
56
  }
83
57
  function getPolicyMode(capabilityPlan) {
84
58
  if (capabilityPlan.requiredFeatures.length > 0) {
85
- return "required";
59
+ return 'required';
86
60
  }
87
61
  if (capabilityPlan.optionalFeatures.length > 0) {
88
- return "optional";
62
+ return 'optional';
89
63
  }
90
- return "baseline";
64
+ return 'baseline';
91
65
  }
92
66
  /**
93
67
  * Resolve plugin header floors and capability gates for scaffold selections.
94
68
  */
95
69
  export function resolveScaffoldCompatibilityPolicy(selections, { baseline = DEFAULT_SCAFFOLD_COMPATIBILITY, } = {}) {
96
70
  const capabilityPlan = resolveAiFeatureCapabilityPlan(selections);
97
- const requiresAtLeast = pickHigherVersionFloor(baseline.requiresAtLeast, capabilityPlan.hardMinimums.wordpress);
98
- const requiresPhp = pickHigherVersionFloor(baseline.requiresPhp, capabilityPlan.hardMinimums.php);
99
- const testedUpTo = pickHigherVersionFloor(baseline.testedUpTo, requiresAtLeast);
71
+ const requiresAtLeast = pickHigherScaffoldVersionFloor(baseline.requiresAtLeast, capabilityPlan.hardMinimums.wordpress);
72
+ const requiresPhp = pickHigherScaffoldVersionFloor(baseline.requiresPhp, capabilityPlan.hardMinimums.php);
73
+ const testedUpTo = pickHigherScaffoldVersionFloor(baseline.testedUpTo, requiresAtLeast);
100
74
  return {
101
75
  capabilityPlan,
102
76
  pluginHeader: {
@@ -125,16 +99,16 @@ export function createScaffoldCompatibilityConfig(policy) {
125
99
  /**
126
100
  * Render compatibility metadata as formatted TypeScript object literal JSON.
127
101
  */
128
- export function renderScaffoldCompatibilityConfig(policy, indent = "\t\t") {
102
+ export function renderScaffoldCompatibilityConfig(policy, indent = '\t\t') {
129
103
  const config = createScaffoldCompatibilityConfig(policy);
130
- return JSON.stringify(config, null, "\t")
131
- .split("\n")
104
+ return JSON.stringify(config, null, '\t')
105
+ .split('\n')
132
106
  .map((line, index) => (index === 0 ? line : `${indent}${line}`))
133
- .join("\n");
107
+ .join('\n');
134
108
  }
135
109
  function replacePluginHeaderVersionFloor(source, pattern, policyValue) {
136
110
  return source.replace(pattern, (_match, prefix, currentValue, lineEnding) => {
137
- const versionPrefix = prefix.endsWith(":") ? `${prefix} ` : prefix;
111
+ const versionPrefix = prefix.endsWith(':') ? `${prefix} ` : prefix;
138
112
  return `${versionPrefix}${pickHigherHeaderVersionFloor(policyValue, currentValue)}${lineEnding}`;
139
113
  });
140
114
  }
@@ -19,6 +19,13 @@ export declare function toSnakeCase(input: string): string;
19
19
  * @returns A PascalCase string derived from the normalized kebab-case form.
20
20
  */
21
21
  export declare function toPascalCase(input: string): string;
22
+ /**
23
+ * Normalize arbitrary text into a camelCase identifier.
24
+ *
25
+ * @param input Raw text that may contain spaces, punctuation, or camelCase.
26
+ * @returns A camelCase string derived from the normalized PascalCase form.
27
+ */
28
+ export declare function toCamelCase(input: string): string;
22
29
  /**
23
30
  * Convert delimited text to PascalCase while preserving each segment's
24
31
  * existing internal casing.
@@ -10,10 +10,10 @@ function capitalizeSegment(segment) {
10
10
  export function toKebabCase(input) {
11
11
  return input
12
12
  .trim()
13
- .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
14
- .replace(/[^A-Za-z0-9]+/g, "-")
15
- .replace(/^-+|-+$/g, "")
16
- .replace(/-{2,}/g, "-")
13
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
14
+ .replace(/[^A-Za-z0-9]+/g, '-')
15
+ .replace(/^-+|-+$/g, '')
16
+ .replace(/-{2,}/g, '-')
17
17
  .toLowerCase();
18
18
  }
19
19
  /**
@@ -23,7 +23,7 @@ export function toKebabCase(input) {
23
23
  * @returns A lowercase snake_case string derived from the kebab-case form.
24
24
  */
25
25
  export function toSnakeCase(input) {
26
- return toKebabCase(input).replace(/-/g, "_");
26
+ return toKebabCase(input).replace(/-/g, '_');
27
27
  }
28
28
  /**
29
29
  * Normalize arbitrary text into a PascalCase identifier.
@@ -33,10 +33,20 @@ export function toSnakeCase(input) {
33
33
  */
34
34
  export function toPascalCase(input) {
35
35
  return toKebabCase(input)
36
- .split("-")
36
+ .split('-')
37
37
  .filter(Boolean)
38
38
  .map(capitalizeSegment)
39
- .join("");
39
+ .join('');
40
+ }
41
+ /**
42
+ * Normalize arbitrary text into a camelCase identifier.
43
+ *
44
+ * @param input Raw text that may contain spaces, punctuation, or camelCase.
45
+ * @returns A camelCase string derived from the normalized PascalCase form.
46
+ */
47
+ export function toCamelCase(input) {
48
+ const pascalCase = toPascalCase(input);
49
+ return `${pascalCase.charAt(0).toLowerCase()}${pascalCase.slice(1)}`;
40
50
  }
41
51
  /**
42
52
  * Convert delimited text to PascalCase while preserving each segment's
@@ -47,12 +57,12 @@ export function toPascalCase(input) {
47
57
  */
48
58
  export function toSegmentPascalCase(input) {
49
59
  return input
50
- .replace(/[^A-Za-z0-9]+/g, " ")
60
+ .replace(/[^A-Za-z0-9]+/g, ' ')
51
61
  .trim()
52
62
  .split(/\s+/)
53
63
  .filter(Boolean)
54
64
  .map(capitalizeSegment)
55
- .join("");
65
+ .join('');
56
66
  }
57
67
  /**
58
68
  * Normalize arbitrary text into a human-readable title.
@@ -62,8 +72,8 @@ export function toSegmentPascalCase(input) {
62
72
  */
63
73
  export function toTitleCase(input) {
64
74
  return toKebabCase(input)
65
- .split("-")
75
+ .split('-')
66
76
  .filter(Boolean)
67
77
  .map(capitalizeSegment)
68
- .join(" ");
78
+ .join(' ');
69
79
  }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Parse a dotted version floor into numeric parts for stable comparisons.
3
+ *
4
+ * @param value Human-authored version floor such as `7.0` or `8.1.2`.
5
+ * @returns Numeric version segments suitable for ordered comparison.
6
+ * @throws When any segment contains non-digit characters.
7
+ */
8
+ export declare function parseVersionFloorParts(value: string): number[];
9
+ /**
10
+ * Compare two dotted version floors.
11
+ *
12
+ * Missing trailing segments are treated as zero so `7.0` equals `7.0.0`.
13
+ *
14
+ * @param left Left-hand version floor.
15
+ * @param right Right-hand version floor.
16
+ * @returns `1` when left is higher, `-1` when right is higher, or `0` when equal.
17
+ */
18
+ export declare function compareVersionFloors(left: string, right: string): number;
19
+ /**
20
+ * Pick the higher version floor while tolerating undefined values.
21
+ *
22
+ * @param current Current resolved floor, when present.
23
+ * @param candidate Candidate floor to compare against the current floor.
24
+ * @returns The higher defined floor, or undefined when neither value exists.
25
+ */
26
+ export declare function pickHigherVersionFloor(current: string | undefined, candidate: string | undefined): string | undefined;