@wp-typia/project-tools 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +32 -0
  2. package/dist/runtime/cli-add.d.ts +38 -0
  3. package/dist/runtime/cli-add.js +561 -0
  4. package/dist/runtime/cli-core.d.ts +25 -0
  5. package/dist/runtime/cli-core.js +25 -0
  6. package/dist/runtime/cli-doctor.d.ts +34 -0
  7. package/dist/runtime/cli-doctor.js +131 -0
  8. package/dist/runtime/cli-help.d.ts +9 -0
  9. package/dist/runtime/cli-help.js +37 -0
  10. package/dist/runtime/cli-prompt.d.ts +21 -0
  11. package/dist/runtime/cli-prompt.js +53 -0
  12. package/dist/runtime/cli-scaffold.d.ts +79 -0
  13. package/dist/runtime/cli-scaffold.js +206 -0
  14. package/dist/runtime/cli-templates.d.ts +30 -0
  15. package/dist/runtime/cli-templates.js +61 -0
  16. package/dist/runtime/index.d.ts +9 -0
  17. package/dist/runtime/index.js +7 -0
  18. package/dist/runtime/json-utils.d.ts +10 -0
  19. package/dist/runtime/json-utils.js +12 -0
  20. package/dist/runtime/local-dev-presets.d.ts +26 -0
  21. package/dist/runtime/local-dev-presets.js +132 -0
  22. package/dist/runtime/metadata-analysis.d.ts +11 -0
  23. package/dist/runtime/metadata-analysis.js +285 -0
  24. package/dist/runtime/metadata-model.d.ts +84 -0
  25. package/dist/runtime/metadata-model.js +59 -0
  26. package/dist/runtime/metadata-parser.d.ts +53 -0
  27. package/dist/runtime/metadata-parser.js +794 -0
  28. package/dist/runtime/metadata-php-render.d.ts +29 -0
  29. package/dist/runtime/metadata-php-render.js +549 -0
  30. package/dist/runtime/metadata-projection.d.ts +7 -0
  31. package/dist/runtime/metadata-projection.js +233 -0
  32. package/dist/runtime/migration-constants.d.ts +15 -0
  33. package/dist/runtime/migration-constants.js +16 -0
  34. package/dist/runtime/migration-diff.d.ts +2 -0
  35. package/dist/runtime/migration-diff.js +537 -0
  36. package/dist/runtime/migration-fixtures.d.ts +8 -0
  37. package/dist/runtime/migration-fixtures.js +94 -0
  38. package/dist/runtime/migration-fuzz-plan.d.ts +2 -0
  39. package/dist/runtime/migration-fuzz-plan.js +50 -0
  40. package/dist/runtime/migration-manifest.d.ts +19 -0
  41. package/dist/runtime/migration-manifest.js +129 -0
  42. package/dist/runtime/migration-project.d.ts +94 -0
  43. package/dist/runtime/migration-project.js +1101 -0
  44. package/dist/runtime/migration-render.d.ts +11 -0
  45. package/dist/runtime/migration-render.js +741 -0
  46. package/dist/runtime/migration-risk.d.ts +4 -0
  47. package/dist/runtime/migration-risk.js +52 -0
  48. package/dist/runtime/migration-types.d.ts +249 -0
  49. package/dist/runtime/migration-types.js +1 -0
  50. package/dist/runtime/migration-ui-capability.d.ts +17 -0
  51. package/dist/runtime/migration-ui-capability.js +190 -0
  52. package/dist/runtime/migration-utils.d.ts +69 -0
  53. package/dist/runtime/migration-utils.js +246 -0
  54. package/dist/runtime/migrations.d.ts +249 -0
  55. package/dist/runtime/migrations.js +1061 -0
  56. package/dist/runtime/object-utils.d.ts +12 -0
  57. package/dist/runtime/object-utils.js +14 -0
  58. package/dist/runtime/package-managers.d.ts +28 -0
  59. package/dist/runtime/package-managers.js +156 -0
  60. package/dist/runtime/package-versions.d.ts +10 -0
  61. package/dist/runtime/package-versions.js +68 -0
  62. package/dist/runtime/scaffold-onboarding.d.ts +32 -0
  63. package/dist/runtime/scaffold-onboarding.js +99 -0
  64. package/dist/runtime/scaffold.d.ts +146 -0
  65. package/dist/runtime/scaffold.js +612 -0
  66. package/dist/runtime/schema-core.d.ts +267 -0
  67. package/dist/runtime/schema-core.js +597 -0
  68. package/dist/runtime/starter-manifests.d.ts +25 -0
  69. package/dist/runtime/starter-manifests.js +383 -0
  70. package/dist/runtime/string-case.d.ts +36 -0
  71. package/dist/runtime/string-case.js +69 -0
  72. package/dist/runtime/template-builtins.d.ts +38 -0
  73. package/dist/runtime/template-builtins.js +72 -0
  74. package/dist/runtime/template-defaults.d.ts +75 -0
  75. package/dist/runtime/template-defaults.js +65 -0
  76. package/dist/runtime/template-registry.d.ts +36 -0
  77. package/dist/runtime/template-registry.js +94 -0
  78. package/dist/runtime/template-render.d.ts +24 -0
  79. package/dist/runtime/template-render.js +113 -0
  80. package/dist/runtime/template-source.d.ts +71 -0
  81. package/dist/runtime/template-source.js +821 -0
  82. package/dist/runtime/typia-tags.d.ts +1 -0
  83. package/dist/runtime/typia-tags.js +1 -0
  84. package/package.json +79 -0
  85. package/templates/_shared/base/languages/.gitkeep +1 -0
  86. package/templates/_shared/base/package.json.mustache +41 -0
  87. package/templates/_shared/base/scripts/sync-types-to-block-json.ts.mustache +118 -0
  88. package/templates/_shared/base/src/hooks.ts.mustache +19 -0
  89. package/templates/_shared/base/src/validator-toolkit.ts.mustache +31 -0
  90. package/templates/_shared/base/tsconfig.json.mustache +21 -0
  91. package/templates/_shared/base/webpack.config.js.mustache +99 -0
  92. package/templates/_shared/base/{{slugKebabCase}}.php.mustache +53 -0
  93. package/templates/_shared/compound/core/package.json.mustache +45 -0
  94. package/templates/_shared/compound/core/scripts/add-compound-child.ts.mustache +559 -0
  95. package/templates/_shared/compound/core/scripts/block-config.ts.mustache +13 -0
  96. package/templates/_shared/compound/core/scripts/sync-types-to-block-json.ts.mustache +53 -0
  97. package/templates/_shared/compound/core/webpack.config.js.mustache +141 -0
  98. package/templates/_shared/compound/core/{{slugKebabCase}}.php.mustache +51 -0
  99. package/templates/_shared/compound/persistence/package.json.mustache +50 -0
  100. package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +59 -0
  101. package/templates/_shared/compound/persistence/scripts/sync-rest-contracts.ts.mustache +101 -0
  102. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api-types.ts.mustache +21 -0
  103. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api-validators.ts.mustache +32 -0
  104. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api.ts.mustache +68 -0
  105. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/block.json.mustache +52 -0
  106. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/data.ts.mustache +192 -0
  107. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/edit.tsx.mustache +123 -0
  108. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/hooks.ts.mustache +11 -0
  109. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/interactivity.ts.mustache +132 -0
  110. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/render.php.mustache +158 -0
  111. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/save.tsx.mustache +3 -0
  112. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/types.ts.mustache +56 -0
  113. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/validators.ts.mustache +32 -0
  114. package/templates/_shared/compound/persistence-auth/{{slugKebabCase}}.php.mustache +294 -0
  115. package/templates/_shared/compound/persistence-public/{{slugKebabCase}}.php.mustache +312 -0
  116. package/templates/_shared/migration-ui/common/src/admin/migration-dashboard.tsx +394 -0
  117. package/templates/_shared/migration-ui/common/src/migration-detector.ts +9 -0
  118. package/templates/_shared/migration-ui/common/src/migrations/helpers.ts +490 -0
  119. package/templates/_shared/migration-ui/common/src/migrations/index.ts +886 -0
  120. package/templates/_shared/persistence/auth/{{slugKebabCase}}.php.mustache +290 -0
  121. package/templates/_shared/persistence/core/package.json.mustache +46 -0
  122. package/templates/_shared/persistence/core/scripts/sync-rest-contracts.ts.mustache +113 -0
  123. package/templates/_shared/persistence/core/scripts/sync-types-to-block-json.ts.mustache +125 -0
  124. package/templates/_shared/persistence/core/src/api-types.ts.mustache +21 -0
  125. package/templates/_shared/persistence/core/src/api-validators.ts.mustache +32 -0
  126. package/templates/_shared/persistence/core/src/api.ts.mustache +68 -0
  127. package/templates/_shared/persistence/core/src/data.ts.mustache +192 -0
  128. package/templates/_shared/persistence/core/src/index.tsx.mustache +25 -0
  129. package/templates/_shared/persistence/core/src/interactivity.ts.mustache +134 -0
  130. package/templates/_shared/persistence/core/src/save.tsx.mustache +5 -0
  131. package/templates/_shared/persistence/core/src/validators.ts.mustache +32 -0
  132. package/templates/_shared/persistence/core/{{slugKebabCase}}.php.mustache +336 -0
  133. package/templates/_shared/persistence/public/{{slugKebabCase}}.php.mustache +308 -0
  134. package/templates/_shared/presets/test-preset/.wp-env.test.json.mustache +16 -0
  135. package/templates/_shared/presets/test-preset/playwright.config.ts.mustache +22 -0
  136. package/templates/_shared/presets/test-preset/scripts/wait-for-wp-env.mjs.mustache +102 -0
  137. package/templates/_shared/presets/test-preset/scripts/wp-env-utils.cjs.mustache +32 -0
  138. package/templates/_shared/presets/test-preset/tests/e2e/smoke.spec.ts.mustache +34 -0
  139. package/templates/_shared/presets/wp-env/.wp-env.json.mustache +16 -0
  140. package/templates/_shared/rest-helpers/auth/inc/rest-auth.php.mustache +37 -0
  141. package/templates/_shared/rest-helpers/public/inc/rest-public.php.mustache +314 -0
  142. package/templates/_shared/rest-helpers/shared/inc/rest-shared.php.mustache +58 -0
  143. package/templates/_shared/workspace/persistence-auth/inc/rest-auth.php.mustache +36 -0
  144. package/templates/_shared/workspace/persistence-auth/inc/rest-shared.php.mustache +55 -0
  145. package/templates/_shared/workspace/persistence-auth/server.php.mustache +237 -0
  146. package/templates/_shared/workspace/persistence-public/inc/rest-public.php.mustache +273 -0
  147. package/templates/_shared/workspace/persistence-public/inc/rest-shared.php.mustache +55 -0
  148. package/templates/_shared/workspace/persistence-public/server.php.mustache +252 -0
  149. package/templates/basic/src/block.json.mustache +51 -0
  150. package/templates/basic/src/edit.tsx.mustache +128 -0
  151. package/templates/basic/src/editor.scss.mustache +8 -0
  152. package/templates/basic/src/hooks.ts.mustache +18 -0
  153. package/templates/basic/src/index.tsx.mustache +45 -0
  154. package/templates/basic/src/save.tsx.mustache +30 -0
  155. package/templates/basic/src/style.scss.mustache +40 -0
  156. package/templates/basic/src/types.ts.mustache +56 -0
  157. package/templates/basic/src/validators.ts.mustache +26 -0
  158. package/templates/compound/src/blocks/{{slugKebabCase}}/block.json.mustache +37 -0
  159. package/templates/compound/src/blocks/{{slugKebabCase}}/children.ts.mustache +25 -0
  160. package/templates/compound/src/blocks/{{slugKebabCase}}/edit.tsx.mustache +93 -0
  161. package/templates/compound/src/blocks/{{slugKebabCase}}/hooks.ts.mustache +11 -0
  162. package/templates/compound/src/blocks/{{slugKebabCase}}/index.tsx.mustache +25 -0
  163. package/templates/compound/src/blocks/{{slugKebabCase}}/save.tsx.mustache +32 -0
  164. package/templates/compound/src/blocks/{{slugKebabCase}}/style.scss.mustache +31 -0
  165. package/templates/compound/src/blocks/{{slugKebabCase}}/types.ts.mustache +13 -0
  166. package/templates/compound/src/blocks/{{slugKebabCase}}/validators.ts.mustache +17 -0
  167. package/templates/compound/src/blocks/{{slugKebabCase}}-item/block.json.mustache +35 -0
  168. package/templates/compound/src/blocks/{{slugKebabCase}}-item/edit.tsx.mustache +50 -0
  169. package/templates/compound/src/blocks/{{slugKebabCase}}-item/hooks.ts.mustache +11 -0
  170. package/templates/compound/src/blocks/{{slugKebabCase}}-item/index.tsx.mustache +25 -0
  171. package/templates/compound/src/blocks/{{slugKebabCase}}-item/save.tsx.mustache +24 -0
  172. package/templates/compound/src/blocks/{{slugKebabCase}}-item/types.ts.mustache +12 -0
  173. package/templates/compound/src/blocks/{{slugKebabCase}}-item/validators.ts.mustache +17 -0
  174. package/templates/interactivity/package.json.mustache +42 -0
  175. package/templates/interactivity/src/block.json.mustache +73 -0
  176. package/templates/interactivity/src/edit.tsx.mustache +270 -0
  177. package/templates/interactivity/src/index.tsx.mustache +32 -0
  178. package/templates/interactivity/src/interactivity.ts.mustache +152 -0
  179. package/templates/interactivity/src/save.tsx.mustache +101 -0
  180. package/templates/interactivity/src/style.scss.mustache +60 -0
  181. package/templates/interactivity/src/types.ts.mustache +32 -0
  182. package/templates/interactivity/src/validators.ts.mustache +36 -0
  183. package/templates/persistence/src/block.json.mustache +52 -0
  184. package/templates/persistence/src/edit.tsx.mustache +165 -0
  185. package/templates/persistence/src/render.php.mustache +126 -0
  186. package/templates/persistence/src/style.scss.mustache +46 -0
  187. package/templates/persistence/src/types.ts.mustache +55 -0
