@wp-typia/project-tools 0.23.0 → 0.24.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.
- package/dist/runtime/ai-feature-artifacts.js +4 -1
- package/dist/runtime/block-generator-service-spec.js +2 -1
- package/dist/runtime/built-in-block-non-ts-basic-artifacts.d.ts +9 -0
- package/dist/runtime/built-in-block-non-ts-basic-artifacts.js +84 -0
- package/dist/runtime/built-in-block-non-ts-compound-artifacts.d.ts +9 -0
- package/dist/runtime/built-in-block-non-ts-compound-artifacts.js +36 -0
- package/dist/runtime/built-in-block-non-ts-compound-templates.d.ts +23 -0
- package/dist/runtime/built-in-block-non-ts-compound-templates.js +453 -0
- package/dist/runtime/built-in-block-non-ts-family-artifacts.d.ts +8 -26
- package/dist/runtime/built-in-block-non-ts-family-artifacts.js +8 -1034
- package/dist/runtime/built-in-block-non-ts-interactivity-artifacts.d.ts +9 -0
- package/dist/runtime/built-in-block-non-ts-interactivity-artifacts.js +83 -0
- package/dist/runtime/built-in-block-non-ts-persistence-artifacts.d.ts +9 -0
- package/dist/runtime/built-in-block-non-ts-persistence-artifacts.js +33 -0
- package/dist/runtime/built-in-block-non-ts-persistence-templates.d.ts +23 -0
- package/dist/runtime/built-in-block-non-ts-persistence-templates.js +395 -0
- package/dist/runtime/cli-add-block-json.js +5 -1
- package/dist/runtime/cli-add-collision.js +8 -0
- package/dist/runtime/cli-add-help.js +14 -10
- package/dist/runtime/cli-add-kind-ids.d.ts +1 -1
- package/dist/runtime/cli-add-kind-ids.js +1 -0
- package/dist/runtime/cli-add-types.d.ts +45 -6
- package/dist/runtime/cli-add-types.js +2 -0
- package/dist/runtime/cli-add-validation.d.ts +7 -0
- package/dist/runtime/cli-add-validation.js +9 -0
- package/dist/runtime/cli-add-workspace-ability-anchors.d.ts +24 -0
- package/dist/runtime/cli-add-workspace-ability-anchors.js +294 -0
- package/dist/runtime/cli-add-workspace-ability-registry.d.ts +10 -0
- package/dist/runtime/cli-add-workspace-ability-registry.js +51 -0
- package/dist/runtime/cli-add-workspace-ability-scaffold.d.ts +1 -1
- package/dist/runtime/cli-add-workspace-ability-scaffold.js +5 -308
- package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +6 -2
- package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.d.ts +34 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.js +483 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-default.d.ts +30 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-default.js +310 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-rest.d.ts +25 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-rest.js +124 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-settings.d.ts +34 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-settings.js +370 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-shared.d.ts +49 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-shared.js +259 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates.d.ts +18 -27
- package/dist/runtime/cli-add-workspace-admin-view-templates.js +30 -1326
- package/dist/runtime/cli-add-workspace-ai-anchors.d.ts +4 -4
- package/dist/runtime/cli-add-workspace-ai-anchors.js +8 -233
- package/dist/runtime/cli-add-workspace-ai-scaffold.js +4 -2
- package/dist/runtime/cli-add-workspace-ai-source-emitters.d.ts +1 -4
- package/dist/runtime/cli-add-workspace-ai-source-emitters.js +1 -129
- package/dist/runtime/cli-add-workspace-ai-sync-rest-anchors.d.ts +5 -0
- package/dist/runtime/cli-add-workspace-ai-sync-rest-anchors.js +236 -0
- package/dist/runtime/cli-add-workspace-ai-sync-script-source.d.ts +4 -0
- package/dist/runtime/cli-add-workspace-ai-sync-script-source.js +145 -0
- package/dist/runtime/cli-add-workspace-assets.d.ts +6 -63
- package/dist/runtime/cli-add-workspace-assets.js +6 -950
- package/dist/runtime/cli-add-workspace-binding-source-anchors.d.ts +23 -0
- package/dist/runtime/cli-add-workspace-binding-source-anchors.js +112 -0
- package/dist/runtime/cli-add-workspace-binding-source-source-emitters.d.ts +33 -0
- package/dist/runtime/cli-add-workspace-binding-source-source-emitters.js +436 -0
- package/dist/runtime/cli-add-workspace-binding-source-types.d.ts +20 -0
- package/dist/runtime/cli-add-workspace-binding-source-types.js +1 -0
- package/dist/runtime/cli-add-workspace-binding-source.d.ts +40 -0
- package/dist/runtime/cli-add-workspace-binding-source.js +275 -0
- package/dist/runtime/cli-add-workspace-block-style.d.ts +22 -0
- package/dist/runtime/cli-add-workspace-block-style.js +148 -0
- package/dist/runtime/cli-add-workspace-block-transform.d.ts +32 -0
- package/dist/runtime/cli-add-workspace-block-transform.js +197 -0
- package/dist/runtime/cli-add-workspace-contract.js +1 -1
- package/dist/runtime/cli-add-workspace-core-variation.d.ts +20 -0
- package/dist/runtime/cli-add-workspace-core-variation.js +322 -0
- package/dist/runtime/cli-add-workspace-editor-plugin-anchors.d.ts +37 -0
- package/dist/runtime/cli-add-workspace-editor-plugin-anchors.js +206 -0
- package/dist/runtime/cli-add-workspace-editor-plugin-source-emitters.d.ts +47 -0
- package/dist/runtime/cli-add-workspace-editor-plugin-source-emitters.js +219 -0
- package/dist/runtime/cli-add-workspace-editor-plugin.d.ts +22 -0
- package/dist/runtime/cli-add-workspace-editor-plugin.js +78 -0
- package/dist/runtime/cli-add-workspace-hooked-block.d.ts +23 -0
- package/dist/runtime/cli-add-workspace-hooked-block.js +57 -0
- package/dist/runtime/cli-add-workspace-integration-env-files.d.ts +33 -0
- package/dist/runtime/cli-add-workspace-integration-env-files.js +65 -0
- package/dist/runtime/cli-add-workspace-integration-env-package-json.d.ts +38 -0
- package/dist/runtime/cli-add-workspace-integration-env-package-json.js +122 -0
- package/dist/runtime/cli-add-workspace-integration-env-source-emitters.d.ts +44 -0
- package/dist/runtime/cli-add-workspace-integration-env-source-emitters.js +262 -0
- package/dist/runtime/cli-add-workspace-integration-env.d.ts +3 -1
- package/dist/runtime/cli-add-workspace-integration-env.js +10 -313
- package/dist/runtime/cli-add-workspace-pattern-anchors.d.ts +10 -0
- package/dist/runtime/cli-add-workspace-pattern-anchors.js +95 -0
- package/dist/runtime/cli-add-workspace-pattern-options.d.ts +20 -0
- package/dist/runtime/cli-add-workspace-pattern-options.js +113 -0
- package/dist/runtime/cli-add-workspace-pattern-source-emitters.d.ts +20 -0
- package/dist/runtime/cli-add-workspace-pattern-source-emitters.js +57 -0
- package/dist/runtime/cli-add-workspace-pattern.d.ts +42 -0
- package/dist/runtime/cli-add-workspace-pattern.js +99 -0
- package/dist/runtime/cli-add-workspace-post-meta.js +1 -1
- package/dist/runtime/cli-add-workspace-registration-hooks.d.ts +50 -0
- package/dist/runtime/cli-add-workspace-registration-hooks.js +162 -0
- package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +9 -4
- package/dist/runtime/cli-add-workspace-rest-anchors.js +9 -428
- package/dist/runtime/cli-add-workspace-rest-bootstrap-anchors.d.ts +17 -0
- package/dist/runtime/cli-add-workspace-rest-bootstrap-anchors.js +108 -0
- package/dist/runtime/cli-add-workspace-rest-contract-sync-anchors.d.ts +9 -0
- package/dist/runtime/cli-add-workspace-rest-contract-sync-anchors.js +142 -0
- package/dist/runtime/cli-add-workspace-rest-generated-source-emitters.d.ts +51 -0
- package/dist/runtime/cli-add-workspace-rest-generated-source-emitters.js +415 -0
- package/dist/runtime/cli-add-workspace-rest-generated.d.ts +9 -0
- package/dist/runtime/cli-add-workspace-rest-generated.js +160 -0
- package/dist/runtime/cli-add-workspace-rest-manual-source-emitters.d.ts +80 -0
- package/dist/runtime/cli-add-workspace-rest-manual-source-emitters.js +238 -0
- package/dist/runtime/cli-add-workspace-rest-manual.d.ts +8 -0
- package/dist/runtime/cli-add-workspace-rest-manual.js +266 -0
- package/dist/runtime/cli-add-workspace-rest-php-templates.d.ts +18 -0
- package/dist/runtime/cli-add-workspace-rest-php-templates.js +359 -0
- package/dist/runtime/cli-add-workspace-rest-resource-php-routing-template.d.ts +33 -0
- package/dist/runtime/cli-add-workspace-rest-resource-php-routing-template.js +145 -0
- package/dist/runtime/cli-add-workspace-rest-resource-sync-anchors.d.ts +9 -0
- package/dist/runtime/cli-add-workspace-rest-resource-sync-anchors.js +162 -0
- package/dist/runtime/cli-add-workspace-rest-schema-helper-php-template.d.ts +7 -0
- package/dist/runtime/cli-add-workspace-rest-schema-helper-php-template.js +193 -0
- package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +5 -91
- package/dist/runtime/cli-add-workspace-rest-source-emitters.js +5 -642
- package/dist/runtime/cli-add-workspace-rest-source-utils.d.ts +17 -0
- package/dist/runtime/cli-add-workspace-rest-source-utils.js +50 -0
- package/dist/runtime/cli-add-workspace-rest-sync-script-shared.d.ts +56 -0
- package/dist/runtime/cli-add-workspace-rest-sync-script-shared.js +122 -0
- package/dist/runtime/cli-add-workspace-rest-types.d.ts +108 -0
- package/dist/runtime/cli-add-workspace-rest-types.js +1 -0
- package/dist/runtime/cli-add-workspace-rest.d.ts +3 -20
- package/dist/runtime/cli-add-workspace-rest.js +33 -788
- package/dist/runtime/cli-add-workspace-variation.d.ts +22 -0
- package/dist/runtime/cli-add-workspace-variation.js +162 -0
- package/dist/runtime/cli-add-workspace.d.ts +42 -107
- package/dist/runtime/cli-add-workspace.js +42 -674
- package/dist/runtime/cli-add.d.ts +3 -3
- package/dist/runtime/cli-add.js +2 -2
- package/dist/runtime/cli-core.d.ts +3 -2
- package/dist/runtime/cli-core.js +2 -2
- package/dist/runtime/cli-diagnostics.d.ts +3 -1
- package/dist/runtime/cli-diagnostics.js +17 -5
- package/dist/runtime/cli-doctor-workspace-bindings.js +63 -1
- package/dist/runtime/cli-doctor-workspace-block-addons.d.ts +12 -0
- package/dist/runtime/cli-doctor-workspace-block-addons.js +162 -0
- package/dist/runtime/cli-doctor-workspace-block-iframe.d.ts +9 -0
- package/dist/runtime/cli-doctor-workspace-block-iframe.js +228 -0
- package/dist/runtime/cli-doctor-workspace-block-metadata.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-block-metadata.js +111 -0
- package/dist/runtime/cli-doctor-workspace-blocks.js +6 -424
- package/dist/runtime/cli-doctor-workspace-features-abilities.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-abilities.js +112 -0
- package/dist/runtime/cli-doctor-workspace-features-admin-views.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-admin-views.js +128 -0
- package/dist/runtime/cli-doctor-workspace-features-ai.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-ai.js +57 -0
- package/dist/runtime/cli-doctor-workspace-features-editor-plugins.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-editor-plugins.js +80 -0
- package/dist/runtime/cli-doctor-workspace-features-post-meta.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-post-meta.js +77 -0
- package/dist/runtime/cli-doctor-workspace-features-rest.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-rest.js +120 -0
- package/dist/runtime/cli-doctor-workspace-features.js +14 -487
- package/dist/runtime/cli-doctor.d.ts +54 -3
- package/dist/runtime/cli-doctor.js +92 -10
- package/dist/runtime/cli-help.js +12 -7
- package/dist/runtime/cli-init-package-json.js +4 -2
- package/dist/runtime/cli-prompt.d.ts +16 -2
- package/dist/runtime/cli-prompt.js +29 -12
- package/dist/runtime/cli-scaffold.d.ts +2 -1
- package/dist/runtime/cli-scaffold.js +19 -10
- package/dist/runtime/external-template-guards.js +4 -6
- package/dist/runtime/index.d.ts +6 -3
- package/dist/runtime/index.js +4 -2
- package/dist/runtime/json-utils.d.ts +62 -4
- package/dist/runtime/json-utils.js +78 -4
- package/dist/runtime/local-dev-presets.js +6 -2
- package/dist/runtime/migration-ui-capability.js +4 -1
- package/dist/runtime/migration-utils.js +4 -1
- package/dist/runtime/package-managers.js +6 -1
- package/dist/runtime/package-versions.d.ts +1 -0
- package/dist/runtime/package-versions.js +16 -3
- package/dist/runtime/pattern-catalog.d.ts +122 -0
- package/dist/runtime/pattern-catalog.js +471 -0
- package/dist/runtime/post-meta-binding-fields.d.ts +46 -0
- package/dist/runtime/post-meta-binding-fields.js +135 -0
- package/dist/runtime/scaffold-bootstrap.js +7 -2
- package/dist/runtime/scaffold-package-manager-files.js +5 -1
- package/dist/runtime/scaffold-repository-reference.js +4 -2
- package/dist/runtime/scaffold-template-variables.js +2 -1
- package/dist/runtime/scaffold.d.ts +18 -1
- package/dist/runtime/scaffold.js +55 -2
- package/dist/runtime/temp-roots.js +4 -1
- package/dist/runtime/template-layers.js +4 -1
- package/dist/runtime/template-registry.js +9 -3
- package/dist/runtime/template-source-contracts.d.ts +2 -0
- package/dist/runtime/template-source-normalization.js +2 -1
- package/dist/runtime/template-source-remote.js +18 -5
- package/dist/runtime/template-source-seeds.js +10 -3
- package/dist/runtime/typia-llm-json-schema.d.ts +24 -0
- package/dist/runtime/typia-llm-json-schema.js +33 -0
- package/dist/runtime/typia-llm-openapi-constraints.d.ts +20 -0
- package/dist/runtime/typia-llm-openapi-constraints.js +254 -0
- package/dist/runtime/typia-llm-projection.d.ts +25 -0
- package/dist/runtime/typia-llm-projection.js +58 -0
- package/dist/runtime/typia-llm-render.d.ts +21 -0
- package/dist/runtime/typia-llm-render.js +252 -0
- package/dist/runtime/typia-llm-sync.d.ts +10 -0
- package/dist/runtime/typia-llm-sync.js +63 -0
- package/dist/runtime/typia-llm-types.d.ts +197 -0
- package/dist/runtime/typia-llm-types.js +1 -0
- package/dist/runtime/typia-llm.d.ts +9 -255
- package/dist/runtime/typia-llm.js +5 -634
- package/dist/runtime/workspace-inventory-mutations.js +15 -1
- package/dist/runtime/workspace-inventory-parser-entries.d.ts +17 -0
- package/dist/runtime/workspace-inventory-parser-entries.js +157 -0
- package/dist/runtime/workspace-inventory-parser-validation.d.ts +104 -0
- package/dist/runtime/workspace-inventory-parser-validation.js +34 -0
- package/dist/runtime/workspace-inventory-parser.d.ts +3 -45
- package/dist/runtime/workspace-inventory-parser.js +3 -581
- package/dist/runtime/workspace-inventory-section-descriptors.d.ts +19 -0
- package/dist/runtime/workspace-inventory-section-descriptors.js +443 -0
- package/dist/runtime/workspace-inventory-templates.d.ts +3 -3
- package/dist/runtime/workspace-inventory-templates.js +10 -1
- package/dist/runtime/workspace-inventory-types.d.ts +10 -1
- package/dist/runtime/workspace-project.js +4 -6
- package/package.json +8 -3
- package/templates/_shared/compound/core/scripts/block-config.ts.mustache +22 -0
- package/templates/_shared/compound/core/scripts/sync-types-to-block-json.ts.mustache +103 -2
- package/templates/_shared/compound/core/src/inner-blocks-templates.ts.mustache +13 -0
- package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +22 -1
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { promises as fsp } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ensureBlockConfigCanAddRestManifests } from "./cli-add-block-legacy-validator.js";
|
|
4
|
+
import { assertValidManualRestContractAuth, assertValidManualRestContractHttpMethod, assertValidTypeScriptIdentifier, collectRestRouteNamedCaptureNames, resolveOptionalPhpCallbackReference, resolveOptionalPhpClassReference, resolveManualRestContractPathPattern, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
|
|
5
|
+
import { ensureRestResourceSyncScriptAnchors, } from "./cli-add-workspace-rest-resource-sync-anchors.js";
|
|
6
|
+
import { buildManualRestContractApiSource, buildManualRestContractConfigEntry, buildManualRestContractTypesSource, buildManualRestContractValidatorsSource, } from "./cli-add-workspace-rest-manual-source-emitters.js";
|
|
7
|
+
import { syncManualRestContractArtifacts } from "./rest-resource-artifacts.js";
|
|
8
|
+
import { toPascalCase, toTitleCase } from "./string-case.js";
|
|
9
|
+
import { appendWorkspaceInventoryEntries } from "./workspace-inventory.js";
|
|
10
|
+
const MANUAL_REST_REQUEST_BODY_FIELD_NAMES = new Set(["payload", "comment"]);
|
|
11
|
+
const MANUAL_REST_RESPONSE_FIELD_NAMES = new Set([
|
|
12
|
+
"id",
|
|
13
|
+
"status",
|
|
14
|
+
"message",
|
|
15
|
+
"updatedAt",
|
|
16
|
+
]);
|
|
17
|
+
function resolveManualRestSecretPreserveOnEmpty(value) {
|
|
18
|
+
return value ?? true;
|
|
19
|
+
}
|
|
20
|
+
function resolveManualRestSecretStateFieldCandidate(options) {
|
|
21
|
+
const candidates = [
|
|
22
|
+
options.secretStateFieldName,
|
|
23
|
+
options.secretHasValueFieldName,
|
|
24
|
+
options.secretMaskedResponseFieldName,
|
|
25
|
+
].filter((value) => typeof value === "string");
|
|
26
|
+
const distinct = Array.from(new Set(candidates));
|
|
27
|
+
if (distinct.length > 1) {
|
|
28
|
+
throw new Error("Manual REST contract secret state, has-value, and masked response field flags must match when combined.");
|
|
29
|
+
}
|
|
30
|
+
return distinct[0];
|
|
31
|
+
}
|
|
32
|
+
function resolveManualRestRoutePathPattern(options) {
|
|
33
|
+
const trimmedPathPattern = typeof options.pathPattern === "string"
|
|
34
|
+
? options.pathPattern.trim()
|
|
35
|
+
: undefined;
|
|
36
|
+
const trimmedRoutePattern = typeof options.routePattern === "string"
|
|
37
|
+
? options.routePattern.trim()
|
|
38
|
+
: undefined;
|
|
39
|
+
if (trimmedPathPattern &&
|
|
40
|
+
trimmedRoutePattern &&
|
|
41
|
+
trimmedPathPattern !== trimmedRoutePattern) {
|
|
42
|
+
throw new Error("Manual REST contract --path and --route-pattern must match when both are provided. Use one route pattern flag for provider routes.");
|
|
43
|
+
}
|
|
44
|
+
return resolveManualRestContractPathPattern(options.restResourceSlug, options.pathPattern ?? options.routePattern);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Scaffold a type-only external REST contract for workspace consumers.
|
|
48
|
+
*
|
|
49
|
+
* @param options Resolved workspace and raw manual-mode command options.
|
|
50
|
+
* @returns Resolved scaffold metadata for the manual REST contract.
|
|
51
|
+
*/
|
|
52
|
+
export async function scaffoldManualRestContract({ auth, bodyTypeName, controllerClass, controllerExtends, method, namespace, pathPattern, permissionCallback, queryTypeName, responseTypeName, restResourceSlug, routePattern, secretFieldName, secretHasValueFieldName, secretMaskedResponseFieldName, secretPreserveOnEmpty, secretStateFieldName, workspace, }) {
|
|
53
|
+
const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
|
|
54
|
+
const syncRestScriptPath = path.join(workspace.projectDir, "scripts", "sync-rest-contracts.ts");
|
|
55
|
+
const restResourceDir = path.join(workspace.projectDir, "src", "rest", restResourceSlug);
|
|
56
|
+
const typesFilePath = path.join(restResourceDir, "api-types.ts");
|
|
57
|
+
const validatorsFilePath = path.join(restResourceDir, "api-validators.ts");
|
|
58
|
+
const apiFilePath = path.join(restResourceDir, "api.ts");
|
|
59
|
+
const pascalCase = toPascalCase(restResourceSlug);
|
|
60
|
+
const resolvedAuth = assertValidManualRestContractAuth(auth);
|
|
61
|
+
const resolvedMethod = assertValidManualRestContractHttpMethod(method);
|
|
62
|
+
const resolvedPathPattern = resolveManualRestRoutePathPattern({
|
|
63
|
+
pathPattern,
|
|
64
|
+
restResourceSlug,
|
|
65
|
+
routePattern,
|
|
66
|
+
});
|
|
67
|
+
const pathParameterNames = collectRestRouteNamedCaptureNames(resolvedPathPattern);
|
|
68
|
+
const resolvedPermissionCallback = resolveOptionalPhpCallbackReference("Manual REST contract permission callback", permissionCallback);
|
|
69
|
+
const resolvedControllerClass = resolveOptionalPhpClassReference("Manual REST contract controller class", controllerClass);
|
|
70
|
+
const resolvedControllerExtends = resolveOptionalPhpClassReference("Manual REST contract controller base class", controllerExtends);
|
|
71
|
+
if (resolvedControllerExtends && !resolvedControllerClass) {
|
|
72
|
+
throw new Error("Manual REST contract controller base class requires --controller-class.");
|
|
73
|
+
}
|
|
74
|
+
const resolvedQueryTypeName = assertValidTypeScriptIdentifier("Manual REST contract query type", queryTypeName ?? `${pascalCase}Query`, "wp-typia add rest-resource <name> --manual [--query-type <ExportedQueryType>]");
|
|
75
|
+
const resolvedResponseTypeName = assertValidTypeScriptIdentifier("Manual REST contract response type", responseTypeName ?? `${pascalCase}Response`, "wp-typia add rest-resource <name> --manual [--response-type <ExportedResponseType>]");
|
|
76
|
+
const defaultsToBody = bodyTypeName == null && ["PATCH", "POST", "PUT"].includes(resolvedMethod);
|
|
77
|
+
const resolvedBodyTypeName = bodyTypeName != null || defaultsToBody
|
|
78
|
+
? assertValidTypeScriptIdentifier("Manual REST contract body type", bodyTypeName ?? `${pascalCase}Request`, "wp-typia add rest-resource <name> --manual [--body-type <ExportedBodyType>]")
|
|
79
|
+
: undefined;
|
|
80
|
+
if (resolvedMethod === "GET" && resolvedBodyTypeName) {
|
|
81
|
+
throw new Error("Manual REST contract GET routes cannot define a body type. Remove --body-type or use POST, PUT, or PATCH.");
|
|
82
|
+
}
|
|
83
|
+
const secretStateFieldCandidate = resolveManualRestSecretStateFieldCandidate({
|
|
84
|
+
secretHasValueFieldName,
|
|
85
|
+
secretMaskedResponseFieldName,
|
|
86
|
+
secretStateFieldName,
|
|
87
|
+
});
|
|
88
|
+
if (secretPreserveOnEmpty !== undefined && !secretFieldName) {
|
|
89
|
+
throw new Error("Manual REST contract --secret-preserve-on-empty requires --secret-field.");
|
|
90
|
+
}
|
|
91
|
+
if (secretStateFieldCandidate !== undefined && !secretFieldName) {
|
|
92
|
+
throw new Error("Manual REST contract secret state, has-value, and masked response field flags require --secret-field.");
|
|
93
|
+
}
|
|
94
|
+
if (secretFieldName && !resolvedBodyTypeName) {
|
|
95
|
+
throw new Error("Manual REST contract secret fields require a request body. Use POST, PUT, or PATCH so a request body is generated.");
|
|
96
|
+
}
|
|
97
|
+
const resolvedSecretFieldName = secretFieldName
|
|
98
|
+
? assertValidTypeScriptIdentifier("Manual REST contract secret field", secretFieldName, "wp-typia add rest-resource <name> --manual --method POST --secret-field <field>")
|
|
99
|
+
: undefined;
|
|
100
|
+
const resolvedSecretPreserveOnEmpty = resolvedSecretFieldName
|
|
101
|
+
? resolveManualRestSecretPreserveOnEmpty(secretPreserveOnEmpty)
|
|
102
|
+
: undefined;
|
|
103
|
+
const resolvedSecretStateFieldName = resolvedSecretFieldName
|
|
104
|
+
? assertValidTypeScriptIdentifier("Manual REST contract secret state field", secretStateFieldCandidate ??
|
|
105
|
+
`has${toPascalCase(resolvedSecretFieldName)}`, "wp-typia add rest-resource <name> --manual --method POST --secret-state-field <field>")
|
|
106
|
+
: undefined;
|
|
107
|
+
if (resolvedSecretFieldName &&
|
|
108
|
+
MANUAL_REST_REQUEST_BODY_FIELD_NAMES.has(resolvedSecretFieldName)) {
|
|
109
|
+
throw new Error(`Manual REST contract secret field must not reuse scaffolded request body fields: ${Array.from(MANUAL_REST_REQUEST_BODY_FIELD_NAMES).join(", ")}.`);
|
|
110
|
+
}
|
|
111
|
+
if (resolvedSecretStateFieldName &&
|
|
112
|
+
MANUAL_REST_RESPONSE_FIELD_NAMES.has(resolvedSecretStateFieldName)) {
|
|
113
|
+
throw new Error(`Manual REST contract secret state field must not reuse scaffolded response fields: ${Array.from(MANUAL_REST_RESPONSE_FIELD_NAMES).join(", ")}.`);
|
|
114
|
+
}
|
|
115
|
+
if (resolvedSecretFieldName &&
|
|
116
|
+
resolvedSecretStateFieldName &&
|
|
117
|
+
resolvedSecretFieldName === resolvedSecretStateFieldName) {
|
|
118
|
+
throw new Error("Manual REST contract secret state field must be different from the raw secret field.");
|
|
119
|
+
}
|
|
120
|
+
const manualTypeNames = [
|
|
121
|
+
resolvedQueryTypeName,
|
|
122
|
+
resolvedResponseTypeName,
|
|
123
|
+
resolvedBodyTypeName,
|
|
124
|
+
].filter((value) => value != null);
|
|
125
|
+
const duplicateManualTypeNames = manualTypeNames.filter((name, index) => manualTypeNames.indexOf(name) !== index);
|
|
126
|
+
if (duplicateManualTypeNames.length > 0) {
|
|
127
|
+
throw new Error(`Manual REST contract type names must be unique: ${Array.from(new Set(duplicateManualTypeNames)).join(", ")}. Use distinct --query-type, --body-type, and --response-type values.`);
|
|
128
|
+
}
|
|
129
|
+
const mutationSnapshot = {
|
|
130
|
+
fileSources: await snapshotWorkspaceFiles([
|
|
131
|
+
blockConfigPath,
|
|
132
|
+
syncRestScriptPath,
|
|
133
|
+
]),
|
|
134
|
+
snapshotDirs: [],
|
|
135
|
+
targetPaths: [restResourceDir],
|
|
136
|
+
};
|
|
137
|
+
try {
|
|
138
|
+
await fsp.mkdir(restResourceDir, { recursive: true });
|
|
139
|
+
await ensureRestResourceSyncScriptAnchors(workspace);
|
|
140
|
+
await fsp.writeFile(typesFilePath, buildManualRestContractTypesSource({
|
|
141
|
+
...(resolvedBodyTypeName
|
|
142
|
+
? { bodyTypeName: resolvedBodyTypeName }
|
|
143
|
+
: {}),
|
|
144
|
+
pathParameterNames,
|
|
145
|
+
queryTypeName: resolvedQueryTypeName,
|
|
146
|
+
responseTypeName: resolvedResponseTypeName,
|
|
147
|
+
restResourceSlug,
|
|
148
|
+
...(resolvedSecretFieldName
|
|
149
|
+
? { secretFieldName: resolvedSecretFieldName }
|
|
150
|
+
: {}),
|
|
151
|
+
...(resolvedSecretPreserveOnEmpty !== undefined
|
|
152
|
+
? { secretPreserveOnEmpty: resolvedSecretPreserveOnEmpty }
|
|
153
|
+
: {}),
|
|
154
|
+
...(resolvedSecretStateFieldName
|
|
155
|
+
? { secretStateFieldName: resolvedSecretStateFieldName }
|
|
156
|
+
: {}),
|
|
157
|
+
}), "utf8");
|
|
158
|
+
await fsp.writeFile(validatorsFilePath, buildManualRestContractValidatorsSource({
|
|
159
|
+
...(resolvedBodyTypeName
|
|
160
|
+
? { bodyTypeName: resolvedBodyTypeName }
|
|
161
|
+
: {}),
|
|
162
|
+
queryTypeName: resolvedQueryTypeName,
|
|
163
|
+
responseTypeName: resolvedResponseTypeName,
|
|
164
|
+
}), "utf8");
|
|
165
|
+
await fsp.writeFile(apiFilePath, buildManualRestContractApiSource({
|
|
166
|
+
...(resolvedBodyTypeName
|
|
167
|
+
? { bodyTypeName: resolvedBodyTypeName }
|
|
168
|
+
: {}),
|
|
169
|
+
queryTypeName: resolvedQueryTypeName,
|
|
170
|
+
restResourceSlug,
|
|
171
|
+
}), "utf8");
|
|
172
|
+
await syncManualRestContractArtifacts({
|
|
173
|
+
clientFile: `src/rest/${restResourceSlug}/api-client.ts`,
|
|
174
|
+
outputDir: restResourceDir,
|
|
175
|
+
projectDir: workspace.projectDir,
|
|
176
|
+
typesFile: `src/rest/${restResourceSlug}/api-types.ts`,
|
|
177
|
+
validatorsFile: `src/rest/${restResourceSlug}/api-validators.ts`,
|
|
178
|
+
variables: {
|
|
179
|
+
auth: resolvedAuth,
|
|
180
|
+
...(resolvedBodyTypeName
|
|
181
|
+
? { bodyTypeName: resolvedBodyTypeName }
|
|
182
|
+
: {}),
|
|
183
|
+
method: resolvedMethod,
|
|
184
|
+
namespace,
|
|
185
|
+
pascalCase,
|
|
186
|
+
pathPattern: resolvedPathPattern,
|
|
187
|
+
queryTypeName: resolvedQueryTypeName,
|
|
188
|
+
responseTypeName: resolvedResponseTypeName,
|
|
189
|
+
slugKebabCase: restResourceSlug,
|
|
190
|
+
title: toTitleCase(restResourceSlug),
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
await appendWorkspaceInventoryEntries(workspace.projectDir, {
|
|
194
|
+
restResourceEntries: [
|
|
195
|
+
buildManualRestContractConfigEntry({
|
|
196
|
+
auth: resolvedAuth,
|
|
197
|
+
...(resolvedBodyTypeName
|
|
198
|
+
? { bodyTypeName: resolvedBodyTypeName }
|
|
199
|
+
: {}),
|
|
200
|
+
...(resolvedControllerClass
|
|
201
|
+
? { controllerClass: resolvedControllerClass }
|
|
202
|
+
: {}),
|
|
203
|
+
...(resolvedControllerExtends
|
|
204
|
+
? { controllerExtends: resolvedControllerExtends }
|
|
205
|
+
: {}),
|
|
206
|
+
method: resolvedMethod,
|
|
207
|
+
namespace,
|
|
208
|
+
pathPattern: resolvedPathPattern,
|
|
209
|
+
...(resolvedPermissionCallback
|
|
210
|
+
? { permissionCallback: resolvedPermissionCallback }
|
|
211
|
+
: {}),
|
|
212
|
+
queryTypeName: resolvedQueryTypeName,
|
|
213
|
+
responseTypeName: resolvedResponseTypeName,
|
|
214
|
+
restResourceSlug,
|
|
215
|
+
...(resolvedSecretFieldName
|
|
216
|
+
? { secretFieldName: resolvedSecretFieldName }
|
|
217
|
+
: {}),
|
|
218
|
+
...(resolvedSecretPreserveOnEmpty !== undefined
|
|
219
|
+
? { secretPreserveOnEmpty: resolvedSecretPreserveOnEmpty }
|
|
220
|
+
: {}),
|
|
221
|
+
...(resolvedSecretStateFieldName
|
|
222
|
+
? { secretStateFieldName: resolvedSecretStateFieldName }
|
|
223
|
+
: {}),
|
|
224
|
+
}),
|
|
225
|
+
],
|
|
226
|
+
transformSource: ensureBlockConfigCanAddRestManifests,
|
|
227
|
+
});
|
|
228
|
+
return {
|
|
229
|
+
auth: resolvedAuth,
|
|
230
|
+
...(resolvedBodyTypeName
|
|
231
|
+
? { bodyTypeName: resolvedBodyTypeName }
|
|
232
|
+
: {}),
|
|
233
|
+
...(resolvedControllerClass
|
|
234
|
+
? { controllerClass: resolvedControllerClass }
|
|
235
|
+
: {}),
|
|
236
|
+
...(resolvedControllerExtends
|
|
237
|
+
? { controllerExtends: resolvedControllerExtends }
|
|
238
|
+
: {}),
|
|
239
|
+
method: resolvedMethod,
|
|
240
|
+
methods: [],
|
|
241
|
+
mode: "manual",
|
|
242
|
+
namespace,
|
|
243
|
+
pathPattern: resolvedPathPattern,
|
|
244
|
+
...(resolvedPermissionCallback
|
|
245
|
+
? { permissionCallback: resolvedPermissionCallback }
|
|
246
|
+
: {}),
|
|
247
|
+
projectDir: workspace.projectDir,
|
|
248
|
+
queryTypeName: resolvedQueryTypeName,
|
|
249
|
+
restResourceSlug,
|
|
250
|
+
responseTypeName: resolvedResponseTypeName,
|
|
251
|
+
...(resolvedSecretFieldName
|
|
252
|
+
? { secretFieldName: resolvedSecretFieldName }
|
|
253
|
+
: {}),
|
|
254
|
+
...(resolvedSecretPreserveOnEmpty !== undefined
|
|
255
|
+
? { secretPreserveOnEmpty: resolvedSecretPreserveOnEmpty }
|
|
256
|
+
: {}),
|
|
257
|
+
...(resolvedSecretStateFieldName
|
|
258
|
+
? { secretStateFieldName: resolvedSecretStateFieldName }
|
|
259
|
+
: {}),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
await rollbackWorkspaceMutation(mutationSnapshot);
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type RestResourceMethodId } from "./cli-add-shared.js";
|
|
2
|
+
export { buildWorkspaceRestSchemaHelperPhpSource, } from "./cli-add-workspace-rest-schema-helper-php-template.js";
|
|
3
|
+
/**
|
|
4
|
+
* Build the PHP route/controller glue for generated workspace REST resources.
|
|
5
|
+
*
|
|
6
|
+
* @param restResourceSlug Normalized REST resource slug.
|
|
7
|
+
* @param namespace WordPress REST namespace, such as `vendor/v1`.
|
|
8
|
+
* @param phpPrefix Plugin PHP function prefix.
|
|
9
|
+
* @param methods REST operations to expose.
|
|
10
|
+
* @param options Optional generated route and controller customizations.
|
|
11
|
+
* @returns A complete PHP source file for the generated REST resource.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildRestResourcePhpSource(restResourceSlug: string, namespace: string, phpPrefix: string, methods: RestResourceMethodId[], options: {
|
|
14
|
+
controllerClass?: string;
|
|
15
|
+
controllerExtends?: string;
|
|
16
|
+
permissionCallback?: string;
|
|
17
|
+
routePattern: string;
|
|
18
|
+
}): string;
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { quotePhpString } from "./php-utils.js";
|
|
2
|
+
import { toTitleCase } from "./string-case.js";
|
|
3
|
+
import { buildRestResourceControllerBootstrapSource, buildRestResourceControllerClassSource, buildRestResourceRouteRegistrations, } from "./cli-add-workspace-rest-resource-php-routing-template.js";
|
|
4
|
+
export { buildWorkspaceRestSchemaHelperPhpSource, } from "./cli-add-workspace-rest-schema-helper-php-template.js";
|
|
5
|
+
/**
|
|
6
|
+
* Build the PHP route/controller glue for generated workspace REST resources.
|
|
7
|
+
*
|
|
8
|
+
* @param restResourceSlug Normalized REST resource slug.
|
|
9
|
+
* @param namespace WordPress REST namespace, such as `vendor/v1`.
|
|
10
|
+
* @param phpPrefix Plugin PHP function prefix.
|
|
11
|
+
* @param methods REST operations to expose.
|
|
12
|
+
* @param options Optional generated route and controller customizations.
|
|
13
|
+
* @returns A complete PHP source file for the generated REST resource.
|
|
14
|
+
*/
|
|
15
|
+
export function buildRestResourcePhpSource(restResourceSlug, namespace, phpPrefix, methods, options) {
|
|
16
|
+
const restResourceTitle = toTitleCase(restResourceSlug);
|
|
17
|
+
const restResourcePhpId = restResourceSlug.replace(/-/g, "_");
|
|
18
|
+
const canWriteFunctionName = `${phpPrefix}_${restResourcePhpId}_can_manage_rest_resource`;
|
|
19
|
+
const getItemsFunctionName = `${phpPrefix}_${restResourcePhpId}_get_rest_resource_items`;
|
|
20
|
+
const validatePayloadFunctionName = `${phpPrefix}_${restResourcePhpId}_validate_rest_resource_payload`;
|
|
21
|
+
const normalizeItemFunctionName = `${phpPrefix}_${restResourcePhpId}_normalize_rest_resource_item`;
|
|
22
|
+
const saveItemsFunctionName = `${phpPrefix}_${restResourcePhpId}_save_rest_resource_items`;
|
|
23
|
+
const getOptionNameFunctionName = `${phpPrefix}_${restResourcePhpId}_get_rest_resource_option_name`;
|
|
24
|
+
const listHandlerName = `${phpPrefix}_${restResourcePhpId}_handle_list_rest_resource`;
|
|
25
|
+
const readHandlerName = `${phpPrefix}_${restResourcePhpId}_handle_read_rest_resource`;
|
|
26
|
+
const createHandlerName = `${phpPrefix}_${restResourcePhpId}_handle_create_rest_resource`;
|
|
27
|
+
const updateHandlerName = `${phpPrefix}_${restResourcePhpId}_handle_update_rest_resource`;
|
|
28
|
+
const deleteHandlerName = `${phpPrefix}_${restResourcePhpId}_handle_delete_rest_resource`;
|
|
29
|
+
const registerRoutesFunctionName = `${phpPrefix}_${restResourcePhpId}_register_rest_routes`;
|
|
30
|
+
const controllerVariableName = options.controllerClass ? "$controller" : undefined;
|
|
31
|
+
const routeRegistrations = buildRestResourceRouteRegistrations(restResourceSlug, methods, {
|
|
32
|
+
canWriteFunctionName,
|
|
33
|
+
createHandlerName,
|
|
34
|
+
deleteHandlerName,
|
|
35
|
+
listHandlerName,
|
|
36
|
+
readHandlerName,
|
|
37
|
+
updateHandlerName,
|
|
38
|
+
}, {
|
|
39
|
+
...(controllerVariableName ? { controllerVariableName } : {}),
|
|
40
|
+
...(options.permissionCallback
|
|
41
|
+
? { permissionCallback: options.permissionCallback }
|
|
42
|
+
: {}),
|
|
43
|
+
routePattern: options.routePattern,
|
|
44
|
+
});
|
|
45
|
+
const controllerClassSource = options.controllerClass
|
|
46
|
+
? buildRestResourceControllerClassSource({
|
|
47
|
+
controllerClass: options.controllerClass,
|
|
48
|
+
...(options.controllerExtends
|
|
49
|
+
? { controllerExtends: options.controllerExtends }
|
|
50
|
+
: {}),
|
|
51
|
+
functions: {
|
|
52
|
+
canWriteFunctionName,
|
|
53
|
+
createHandlerName,
|
|
54
|
+
deleteHandlerName,
|
|
55
|
+
listHandlerName,
|
|
56
|
+
readHandlerName,
|
|
57
|
+
updateHandlerName,
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
: "";
|
|
61
|
+
const controllerBootstrapSource = buildRestResourceControllerBootstrapSource(options.controllerClass);
|
|
62
|
+
return `<?php
|
|
63
|
+
if ( ! defined( 'ABSPATH' ) ) {
|
|
64
|
+
\treturn;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if ( ! function_exists( '${getOptionNameFunctionName}' ) ) {
|
|
68
|
+
\tfunction ${getOptionNameFunctionName}() {
|
|
69
|
+
\t\treturn ${quotePhpString(`${phpPrefix}_${restResourcePhpId}_rest_resource_items`)};
|
|
70
|
+
\t}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if ( ! function_exists( '${normalizeItemFunctionName}' ) ) {
|
|
74
|
+
\tfunction ${normalizeItemFunctionName}( array $item ) {
|
|
75
|
+
\t\treturn array(
|
|
76
|
+
\t\t\t'id' => isset( $item['id'] ) ? (int) $item['id'] : 0,
|
|
77
|
+
\t\t\t'title' => isset( $item['title'] ) ? (string) $item['title'] : '',
|
|
78
|
+
\t\t\t'content' => isset( $item['content'] ) ? (string) $item['content'] : '',
|
|
79
|
+
\t\t\t'status' => isset( $item['status'] ) && 'published' === $item['status'] ? 'published' : 'draft',
|
|
80
|
+
\t\t\t'updatedAt' => isset( $item['updatedAt'] ) ? (string) $item['updatedAt'] : gmdate( 'c' ),
|
|
81
|
+
\t\t);
|
|
82
|
+
\t}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if ( ! function_exists( '${getItemsFunctionName}' ) ) {
|
|
86
|
+
\tfunction ${getItemsFunctionName}() {
|
|
87
|
+
\t\t$seed_items = array(
|
|
88
|
+
\t\t\tarray(
|
|
89
|
+
\t\t\t\t'id' => 1,
|
|
90
|
+
\t\t\t\t'title' => ${quotePhpString(`${restResourceTitle} Starter`)},
|
|
91
|
+
\t\t\t\t'content' => ${quotePhpString(`Replace this seeded ${restResourceTitle.toLowerCase()} content with your plugin data source.`)},
|
|
92
|
+
\t\t\t\t'status' => 'draft',
|
|
93
|
+
\t\t\t\t'updatedAt' => '2026-01-01T00:00:00Z',
|
|
94
|
+
\t\t\t),
|
|
95
|
+
\t\t);
|
|
96
|
+
\t\t$items = get_option( ${getOptionNameFunctionName}(), $seed_items );
|
|
97
|
+
|
|
98
|
+
\t\tif ( ! is_array( $items ) ) {
|
|
99
|
+
\t\t\t$items = $seed_items;
|
|
100
|
+
\t\t}
|
|
101
|
+
|
|
102
|
+
\t\treturn array_values(
|
|
103
|
+
\t\t\tarray_map(
|
|
104
|
+
\t\t\t\t'${normalizeItemFunctionName}',
|
|
105
|
+
\t\t\t\tarray_filter(
|
|
106
|
+
\t\t\t\t\t$items,
|
|
107
|
+
\t\t\t\t\t'is_array'
|
|
108
|
+
\t\t\t\t)
|
|
109
|
+
\t\t\t)
|
|
110
|
+
\t\t);
|
|
111
|
+
\t}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if ( ! function_exists( '${saveItemsFunctionName}' ) ) {
|
|
115
|
+
\tfunction ${saveItemsFunctionName}( array $items ) {
|
|
116
|
+
\t\tupdate_option(
|
|
117
|
+
\t\t\t${getOptionNameFunctionName}(),
|
|
118
|
+
\t\t\tarray_values(
|
|
119
|
+
\t\t\t\tarray_map(
|
|
120
|
+
\t\t\t\t\t'${normalizeItemFunctionName}',
|
|
121
|
+
\t\t\t\t\t$items
|
|
122
|
+
\t\t\t\t)
|
|
123
|
+
\t\t\t),
|
|
124
|
+
\t\t\tfalse
|
|
125
|
+
\t\t);
|
|
126
|
+
\t}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if ( ! function_exists( '${validatePayloadFunctionName}' ) ) {
|
|
130
|
+
\tfunction ${validatePayloadFunctionName}( $value, $schema_name, $param_name ) {
|
|
131
|
+
\t\tif ( ! function_exists( '${phpPrefix}_validate_and_sanitize_rest_payload' ) ) {
|
|
132
|
+
\t\t\treturn new WP_Error(
|
|
133
|
+
\t\t\t\t'missing_rest_schema_helper',
|
|
134
|
+
\t\t\t\t'Missing REST schema helper. Ensure inc/rest-schema.php is loaded before REST resources.',
|
|
135
|
+
\t\t\t\tarray( 'status' => 500 )
|
|
136
|
+
\t\t\t);
|
|
137
|
+
\t\t}
|
|
138
|
+
|
|
139
|
+
\t\treturn ${phpPrefix}_validate_and_sanitize_rest_payload(
|
|
140
|
+
\t\t\t$value,
|
|
141
|
+
\t\t\t$schema_name,
|
|
142
|
+
\t\t\t$param_name,
|
|
143
|
+
\t\t\tarray( 'resource' => ${quotePhpString(restResourceSlug)} )
|
|
144
|
+
\t\t);
|
|
145
|
+
\t}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if ( ! function_exists( '${canWriteFunctionName}' ) ) {
|
|
149
|
+
\tfunction ${canWriteFunctionName}() {
|
|
150
|
+
\t\treturn current_user_can( 'edit_posts' );
|
|
151
|
+
\t}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if ( ! function_exists( '${listHandlerName}' ) ) {
|
|
155
|
+
\tfunction ${listHandlerName}( WP_REST_Request $request ) {
|
|
156
|
+
\t\t$payload_input = array();
|
|
157
|
+
\t\t$page = $request->get_param( 'page' );
|
|
158
|
+
\t\t$per_page = $request->get_param( 'perPage' );
|
|
159
|
+
\t\t$search = $request->get_param( 'search' );
|
|
160
|
+
|
|
161
|
+
\t\tif ( null !== $page ) {
|
|
162
|
+
\t\t\t$payload_input['page'] = $page;
|
|
163
|
+
\t\t}
|
|
164
|
+
\t\tif ( null !== $per_page ) {
|
|
165
|
+
\t\t\t$payload_input['perPage'] = $per_page;
|
|
166
|
+
\t\t}
|
|
167
|
+
\t\tif ( null !== $search ) {
|
|
168
|
+
\t\t\t$payload_input['search'] = $search;
|
|
169
|
+
\t\t}
|
|
170
|
+
|
|
171
|
+
\t\t$payload = ${validatePayloadFunctionName}(
|
|
172
|
+
\t\t\t$payload_input,
|
|
173
|
+
\t\t\t'list-query',
|
|
174
|
+
\t\t\t'query'
|
|
175
|
+
\t\t);
|
|
176
|
+
|
|
177
|
+
\t\tif ( is_wp_error( $payload ) ) {
|
|
178
|
+
\t\t\treturn $payload;
|
|
179
|
+
\t\t}
|
|
180
|
+
|
|
181
|
+
\t\t$page = isset( $payload['page'] ) ? max( 1, (int) $payload['page'] ) : 1;
|
|
182
|
+
\t\t$per_page = isset( $payload['perPage'] ) ? min( 50, max( 1, (int) $payload['perPage'] ) ) : 10;
|
|
183
|
+
\t\t$search = isset( $payload['search'] ) ? strtolower( (string) $payload['search'] ) : '';
|
|
184
|
+
\t\t$items = ${getItemsFunctionName}();
|
|
185
|
+
|
|
186
|
+
\t\tif ( '' !== $search ) {
|
|
187
|
+
\t\t\t$items = array_values(
|
|
188
|
+
\t\t\t\tarray_filter(
|
|
189
|
+
\t\t\t\t\t$items,
|
|
190
|
+
\t\t\t\t\tstatic function ( $item ) use ( $search ) {
|
|
191
|
+
\t\t\t\t\t\treturn false !== strpos( strtolower( (string) ( $item['title'] ?? '' ) ), $search ) ||
|
|
192
|
+
\t\t\t\t\t\t\tfalse !== strpos( strtolower( (string) ( $item['content'] ?? '' ) ), $search );
|
|
193
|
+
\t\t\t\t\t}
|
|
194
|
+
\t\t\t\t)
|
|
195
|
+
\t\t\t);
|
|
196
|
+
\t\t}
|
|
197
|
+
|
|
198
|
+
\t\t$total = count( $items );
|
|
199
|
+
\t\t$items = array_slice( $items, ( $page - 1 ) * $per_page, $per_page );
|
|
200
|
+
|
|
201
|
+
\t\treturn rest_ensure_response(
|
|
202
|
+
\t\t\tarray(
|
|
203
|
+
\t\t\t\t'items' => $items,
|
|
204
|
+
\t\t\t\t'page' => $page,
|
|
205
|
+
\t\t\t\t'perPage' => $per_page,
|
|
206
|
+
\t\t\t\t'total' => $total,
|
|
207
|
+
\t\t\t)
|
|
208
|
+
\t\t);
|
|
209
|
+
\t}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if ( ! function_exists( '${readHandlerName}' ) ) {
|
|
213
|
+
\tfunction ${readHandlerName}( WP_REST_Request $request ) {
|
|
214
|
+
\t\t$payload = ${validatePayloadFunctionName}(
|
|
215
|
+
\t\t\tarray(
|
|
216
|
+
\t\t\t\t'id' => $request->get_param( 'id' ),
|
|
217
|
+
\t\t\t),
|
|
218
|
+
\t\t\t'read-query',
|
|
219
|
+
\t\t\t'query'
|
|
220
|
+
\t\t);
|
|
221
|
+
|
|
222
|
+
\t\tif ( is_wp_error( $payload ) ) {
|
|
223
|
+
\t\t\treturn $payload;
|
|
224
|
+
\t\t}
|
|
225
|
+
|
|
226
|
+
\t\tforeach ( ${getItemsFunctionName}() as $item ) {
|
|
227
|
+
\t\t\tif ( (int) $item['id'] === (int) $payload['id'] ) {
|
|
228
|
+
\t\t\t\treturn rest_ensure_response( $item );
|
|
229
|
+
\t\t\t}
|
|
230
|
+
\t\t}
|
|
231
|
+
|
|
232
|
+
\t\treturn new WP_Error( 'rest_not_found', 'Resource not found.', array( 'status' => 404 ) );
|
|
233
|
+
\t}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if ( ! function_exists( '${createHandlerName}' ) ) {
|
|
237
|
+
\tfunction ${createHandlerName}( WP_REST_Request $request ) {
|
|
238
|
+
\t\t$payload = ${validatePayloadFunctionName}( $request->get_json_params(), 'create-request', 'body' );
|
|
239
|
+
\t\tif ( is_wp_error( $payload ) ) {
|
|
240
|
+
\t\t\treturn $payload;
|
|
241
|
+
\t\t}
|
|
242
|
+
|
|
243
|
+
\t\t$items = ${getItemsFunctionName}();
|
|
244
|
+
\t\t$next_id = 1;
|
|
245
|
+
\t\tforeach ( $items as $item ) {
|
|
246
|
+
\t\t\t$next_id = max( $next_id, (int) $item['id'] + 1 );
|
|
247
|
+
\t\t}
|
|
248
|
+
|
|
249
|
+
\t\t$record = ${normalizeItemFunctionName}(
|
|
250
|
+
\t\t\tarray(
|
|
251
|
+
\t\t\t\t'id' => $next_id,
|
|
252
|
+
\t\t\t\t'title' => (string) $payload['title'],
|
|
253
|
+
\t\t\t\t'content' => isset( $payload['content'] ) ? (string) $payload['content'] : '',
|
|
254
|
+
\t\t\t\t'status' => isset( $payload['status'] ) ? (string) $payload['status'] : 'draft',
|
|
255
|
+
\t\t\t\t'updatedAt' => gmdate( 'c' ),
|
|
256
|
+
\t\t\t)
|
|
257
|
+
\t\t);
|
|
258
|
+
|
|
259
|
+
\t\t$items[] = $record;
|
|
260
|
+
\t\t${saveItemsFunctionName}( $items );
|
|
261
|
+
|
|
262
|
+
\t\treturn rest_ensure_response( $record );
|
|
263
|
+
\t}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if ( ! function_exists( '${updateHandlerName}' ) ) {
|
|
267
|
+
\tfunction ${updateHandlerName}( WP_REST_Request $request ) {
|
|
268
|
+
\t\t$query = ${validatePayloadFunctionName}(
|
|
269
|
+
\t\t\tarray(
|
|
270
|
+
\t\t\t\t'id' => $request->get_param( 'id' ),
|
|
271
|
+
\t\t\t),
|
|
272
|
+
\t\t\t'update-query',
|
|
273
|
+
\t\t\t'query'
|
|
274
|
+
\t\t);
|
|
275
|
+
\t\tif ( is_wp_error( $query ) ) {
|
|
276
|
+
\t\t\treturn $query;
|
|
277
|
+
\t\t}
|
|
278
|
+
|
|
279
|
+
\t\t$payload = ${validatePayloadFunctionName}( $request->get_json_params(), 'update-request', 'body' );
|
|
280
|
+
\t\tif ( is_wp_error( $payload ) ) {
|
|
281
|
+
\t\t\treturn $payload;
|
|
282
|
+
\t\t}
|
|
283
|
+
|
|
284
|
+
\t\t$items = ${getItemsFunctionName}();
|
|
285
|
+
\t\tforeach ( $items as $index => $item ) {
|
|
286
|
+
\t\t\tif ( (int) $item['id'] !== (int) $query['id'] ) {
|
|
287
|
+
\t\t\t\tcontinue;
|
|
288
|
+
\t\t\t}
|
|
289
|
+
|
|
290
|
+
\t\t\t$items[ $index ] = ${normalizeItemFunctionName}(
|
|
291
|
+
\t\t\t\tarray(
|
|
292
|
+
\t\t\t\t\t'id' => $item['id'],
|
|
293
|
+
\t\t\t\t\t'title' => isset( $payload['title'] ) ? (string) $payload['title'] : (string) $item['title'],
|
|
294
|
+
\t\t\t\t\t'content' => array_key_exists( 'content', $payload ) ? (string) $payload['content'] : (string) $item['content'],
|
|
295
|
+
\t\t\t\t\t'status' => isset( $payload['status'] ) ? (string) $payload['status'] : (string) $item['status'],
|
|
296
|
+
\t\t\t\t\t'updatedAt' => gmdate( 'c' ),
|
|
297
|
+
\t\t\t\t)
|
|
298
|
+
\t\t\t);
|
|
299
|
+
|
|
300
|
+
\t\t\t${saveItemsFunctionName}( $items );
|
|
301
|
+
\t\t\treturn rest_ensure_response( $items[ $index ] );
|
|
302
|
+
\t\t}
|
|
303
|
+
|
|
304
|
+
\t\treturn new WP_Error( 'rest_not_found', 'Resource not found.', array( 'status' => 404 ) );
|
|
305
|
+
\t}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if ( ! function_exists( '${deleteHandlerName}' ) ) {
|
|
309
|
+
\tfunction ${deleteHandlerName}( WP_REST_Request $request ) {
|
|
310
|
+
\t\t$query = ${validatePayloadFunctionName}(
|
|
311
|
+
\t\t\tarray(
|
|
312
|
+
\t\t\t\t'id' => $request->get_param( 'id' ),
|
|
313
|
+
\t\t\t),
|
|
314
|
+
\t\t\t'delete-query',
|
|
315
|
+
\t\t\t'query'
|
|
316
|
+
\t\t);
|
|
317
|
+
\t\tif ( is_wp_error( $query ) ) {
|
|
318
|
+
\t\t\treturn $query;
|
|
319
|
+
\t\t}
|
|
320
|
+
|
|
321
|
+
\t\t$items = ${getItemsFunctionName}();
|
|
322
|
+
\t\t$filtered = array_values(
|
|
323
|
+
\t\t\tarray_filter(
|
|
324
|
+
\t\t\t\t$items,
|
|
325
|
+
\t\t\t\tstatic function ( $item ) use ( $query ) {
|
|
326
|
+
\t\t\t\t\treturn (int) $item['id'] !== (int) $query['id'];
|
|
327
|
+
\t\t\t\t}
|
|
328
|
+
\t\t\t)
|
|
329
|
+
\t\t);
|
|
330
|
+
\t\t$was_deleted = count( $filtered ) !== count( $items );
|
|
331
|
+
|
|
332
|
+
\t\tif ( ! $was_deleted ) {
|
|
333
|
+
\t\t\treturn new WP_Error( 'rest_not_found', 'Resource not found.', array( 'status' => 404 ) );
|
|
334
|
+
\t\t}
|
|
335
|
+
|
|
336
|
+
\t\t${saveItemsFunctionName}( $filtered );
|
|
337
|
+
|
|
338
|
+
\t\treturn rest_ensure_response(
|
|
339
|
+
\t\t\tarray(
|
|
340
|
+
\t\t\t\t'deleted' => true,
|
|
341
|
+
\t\t\t\t'id' => (int) $query['id'],
|
|
342
|
+
\t\t\t)
|
|
343
|
+
\t\t);
|
|
344
|
+
\t}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
${controllerClassSource}
|
|
348
|
+
if ( ! function_exists( '${registerRoutesFunctionName}' ) ) {
|
|
349
|
+
\tfunction ${registerRoutesFunctionName}() {
|
|
350
|
+
\t\t$namespace = ${quotePhpString(namespace)};
|
|
351
|
+
|
|
352
|
+
${controllerBootstrapSource}
|
|
353
|
+
${routeRegistrations}
|
|
354
|
+
\t}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
add_action( 'rest_api_init', '${registerRoutesFunctionName}' );
|
|
358
|
+
`;
|
|
359
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type RestResourceMethodId } from "./cli-add-shared.js";
|
|
2
|
+
export interface RestResourcePhpFunctionNames {
|
|
3
|
+
canWriteFunctionName: string;
|
|
4
|
+
createHandlerName: string;
|
|
5
|
+
deleteHandlerName: string;
|
|
6
|
+
listHandlerName: string;
|
|
7
|
+
readHandlerName: string;
|
|
8
|
+
updateHandlerName: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Build the `register_rest_route` calls for generated REST resource PHP files.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildRestResourceRouteRegistrations(restResourceSlug: string, methods: RestResourceMethodId[], functions: RestResourcePhpFunctionNames, options: {
|
|
14
|
+
controllerVariableName?: string;
|
|
15
|
+
permissionCallback?: string;
|
|
16
|
+
routePattern: string;
|
|
17
|
+
}): string;
|
|
18
|
+
/**
|
|
19
|
+
* Normalize a configured PHP class name into a global `::class` reference.
|
|
20
|
+
*/
|
|
21
|
+
export declare function toPhpClassConstantReference(classReference: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Build an optional controller shim class for generated REST resource handlers.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildRestResourceControllerClassSource(options: {
|
|
26
|
+
controllerClass: string;
|
|
27
|
+
controllerExtends?: string;
|
|
28
|
+
functions: RestResourcePhpFunctionNames;
|
|
29
|
+
}): string;
|
|
30
|
+
/**
|
|
31
|
+
* Build the controller instantiation block used inside REST route registration.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildRestResourceControllerBootstrapSource(controllerClass: string | undefined): string;
|