@wp-typia/project-tools 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/dist/runtime/ai-feature-artifacts.js +4 -1
  2. package/dist/runtime/block-generator-service-spec.js +2 -1
  3. package/dist/runtime/built-in-block-non-ts-basic-artifacts.d.ts +9 -0
  4. package/dist/runtime/built-in-block-non-ts-basic-artifacts.js +84 -0
  5. package/dist/runtime/built-in-block-non-ts-compound-artifacts.d.ts +9 -0
  6. package/dist/runtime/built-in-block-non-ts-compound-artifacts.js +36 -0
  7. package/dist/runtime/built-in-block-non-ts-compound-templates.d.ts +23 -0
  8. package/dist/runtime/built-in-block-non-ts-compound-templates.js +453 -0
  9. package/dist/runtime/built-in-block-non-ts-family-artifacts.d.ts +8 -26
  10. package/dist/runtime/built-in-block-non-ts-family-artifacts.js +8 -1034
  11. package/dist/runtime/built-in-block-non-ts-interactivity-artifacts.d.ts +9 -0
  12. package/dist/runtime/built-in-block-non-ts-interactivity-artifacts.js +83 -0
  13. package/dist/runtime/built-in-block-non-ts-persistence-artifacts.d.ts +9 -0
  14. package/dist/runtime/built-in-block-non-ts-persistence-artifacts.js +33 -0
  15. package/dist/runtime/built-in-block-non-ts-persistence-templates.d.ts +23 -0
  16. package/dist/runtime/built-in-block-non-ts-persistence-templates.js +395 -0
  17. package/dist/runtime/cli-add-block-json.js +5 -1
  18. package/dist/runtime/cli-add-collision.js +8 -0
  19. package/dist/runtime/cli-add-help.js +14 -10
  20. package/dist/runtime/cli-add-kind-ids.d.ts +1 -1
  21. package/dist/runtime/cli-add-kind-ids.js +1 -0
  22. package/dist/runtime/cli-add-types.d.ts +45 -6
  23. package/dist/runtime/cli-add-types.js +2 -0
  24. package/dist/runtime/cli-add-validation.d.ts +7 -0
  25. package/dist/runtime/cli-add-validation.js +9 -0
  26. package/dist/runtime/cli-add-workspace-ability-anchors.d.ts +24 -0
  27. package/dist/runtime/cli-add-workspace-ability-anchors.js +294 -0
  28. package/dist/runtime/cli-add-workspace-ability-registry.d.ts +10 -0
  29. package/dist/runtime/cli-add-workspace-ability-registry.js +51 -0
  30. package/dist/runtime/cli-add-workspace-ability-scaffold.d.ts +1 -1
  31. package/dist/runtime/cli-add-workspace-ability-scaffold.js +5 -308
  32. package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +6 -2
  33. package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.d.ts +34 -0
  34. package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.js +483 -0
  35. package/dist/runtime/cli-add-workspace-admin-view-templates-default.d.ts +30 -0
  36. package/dist/runtime/cli-add-workspace-admin-view-templates-default.js +310 -0
  37. package/dist/runtime/cli-add-workspace-admin-view-templates-rest.d.ts +25 -0
  38. package/dist/runtime/cli-add-workspace-admin-view-templates-rest.js +124 -0
  39. package/dist/runtime/cli-add-workspace-admin-view-templates-settings.d.ts +34 -0
  40. package/dist/runtime/cli-add-workspace-admin-view-templates-settings.js +370 -0
  41. package/dist/runtime/cli-add-workspace-admin-view-templates-shared.d.ts +49 -0
  42. package/dist/runtime/cli-add-workspace-admin-view-templates-shared.js +259 -0
  43. package/dist/runtime/cli-add-workspace-admin-view-templates.d.ts +18 -27
  44. package/dist/runtime/cli-add-workspace-admin-view-templates.js +30 -1326
  45. package/dist/runtime/cli-add-workspace-ai-anchors.d.ts +4 -4
  46. package/dist/runtime/cli-add-workspace-ai-anchors.js +8 -233
  47. package/dist/runtime/cli-add-workspace-ai-scaffold.js +4 -2
  48. package/dist/runtime/cli-add-workspace-ai-source-emitters.d.ts +1 -4
  49. package/dist/runtime/cli-add-workspace-ai-source-emitters.js +1 -129
  50. package/dist/runtime/cli-add-workspace-ai-sync-rest-anchors.d.ts +5 -0
  51. package/dist/runtime/cli-add-workspace-ai-sync-rest-anchors.js +236 -0
  52. package/dist/runtime/cli-add-workspace-ai-sync-script-source.d.ts +4 -0
  53. package/dist/runtime/cli-add-workspace-ai-sync-script-source.js +145 -0
  54. package/dist/runtime/cli-add-workspace-assets.d.ts +6 -63
  55. package/dist/runtime/cli-add-workspace-assets.js +6 -950
  56. package/dist/runtime/cli-add-workspace-binding-source-anchors.d.ts +23 -0
  57. package/dist/runtime/cli-add-workspace-binding-source-anchors.js +112 -0
  58. package/dist/runtime/cli-add-workspace-binding-source-source-emitters.d.ts +33 -0
  59. package/dist/runtime/cli-add-workspace-binding-source-source-emitters.js +436 -0
  60. package/dist/runtime/cli-add-workspace-binding-source-types.d.ts +20 -0
  61. package/dist/runtime/cli-add-workspace-binding-source-types.js +1 -0
  62. package/dist/runtime/cli-add-workspace-binding-source.d.ts +40 -0
  63. package/dist/runtime/cli-add-workspace-binding-source.js +275 -0
  64. package/dist/runtime/cli-add-workspace-block-style.d.ts +22 -0
  65. package/dist/runtime/cli-add-workspace-block-style.js +148 -0
  66. package/dist/runtime/cli-add-workspace-block-transform.d.ts +32 -0
  67. package/dist/runtime/cli-add-workspace-block-transform.js +197 -0
  68. package/dist/runtime/cli-add-workspace-contract.js +1 -1
  69. package/dist/runtime/cli-add-workspace-core-variation.d.ts +20 -0
  70. package/dist/runtime/cli-add-workspace-core-variation.js +322 -0
  71. package/dist/runtime/cli-add-workspace-editor-plugin-anchors.d.ts +37 -0
  72. package/dist/runtime/cli-add-workspace-editor-plugin-anchors.js +206 -0
  73. package/dist/runtime/cli-add-workspace-editor-plugin-source-emitters.d.ts +47 -0
  74. package/dist/runtime/cli-add-workspace-editor-plugin-source-emitters.js +219 -0
  75. package/dist/runtime/cli-add-workspace-editor-plugin.d.ts +22 -0
  76. package/dist/runtime/cli-add-workspace-editor-plugin.js +78 -0
  77. package/dist/runtime/cli-add-workspace-hooked-block.d.ts +23 -0
  78. package/dist/runtime/cli-add-workspace-hooked-block.js +57 -0
  79. package/dist/runtime/cli-add-workspace-integration-env-files.d.ts +33 -0
  80. package/dist/runtime/cli-add-workspace-integration-env-files.js +65 -0
  81. package/dist/runtime/cli-add-workspace-integration-env-package-json.d.ts +38 -0
  82. package/dist/runtime/cli-add-workspace-integration-env-package-json.js +122 -0
  83. package/dist/runtime/cli-add-workspace-integration-env-source-emitters.d.ts +44 -0
  84. package/dist/runtime/cli-add-workspace-integration-env-source-emitters.js +262 -0
  85. package/dist/runtime/cli-add-workspace-integration-env.d.ts +3 -1
  86. package/dist/runtime/cli-add-workspace-integration-env.js +10 -313
  87. package/dist/runtime/cli-add-workspace-pattern-anchors.d.ts +10 -0
  88. package/dist/runtime/cli-add-workspace-pattern-anchors.js +95 -0
  89. package/dist/runtime/cli-add-workspace-pattern-options.d.ts +20 -0
  90. package/dist/runtime/cli-add-workspace-pattern-options.js +113 -0
  91. package/dist/runtime/cli-add-workspace-pattern-source-emitters.d.ts +20 -0
  92. package/dist/runtime/cli-add-workspace-pattern-source-emitters.js +57 -0
  93. package/dist/runtime/cli-add-workspace-pattern.d.ts +42 -0
  94. package/dist/runtime/cli-add-workspace-pattern.js +99 -0
  95. package/dist/runtime/cli-add-workspace-post-meta.js +1 -1
  96. package/dist/runtime/cli-add-workspace-registration-hooks.d.ts +50 -0
  97. package/dist/runtime/cli-add-workspace-registration-hooks.js +162 -0
  98. package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +9 -4
  99. package/dist/runtime/cli-add-workspace-rest-anchors.js +9 -428
  100. package/dist/runtime/cli-add-workspace-rest-bootstrap-anchors.d.ts +17 -0
  101. package/dist/runtime/cli-add-workspace-rest-bootstrap-anchors.js +108 -0
  102. package/dist/runtime/cli-add-workspace-rest-contract-sync-anchors.d.ts +9 -0
  103. package/dist/runtime/cli-add-workspace-rest-contract-sync-anchors.js +142 -0
  104. package/dist/runtime/cli-add-workspace-rest-generated-source-emitters.d.ts +51 -0
  105. package/dist/runtime/cli-add-workspace-rest-generated-source-emitters.js +415 -0
  106. package/dist/runtime/cli-add-workspace-rest-generated.d.ts +9 -0
  107. package/dist/runtime/cli-add-workspace-rest-generated.js +160 -0
  108. package/dist/runtime/cli-add-workspace-rest-manual-source-emitters.d.ts +80 -0
  109. package/dist/runtime/cli-add-workspace-rest-manual-source-emitters.js +238 -0
  110. package/dist/runtime/cli-add-workspace-rest-manual.d.ts +8 -0
  111. package/dist/runtime/cli-add-workspace-rest-manual.js +266 -0
  112. package/dist/runtime/cli-add-workspace-rest-php-templates.d.ts +18 -0
  113. package/dist/runtime/cli-add-workspace-rest-php-templates.js +359 -0
  114. package/dist/runtime/cli-add-workspace-rest-resource-php-routing-template.d.ts +33 -0
  115. package/dist/runtime/cli-add-workspace-rest-resource-php-routing-template.js +145 -0
  116. package/dist/runtime/cli-add-workspace-rest-resource-sync-anchors.d.ts +9 -0
  117. package/dist/runtime/cli-add-workspace-rest-resource-sync-anchors.js +162 -0
  118. package/dist/runtime/cli-add-workspace-rest-schema-helper-php-template.d.ts +7 -0
  119. package/dist/runtime/cli-add-workspace-rest-schema-helper-php-template.js +193 -0
  120. package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +5 -91
  121. package/dist/runtime/cli-add-workspace-rest-source-emitters.js +5 -642
  122. package/dist/runtime/cli-add-workspace-rest-source-utils.d.ts +17 -0
  123. package/dist/runtime/cli-add-workspace-rest-source-utils.js +50 -0
  124. package/dist/runtime/cli-add-workspace-rest-sync-script-shared.d.ts +56 -0
  125. package/dist/runtime/cli-add-workspace-rest-sync-script-shared.js +122 -0
  126. package/dist/runtime/cli-add-workspace-rest-types.d.ts +108 -0
  127. package/dist/runtime/cli-add-workspace-rest-types.js +1 -0
  128. package/dist/runtime/cli-add-workspace-rest.d.ts +3 -20
  129. package/dist/runtime/cli-add-workspace-rest.js +33 -788
  130. package/dist/runtime/cli-add-workspace-variation.d.ts +22 -0
  131. package/dist/runtime/cli-add-workspace-variation.js +162 -0
  132. package/dist/runtime/cli-add-workspace.d.ts +42 -107
  133. package/dist/runtime/cli-add-workspace.js +42 -674
  134. package/dist/runtime/cli-add.d.ts +3 -3
  135. package/dist/runtime/cli-add.js +2 -2
  136. package/dist/runtime/cli-core.d.ts +3 -2
  137. package/dist/runtime/cli-core.js +2 -2
  138. package/dist/runtime/cli-diagnostics.d.ts +3 -1
  139. package/dist/runtime/cli-diagnostics.js +17 -5
  140. package/dist/runtime/cli-doctor-workspace-bindings.js +63 -1
  141. package/dist/runtime/cli-doctor-workspace-block-addons.d.ts +12 -0
  142. package/dist/runtime/cli-doctor-workspace-block-addons.js +162 -0
  143. package/dist/runtime/cli-doctor-workspace-block-iframe.d.ts +9 -0
  144. package/dist/runtime/cli-doctor-workspace-block-iframe.js +228 -0
  145. package/dist/runtime/cli-doctor-workspace-block-metadata.d.ts +11 -0
  146. package/dist/runtime/cli-doctor-workspace-block-metadata.js +111 -0
  147. package/dist/runtime/cli-doctor-workspace-blocks.js +6 -424
  148. package/dist/runtime/cli-doctor-workspace-features-abilities.d.ts +11 -0
  149. package/dist/runtime/cli-doctor-workspace-features-abilities.js +112 -0
  150. package/dist/runtime/cli-doctor-workspace-features-admin-views.d.ts +11 -0
  151. package/dist/runtime/cli-doctor-workspace-features-admin-views.js +128 -0
  152. package/dist/runtime/cli-doctor-workspace-features-ai.d.ts +11 -0
  153. package/dist/runtime/cli-doctor-workspace-features-ai.js +57 -0
  154. package/dist/runtime/cli-doctor-workspace-features-editor-plugins.d.ts +11 -0
  155. package/dist/runtime/cli-doctor-workspace-features-editor-plugins.js +80 -0
  156. package/dist/runtime/cli-doctor-workspace-features-post-meta.d.ts +11 -0
  157. package/dist/runtime/cli-doctor-workspace-features-post-meta.js +77 -0
  158. package/dist/runtime/cli-doctor-workspace-features-rest.d.ts +11 -0
  159. package/dist/runtime/cli-doctor-workspace-features-rest.js +120 -0
  160. package/dist/runtime/cli-doctor-workspace-features.js +14 -487
  161. package/dist/runtime/cli-doctor.d.ts +54 -3
  162. package/dist/runtime/cli-doctor.js +92 -10
  163. package/dist/runtime/cli-help.js +12 -7
  164. package/dist/runtime/cli-init-package-json.js +4 -2
  165. package/dist/runtime/cli-prompt.d.ts +16 -2
  166. package/dist/runtime/cli-prompt.js +29 -12
  167. package/dist/runtime/cli-scaffold.d.ts +2 -1
  168. package/dist/runtime/cli-scaffold.js +19 -10
  169. package/dist/runtime/external-template-guards.js +4 -6
  170. package/dist/runtime/index.d.ts +6 -3
  171. package/dist/runtime/index.js +4 -2
  172. package/dist/runtime/json-utils.d.ts +62 -4
  173. package/dist/runtime/json-utils.js +78 -4
  174. package/dist/runtime/local-dev-presets.js +6 -2
  175. package/dist/runtime/migration-ui-capability.js +4 -1
  176. package/dist/runtime/migration-utils.js +4 -1
  177. package/dist/runtime/package-managers.js +6 -1
  178. package/dist/runtime/package-versions.d.ts +1 -0
  179. package/dist/runtime/package-versions.js +16 -3
  180. package/dist/runtime/pattern-catalog.d.ts +122 -0
  181. package/dist/runtime/pattern-catalog.js +471 -0
  182. package/dist/runtime/post-meta-binding-fields.d.ts +46 -0
  183. package/dist/runtime/post-meta-binding-fields.js +135 -0
  184. package/dist/runtime/scaffold-bootstrap.js +7 -2
  185. package/dist/runtime/scaffold-package-manager-files.js +5 -1
  186. package/dist/runtime/scaffold-repository-reference.js +4 -2
  187. package/dist/runtime/scaffold-template-variables.js +2 -1
  188. package/dist/runtime/scaffold.d.ts +18 -1
  189. package/dist/runtime/scaffold.js +55 -2
  190. package/dist/runtime/temp-roots.js +4 -1
  191. package/dist/runtime/template-layers.js +4 -1
  192. package/dist/runtime/template-registry.js +9 -3
  193. package/dist/runtime/template-source-contracts.d.ts +2 -0
  194. package/dist/runtime/template-source-normalization.js +2 -1
  195. package/dist/runtime/template-source-remote.js +18 -5
  196. package/dist/runtime/template-source-seeds.js +10 -3
  197. package/dist/runtime/typia-llm-json-schema.d.ts +24 -0
  198. package/dist/runtime/typia-llm-json-schema.js +33 -0
  199. package/dist/runtime/typia-llm-openapi-constraints.d.ts +20 -0
  200. package/dist/runtime/typia-llm-openapi-constraints.js +254 -0
  201. package/dist/runtime/typia-llm-projection.d.ts +25 -0
  202. package/dist/runtime/typia-llm-projection.js +58 -0
  203. package/dist/runtime/typia-llm-render.d.ts +21 -0
  204. package/dist/runtime/typia-llm-render.js +252 -0
  205. package/dist/runtime/typia-llm-sync.d.ts +10 -0
  206. package/dist/runtime/typia-llm-sync.js +63 -0
  207. package/dist/runtime/typia-llm-types.d.ts +197 -0
  208. package/dist/runtime/typia-llm-types.js +1 -0
  209. package/dist/runtime/typia-llm.d.ts +9 -255
  210. package/dist/runtime/typia-llm.js +5 -634
  211. package/dist/runtime/workspace-inventory-mutations.js +15 -1
  212. package/dist/runtime/workspace-inventory-parser-entries.d.ts +17 -0
  213. package/dist/runtime/workspace-inventory-parser-entries.js +157 -0
  214. package/dist/runtime/workspace-inventory-parser-validation.d.ts +104 -0
  215. package/dist/runtime/workspace-inventory-parser-validation.js +34 -0
  216. package/dist/runtime/workspace-inventory-parser.d.ts +3 -45
  217. package/dist/runtime/workspace-inventory-parser.js +3 -581
  218. package/dist/runtime/workspace-inventory-section-descriptors.d.ts +19 -0
  219. package/dist/runtime/workspace-inventory-section-descriptors.js +443 -0
  220. package/dist/runtime/workspace-inventory-templates.d.ts +3 -3
  221. package/dist/runtime/workspace-inventory-templates.js +10 -1
  222. package/dist/runtime/workspace-inventory-types.d.ts +10 -1
  223. package/dist/runtime/workspace-project.js +4 -6
  224. package/package.json +8 -3
  225. package/templates/_shared/compound/core/scripts/block-config.ts.mustache +22 -0
  226. package/templates/_shared/compound/core/scripts/sync-types-to-block-json.ts.mustache +103 -2
  227. package/templates/_shared/compound/core/src/inner-blocks-templates.ts.mustache +13 -0
  228. package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +22 -1
