@wp-typia/project-tools 0.16.10 → 0.16.12

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 (104) hide show
  1. package/README.md +9 -3
  2. package/dist/runtime/built-in-block-artifact-documents.d.ts +3 -0
  3. package/dist/runtime/built-in-block-artifact-documents.js +2 -0
  4. package/dist/runtime/built-in-block-artifact-types.d.ts +51 -0
  5. package/dist/runtime/built-in-block-artifact-types.js +304 -0
  6. package/dist/runtime/built-in-block-artifacts.js +4 -803
  7. package/dist/runtime/built-in-block-attribute-emitters.d.ts +71 -0
  8. package/dist/runtime/built-in-block-attribute-emitters.js +176 -0
  9. package/dist/runtime/built-in-block-attribute-specs.d.ts +38 -0
  10. package/dist/runtime/built-in-block-attribute-specs.js +358 -0
  11. package/dist/runtime/built-in-block-code-templates/basic.d.ts +4 -0
  12. package/dist/runtime/built-in-block-code-templates/basic.js +249 -0
  13. package/dist/runtime/built-in-block-code-templates/compound-child.d.ts +4 -0
  14. package/dist/runtime/built-in-block-code-templates/compound-child.js +138 -0
  15. package/dist/runtime/built-in-block-code-templates/compound-parent.d.ts +6 -0
  16. package/dist/runtime/built-in-block-code-templates/compound-parent.js +227 -0
  17. package/dist/runtime/built-in-block-code-templates/compound-persistence.d.ts +4 -0
  18. package/dist/runtime/built-in-block-code-templates/compound-persistence.js +478 -0
  19. package/dist/runtime/built-in-block-code-templates/compound.d.ts +3 -0
  20. package/dist/runtime/built-in-block-code-templates/compound.js +3 -0
  21. package/dist/runtime/built-in-block-code-templates/interactivity.d.ts +5 -0
  22. package/dist/runtime/built-in-block-code-templates/interactivity.js +547 -0
  23. package/dist/runtime/built-in-block-code-templates/persistence.d.ts +5 -0
  24. package/dist/runtime/built-in-block-code-templates/persistence.js +550 -0
  25. package/dist/runtime/built-in-block-code-templates/shared.d.ts +16 -0
  26. package/dist/runtime/built-in-block-code-templates/shared.js +53 -0
  27. package/dist/runtime/built-in-block-code-templates.d.ts +5 -32
  28. package/dist/runtime/built-in-block-code-templates.js +5 -2230
  29. package/dist/runtime/cli-add-block-config.d.ts +6 -0
  30. package/dist/runtime/cli-add-block-config.js +143 -0
  31. package/dist/runtime/cli-add-block-legacy-validator.d.ts +4 -0
  32. package/dist/runtime/cli-add-block-legacy-validator.js +168 -0
  33. package/dist/runtime/cli-add-block.js +3 -301
  34. package/dist/runtime/cli-add-workspace-assets.d.ts +38 -0
  35. package/dist/runtime/cli-add-workspace-assets.js +399 -0
  36. package/dist/runtime/cli-add-workspace.d.ts +2 -38
  37. package/dist/runtime/cli-add-workspace.js +5 -396
  38. package/dist/runtime/cli-doctor-environment.d.ts +12 -0
  39. package/dist/runtime/cli-doctor-environment.js +123 -0
  40. package/dist/runtime/cli-doctor-workspace.d.ts +14 -0
  41. package/dist/runtime/cli-doctor-workspace.js +296 -0
  42. package/dist/runtime/cli-doctor.d.ts +4 -2
  43. package/dist/runtime/cli-doctor.js +10 -405
  44. package/dist/runtime/cli-help.js +1 -1
  45. package/dist/runtime/cli-scaffold.js +1 -1
  46. package/dist/runtime/migration-command-surface.d.ts +67 -0
  47. package/dist/runtime/migration-command-surface.js +189 -0
  48. package/dist/runtime/migration-diff-rename.d.ts +13 -0
  49. package/dist/runtime/migration-diff-rename.js +192 -0
  50. package/dist/runtime/migration-diff-transform.d.ts +14 -0
  51. package/dist/runtime/migration-diff-transform.js +105 -0
  52. package/dist/runtime/migration-diff.js +12 -297
  53. package/dist/runtime/migration-generated-artifacts.d.ts +3 -0
  54. package/dist/runtime/migration-generated-artifacts.js +41 -0
  55. package/dist/runtime/migration-maintenance.d.ts +51 -0
  56. package/dist/runtime/migration-maintenance.js +380 -0
  57. package/dist/runtime/migration-planning.d.ts +23 -0
  58. package/dist/runtime/migration-planning.js +131 -0
  59. package/dist/runtime/migration-project-config-source.d.ts +6 -0
  60. package/dist/runtime/migration-project-config-source.js +424 -0
  61. package/dist/runtime/migration-project-layout-discovery.d.ts +61 -0
  62. package/dist/runtime/migration-project-layout-discovery.js +337 -0
  63. package/dist/runtime/migration-project-layout-paths.d.ts +135 -0
  64. package/dist/runtime/migration-project-layout-paths.js +288 -0
  65. package/dist/runtime/migration-project-layout.d.ts +3 -0
  66. package/dist/runtime/migration-project-layout.js +2 -0
  67. package/dist/runtime/migration-project-workspace.d.ts +47 -0
  68. package/dist/runtime/migration-project-workspace.js +212 -0
  69. package/dist/runtime/migration-project.d.ts +4 -94
  70. package/dist/runtime/migration-project.js +3 -1101
  71. package/dist/runtime/migration-render-diff-rule.d.ts +5 -0
  72. package/dist/runtime/migration-render-diff-rule.js +120 -0
  73. package/dist/runtime/migration-render-execution.d.ts +3 -0
  74. package/dist/runtime/migration-render-execution.js +428 -0
  75. package/dist/runtime/migration-render-generated.d.ts +27 -0
  76. package/dist/runtime/migration-render-generated.js +230 -0
  77. package/dist/runtime/migration-render-support.d.ts +3 -0
  78. package/dist/runtime/migration-render-support.js +16 -0
  79. package/dist/runtime/migration-render.d.ts +3 -33
  80. package/dist/runtime/migration-render.js +3 -789
  81. package/dist/runtime/migration-ui-capability.js +1 -1
  82. package/dist/runtime/migrations.d.ts +24 -118
  83. package/dist/runtime/migrations.js +12 -700
  84. package/dist/runtime/scaffold-bootstrap.d.ts +45 -0
  85. package/dist/runtime/scaffold-bootstrap.js +185 -0
  86. package/dist/runtime/scaffold-package-manager-files.d.ts +35 -0
  87. package/dist/runtime/scaffold-package-manager-files.js +79 -0
  88. package/dist/runtime/scaffold.d.ts +1 -12
  89. package/dist/runtime/scaffold.js +10 -393
  90. package/dist/runtime/template-source-contracts.d.ts +81 -0
  91. package/dist/runtime/template-source-contracts.js +1 -0
  92. package/dist/runtime/template-source-external.d.ts +21 -0
  93. package/dist/runtime/template-source-external.js +184 -0
  94. package/dist/runtime/template-source-locators.d.ts +4 -0
  95. package/dist/runtime/template-source-locators.js +72 -0
  96. package/dist/runtime/template-source-normalization.d.ts +7 -0
  97. package/dist/runtime/template-source-normalization.js +53 -0
  98. package/dist/runtime/template-source-remote.d.ts +23 -0
  99. package/dist/runtime/template-source-remote.js +336 -0
  100. package/dist/runtime/template-source-seeds.d.ts +12 -0
  101. package/dist/runtime/template-source-seeds.js +243 -0
  102. package/dist/runtime/template-source.d.ts +4 -86
  103. package/dist/runtime/template-source.js +9 -828
  104. package/package.json +5 -5
