@wp-typia/project-tools 0.13.4 → 0.15.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 (31) hide show
  1. package/dist/runtime/scaffold-onboarding.js +8 -4
  2. package/dist/runtime/scaffold.d.ts +1 -0
  3. package/dist/runtime/scaffold.js +3 -0
  4. package/dist/runtime/schema-core.d.ts +1 -0
  5. package/dist/runtime/schema-core.js +188 -8
  6. package/package.json +1 -1
  7. package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +16 -0
  8. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api-types.ts.mustache +10 -0
  9. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api.ts.mustache +51 -38
  10. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/data.ts.mustache +76 -33
  11. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/interactivity.ts.mustache +206 -41
  12. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/render.php.mustache +37 -43
  13. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/transport.ts.mustache +254 -0
  14. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/types.ts.mustache +13 -8
  15. package/templates/_shared/compound/persistence-auth/{{slugKebabCase}}.php.mustache +139 -0
  16. package/templates/_shared/compound/persistence-public/{{slugKebabCase}}.php.mustache +159 -0
  17. package/templates/_shared/persistence/auth/{{slugKebabCase}}.php.mustache +139 -0
  18. package/templates/_shared/persistence/core/scripts/sync-rest-contracts.ts.mustache +16 -0
  19. package/templates/_shared/persistence/core/src/api-types.ts.mustache +10 -0
  20. package/templates/_shared/persistence/core/src/api.ts.mustache +51 -38
  21. package/templates/_shared/persistence/core/src/data.ts.mustache +76 -33
  22. package/templates/_shared/persistence/core/src/interactivity.ts.mustache +206 -43
  23. package/templates/_shared/persistence/core/src/transport.ts.mustache +254 -0
  24. package/templates/_shared/persistence/public/{{slugKebabCase}}.php.mustache +159 -0
  25. package/templates/_shared/rest-helpers/public/inc/rest-public.php.mustache +1 -1
  26. package/templates/_shared/workspace/persistence-auth/server.php.mustache +139 -0
  27. package/templates/_shared/workspace/persistence-public/inc/rest-public.php.mustache +1 -1
  28. package/templates/_shared/workspace/persistence-public/server.php.mustache +159 -0
  29. package/templates/persistence/src/edit.tsx.mustache +1 -1
  30. package/templates/persistence/src/render.php.mustache +37 -43
  31. package/templates/persistence/src/types.ts.mustache +13 -9
@@ -200,6 +200,126 @@ function {{phpPrefix}}_build_state_response( $post_id, $resource_key, $count ) {
200
200
  );
201
201
  }
202
202
 
