wp-typia 0.22.9 → 0.23.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.
@@ -170926,6 +170926,18 @@ var REST_RESOURCE_METHOD_IDS = [
170926
170926
  "update",
170927
170927
  "delete"
170928
170928
  ];
170929
+ var MANUAL_REST_CONTRACT_HTTP_METHOD_IDS = [
170930
+ "DELETE",
170931
+ "GET",
170932
+ "PATCH",
170933
+ "POST",
170934
+ "PUT"
170935
+ ];
170936
+ var MANUAL_REST_CONTRACT_AUTH_IDS = [
170937
+ "authenticated",
170938
+ "public",
170939
+ "public-write-protected"
170940
+ ];
170929
170941
  var EDITOR_PLUGIN_SLOT_IDS = ["sidebar", "document-setting-panel"];
170930
170942
  var EDITOR_PLUGIN_SLOT_ALIASES = {
170931
170943
  PluginDocumentSettingPanel: "document-setting-panel",
@@ -170940,6 +170952,10 @@ function resolveEditorPluginSlotAlias(slot) {
170940
170952
  }
170941
170953
  return EDITOR_PLUGIN_SLOT_ALIASES[trimmed];
170942
170954
  }
170955
+ var INTEGRATION_ENV_SERVICE_IDS = [
170956
+ "none",
170957
+ "docker-compose"
170958
+ ];
170943
170959
  var ADD_BLOCK_TEMPLATE_IDS = [
170944
170960
  "basic",
170945
170961
  "interactivity",
@@ -170947,7 +170963,9 @@ var ADD_BLOCK_TEMPLATE_IDS = [
170947
170963
  "compound"
170948
170964
  ];
170949
170965
  function suggestAddBlockTemplateId(templateId) {
170950
- return suggestCloseId(templateId, ADD_BLOCK_TEMPLATE_IDS);
170966
+ return suggestCloseId(templateId, ADD_BLOCK_TEMPLATE_IDS, {
170967
+ maxDistance: 3
170968
+ });
170951
170969
  }
170952
170970
 
170953
170971
  // ../wp-typia-project-tools/src/runtime/hooked-blocks.ts
@@ -171036,7 +171054,97 @@ function toTitleCase(input) {
171036
171054
 
171037
171055
  // ../wp-typia-project-tools/src/runtime/cli-add-validation.ts
171038
171056
  var WORKSPACE_GENERATED_SLUG_PATTERN = /^[a-z][a-z0-9-]*$/;
171057
+ var WORDPRESS_POST_TYPE_PATTERN = /^[a-z0-9_][a-z0-9_-]*$/u;
171058
+ var TYPESCRIPT_IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/u;
171059
+ var TYPESCRIPT_RESERVED_IDENTIFIERS = new Set([
171060
+ "abstract",
171061
+ "any",
171062
+ "as",
171063
+ "asserts",
171064
+ "async",
171065
+ "await",
171066
+ "bigint",
171067
+ "boolean",
171068
+ "break",
171069
+ "case",
171070
+ "catch",
171071
+ "class",
171072
+ "const",
171073
+ "constructor",
171074
+ "continue",
171075
+ "debugger",
171076
+ "declare",
171077
+ "default",
171078
+ "delete",
171079
+ "do",
171080
+ "else",
171081
+ "enum",
171082
+ "export",
171083
+ "extends",
171084
+ "false",
171085
+ "finally",
171086
+ "for",
171087
+ "from",
171088
+ "function",
171089
+ "get",
171090
+ "global",
171091
+ "if",
171092
+ "implements",
171093
+ "import",
171094
+ "in",
171095
+ "infer",
171096
+ "instanceof",
171097
+ "interface",
171098
+ "intrinsic",
171099
+ "is",
171100
+ "keyof",
171101
+ "let",
171102
+ "module",
171103
+ "namespace",
171104
+ "never",
171105
+ "new",
171106
+ "null",
171107
+ "number",
171108
+ "object",
171109
+ "of",
171110
+ "out",
171111
+ "override",
171112
+ "package",
171113
+ "private",
171114
+ "protected",
171115
+ "public",
171116
+ "readonly",
171117
+ "require",
171118
+ "return",
171119
+ "satisfies",
171120
+ "set",
171121
+ "static",
171122
+ "string",
171123
+ "super",
171124
+ "switch",
171125
+ "symbol",
171126
+ "this",
171127
+ "throw",
171128
+ "true",
171129
+ "try",
171130
+ "type",
171131
+ "typeof",
171132
+ "undefined",
171133
+ "unique",
171134
+ "unknown",
171135
+ "using",
171136
+ "var",
171137
+ "void",
171138
+ "while",
171139
+ "with",
171140
+ "yield"
171141
+ ]);
171039
171142
  var REST_RESOURCE_NAMESPACE_PATTERN = /^[a-z][a-z0-9-]*(?:\/[a-z0-9-]+)+$/u;
171143
+ var PHP_IDENTIFIER_PATTERN = "[A-Za-z_][A-Za-z0-9_]*";
171144
+ var PHP_QUALIFIED_NAME_PATTERN = new RegExp(`^\\\\?${PHP_IDENTIFIER_PATTERN}(?:\\\\${PHP_IDENTIFIER_PATTERN})*$`, "u");
171145
+ var PHP_CALLBACK_REFERENCE_PATTERN = new RegExp(`^\\\\?${PHP_IDENTIFIER_PATTERN}(?:\\\\${PHP_IDENTIFIER_PATTERN})*(?:::${PHP_IDENTIFIER_PATTERN})?$`, "u");
171146
+ var REST_ROUTE_NAMED_CAPTURE_PATTERN = /\(\?P<([A-Za-z_][A-Za-z0-9_]*)>/gu;
171147
+ var REST_ROUTE_UNSUPPORTED_CAPTURE_PATTERN = /\((?!\?(?:P<[A-Za-z_][A-Za-z0-9_]*>|:))/u;
171040
171148
  function assertValidGeneratedSlug(label, slug, usage) {
171041
171149
  if (!slug) {
171042
171150
  throw new Error(`${label} is required. Use \`${usage}\`.`);
@@ -171046,6 +171154,19 @@ function assertValidGeneratedSlug(label, slug, usage) {
171046
171154
  }
171047
171155
  return slug;
171048
171156
  }
171157
+ function assertValidTypeScriptIdentifier(label, value, usage) {
171158
+ const trimmed = value.trim();
171159
+ if (!trimmed) {
171160
+ throw new Error(`${label} is required. Use \`${usage}\`.`);
171161
+ }
171162
+ if (!TYPESCRIPT_IDENTIFIER_PATTERN.test(trimmed)) {
171163
+ throw new Error(`${label} must be a valid TypeScript identifier, such as ExternalRetrieveResponse.`);
171164
+ }
171165
+ if (TYPESCRIPT_RESERVED_IDENTIFIERS.has(trimmed)) {
171166
+ throw new Error(`${label} must not be a reserved TypeScript keyword, such as ${trimmed}.`);
171167
+ }
171168
+ return trimmed;
171169
+ }
171049
171170
  function assertValidRestResourceNamespace(namespace) {
171050
171171
  const trimmed = namespace.trim();
171051
171172
  if (!trimmed) {
@@ -171059,6 +171180,34 @@ function assertValidRestResourceNamespace(namespace) {
171059
171180
  function resolveRestResourceNamespace(workspaceNamespace, namespace) {
171060
171181
  return assertValidRestResourceNamespace(namespace ?? `${workspaceNamespace}/v1`);
171061
171182
  }
171183
+ function assertValidPostMetaPostType(postType) {
171184
+ const trimmed = postType.trim();
171185
+ if (!trimmed) {
171186
+ throw new Error("`wp-typia add post-meta` requires --post-type <post-type>.");
171187
+ }
171188
+ if (trimmed.length > 20) {
171189
+ throw new Error("Post meta post type must be 20 characters or fewer.");
171190
+ }
171191
+ if (!WORDPRESS_POST_TYPE_PATTERN.test(trimmed)) {
171192
+ throw new Error("Post meta post type must use a WordPress post type key such as `post` or `example_post_type`.");
171193
+ }
171194
+ return trimmed;
171195
+ }
171196
+ function resolvePostMetaKey({
171197
+ metaKey,
171198
+ phpPrefix,
171199
+ slug
171200
+ }) {
171201
+ const resolvedMetaKey = metaKey ?? `_${toSnakeCase(`${phpPrefix}_${slug}`)}`;
171202
+ const trimmed = resolvedMetaKey.trim();
171203
+ if (!trimmed) {
171204
+ throw new Error("Post meta key cannot be empty.");
171205
+ }
171206
+ if (/[\p{Cc}\s]/u.test(trimmed)) {
171207
+ throw new Error("Post meta key must not contain whitespace or control characters.");
171208
+ }
171209
+ return trimmed;
171210
+ }
171062
171211
  function assertValidRestResourceMethods(methods) {
171063
171212
  const rawMethods = typeof methods === "string" && methods.trim().length > 0 ? methods.split(",").map((value) => value.trim()).filter(Boolean) : ["list", "read", "create"];
171064
171213
  const normalizedMethods = Array.from(new Set(rawMethods));
@@ -171071,6 +171220,91 @@ function assertValidRestResourceMethods(methods) {
171071
171220
  }
171072
171221
  return normalizedMethods;
171073
171222
  }
171223
+ function resolveOptionalPhpCallbackReference(label, callback) {
171224
+ if (callback == null) {
171225
+ return;
171226
+ }
171227
+ const trimmed = callback.trim();
171228
+ if (!trimmed) {
171229
+ throw new Error(`${label} cannot be empty.`);
171230
+ }
171231
+ if (!PHP_CALLBACK_REFERENCE_PATTERN.test(trimmed)) {
171232
+ throw new Error(`${label} must be a PHP function reference or ClassName::method callback.`);
171233
+ }
171234
+ return trimmed;
171235
+ }
171236
+ function resolveOptionalPhpClassReference(label, classReference) {
171237
+ if (classReference == null) {
171238
+ return;
171239
+ }
171240
+ const trimmed = classReference.trim();
171241
+ if (!trimmed) {
171242
+ throw new Error(`${label} cannot be empty.`);
171243
+ }
171244
+ if (!PHP_QUALIFIED_NAME_PATTERN.test(trimmed)) {
171245
+ throw new Error(`${label} must be a PHP class reference such as Demo_Rest_Controller or Vendor\\Plugin\\Controller.`);
171246
+ }
171247
+ return trimmed;
171248
+ }
171249
+ function assertValidManualRestContractHttpMethod(method = "GET") {
171250
+ const normalized = method.trim().toUpperCase();
171251
+ if (MANUAL_REST_CONTRACT_HTTP_METHOD_IDS.includes(normalized)) {
171252
+ return normalized;
171253
+ }
171254
+ throw new Error(`Manual REST contract method must be one of: ${MANUAL_REST_CONTRACT_HTTP_METHOD_IDS.join(", ")}.`);
171255
+ }
171256
+ function assertValidManualRestContractAuth(auth = "public") {
171257
+ const normalized = auth.trim().toLowerCase();
171258
+ if (MANUAL_REST_CONTRACT_AUTH_IDS.includes(normalized)) {
171259
+ return normalized;
171260
+ }
171261
+ throw new Error(`Manual REST contract auth must be one of: ${MANUAL_REST_CONTRACT_AUTH_IDS.join(", ")}.`);
171262
+ }
171263
+ function resolveRestRoutePathPattern(options) {
171264
+ const explicitPath = typeof options.pathPattern === "string" ? options.pathPattern.trim() : undefined;
171265
+ if (explicitPath === "") {
171266
+ throw new Error(options.emptyMessage);
171267
+ }
171268
+ const trimmed = explicitPath ?? options.defaultPath;
171269
+ if (/^https?:\/\//iu.test(trimmed)) {
171270
+ throw new Error(`${options.label} must be a route pattern relative to the namespace, not an absolute URL.`);
171271
+ }
171272
+ const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
171273
+ if (!withLeadingSlash || withLeadingSlash === "/") {
171274
+ throw new Error(options.emptyMessage);
171275
+ }
171276
+ if (/\s/u.test(withLeadingSlash)) {
171277
+ throw new Error(`${options.label} must not contain whitespace.`);
171278
+ }
171279
+ return withLeadingSlash;
171280
+ }
171281
+ function isGeneratedRestResourceRoutePatternCompatible(routePattern) {
171282
+ const namedCaptures = Array.from(routePattern.matchAll(REST_ROUTE_NAMED_CAPTURE_PATTERN), (match) => match[1]);
171283
+ const hasRegexGroup = routePattern.includes("(");
171284
+ const hasUnsupportedCapture = REST_ROUTE_UNSUPPORTED_CAPTURE_PATTERN.test(routePattern);
171285
+ return !hasUnsupportedCapture && (!hasRegexGroup || namedCaptures.length === 1 && namedCaptures[0] === "id");
171286
+ }
171287
+ function resolveManualRestContractPathPattern(slug, pathPattern) {
171288
+ return resolveRestRoutePathPattern({
171289
+ defaultPath: `/${slug}`,
171290
+ emptyMessage: "Manual REST contract path is required. Use `--path <route-pattern>` such as `/external-record/(?P<id>[\\d]+)`.",
171291
+ label: "Manual REST contract path",
171292
+ pathPattern
171293
+ });
171294
+ }
171295
+ function resolveGeneratedRestResourceRoutePattern(slug, routePattern) {
171296
+ const resolvedRoutePattern = resolveRestRoutePathPattern({
171297
+ defaultPath: `/${slug}/item`,
171298
+ emptyMessage: "Generated REST resource route pattern is required. Use `--route-pattern <route-pattern>` such as `/records/(?P<id>[\\d]+)`.",
171299
+ label: "Generated REST resource route pattern",
171300
+ pathPattern: routePattern
171301
+ });
171302
+ const hasExplicitRoutePattern = typeof routePattern === "string" && routePattern.trim().length > 0;
171303
+ if (hasExplicitRoutePattern && !isGeneratedRestResourceRoutePatternCompatible(resolvedRoutePattern)) {
171304
+ throw new Error("Generated REST resource route pattern must use only an `(?P<id>...)` named capture when using regex groups.");
171305
+ }
171306
+ return resolvedRoutePattern;
171307
+ }
171074
171308
  function assertValidHookedBlockPosition(position) {
171075
171309
  if (HOOKED_BLOCK_POSITION_IDS.includes(position)) {
171076
171310
  return position;
@@ -171103,6 +171337,13 @@ function assertValidEditorPluginSlot(slot = "sidebar") {
171103
171337
  }
171104
171338
  throw new Error(`Editor plugin slot must be one of: ${EDITOR_PLUGIN_SLOT_IDS.join(", ")}. Legacy aliases: PluginSidebar, PluginDocumentSettingPanel.`);
171105
171339
  }
171340
+ function assertValidIntegrationEnvService(service = "none") {
171341
+ const trimmed = service.trim();
171342
+ if (INTEGRATION_ENV_SERVICE_IDS.includes(trimmed)) {
171343
+ return trimmed;
171344
+ }
171345
+ throw new Error(`Integration environment service must be one of: ${INTEGRATION_ENV_SERVICE_IDS.join(", ")}.`);
171346
+ }
171106
171347
 
171107
171348
  // ../wp-typia-project-tools/src/runtime/fs-async.ts
171108
171349
  import { promises as fsp } from "fs";
@@ -171125,10 +171366,13 @@ async function readOptionalUtf8File(filePath) {
171125
171366
  }
171126
171367
  }
171127
171368
  function getNodeErrorCode(error) {
171128
- return typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
171369
+ return getOptionalNodeErrorCode(error) ?? "";
171370
+ }
171371
+ function getOptionalNodeErrorCode(error) {
171372
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code) : undefined;
171129
171373
  }
171130
171374
  function isFileNotFoundError(error) {
171131
- return getNodeErrorCode(error) === "ENOENT";
171375
+ return getOptionalNodeErrorCode(error) === "ENOENT";
171132
171376
  }
171133
171377
 
171134
171378
  // ../wp-typia-project-tools/src/runtime/cli-add-help.ts
@@ -171136,12 +171380,16 @@ function formatAddHelpText() {
171136
171380
  return `Usage:
171137
171381
  wp-typia add admin-view <name> [--source <rest-resource:slug|core-data:kind/name>] [--dry-run]
171138
171382
  wp-typia add block <name> [--template <${ADD_BLOCK_TEMPLATE_IDS.join("|")}>] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--inner-blocks-preset <freeform|ordered|horizontal|locked-structure>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--dry-run]
171383
+ wp-typia add integration-env <name> [--wp-env] [--service <none|docker-compose>] [--dry-run]
171139
171384
  wp-typia add variation <name> --block <block-slug> [--dry-run]
171140
171385
  wp-typia add style <name> --block <block-slug> [--dry-run]
171141
171386
  wp-typia add transform <name> --from <namespace/block> --to <block-slug|namespace/block-slug> [--dry-run]
171142
171387
  wp-typia add pattern <name> [--dry-run]
171143
171388
  wp-typia add binding-source <name> [--block <block-slug|namespace/block-slug> --attribute <attribute>] [--dry-run]
171144
- wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <${REST_RESOURCE_METHOD_IDS.join(",")}>] [--dry-run]
171389
+ wp-typia add contract <name> [--type <ExportedTypeName>] [--dry-run]
171390
+ wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <${REST_RESOURCE_METHOD_IDS.join(",")}>] [--route-pattern <route-pattern>] [--permission-callback <callback>] [--controller-class <ClassName>] [--controller-extends <BaseClass>] [--dry-run]
171391
+ wp-typia add rest-resource <name> --manual [--namespace <vendor/v1>] [--method <GET|POST|PUT|PATCH|DELETE>] [--auth <public|authenticated|public-write-protected>] [--path <route-pattern>] [--query-type <Type>] [--body-type <Type>] [--response-type <Type>] [--dry-run]
171392
+ wp-typia add post-meta <name> --post-type <post-type> [--type <ExportedTypeName>] [--meta-key <meta-key>] [--hide-from-rest] [--dry-run]
171145
171393
  wp-typia add ability <name> [--dry-run]
171146
171394
  wp-typia add ai-feature <name> [--namespace <vendor/v1>] [--dry-run]
171147
171395
  wp-typia add hooked-block <block-slug> --anchor <anchor-block-name> --position <${HOOKED_BLOCK_POSITION_IDS.join("|")}> [--dry-run]
@@ -171155,13 +171403,18 @@ Notes:
171155
171403
  Pass \`--source rest-resource:<slug>\` to reuse a list-capable REST resource.
171156
171404
  Pass \`--source core-data:postType/post\` or \`--source core-data:taxonomy/category\` to bind a WordPress-owned entity collection.
171157
171405
  Generated admin-view workspaces add \`@wp-typia/dataviews\` and the needed WordPress DataViews packages as opt-in dependencies.
171406
+ \`add integration-env\` generates an opt-in local smoke starter under \`scripts/integration-smoke/\`, updates \`.env.example\`, and can add \`@wordpress/env\` plus \`.wp-env.json\` when \`--wp-env\` is passed.
171407
+ Pass \`--service docker-compose\` to include a placeholder local service stack that can be adapted to project-specific dependencies.
171158
171408
  \`query-loop\` is a create-time scaffold family. Use \`wp-typia create <project-dir> --template query-loop\` instead of \`wp-typia add block\`.
171159
171409
  \`add variation\` targets an existing block slug from \`scripts/block-config.ts\`.
171160
171410
  \`add style\` registers a Block Styles option for an existing generated block.
171161
171411
  \`add transform\` adds a block-to-block transform into an existing generated block.
171162
171412
  \`add pattern\` scaffolds a namespaced PHP pattern shell under \`src/patterns/\`.
171163
171413
  \`add binding-source\` scaffolds shared PHP and editor registration under \`src/bindings/\`; pass \`--block\` and \`--attribute\` together to declare an end-to-end bindable attribute on an existing generated block.
171164
- \`add rest-resource\` scaffolds plugin-level TypeScript REST contracts under \`src/rest/\` and PHP route glue under \`inc/rest/\`.
171414
+ \`add contract\` registers a standalone TypeScript wire contract under \`src/contracts/\` and generates a stable JSON Schema artifact without creating PHP route glue.
171415
+ \`add rest-resource\` scaffolds plugin-level TypeScript REST contracts under \`src/rest/\` and PHP route glue under \`inc/rest/\`. Use \`--route-pattern\`, \`--permission-callback\`, \`--controller-class\`, and \`--controller-extends\` when an existing WordPress controller or permission model needs to own part of the generated route surface.
171416
+ Pass \`--manual\` with \`add rest-resource\` to track an external REST route with typed schemas, OpenAPI, clients, and drift checks without generating PHP route/controller files.
171417
+ \`add post-meta\` scaffolds a typed post meta contract under \`src/post-meta/\`, generates a schema artifact, and wires \`register_post_meta()\` helpers under \`inc/post-meta/\`.
171165
171418
  \`add ability\` scaffolds typed workflow abilities under \`src/abilities/\` and server registration under \`inc/abilities/\`.
171166
171419
  \`add ai-feature\` scaffolds server-owned AI feature endpoints under \`src/ai-features/\` and PHP route glue under \`inc/ai-features/\`.
171167
171420
  \`add hooked-block\` patches an existing workspace block's \`block.json\` \`blockHooks\` metadata.
@@ -171260,14 +171513,7 @@ async function patchFile(filePath, transform) {
171260
171513
  }
171261
171514
  }
171262
171515
  async function readOptionalFile(filePath) {
171263
- try {
171264
- return await fsp2.readFile(filePath, "utf8");
171265
- } catch (error) {
171266
- if (isFileNotFoundError2(error)) {
171267
- return null;
171268
- }
171269
- throw error;
171270
- }
171516
+ return readOptionalUtf8File(filePath);
171271
171517
  }
171272
171518
  async function restoreOptionalFile(filePath, source) {
171273
171519
  if (source === null) {
@@ -171295,9 +171541,6 @@ async function rollbackWorkspaceMutation(snapshot) {
171295
171541
  await restoreOptionalFile(filePath, source);
171296
171542
  }
171297
171543
  }
171298
- function isFileNotFoundError2(error) {
171299
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
171300
- }
171301
171544
  // ../wp-typia-project-tools/src/runtime/cli-add-block-json.ts
171302
171545
  import path2 from "path";
171303
171546
  import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
@@ -171448,6 +171691,40 @@ var REST_RESOURCE_COLLISION_DESCRIPTOR = {
171448
171691
  message: ({ slug }) => `A REST resource inventory entry already exists for ${slug}. Choose a different name.`
171449
171692
  }
171450
171693
  };
171694
+ var POST_META_COLLISION_DESCRIPTOR = {
171695
+ filesystemCollisions: [
171696
+ {
171697
+ label: "A post meta contract",
171698
+ relativePath: ({ slug }) => path3.join("src", "post-meta", slug)
171699
+ },
171700
+ {
171701
+ label: "A post meta bootstrap",
171702
+ relativePath: ({ slug }) => path3.join("inc", "post-meta", `${slug}.php`)
171703
+ }
171704
+ ],
171705
+ inventoryCollision: {
171706
+ entries: (inventory) => inventory.postMeta,
171707
+ exists: (entry, { slug }) => entry.slug === slug,
171708
+ message: ({ slug }) => `A post meta inventory entry already exists for ${slug}. Choose a different name.`
171709
+ }
171710
+ };
171711
+ var CONTRACT_COLLISION_DESCRIPTOR = {
171712
+ filesystemCollisions: [
171713
+ {
171714
+ label: "A standalone contract",
171715
+ relativePath: ({ slug }) => path3.join("src", "contracts", `${slug}.ts`)
171716
+ },
171717
+ {
171718
+ label: "A standalone contract schema",
171719
+ relativePath: ({ slug }) => path3.join("src", "contracts", `${slug}.schema.json`)
171720
+ }
171721
+ ],
171722
+ inventoryCollision: {
171723
+ entries: (inventory) => inventory.contracts,
171724
+ exists: (entry, { slug }) => entry.slug === slug,
171725
+ message: ({ slug }) => `A standalone contract inventory entry already exists for ${slug}. Choose a different name.`
171726
+ }
171727
+ };
171451
171728
  var ADMIN_VIEW_COLLISION_DESCRIPTOR = {
171452
171729
  filesystemCollisions: [
171453
171730
  {
@@ -171560,6 +171837,25 @@ function assertRestResourceDoesNotExist(projectDir, restResourceSlug, inventory)
171560
171837
  projectDir
171561
171838
  });
171562
171839
  }
171840
+ function assertPostMetaDoesNotExist(projectDir, postMetaSlug, postType, metaKey, inventory) {
171841
+ assertAddKindScaffoldDoesNotExist({
171842
+ context: { metaKey, postType, slug: postMetaSlug },
171843
+ descriptor: POST_META_COLLISION_DESCRIPTOR,
171844
+ inventory,
171845
+ projectDir
171846
+ });
171847
+ if (inventory.postMeta.some((entry) => entry.postType === postType && entry.metaKey === metaKey)) {
171848
+ throw new Error(`A post meta inventory entry already registers ${metaKey} for ${postType}. Choose a different meta key or post type.`);
171849
+ }
171850
+ }
171851
+ function assertContractDoesNotExist(projectDir, contractSlug, inventory) {
171852
+ assertAddKindScaffoldDoesNotExist({
171853
+ context: { slug: contractSlug },
171854
+ descriptor: CONTRACT_COLLISION_DESCRIPTOR,
171855
+ inventory,
171856
+ projectDir
171857
+ });
171858
+ }
171563
171859
  function assertAdminViewDoesNotExist(projectDir, adminViewSlug, inventory) {
171564
171860
  assertAddKindScaffoldDoesNotExist({
171565
171861
  context: { slug: adminViewSlug },
@@ -171592,1126 +171888,1313 @@ function assertEditorPluginDoesNotExist(projectDir, editorPluginSlug, inventory)
171592
171888
  projectDir
171593
171889
  });
171594
171890
  }
171595
- // ../wp-typia-project-tools/src/runtime/workspace-inventory.ts
171596
- var import_typescript2 = __toESM(require_typescript(), 1);
171891
+ // ../wp-typia-project-tools/src/runtime/workspace-inventory-read.ts
171597
171892
  import { readFileSync } from "fs";
171598
171893
  import path4 from "path";
171599
- import { readFile, writeFile } from "fs/promises";
171894
+ import { readFile } from "fs/promises";
171600
171895
 
171601
- // ../wp-typia-project-tools/src/runtime/php-utils.ts
171602
- function escapeRegex(value) {
171603
- return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
171896
+ // ../wp-typia-project-tools/src/runtime/workspace-inventory-parser.ts
171897
+ var import_typescript2 = __toESM(require_typescript(), 1);
171898
+
171899
+ // ../wp-typia-project-tools/src/runtime/ts-property-names.ts
171900
+ var import_typescript = __toESM(require_typescript(), 1);
171901
+ function getPropertyNameText(name) {
171902
+ if (import_typescript.default.isIdentifier(name) || import_typescript.default.isStringLiteral(name) || import_typescript.default.isNumericLiteral(name)) {
171903
+ return name.text;
171904
+ }
171905
+ return null;
171604
171906
  }
171605
- function quotePhpString(value) {
171606
- return `'${value.replace(/\\/gu, "\\\\").replace(/'/gu, "\\'")}'`;
171907
+
171908
+ // ../wp-typia-project-tools/src/runtime/workspace-inventory-templates.ts
171909
+ var BLOCK_CONFIG_ENTRY_MARKER = "\t// wp-typia add block entries";
171910
+ var VARIATION_CONFIG_ENTRY_MARKER = "\t// wp-typia add variation entries";
171911
+ var BLOCK_STYLE_CONFIG_ENTRY_MARKER = "\t// wp-typia add style entries";
171912
+ var BLOCK_TRANSFORM_CONFIG_ENTRY_MARKER = "\t// wp-typia add transform entries";
171913
+ var PATTERN_CONFIG_ENTRY_MARKER = "\t// wp-typia add pattern entries";
171914
+ var BINDING_SOURCE_CONFIG_ENTRY_MARKER = "\t// wp-typia add binding-source entries";
171915
+ var CONTRACT_CONFIG_ENTRY_MARKER = "\t// wp-typia add contract entries";
171916
+ var REST_RESOURCE_CONFIG_ENTRY_MARKER = "\t// wp-typia add rest-resource entries";
171917
+ var POST_META_CONFIG_ENTRY_MARKER = "\t// wp-typia add post-meta entries";
171918
+ var ABILITY_CONFIG_ENTRY_MARKER = "\t// wp-typia add ability entries";
171919
+ var AI_FEATURE_CONFIG_ENTRY_MARKER = "\t// wp-typia add ai-feature entries";
171920
+ var ADMIN_VIEW_CONFIG_ENTRY_MARKER = "\t// wp-typia add admin-view entries";
171921
+ var EDITOR_PLUGIN_CONFIG_ENTRY_MARKER = "\t// wp-typia add editor-plugin entries";
171922
+ var VARIATIONS_INTERFACE_SECTION = `
171923
+
171924
+ export interface WorkspaceVariationConfig {
171925
+ block: string;
171926
+ file: string;
171927
+ slug: string;
171607
171928
  }
171608
- function hasPhpFunctionDefinition(source, functionName) {
171609
- return new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\(`, "u").test(source);
171929
+ `;
171930
+ var VARIATIONS_CONST_SECTION = `
171931
+
171932
+ export const VARIATIONS: WorkspaceVariationConfig[] = [
171933
+ // wp-typia add variation entries
171934
+ ];
171935
+ `;
171936
+ var BLOCK_STYLES_INTERFACE_SECTION = `
171937
+
171938
+ export interface WorkspaceBlockStyleConfig {
171939
+ block: string;
171940
+ file: string;
171941
+ slug: string;
171610
171942
  }
171611
- function isPhpIdentifierStart(character) {
171612
- return /^[A-Za-z_]$/u.test(character ?? "");
171943
+ `;
171944
+ var BLOCK_STYLES_CONST_SECTION = `
171945
+
171946
+ export const BLOCK_STYLES: WorkspaceBlockStyleConfig[] = [
171947
+ // wp-typia add style entries
171948
+ ];
171949
+ `;
171950
+ var BLOCK_TRANSFORMS_INTERFACE_SECTION = `
171951
+
171952
+ export interface WorkspaceBlockTransformConfig {
171953
+ block: string;
171954
+ file: string;
171955
+ from: string;
171956
+ slug: string;
171957
+ to: string;
171613
171958
  }
171614
- function isPhpIdentifierPart(character) {
171615
- return /^[A-Za-z0-9_]$/u.test(character ?? "");
171959
+ `;
171960
+ var BLOCK_TRANSFORMS_CONST_SECTION = `
171961
+
171962
+ export const BLOCK_TRANSFORMS: WorkspaceBlockTransformConfig[] = [
171963
+ // wp-typia add transform entries
171964
+ ];
171965
+ `;
171966
+ var PATTERNS_INTERFACE_SECTION = `
171967
+
171968
+ export interface WorkspacePatternConfig {
171969
+ file: string;
171970
+ slug: string;
171616
171971
  }
171617
- function isPhpLineStart(source, index) {
171618
- return index === 0 || source[index - 1] === `
171619
171972
  `;
171973
+ var PATTERNS_CONST_SECTION = `
171974
+
171975
+ export const PATTERNS: WorkspacePatternConfig[] = [
171976
+ // wp-typia add pattern entries
171977
+ ];
171978
+ `;
171979
+ var BINDING_SOURCES_INTERFACE_SECTION = `
171980
+
171981
+ export interface WorkspaceBindingSourceConfig {
171982
+ attribute?: string;
171983
+ block?: string;
171984
+ editorFile: string;
171985
+ serverFile: string;
171986
+ slug: string;
171620
171987
  }
171621
- function isPhpHorizontalWhitespace(character) {
171622
- return character === " " || character === "\t";
171988
+ `;
171989
+ var BINDING_SOURCES_CONST_SECTION = `
171990
+
171991
+ export const BINDING_SOURCES: WorkspaceBindingSourceConfig[] = [
171992
+ // wp-typia add binding-source entries
171993
+ ];
171994
+ `;
171995
+ var CONTRACTS_INTERFACE_SECTION = `
171996
+
171997
+ export interface WorkspaceContractConfig {
171998
+ schemaFile: string;
171999
+ slug: string;
172000
+ sourceTypeName: string;
172001
+ typesFile: string;
171623
172002
  }
171624
- function isPhpWhitespace(character) {
171625
- return typeof character === "string" && /\s/u.test(character);
172003
+ `;
172004
+ var CONTRACTS_CONST_SECTION = `
172005
+
172006
+ export const CONTRACTS: WorkspaceContractConfig[] = [
172007
+ // wp-typia add contract entries
172008
+ ];
172009
+ `;
172010
+ var REST_RESOURCES_INTERFACE_SECTION = `
172011
+
172012
+ export interface WorkspaceRestResourceBaseConfig {
172013
+ apiFile: string;
172014
+ auth?: 'authenticated' | 'public' | 'public-write-protected';
172015
+ bodyTypeName?: string;
172016
+ clientFile: string;
172017
+ controllerClass?: string;
172018
+ controllerExtends?: string;
172019
+ namespace: string;
172020
+ openApiFile: string;
172021
+ permissionCallback?: string;
172022
+ restManifest?: ReturnType<
172023
+ typeof import( '@wp-typia/block-runtime/metadata-core' ).defineEndpointManifest
172024
+ >;
172025
+ secretFieldName?: string;
172026
+ secretStateFieldName?: string;
172027
+ slug: string;
172028
+ typesFile: string;
172029
+ validatorsFile: string;
171626
172030
  }
171627
- function findPhpLineBoundary(source, index) {
171628
- const newlineIndex = source.indexOf(`
171629
- `, index);
171630
- if (newlineIndex === -1) {
171631
- return {
171632
- contentEnd: source.endsWith("\r") ? source.length - 1 : source.length,
171633
- nextStart: source.length
171634
- };
171635
- }
171636
- return {
171637
- contentEnd: source[newlineIndex - 1] === "\r" ? newlineIndex - 1 : newlineIndex,
171638
- nextStart: newlineIndex + 1
171639
- };
172031
+
172032
+ export interface GeneratedWorkspaceRestResourceConfig extends WorkspaceRestResourceBaseConfig {
172033
+ dataFile: string;
172034
+ methods: Array< 'list' | 'read' | 'create' | 'update' | 'delete' >;
172035
+ mode?: 'generated';
172036
+ phpFile: string;
172037
+ routePattern?: string;
171640
172038
  }
171641
- function parsePhpHeredocStart(source, index) {
171642
- if (!source.startsWith("<<<", index)) {
171643
- return null;
171644
- }
171645
- let cursor = index + 3;
171646
- while (isPhpHorizontalWhitespace(source[cursor])) {
171647
- cursor += 1;
171648
- }
171649
- const quote = source[cursor] === "'" || source[cursor] === '"' ? source[cursor] : "";
171650
- if (quote) {
171651
- cursor += 1;
171652
- }
171653
- if (!isPhpIdentifierStart(source[cursor])) {
171654
- return null;
171655
- }
171656
- const delimiterStart = cursor;
171657
- cursor += 1;
171658
- while (isPhpIdentifierPart(source[cursor])) {
171659
- cursor += 1;
171660
- }
171661
- const delimiter = source.slice(delimiterStart, cursor);
171662
- if (quote) {
171663
- if (source[cursor] !== quote) {
171664
- return null;
171665
- }
171666
- cursor += 1;
171667
- }
171668
- const lineBoundary = findPhpLineBoundary(source, cursor);
171669
- if (source.slice(cursor, lineBoundary.contentEnd).trim() !== "") {
171670
- return null;
171671
- }
171672
- return {
171673
- contentStart: lineBoundary.nextStart,
171674
- delimiter
171675
- };
172039
+
172040
+ export interface ManualWorkspaceRestResourceConfig extends WorkspaceRestResourceBaseConfig {
172041
+ method: 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT';
172042
+ methods: [];
172043
+ mode: 'manual';
172044
+ pathPattern: string;
172045
+ queryTypeName: string;
172046
+ responseTypeName: string;
171676
172047
  }
171677
- function findPhpHeredocClosingEnd(source, index, delimiter) {
171678
- if (!isPhpLineStart(source, index)) {
171679
- return null;
171680
- }
171681
- let cursor = index;
171682
- while (isPhpHorizontalWhitespace(source[cursor])) {
171683
- cursor += 1;
171684
- }
171685
- if (!source.startsWith(delimiter, cursor)) {
171686
- return null;
171687
- }
171688
- cursor += delimiter.length;
171689
- if (isPhpIdentifierPart(source[cursor])) {
171690
- return null;
171691
- }
171692
- let continuationCursor = cursor;
171693
- while (isPhpHorizontalWhitespace(source[continuationCursor])) {
171694
- continuationCursor += 1;
171695
- }
171696
- const continuation = source[continuationCursor];
171697
- if (continuationCursor >= source.length || continuation === "\r" || continuation === `
171698
- ` || !isPhpIdentifierPart(continuation)) {
171699
- return cursor;
171700
- }
171701
- return null;
172048
+
172049
+ export type WorkspaceRestResourceConfig =
172050
+ | GeneratedWorkspaceRestResourceConfig
172051
+ | ManualWorkspaceRestResourceConfig;
172052
+ `;
172053
+ var REST_RESOURCES_CONST_SECTION = `
172054
+
172055
+ export const REST_RESOURCES: WorkspaceRestResourceConfig[] = [
172056
+ // wp-typia add rest-resource entries
172057
+ ];
172058
+ `;
172059
+ var POST_META_INTERFACE_SECTION = `
172060
+
172061
+ export interface WorkspacePostMetaConfig {
172062
+ metaKey: string;
172063
+ phpFile: string;
172064
+ postType: string;
172065
+ schemaFile: string;
172066
+ showInRest: boolean;
172067
+ slug: string;
172068
+ sourceTypeName: string;
172069
+ typesFile: string;
171702
172070
  }
171703
- function skipPhpCallTrivia(source, index) {
171704
- let cursor = index;
171705
- while (cursor < source.length) {
171706
- while (isPhpWhitespace(source[cursor])) {
171707
- cursor += 1;
171708
- }
171709
- if (source[cursor] === "/" && source[cursor + 1] === "*") {
171710
- const commentEnd = source.indexOf("*/", cursor + 2);
171711
- if (commentEnd === -1) {
171712
- return null;
171713
- }
171714
- cursor = commentEnd + 2;
171715
- continue;
171716
- }
171717
- if (source[cursor] === "/" && source[cursor + 1] === "/") {
171718
- cursor = findPhpLineBoundary(source, cursor + 2).nextStart;
171719
- continue;
171720
- }
171721
- if (source[cursor] === "#" && source[cursor + 1] !== "[") {
171722
- cursor = findPhpLineBoundary(source, cursor + 1).nextStart;
171723
- continue;
171724
- }
171725
- return cursor;
171726
- }
171727
- return cursor;
172071
+ `;
172072
+ var POST_META_CONST_SECTION = `
172073
+
172074
+ export const POST_META: WorkspacePostMetaConfig[] = [
172075
+ // wp-typia add post-meta entries
172076
+ ];
172077
+ `;
172078
+ var WORKSPACE_COMPATIBILITY_CONFIG_FIELD = ` compatibility?: {
172079
+ hardMinimums: {
172080
+ php?: string;
172081
+ wordpress?: string;
172082
+ };
172083
+ mode: 'baseline' | 'optional' | 'required';
172084
+ optionalFeatureIds: string[];
172085
+ optionalFeatures: string[];
172086
+ requiredFeatureIds: string[];
172087
+ requiredFeatures: string[];
172088
+ runtimeGates: string[];
172089
+ };
172090
+ `;
172091
+ var ABILITIES_INTERFACE_SECTION = `
172092
+
172093
+ export interface WorkspaceAbilityConfig {
172094
+ clientFile: string;
172095
+ ${WORKSPACE_COMPATIBILITY_CONFIG_FIELD} configFile: string;
172096
+ dataFile: string;
172097
+ inputSchemaFile: string;
172098
+ inputTypeName: string;
172099
+ outputSchemaFile: string;
172100
+ outputTypeName: string;
172101
+ phpFile: string;
172102
+ slug: string;
172103
+ typesFile: string;
171728
172104
  }
171729
- function matchesPhpFunctionCallAt(source, index, functionName) {
171730
- if (!source.startsWith(functionName, index)) {
171731
- return false;
171732
- }
171733
- if (isPhpIdentifierPart(source[index - 1])) {
171734
- return false;
171735
- }
171736
- const cursor = index + functionName.length;
171737
- if (isPhpIdentifierPart(source[cursor])) {
171738
- return false;
171739
- }
171740
- const callStart = skipPhpCallTrivia(source, cursor);
171741
- return callStart !== null && source[callStart] === "(";
172105
+ `;
172106
+ var ABILITIES_CONST_SECTION = `
172107
+
172108
+ export const ABILITIES: WorkspaceAbilityConfig[] = [
172109
+ // wp-typia add ability entries
172110
+ ];
172111
+ `;
172112
+ var AI_FEATURES_INTERFACE_SECTION = `
172113
+
172114
+ export interface WorkspaceAiFeatureConfig {
172115
+ aiSchemaFile: string;
172116
+ apiFile: string;
172117
+ clientFile: string;
172118
+ ${WORKSPACE_COMPATIBILITY_CONFIG_FIELD} dataFile: string;
172119
+ namespace: string;
172120
+ openApiFile: string;
172121
+ phpFile: string;
172122
+ restManifest?: ReturnType<
172123
+ typeof import( '@wp-typia/block-runtime/metadata-core' ).defineEndpointManifest
172124
+ >;
172125
+ slug: string;
172126
+ typesFile: string;
172127
+ validatorsFile: string;
171742
172128
  }
171743
- function createPhpScannerState() {
171744
- return {
171745
- heredocDelimiter: "",
171746
- interpolationComment: "",
171747
- interpolationDepth: 0,
171748
- interpolationQuote: "",
171749
- mode: "code"
171750
- };
172129
+ `;
172130
+ var AI_FEATURES_CONST_SECTION = `
172131
+
172132
+ export const AI_FEATURES: WorkspaceAiFeatureConfig[] = [
172133
+ // wp-typia add ai-feature entries
172134
+ ];
172135
+ `;
172136
+ var ADMIN_VIEWS_INTERFACE_SECTION = `
172137
+
172138
+ export interface WorkspaceAdminViewConfig {
172139
+ file: string;
172140
+ phpFile: string;
172141
+ slug: string;
172142
+ source?: string;
171751
172143
  }
171752
- function advancePhpScanner(source, index, state) {
171753
- const character = source[index];
171754
- if (state.mode === "heredoc") {
171755
- const closingEnd = findPhpHeredocClosingEnd(source, index, state.heredocDelimiter);
171756
- if (closingEnd !== null) {
171757
- state.mode = "code";
171758
- state.heredocDelimiter = "";
171759
- return { ambiguous: false, inCode: false, index: closingEnd };
171760
- }
171761
- const nextLineStart = findPhpLineBoundary(source, index).nextStart;
171762
- if (nextLineStart <= index) {
171763
- return { ambiguous: true, inCode: false, index };
171764
- }
171765
- return { ambiguous: false, inCode: false, index: nextLineStart };
172144
+ `;
172145
+ var ADMIN_VIEWS_CONST_SECTION = `
172146
+
172147
+ export const ADMIN_VIEWS: WorkspaceAdminViewConfig[] = [
172148
+ // wp-typia add admin-view entries
172149
+ ];
172150
+ `;
172151
+ var EDITOR_PLUGINS_INTERFACE_SECTION = `
172152
+
172153
+ export interface WorkspaceEditorPluginConfig {
172154
+ file: string;
172155
+ slug: string;
172156
+ slot: string;
172157
+ }
172158
+ `;
172159
+ var EDITOR_PLUGINS_CONST_SECTION = `
172160
+
172161
+ export const EDITOR_PLUGINS: WorkspaceEditorPluginConfig[] = [
172162
+ // wp-typia add editor-plugin entries
172163
+ ];
172164
+ `;
172165
+
172166
+ // ../wp-typia-project-tools/src/runtime/workspace-inventory-parser.ts
172167
+ function defineInventoryEntryParser() {
172168
+ return (descriptor) => descriptor;
172169
+ }
172170
+ var BLOCK_INVENTORY_SECTION = {
172171
+ append: {
172172
+ marker: BLOCK_CONFIG_ENTRY_MARKER,
172173
+ optionKey: "blockEntries"
172174
+ },
172175
+ parse: {
172176
+ entriesKey: "blocks",
172177
+ entry: defineInventoryEntryParser()({
172178
+ entryName: "BLOCKS",
172179
+ fields: [
172180
+ { key: "apiTypesFile" },
172181
+ { key: "attributeTypeName" },
172182
+ { key: "openApiFile" },
172183
+ { key: "slug", required: true },
172184
+ { key: "typesFile", required: true }
172185
+ ]
172186
+ }),
172187
+ exportName: "BLOCKS",
172188
+ required: true
171766
172189
  }
171767
- if (state.mode === "single-quoted" || state.mode === "double-quoted") {
171768
- const quote = state.mode === "single-quoted" ? "'" : '"';
171769
- if (character === "\\") {
171770
- return { ambiguous: false, inCode: false, index: index + 2 };
172190
+ };
172191
+ var INVENTORY_SECTIONS = [
172192
+ {
172193
+ append: {
172194
+ marker: VARIATION_CONFIG_ENTRY_MARKER,
172195
+ optionKey: "variationEntries"
172196
+ },
172197
+ interface: {
172198
+ name: "WorkspaceVariationConfig",
172199
+ section: VARIATIONS_INTERFACE_SECTION
172200
+ },
172201
+ parse: {
172202
+ entriesKey: "variations",
172203
+ entry: defineInventoryEntryParser()({
172204
+ entryName: "VARIATIONS",
172205
+ fields: [
172206
+ { key: "block", required: true },
172207
+ { key: "file", required: true },
172208
+ { key: "slug", required: true }
172209
+ ]
172210
+ }),
172211
+ hasSectionKey: "hasVariationsSection"
172212
+ },
172213
+ value: {
172214
+ name: "VARIATIONS",
172215
+ section: VARIATIONS_CONST_SECTION
171771
172216
  }
171772
- if (state.mode === "double-quoted" && character === "{" && source[index + 1] === "$") {
171773
- state.mode = "double-quoted-interpolation";
171774
- state.interpolationComment = "";
171775
- state.interpolationDepth = 1;
171776
- state.interpolationQuote = "";
171777
- return { ambiguous: false, inCode: false, index: index + 2 };
171778
- }
171779
- if (character === quote) {
171780
- state.mode = "code";
171781
- }
171782
- return { ambiguous: false, inCode: false, index: index + 1 };
171783
- }
171784
- if (state.mode === "double-quoted-interpolation") {
171785
- if (state.interpolationQuote) {
171786
- if (character === "\\") {
171787
- return { ambiguous: false, inCode: false, index: index + 2 };
171788
- }
171789
- if (character === state.interpolationQuote) {
171790
- state.interpolationQuote = "";
171791
- }
171792
- return { ambiguous: false, inCode: false, index: index + 1 };
171793
- }
171794
- if (state.interpolationComment === "line") {
171795
- if (character === "\r" || character === `
171796
- `) {
171797
- state.interpolationComment = "";
171798
- }
171799
- return { ambiguous: false, inCode: false, index: index + 1 };
171800
- }
171801
- if (state.interpolationComment === "block") {
171802
- if (character === "*" && source[index + 1] === "/") {
171803
- state.interpolationComment = "";
171804
- return { ambiguous: false, inCode: false, index: index + 2 };
171805
- }
171806
- return { ambiguous: false, inCode: false, index: index + 1 };
171807
- }
171808
- if (character === "/" && source[index + 1] === "/") {
171809
- state.interpolationComment = "line";
171810
- return { ambiguous: false, inCode: false, index: index + 2 };
172217
+ },
172218
+ {
172219
+ append: {
172220
+ marker: BLOCK_STYLE_CONFIG_ENTRY_MARKER,
172221
+ optionKey: "blockStyleEntries"
172222
+ },
172223
+ interface: {
172224
+ name: "WorkspaceBlockStyleConfig",
172225
+ section: BLOCK_STYLES_INTERFACE_SECTION
172226
+ },
172227
+ parse: {
172228
+ entriesKey: "blockStyles",
172229
+ entry: defineInventoryEntryParser()({
172230
+ entryName: "BLOCK_STYLES",
172231
+ fields: [
172232
+ { key: "block", required: true },
172233
+ { key: "file", required: true },
172234
+ { key: "slug", required: true }
172235
+ ]
172236
+ }),
172237
+ hasSectionKey: "hasBlockStylesSection"
172238
+ },
172239
+ value: {
172240
+ name: "BLOCK_STYLES",
172241
+ section: BLOCK_STYLES_CONST_SECTION
171811
172242
  }
171812
- if (character === "#" && source[index + 1] !== "[") {
171813
- state.interpolationComment = "line";
171814
- return { ambiguous: false, inCode: false, index: index + 1 };
172243
+ },
172244
+ {
172245
+ append: {
172246
+ marker: BLOCK_TRANSFORM_CONFIG_ENTRY_MARKER,
172247
+ optionKey: "blockTransformEntries"
172248
+ },
172249
+ interface: {
172250
+ name: "WorkspaceBlockTransformConfig",
172251
+ section: BLOCK_TRANSFORMS_INTERFACE_SECTION
172252
+ },
172253
+ parse: {
172254
+ entriesKey: "blockTransforms",
172255
+ entry: defineInventoryEntryParser()({
172256
+ entryName: "BLOCK_TRANSFORMS",
172257
+ fields: [
172258
+ { key: "block", required: true },
172259
+ { key: "file", required: true },
172260
+ { key: "from", required: true },
172261
+ { key: "slug", required: true },
172262
+ { key: "to", required: true }
172263
+ ]
172264
+ }),
172265
+ hasSectionKey: "hasBlockTransformsSection"
172266
+ },
172267
+ value: {
172268
+ name: "BLOCK_TRANSFORMS",
172269
+ section: BLOCK_TRANSFORMS_CONST_SECTION
171815
172270
  }
171816
- if (character === "/" && source[index + 1] === "*") {
171817
- state.interpolationComment = "block";
171818
- return { ambiguous: false, inCode: false, index: index + 2 };
172271
+ },
172272
+ {
172273
+ append: {
172274
+ marker: PATTERN_CONFIG_ENTRY_MARKER,
172275
+ optionKey: "patternEntries"
172276
+ },
172277
+ interface: {
172278
+ name: "WorkspacePatternConfig",
172279
+ section: PATTERNS_INTERFACE_SECTION
172280
+ },
172281
+ parse: {
172282
+ entriesKey: "patterns",
172283
+ entry: defineInventoryEntryParser()({
172284
+ entryName: "PATTERNS",
172285
+ fields: [
172286
+ { key: "file", required: true },
172287
+ { key: "slug", required: true }
172288
+ ]
172289
+ }),
172290
+ hasSectionKey: "hasPatternsSection"
172291
+ },
172292
+ value: {
172293
+ name: "PATTERNS",
172294
+ section: PATTERNS_CONST_SECTION
171819
172295
  }
171820
- if (character === "'" || character === '"') {
171821
- state.interpolationQuote = character;
171822
- return { ambiguous: false, inCode: false, index: index + 1 };
172296
+ },
172297
+ {
172298
+ append: {
172299
+ marker: BINDING_SOURCE_CONFIG_ENTRY_MARKER,
172300
+ optionKey: "bindingSourceEntries"
172301
+ },
172302
+ interface: {
172303
+ name: "WorkspaceBindingSourceConfig",
172304
+ section: BINDING_SOURCES_INTERFACE_SECTION
172305
+ },
172306
+ parse: {
172307
+ entriesKey: "bindingSources",
172308
+ entry: defineInventoryEntryParser()({
172309
+ entryName: "BINDING_SOURCES",
172310
+ fields: [
172311
+ { key: "attribute" },
172312
+ { key: "block" },
172313
+ { key: "editorFile", required: true },
172314
+ { key: "serverFile", required: true },
172315
+ { key: "slug", required: true }
172316
+ ]
172317
+ }),
172318
+ hasSectionKey: "hasBindingSourcesSection"
172319
+ },
172320
+ value: {
172321
+ name: "BINDING_SOURCES",
172322
+ section: BINDING_SOURCES_CONST_SECTION
171823
172323
  }
171824
- if (character === "{") {
171825
- state.interpolationDepth += 1;
171826
- return { ambiguous: false, inCode: false, index: index + 1 };
172324
+ },
172325
+ {
172326
+ append: {
172327
+ marker: CONTRACT_CONFIG_ENTRY_MARKER,
172328
+ optionKey: "contractEntries"
172329
+ },
172330
+ interface: {
172331
+ name: "WorkspaceContractConfig",
172332
+ section: CONTRACTS_INTERFACE_SECTION
172333
+ },
172334
+ parse: {
172335
+ entriesKey: "contracts",
172336
+ entry: defineInventoryEntryParser()({
172337
+ entryName: "CONTRACTS",
172338
+ fields: [
172339
+ { key: "schemaFile", required: true },
172340
+ { key: "slug", required: true },
172341
+ { key: "sourceTypeName", required: true },
172342
+ { key: "typesFile", required: true }
172343
+ ]
172344
+ }),
172345
+ hasSectionKey: "hasContractsSection"
172346
+ },
172347
+ value: {
172348
+ name: "CONTRACTS",
172349
+ section: CONTRACTS_CONST_SECTION
171827
172350
  }
171828
- if (character === "}") {
171829
- state.interpolationDepth -= 1;
171830
- if (state.interpolationDepth <= 0) {
171831
- state.interpolationComment = "";
171832
- state.interpolationDepth = 0;
171833
- state.mode = "double-quoted";
171834
- }
171835
- return { ambiguous: false, inCode: false, index: index + 1 };
172351
+ },
172352
+ {
172353
+ append: {
172354
+ marker: REST_RESOURCE_CONFIG_ENTRY_MARKER,
172355
+ optionKey: "restResourceEntries"
172356
+ },
172357
+ interface: {
172358
+ name: "WorkspaceRestResourceConfig",
172359
+ section: REST_RESOURCES_INTERFACE_SECTION
172360
+ },
172361
+ parse: {
172362
+ entriesKey: "restResources",
172363
+ entry: defineInventoryEntryParser()({
172364
+ entryName: "REST_RESOURCES",
172365
+ fields: [
172366
+ { key: "apiFile", required: true },
172367
+ {
172368
+ key: "auth",
172369
+ validate: (value, context) => {
172370
+ if (typeof value === "string" && !MANUAL_REST_CONTRACT_AUTH_IDS.includes(value)) {
172371
+ throw new Error(`${context.entryName}[${context.elementIndex}].${context.key} must be one of: ${MANUAL_REST_CONTRACT_AUTH_IDS.join(", ")}.`);
172372
+ }
172373
+ }
172374
+ },
172375
+ { key: "bodyTypeName" },
172376
+ { key: "clientFile", required: true },
172377
+ { key: "controllerClass" },
172378
+ { key: "controllerExtends" },
172379
+ { key: "dataFile" },
172380
+ {
172381
+ key: "method",
172382
+ validate: (value, context) => {
172383
+ if (typeof value === "string" && !MANUAL_REST_CONTRACT_HTTP_METHOD_IDS.includes(value)) {
172384
+ throw new Error(`${context.entryName}[${context.elementIndex}].${context.key} must be one of: ${MANUAL_REST_CONTRACT_HTTP_METHOD_IDS.join(", ")}.`);
172385
+ }
172386
+ }
172387
+ },
172388
+ {
172389
+ key: "methods",
172390
+ kind: "stringArray",
172391
+ required: true,
172392
+ validate: (value, context) => {
172393
+ const methods = Array.isArray(value) ? value : [];
172394
+ const invalidMethods = methods.filter((method) => !REST_RESOURCE_METHOD_IDS.includes(method));
172395
+ if (invalidMethods.length > 0) {
172396
+ throw new Error(`${context.entryName}[${context.elementIndex}].${context.key} includes unsupported values: ${invalidMethods.join(", ")}.`);
172397
+ }
172398
+ }
172399
+ },
172400
+ {
172401
+ key: "mode",
172402
+ validate: (value, context) => {
172403
+ if (typeof value === "string" && value !== "generated" && value !== "manual") {
172404
+ throw new Error(`${context.entryName}[${context.elementIndex}].${context.key} must be generated or manual.`);
172405
+ }
172406
+ }
172407
+ },
172408
+ { key: "namespace", required: true },
172409
+ { key: "openApiFile", required: true },
172410
+ { key: "pathPattern" },
172411
+ { key: "permissionCallback" },
172412
+ { key: "phpFile" },
172413
+ { key: "queryTypeName" },
172414
+ { key: "responseTypeName" },
172415
+ { key: "routePattern" },
172416
+ { key: "secretFieldName" },
172417
+ { key: "secretStateFieldName" },
172418
+ { key: "slug", required: true },
172419
+ { key: "typesFile", required: true },
172420
+ { key: "validatorsFile", required: true }
172421
+ ]
172422
+ }),
172423
+ hasSectionKey: "hasRestResourcesSection"
172424
+ },
172425
+ value: {
172426
+ name: "REST_RESOURCES",
172427
+ section: REST_RESOURCES_CONST_SECTION
171836
172428
  }
171837
- return { ambiguous: false, inCode: false, index: index + 1 };
171838
- }
171839
- if (state.mode === "line-comment") {
171840
- if (character === "\r" || character === `
171841
- `) {
171842
- state.mode = "code";
172429
+ },
172430
+ {
172431
+ append: {
172432
+ marker: POST_META_CONFIG_ENTRY_MARKER,
172433
+ optionKey: "postMetaEntries"
172434
+ },
172435
+ interface: {
172436
+ name: "WorkspacePostMetaConfig",
172437
+ section: POST_META_INTERFACE_SECTION
172438
+ },
172439
+ parse: {
172440
+ entriesKey: "postMeta",
172441
+ entry: defineInventoryEntryParser()({
172442
+ entryName: "POST_META",
172443
+ fields: [
172444
+ { key: "metaKey", required: true },
172445
+ { key: "phpFile", required: true },
172446
+ { key: "postType", required: true },
172447
+ { key: "schemaFile", required: true },
172448
+ { key: "showInRest", kind: "boolean", required: true },
172449
+ { key: "slug", required: true },
172450
+ { key: "sourceTypeName", required: true },
172451
+ { key: "typesFile", required: true }
172452
+ ]
172453
+ }),
172454
+ hasSectionKey: "hasPostMetaSection"
172455
+ },
172456
+ value: {
172457
+ name: "POST_META",
172458
+ section: POST_META_CONST_SECTION
171843
172459
  }
171844
- return { ambiguous: false, inCode: false, index: index + 1 };
171845
- }
171846
- if (state.mode === "block-comment") {
171847
- if (character === "*" && source[index + 1] === "/") {
171848
- state.mode = "code";
171849
- return { ambiguous: false, inCode: false, index: index + 2 };
172460
+ },
172461
+ {
172462
+ append: {
172463
+ marker: ABILITY_CONFIG_ENTRY_MARKER,
172464
+ optionKey: "abilityEntries"
172465
+ },
172466
+ interface: {
172467
+ name: "WorkspaceAbilityConfig",
172468
+ section: ABILITIES_INTERFACE_SECTION
172469
+ },
172470
+ parse: {
172471
+ entriesKey: "abilities",
172472
+ entry: defineInventoryEntryParser()({
172473
+ entryName: "ABILITIES",
172474
+ fields: [
172475
+ { key: "clientFile", required: true },
172476
+ { key: "configFile", required: true },
172477
+ { key: "dataFile", required: true },
172478
+ { key: "inputSchemaFile", required: true },
172479
+ { key: "inputTypeName", required: true },
172480
+ { key: "outputSchemaFile", required: true },
172481
+ { key: "outputTypeName", required: true },
172482
+ { key: "phpFile", required: true },
172483
+ { key: "slug", required: true },
172484
+ { key: "typesFile", required: true }
172485
+ ]
172486
+ }),
172487
+ hasSectionKey: "hasAbilitiesSection"
172488
+ },
172489
+ value: {
172490
+ name: "ABILITIES",
172491
+ section: ABILITIES_CONST_SECTION
172492
+ }
172493
+ },
172494
+ {
172495
+ append: {
172496
+ marker: AI_FEATURE_CONFIG_ENTRY_MARKER,
172497
+ optionKey: "aiFeatureEntries"
172498
+ },
172499
+ interface: {
172500
+ name: "WorkspaceAiFeatureConfig",
172501
+ section: AI_FEATURES_INTERFACE_SECTION
172502
+ },
172503
+ parse: {
172504
+ entriesKey: "aiFeatures",
172505
+ entry: defineInventoryEntryParser()({
172506
+ entryName: "AI_FEATURES",
172507
+ fields: [
172508
+ { key: "aiSchemaFile", required: true },
172509
+ { key: "apiFile", required: true },
172510
+ { key: "clientFile", required: true },
172511
+ { key: "dataFile", required: true },
172512
+ { key: "namespace", required: true },
172513
+ { key: "openApiFile", required: true },
172514
+ { key: "phpFile", required: true },
172515
+ { key: "slug", required: true },
172516
+ { key: "typesFile", required: true },
172517
+ { key: "validatorsFile", required: true }
172518
+ ]
172519
+ }),
172520
+ hasSectionKey: "hasAiFeaturesSection"
172521
+ },
172522
+ value: {
172523
+ name: "AI_FEATURES",
172524
+ section: AI_FEATURES_CONST_SECTION
172525
+ }
172526
+ },
172527
+ {
172528
+ append: {
172529
+ marker: ADMIN_VIEW_CONFIG_ENTRY_MARKER,
172530
+ optionKey: "adminViewEntries"
172531
+ },
172532
+ interface: {
172533
+ name: "WorkspaceAdminViewConfig",
172534
+ section: ADMIN_VIEWS_INTERFACE_SECTION
172535
+ },
172536
+ parse: {
172537
+ entriesKey: "adminViews",
172538
+ entry: defineInventoryEntryParser()({
172539
+ entryName: "ADMIN_VIEWS",
172540
+ fields: [
172541
+ { key: "file", required: true },
172542
+ { key: "phpFile", required: true },
172543
+ { key: "slug", required: true },
172544
+ { key: "source" }
172545
+ ]
172546
+ }),
172547
+ hasSectionKey: "hasAdminViewsSection"
172548
+ },
172549
+ value: {
172550
+ name: "ADMIN_VIEWS",
172551
+ section: ADMIN_VIEWS_CONST_SECTION
172552
+ }
172553
+ },
172554
+ {
172555
+ append: {
172556
+ marker: EDITOR_PLUGIN_CONFIG_ENTRY_MARKER,
172557
+ optionKey: "editorPluginEntries"
172558
+ },
172559
+ interface: {
172560
+ name: "WorkspaceEditorPluginConfig",
172561
+ section: EDITOR_PLUGINS_INTERFACE_SECTION
172562
+ },
172563
+ parse: {
172564
+ entriesKey: "editorPlugins",
172565
+ entry: defineInventoryEntryParser()({
172566
+ entryName: "EDITOR_PLUGINS",
172567
+ fields: [
172568
+ { key: "file", required: true },
172569
+ { key: "slug", required: true },
172570
+ { key: "slot", required: true }
172571
+ ]
172572
+ }),
172573
+ hasSectionKey: "hasEditorPluginsSection"
172574
+ },
172575
+ value: {
172576
+ name: "EDITOR_PLUGINS",
172577
+ section: EDITOR_PLUGINS_CONST_SECTION
171850
172578
  }
171851
- return { ambiguous: false, inCode: false, index: index + 1 };
171852
- }
171853
- if (character === "'") {
171854
- state.mode = "single-quoted";
171855
- return { ambiguous: false, inCode: false, index: index + 1 };
171856
- }
171857
- if (character === '"') {
171858
- state.mode = "double-quoted";
171859
- return { ambiguous: false, inCode: false, index: index + 1 };
171860
- }
171861
- if (character === "/" && source[index + 1] === "/") {
171862
- state.mode = "line-comment";
171863
- return { ambiguous: false, inCode: false, index: index + 2 };
171864
- }
171865
- if (character === "#" && source[index + 1] !== "[") {
171866
- state.mode = "line-comment";
171867
- return { ambiguous: false, inCode: false, index: index + 1 };
171868
- }
171869
- if (character === "/" && source[index + 1] === "*") {
171870
- state.mode = "block-comment";
171871
- return { ambiguous: false, inCode: false, index: index + 2 };
171872
172579
  }
171873
- if (character === "<") {
171874
- const heredocStart = parsePhpHeredocStart(source, index);
171875
- if (heredocStart) {
171876
- state.mode = "heredoc";
171877
- state.heredocDelimiter = heredocStart.delimiter;
172580
+ ];
172581
+ function findExportedArrayLiteral(sourceFile, exportName) {
172582
+ for (const statement of sourceFile.statements) {
172583
+ if (!import_typescript2.default.isVariableStatement(statement)) {
172584
+ continue;
172585
+ }
172586
+ if (!statement.modifiers?.some((modifier) => modifier.kind === import_typescript2.default.SyntaxKind.ExportKeyword)) {
172587
+ continue;
172588
+ }
172589
+ for (const declaration of statement.declarationList.declarations) {
172590
+ if (!import_typescript2.default.isIdentifier(declaration.name) || declaration.name.text !== exportName) {
172591
+ continue;
172592
+ }
172593
+ if (declaration.initializer && import_typescript2.default.isArrayLiteralExpression(declaration.initializer)) {
172594
+ return {
172595
+ array: declaration.initializer,
172596
+ found: true
172597
+ };
172598
+ }
171878
172599
  return {
171879
- ambiguous: false,
171880
- inCode: false,
171881
- index: heredocStart.contentStart
172600
+ array: null,
172601
+ found: true
171882
172602
  };
171883
172603
  }
171884
172604
  }
171885
- return { ambiguous: false, inCode: true, index };
172605
+ return {
172606
+ array: null,
172607
+ found: false
172608
+ };
171886
172609
  }
171887
- function hasPhpFunctionCall(source, functionName) {
171888
- const scanner = createPhpScannerState();
171889
- let index = 0;
171890
- while (index < source.length) {
171891
- const scan = advancePhpScanner(source, index, scanner);
171892
- if (scan.ambiguous) {
171893
- return false;
172610
+ function getOptionalStringProperty(entryName, elementIndex, objectLiteral, key) {
172611
+ for (const property of objectLiteral.properties) {
172612
+ if (!import_typescript2.default.isPropertyAssignment(property)) {
172613
+ continue;
171894
172614
  }
171895
- if (!scan.inCode) {
171896
- index = scan.index;
172615
+ const propertyName = getPropertyNameText(property.name);
172616
+ if (propertyName !== key) {
171897
172617
  continue;
171898
172618
  }
171899
- if (matchesPhpFunctionCallAt(source, index, functionName)) {
171900
- return true;
172619
+ if (import_typescript2.default.isStringLiteralLike(property.initializer)) {
172620
+ return property.initializer.text;
171901
172621
  }
171902
- index += 1;
172622
+ throw new Error(`${entryName}[${elementIndex}] must use a string literal for "${key}" in scripts/block-config.ts.`);
171903
172623
  }
171904
- return false;
172624
+ return;
171905
172625
  }
171906
- function findPhpFunctionRange(source, functionName, options = {}) {
171907
- const signaturePattern = new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\([^)]*\\)\\s*(?::\\s*[^{};]+)?\\s*\\{`, "u");
171908
- const signatureMatch = signaturePattern.exec(source);
171909
- if (!signatureMatch) {
171910
- return null;
171911
- }
171912
- const functionStart = signatureMatch.index;
171913
- const openBraceOffset = signatureMatch[0].lastIndexOf("{");
171914
- if (openBraceOffset === -1) {
171915
- return null;
171916
- }
171917
- const openBraceIndex = functionStart + openBraceOffset;
171918
- let depth = 0;
171919
- const scanner = createPhpScannerState();
171920
- let index = openBraceIndex;
171921
- while (index < source.length) {
171922
- const scan = advancePhpScanner(source, index, scanner);
171923
- if (scan.ambiguous) {
171924
- return null;
171925
- }
171926
- if (!scan.inCode) {
171927
- index = scan.index;
172626
+ function getOptionalStringArrayProperty(entryName, elementIndex, objectLiteral, key) {
172627
+ for (const property of objectLiteral.properties) {
172628
+ if (!import_typescript2.default.isPropertyAssignment(property)) {
171928
172629
  continue;
171929
172630
  }
171930
- const character = source[index];
171931
- if (character === "{") {
171932
- depth += 1;
171933
- index += 1;
172631
+ const propertyName = getPropertyNameText(property.name);
172632
+ if (propertyName !== key) {
171934
172633
  continue;
171935
172634
  }
171936
- if (character !== "}") {
171937
- index += 1;
171938
- continue;
172635
+ if (!import_typescript2.default.isArrayLiteralExpression(property.initializer)) {
172636
+ throw new Error(`${entryName}[${elementIndex}] must use an array literal for "${key}" in scripts/block-config.ts.`);
171939
172637
  }
171940
- depth -= 1;
171941
- if (depth === 0) {
171942
- let end = index + 1;
171943
- if (options.includeTrailingNewlines ?? true) {
171944
- while (end < source.length && /[\r\n]/u.test(source[end] ?? "")) {
171945
- end += 1;
171946
- }
172638
+ return property.initializer.elements.map((element, itemIndex) => {
172639
+ if (!import_typescript2.default.isStringLiteralLike(element)) {
172640
+ throw new Error(`${entryName}[${elementIndex}].${key}[${itemIndex}] must use a string literal in scripts/block-config.ts.`);
171947
172641
  }
171948
- return {
171949
- end,
171950
- source: source.slice(functionStart, end),
171951
- start: functionStart
171952
- };
171953
- }
171954
- index += 1;
172642
+ return element.text;
172643
+ });
171955
172644
  }
171956
- return null;
172645
+ return;
171957
172646
  }
171958
- function replacePhpFunctionDefinition(source, functionName, replacement, options = {}) {
171959
- const functionRange = findPhpFunctionRange(source, functionName, options);
171960
- if (!functionRange) {
171961
- return null;
172647
+ function getOptionalBooleanProperty(entryName, elementIndex, objectLiteral, key) {
172648
+ for (const property of objectLiteral.properties) {
172649
+ if (!import_typescript2.default.isPropertyAssignment(property)) {
172650
+ continue;
172651
+ }
172652
+ const propertyName = getPropertyNameText(property.name);
172653
+ if (propertyName !== key) {
172654
+ continue;
172655
+ }
172656
+ if (property.initializer.kind === import_typescript2.default.SyntaxKind.TrueKeyword) {
172657
+ return true;
172658
+ }
172659
+ if (property.initializer.kind === import_typescript2.default.SyntaxKind.FalseKeyword) {
172660
+ return false;
172661
+ }
172662
+ throw new Error(`${entryName}[${elementIndex}] must use a boolean literal for "${key}" in scripts/block-config.ts.`);
171962
172663
  }
171963
- return [
171964
- source.slice(0, functionRange.start),
171965
- options.trimReplacementStart ? replacement.trimStart() : replacement,
171966
- source.slice(functionRange.end)
171967
- ].join("");
172664
+ return;
171968
172665
  }
171969
-
171970
- // ../wp-typia-project-tools/src/runtime/ts-property-names.ts
171971
- var import_typescript = __toESM(require_typescript(), 1);
171972
- function getPropertyNameText(name) {
171973
- if (import_typescript.default.isIdentifier(name) || import_typescript.default.isStringLiteral(name) || import_typescript.default.isNumericLiteral(name)) {
171974
- return name.text;
172666
+ function isMissingRequiredInventoryValue(value) {
172667
+ return value === undefined || typeof value === "string" && value.length === 0;
172668
+ }
172669
+ function formatMissingRequiredInventoryFields(keys) {
172670
+ return keys.length === 1 ? `required "${keys[0]}"` : `required fields ${keys.map((key) => `"${key}"`).join(", ")}`;
172671
+ }
172672
+ function assertParsedInventoryEntry(entry, descriptor, elementIndex) {
172673
+ const missingRequiredKeys = descriptor.fields.filter((field) => field.required === true && isMissingRequiredInventoryValue(entry[field.key])).map((field) => field.key);
172674
+ if (missingRequiredKeys.length > 0) {
172675
+ throw new Error(`${descriptor.entryName}[${elementIndex}] is missing ${formatMissingRequiredInventoryFields(missingRequiredKeys)} in scripts/block-config.ts.`);
171975
172676
  }
171976
- return null;
171977
172677
  }
171978
-
171979
- // ../wp-typia-project-tools/src/runtime/workspace-inventory.ts
171980
- function defineInventoryEntryParser() {
171981
- return (descriptor) => descriptor;
172678
+ function parseInventoryEntries(arrayLiteral, descriptor) {
172679
+ return arrayLiteral.elements.map((element, elementIndex) => {
172680
+ if (!import_typescript2.default.isObjectLiteralExpression(element)) {
172681
+ throw new Error(`${descriptor.entryName}[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
172682
+ }
172683
+ const entry = {};
172684
+ for (const field of descriptor.fields) {
172685
+ const kind = field.kind ?? "string";
172686
+ const value = kind === "stringArray" ? getOptionalStringArrayProperty(descriptor.entryName, elementIndex, element, field.key) : kind === "boolean" ? getOptionalBooleanProperty(descriptor.entryName, elementIndex, element, field.key) : getOptionalStringProperty(descriptor.entryName, elementIndex, element, field.key);
172687
+ field.validate?.(value, {
172688
+ elementIndex,
172689
+ entryName: descriptor.entryName,
172690
+ key: field.key
172691
+ });
172692
+ entry[field.key] = value;
172693
+ }
172694
+ assertParsedInventoryEntry(entry, descriptor, elementIndex);
172695
+ return entry;
172696
+ });
171982
172697
  }
171983
- var BLOCK_CONFIG_ENTRY_MARKER = "\t// wp-typia add block entries";
171984
- var VARIATION_CONFIG_ENTRY_MARKER = "\t// wp-typia add variation entries";
171985
- var BLOCK_STYLE_CONFIG_ENTRY_MARKER = "\t// wp-typia add style entries";
171986
- var BLOCK_TRANSFORM_CONFIG_ENTRY_MARKER = "\t// wp-typia add transform entries";
171987
- var PATTERN_CONFIG_ENTRY_MARKER = "\t// wp-typia add pattern entries";
171988
- var BINDING_SOURCE_CONFIG_ENTRY_MARKER = "\t// wp-typia add binding-source entries";
171989
- var REST_RESOURCE_CONFIG_ENTRY_MARKER = "\t// wp-typia add rest-resource entries";
171990
- var ABILITY_CONFIG_ENTRY_MARKER = "\t// wp-typia add ability entries";
171991
- var AI_FEATURE_CONFIG_ENTRY_MARKER = "\t// wp-typia add ai-feature entries";
171992
- var ADMIN_VIEW_CONFIG_ENTRY_MARKER = "\t// wp-typia add admin-view entries";
171993
- var EDITOR_PLUGIN_CONFIG_ENTRY_MARKER = "\t// wp-typia add editor-plugin entries";
171994
- var VARIATIONS_INTERFACE_SECTION = `
171995
-
171996
- export interface WorkspaceVariationConfig {
171997
- block: string;
171998
- file: string;
171999
- slug: string;
172698
+ function parseInventorySection(sourceFile, descriptor) {
172699
+ if (!descriptor.parse) {
172700
+ return {
172701
+ entries: [],
172702
+ found: false
172703
+ };
172704
+ }
172705
+ const exportName = descriptor.parse.exportName ?? descriptor.value?.name;
172706
+ if (!exportName) {
172707
+ throw new Error("Inventory parser descriptor is missing an export name.");
172708
+ }
172709
+ const exportedArray = findExportedArrayLiteral(sourceFile, exportName);
172710
+ if (!exportedArray.found) {
172711
+ if (descriptor.parse.required) {
172712
+ throw new Error(`scripts/block-config.ts must export a ${exportName} array.`);
172713
+ }
172714
+ return {
172715
+ entries: [],
172716
+ found: false
172717
+ };
172718
+ }
172719
+ if (!exportedArray.array) {
172720
+ if (descriptor.parse.required) {
172721
+ throw new Error(`scripts/block-config.ts must export a ${exportName} array.`);
172722
+ }
172723
+ throw new Error(`scripts/block-config.ts must export ${exportName} as an array literal.`);
172724
+ }
172725
+ return {
172726
+ entries: parseInventoryEntries(exportedArray.array, descriptor.parse.entry),
172727
+ found: true
172728
+ };
172000
172729
  }
172001
- `;
172002
- var VARIATIONS_CONST_SECTION = `
172003
-
172004
- export const VARIATIONS: WorkspaceVariationConfig[] = [
172005
- // wp-typia add variation entries
172006
- ];
172007
- `;
172008
- var BLOCK_STYLES_INTERFACE_SECTION = `
172009
-
172010
- export interface WorkspaceBlockStyleConfig {
172011
- block: string;
172012
- file: string;
172013
- slug: string;
172730
+ function parseWorkspaceInventorySource(source) {
172731
+ const sourceFile = import_typescript2.default.createSourceFile("block-config.ts", source, import_typescript2.default.ScriptTarget.Latest, true, import_typescript2.default.ScriptKind.TS);
172732
+ const parsedInventory = {
172733
+ abilities: [],
172734
+ adminViews: [],
172735
+ aiFeatures: [],
172736
+ bindingSources: [],
172737
+ blockStyles: [],
172738
+ blockTransforms: [],
172739
+ blocks: parseInventorySection(sourceFile, BLOCK_INVENTORY_SECTION).entries,
172740
+ contracts: [],
172741
+ editorPlugins: [],
172742
+ hasAbilitiesSection: false,
172743
+ hasAdminViewsSection: false,
172744
+ hasAiFeaturesSection: false,
172745
+ hasBindingSourcesSection: false,
172746
+ hasBlockStylesSection: false,
172747
+ hasBlockTransformsSection: false,
172748
+ hasContractsSection: false,
172749
+ hasEditorPluginsSection: false,
172750
+ hasPatternsSection: false,
172751
+ hasPostMetaSection: false,
172752
+ hasRestResourcesSection: false,
172753
+ hasVariationsSection: false,
172754
+ patterns: [],
172755
+ postMeta: [],
172756
+ restResources: [],
172757
+ source,
172758
+ variations: []
172759
+ };
172760
+ const mutableInventory = parsedInventory;
172761
+ for (const section of INVENTORY_SECTIONS) {
172762
+ if (!section.parse) {
172763
+ continue;
172764
+ }
172765
+ const parsedSection = parseInventorySection(sourceFile, section);
172766
+ mutableInventory[section.parse.entriesKey] = parsedSection.entries;
172767
+ if (section.parse.hasSectionKey) {
172768
+ mutableInventory[section.parse.hasSectionKey] = parsedSection.found;
172769
+ }
172770
+ }
172771
+ return parsedInventory;
172014
172772
  }
172015
- `;
172016
- var BLOCK_STYLES_CONST_SECTION = `
172017
-
172018
- export const BLOCK_STYLES: WorkspaceBlockStyleConfig[] = [
172019
- // wp-typia add style entries
172020
- ];
172021
- `;
172022
- var BLOCK_TRANSFORMS_INTERFACE_SECTION = `
172023
172773
 
172024
- export interface WorkspaceBlockTransformConfig {
172025
- block: string;
172026
- file: string;
172027
- from: string;
172028
- slug: string;
172029
- to: string;
172774
+ // ../wp-typia-project-tools/src/runtime/workspace-inventory-read.ts
172775
+ function readWorkspaceInventory(projectDir) {
172776
+ const blockConfigPath = path4.join(projectDir, "scripts", "block-config.ts");
172777
+ let source;
172778
+ try {
172779
+ source = readFileSync(blockConfigPath, "utf8");
172780
+ } catch (error) {
172781
+ if (isFileNotFoundError(error)) {
172782
+ throw new Error(`Workspace inventory file is missing at ${blockConfigPath}. Expected scripts/block-config.ts to exist.`);
172783
+ }
172784
+ throw error;
172785
+ }
172786
+ return {
172787
+ blockConfigPath,
172788
+ ...parseWorkspaceInventorySource(source)
172789
+ };
172030
172790
  }
172031
- `;
172032
- var BLOCK_TRANSFORMS_CONST_SECTION = `
172033
-
172034
- export const BLOCK_TRANSFORMS: WorkspaceBlockTransformConfig[] = [
172035
- // wp-typia add transform entries
172036
- ];
172037
- `;
172038
- var PATTERNS_INTERFACE_SECTION = `
172039
-
172040
- export interface WorkspacePatternConfig {
172041
- file: string;
172042
- slug: string;
172791
+ async function readWorkspaceInventoryAsync(projectDir) {
172792
+ const blockConfigPath = path4.join(projectDir, "scripts", "block-config.ts");
172793
+ let source;
172794
+ try {
172795
+ source = await readFile(blockConfigPath, "utf8");
172796
+ } catch (error) {
172797
+ if (isFileNotFoundError(error)) {
172798
+ throw new Error(`Workspace inventory file is missing at ${blockConfigPath}. Expected scripts/block-config.ts to exist.`);
172799
+ }
172800
+ throw error;
172801
+ }
172802
+ return {
172803
+ blockConfigPath,
172804
+ ...parseWorkspaceInventorySource(source)
172805
+ };
172043
172806
  }
172044
- `;
172045
- var PATTERNS_CONST_SECTION = `
172046
-
172047
- export const PATTERNS: WorkspacePatternConfig[] = [
172048
- // wp-typia add pattern entries
172049
- ];
172050
- `;
172051
- var BINDING_SOURCES_INTERFACE_SECTION = `
172052
-
172053
- export interface WorkspaceBindingSourceConfig {
172054
- attribute?: string;
172055
- block?: string;
172056
- editorFile: string;
172057
- serverFile: string;
172058
- slug: string;
172807
+ function toWorkspaceBlockSelectOptions(blocks) {
172808
+ return blocks.map((block) => ({
172809
+ description: block.typesFile,
172810
+ name: block.slug,
172811
+ value: block.slug
172812
+ }));
172059
172813
  }
172060
- `;
172061
- var BINDING_SOURCES_CONST_SECTION = `
172062
-
172063
- export const BINDING_SOURCES: WorkspaceBindingSourceConfig[] = [
172064
- // wp-typia add binding-source entries
172065
- ];
172066
- `;
172067
- var REST_RESOURCES_INTERFACE_SECTION = `
172814
+ function getWorkspaceBlockSelectOptions(projectDir) {
172815
+ return toWorkspaceBlockSelectOptions(readWorkspaceInventory(projectDir).blocks);
172816
+ }
172817
+ async function getWorkspaceBlockSelectOptionsAsync(projectDir) {
172818
+ return toWorkspaceBlockSelectOptions((await readWorkspaceInventoryAsync(projectDir)).blocks);
172819
+ }
172820
+ // ../wp-typia-project-tools/src/runtime/workspace-inventory-mutations.ts
172821
+ import path5 from "path";
172822
+ import { readFile as readFile2, writeFile } from "fs/promises";
172068
172823
 
172069
- export interface WorkspaceRestResourceConfig {
172070
- apiFile: string;
172071
- clientFile: string;
172072
- dataFile: string;
172073
- methods: Array< 'list' | 'read' | 'create' | 'update' | 'delete' >;
172074
- namespace: string;
172075
- openApiFile: string;
172076
- phpFile: string;
172077
- restManifest?: ReturnType<
172078
- typeof import( '@wp-typia/block-runtime/metadata-core' ).defineEndpointManifest
172079
- >;
172080
- slug: string;
172081
- typesFile: string;
172082
- validatorsFile: string;
172824
+ // ../wp-typia-project-tools/src/runtime/php-utils.ts
172825
+ function escapeRegex(value) {
172826
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
172083
172827
  }
172084
- `;
172085
- var REST_RESOURCES_CONST_SECTION = `
172086
-
172087
- export const REST_RESOURCES: WorkspaceRestResourceConfig[] = [
172088
- // wp-typia add rest-resource entries
172089
- ];
172090
- `;
172091
- var WORKSPACE_COMPATIBILITY_CONFIG_FIELD = ` compatibility?: {
172092
- hardMinimums: {
172093
- php?: string;
172094
- wordpress?: string;
172095
- };
172096
- mode: 'baseline' | 'optional' | 'required';
172097
- optionalFeatureIds: string[];
172098
- optionalFeatures: string[];
172099
- requiredFeatureIds: string[];
172100
- requiredFeatures: string[];
172101
- runtimeGates: string[];
172102
- };
172103
- `;
172104
- var ABILITIES_INTERFACE_SECTION = `
172105
-
172106
- export interface WorkspaceAbilityConfig {
172107
- clientFile: string;
172108
- ${WORKSPACE_COMPATIBILITY_CONFIG_FIELD} configFile: string;
172109
- dataFile: string;
172110
- inputSchemaFile: string;
172111
- inputTypeName: string;
172112
- outputSchemaFile: string;
172113
- outputTypeName: string;
172114
- phpFile: string;
172115
- slug: string;
172116
- typesFile: string;
172828
+ function quotePhpString(value) {
172829
+ return `'${value.replace(/\\/gu, "\\\\").replace(/'/gu, "\\'")}'`;
172117
172830
  }
172118
- `;
172119
- var ABILITIES_CONST_SECTION = `
172120
-
172121
- export const ABILITIES: WorkspaceAbilityConfig[] = [
172122
- // wp-typia add ability entries
172123
- ];
172124
- `;
172125
- var AI_FEATURES_INTERFACE_SECTION = `
172126
-
172127
- export interface WorkspaceAiFeatureConfig {
172128
- aiSchemaFile: string;
172129
- apiFile: string;
172130
- clientFile: string;
172131
- ${WORKSPACE_COMPATIBILITY_CONFIG_FIELD} dataFile: string;
172132
- namespace: string;
172133
- openApiFile: string;
172134
- phpFile: string;
172135
- restManifest?: ReturnType<
172136
- typeof import( '@wp-typia/block-runtime/metadata-core' ).defineEndpointManifest
172137
- >;
172138
- slug: string;
172139
- typesFile: string;
172140
- validatorsFile: string;
172831
+ function hasPhpFunctionDefinition(source, functionName) {
172832
+ return new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\(`, "u").test(source);
172141
172833
  }
172142
- `;
172143
- var AI_FEATURES_CONST_SECTION = `
172144
-
172145
- export const AI_FEATURES: WorkspaceAiFeatureConfig[] = [
172146
- // wp-typia add ai-feature entries
172147
- ];
172148
- `;
172149
- var ADMIN_VIEWS_INTERFACE_SECTION = `
172150
-
172151
- export interface WorkspaceAdminViewConfig {
172152
- file: string;
172153
- phpFile: string;
172154
- slug: string;
172155
- source?: string;
172834
+ function isPhpIdentifierStart(character) {
172835
+ return /^[A-Za-z_]$/u.test(character ?? "");
172156
172836
  }
172157
- `;
172158
- var ADMIN_VIEWS_CONST_SECTION = `
172159
-
172160
- export const ADMIN_VIEWS: WorkspaceAdminViewConfig[] = [
172161
- // wp-typia add admin-view entries
172162
- ];
172163
- `;
172164
- var EDITOR_PLUGINS_INTERFACE_SECTION = `
172165
-
172166
- export interface WorkspaceEditorPluginConfig {
172167
- file: string;
172168
- slug: string;
172169
- slot: string;
172837
+ function isPhpIdentifierPart(character) {
172838
+ return /^[A-Za-z0-9_]$/u.test(character ?? "");
172170
172839
  }
172840
+ function isPhpLineStart(source, index) {
172841
+ return index === 0 || source[index - 1] === `
172171
172842
  `;
172172
- var EDITOR_PLUGINS_CONST_SECTION = `
172173
-
172174
- export const EDITOR_PLUGINS: WorkspaceEditorPluginConfig[] = [
172175
- // wp-typia add editor-plugin entries
172176
- ];
172177
- `;
172178
- var BLOCK_INVENTORY_SECTION = {
172179
- append: {
172180
- marker: BLOCK_CONFIG_ENTRY_MARKER,
172181
- optionKey: "blockEntries"
172182
- },
172183
- parse: {
172184
- entriesKey: "blocks",
172185
- entry: defineInventoryEntryParser()({
172186
- entryName: "BLOCKS",
172187
- fields: [
172188
- { key: "apiTypesFile" },
172189
- { key: "attributeTypeName" },
172190
- { key: "openApiFile" },
172191
- { key: "slug", required: true },
172192
- { key: "typesFile", required: true }
172193
- ]
172194
- }),
172195
- exportName: "BLOCKS",
172196
- required: true
172843
+ }
172844
+ function isPhpHorizontalWhitespace(character) {
172845
+ return character === " " || character === "\t";
172846
+ }
172847
+ function isPhpWhitespace(character) {
172848
+ return typeof character === "string" && /\s/u.test(character);
172849
+ }
172850
+ function findPhpLineBoundary(source, index) {
172851
+ const newlineIndex = source.indexOf(`
172852
+ `, index);
172853
+ if (newlineIndex === -1) {
172854
+ return {
172855
+ contentEnd: source.endsWith("\r") ? source.length - 1 : source.length,
172856
+ nextStart: source.length
172857
+ };
172197
172858
  }
172198
- };
172199
- var INVENTORY_SECTIONS = [
172200
- {
172201
- append: {
172202
- marker: VARIATION_CONFIG_ENTRY_MARKER,
172203
- optionKey: "variationEntries"
172204
- },
172205
- interface: {
172206
- name: "WorkspaceVariationConfig",
172207
- section: VARIATIONS_INTERFACE_SECTION
172208
- },
172209
- parse: {
172210
- entriesKey: "variations",
172211
- entry: defineInventoryEntryParser()({
172212
- entryName: "VARIATIONS",
172213
- fields: [
172214
- { key: "block", required: true },
172215
- { key: "file", required: true },
172216
- { key: "slug", required: true }
172217
- ]
172218
- }),
172219
- hasSectionKey: "hasVariationsSection"
172220
- },
172221
- value: {
172222
- name: "VARIATIONS",
172223
- section: VARIATIONS_CONST_SECTION
172859
+ return {
172860
+ contentEnd: source[newlineIndex - 1] === "\r" ? newlineIndex - 1 : newlineIndex,
172861
+ nextStart: newlineIndex + 1
172862
+ };
172863
+ }
172864
+ function parsePhpHeredocStart(source, index) {
172865
+ if (!source.startsWith("<<<", index)) {
172866
+ return null;
172867
+ }
172868
+ let cursor = index + 3;
172869
+ while (isPhpHorizontalWhitespace(source[cursor])) {
172870
+ cursor += 1;
172871
+ }
172872
+ const quote = source[cursor] === "'" || source[cursor] === '"' ? source[cursor] : "";
172873
+ if (quote) {
172874
+ cursor += 1;
172875
+ }
172876
+ if (!isPhpIdentifierStart(source[cursor])) {
172877
+ return null;
172878
+ }
172879
+ const delimiterStart = cursor;
172880
+ cursor += 1;
172881
+ while (isPhpIdentifierPart(source[cursor])) {
172882
+ cursor += 1;
172883
+ }
172884
+ const delimiter = source.slice(delimiterStart, cursor);
172885
+ if (quote) {
172886
+ if (source[cursor] !== quote) {
172887
+ return null;
172224
172888
  }
172225
- },
172226
- {
172227
- append: {
172228
- marker: BLOCK_STYLE_CONFIG_ENTRY_MARKER,
172229
- optionKey: "blockStyleEntries"
172230
- },
172231
- interface: {
172232
- name: "WorkspaceBlockStyleConfig",
172233
- section: BLOCK_STYLES_INTERFACE_SECTION
172234
- },
172235
- parse: {
172236
- entriesKey: "blockStyles",
172237
- entry: defineInventoryEntryParser()({
172238
- entryName: "BLOCK_STYLES",
172239
- fields: [
172240
- { key: "block", required: true },
172241
- { key: "file", required: true },
172242
- { key: "slug", required: true }
172243
- ]
172244
- }),
172245
- hasSectionKey: "hasBlockStylesSection"
172246
- },
172247
- value: {
172248
- name: "BLOCK_STYLES",
172249
- section: BLOCK_STYLES_CONST_SECTION
172889
+ cursor += 1;
172890
+ }
172891
+ const lineBoundary = findPhpLineBoundary(source, cursor);
172892
+ if (source.slice(cursor, lineBoundary.contentEnd).trim() !== "") {
172893
+ return null;
172894
+ }
172895
+ return {
172896
+ contentStart: lineBoundary.nextStart,
172897
+ delimiter
172898
+ };
172899
+ }
172900
+ function findPhpHeredocClosingEnd(source, index, delimiter) {
172901
+ if (!isPhpLineStart(source, index)) {
172902
+ return null;
172903
+ }
172904
+ let cursor = index;
172905
+ while (isPhpHorizontalWhitespace(source[cursor])) {
172906
+ cursor += 1;
172907
+ }
172908
+ if (!source.startsWith(delimiter, cursor)) {
172909
+ return null;
172910
+ }
172911
+ cursor += delimiter.length;
172912
+ if (isPhpIdentifierPart(source[cursor])) {
172913
+ return null;
172914
+ }
172915
+ let continuationCursor = cursor;
172916
+ while (isPhpHorizontalWhitespace(source[continuationCursor])) {
172917
+ continuationCursor += 1;
172918
+ }
172919
+ const continuation = source[continuationCursor];
172920
+ if (continuationCursor >= source.length || continuation === "\r" || continuation === `
172921
+ ` || !isPhpIdentifierPart(continuation)) {
172922
+ return cursor;
172923
+ }
172924
+ return null;
172925
+ }
172926
+ function skipPhpCallTrivia(source, index) {
172927
+ let cursor = index;
172928
+ while (cursor < source.length) {
172929
+ while (isPhpWhitespace(source[cursor])) {
172930
+ cursor += 1;
172250
172931
  }
172251
- },
172252
- {
172253
- append: {
172254
- marker: BLOCK_TRANSFORM_CONFIG_ENTRY_MARKER,
172255
- optionKey: "blockTransformEntries"
172256
- },
172257
- interface: {
172258
- name: "WorkspaceBlockTransformConfig",
172259
- section: BLOCK_TRANSFORMS_INTERFACE_SECTION
172260
- },
172261
- parse: {
172262
- entriesKey: "blockTransforms",
172263
- entry: defineInventoryEntryParser()({
172264
- entryName: "BLOCK_TRANSFORMS",
172265
- fields: [
172266
- { key: "block", required: true },
172267
- { key: "file", required: true },
172268
- { key: "from", required: true },
172269
- { key: "slug", required: true },
172270
- { key: "to", required: true }
172271
- ]
172272
- }),
172273
- hasSectionKey: "hasBlockTransformsSection"
172274
- },
172275
- value: {
172276
- name: "BLOCK_TRANSFORMS",
172277
- section: BLOCK_TRANSFORMS_CONST_SECTION
172932
+ if (source[cursor] === "/" && source[cursor + 1] === "*") {
172933
+ const commentEnd = source.indexOf("*/", cursor + 2);
172934
+ if (commentEnd === -1) {
172935
+ return null;
172936
+ }
172937
+ cursor = commentEnd + 2;
172938
+ continue;
172278
172939
  }
172279
- },
172280
- {
172281
- append: {
172282
- marker: PATTERN_CONFIG_ENTRY_MARKER,
172283
- optionKey: "patternEntries"
172284
- },
172285
- interface: {
172286
- name: "WorkspacePatternConfig",
172287
- section: PATTERNS_INTERFACE_SECTION
172288
- },
172289
- parse: {
172290
- entriesKey: "patterns",
172291
- entry: defineInventoryEntryParser()({
172292
- entryName: "PATTERNS",
172293
- fields: [
172294
- { key: "file", required: true },
172295
- { key: "slug", required: true }
172296
- ]
172297
- }),
172298
- hasSectionKey: "hasPatternsSection"
172299
- },
172300
- value: {
172301
- name: "PATTERNS",
172302
- section: PATTERNS_CONST_SECTION
172940
+ if (source[cursor] === "/" && source[cursor + 1] === "/") {
172941
+ cursor = findPhpLineBoundary(source, cursor + 2).nextStart;
172942
+ continue;
172303
172943
  }
172304
- },
172305
- {
172306
- append: {
172307
- marker: BINDING_SOURCE_CONFIG_ENTRY_MARKER,
172308
- optionKey: "bindingSourceEntries"
172309
- },
172310
- interface: {
172311
- name: "WorkspaceBindingSourceConfig",
172312
- section: BINDING_SOURCES_INTERFACE_SECTION
172313
- },
172314
- parse: {
172315
- entriesKey: "bindingSources",
172316
- entry: defineInventoryEntryParser()({
172317
- entryName: "BINDING_SOURCES",
172318
- fields: [
172319
- { key: "attribute" },
172320
- { key: "block" },
172321
- { key: "editorFile", required: true },
172322
- { key: "serverFile", required: true },
172323
- { key: "slug", required: true }
172324
- ]
172325
- }),
172326
- hasSectionKey: "hasBindingSourcesSection"
172327
- },
172328
- value: {
172329
- name: "BINDING_SOURCES",
172330
- section: BINDING_SOURCES_CONST_SECTION
172944
+ if (source[cursor] === "#" && source[cursor + 1] !== "[") {
172945
+ cursor = findPhpLineBoundary(source, cursor + 1).nextStart;
172946
+ continue;
172331
172947
  }
172332
- },
172333
- {
172334
- append: {
172335
- marker: REST_RESOURCE_CONFIG_ENTRY_MARKER,
172336
- optionKey: "restResourceEntries"
172337
- },
172338
- interface: {
172339
- name: "WorkspaceRestResourceConfig",
172340
- section: REST_RESOURCES_INTERFACE_SECTION
172341
- },
172342
- parse: {
172343
- entriesKey: "restResources",
172344
- entry: defineInventoryEntryParser()({
172345
- entryName: "REST_RESOURCES",
172346
- fields: [
172347
- { key: "apiFile", required: true },
172348
- { key: "clientFile", required: true },
172349
- { key: "dataFile", required: true },
172350
- {
172351
- key: "methods",
172352
- kind: "stringArray",
172353
- required: true,
172354
- validate: (value, context) => {
172355
- const methods = Array.isArray(value) ? value : [];
172356
- const invalidMethods = methods.filter((method) => !REST_RESOURCE_METHOD_IDS.includes(method));
172357
- if (invalidMethods.length > 0) {
172358
- throw new Error(`${context.entryName}[${context.elementIndex}].${context.key} includes unsupported values: ${invalidMethods.join(", ")}.`);
172359
- }
172360
- }
172361
- },
172362
- { key: "namespace", required: true },
172363
- { key: "openApiFile", required: true },
172364
- { key: "phpFile", required: true },
172365
- { key: "slug", required: true },
172366
- { key: "typesFile", required: true },
172367
- { key: "validatorsFile", required: true }
172368
- ]
172369
- }),
172370
- hasSectionKey: "hasRestResourcesSection"
172371
- },
172372
- value: {
172373
- name: "REST_RESOURCES",
172374
- section: REST_RESOURCES_CONST_SECTION
172948
+ return cursor;
172949
+ }
172950
+ return cursor;
172951
+ }
172952
+ function matchesPhpFunctionCallAt(source, index, functionName) {
172953
+ if (!source.startsWith(functionName, index)) {
172954
+ return false;
172955
+ }
172956
+ if (isPhpIdentifierPart(source[index - 1])) {
172957
+ return false;
172958
+ }
172959
+ const cursor = index + functionName.length;
172960
+ if (isPhpIdentifierPart(source[cursor])) {
172961
+ return false;
172962
+ }
172963
+ const callStart = skipPhpCallTrivia(source, cursor);
172964
+ return callStart !== null && source[callStart] === "(";
172965
+ }
172966
+ function createPhpScannerState() {
172967
+ return {
172968
+ heredocDelimiter: "",
172969
+ interpolationComment: "",
172970
+ interpolationDepth: 0,
172971
+ interpolationQuote: "",
172972
+ mode: "code"
172973
+ };
172974
+ }
172975
+ function advancePhpScanner(source, index, state) {
172976
+ const character = source[index];
172977
+ if (state.mode === "heredoc") {
172978
+ const closingEnd = findPhpHeredocClosingEnd(source, index, state.heredocDelimiter);
172979
+ if (closingEnd !== null) {
172980
+ state.mode = "code";
172981
+ state.heredocDelimiter = "";
172982
+ return { ambiguous: false, inCode: false, index: closingEnd };
172375
172983
  }
172376
- },
172377
- {
172378
- append: {
172379
- marker: ABILITY_CONFIG_ENTRY_MARKER,
172380
- optionKey: "abilityEntries"
172381
- },
172382
- interface: {
172383
- name: "WorkspaceAbilityConfig",
172384
- section: ABILITIES_INTERFACE_SECTION
172385
- },
172386
- parse: {
172387
- entriesKey: "abilities",
172388
- entry: defineInventoryEntryParser()({
172389
- entryName: "ABILITIES",
172390
- fields: [
172391
- { key: "clientFile", required: true },
172392
- { key: "configFile", required: true },
172393
- { key: "dataFile", required: true },
172394
- { key: "inputSchemaFile", required: true },
172395
- { key: "inputTypeName", required: true },
172396
- { key: "outputSchemaFile", required: true },
172397
- { key: "outputTypeName", required: true },
172398
- { key: "phpFile", required: true },
172399
- { key: "slug", required: true },
172400
- { key: "typesFile", required: true }
172401
- ]
172402
- }),
172403
- hasSectionKey: "hasAbilitiesSection"
172404
- },
172405
- value: {
172406
- name: "ABILITIES",
172407
- section: ABILITIES_CONST_SECTION
172984
+ const nextLineStart = findPhpLineBoundary(source, index).nextStart;
172985
+ if (nextLineStart <= index) {
172986
+ return { ambiguous: true, inCode: false, index };
172408
172987
  }
172409
- },
172410
- {
172411
- append: {
172412
- marker: AI_FEATURE_CONFIG_ENTRY_MARKER,
172413
- optionKey: "aiFeatureEntries"
172414
- },
172415
- interface: {
172416
- name: "WorkspaceAiFeatureConfig",
172417
- section: AI_FEATURES_INTERFACE_SECTION
172418
- },
172419
- parse: {
172420
- entriesKey: "aiFeatures",
172421
- entry: defineInventoryEntryParser()({
172422
- entryName: "AI_FEATURES",
172423
- fields: [
172424
- { key: "aiSchemaFile", required: true },
172425
- { key: "apiFile", required: true },
172426
- { key: "clientFile", required: true },
172427
- { key: "dataFile", required: true },
172428
- { key: "namespace", required: true },
172429
- { key: "openApiFile", required: true },
172430
- { key: "phpFile", required: true },
172431
- { key: "slug", required: true },
172432
- { key: "typesFile", required: true },
172433
- { key: "validatorsFile", required: true }
172434
- ]
172435
- }),
172436
- hasSectionKey: "hasAiFeaturesSection"
172437
- },
172438
- value: {
172439
- name: "AI_FEATURES",
172440
- section: AI_FEATURES_CONST_SECTION
172988
+ return { ambiguous: false, inCode: false, index: nextLineStart };
172989
+ }
172990
+ if (state.mode === "single-quoted" || state.mode === "double-quoted") {
172991
+ const quote = state.mode === "single-quoted" ? "'" : '"';
172992
+ if (character === "\\") {
172993
+ return { ambiguous: false, inCode: false, index: index + 2 };
172441
172994
  }
172442
- },
172443
- {
172444
- append: {
172445
- marker: ADMIN_VIEW_CONFIG_ENTRY_MARKER,
172446
- optionKey: "adminViewEntries"
172447
- },
172448
- interface: {
172449
- name: "WorkspaceAdminViewConfig",
172450
- section: ADMIN_VIEWS_INTERFACE_SECTION
172451
- },
172452
- parse: {
172453
- entriesKey: "adminViews",
172454
- entry: defineInventoryEntryParser()({
172455
- entryName: "ADMIN_VIEWS",
172456
- fields: [
172457
- { key: "file", required: true },
172458
- { key: "phpFile", required: true },
172459
- { key: "slug", required: true },
172460
- { key: "source" }
172461
- ]
172462
- }),
172463
- hasSectionKey: "hasAdminViewsSection"
172464
- },
172465
- value: {
172466
- name: "ADMIN_VIEWS",
172467
- section: ADMIN_VIEWS_CONST_SECTION
172995
+ if (state.mode === "double-quoted" && character === "{" && source[index + 1] === "$") {
172996
+ state.mode = "double-quoted-interpolation";
172997
+ state.interpolationComment = "";
172998
+ state.interpolationDepth = 1;
172999
+ state.interpolationQuote = "";
173000
+ return { ambiguous: false, inCode: false, index: index + 2 };
172468
173001
  }
172469
- },
172470
- {
172471
- append: {
172472
- marker: EDITOR_PLUGIN_CONFIG_ENTRY_MARKER,
172473
- optionKey: "editorPluginEntries"
172474
- },
172475
- interface: {
172476
- name: "WorkspaceEditorPluginConfig",
172477
- section: EDITOR_PLUGINS_INTERFACE_SECTION
172478
- },
172479
- parse: {
172480
- entriesKey: "editorPlugins",
172481
- entry: defineInventoryEntryParser()({
172482
- entryName: "EDITOR_PLUGINS",
172483
- fields: [
172484
- { key: "file", required: true },
172485
- { key: "slug", required: true },
172486
- { key: "slot", required: true }
172487
- ]
172488
- }),
172489
- hasSectionKey: "hasEditorPluginsSection"
172490
- },
172491
- value: {
172492
- name: "EDITOR_PLUGINS",
172493
- section: EDITOR_PLUGINS_CONST_SECTION
173002
+ if (character === quote) {
173003
+ state.mode = "code";
172494
173004
  }
173005
+ return { ambiguous: false, inCode: false, index: index + 1 };
172495
173006
  }
172496
- ];
172497
- function findExportedArrayLiteral(sourceFile, exportName) {
172498
- for (const statement of sourceFile.statements) {
172499
- if (!import_typescript2.default.isVariableStatement(statement)) {
172500
- continue;
173007
+ if (state.mode === "double-quoted-interpolation") {
173008
+ if (state.interpolationQuote) {
173009
+ if (character === "\\") {
173010
+ return { ambiguous: false, inCode: false, index: index + 2 };
173011
+ }
173012
+ if (character === state.interpolationQuote) {
173013
+ state.interpolationQuote = "";
173014
+ }
173015
+ return { ambiguous: false, inCode: false, index: index + 1 };
172501
173016
  }
172502
- if (!statement.modifiers?.some((modifier) => modifier.kind === import_typescript2.default.SyntaxKind.ExportKeyword)) {
172503
- continue;
173017
+ if (state.interpolationComment === "line") {
173018
+ if (character === "\r" || character === `
173019
+ `) {
173020
+ state.interpolationComment = "";
173021
+ }
173022
+ return { ambiguous: false, inCode: false, index: index + 1 };
172504
173023
  }
172505
- for (const declaration of statement.declarationList.declarations) {
172506
- if (!import_typescript2.default.isIdentifier(declaration.name) || declaration.name.text !== exportName) {
172507
- continue;
173024
+ if (state.interpolationComment === "block") {
173025
+ if (character === "*" && source[index + 1] === "/") {
173026
+ state.interpolationComment = "";
173027
+ return { ambiguous: false, inCode: false, index: index + 2 };
172508
173028
  }
172509
- if (declaration.initializer && import_typescript2.default.isArrayLiteralExpression(declaration.initializer)) {
172510
- return {
172511
- array: declaration.initializer,
172512
- found: true
172513
- };
173029
+ return { ambiguous: false, inCode: false, index: index + 1 };
173030
+ }
173031
+ if (character === "/" && source[index + 1] === "/") {
173032
+ state.interpolationComment = "line";
173033
+ return { ambiguous: false, inCode: false, index: index + 2 };
173034
+ }
173035
+ if (character === "#" && source[index + 1] !== "[") {
173036
+ state.interpolationComment = "line";
173037
+ return { ambiguous: false, inCode: false, index: index + 1 };
173038
+ }
173039
+ if (character === "/" && source[index + 1] === "*") {
173040
+ state.interpolationComment = "block";
173041
+ return { ambiguous: false, inCode: false, index: index + 2 };
173042
+ }
173043
+ if (character === "'" || character === '"') {
173044
+ state.interpolationQuote = character;
173045
+ return { ambiguous: false, inCode: false, index: index + 1 };
173046
+ }
173047
+ if (character === "{") {
173048
+ state.interpolationDepth += 1;
173049
+ return { ambiguous: false, inCode: false, index: index + 1 };
173050
+ }
173051
+ if (character === "}") {
173052
+ state.interpolationDepth -= 1;
173053
+ if (state.interpolationDepth <= 0) {
173054
+ state.interpolationComment = "";
173055
+ state.interpolationDepth = 0;
173056
+ state.mode = "double-quoted";
172514
173057
  }
172515
- return {
172516
- array: null,
172517
- found: true
172518
- };
173058
+ return { ambiguous: false, inCode: false, index: index + 1 };
172519
173059
  }
173060
+ return { ambiguous: false, inCode: false, index: index + 1 };
172520
173061
  }
172521
- return {
172522
- array: null,
172523
- found: false
172524
- };
172525
- }
172526
- function getOptionalStringProperty(entryName, elementIndex, objectLiteral, key) {
172527
- for (const property of objectLiteral.properties) {
172528
- if (!import_typescript2.default.isPropertyAssignment(property)) {
172529
- continue;
173062
+ if (state.mode === "line-comment") {
173063
+ if (character === "\r" || character === `
173064
+ `) {
173065
+ state.mode = "code";
172530
173066
  }
172531
- const propertyName = getPropertyNameText(property.name);
172532
- if (propertyName !== key) {
172533
- continue;
173067
+ return { ambiguous: false, inCode: false, index: index + 1 };
173068
+ }
173069
+ if (state.mode === "block-comment") {
173070
+ if (character === "*" && source[index + 1] === "/") {
173071
+ state.mode = "code";
173072
+ return { ambiguous: false, inCode: false, index: index + 2 };
172534
173073
  }
172535
- if (import_typescript2.default.isStringLiteralLike(property.initializer)) {
172536
- return property.initializer.text;
173074
+ return { ambiguous: false, inCode: false, index: index + 1 };
173075
+ }
173076
+ if (character === "'") {
173077
+ state.mode = "single-quoted";
173078
+ return { ambiguous: false, inCode: false, index: index + 1 };
173079
+ }
173080
+ if (character === '"') {
173081
+ state.mode = "double-quoted";
173082
+ return { ambiguous: false, inCode: false, index: index + 1 };
173083
+ }
173084
+ if (character === "/" && source[index + 1] === "/") {
173085
+ state.mode = "line-comment";
173086
+ return { ambiguous: false, inCode: false, index: index + 2 };
173087
+ }
173088
+ if (character === "#" && source[index + 1] !== "[") {
173089
+ state.mode = "line-comment";
173090
+ return { ambiguous: false, inCode: false, index: index + 1 };
173091
+ }
173092
+ if (character === "/" && source[index + 1] === "*") {
173093
+ state.mode = "block-comment";
173094
+ return { ambiguous: false, inCode: false, index: index + 2 };
173095
+ }
173096
+ if (character === "<") {
173097
+ const heredocStart = parsePhpHeredocStart(source, index);
173098
+ if (heredocStart) {
173099
+ state.mode = "heredoc";
173100
+ state.heredocDelimiter = heredocStart.delimiter;
173101
+ return {
173102
+ ambiguous: false,
173103
+ inCode: false,
173104
+ index: heredocStart.contentStart
173105
+ };
172537
173106
  }
172538
- throw new Error(`${entryName}[${elementIndex}] must use a string literal for "${key}" in scripts/block-config.ts.`);
172539
173107
  }
172540
- return;
173108
+ return { ambiguous: false, inCode: true, index };
172541
173109
  }
172542
- function getOptionalStringArrayProperty(entryName, elementIndex, objectLiteral, key) {
172543
- for (const property of objectLiteral.properties) {
172544
- if (!import_typescript2.default.isPropertyAssignment(property)) {
172545
- continue;
173110
+ function hasPhpFunctionCall(source, functionName) {
173111
+ const scanner = createPhpScannerState();
173112
+ let index = 0;
173113
+ while (index < source.length) {
173114
+ const scan = advancePhpScanner(source, index, scanner);
173115
+ if (scan.ambiguous) {
173116
+ return false;
172546
173117
  }
172547
- const propertyName = getPropertyNameText(property.name);
172548
- if (propertyName !== key) {
173118
+ if (!scan.inCode) {
173119
+ index = scan.index;
172549
173120
  continue;
172550
173121
  }
172551
- if (!import_typescript2.default.isArrayLiteralExpression(property.initializer)) {
172552
- throw new Error(`${entryName}[${elementIndex}] must use an array literal for "${key}" in scripts/block-config.ts.`);
173122
+ if (matchesPhpFunctionCallAt(source, index, functionName)) {
173123
+ return true;
172553
173124
  }
172554
- return property.initializer.elements.map((element, itemIndex) => {
172555
- if (!import_typescript2.default.isStringLiteralLike(element)) {
172556
- throw new Error(`${entryName}[${elementIndex}].${key}[${itemIndex}] must use a string literal in scripts/block-config.ts.`);
172557
- }
172558
- return element.text;
172559
- });
172560
- }
172561
- return;
172562
- }
172563
- function isMissingRequiredInventoryValue(value) {
172564
- return value === undefined || typeof value === "string" && value.length === 0;
172565
- }
172566
- function formatMissingRequiredInventoryFields(keys) {
172567
- return keys.length === 1 ? `required "${keys[0]}"` : `required fields ${keys.map((key) => `"${key}"`).join(", ")}`;
172568
- }
172569
- function assertParsedInventoryEntry(entry, descriptor, elementIndex) {
172570
- const missingRequiredKeys = descriptor.fields.filter((field) => field.required === true && isMissingRequiredInventoryValue(entry[field.key])).map((field) => field.key);
172571
- if (missingRequiredKeys.length > 0) {
172572
- throw new Error(`${descriptor.entryName}[${elementIndex}] is missing ${formatMissingRequiredInventoryFields(missingRequiredKeys)} in scripts/block-config.ts.`);
173125
+ index += 1;
172573
173126
  }
173127
+ return false;
172574
173128
  }
172575
- function parseInventoryEntries(arrayLiteral, descriptor) {
172576
- return arrayLiteral.elements.map((element, elementIndex) => {
172577
- if (!import_typescript2.default.isObjectLiteralExpression(element)) {
172578
- throw new Error(`${descriptor.entryName}[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
172579
- }
172580
- const entry = {};
172581
- for (const field of descriptor.fields) {
172582
- const kind = field.kind ?? "string";
172583
- const value = kind === "stringArray" ? getOptionalStringArrayProperty(descriptor.entryName, elementIndex, element, field.key) : getOptionalStringProperty(descriptor.entryName, elementIndex, element, field.key);
172584
- field.validate?.(value, {
172585
- elementIndex,
172586
- entryName: descriptor.entryName,
172587
- key: field.key
172588
- });
172589
- entry[field.key] = value;
172590
- }
172591
- assertParsedInventoryEntry(entry, descriptor, elementIndex);
172592
- return entry;
172593
- });
172594
- }
172595
- function parseInventorySection(sourceFile, descriptor) {
172596
- if (!descriptor.parse) {
172597
- return {
172598
- entries: [],
172599
- found: false
172600
- };
173129
+ function findPhpFunctionRange(source, functionName, options = {}) {
173130
+ const signaturePattern = new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\([^)]*\\)\\s*(?::\\s*[^{};]+)?\\s*\\{`, "u");
173131
+ const signatureMatch = signaturePattern.exec(source);
173132
+ if (!signatureMatch) {
173133
+ return null;
172601
173134
  }
172602
- const exportName = descriptor.parse.exportName ?? descriptor.value?.name;
172603
- if (!exportName) {
172604
- throw new Error("Inventory parser descriptor is missing an export name.");
173135
+ const functionStart = signatureMatch.index;
173136
+ const openBraceOffset = signatureMatch[0].lastIndexOf("{");
173137
+ if (openBraceOffset === -1) {
173138
+ return null;
172605
173139
  }
172606
- const exportedArray = findExportedArrayLiteral(sourceFile, exportName);
172607
- if (!exportedArray.found) {
172608
- if (descriptor.parse.required) {
172609
- throw new Error(`scripts/block-config.ts must export a ${exportName} array.`);
173140
+ const openBraceIndex = functionStart + openBraceOffset;
173141
+ let depth = 0;
173142
+ const scanner = createPhpScannerState();
173143
+ let index = openBraceIndex;
173144
+ while (index < source.length) {
173145
+ const scan = advancePhpScanner(source, index, scanner);
173146
+ if (scan.ambiguous) {
173147
+ return null;
172610
173148
  }
172611
- return {
172612
- entries: [],
172613
- found: false
172614
- };
172615
- }
172616
- if (!exportedArray.array) {
172617
- if (descriptor.parse.required) {
172618
- throw new Error(`scripts/block-config.ts must export a ${exportName} array.`);
173149
+ if (!scan.inCode) {
173150
+ index = scan.index;
173151
+ continue;
172619
173152
  }
172620
- throw new Error(`scripts/block-config.ts must export ${exportName} as an array literal.`);
172621
- }
172622
- return {
172623
- entries: parseInventoryEntries(exportedArray.array, descriptor.parse.entry),
172624
- found: true
172625
- };
172626
- }
172627
- function parseWorkspaceInventorySource(source) {
172628
- const sourceFile = import_typescript2.default.createSourceFile("block-config.ts", source, import_typescript2.default.ScriptTarget.Latest, true, import_typescript2.default.ScriptKind.TS);
172629
- const parsedInventory = {
172630
- abilities: [],
172631
- adminViews: [],
172632
- aiFeatures: [],
172633
- bindingSources: [],
172634
- blockStyles: [],
172635
- blockTransforms: [],
172636
- blocks: parseInventorySection(sourceFile, BLOCK_INVENTORY_SECTION).entries,
172637
- editorPlugins: [],
172638
- hasAbilitiesSection: false,
172639
- hasAdminViewsSection: false,
172640
- hasAiFeaturesSection: false,
172641
- hasBindingSourcesSection: false,
172642
- hasBlockStylesSection: false,
172643
- hasBlockTransformsSection: false,
172644
- hasEditorPluginsSection: false,
172645
- hasPatternsSection: false,
172646
- hasRestResourcesSection: false,
172647
- hasVariationsSection: false,
172648
- patterns: [],
172649
- restResources: [],
172650
- source,
172651
- variations: []
172652
- };
172653
- const mutableInventory = parsedInventory;
172654
- for (const section of INVENTORY_SECTIONS) {
172655
- if (!section.parse) {
173153
+ const character = source[index];
173154
+ if (character === "{") {
173155
+ depth += 1;
173156
+ index += 1;
172656
173157
  continue;
172657
173158
  }
172658
- const parsedSection = parseInventorySection(sourceFile, section);
172659
- mutableInventory[section.parse.entriesKey] = parsedSection.entries;
172660
- if (section.parse.hasSectionKey) {
172661
- mutableInventory[section.parse.hasSectionKey] = parsedSection.found;
173159
+ if (character !== "}") {
173160
+ index += 1;
173161
+ continue;
172662
173162
  }
172663
- }
172664
- return parsedInventory;
172665
- }
172666
- function readWorkspaceInventory(projectDir) {
172667
- const blockConfigPath = path4.join(projectDir, "scripts", "block-config.ts");
172668
- let source;
172669
- try {
172670
- source = readFileSync(blockConfigPath, "utf8");
172671
- } catch (error) {
172672
- if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
172673
- throw new Error(`Workspace inventory file is missing at ${blockConfigPath}. Expected scripts/block-config.ts to exist.`);
173163
+ depth -= 1;
173164
+ if (depth === 0) {
173165
+ let end = index + 1;
173166
+ if (options.includeTrailingNewlines ?? true) {
173167
+ while (end < source.length && /[\r\n]/u.test(source[end] ?? "")) {
173168
+ end += 1;
173169
+ }
173170
+ }
173171
+ return {
173172
+ end,
173173
+ source: source.slice(functionStart, end),
173174
+ start: functionStart
173175
+ };
172674
173176
  }
172675
- throw error;
173177
+ index += 1;
172676
173178
  }
172677
- return {
172678
- blockConfigPath,
172679
- ...parseWorkspaceInventorySource(source)
172680
- };
173179
+ return null;
172681
173180
  }
172682
- async function readWorkspaceInventoryAsync(projectDir) {
172683
- const blockConfigPath = path4.join(projectDir, "scripts", "block-config.ts");
172684
- let source;
172685
- try {
172686
- source = await readFile(blockConfigPath, "utf8");
172687
- } catch (error) {
172688
- if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
172689
- throw new Error(`Workspace inventory file is missing at ${blockConfigPath}. Expected scripts/block-config.ts to exist.`);
172690
- }
172691
- throw error;
173181
+ function replacePhpFunctionDefinition(source, functionName, replacement, options = {}) {
173182
+ const functionRange = findPhpFunctionRange(source, functionName, options);
173183
+ if (!functionRange) {
173184
+ return null;
172692
173185
  }
172693
- return {
172694
- blockConfigPath,
172695
- ...parseWorkspaceInventorySource(source)
172696
- };
172697
- }
172698
- function toWorkspaceBlockSelectOptions(blocks) {
172699
- return blocks.map((block) => ({
172700
- description: block.typesFile,
172701
- name: block.slug,
172702
- value: block.slug
172703
- }));
172704
- }
172705
- function getWorkspaceBlockSelectOptions(projectDir) {
172706
- return toWorkspaceBlockSelectOptions(readWorkspaceInventory(projectDir).blocks);
172707
- }
172708
- async function getWorkspaceBlockSelectOptionsAsync(projectDir) {
172709
- return toWorkspaceBlockSelectOptions((await readWorkspaceInventoryAsync(projectDir)).blocks);
173186
+ return [
173187
+ source.slice(0, functionRange.start),
173188
+ options.trimReplacementStart ? replacement.trimStart() : replacement,
173189
+ source.slice(functionRange.end)
173190
+ ].join("");
172710
173191
  }
173192
+
173193
+ // ../wp-typia-project-tools/src/runtime/workspace-inventory-mutations.ts
172711
173194
  function ensureWorkspaceInventorySections(source) {
172712
173195
  let nextSource = source.trimEnd();
172713
173196
  for (const section of INVENTORY_SECTIONS) {
172714
- if (section.interface && !hasExportedInterface(nextSource, section.interface.name)) {
173197
+ if (section.interface && !hasExportedTypeDeclaration(nextSource, section.interface.name)) {
172715
173198
  nextSource += section.interface.section;
172716
173199
  }
172717
173200
  if (section.value && !hasExportedConst(nextSource, section.value.name)) {
@@ -172721,8 +173204,8 @@ function ensureWorkspaceInventorySections(source) {
172721
173204
  return `${nextSource}
172722
173205
  `;
172723
173206
  }
172724
- function hasExportedInterface(source, interfaceName) {
172725
- return new RegExp(`export\\s+interface\\s+${escapeRegex(interfaceName)}\\b`, "u").test(source);
173207
+ function hasExportedTypeDeclaration(source, interfaceName) {
173208
+ return new RegExp(`export\\s+(?:interface|type)\\s+${escapeRegex(interfaceName)}\\b`, "u").test(source);
172726
173209
  }
172727
173210
  function hasExportedConst(source, constName) {
172728
173211
  return new RegExp(`export\\s+const\\s+${escapeRegex(constName)}\\b`, "u").test(source);
@@ -172734,9 +173217,10 @@ function appendEntriesAtMarker(source, marker, entries) {
172734
173217
  if (!source.includes(marker)) {
172735
173218
  throw new Error(`Workspace inventory marker "${marker}" is missing in scripts/block-config.ts.`);
172736
173219
  }
172737
- return source.replace(marker, `${entries.join(`
173220
+ const replacement = `${entries.join(`
172738
173221
  `)}
172739
- ${marker}`);
173222
+ ${marker}`;
173223
+ return source.replace(marker, () => replacement);
172740
173224
  }
172741
173225
  function appendInventorySectionEntries(source, options) {
172742
173226
  let nextSource = source;
@@ -172774,6 +173258,37 @@ function ensureInterfaceField(source, interfaceName, fieldName, fieldSource) {
172774
173258
  return `${start}${body}${body.length > 0 && !body.endsWith(lineEnding) ? lineEnding : ""}${formattedFieldSource}${end}`;
172775
173259
  });
172776
173260
  }
173261
+ function upsertInterfaceField(source, interfaceName, fieldName, fieldSource) {
173262
+ const interfacePattern = new RegExp(`(export\\s+interface\\s+${escapeRegex(interfaceName)}\\s*\\{\\r?\\n)([\\s\\S]*?)(\\r?\\n\\})`, "u");
173263
+ return source.replace(interfacePattern, (match, start, body, end) => {
173264
+ const lineEnding = start.endsWith(`\r
173265
+ `) ? `\r
173266
+ ` : `
173267
+ `;
173268
+ const formattedFieldSource = `${fieldSource.replace(/\r?\n$/u, "").split(`
173269
+ `).join(lineEnding)}${lineEnding}`;
173270
+ const existingFieldPattern = new RegExp(`(^[ \\t]*${escapeRegex(fieldName)}\\??:\\s*[^;\\r\\n]+;?\\r?\\n?)`, "mu");
173271
+ const existingFieldMatch = existingFieldPattern.exec(body);
173272
+ if (existingFieldMatch?.[0]) {
173273
+ if (existingFieldMatch[0].trim() === fieldSource.trim()) {
173274
+ return match;
173275
+ }
173276
+ return `${start}${body.slice(0, existingFieldMatch.index)}${formattedFieldSource}${body.slice(existingFieldMatch.index + existingFieldMatch[0].length)}${end}`;
173277
+ }
173278
+ const memberPattern = /^[ \t]*([A-Za-z_$][\w$]*)\??:/gmu;
173279
+ for (const member of body.matchAll(memberPattern)) {
173280
+ const memberIndex = member.index;
173281
+ const memberName = member[1];
173282
+ if (memberIndex === undefined || !memberName) {
173283
+ continue;
173284
+ }
173285
+ if (memberName.localeCompare(fieldName) > 0) {
173286
+ return `${start}${body.slice(0, memberIndex)}${formattedFieldSource}${body.slice(memberIndex)}${end}`;
173287
+ }
173288
+ }
173289
+ return `${start}${body}${body.length > 0 && !body.endsWith(lineEnding) ? lineEnding : ""}${formattedFieldSource}${end}`;
173290
+ });
173291
+ }
172777
173292
  function normalizeInterfaceFieldBlock(source, interfaceName, fieldName, fieldSource, requiredFragments) {
172778
173293
  const interfacePattern = new RegExp(`(export\\s+interface\\s+${escapeRegex(interfaceName)}\\s*\\{\\r?\\n)([\\s\\S]*?)(\\r?\\n\\})`, "u");
172779
173294
  return source.replace(interfacePattern, (match, start, body, end) => {
@@ -172807,17 +173322,35 @@ function updateWorkspaceInventorySource(source, options = {}) {
172807
173322
  nextSource = normalizeInterfaceFieldBlock(nextSource, "WorkspaceAbilityConfig", "compatibility", WORKSPACE_COMPATIBILITY_CONFIG_FIELD, ["optionalFeatureIds: string[];", "requiredFeatureIds: string[];"]);
172808
173323
  nextSource = ensureInterfaceField(nextSource, "WorkspaceAiFeatureConfig", "compatibility", WORKSPACE_COMPATIBILITY_CONFIG_FIELD);
172809
173324
  nextSource = normalizeInterfaceFieldBlock(nextSource, "WorkspaceAiFeatureConfig", "compatibility", WORKSPACE_COMPATIBILITY_CONFIG_FIELD, ["optionalFeatureIds: string[];", "requiredFeatureIds: string[];"]);
173325
+ for (const [fieldName, fieldSource] of [
173326
+ ["auth", "\tauth?: 'authenticated' | 'public' | 'public-write-protected';"],
173327
+ ["bodyTypeName", "\tbodyTypeName?: string;"],
173328
+ ["controllerClass", "\tcontrollerClass?: string;"],
173329
+ ["controllerExtends", "\tcontrollerExtends?: string;"],
173330
+ ["dataFile", "\tdataFile?: string;"],
173331
+ ["method", "\tmethod?: 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT';"],
173332
+ ["mode", "\tmode?: 'generated' | 'manual';"],
173333
+ ["pathPattern", "\tpathPattern?: string;"],
173334
+ ["permissionCallback", "\tpermissionCallback?: string;"],
173335
+ ["phpFile", "\tphpFile?: string;"],
173336
+ ["queryTypeName", "\tqueryTypeName?: string;"],
173337
+ ["responseTypeName", "\tresponseTypeName?: string;"],
173338
+ ["routePattern", "\troutePattern?: string;"],
173339
+ ["secretFieldName", "\tsecretFieldName?: string;"],
173340
+ ["secretStateFieldName", "\tsecretStateFieldName?: string;"]
173341
+ ]) {
173342
+ nextSource = upsertInterfaceField(nextSource, "WorkspaceRestResourceConfig", fieldName, fieldSource);
173343
+ }
172810
173344
  return nextSource;
172811
173345
  }
172812
173346
  async function appendWorkspaceInventoryEntries(projectDir, options) {
172813
- const blockConfigPath = path4.join(projectDir, "scripts", "block-config.ts");
172814
- const source = await readFile(blockConfigPath, "utf8");
173347
+ const blockConfigPath = path5.join(projectDir, "scripts", "block-config.ts");
173348
+ const source = await readFile2(blockConfigPath, "utf8");
172815
173349
  const nextSource = updateWorkspaceInventorySource(source, options);
172816
173350
  if (nextSource !== source) {
172817
173351
  await writeFile(blockConfigPath, nextSource, "utf8");
172818
173352
  }
172819
173353
  }
173354
+ export { toKebabCase, toSnakeCase, toPascalCase, toCamelCase, toSegmentPascalCase, toTitleCase, validateBlockSlug, validateNamespace, normalizeBlockSlug, resolveNonEmptyNormalizedBlockSlug, buildBlockCssClassName, buildFrontendCssClassName, resolveScaffoldIdentifiers, REST_RESOURCE_METHOD_IDS, MANUAL_REST_CONTRACT_HTTP_METHOD_IDS, MANUAL_REST_CONTRACT_AUTH_IDS, EDITOR_PLUGIN_SLOT_IDS, resolveEditorPluginSlotAlias, ADD_BLOCK_TEMPLATE_IDS, suggestAddBlockTemplateId, HOOKED_BLOCK_POSITION_SET, HOOKED_BLOCK_ANCHOR_PATTERN, REST_RESOURCE_NAMESPACE_PATTERN, assertValidGeneratedSlug, assertValidTypeScriptIdentifier, resolveRestResourceNamespace, assertValidPostMetaPostType, resolvePostMetaKey, assertValidRestResourceMethods, resolveOptionalPhpCallbackReference, resolveOptionalPhpClassReference, assertValidManualRestContractHttpMethod, assertValidManualRestContractAuth, isGeneratedRestResourceRoutePatternCompatible, resolveManualRestContractPathPattern, resolveGeneratedRestResourceRoutePattern, assertValidHookedBlockPosition, buildWorkspacePhpPrefix, isAddBlockTemplateId, quoteTsString, assertValidHookAnchor, assertValidEditorPluginSlot, assertValidIntegrationEnvService, pathExists, readOptionalUtf8File, getNodeErrorCode, getOptionalNodeErrorCode, isFileNotFoundError, getWorkspaceBootstrapPath, patchFile, readOptionalFile, snapshotWorkspaceFiles, rollbackWorkspaceMutation, resolveWorkspaceBlock, readWorkspaceBlockJson, getMutableBlockHooks, assertVariationDoesNotExist, assertBlockStyleDoesNotExist, assertBlockTransformDoesNotExist, assertPatternDoesNotExist, assertBindingSourceDoesNotExist, assertRestResourceDoesNotExist, assertPostMetaDoesNotExist, assertContractDoesNotExist, assertAdminViewDoesNotExist, assertAbilityDoesNotExist, assertAiFeatureDoesNotExist, assertEditorPluginDoesNotExist, formatAddHelpText, require_typescript, getPropertyNameText, readWorkspaceInventory, readWorkspaceInventoryAsync, getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, escapeRegex, quotePhpString, hasPhpFunctionDefinition, hasPhpFunctionCall, findPhpFunctionRange, replacePhpFunctionDefinition, updateWorkspaceInventorySource, appendWorkspaceInventoryEntries };
172820
173355
 
172821
- export { toKebabCase, toSnakeCase, toPascalCase, toCamelCase, toSegmentPascalCase, toTitleCase, validateBlockSlug, validateNamespace, normalizeBlockSlug, resolveNonEmptyNormalizedBlockSlug, buildBlockCssClassName, buildFrontendCssClassName, resolveScaffoldIdentifiers, REST_RESOURCE_METHOD_IDS, EDITOR_PLUGIN_SLOT_IDS, resolveEditorPluginSlotAlias, ADD_BLOCK_TEMPLATE_IDS, suggestAddBlockTemplateId, HOOKED_BLOCK_POSITION_SET, HOOKED_BLOCK_ANCHOR_PATTERN, REST_RESOURCE_NAMESPACE_PATTERN, assertValidGeneratedSlug, resolveRestResourceNamespace, assertValidRestResourceMethods, assertValidHookedBlockPosition, buildWorkspacePhpPrefix, isAddBlockTemplateId, quoteTsString, assertValidHookAnchor, assertValidEditorPluginSlot, getWorkspaceBootstrapPath, patchFile, readOptionalFile, snapshotWorkspaceFiles, rollbackWorkspaceMutation, pathExists, readOptionalUtf8File, getNodeErrorCode, resolveWorkspaceBlock, readWorkspaceBlockJson, getMutableBlockHooks, assertVariationDoesNotExist, assertBlockStyleDoesNotExist, assertBlockTransformDoesNotExist, assertPatternDoesNotExist, assertBindingSourceDoesNotExist, assertRestResourceDoesNotExist, assertAdminViewDoesNotExist, assertAbilityDoesNotExist, assertAiFeatureDoesNotExist, assertEditorPluginDoesNotExist, formatAddHelpText, require_typescript, escapeRegex, quotePhpString, hasPhpFunctionDefinition, hasPhpFunctionCall, findPhpFunctionRange, replacePhpFunctionDefinition, getPropertyNameText, readWorkspaceInventory, readWorkspaceInventoryAsync, getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, updateWorkspaceInventorySource, appendWorkspaceInventoryEntries };
172822
-
172823
- //# debugId=3C14848EF1DAC47E64756E2164756E21
173356
+ //# debugId=E79B1182F12A2D7664756E2164756E21