@@ -0,0 +1,550 @@
1
+ export const PERSISTENCE_EDIT_TEMPLATE = `import type { BlockEditProps } from '@wp-typia/block-types/blocks/registration';
2
+ import { __ } from '@wordpress/i18n';
3
+ import {
4
+ AlignmentToolbar,
5
+ BlockControls,
6
+ InspectorControls,
7
+ RichText,
8
+ useBlockProps,
9
+ } from '@wordpress/block-editor';
10
+ import {
11
+ Notice,
12
+ PanelBody,
13
+ TextControl,
14
+ } from '@wordpress/components';
15
+ import currentManifest from './manifest-document';
16
+ import {
17
+ InspectorFromManifest,
18
+ useEditorFields,
19
+ useTypedAttributeUpdater,
20
+ } from '@wp-typia/block-runtime/inspector';
21
+ import type { {{pascalCase}}Attributes } from './types';
22
+ import {
23
+ sanitize{{pascalCase}}Attributes,
24
+ validate{{pascalCase}}Attributes,
25
+ } from './validators';
26
+ import { useTypiaValidation } from './hooks';
27
+
28
+ type EditProps = BlockEditProps< {{pascalCase}}Attributes >;
29
+
30
+ export default function Edit( {
31
+ attributes,
32
+ setAttributes,
33
+ }: EditProps ) {
34
+ const editorFields = useEditorFields(
35
+ currentManifest,
36
+ {
37
+ manual: [ 'content', 'resourceKey' ],
38
+ labels: {
39
+ buttonLabel: __( 'Button Label', '{{textDomain}}' ),
40
+ resourceKey: __( 'Resource Key', '{{textDomain}}' ),
41
+ showCount: __( 'Show Count', '{{textDomain}}' ),
42
+ },
43
+ }
44
+ );
45
+ const { errorMessages, isValid } = useTypiaValidation(
46
+ attributes,
47
+ validate{{pascalCase}}Attributes
48
+ );
49
+ const validateEditorUpdate = (
50
+ nextAttributes: {{pascalCase}}Attributes
51
+ ) => {
52
+ try {
53
+ return {
54
+ data: sanitize{{pascalCase}}Attributes( nextAttributes ),
55
+ errors: [],
56
+ isValid: true as const,
57
+ };
58
+ } catch {
59
+ return validate{{pascalCase}}Attributes( nextAttributes );
60
+ }
61
+ };
62
+ const { updateField } = useTypedAttributeUpdater(
63
+ attributes,
64
+ setAttributes,
65
+ validateEditorUpdate
66
+ );
67
+ const alignmentValue = editorFields.getStringValue(
68
+ attributes,
69
+ 'alignment',
70
+ 'left'
71
+ );
72
+ const persistencePolicy = '{{persistencePolicy}}';
73
+ const persistencePolicyDescription = __(
74
+ {{persistencePolicyDescriptionJson}},
75
+ '{{textDomain}}'
76
+ );
77
+
78
+ return (
79
+ <>
80
+ <BlockControls>
81
+ <AlignmentToolbar
82
+ value={ alignmentValue }
83
+ onChange={ ( value ) =>
84
+ updateField(
85
+ 'alignment',
86
+ ( value || alignmentValue ) as NonNullable< {{pascalCase}}Attributes[ 'alignment' ] >
87
+ )
88
+ }
89
+ />
90
+ </BlockControls>
91
+ <InspectorControls>
92
+ <InspectorFromManifest
93
+ attributes={ attributes }
94
+ fieldLookup={ editorFields }
95
+ onChange={ updateField }
96
+ paths={ [ 'alignment', 'isVisible', 'showCount', 'buttonLabel' ] }
97
+ title={ __( 'Persistence Settings', '{{textDomain}}' ) }
98
+ >
99
+ <TextControl
100
+ label={ __( 'Resource Key', '{{textDomain}}' ) }
101
+ value={ attributes.resourceKey ?? '' }
102
+ onChange={ ( value ) => updateField( 'resourceKey', value ) }
103
+ help={ __( 'Stable persisted identifier used by the storage-backed counter endpoint.', '{{textDomain}}' ) }
104
+ />
105
+ <Notice status="info" isDismissible={ false }>
106
+ { __( 'Storage mode: {{dataStorageMode}}', '{{textDomain}}' ) }
107
+ </Notice>
108
+ <Notice status="info" isDismissible={ false }>
109
+ { __( 'Persistence policy: {{persistencePolicy}}', '{{textDomain}}' ) }
110
+ <br />
111
+ { persistencePolicyDescription }
112
+ </Notice>
113
+ <Notice status="info" isDismissible={ false }>
114
+ { __( 'Render mode: dynamic. \`render.php\` bootstraps durable post context, while fresh session-only write data is loaded from the dedicated \`/bootstrap\` endpoint after hydration.', '{{textDomain}}' ) }
115
+ </Notice>
116
+ </InspectorFromManifest>
117
+ { ! isValid && (
118
+ <PanelBody
119
+ title={ __( 'Validation Errors', '{{textDomain}}' ) }
120
+ initialOpen
121
+ >
122
+ { errorMessages.map( ( error, index ) => (
123
+ <Notice key={ index } status="error" isDismissible={ false }>
124
+ { error }
125
+ </Notice>
126
+ ) ) }
127
+ </PanelBody>
128
+ ) }
129
+ </InspectorControls>
130
+ <div
131
+ { ...useBlockProps( {
132
+ className: '{{cssClassName}}',
133
+ style: {
134
+ textAlign:
135
+ alignmentValue as NonNullable< {{pascalCase}}Attributes[ 'alignment' ] >,
136
+ },
137
+ } ) }
138
+ >
139
+ <RichText
140
+ tagName="p"
141
+ value={ attributes.content }
142
+ onChange={ ( value ) => updateField( 'content', value ) }
143
+ placeholder={ __( {{titleJson}} + ' persistence block', '{{textDomain}}' ) }
144
+ />
145
+ <p className="{{cssClassName}}__meta">
146
+ { __( 'Resource key:', '{{textDomain}}' ) } { attributes.resourceKey || '—' }
147
+ </p>
148
+ <p className="{{cssClassName}}__meta">
149
+ { __( 'Storage mode:', '{{textDomain}}' ) } {{dataStorageMode}}
150
+ </p>
151
+ <p className="{{cssClassName}}__meta">
152
+ { __( 'Persistence policy:', '{{textDomain}}' ) } {{persistencePolicy}}
153
+ </p>
154
+ { ! isValid && (
155
+ <Notice status="error" isDismissible={ false }>
156
+ <ul>
157
+ { errorMessages.map( ( error, index ) => <li key={ index }>{ error }</li> ) }
158
+ </ul>
159
+ </Notice>
160
+ ) }
161
+ </div>
162
+ </>
163
+ );
164
+ }
165
+ `;
166
+ export const PERSISTENCE_INDEX_TEMPLATE = `import {
167
+ \tregisterScaffoldBlockType,
168
+ \ttype BlockConfiguration,
169
+ } from '@wp-typia/block-types/blocks/registration';
170
+ import {
171
+ \tbuildScaffoldBlockRegistration,
172
+ \tparseScaffoldBlockMetadata,
173
+ } from '@wp-typia/block-runtime/blocks';
174
+
175
+ import Edit from './edit';
176
+ import Save from './save';
177
+ import metadata from './block-metadata';
178
+ import './style.scss';
179
+
180
+ import type { {{pascalCase}}Attributes } from './types';
181
+
182
+ const registration = buildScaffoldBlockRegistration(
183
+ \tparseScaffoldBlockMetadata<BlockConfiguration< {{pascalCase}}Attributes >>( metadata ),
184
+ \t{
185
+ \t\tedit: Edit,
186
+ \t\tsave: Save,
187
+ \t}
188
+ );
189
+
190
+ registerScaffoldBlockType(registration.name, registration.settings);
191
+ `;
192
+ export const PERSISTENCE_SAVE_TEMPLATE = `export default function Save() {
193
+ \t// This block is intentionally server-rendered. PHP bootstraps post context,
194
+ \t// storage-backed state, and write-policy data before the frontend hydrates.
195
+ \treturn null;
196
+ }
197
+ `;
198
+ export const PERSISTENCE_VALIDATORS_TEMPLATE = `import typia from 'typia';
199
+ import currentManifest from './manifest-defaults-document';
200
+ import type {
201
+ \t{{pascalCase}}Attributes,
202
+ \t{{pascalCase}}ValidationResult,
203
+ } from './types';
204
+ import { generateResourceKey } from '@wp-typia/block-runtime/identifiers';
205
+ import { createTemplateValidatorToolkit } from './validator-toolkit';
206
+
207
+ const scaffoldValidators = createTemplateValidatorToolkit< {{pascalCase}}Attributes >( {
208
+ \tassert: typia.createAssert< {{pascalCase}}Attributes >(),
209
+ \tclone: typia.misc.createClone< {{pascalCase}}Attributes >() as (
210
+ \t\tvalue: {{pascalCase}}Attributes,
211
+ \t) => {{pascalCase}}Attributes,
212
+ \tis: typia.createIs< {{pascalCase}}Attributes >(),
213
+ \tmanifest: currentManifest,
214
+ \tprune: typia.misc.createPrune< {{pascalCase}}Attributes >(),
215
+ \trandom: typia.createRandom< {{pascalCase}}Attributes >() as (
216
+ \t\t...args: unknown[]
217
+ \t) => {{pascalCase}}Attributes,
218
+ \tfinalize: ( normalized ) => ( {
219
+ \t\t...normalized,
220
+ \t\tresourceKey:
221
+ \t\t\tnormalized.resourceKey && normalized.resourceKey.length > 0
222
+ \t\t\t\t? normalized.resourceKey
223
+ \t\t\t\t: generateResourceKey( '{{slugKebabCase}}' ),
224
+ \t} ),
225
+ \tvalidate: typia.createValidate< {{pascalCase}}Attributes >(),
226
+ } );
227
+
228
+ export const validators = scaffoldValidators.validators;
229
+
230
+ export const validate{{pascalCase}}Attributes =
231
+ \tscaffoldValidators.validateAttributes as (
232
+ \t\tattributes: unknown
233
+ \t) => {{pascalCase}}ValidationResult;
234
+
235
+ export const sanitize{{pascalCase}}Attributes =
236
+ \tscaffoldValidators.sanitizeAttributes as (
237
+ \t\tattributes: Partial< {{pascalCase}}Attributes >
238
+ \t) => {{pascalCase}}Attributes;
239
+
240
+ export const createAttributeUpdater = scaffoldValidators.createAttributeUpdater;
241
+ `;
242
+ export const PERSISTENCE_INTERACTIVITY_TEMPLATE = `import { getContext, store } from '@wordpress/interactivity';
243
+ import { generatePublicWriteRequestId } from '@wp-typia/block-runtime/identifiers';
244
+
245
+ import { fetchBootstrap, fetchState, writeState } from './api';
246
+ import type {
247
+ \t{{pascalCase}}ClientState,
248
+ \t{{pascalCase}}Context,
249
+ \t{{pascalCase}}State,
250
+ } from './types';
251
+ import type {
252
+ \t{{pascalCase}}WriteStateRequest,
253
+ } from './api-types';
254
+
255
+ function hasExpiredPublicWriteToken(
256
+ \texpiresAt?: number
257
+ ): boolean {
258
+ \treturn (
259
+ \t\ttypeof expiresAt === 'number' &&
260
+ \t\texpiresAt > 0 &&
261
+ \t\tDate.now() >= expiresAt * 1000
262
+ \t);
263
+ }
264
+
265
+ function getWriteBlockedMessage(
266
+ \tcontext: {{pascalCase}}Context
267
+ ): string {
268
+ \treturn context.persistencePolicy === 'authenticated'
269
+ \t\t? 'Sign in to persist this counter.'
270
+ \t\t: 'Public writes are temporarily unavailable.';
271
+ }
272
+
273
+ const BOOTSTRAP_MAX_ATTEMPTS = 3;
274
+ const BOOTSTRAP_RETRY_DELAYS_MS = [ 250, 500 ];
275
+
276
+ async function waitForBootstrapRetry( delayMs: number ): Promise< void > {
277
+ \tawait new Promise( ( resolve ) => {
278
+ \t\tsetTimeout( resolve, delayMs );
279
+ \t} );
280
+ }
281
+
282
+ function getClientState(
283
+ \tcontext: {{pascalCase}}Context
284
+ ): {{pascalCase}}ClientState {
285
+ \tif ( context.client ) {
286
+ \t\treturn context.client;
287
+ \t}
288
+
289
+ \tcontext.client = {
290
+ \t\tbootstrapError: '',
291
+ \t\twriteExpiry: 0,
292
+ \t\twriteNonce: '',
293
+ \t\twriteToken: '',
294
+ \t};
295
+
296
+ \treturn context.client;
297
+ }
298
+
299
+ function clearBootstrapError(
300
+ \tcontext: {{pascalCase}}Context,
301
+ \tclientState: {{pascalCase}}ClientState
302
+ ): void {
303
+ \tif ( context.error === clientState.bootstrapError ) {
304
+ \t\tcontext.error = '';
305
+ \t}
306
+ \tclientState.bootstrapError = '';
307
+ }
308
+
309
+ function setBootstrapError(
310
+ \tcontext: {{pascalCase}}Context,
311
+ \tclientState: {{pascalCase}}ClientState,
312
+ \tmessage: string
313
+ ): void {
314
+ \tclientState.bootstrapError = message;
315
+ \tcontext.error = message;
316
+ }
317
+
318
+ const { actions, state } = store( '{{slugKebabCase}}', {
319
+ \tstate: {
320
+ \t\tisHydrated: false,
321
+ \t} as {{pascalCase}}State,
322
+
323
+ \tactions: {
324
+ \t\tasync loadState() {
325
+ \t\t\tconst context = getContext< {{pascalCase}}Context >();
326
+ \t\t\tif ( context.postId <= 0 || ! context.resourceKey ) {
327
+ \t\t\t\treturn;
328
+ \t\t\t}
329
+
330
+ \t\t\tcontext.isLoading = true;
331
+ \t\t\tcontext.error = '';
332
+
333
+ \t\t\ttry {
334
+ \t\t\t\tconst result = await fetchState( {
335
+ \t\t\t\t\tpostId: context.postId,
336
+ \t\t\t\t\tresourceKey: context.resourceKey,
337
+ \t\t\t\t}, {
338
+ \t\t\t\t\ttransportTarget: 'frontend',
339
+ \t\t\t\t} );
340
+ \t\t\t\tif ( ! result.isValid || ! result.data ) {
341
+ \t\t\t\t\tcontext.error = result.errors[ 0 ]?.expected ?? 'Unable to load counter';
342
+ \t\t\t\t\treturn;
343
+ \t\t\t\t}
344
+ \t\t\t\tcontext.count = result.data.count;
345
+ \t\t\t} catch ( error ) {
346
+ \t\t\t\tcontext.error =
347
+ \t\t\t\t\terror instanceof Error ? error.message : 'Unknown loading error';
348
+ \t\t\t} finally {
349
+ \t\t\t\tcontext.isLoading = false;
350
+ \t\t\t}
351
+ \t\t},
352
+ \t\tasync loadBootstrap() {
353
+ \t\t\tconst context = getContext< {{pascalCase}}Context >();
354
+ \t\t\tconst clientState = getClientState( context );
355
+ \t\t\tif ( context.postId <= 0 || ! context.resourceKey ) {
356
+ \t\t\t\tcontext.bootstrapReady = true;
357
+ \t\t\t\tcontext.canWrite = false;
358
+ \t\t\t\tclientState.bootstrapError = '';
359
+ \t\t\t\tclientState.writeExpiry = 0;
360
+ \t\t\t\tclientState.writeNonce = '';
361
+ \t\t\t\tclientState.writeToken = '';
362
+ \t\t\t\treturn;
363
+ \t\t\t}
364
+
365
+ \t\t\tcontext.isBootstrapping = true;
366
+
367
+ \t\t\tlet bootstrapSucceeded = false;
368
+ \t\t\tlet lastBootstrapError =
369
+ \t\t\t\t'Unable to initialize write access';
370
+ \t\t\tconst includePublicWriteCredentials = {{isPublicPersistencePolicy}};
371
+ \t\t\tconst includeRestNonce = {{isAuthenticatedPersistencePolicy}};
372
+
373
+ \t\t\tfor ( let attempt = 1; attempt <= BOOTSTRAP_MAX_ATTEMPTS; attempt += 1 ) {
374
+ \t\t\t\ttry {
375
+ \t\t\t\t\tconst result = await fetchBootstrap( {
376
+ \t\t\t\t\t\tpostId: context.postId,
377
+ \t\t\t\t\t\tresourceKey: context.resourceKey,
378
+ \t\t\t\t\t}, {
379
+ \t\t\t\t\t\ttransportTarget: 'frontend',
380
+ \t\t\t\t\t} );
381
+ \t\t\t\t\tif ( ! result.isValid || ! result.data ) {
382
+ \t\t\t\t\t\tlastBootstrapError =
383
+ \t\t\t\t\t\t\tresult.errors[ 0 ]?.expected ??
384
+ \t\t\t\t\t\t\t'Unable to initialize write access';
385
+ \t\t\t\t\t\tif ( attempt < BOOTSTRAP_MAX_ATTEMPTS ) {
386
+ \t\t\t\t\t\t\tawait waitForBootstrapRetry(
387
+ \t\t\t\t\t\t\t\tBOOTSTRAP_RETRY_DELAYS_MS[ attempt - 1 ] ?? 750
388
+ \t\t\t\t\t\t\t);
389
+ \t\t\t\t\t\t\tcontinue;
390
+ \t\t\t\t\t\t}
391
+ \t\t\t\t\t\tbreak;
392
+ \t\t\t\t\t}
393
+
394
+ \t\t\t\t\tclientState.writeExpiry =
395
+ \t\t\t\t\t\tincludePublicWriteCredentials &&
396
+ \t\t\t\t\t\t'publicWriteExpiresAt' in result.data &&
397
+ \t\t\t\t\t\ttypeof result.data.publicWriteExpiresAt === 'number' &&
398
+ \t\t\t\t\t\tresult.data.publicWriteExpiresAt > 0
399
+ \t\t\t\t\t\t\t? result.data.publicWriteExpiresAt
400
+ \t\t\t\t\t\t\t: 0;
401
+ \t\t\t\t\tclientState.writeToken =
402
+ \t\t\t\t\t\tincludePublicWriteCredentials &&
403
+ \t\t\t\t\t\t'publicWriteToken' in result.data &&
404
+ \t\t\t\t\t\ttypeof result.data.publicWriteToken === 'string' &&
405
+ \t\t\t\t\t\tresult.data.publicWriteToken.length > 0
406
+ \t\t\t\t\t\t\t? result.data.publicWriteToken
407
+ \t\t\t\t\t\t\t: '';
408
+ \t\t\t\t\tclientState.writeNonce =
409
+ \t\t\t\t\t\tincludeRestNonce &&
410
+ \t\t\t\t\t\t'restNonce' in result.data &&
411
+ \t\t\t\t\t\ttypeof result.data.restNonce === 'string' &&
412
+ \t\t\t\t\t\tresult.data.restNonce.length > 0
413
+ \t\t\t\t\t\t\t? result.data.restNonce
414
+ \t\t\t\t\t\t\t: '';
415
+ \t\t\t\t\tcontext.bootstrapReady = true;
416
+ \t\t\t\t\tcontext.canWrite =
417
+ \t\t\t\t\t\tresult.data.canWrite === true &&
418
+ \t\t\t\t\t\t(
419
+ \t\t\t\t\t\t\tcontext.persistencePolicy === 'authenticated'
420
+ \t\t\t\t\t\t\t\t? clientState.writeNonce.length > 0
421
+ \t\t\t\t\t\t\t\t: clientState.writeToken.length > 0 &&
422
+ \t\t\t\t\t\t\t\t\t! hasExpiredPublicWriteToken( clientState.writeExpiry )
423
+ \t\t\t\t\t\t);
424
+ \t\t\t\t\tclearBootstrapError( context, clientState );
425
+ \t\t\t\t\tbootstrapSucceeded = true;
426
+ \t\t\t\t\tbreak;
427
+ \t\t\t\t} catch ( error ) {
428
+ \t\t\t\t\tlastBootstrapError =
429
+ \t\t\t\t\t\terror instanceof Error ? error.message : 'Unknown bootstrap error';
430
+ \t\t\t\t\tif ( attempt < BOOTSTRAP_MAX_ATTEMPTS ) {
431
+ \t\t\t\t\t\tawait waitForBootstrapRetry(
432
+ \t\t\t\t\t\t\tBOOTSTRAP_RETRY_DELAYS_MS[ attempt - 1 ] ?? 750
433
+ \t\t\t\t\t\t);
434
+ \t\t\t\t\t\tcontinue;
435
+ \t\t\t\t\t}
436
+ \t\t\t\t\tbreak;
437
+ \t\t\t\t}
438
+ \t\t\t}
439
+
440
+ \t\t\tif ( ! bootstrapSucceeded ) {
441
+ \t\t\t\tcontext.bootstrapReady = false;
442
+ \t\t\t\tcontext.canWrite = false;
443
+ \t\t\t\tclientState.writeExpiry = 0;
444
+ \t\t\t\tclientState.writeNonce = '';
445
+ \t\t\t\tclientState.writeToken = '';
446
+ \t\t\t\tsetBootstrapError( context, clientState, lastBootstrapError );
447
+ \t\t\t}
448
+ \t\t\tcontext.isBootstrapping = false;
449
+ \t\t},
450
+ \t\tasync increment() {
451
+ \t\t\tconst context = getContext< {{pascalCase}}Context >();
452
+ \t\t\tconst clientState = getClientState( context );
453
+ \t\t\tif ( context.postId <= 0 || ! context.resourceKey ) {
454
+ \t\t\t\treturn;
455
+ \t\t\t}
456
+ \t\t\tif ( ! context.bootstrapReady ) {
457
+ \t\t\t\tawait actions.loadBootstrap();
458
+ \t\t\t}
459
+ \t\t\tif ( ! context.bootstrapReady ) {
460
+ \t\t\t\tcontext.error = 'Write access is still initializing.';
461
+ \t\t\t\treturn;
462
+ \t\t\t}
463
+ \t\t\tif (
464
+ \t\t\t\tcontext.persistencePolicy === 'public' &&
465
+ \t\t\t\thasExpiredPublicWriteToken( clientState.writeExpiry )
466
+ \t\t\t) {
467
+ \t\t\t\tawait actions.loadBootstrap();
468
+ \t\t\t}
469
+ \t\t\tif (
470
+ \t\t\t\tcontext.persistencePolicy === 'public' &&
471
+ \t\t\t\thasExpiredPublicWriteToken( clientState.writeExpiry )
472
+ \t\t\t) {
473
+ \t\t\t\tcontext.canWrite = false;
474
+ \t\t\t\tcontext.error = getWriteBlockedMessage( context );
475
+ \t\t\t\treturn;
476
+ \t\t\t}
477
+ \t\t\tif ( ! context.canWrite ) {
478
+ \t\t\t\tcontext.error = getWriteBlockedMessage( context );
479
+ \t\t\t\treturn;
480
+ \t\t\t}
481
+
482
+ \t\t\tcontext.isSaving = true;
483
+ \t\t\tcontext.error = '';
484
+
485
+ \t\t\ttry {
486
+ \t\t\t\tconst request = {
487
+ \t\t\t\t\tdelta: 1,
488
+ \t\t\t\t\tpostId: context.postId,
489
+ \t\t\t\t\tresourceKey: context.resourceKey,
490
+ \t\t\t\t} as {{pascalCase}}WriteStateRequest;
491
+ \t\t\t\tif ( {{isPublicPersistencePolicy}} ) {
492
+ \t\t\t\t\trequest.publicWriteRequestId =
493
+ \t\t\t\t\t\tgeneratePublicWriteRequestId() as {{pascalCase}}WriteStateRequest[ 'publicWriteRequestId' ];
494
+ \t\t\t\t\tif ( clientState.writeToken.length > 0 ) {
495
+ \t\t\t\t\t\trequest.publicWriteToken =
496
+ \t\t\t\t\t\t\tclientState.writeToken as {{pascalCase}}WriteStateRequest[ 'publicWriteToken' ];
497
+ \t\t\t\t\t}
498
+ \t\t\t\t}
499
+ \t\t\t\tconst result = await writeState( request, {
500
+ \t\t\t\t\trestNonce:
501
+ \t\t\t\t\t\tclientState.writeNonce.length > 0
502
+ \t\t\t\t\t\t\t? clientState.writeNonce
503
+ \t\t\t\t\t\t\t: undefined,
504
+ \t\t\t\t\ttransportTarget: 'frontend',
505
+ \t\t\t\t} );
506
+ \t\t\t\tif ( ! result.isValid || ! result.data ) {
507
+ \t\t\t\t\tcontext.error = result.errors[ 0 ]?.expected ?? 'Unable to update counter';
508
+ \t\t\t\t\treturn;
509
+ \t\t\t\t}
510
+ \t\t\t\tcontext.count = result.data.count;
511
+ \t\t\t\tcontext.storage = result.data.storage;
512
+ \t\t\t} catch ( error ) {
513
+ \t\t\t\tcontext.error =
514
+ \t\t\t\t\terror instanceof Error ? error.message : 'Unknown update error';
515
+ \t\t\t} finally {
516
+ \t\t\t\tcontext.isSaving = false;
517
+ \t\t\t}
518
+ \t\t},
519
+ \t},
520
+
521
+ \tcallbacks: {
522
+ \t\tinit() {
523
+ \t\t\tconst context = getContext< {{pascalCase}}Context >();
524
+ \t\t\tcontext.client = {
525
+ \t\t\t\tbootstrapError: '',
526
+ \t\t\t\twriteExpiry: 0,
527
+ \t\t\t\twriteNonce: '',
528
+ \t\t\t\twriteToken: '',
529
+ \t\t\t};
530
+ \t\t\tcontext.bootstrapReady = false;
531
+ \t\t\tcontext.canWrite = false;
532
+ \t\t\tcontext.count = 0;
533
+ \t\t\tcontext.error = '';
534
+ \t\t\tcontext.isBootstrapping = false;
535
+ \t\t\tcontext.isLoading = false;
536
+ \t\t\tcontext.isSaving = false;
537
+ \t\t},
538
+ \t\tmounted() {
539
+ \t\t\tstate.isHydrated = true;
540
+ \t\t\tif ( typeof document !== 'undefined' ) {
541
+ \t\t\t\tdocument.documentElement.dataset[ '{{slugCamelCase}}Hydrated' ] = 'true';
542
+ \t\t\t}
543
+ \t\t\tvoid Promise.allSettled( [
544
+ \t\t\t\tactions.loadState(),
545
+ \t\t\t\tactions.loadBootstrap(),
546
+ \t\t\t] );
547
+ \t\t},
548
+ \t},
549
+ } );
550
+ `;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Template for the shared validation hooks module used by built-in scaffolds.
3
+ */
4
+ export declare const SHARED_HOOKS_TEMPLATE = "import { useMemo } from '@wordpress/element';\n\nimport {\n\tcreateUseTypiaValidationHook,\n\tformatValidationError,\n\tformatValidationErrors,\n} from '@wp-typia/block-runtime/validation';\n\nexport {\n\tformatValidationError,\n\tformatValidationErrors,\n\ttype TypiaValidationError,\n\ttype ValidationResult,\n\ttype ValidationState,\n} from '@wp-typia/block-runtime/validation';\n\nexport const useTypiaValidation = createUseTypiaValidationHook( {\n\tuseMemo,\n} );\n";
5
+ /**
6
+ * Template for the generated block metadata wrapper around `block.json`.
7
+ */
8
+ export declare const BLOCK_METADATA_WRAPPER_TEMPLATE = "import rawMetadata from './block.json';\nimport { defineScaffoldBlockMetadata } from '@wp-typia/block-runtime/blocks';\n\nconst metadata = defineScaffoldBlockMetadata(rawMetadata);\n\nexport default metadata;\n";
9
+ /**
10
+ * Template for the generated manifest document wrapper around `typia.manifest.json`.
11
+ */
12
+ export declare const MANIFEST_DOCUMENT_WRAPPER_TEMPLATE = "import rawCurrentManifest from './typia.manifest.json';\nimport { defineManifestDocument } from '@wp-typia/block-runtime/editor';\n\nconst currentManifest = defineManifestDocument(rawCurrentManifest);\n\nexport default currentManifest;\n";
13
+ /**
14
+ * Template for the generated manifest-defaults wrapper used by validators.
15
+ */
16
+ export declare const MANIFEST_DEFAULTS_DOCUMENT_WRAPPER_TEMPLATE = "import rawCurrentManifest from './typia.manifest.json';\nimport { defineManifestDefaultsDocument } from '@wp-typia/block-runtime/defaults';\n\nconst currentManifest = defineManifestDefaultsDocument(rawCurrentManifest);\n\nexport default currentManifest;\n";
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Template for the shared validation hooks module used by built-in scaffolds.
3
+ */
4
+ export const SHARED_HOOKS_TEMPLATE = `import { useMemo } from '@wordpress/element';
5
+
6
+ import {
7
+ createUseTypiaValidationHook,
8
+ formatValidationError,
9
+ formatValidationErrors,
10
+ } from '@wp-typia/block-runtime/validation';
11
+
12
+ export {
13
+ formatValidationError,
14
+ formatValidationErrors,
15
+ type TypiaValidationError,
16
+ type ValidationResult,
17
+ type ValidationState,
18
+ } from '@wp-typia/block-runtime/validation';
19
+
20
+ export const useTypiaValidation = createUseTypiaValidationHook( {
21
+ useMemo,
22
+ } );
23
+ `;
24
+ /**
25
+ * Template for the generated block metadata wrapper around `block.json`.
26
+ */
27
+ export const BLOCK_METADATA_WRAPPER_TEMPLATE = `import rawMetadata from './block.json';
28
+ import { defineScaffoldBlockMetadata } from '@wp-typia/block-runtime/blocks';
29
+
30
+ const metadata = defineScaffoldBlockMetadata(rawMetadata);
31
+
32
+ export default metadata;
33
+ `;
34
+ /**
35
+ * Template for the generated manifest document wrapper around `typia.manifest.json`.
36
+ */
37
+ export const MANIFEST_DOCUMENT_WRAPPER_TEMPLATE = `import rawCurrentManifest from './typia.manifest.json';
38
+ import { defineManifestDocument } from '@wp-typia/block-runtime/editor';
39
+
40
+ const currentManifest = defineManifestDocument(rawCurrentManifest);
41
+
42
+ export default currentManifest;
43
+ `;
44
+ /**
45
+ * Template for the generated manifest-defaults wrapper used by validators.
46
+ */
47
+ export const MANIFEST_DEFAULTS_DOCUMENT_WRAPPER_TEMPLATE = `import rawCurrentManifest from './typia.manifest.json';
48
+ import { defineManifestDefaultsDocument } from '@wp-typia/block-runtime/defaults';
49
+
50
+ const currentManifest = defineManifestDefaultsDocument(rawCurrentManifest);
51
+
52
+ export default currentManifest;
53
+ `;