203
+ function {{phpPrefix}}_block_tree_has_resource_key( $blocks, $block_name, $resource_key ) {
204
+ if ( ! is_array( $blocks ) ) {
205
+ return false;
206
+ }
207
+
208
+ foreach ( $blocks as $block ) {
209
+ if ( ! is_array( $block ) ) {
210
+ continue;
211
+ }
212
+
213
+ if ( (string) ( $block['blockName'] ?? '' ) === $block_name ) {
214
+ $attributes = isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : array();
215
+ $candidate_resource_key = array_key_exists( 'resourceKey', $attributes )
216
+ ? (string) $attributes['resourceKey']
217
+ : 'primary';
218
+ if ( (string) $resource_key === $candidate_resource_key ) {
219
+ return true;
220
+ }
221
+ }
222
+
223
+ if (
224
+ isset( $block['innerBlocks'] ) &&
225
+ {{phpPrefix}}_block_tree_has_resource_key( $block['innerBlocks'], $block_name, $resource_key )
226
+ ) {
227
+ return true;
228
+ }
229
+ }
230
+
231
+ return false;
232
+ }
233
+
234
+ function {{phpPrefix}}_get_rendered_block_instance_key( $post_id, $block_name, $resource_key ) {
235
+ return 'wpt_pri_' . md5( implode( '|', array( (string) $block_name, (int) $post_id, (string) $resource_key ) ) );
236
+ }
237
+
238
+ function {{phpPrefix}}_record_rendered_block_instance( $post_id, $block_name, $resource_key ) {
239
+ if ( $post_id <= 0 || '' === (string) $resource_key || '' === (string) $block_name ) {
240
+ return;
241
+ }
242
+
243
+ set_transient(
244
+ {{phpPrefix}}_get_rendered_block_instance_key( $post_id, $block_name, $resource_key ),
245
+ 1,
246
+ 5 * MINUTE_IN_SECONDS
247
+ );
248
+ }
249
+
250
+ function {{phpPrefix}}_has_rendered_block_instance( $post_id, $resource_key ) {
251
+ if ( $post_id <= 0 || '' === (string) $resource_key ) {
252
+ return false;
253
+ }
254
+
255
+ if (
256
+ false !== get_transient(
257
+ {{phpPrefix}}_get_rendered_block_instance_key(
258
+ $post_id,
259
+ '{{namespace}}/{{slugKebabCase}}',
260
+ $resource_key
261
+ )
262
+ )
263
+ ) {
264
+ return true;
265
+ }
266
+
267
+ $post = get_post( $post_id );
268
+ if ( ! ( $post instanceof WP_Post ) ) {
269
+ return false;
270
+ }
271
+
272
+ if ( ! is_post_publicly_viewable( $post ) ) {
273
+ return false;
274
+ }
275
+
276
+ return {{phpPrefix}}_block_tree_has_resource_key(
277
+ parse_blocks( (string) $post->post_content ),
278
+ '{{namespace}}/{{slugKebabCase}}',
279
+ (string) $resource_key
280
+ );
281
+ }
282
+
283
+ function {{phpPrefix}}_build_bootstrap_response( $post_id, $resource_key ) {
284
+ $response = array(
285
+ 'canWrite' => false,
286
+ );
287
+ $post = get_post( $post_id );
288
+
289
+ if (
290
+ $post_id <= 0 ||
291
+ ! ( $post instanceof WP_Post ) ||
292
+ ! is_post_publicly_viewable( $post ) ||
293
+ ! function_exists( '{{phpPrefix}}_create_public_write_token' ) ||
294
+ ! {{phpPrefix}}_has_rendered_block_instance( (int) $post_id, (string) $resource_key )
295
+ ) {
296
+ return $response;
297
+ }
298
+
299
+ $public_write = {{phpPrefix}}_create_public_write_token( (int) $post_id, (string) $resource_key );
300
+ if ( ! is_array( $public_write ) ) {
301
+ return $response;
302
+ }
303
+
304
+ $token = isset( $public_write['token'] ) ? (string) $public_write['token'] : '';
305
+ $expires_at = isset( $public_write['expiresAt'] ) ? (int) $public_write['expiresAt'] : 0;
306
+ $is_expired = $expires_at > 0 && $expires_at <= time();
307
+
308
+ if ( '' !== $token && $expires_at > 0 && ! $is_expired ) {
309
+ $response['publicWriteToken'] = $token;
310
+ $response['canWrite'] = true;
311
+ }
312
+
313
+ if ( $expires_at > 0 ) {
314
+ $response['publicWriteExpiresAt'] = $expires_at;
315
+ if ( $is_expired ) {
316
+ $response['canWrite'] = false;
317
+ }
318
+ }
319
+
320
+ return $response;
321
+ }
322
+
203
323
  // Route handlers are the main product-level extension point for request/response shaping.
204
324
  function {{phpPrefix}}_handle_get_state( WP_REST_Request $request ) {
205
325
  $payload = {{phpPrefix}}_validate_and_sanitize_request(
@@ -226,6 +346,33 @@ function {{phpPrefix}}_handle_get_state( WP_REST_Request $request ) {
226
346
  );
227
347
  }
228
348
 
349
+ function {{phpPrefix}}_handle_get_bootstrap( WP_REST_Request $request ) {
350
+ $payload = {{phpPrefix}}_validate_and_sanitize_request(
351
+ array(
352
+ 'postId' => $request->get_param( 'postId' ),
353
+ 'resourceKey' => $request->get_param( 'resourceKey' ),
354
+ ),
355
+ {{phpPrefix}}_get_rest_build_dir(),
356
+ 'bootstrap-query',
357
+ 'query'
358
+ );
359
+
360
+ if ( is_wp_error( $payload ) ) {
361
+ return $payload;
362
+ }
363
+
364
+ $response = rest_ensure_response(
365
+ {{phpPrefix}}_build_bootstrap_response(
366
+ (int) $payload['postId'],
367
+ (string) $payload['resourceKey']
368
+ )
369
+ );
370
+
371
+ $response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0, s-maxage=0' );
372
+ $response->header( 'Pragma', 'no-cache' );
373
+ return $response;
374
+ }
375
+
229
376
  function {{phpPrefix}}_handle_write_state( WP_REST_Request $request ) {
230
377
  $payload = {{phpPrefix}}_validate_and_sanitize_request(
231
378
  $request->get_json_params(),
@@ -290,6 +437,18 @@ function {{phpPrefix}}_register_routes() {
290
437
  ),
291
438
  )
292
439
  );
440
+
441
+ register_rest_route(
442
+ '{{namespace}}/v1',
443
+ '/{{slugKebabCase}}/bootstrap',
444
+ array(
445
+ array(
446
+ 'methods' => WP_REST_Server::READABLE,
447
+ 'callback' => '{{phpPrefix}}_handle_get_bootstrap',
448
+ 'permission_callback' => '__return_true',
449
+ ),
450
+ )
451
+ );
293
452
  }