@@ -0,0 +1,192 @@
1
+ import {
2
+ useEndpointMutation,
3
+ useEndpointQuery,
4
+ type UseEndpointMutationOptions,
5
+ type UseEndpointQueryOptions,
6
+ } from '@wp-typia/rest/react';
7
+
8
+ import type {
9
+ {{pascalCase}}StateQuery,
10
+ {{pascalCase}}StateResponse,
11
+ {{pascalCase}}WriteStateRequest,
12
+ } from './api-types';
13
+ import {
14
+ resolveRestNonce,
15
+ stateEndpoint,
16
+ writeStateEndpoint,
17
+ } from './api';
18
+
19
+ {{#isAuthenticatedPersistencePolicy}}
20
+ function buildWriteCallOptions( restNonce?: string ) {
21
+ const nonce = resolveRestNonce( restNonce );
22
+
23
+ return nonce
24
+ ? {
25
+ requestOptions: {
26
+ headers: {
27
+ 'X-WP-Nonce': nonce,
28
+ },
29
+ },
30
+ }
31
+ : undefined;
32
+ }
33
+ {{/isAuthenticatedPersistencePolicy}}
34
+
35
+ interface WriteStateMutationContext< Context > {
36
+ previous:
37
+ | {{pascalCase}}StateResponse
38
+ | undefined;
39
+ userContext: Context | undefined;
40
+ }
41
+
42
+ export interface Use{{pascalCase}}StateQueryOptions<
43
+ Selected = {{pascalCase}}StateResponse,
44
+ > extends Omit<
45
+ UseEndpointQueryOptions<
46
+ {{pascalCase}}StateQuery,
47
+ {{pascalCase}}StateResponse,
48
+ Selected
49
+ >,
50
+ 'resolveCallOptions'
51
+ > {
52
+ {{#isAuthenticatedPersistencePolicy}}
53
+ restNonce?: string;
54
+ {{/isAuthenticatedPersistencePolicy}}
55
+ }
56
+
57
+ export interface UseWrite{{pascalCase}}StateMutationOptions<
58
+ Context = unknown,
59
+ > extends Omit<
60
+ UseEndpointMutationOptions<
61
+ {{pascalCase}}WriteStateRequest,
62
+ {{pascalCase}}StateResponse,
63
+ WriteStateMutationContext< Context >
64
+ >,
65
+ 'invalidate' | 'onError' | 'onMutate'{{#isAuthenticatedPersistencePolicy}} | 'resolveCallOptions'{{/isAuthenticatedPersistencePolicy}}
66
+ > {
67
+ onError?: (
68
+ error: unknown,
69
+ request: {{pascalCase}}WriteStateRequest,
70
+ client: import('@wp-typia/rest/react').EndpointDataClient,
71
+ context: Context | undefined
72
+ ) => void | Promise<void>;
73
+ onMutate?: (
74
+ request: {{pascalCase}}WriteStateRequest,
75
+ client: import('@wp-typia/rest/react').EndpointDataClient
76
+ ) => Context | Promise<Context>;
77
+ {{#isAuthenticatedPersistencePolicy}}
78
+ restNonce?: string;
79
+ {{/isAuthenticatedPersistencePolicy}}
80
+ }
81
+
82
+ export function use{{pascalCase}}StateQuery<
83
+ Selected = {{pascalCase}}StateResponse,
84
+ >(
85
+ request: {{pascalCase}}StateQuery,
86
+ options: Use{{pascalCase}}StateQueryOptions< Selected > = {}
87
+ ) {
88
+ {{#isAuthenticatedPersistencePolicy}}
89
+ const {
90
+ restNonce,
91
+ ...queryOptions
92
+ } = options;
93
+
94
+ return useEndpointQuery( stateEndpoint, request, {
95
+ ...queryOptions,
96
+ resolveCallOptions: () => buildWriteCallOptions( restNonce ),
97
+ } );
98
+ {{/isAuthenticatedPersistencePolicy}}
99
+ {{^isAuthenticatedPersistencePolicy}}
100
+ return useEndpointQuery( stateEndpoint, request, options );
101
+ {{/isAuthenticatedPersistencePolicy}}
102
+ }
103
+
104
+ export function useWrite{{pascalCase}}StateMutation<
105
+ Context = unknown,
106
+ >(
107
+ options: UseWrite{{pascalCase}}StateMutationOptions< Context > = {}
108
+ ) {
109
+ const {
110
+ onError,
111
+ onMutate,
112
+ {{#isAuthenticatedPersistencePolicy}}
113
+ restNonce,
114
+ {{/isAuthenticatedPersistencePolicy}}
115
+ ...mutationOptions
116
+ } = options;
117
+
118
+ return useEndpointMutation( writeStateEndpoint, {
119
+ ...mutationOptions,
120
+ invalidate: ( _data, request ) => ( {
121
+ endpoint: stateEndpoint,
122
+ request: {
123
+ postId: request.postId,
124
+ resourceKey: request.resourceKey,
125
+ },
126
+ } ),
127
+ onError: async ( error, request, client, context ) => {
128
+ if ( context?.previous ) {
129
+ client.setData(
130
+ stateEndpoint,
131
+ {
132
+ postId: request.postId,
133
+ resourceKey: request.resourceKey,
134
+ },
135
+ context.previous
136
+ );
137
+ }
138
+
139
+ await onError?.( error, request, client, context?.userContext );
140
+ },
141
+ onMutate: async ( request, client ) => {
142
+ const queryRequest = {
143
+ postId: request.postId,
144
+ resourceKey: request.resourceKey,
145
+ } satisfies {{pascalCase}}StateQuery;
146
+ const previous = client.getData(
147
+ stateEndpoint,
148
+ queryRequest
149
+ );
150
+
151
+ if ( previous !== undefined ) {
152
+ client.setData(
153
+ stateEndpoint,
154
+ queryRequest,
155
+ ( current ) => {
156
+ if ( ! current ) {
157
+ return current;
158
+ }
159
+
160
+ return {
161
+ ...current,
162
+ count: Math.max(
163
+ 0,
164
+ current.count + ( request.delta ?? 1 )
165
+ ),
166
+ };
167
+ }
168
+ );
169
+ }
170
+
171
+ let userContext: Context | undefined;
172
+ try {
173
+ userContext = onMutate
174
+ ? await onMutate( request, client )
175
+ : undefined;
176
+ } catch ( error ) {
177
+ if ( previous !== undefined ) {
178
+ client.setData( stateEndpoint, queryRequest, previous );
179
+ }
180
+ throw error;
181
+ }
182
+
183
+ return {
184
+ previous,
185
+ userContext,
186
+ };
187
+ },
188
+ {{#isAuthenticatedPersistencePolicy}}
189
+ resolveCallOptions: () => buildWriteCallOptions( restNonce ),
190
+ {{/isAuthenticatedPersistencePolicy}}
191
+ } );
192
+ }
@@ -0,0 +1,25 @@
1
+ import { registerBlockType } from '@wordpress/blocks';
2
+ import type { BlockConfiguration } from '@wordpress/blocks';
3
+ import {
4
+ buildScaffoldBlockRegistration,
5
+ type ScaffoldBlockMetadata,
6
+ } from '@wp-typia/block-runtime/blocks';
7
+
8
+ import Edit from './edit';
9
+ import Save from './save';
10
+ import metadata from './block.json';
11
+ import './style.scss';
12
+
13
+ import type { {{pascalCase}}Attributes } from './types';
14
+
15
+ const registration = buildScaffoldBlockRegistration<
16
+ BlockConfiguration< {{pascalCase}}Attributes >
17
+ >( metadata as ScaffoldBlockMetadata, {
18
+ edit: Edit,
19
+ save: Save,
20
+ } );
21
+
22
+ registerBlockType< {{pascalCase}}Attributes >(
23
+ registration.name,
24
+ registration.settings
25
+ );
@@ -0,0 +1,134 @@
1
+ import { getContext, store } from '@wordpress/interactivity';
2
+ import { generatePublicWriteRequestId } from '@wp-typia/block-runtime/identifiers';
3
+
4
+ import { fetchState, writeState } from './api';
5
+ import type { {{pascalCase}}Context, {{pascalCase}}State } from './types';
6
+
7
+ function hasExpiredPublicWriteToken(
8
+ context: {{pascalCase}}Context
9
+ ): boolean {
10
+ return (
11
+ context.persistencePolicy === 'public' &&
12
+ typeof context.publicWriteExpiresAt === 'number' &&
13
+ context.publicWriteExpiresAt > 0 &&
14
+ Date.now() >= context.publicWriteExpiresAt * 1000
15
+ );
16
+ }
17
+
18
+ function getWriteBlockedMessage(
19
+ context: {{pascalCase}}Context
20
+ ): string {
21
+ return context.persistencePolicy === 'authenticated'
22
+ ? 'Sign in to persist this counter.'
23
+ : 'Reload the page to refresh this write token.';
24
+ }
25
+
26
+ const { actions, state } = store( '{{slugKebabCase}}', {
27
+ state: {
28
+ canWrite: false,
29
+ count: 0,
30
+ error: undefined,
31
+ isHydrated: false,
32
+ isLoading: false,
33
+ isSaving: false,
34
+ isVisible: true,
35
+ } as {{pascalCase}}State,
36
+
37
+ actions: {
38
+ async loadCounter() {
39
+ const context = getContext< {{pascalCase}}Context >();
40
+ if ( context.postId <= 0 || ! context.resourceKey ) {
41
+ return;
42
+ }
43
+
44
+ state.isLoading = true;
45
+ state.error = undefined;
46
+
47
+ try {
48
+ const result = await fetchState( {
49
+ postId: context.postId,
50
+ resourceKey: context.resourceKey,
51
+ } );
52
+ if ( ! result.isValid || ! result.data ) {
53
+ state.error = result.errors[ 0 ]?.expected ?? 'Unable to load counter';
54
+ return;
55
+ }
56
+ context.count = result.data.count;
57
+ context.storage = result.data.storage;
58
+ state.count = result.data.count;
59
+ } catch ( error ) {
60
+ state.error =
61
+ error instanceof Error ? error.message : 'Unknown loading error';
62
+ } finally {
63
+ state.isLoading = false;
64
+ }
65
+ },
66
+ async increment() {
67
+ const context = getContext< {{pascalCase}}Context >();
68
+ if ( context.postId <= 0 || ! context.resourceKey ) {
69
+ return;
70
+ }
71
+ if ( hasExpiredPublicWriteToken( context ) ) {
72
+ context.canWrite = false;
73
+ state.canWrite = false;
74
+ state.error = getWriteBlockedMessage( context );
75
+ return;
76
+ }
77
+ if ( ! context.canWrite || ! state.canWrite ) {
78
+ state.error = getWriteBlockedMessage( context );
79
+ return;
80
+ }
81
+
82
+ state.isSaving = true;
83
+ state.error = undefined;
84
+
85
+ try {
86
+ const result = await writeState( {
87
+ delta: 1,
88
+ postId: context.postId,
89
+ publicWriteRequestId:
90
+ context.persistencePolicy === 'public'
91
+ ? generatePublicWriteRequestId()
92
+ : undefined,
93
+ publicWriteToken:
94
+ context.persistencePolicy === 'public' &&
95
+ typeof context.publicWriteToken === 'string' &&
96
+ context.publicWriteToken.length > 0
97
+ ? context.publicWriteToken
98
+ : undefined,
99
+ resourceKey: context.resourceKey,
100
+ }, context.restNonce );
101
+ if ( ! result.isValid || ! result.data ) {
102
+ state.error = result.errors[ 0 ]?.expected ?? 'Unable to update counter';
103
+ return;
104
+ }
105
+ context.count = result.data.count;
106
+ context.storage = result.data.storage;
107
+ state.count = result.data.count;
108
+ } catch ( error ) {
109
+ state.error =
110
+ error instanceof Error ? error.message : 'Unknown update error';
111
+ } finally {
112
+ state.isSaving = false;
113
+ }
114
+ },
115
+ },
116
+
117
+ callbacks: {
118
+ init() {
119
+ const context = getContext< {{pascalCase}}Context >();
120
+ context.canWrite =
121
+ context.canWrite && ! hasExpiredPublicWriteToken( context );
122
+ state.canWrite = context.canWrite;
123
+ state.isVisible = context.isVisible;
124
+ state.count = context.count;
125
+ },
126
+ mounted() {
127
+ state.isHydrated = true;
128
+ if ( typeof document !== 'undefined' ) {
129
+ document.documentElement.dataset[ '{{slugCamelCase}}Hydrated' ] = 'true';
130
+ }
131
+ void actions.loadCounter();
132
+ },
133
+ },
134
+ } );
@@ -0,0 +1,5 @@
1
+ export default function Save() {
2
+ // This block is intentionally server-rendered. PHP bootstraps post context,
3
+ // storage-backed state, and write-policy data before the frontend hydrates.
4
+ return null;
5
+ }
@@ -0,0 +1,32 @@
1
+ import currentManifest from './typia.manifest.json';
2
+ import type {
3
+ {{pascalCase}}Attributes,
4
+ {{pascalCase}}ValidationResult,
5
+ } from './types';
6
+ import { generateResourceKey } from '@wp-typia/block-runtime/identifiers';
7
+ import { createTemplateValidatorToolkit } from './validator-toolkit';
8
+
9
+ const scaffoldValidators = createTemplateValidatorToolkit< {{pascalCase}}Attributes >( {
10
+ manifest: currentManifest,
11
+ finalize: ( normalized ) => ( {
12
+ ...normalized,
13
+ resourceKey:
14
+ normalized.resourceKey && normalized.resourceKey.length > 0
15
+ ? normalized.resourceKey
16
+ : generateResourceKey( '{{slugKebabCase}}' ),
17
+ } ),
18
+ } );
19
+
20
+ export const validators = scaffoldValidators.validators;
21
+
22
+ export const validate{{pascalCase}}Attributes =
23
+ scaffoldValidators.validateAttributes as (
24
+ attributes: unknown
25
+ ) => {{pascalCase}}ValidationResult;
26
+
27
+ export const sanitize{{pascalCase}}Attributes =
28
+ scaffoldValidators.sanitizeAttributes as (
29
+ attributes: Partial< {{pascalCase}}Attributes >
30
+ ) => {{pascalCase}}Attributes;
31
+
32
+ export const createAttributeUpdater = scaffoldValidators.createAttributeUpdater;