@@ -0,0 +1,9 @@
1
+ import type { BuiltInCodeArtifact } from "./built-in-block-code-artifacts.js";
2
+ import type { ScaffoldTemplateVariables } from "./scaffold.js";
3
+ /**
4
+ * Builds the interactivity family non-TypeScript scaffold artifacts.
5
+ *
6
+ * @param variables Scaffold template variables used to render the artifact set.
7
+ * @returns The SCSS artifacts owned by the interactivity template family.
8
+ */
9
+ export declare function buildInteractivityArtifacts(variables: ScaffoldTemplateVariables): BuiltInCodeArtifact[];
@@ -0,0 +1,83 @@
1
+ import { renderArtifact } from "./built-in-block-non-ts-render-utils.js";
2
+ const INTERACTIVITY_STYLE_TEMPLATE = `.{{cssClassName}} {
3
+ position: relative;
4
+ padding: 1rem;
5
+ border: 1px solid #dcdcde;
6
+ border-radius: 0.75rem;
7
+ background: #fff;
8
+
9
+ &__content {
10
+ display: grid;
11
+ gap: 0.75rem;
12
+ }
13
+
14
+ &__counter {
15
+ display: inline-flex;
16
+ gap: 0.35rem;
17
+ align-items: center;
18
+ font-size: 0.9rem;
19
+ font-weight: 600;
20
+ }
21
+
22
+ &__progress {
23
+ width: 100%;
24
+ height: 0.5rem;
25
+ overflow: hidden;
26
+ background: #f0f0f1;
27
+ border-radius: 999px;
28
+ }
29
+
30
+ &__progress-bar {
31
+ height: 100%;
32
+ background: #3858e9;
33
+ transition: width 0.2s ease;
34
+ }
35
+
36
+ &__animation {
37
+ min-height: 1.5rem;
38
+ font-size: 0.85rem;
39
+ color: #3858e9;
40
+ opacity: 0;
41
+ transition: opacity 0.2s ease;
42
+
43
+ &.is-active {
44
+ opacity: 1;
45
+ }
46
+ }
47
+
48
+ &__completion {
49
+ font-weight: 700;
50
+ color: #06752d;
51
+ }
52
+
53
+ &__reset {
54
+ align-self: start;
55
+ padding: 0.4rem 0.7rem;
56
+ border: 1px solid #dcdcde;
57
+ border-radius: 999px;
58
+ background: transparent;
59
+ cursor: pointer;
60
+ }
61
+ }
62
+ `;
63
+ const INTERACTIVITY_EDITOR_STYLE_TEMPLATE = `/**
64
+ * {{title}} Block Editor Styles
65
+ */
66
+
67
+ .{{cssClassName}} {
68
+ outline: 1px dashed #ddd;
69
+ outline-offset: -1px;
70
+ }
71
+ `;
72
+ /**
73
+ * Builds the interactivity family non-TypeScript scaffold artifacts.
74
+ *
75
+ * @param variables Scaffold template variables used to render the artifact set.
76
+ * @returns The SCSS artifacts owned by the interactivity template family.
77
+ */
78
+ export function buildInteractivityArtifacts(variables) {
79
+ return [
80
+ renderArtifact("src/editor.scss", INTERACTIVITY_EDITOR_STYLE_TEMPLATE, variables),
81
+ renderArtifact("src/style.scss", INTERACTIVITY_STYLE_TEMPLATE, variables),
82
+ ];
83
+ }
@@ -0,0 +1,9 @@
1
+ import type { BuiltInCodeArtifact } from "./built-in-block-code-artifacts.js";
2
+ import type { ScaffoldTemplateVariables } from "./scaffold.js";
3
+ /**
4
+ * Builds the persistence family non-TypeScript scaffold artifacts.
5
+ *
6
+ * @param variables Scaffold template variables used to render the artifact set.
7
+ * @returns The persistence SCSS and PHP artifacts for the selected render targets.
8
+ */
9
+ export declare function buildPersistenceArtifacts(variables: ScaffoldTemplateVariables): BuiltInCodeArtifact[];
@@ -0,0 +1,33 @@
1
+ import { buildAlternateRenderEntryArtifact, renderArtifact, } from "./built-in-block-non-ts-render-utils.js";
2
+ import { PERSISTENCE_RENDER_TARGETS_TEMPLATE, PERSISTENCE_RENDER_TEMPLATE, PERSISTENCE_STYLE_TEMPLATE, } from "./built-in-block-non-ts-persistence-templates.js";
3
+ import { getScaffoldAlternateRenderTargets } from "./scaffold-template-variable-groups.js";
4
+ /**
5
+ * Builds the persistence family non-TypeScript scaffold artifacts.
6
+ *
7
+ * @param variables Scaffold template variables used to render the artifact set.
8
+ * @returns The persistence SCSS and PHP artifacts for the selected render targets.
9
+ */
10
+ export function buildPersistenceArtifacts(variables) {
11
+ const alternateRenderTargets = getScaffoldAlternateRenderTargets(variables);
12
+ if (!alternateRenderTargets.enabled) {
13
+ return [
14
+ renderArtifact("src/style.scss", PERSISTENCE_STYLE_TEMPLATE, variables),
15
+ renderArtifact("src/render.php", PERSISTENCE_RENDER_TEMPLATE, variables),
16
+ ];
17
+ }
18
+ const artifacts = [
19
+ renderArtifact("src/style.scss", PERSISTENCE_STYLE_TEMPLATE, variables),
20
+ renderArtifact("src/render-targets.php", PERSISTENCE_RENDER_TARGETS_TEMPLATE, variables),
21
+ buildAlternateRenderEntryArtifact("src/render.php", "web", variables),
22
+ ];
23
+ if (alternateRenderTargets.hasEmail) {
24
+ artifacts.push(buildAlternateRenderEntryArtifact("src/render-email.php", "email", variables));
25
+ }
26
+ if (alternateRenderTargets.hasMjml) {
27
+ artifacts.push(buildAlternateRenderEntryArtifact("src/render-mjml.php", "mjml", variables));
28
+ }
29
+ if (alternateRenderTargets.hasPlainText) {
30
+ artifacts.push(buildAlternateRenderEntryArtifact("src/render-text.php", "plain-text", variables));
31
+ }
32
+ return artifacts;
33
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * SCSS template for persistence block editor and frontend styles.
3
+ *
4
+ * @remarks Consumed by `buildPersistenceArtifacts()` with
5
+ * `{{cssClassName}}` and `{{frontendCssClassName}}` scaffold placeholders.
6
+ */
7
+ export declare const PERSISTENCE_STYLE_TEMPLATE = ".{{cssClassName}} {\n\tborder: 1px solid #dcdcde;\n\tborder-radius: 12px;\n\tpadding: 16px;\n\tbackground: #fff;\n\n\t&__meta {\n\t\tcolor: #50575e;\n\t\tfont-size: 13px;\n\t\tmargin: 8px 0 0;\n\t}\n}\n\n.{{frontendCssClassName}} {\n\tdisplay: grid;\n\tgap: 12px;\n\tborder: 1px solid #dcdcde;\n\tborder-radius: 12px;\n\tpadding: 16px;\n\tbackground: #f6f7f7;\n\n\t&__count {\n\t\tdisplay: inline-flex;\n\t\tmin-width: 3rem;\n\t\tjustify-content: center;\n\t\tfont-weight: 700;\n\t}\n\n\t&__notice,\n\t&__error {\n\t\tmargin: 0;\n\t\tfont-size: 13px;\n\t}\n\n\t&__notice {\n\t\tcolor: #50575e;\n\t}\n\n\t&__error {\n\t\tcolor: #b32d2e;\n\t}\n\n\tbutton {\n\t\twidth: fit-content;\n\t}\n}\n";
8
+ /**
9
+ * Main PHP `render.php` template for the persistence block.
10
+ *
11
+ * @remarks Consumed by `buildPersistenceArtifacts()` with scaffold
12
+ * placeholders for block identity, namespace, text domain, storage mode,
13
+ * persistence policy, and frontend CSS class names.
14
+ */
15
+ export declare const PERSISTENCE_RENDER_TEMPLATE = "<?php\n/**\n * Dynamic render entry for the {{title}} block.\n *\n * @package {{pascalCase}}\n */\n\nif ( ! defined( 'ABSPATH' ) ) {\n\texit;\n}\n\n$validator_path = __DIR__ . '/typia-validator.php';\nif ( ! file_exists( $validator_path ) ) {\n\treturn '';\n}\n\n$validator = require $validator_path;\nif ( ! is_object( $validator ) || ! method_exists( $validator, 'apply_defaults' ) || ! method_exists( $validator, 'validate' ) ) {\n\treturn '';\n}\n\n$normalized = $validator->apply_defaults( is_array( $attributes ) ? $attributes : array() );\n$validation = $validator->validate( $normalized );\n$resource_key = isset( $normalized['resourceKey'] ) ? (string) $normalized['resourceKey'] : '';\n\nif ( empty( $validation['valid'] ) || '' === $resource_key ) {\n\treturn '';\n}\n\n$alignment = isset( $normalized['alignment'] ) ? (string) $normalized['alignment'] : 'left';\n$button_label = isset( $normalized['buttonLabel'] ) ? (string) $normalized['buttonLabel'] : 'Persist Count';\n$content = isset( $normalized['content'] ) ? (string) $normalized['content'] : '';\n$post_id = is_object( $block ) && isset( $block->context['postId'] )\n\t? (int) $block->context['postId']\n\t: (int) get_queried_object_id();\n$storage_mode = '{{dataStorageMode}}';\n$persistence_policy = '{{persistencePolicy}}';\n\n{{phpPrefix}}_record_rendered_block_instance(\n\t(int) $post_id,\n\t'{{namespace}}/{{slugKebabCase}}',\n\t$resource_key\n);\n\n$notice_message = 'authenticated' === $persistence_policy\n\t? __( 'Sign in to persist this counter.', '{{textDomain}}' )\n\t: __( 'Public writes are temporarily unavailable.', '{{textDomain}}' );\n$context = array(\n\t'bootstrapReady' => false,\n\t'buttonLabel' => $button_label,\n\t'canWrite' => false,\n\t'client' => array(\n\t\t'writeExpiry' => 0,\n\t\t'writeNonce' => '',\n\t\t'writeToken' => '',\n\t),\n\t'count' => 0,\n\t'error' => '',\n\t'isBootstrapping' => false,\n\t'isLoading' => false,\n\t'isSaving' => false,\n\t'isVisible' => ! empty( $normalized['isVisible'] ),\n\t'persistencePolicy' => $persistence_policy,\n\t'postId' => (int) $post_id,\n\t'resourceKey' => $resource_key,\n\t'storage' => $storage_mode,\n);\n\n$wrapper_attributes = get_block_wrapper_attributes(\n\tarray(\n\t\t'data-wp-context' => wp_json_encode( $context ),\n\t\t'data-wp-interactive' => '{{slugKebabCase}}',\n\t\t'data-wp-init' => 'callbacks.init',\n\t\t'data-wp-run--mounted' => 'callbacks.mounted',\n\t)\n);\n?>\n\n<div <?php echo $wrapper_attributes; ?>>\n\t<div class=\"{{frontendCssClassName}}\">\n\t\t<p class=\"{{frontendCssClassName}}__content\" style=\"<?php echo esc_attr( 'text-align:' . $alignment ); ?>\">\n\t\t\t<?php echo esc_html( $content ); ?>\n\t\t</p>\n\t\t<p\n\t\t\tclass=\"{{frontendCssClassName}}__notice\"\n\t\t\tdata-wp-bind--hidden=\"!context.bootstrapReady || context.canWrite\"\n\t\t\thidden\n\t\t>\n\t\t\t<?php echo esc_html( $notice_message ); ?>\n\t\t</p>\n\t\t<p\n\t\t\tclass=\"{{frontendCssClassName}}__error\"\n\t\t\trole=\"status\"\n\t\t\taria-live=\"polite\"\n\t\t\taria-atomic=\"true\"\n\t\t\tdata-wp-bind--hidden=\"!context.error\"\n\t\t\tdata-wp-text=\"context.error\"\n\t\t\thidden\n\t\t></p>\n\t\t<?php if ( ! empty( $normalized['showCount'] ) ) : ?>\n\t\t\t<span\n\t\t\t\tclass=\"{{frontendCssClassName}}__count\"\n\t\t\t\trole=\"status\"\n\t\t\t\taria-live=\"polite\"\n\t\t\t\taria-atomic=\"true\"\n\t\t\t\tdata-wp-text=\"context.count\"\n\t\t\t>\n\t\t\t\t0\n\t\t\t</span>\n\t\t<?php endif; ?>\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tdisabled\n\t\t\tdata-wp-bind--disabled=\"!context.canWrite\"\n\t\t\tdata-wp-on--click=\"actions.increment\"\n\t\t>\n\t\t\t<?php echo esc_html( $button_label ); ?>\n\t\t</button>\n\t</div>\n</div>\n";
16
+ /**
17
+ * PHP helper template for persistence alternate render targets.
18
+ *
19
+ * @remarks Consumed by `buildPersistenceArtifacts()` for email, MJML,
20
+ * plain-text, and web render entries with block identity, namespace, text
21
+ * domain, storage, policy, frontend class, and title placeholders.
22
+ */
23
+ export declare const PERSISTENCE_RENDER_TARGETS_TEMPLATE = "<?php\n/**\n * Alternate render target helpers for the {{title}} block.\n *\n * @package {{pascalCase}}\n */\n\nif ( ! defined( 'ABSPATH' ) ) {\n\texit;\n}\n\nif ( ! function_exists( '{{phpPrefix}}_{{slugSnakeCase}}_build_render_context' ) ) {\n\tfunction {{phpPrefix}}_{{slugSnakeCase}}_build_render_context( $attributes, $content, $block ) {\n\t\t$validator_path = __DIR__ . '/typia-validator.php';\n\t\tif ( ! file_exists( $validator_path ) ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t$validator = require $validator_path;\n\t\tif ( ! is_object( $validator ) || ! method_exists( $validator, 'apply_defaults' ) || ! method_exists( $validator, 'validate' ) ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t$normalized = $validator->apply_defaults( is_array( $attributes ) ? $attributes : array() );\n\t\t$validation = $validator->validate( $normalized );\n\t\t$resource_key = isset( $normalized['resourceKey'] ) ? (string) $normalized['resourceKey'] : '';\n\n\t\tif ( empty( $validation['valid'] ) || '' === $resource_key ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t$alignment = isset( $normalized['alignment'] ) ? (string) $normalized['alignment'] : 'left';\n\t\t$button_label = isset( $normalized['buttonLabel'] ) ? (string) $normalized['buttonLabel'] : 'Persist Count';\n\t\t$rendered_content = isset( $normalized['content'] ) ? (string) $normalized['content'] : '';\n\t\t$post_id = is_object( $block ) && isset( $block->context['postId'] )\n\t\t\t? (int) $block->context['postId']\n\t\t\t: (int) get_queried_object_id();\n\t\t$storage_mode = '{{dataStorageMode}}';\n\t\t$persistence_policy = '{{persistencePolicy}}';\n\n\t\t{{phpPrefix}}_record_rendered_block_instance(\n\t\t\t(int) $post_id,\n\t\t\t'{{namespace}}/{{slugKebabCase}}',\n\t\t\t$resource_key\n\t\t);\n\n\t\t$notice_message = 'authenticated' === $persistence_policy\n\t\t\t? __( 'Sign in to persist this counter.', '{{textDomain}}' )\n\t\t\t: __( 'Public writes are temporarily unavailable.', '{{textDomain}}' );\n\t\t$web_context = array(\n\t\t\t'bootstrapReady' => false,\n\t\t\t'buttonLabel' => $button_label,\n\t\t\t'canWrite' => false,\n\t\t\t'client' => array(\n\t\t\t\t'writeExpiry' => 0,\n\t\t\t\t'writeNonce' => '',\n\t\t\t\t'writeToken' => '',\n\t\t\t),\n\t\t\t'count' => 0,\n\t\t\t'error' => '',\n\t\t\t'isBootstrapping' => false,\n\t\t\t'isLoading' => false,\n\t\t\t'isSaving' => false,\n\t\t\t'isVisible' => ! empty( $normalized['isVisible'] ),\n\t\t\t'persistencePolicy' => $persistence_policy,\n\t\t\t'postId' => (int) $post_id,\n\t\t\t'resourceKey' => $resource_key,\n\t\t\t'storage' => $storage_mode,\n\t\t);\n\n\t\treturn array(\n\t\t\t'alignment' => $alignment,\n\t\t\t'buttonLabel' => $button_label,\n\t\t\t'content' => $rendered_content,\n\t\t\t'frontendClassName' => '{{frontendCssClassName}}',\n\t\t\t'isVisible' => ! empty( $normalized['isVisible'] ),\n\t\t\t'normalized' => $normalized,\n\t\t\t'noticeMessage' => $notice_message,\n\t\t\t'postId' => (int) $post_id,\n\t\t\t'resourceKey' => $resource_key,\n\t\t\t'showCount' => ! empty( $normalized['showCount'] ),\n\t\t\t'title' => {{titleJson}},\n\t\t\t'webContext' => $web_context,\n\t\t);\n\t}\n}\n\nif ( ! function_exists( '{{phpPrefix}}_{{slugSnakeCase}}_render_target' ) ) {\n\tfunction {{phpPrefix}}_{{slugSnakeCase}}_render_target( string $target, $attributes, $content = '', $block = null ): string {\n\t\t$context = {{phpPrefix}}_{{slugSnakeCase}}_build_render_context( $attributes, $content, $block );\n\t\tif ( null === $context || empty( $context['isVisible'] ) ) {\n\t\t\treturn '';\n\t\t}\n\n\t\t$target_context = apply_filters(\n\t\t\t'{{phpPrefix}}_{{slugSnakeCase}}_render_target_context',\n\t\t\t$context,\n\t\t\t$target,\n\t\t\t$attributes,\n\t\t\t$block\n\t\t);\n\n\t\t$markup = '';\n\t\tswitch ( $target ) {\n\t\t\tcase 'email':\n\t\t\t\t$parts = array(\n\t\t\t\t\t'<div class=\"' . esc_attr( $target_context['frontendClassName'] ) . '-email\">',\n\t\t\t\t\t'<p style=\"text-align:' . esc_attr( $target_context['alignment'] ) . ';\">' . esc_html( $target_context['content'] ) . '</p>',\n\t\t\t\t);\n\t\t\t\tif ( ! empty( $target_context['showCount'] ) ) {\n\t\t\t\t\t$parts[] = '<p><strong>' . esc_html__( 'Count', '{{textDomain}}' ) . ':</strong> ' . esc_html( (string) $target_context['webContext']['count'] ) . '</p>';\n\t\t\t\t}\n\t\t\t\t$parts[] = '<p>' . esc_html( $target_context['noticeMessage'] ) . '</p>';\n\t\t\t\t$parts[] = '</div>';\n\t\t\t\t$markup = implode( '', $parts );\n\t\t\t\tbreak;\n\t\t\tcase 'mjml':\n\t\t\t\t$markup = '<mjml><mj-body><mj-section><mj-column><mj-text align=\"' . esc_attr( $target_context['alignment'] ) . '\">' . esc_html( $target_context['content'] ) . '</mj-text>';\n\t\t\t\tif ( ! empty( $target_context['showCount'] ) ) {\n\t\t\t\t\t$markup .= '<mj-text>' . esc_html__( 'Count', '{{textDomain}}' ) . ': ' . esc_html( (string) $target_context['webContext']['count'] ) . '</mj-text>';\n\t\t\t\t}\n\t\t\t\t$markup .= '<mj-text>' . esc_html( $target_context['noticeMessage'] ) . '</mj-text></mj-column></mj-section></mj-body></mjml>';\n\t\t\t\tbreak;\n\t\t\tcase 'plain-text':\n\t\t\t\t$markup = implode(\n\t\t\t\t\t\"\\n\",\n\t\t\t\t\tarray_filter(\n\t\t\t\t\t\tarray(\n\t\t\t\t\t\t\t$target_context['title'],\n\t\t\t\t\t\t\t$target_context['content'],\n\t\t\t\t\t\t\t! empty( $target_context['showCount'] )\n\t\t\t\t\t\t\t\t? sprintf( '%s: %s', __( 'Count', '{{textDomain}}' ), (string) $target_context['webContext']['count'] )\n\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t\t$target_context['noticeMessage'],\n\t\t\t\t\t\t),\n\t\t\t\t\t\tstatic fn( $value ) => is_string( $value ) && '' !== $value\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 'web':\n\t\t\tdefault:\n\t\t\t\t$wrapper_attributes = get_block_wrapper_attributes(\n\t\t\t\t\tarray(\n\t\t\t\t\t\t'data-wp-context' => wp_json_encode( $target_context['webContext'] ),\n\t\t\t\t\t\t'data-wp-interactive' => '{{slugKebabCase}}',\n\t\t\t\t\t\t'data-wp-init' => 'callbacks.init',\n\t\t\t\t\t\t'data-wp-run--mounted' => 'callbacks.mounted',\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t\tob_start();\n\t\t\t\t?>\n<div <?php echo $wrapper_attributes; ?>>\n\t<div class=\"{{frontendCssClassName}}\">\n\t\t<p class=\"{{frontendCssClassName}}__content\" style=\"<?php echo esc_attr( 'text-align:' . $target_context['alignment'] ); ?>\">\n\t\t\t<?php echo esc_html( $target_context['content'] ); ?>\n\t\t</p>\n\t\t<p\n\t\t\tclass=\"{{frontendCssClassName}}__notice\"\n\t\t\tdata-wp-bind--hidden=\"!context.bootstrapReady || context.canWrite\"\n\t\t\thidden\n\t\t>\n\t\t\t<?php echo esc_html( $target_context['noticeMessage'] ); ?>\n\t\t</p>\n\t\t<p\n\t\t\tclass=\"{{frontendCssClassName}}__error\"\n\t\t\trole=\"status\"\n\t\t\taria-live=\"polite\"\n\t\t\taria-atomic=\"true\"\n\t\t\tdata-wp-bind--hidden=\"!context.error\"\n\t\t\tdata-wp-text=\"context.error\"\n\t\t\thidden\n\t\t></p>\n\t\t<?php if ( ! empty( $target_context['showCount'] ) ) : ?>\n\t\t\t<span\n\t\t\t\tclass=\"{{frontendCssClassName}}__count\"\n\t\t\t\trole=\"status\"\n\t\t\t\taria-live=\"polite\"\n\t\t\t\taria-atomic=\"true\"\n\t\t\t\tdata-wp-text=\"context.count\"\n\t\t\t>\n\t\t\t\t0\n\t\t\t</span>\n\t\t<?php endif; ?>\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tdisabled\n\t\t\tdata-wp-bind--disabled=\"!context.canWrite\"\n\t\t\tdata-wp-on--click=\"actions.increment\"\n\t\t>\n\t\t\t<?php echo esc_html( $target_context['buttonLabel'] ); ?>\n\t\t</button>\n\t</div>\n</div>\n\t\t\t\t<?php\n\t\t\t\t$markup = (string) ob_get_clean();\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn apply_filters(\n\t\t\t'{{phpPrefix}}_{{slugSnakeCase}}_render_target_markup',\n\t\t\t$markup,\n\t\t\t$target,\n\t\t\t$target_context\n\t\t);\n\t}\n}\n";
@@ -0,0 +1,395 @@
1
+ /**
2
+ * SCSS template for persistence block editor and frontend styles.
3
+ *
4
+ * @remarks Consumed by `buildPersistenceArtifacts()` with
5
+ * `{{cssClassName}}` and `{{frontendCssClassName}}` scaffold placeholders.
6
+ */
7
+ export const PERSISTENCE_STYLE_TEMPLATE = `.{{cssClassName}} {
8
+ border: 1px solid #dcdcde;
9
+ border-radius: 12px;
10
+ padding: 16px;
11
+ background: #fff;
12
+
13
+ &__meta {
14
+ color: #50575e;
15
+ font-size: 13px;
16
+ margin: 8px 0 0;
17
+ }
18
+ }
19
+
20
+ .{{frontendCssClassName}} {
21
+ display: grid;
22
+ gap: 12px;
23
+ border: 1px solid #dcdcde;
24
+ border-radius: 12px;
25
+ padding: 16px;
26
+ background: #f6f7f7;
27
+
28
+ &__count {
29
+ display: inline-flex;
30
+ min-width: 3rem;
31
+ justify-content: center;
32
+ font-weight: 700;
33
+ }
34
+
35
+ &__notice,
36
+ &__error {
37
+ margin: 0;
38
+ font-size: 13px;
39
+ }
40
+
41
+ &__notice {
42
+ color: #50575e;
43
+ }
44
+
45
+ &__error {
46
+ color: #b32d2e;
47
+ }
48
+
49
+ button {
50
+ width: fit-content;
51
+ }
52
+ }
53
+ `;
54
+ /**
55
+ * Main PHP `render.php` template for the persistence block.
56
+ *
57
+ * @remarks Consumed by `buildPersistenceArtifacts()` with scaffold
58
+ * placeholders for block identity, namespace, text domain, storage mode,
59
+ * persistence policy, and frontend CSS class names.
60
+ */
61
+ export const PERSISTENCE_RENDER_TEMPLATE = `<?php
62
+ /**
63
+ * Dynamic render entry for the {{title}} block.
64
+ *
65
+ * @package {{pascalCase}}
66
+ */
67
+
68
+ if ( ! defined( 'ABSPATH' ) ) {
69
+ exit;
70
+ }
71
+
72
+ $validator_path = __DIR__ . '/typia-validator.php';
73
+ if ( ! file_exists( $validator_path ) ) {
74
+ return '';
75
+ }
76
+
77
+ $validator = require $validator_path;
78
+ if ( ! is_object( $validator ) || ! method_exists( $validator, 'apply_defaults' ) || ! method_exists( $validator, 'validate' ) ) {
79
+ return '';
80
+ }
81
+
82
+ $normalized = $validator->apply_defaults( is_array( $attributes ) ? $attributes : array() );
83
+ $validation = $validator->validate( $normalized );
84
+ $resource_key = isset( $normalized['resourceKey'] ) ? (string) $normalized['resourceKey'] : '';
85
+
86
+ if ( empty( $validation['valid'] ) || '' === $resource_key ) {
87
+ return '';
88
+ }
89
+
90
+ $alignment = isset( $normalized['alignment'] ) ? (string) $normalized['alignment'] : 'left';
91
+ $button_label = isset( $normalized['buttonLabel'] ) ? (string) $normalized['buttonLabel'] : 'Persist Count';
92
+ $content = isset( $normalized['content'] ) ? (string) $normalized['content'] : '';
93
+ $post_id = is_object( $block ) && isset( $block->context['postId'] )
94
+ ? (int) $block->context['postId']
95
+ : (int) get_queried_object_id();
96
+ $storage_mode = '{{dataStorageMode}}';
97
+ $persistence_policy = '{{persistencePolicy}}';
98
+
99
+ {{phpPrefix}}_record_rendered_block_instance(
100
+ (int) $post_id,
101
+ '{{namespace}}/{{slugKebabCase}}',
102
+ $resource_key
103
+ );
104
+
105
+ $notice_message = 'authenticated' === $persistence_policy
106
+ ? __( 'Sign in to persist this counter.', '{{textDomain}}' )
107
+ : __( 'Public writes are temporarily unavailable.', '{{textDomain}}' );
108
+ $context = array(
109
+ 'bootstrapReady' => false,
110
+ 'buttonLabel' => $button_label,
111
+ 'canWrite' => false,
112
+ 'client' => array(
113
+ 'writeExpiry' => 0,
114
+ 'writeNonce' => '',
115
+ 'writeToken' => '',
116
+ ),
117
+ 'count' => 0,
118
+ 'error' => '',
119
+ 'isBootstrapping' => false,
120
+ 'isLoading' => false,
121
+ 'isSaving' => false,
122
+ 'isVisible' => ! empty( $normalized['isVisible'] ),
123
+ 'persistencePolicy' => $persistence_policy,
124
+ 'postId' => (int) $post_id,
125
+ 'resourceKey' => $resource_key,
126
+ 'storage' => $storage_mode,
127
+ );
128
+
129
+ $wrapper_attributes = get_block_wrapper_attributes(
130
+ array(
131
+ 'data-wp-context' => wp_json_encode( $context ),
132
+ 'data-wp-interactive' => '{{slugKebabCase}}',
133
+ 'data-wp-init' => 'callbacks.init',
134
+ 'data-wp-run--mounted' => 'callbacks.mounted',
135
+ )
136
+ );
137
+ ?>
138
+
139
+ <div <?php echo $wrapper_attributes; ?>>
140
+ <div class="{{frontendCssClassName}}">
141
+ <p class="{{frontendCssClassName}}__content" style="<?php echo esc_attr( 'text-align:' . $alignment ); ?>">
142
+ <?php echo esc_html( $content ); ?>
143
+ </p>
144
+ <p
145
+ class="{{frontendCssClassName}}__notice"
146
+ data-wp-bind--hidden="!context.bootstrapReady || context.canWrite"
147
+ hidden
148
+ >
149
+ <?php echo esc_html( $notice_message ); ?>
150
+ </p>
151
+ <p
152
+ class="{{frontendCssClassName}}__error"
153
+ role="status"
154
+ aria-live="polite"
155
+ aria-atomic="true"
156
+ data-wp-bind--hidden="!context.error"
157
+ data-wp-text="context.error"
158
+ hidden
159
+ ></p>
160
+ <?php if ( ! empty( $normalized['showCount'] ) ) : ?>
161
+ <span
162
+ class="{{frontendCssClassName}}__count"
163
+ role="status"
164
+ aria-live="polite"
165
+ aria-atomic="true"
166
+ data-wp-text="context.count"
167
+ >
168
+ 0
169
+ </span>
170
+ <?php endif; ?>
171
+ <button
172
+ type="button"
173
+ disabled
174
+ data-wp-bind--disabled="!context.canWrite"
175
+ data-wp-on--click="actions.increment"
176
+ >
177
+ <?php echo esc_html( $button_label ); ?>
178
+ </button>
179
+ </div>
180
+ </div>
181
+ `;
182
+ /**
183
+ * PHP helper template for persistence alternate render targets.
184
+ *
185
+ * @remarks Consumed by `buildPersistenceArtifacts()` for email, MJML,
186
+ * plain-text, and web render entries with block identity, namespace, text
187
+ * domain, storage, policy, frontend class, and title placeholders.
188
+ */
189
+ export const PERSISTENCE_RENDER_TARGETS_TEMPLATE = `<?php
190
+ /**
191
+ * Alternate render target helpers for the {{title}} block.
192
+ *
193
+ * @package {{pascalCase}}
194
+ */
195
+
196
+ if ( ! defined( 'ABSPATH' ) ) {
197
+ exit;
198
+ }
199
+
200
+ if ( ! function_exists( '{{phpPrefix}}_{{slugSnakeCase}}_build_render_context' ) ) {
201
+ function {{phpPrefix}}_{{slugSnakeCase}}_build_render_context( $attributes, $content, $block ) {
202
+ $validator_path = __DIR__ . '/typia-validator.php';
203
+ if ( ! file_exists( $validator_path ) ) {
204
+ return null;
205
+ }
206
+
207
+ $validator = require $validator_path;
208
+ if ( ! is_object( $validator ) || ! method_exists( $validator, 'apply_defaults' ) || ! method_exists( $validator, 'validate' ) ) {
209
+ return null;
210
+ }
211
+
212
+ $normalized = $validator->apply_defaults( is_array( $attributes ) ? $attributes : array() );
213
+ $validation = $validator->validate( $normalized );
214
+ $resource_key = isset( $normalized['resourceKey'] ) ? (string) $normalized['resourceKey'] : '';
215
+
216
+ if ( empty( $validation['valid'] ) || '' === $resource_key ) {
217
+ return null;
218
+ }
219
+
220
+ $alignment = isset( $normalized['alignment'] ) ? (string) $normalized['alignment'] : 'left';
221
+ $button_label = isset( $normalized['buttonLabel'] ) ? (string) $normalized['buttonLabel'] : 'Persist Count';
222
+ $rendered_content = isset( $normalized['content'] ) ? (string) $normalized['content'] : '';
223
+ $post_id = is_object( $block ) && isset( $block->context['postId'] )
224
+ ? (int) $block->context['postId']
225
+ : (int) get_queried_object_id();
226
+ $storage_mode = '{{dataStorageMode}}';
227
+ $persistence_policy = '{{persistencePolicy}}';
228
+
229
+ {{phpPrefix}}_record_rendered_block_instance(
230
+ (int) $post_id,
231
+ '{{namespace}}/{{slugKebabCase}}',
232
+ $resource_key
233
+ );
234
+
235
+ $notice_message = 'authenticated' === $persistence_policy
236
+ ? __( 'Sign in to persist this counter.', '{{textDomain}}' )
237
+ : __( 'Public writes are temporarily unavailable.', '{{textDomain}}' );
238
+ $web_context = array(
239
+ 'bootstrapReady' => false,
240
+ 'buttonLabel' => $button_label,
241
+ 'canWrite' => false,
242
+ 'client' => array(
243
+ 'writeExpiry' => 0,
244
+ 'writeNonce' => '',
245
+ 'writeToken' => '',
246
+ ),
247
+ 'count' => 0,
248
+ 'error' => '',
249
+ 'isBootstrapping' => false,
250
+ 'isLoading' => false,
251
+ 'isSaving' => false,
252
+ 'isVisible' => ! empty( $normalized['isVisible'] ),
253
+ 'persistencePolicy' => $persistence_policy,
254
+ 'postId' => (int) $post_id,
255
+ 'resourceKey' => $resource_key,
256
+ 'storage' => $storage_mode,
257
+ );
258
+
259
+ return array(
260
+ 'alignment' => $alignment,
261
+ 'buttonLabel' => $button_label,
262
+ 'content' => $rendered_content,
263
+ 'frontendClassName' => '{{frontendCssClassName}}',
264
+ 'isVisible' => ! empty( $normalized['isVisible'] ),
265
+ 'normalized' => $normalized,
266
+ 'noticeMessage' => $notice_message,
267
+ 'postId' => (int) $post_id,
268
+ 'resourceKey' => $resource_key,
269
+ 'showCount' => ! empty( $normalized['showCount'] ),
270
+ 'title' => {{titleJson}},
271
+ 'webContext' => $web_context,
272
+ );
273
+ }
274
+ }
275
+
276
+ if ( ! function_exists( '{{phpPrefix}}_{{slugSnakeCase}}_render_target' ) ) {
277
+ function {{phpPrefix}}_{{slugSnakeCase}}_render_target( string $target, $attributes, $content = '', $block = null ): string {
278
+ $context = {{phpPrefix}}_{{slugSnakeCase}}_build_render_context( $attributes, $content, $block );
279
+ if ( null === $context || empty( $context['isVisible'] ) ) {
280
+ return '';
281
+ }
282
+
283
+ $target_context = apply_filters(
284
+ '{{phpPrefix}}_{{slugSnakeCase}}_render_target_context',
285
+ $context,
286
+ $target,
287
+ $attributes,
288
+ $block
289
+ );
290
+
291
+ $markup = '';
292
+ switch ( $target ) {
293
+ case 'email':
294
+ $parts = array(
295
+ '<div class="' . esc_attr( $target_context['frontendClassName'] ) . '-email">',
296
+ '<p style="text-align:' . esc_attr( $target_context['alignment'] ) . ';">' . esc_html( $target_context['content'] ) . '</p>',
297
+ );
298
+ if ( ! empty( $target_context['showCount'] ) ) {
299
+ $parts[] = '<p><strong>' . esc_html__( 'Count', '{{textDomain}}' ) . ':</strong> ' . esc_html( (string) $target_context['webContext']['count'] ) . '</p>';
300
+ }
301
+ $parts[] = '<p>' . esc_html( $target_context['noticeMessage'] ) . '</p>';
302
+ $parts[] = '</div>';
303
+ $markup = implode( '', $parts );
304
+ break;
305
+ case 'mjml':
306
+ $markup = '<mjml><mj-body><mj-section><mj-column><mj-text align="' . esc_attr( $target_context['alignment'] ) . '">' . esc_html( $target_context['content'] ) . '</mj-text>';
307
+ if ( ! empty( $target_context['showCount'] ) ) {
308
+ $markup .= '<mj-text>' . esc_html__( 'Count', '{{textDomain}}' ) . ': ' . esc_html( (string) $target_context['webContext']['count'] ) . '</mj-text>';
309
+ }
310
+ $markup .= '<mj-text>' . esc_html( $target_context['noticeMessage'] ) . '</mj-text></mj-column></mj-section></mj-body></mjml>';
311
+ break;
312
+ case 'plain-text':
313
+ $markup = implode(
314
+ "\\n",
315
+ array_filter(
316
+ array(
317
+ $target_context['title'],
318
+ $target_context['content'],
319
+ ! empty( $target_context['showCount'] )
320
+ ? sprintf( '%s: %s', __( 'Count', '{{textDomain}}' ), (string) $target_context['webContext']['count'] )
321
+ : null,
322
+ $target_context['noticeMessage'],
323
+ ),
324
+ static fn( $value ) => is_string( $value ) && '' !== $value
325
+ )
326
+ );
327
+ break;
328
+ case 'web':
329
+ default:
330
+ $wrapper_attributes = get_block_wrapper_attributes(
331
+ array(
332
+ 'data-wp-context' => wp_json_encode( $target_context['webContext'] ),
333
+ 'data-wp-interactive' => '{{slugKebabCase}}',
334
+ 'data-wp-init' => 'callbacks.init',
335
+ 'data-wp-run--mounted' => 'callbacks.mounted',
336
+ )
337
+ );
338
+ ob_start();
339
+ ?>
340
+ <div <?php echo $wrapper_attributes; ?>>
341
+ <div class="{{frontendCssClassName}}">
342
+ <p class="{{frontendCssClassName}}__content" style="<?php echo esc_attr( 'text-align:' . $target_context['alignment'] ); ?>">
343
+ <?php echo esc_html( $target_context['content'] ); ?>
344
+ </p>
345
+ <p
346
+ class="{{frontendCssClassName}}__notice"
347
+ data-wp-bind--hidden="!context.bootstrapReady || context.canWrite"
348
+ hidden
349
+ >
350
+ <?php echo esc_html( $target_context['noticeMessage'] ); ?>
351
+ </p>
352
+ <p
353
+ class="{{frontendCssClassName}}__error"
354
+ role="status"
355
+ aria-live="polite"
356
+ aria-atomic="true"
357
+ data-wp-bind--hidden="!context.error"
358
+ data-wp-text="context.error"
359
+ hidden
360
+ ></p>
361
+ <?php if ( ! empty( $target_context['showCount'] ) ) : ?>
362
+ <span
363
+ class="{{frontendCssClassName}}__count"
364
+ role="status"
365
+ aria-live="polite"
366
+ aria-atomic="true"
367
+ data-wp-text="context.count"
368
+ >
369
+ 0
370
+ </span>
371
+ <?php endif; ?>
372
+ <button
373
+ type="button"
374
+ disabled
375
+ data-wp-bind--disabled="!context.canWrite"
376
+ data-wp-on--click="actions.increment"
377
+ >
378
+ <?php echo esc_html( $target_context['buttonLabel'] ); ?>
379
+ </button>
380
+ </div>
381
+ </div>
382
+ <?php
383
+ $markup = (string) ob_get_clean();
384
+ break;
385
+ }
386
+
387
+ return apply_filters(
388
+ '{{phpPrefix}}_{{slugSnakeCase}}_render_target_markup',
389
+ $markup,
390
+ $target,
391
+ $target_context
392
+ );
393
+ }
394
+ }
395
+ `;
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
3
3
  import { readOptionalUtf8File } from "./fs-async.js";
4
+ import { safeJsonParse } from "./json-utils.js";
4
5
  /**
5
6
  * Resolve an existing workspace block inventory entry by slug.
6
7
  *
@@ -32,7 +33,10 @@ export async function readWorkspaceBlockJson(projectDir, blockSlug) {
32
33
  }
33
34
  let blockJson;
34
35
  try {
35
- blockJson = parseScaffoldBlockMetadata(JSON.parse(source));
36
+ blockJson = parseScaffoldBlockMetadata(safeJsonParse(source, {
37
+ context: "workspace block metadata",
38
+ filePath: blockJsonPath,
39
+ }));
36
40
  }
37
41
  catch (error) {
38
42
  throw new Error(error instanceof Error
@@ -90,6 +90,14 @@ const PATTERN_COLLISION_DESCRIPTOR = {
90
90
  label: "A pattern",
91
91
  relativePath: ({ slug }) => path.join("src", "patterns", `${slug}.php`),
92
92
  },
93
+ {
94
+ label: "A full pattern",
95
+ relativePath: ({ slug }) => path.join("src", "patterns", "full", `${slug}.php`),
96
+ },
97
+ {
98
+ label: "A section pattern",
99
+ relativePath: ({ slug }) => path.join("src", "patterns", "sections", `${slug}.php`),
100
+ },
93
101
  ],
94
102
  inventoryCollision: {
95
103
  entries: (inventory) => inventory.patterns,