294
453
 
295
454
  function {{phpPrefix}}_register_block() {
@@ -234,7 +234,7 @@ function {{phpPrefix}}_verify_public_write_token( $token, $post_id, $resource_ke
234
234
  if ( time() > $expires_at ) {
235
235
  return new WP_Error(
236
236
  'rest_forbidden',
237
- 'The public write token has expired. Reload the page and try again.',
237
+ 'The public write token has expired. Refresh write access and try again.',
238
238
  array( 'status' => 403 )
239
239
  );
240
240
  }
@@ -158,6 +158,105 @@ function {{phpPrefix}}_build_state_response( $post_id, $resource_key, $count ) {
158
158
  );
159
159
  }
160
160
 
161
+ function {{phpPrefix}}_block_tree_has_resource_key( $blocks, $block_name, $resource_key ) {
162
+ if ( ! is_array( $blocks ) ) {
163
+ return false;
164
+ }
165
+
166
+ foreach ( $blocks as $block ) {
167
+ if ( ! is_array( $block ) ) {
168
+ continue;
169
+ }
170
+
171
+ if ( (string) ( $block['blockName'] ?? '' ) === $block_name ) {
172
+ $attributes = isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : array();
173
+ $candidate_resource_key = array_key_exists( 'resourceKey', $attributes )
174
+ ? (string) $attributes['resourceKey']
175
+ : 'primary';
176
+ if ( (string) $resource_key === $candidate_resource_key ) {
177
+ return true;
178
+ }
179
+ }
180
+
181
+ if (
182
+ isset( $block['innerBlocks'] ) &&
183
+ {{phpPrefix}}_block_tree_has_resource_key( $block['innerBlocks'], $block_name, $resource_key )
184
+ ) {
185
+ return true;
186
+ }
187
+ }
188
+
189
+ return false;
190
+ }
191
+
192
+ function {{phpPrefix}}_get_rendered_block_instance_key( $post_id, $block_name, $resource_key ) {
193
+ return 'wpt_pri_' . md5( implode( '|', array( (string) $block_name, (int) $post_id, (string) $resource_key ) ) );
194
+ }
195
+
196
+ function {{phpPrefix}}_record_rendered_block_instance( $post_id, $block_name, $resource_key ) {
197
+ if ( $post_id <= 0 || '' === (string) $resource_key || '' === (string) $block_name ) {
198
+ return;
199
+ }
200
+
201
+ set_transient(
202
+ {{phpPrefix}}_get_rendered_block_instance_key( $post_id, $block_name, $resource_key ),
203
+ 1,
204
+ 5 * MINUTE_IN_SECONDS
205
+ );
206
+ }
207
+
208
+ function {{phpPrefix}}_has_rendered_block_instance( $post_id, $resource_key ) {
209
+ if ( $post_id <= 0 || '' === (string) $resource_key ) {
210
+ return false;
211
+ }
212
+
213
+ if (
214
+ false !== get_transient(
215
+ {{phpPrefix}}_get_rendered_block_instance_key(
216
+ $post_id,
217
+ '{{namespace}}/{{slugKebabCase}}',
218
+ $resource_key
219
+ )
220
+ )
221
+ ) {
222
+ return true;
223
+ }
224
+
225
+ $post = get_post( $post_id );
226
+ if ( ! ( $post instanceof WP_Post ) ) {
227
+ return false;
228
+ }
229
+
230
+ return {{phpPrefix}}_block_tree_has_resource_key(
231
+ parse_blocks( (string) $post->post_content ),
232
+ '{{namespace}}/{{slugKebabCase}}',
233
+ (string) $resource_key
234
+ );
235
+ }
236
+
237
+ function {{phpPrefix}}_build_bootstrap_response( $post_id, $resource_key ) {
238
+ $post = get_post( $post_id );
239
+ $can_read_post =
240
+ $post instanceof WP_Post &&
241
+ (
242
+ is_post_publicly_viewable( $post ) ||
243
+ current_user_can( 'read_post', $post->ID )
244
+ );
245
+ $can_write = $post_id > 0 &&
246
+ is_user_logged_in() &&
247
+ $can_read_post &&
248
+ {{phpPrefix}}_has_rendered_block_instance( (int) $post_id, (string) $resource_key );
249
+ $response = array(
250
+ 'canWrite' => $can_write,
251
+ );
252
+
253
+ if ( $can_write ) {
254
+ $response['restNonce'] = wp_create_nonce( 'wp_rest' );
255
+ }
256
+
257
+ return $response;
258
+ }
259
+
161
260
  function {{phpPrefix}}_handle_get_state( WP_REST_Request $request ) {
162
261
  $payload = {{phpPrefix}}_validate_and_sanitize_request(
163
262
  array(
@@ -183,6 +282,34 @@ function {{phpPrefix}}_handle_get_state( WP_REST_Request $request ) {
183
282
  );
184
283
  }
185
284
 
285
+ function {{phpPrefix}}_handle_get_bootstrap( WP_REST_Request $request ) {
286
+ $payload = {{phpPrefix}}_validate_and_sanitize_request(
287
+ array(
288
+ 'postId' => $request->get_param( 'postId' ),
289
+ 'resourceKey' => $request->get_param( 'resourceKey' ),
290
+ ),
291
+ {{phpPrefix}}_get_rest_build_dir(),
292
+ 'bootstrap-query',
293
+ 'query'
294
+ );
295
+
296
+ if ( is_wp_error( $payload ) ) {
297
+ return $payload;
298
+ }
299
+
300
+ $response = rest_ensure_response(
301
+ {{phpPrefix}}_build_bootstrap_response(
302
+ (int) $payload['postId'],
303
+ (string) $payload['resourceKey']
304
+ )
305
+ );
306
+
307
+ $response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0, s-maxage=0' );
308
+ $response->header( 'Pragma', 'no-cache' );
309
+ $response->header( 'Vary', 'Cookie' );
310
+ return $response;
311
+ }
312
+
186
313
  function {{phpPrefix}}_handle_write_state( WP_REST_Request $request ) {
187
314
  $payload = {{phpPrefix}}_validate_and_sanitize_request(
188
315
  $request->get_json_params(),
@@ -231,6 +358,18 @@ function {{phpPrefix}}_register_routes() {
231
358
  ),
232
359
  )
233
360
  );
361
+
362
+ register_rest_route(
363
+ '{{namespace}}/v1',
364
+ '/{{slugKebabCase}}/bootstrap',
365
+ array(
366
+ array(
367
+ 'methods' => WP_REST_Server::READABLE,
368
+ 'callback' => '{{phpPrefix}}_handle_get_bootstrap',
369
+ 'permission_callback' => '__return_true',
370
+ ),
371
+ )
372
+ );
234
373
  }
235
374
 
236
375
  add_action( 'init', '{{phpPrefix}}_ensure_storage_installed' );
@@ -226,7 +226,7 @@ function {{phpPrefix}}_verify_public_write_token( $token, $post_id, $resource_ke
226
226
  if ( time() > (int) ( $payload['exp'] ?? 0 ) ) {
227
227
  return new WP_Error(
228
228
  'rest_forbidden',
229
- 'The public write token has expired. Reload the page and try again.',
229
+ 'The public write token has expired. Refresh write access and try again.',
230
230
  array( 'status' => 403 )
231
231
  );
232
232
  }
@@ -158,6 +158,126 @@ function {{phpPrefix}}_build_state_response( $post_id, $resource_key, $count ) {
158
158
  );
159
159
  }
160
160
 
161
+ function {{phpPrefix}}_block_tree_has_resource_key( $blocks, $block_name, $resource_key ) {
162
+ if ( ! is_array( $blocks ) ) {
163
+ return false;
164
+ }
165
+
166
+ foreach ( $blocks as $block ) {
167
+ if ( ! is_array( $block ) ) {
168
+ continue;
169
+ }
170
+
171
+ if ( (string) ( $block['blockName'] ?? '' ) === $block_name ) {
172
+ $attributes = isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : array();
173
+ $candidate_resource_key = array_key_exists( 'resourceKey', $attributes )
174
+ ? (string) $attributes['resourceKey']
175
+ : 'primary';
176
+ if ( (string) $resource_key === $candidate_resource_key ) {
177
+ return true;
178
+ }
179
+ }
180
+
181
+ if (
182
+ isset( $block['innerBlocks'] ) &&
183
+ {{phpPrefix}}_block_tree_has_resource_key( $block['innerBlocks'], $block_name, $resource_key )
184
+ ) {
185
+ return true;
186
+ }
187
+ }
188
+
189
+ return false;
190
+ }
191
+
192
+ function {{phpPrefix}}_get_rendered_block_instance_key( $post_id, $block_name, $resource_key ) {
193
+ return 'wpt_pri_' . md5( implode( '|', array( (string) $block_name, (int) $post_id, (string) $resource_key ) ) );
194
+ }
195
+
196
+ function {{phpPrefix}}_record_rendered_block_instance( $post_id, $block_name, $resource_key ) {
197
+ if ( $post_id <= 0 || '' === (string) $resource_key || '' === (string) $block_name ) {
198
+ return;
199
+ }
200
+
201
+ set_transient(
202
+ {{phpPrefix}}_get_rendered_block_instance_key( $post_id, $block_name, $resource_key ),
203
+ 1,
204
+ 5 * MINUTE_IN_SECONDS
205
+ );
206
+ }
207
+
208
+ function {{phpPrefix}}_has_rendered_block_instance( $post_id, $resource_key ) {
209
+ if ( $post_id <= 0 || '' === (string) $resource_key ) {
210
+ return false;
211
+ }
212
+
213
+ if (
214
+ false !== get_transient(
215
+ {{phpPrefix}}_get_rendered_block_instance_key(
216
+ $post_id,
217
+ '{{namespace}}/{{slugKebabCase}}',
218
+ $resource_key
219
+ )
220
+ )
221
+ ) {
222
+ return true;
223
+ }
224
+
225
+ $post = get_post( $post_id );
226
+ if ( ! ( $post instanceof WP_Post ) ) {
227
+ return false;
228
+ }
229
+
230
+ if ( ! is_post_publicly_viewable( $post ) ) {
231
+ return false;
232
+ }
233
+
234
+ return {{phpPrefix}}_block_tree_has_resource_key(
235
+ parse_blocks( (string) $post->post_content ),
236
+ '{{namespace}}/{{slugKebabCase}}',
237
+ (string) $resource_key
238
+ );
239
+ }
240
+
241
+ function {{phpPrefix}}_build_bootstrap_response( $post_id, $resource_key ) {
242
+ $response = array(
243
+ 'canWrite' => false,
244
+ );
245
+ $post = get_post( $post_id );
246
+
247
+ if (
248
+ $post_id <= 0 ||
249
+ ! ( $post instanceof WP_Post ) ||
250
+ ! is_post_publicly_viewable( $post ) ||
251
+ ! function_exists( '{{phpPrefix}}_create_public_write_token' ) ||
252
+ ! {{phpPrefix}}_has_rendered_block_instance( (int) $post_id, (string) $resource_key )
253
+ ) {
254
+ return $response;
255
+ }
256
+
257
+ $public_write = {{phpPrefix}}_create_public_write_token( (int) $post_id, (string) $resource_key );
258
+ if ( ! is_array( $public_write ) ) {
259
+ return $response;
260
+ }
261
+
262
+ $token = isset( $public_write['token'] ) ? (string) $public_write['token'] : '';
263
+ $expires_at = isset( $public_write['expiresAt'] ) ? (int) $public_write['expiresAt'] : 0;
264
+ $is_expired = $expires_at > 0 && $expires_at <= time();
265
+
266
+ if ( '' !== $token && $expires_at > 0 && ! $is_expired ) {
267
+ $response['publicWriteToken'] = $token;
268
+ $response['canWrite'] = true;
269
+ }
270
+
271
+ if ( $expires_at > 0 ) {
272
+ $response['publicWriteExpiresAt'] = $expires_at;
273
+ if ( $is_expired ) {
274
+ $response['canWrite'] = false;
275
+ }
276
+ }
277
+
278
+ return $response;
279
+ }
280
+
161
281
  function {{phpPrefix}}_handle_get_state( WP_REST_Request $request ) {
162
282
  $payload = {{phpPrefix}}_validate_and_sanitize_request(
163
283
  array(
@@ -183,6 +303,33 @@ function {{phpPrefix}}_handle_get_state( WP_REST_Request $request ) {
183
303
  );
184
304
  }
185
305
 
306
+ function {{phpPrefix}}_handle_get_bootstrap( WP_REST_Request $request ) {
307
+ $payload = {{phpPrefix}}_validate_and_sanitize_request(
308
+ array(
309
+ 'postId' => $request->get_param( 'postId' ),
310
+ 'resourceKey' => $request->get_param( 'resourceKey' ),
311
+ ),
312
+ {{phpPrefix}}_get_rest_build_dir(),
313
+ 'bootstrap-query',
314
+ 'query'
315
+ );
316
+
317
+ if ( is_wp_error( $payload ) ) {
318
+ return $payload;
319
+ }
320
+
321
+ $response = rest_ensure_response(
322
+ {{phpPrefix}}_build_bootstrap_response(
323
+ (int) $payload['postId'],
324
+ (string) $payload['resourceKey']
325
+ )
326
+ );
327
+
328
+ $response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0, s-maxage=0' );
329
+ $response->header( 'Pragma', 'no-cache' );
330
+ return $response;
331
+ }
332
+
186
333
  function {{phpPrefix}}_handle_write_state( WP_REST_Request $request ) {
187
334
  $payload = {{phpPrefix}}_validate_and_sanitize_request(
188
335
  $request->get_json_params(),
@@ -246,6 +393,18 @@ function {{phpPrefix}}_register_routes() {
246
393
  ),
247
394
  )
248
395
  );
396
+
397
+ register_rest_route(
398
+ '{{namespace}}/v1',
399
+ '/{{slugKebabCase}}/bootstrap',
400
+ array(
401
+ array(
402
+ 'methods' => WP_REST_Server::READABLE,
403
+ 'callback' => '{{phpPrefix}}_handle_get_bootstrap',
404
+ 'permission_callback' => '__return_true',
405
+ ),
406
+ )
407
+ );
249
408
  }
250
409
 
251
410
  add_action( 'init', '{{phpPrefix}}_ensure_storage_installed' );
@@ -112,7 +112,7 @@ export default function Edit( {
112
112
  { persistencePolicyDescription }
113
113
  </Notice>
114
114
  <Notice status="info" isDismissible={ false }>
115
- { __( 'Render mode: dynamic. `render.php` bootstraps post context, storage-backed state, and write-policy data before hydration.', '{{textDomain}}' ) }
115
+ { __( '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}}' ) }
116
116
  </Notice>
117
117
  </InspectorFromManifest>
118
118
  { ! isValid && (
@@ -35,45 +35,37 @@ $post_id = is_object( $block ) && isset( $block->context['postId'] )
35
35
  : (int) get_queried_object_id();
36
36
  $storage_mode = '{{dataStorageMode}}';
37
37
  $persistence_policy = '{{persistencePolicy}}';
38
- $can_write = false;
38
+
39
+ {{phpPrefix}}_record_rendered_block_instance(
40
+ (int) $post_id,
41
+ '{{namespace}}/{{slugKebabCase}}',
42
+ $resource_key
43
+ );
44
+
39
45
  $notice_message = 'authenticated' === $persistence_policy
40
46
  ? __( 'Sign in to persist this counter.', '{{textDomain}}' )
41
- : __( 'Reload the page to refresh this write token.', '{{textDomain}}' );
47
+ : __( 'Public writes are temporarily unavailable.', '{{textDomain}}' );
42
48
  $context = array(
43
- 'buttonLabel' => $button_label,
44
- 'canWrite' => false,
45
- 'count' => 0,
46
- 'isVisible' => ! empty( $normalized['isVisible'] ),
47
- 'persistencePolicy' => $persistence_policy,
48
- 'postId' => (int) $post_id,
49
- 'resourceKey' => $resource_key,
50
- 'storage' => $storage_mode,
49
+ 'bootstrapReady' => false,
50
+ 'buttonLabel' => $button_label,
51
+ 'canWrite' => false,
52
+ 'client' => array(
53
+ 'writeExpiry' => 0,
54
+ 'writeNonce' => '',
55
+ 'writeToken' => '',
56
+ ),
57
+ 'count' => 0,
58
+ 'error' => '',
59
+ 'isBootstrapping' => false,
60
+ 'isLoading' => false,
61
+ 'isSaving' => false,
62
+ 'isVisible' => ! empty( $normalized['isVisible'] ),
63
+ 'persistencePolicy' => $persistence_policy,
64
+ 'postId' => (int) $post_id,
65
+ 'resourceKey' => $resource_key,
66
+ 'storage' => $storage_mode,
51
67
  );
52
68
 
53
- if ( 'authenticated' === $persistence_policy ) {
54
- $can_write = $post_id > 0 && is_user_logged_in();
55
- if ( $can_write ) {
56
- $context['restNonce'] = wp_create_nonce( 'wp_rest' );
57
- }
58
- } elseif ( $post_id > 0 && function_exists( '{{phpPrefix}}_create_public_write_token' ) ) {
59
- $public_write = {{phpPrefix}}_create_public_write_token( (int) $post_id, $resource_key );
60
- if ( is_array( $public_write ) ) {
61
- $token = isset( $public_write['token'] ) ? (string) $public_write['token'] : '';
62
- $expires_at = isset( $public_write['expiresAt'] ) ? (int) $public_write['expiresAt'] : 0;
63
-
64
- if ( '' !== $token ) {
65
- $context['publicWriteToken'] = $token;
66
- $can_write = true;
67
- }
68
-
69
- if ( $expires_at > 0 ) {
70
- $context['publicWriteExpiresAt'] = $expires_at;
71
- }
72
- }
73
- }
74
-
75
- $context['canWrite'] = $can_write;
76
-
77
69
  $wrapper_attributes = get_block_wrapper_attributes(
78
70
  array(
79
71
  'data-wp-context' => wp_json_encode( $context ),
@@ -89,18 +81,20 @@ $wrapper_attributes = get_block_wrapper_attributes(
89
81
  <p class="{{frontendCssClassName}}__content" style="<?php echo esc_attr( 'text-align:' . $alignment ); ?>">
90
82
  <?php echo esc_html( $content ); ?>
91
83
  </p>
92
- <?php if ( ! $can_write ) : ?>
93
- <p class="{{frontendCssClassName}}__notice">
94
- <?php echo esc_html( $notice_message ); ?>
95
- </p>
96
- <?php endif; ?>
84
+ <p
85
+ class="{{frontendCssClassName}}__notice"
86
+ data-wp-bind--hidden="!context.bootstrapReady || context.canWrite"
87
+ hidden
88
+ >
89
+ <?php echo esc_html( $notice_message ); ?>
90
+ </p>
97
91
  <p
98
92
  class="{{frontendCssClassName}}__error"
99
93
  role="status"
100
94
  aria-live="polite"
101
95
  aria-atomic="true"
102
- data-wp-bind--hidden="!state.error"
103
- data-wp-text="state.error"
96
+ data-wp-bind--hidden="!context.error"
97
+ data-wp-text="context.error"
104
98
  hidden
105
99
  ></p>
106
100
  <?php if ( ! empty( $normalized['showCount'] ) ) : ?>
@@ -109,14 +103,14 @@ $wrapper_attributes = get_block_wrapper_attributes(
109
103
  role="status"
110
104
  aria-live="polite"
111
105
  aria-atomic="true"
112
- data-wp-text="state.count"
106
+ data-wp-text="context.count"
113
107
  >
114
108
  0
115
109
  </span>
116
110
  <?php endif; ?>
117
111
  <button
118
112
  type="button"
119
- <?php echo $can_write ? '' : 'disabled'; ?>
113
+ disabled
120
114
  data-wp-bind--disabled="!context.canWrite"
121
115
  data-wp-on--click="actions.increment"
122
116
  >