@amazeelabs/silverback-gutenberg 2.6.3 → 2.7.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [2.7.1](https://github.com/amazeeio-solutions/silverback-template/compare/@amazeelabs/silverback-gutenberg@2.7.0...@amazeelabs/silverback-gutenberg@2.7.1) (2026-01-22)
7
+
8
+ **Note:** Version bump only for package @amazeelabs/silverback-gutenberg
9
+
10
+
11
+
12
+
13
+
14
+ # [2.7.0](https://github.com/amazeeio-solutions/silverback-template/compare/@amazeelabs/silverback-gutenberg@2.6.3...@amazeelabs/silverback-gutenberg@2.7.0) (2025-12-09)
15
+
16
+
17
+ ### Features
18
+
19
+ * **SLB-468:** cardinality validator improvement ([a9b3e39](https://github.com/amazeeio-solutions/silverback-template/commit/a9b3e394e4567e2c721c8a65b69e11a2c544040a))
20
+
21
+
22
+
23
+
24
+
6
25
  ## [2.6.3](https://github.com/AmazeeLabs/silverback-template/compare/@amazeelabs/silverback-gutenberg@2.6.2...@amazeelabs/silverback-gutenberg@2.6.3) (2025-09-26)
7
26
 
8
27
  **Note:** Version bump only for package @amazeelabs/silverback-gutenberg
@@ -35,7 +35,7 @@ class BlockSerializer {
35
35
  );
36
36
  }
37
37
 
38
- protected function get_comment_delimited_block_content($block_name = null, $block_attributes = null, $block_content = '') {
38
+ protected function get_comment_delimited_block_content(?string $block_name = null, ?array $block_attributes = null, string $block_content = '') {
39
39
  if ( is_null( $block_name ) ) {
40
40
  return $block_content;
41
41
  }
@@ -60,7 +60,7 @@ class BlockSerializer {
60
60
  );
61
61
  }
62
62
 
63
- protected function strip_core_block_namespace( $block_name = null ) {
63
+ protected function strip_core_block_namespace( ?string $block_name = null ) {
64
64
  if ( is_string( $block_name ) && 0 === strpos( $block_name, 'core/' ) ) {
65
65
  return substr( $block_name, 5 );
66
66
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  namespace Drupal\silverback_gutenberg\GutenbergValidation;
4
4
 
5
+ use Drupal\Component\Utility\Html;
5
6
  use Drupal\Core\StringTranslation\TranslatableMarkup;
6
7
 
7
8
  /**
@@ -73,17 +74,26 @@ trait GutenbergCardinalityValidatorTrait {
73
74
  return $this->validateEmptyInnerBlocks($expected_children);
74
75
  }
75
76
 
76
- // Count blocks, then check if the quantity for each is correct.
77
+ // Count blocks and keep references for additional validations.
77
78
  $countInnerBlockInstances = [];
79
+ $innerBlocksByName = [];
78
80
  foreach ($block['innerBlocks'] as $innerBlock) {
79
- if (!isset($countInnerBlockInstances[$innerBlock['blockName']])) {
80
- $countInnerBlockInstances[$innerBlock['blockName']] = 0;
81
+ $blockName = $innerBlock['blockName'] ?? NULL;
82
+ if ($blockName === NULL) {
83
+ continue;
81
84
  }
82
- $countInnerBlockInstances[$innerBlock['blockName']]++;
85
+ if (!isset($countInnerBlockInstances[$blockName])) {
86
+ $countInnerBlockInstances[$blockName] = 0;
87
+ }
88
+ $countInnerBlockInstances[$blockName]++;
89
+ $innerBlocksByName[$blockName][] = $innerBlock;
83
90
  }
84
91
 
85
92
  foreach ($expected_children as $child) {
86
- if (!isset($countInnerBlockInstances[$child['blockName']]) && $child['min'] > 0) {
93
+ $blockName = $child['blockName'];
94
+ $childBlocks = $innerBlocksByName[$blockName] ?? [];
95
+
96
+ if (!isset($countInnerBlockInstances[$blockName]) && $child['min'] > 0) {
87
97
  $message = $this->getExpectedQuantityErrorMessage($child);
88
98
  return [
89
99
  'is_valid' => FALSE,
@@ -91,13 +101,12 @@ trait GutenbergCardinalityValidatorTrait {
91
101
  ];
92
102
  }
93
103
  // Minimum is set to 0, so we don't care if the block is not present.
94
- if (!isset($countInnerBlockInstances[$child['blockName']]) && $child['min'] === 0) {
95
- return [
96
- 'is_valid' => TRUE,
97
- 'message' => '',
98
- ];
104
+ if (!isset($countInnerBlockInstances[$blockName]) && $child['min'] === 0) {
105
+ continue;
99
106
  }
100
- if ($countInnerBlockInstances[$child['blockName']] < $child['min']) {
107
+
108
+ $blockCount = $countInnerBlockInstances[$blockName] ?? 0;
109
+ if ($blockCount < $child['min']) {
101
110
  return [
102
111
  'is_valid' => FALSE,
103
112
  'message' => \Drupal::translation()->formatPlural($child['min'],
@@ -109,7 +118,7 @@ trait GutenbergCardinalityValidatorTrait {
109
118
  ]),
110
119
  ];
111
120
  }
112
- if ($child['max'] !== GutenbergCardinalityValidatorInterface::CARDINALITY_UNLIMITED && $countInnerBlockInstances[$child['blockName']] > $child['max']) {
121
+ if ($child['max'] !== GutenbergCardinalityValidatorInterface::CARDINALITY_UNLIMITED && $blockCount > $child['max']) {
113
122
  return [
114
123
  'is_valid' => FALSE,
115
124
  'message' => \Drupal::translation()->formatPlural($child['max'],
@@ -121,6 +130,13 @@ trait GutenbergCardinalityValidatorTrait {
121
130
  ]),
122
131
  ];
123
132
  }
133
+
134
+ if (!empty($childBlocks) && !$this->hasPopulatedBlock($childBlocks)) {
135
+ return [
136
+ 'is_valid' => FALSE,
137
+ 'message' => $this->getMissingContentErrorMessage($child),
138
+ ];
139
+ }
124
140
  }
125
141
 
126
142
  return [
@@ -172,7 +188,18 @@ trait GutenbergCardinalityValidatorTrait {
172
188
  private function validateAnyInnerBlocks(array $inner_blocks, array $expected_children): array {
173
189
  $min = $expected_children['min'];
174
190
  $max = $expected_children['max'];
175
- $count = count($inner_blocks['innerBlocks']);
191
+ $innerBlockList = $inner_blocks['innerBlocks'] ?? [];
192
+ $count = count($innerBlockList);
193
+ if (
194
+ $count > 0 &&
195
+ !$this->hasPopulatedBlock($innerBlockList) &&
196
+ !$this->isBlockPopulated($inner_blocks)
197
+ ) {
198
+ return [
199
+ 'is_valid' => FALSE,
200
+ 'message' => $this->getMissingContentErrorMessage(NULL),
201
+ ];
202
+ }
176
203
  if ($count < $min) {
177
204
  return [
178
205
  'is_valid' => FALSE,
@@ -218,4 +245,132 @@ trait GutenbergCardinalityValidatorTrait {
218
245
  return $result;
219
246
  }
220
247
 
248
+ private function blockHasMeaningfulHtml(array $block): bool {
249
+ $innerHTML = $block['innerHTML'] ?? '';
250
+ if (is_string($innerHTML) && $this->stringContainsContent($innerHTML)) {
251
+ return TRUE;
252
+ }
253
+
254
+ if (!empty($block['innerContent']) && is_array($block['innerContent'])) {
255
+ foreach ($block['innerContent'] as $chunk) {
256
+ if (is_string($chunk) && $this->stringContainsContent($chunk)) {
257
+ return TRUE;
258
+ }
259
+ }
260
+ }
261
+
262
+ return FALSE;
263
+ }
264
+
265
+ private function stringContainsContent(string $value): bool {
266
+ $decoded = Html::decodeEntities($value);
267
+ $stripped = trim(strip_tags($decoded));
268
+ if ($stripped !== '') {
269
+ return TRUE;
270
+ }
271
+
272
+ return (bool) preg_match('/<(img|video|audio|iframe|svg|figure|source|embed|object|picture)\b/i', $value);
273
+ }
274
+
275
+ private function blockHasMeaningfulAttributes(array $block): bool {
276
+ $attrs = $block['attrs'] ?? [];
277
+ if (empty($attrs)) {
278
+ return FALSE;
279
+ }
280
+
281
+ foreach ($attrs as $value) {
282
+ if ($this->isMeaningfulValue($value)) {
283
+ return TRUE;
284
+ }
285
+ }
286
+
287
+ return FALSE;
288
+ }
289
+
290
+ private function isMeaningfulValue(mixed $value): bool {
291
+ if ($value === NULL) {
292
+ return FALSE;
293
+ }
294
+ if (is_string($value)) {
295
+ return trim($value) !== '';
296
+ }
297
+ if (is_bool($value)) {
298
+ return $value;
299
+ }
300
+ if (is_numeric($value)) {
301
+ return TRUE;
302
+ }
303
+ if (is_array($value)) {
304
+ foreach ($value as $item) {
305
+ if ($this->isMeaningfulValue($item)) {
306
+ return TRUE;
307
+ }
308
+ }
309
+ return FALSE;
310
+ }
311
+
312
+ return TRUE;
313
+ }
314
+
315
+ /**
316
+ * Checks if any block in the supplied list is populated.
317
+ */
318
+ private function hasPopulatedBlock(array $blocks): bool {
319
+ foreach ($blocks as $block) {
320
+ if (is_array($block) && $this->isBlockPopulated($block)) {
321
+ return TRUE;
322
+ }
323
+ }
324
+ return FALSE;
325
+ }
326
+
327
+ private function isBlockPopulated(array $block): bool {
328
+ $evaluated = FALSE;
329
+
330
+ if (array_key_exists('innerHTML', $block) || array_key_exists('innerContent', $block)) {
331
+ $evaluated = TRUE;
332
+ if ($this->blockHasMeaningfulHtml($block)) {
333
+ return TRUE;
334
+ }
335
+ }
336
+
337
+ if (array_key_exists('attrs', $block)) {
338
+ $evaluated = TRUE;
339
+ if ($this->blockHasMeaningfulAttributes($block)) {
340
+ return TRUE;
341
+ }
342
+ }
343
+
344
+ if (!empty($block['innerBlocks']) && is_array($block['innerBlocks'])) {
345
+ $evaluated = TRUE;
346
+ foreach ($block['innerBlocks'] as $innerBlock) {
347
+ if (is_array($innerBlock) && $this->isBlockPopulated($innerBlock)) {
348
+ return TRUE;
349
+ }
350
+ }
351
+ }
352
+
353
+ if (!$evaluated) {
354
+ return TRUE;
355
+ }
356
+
357
+ return FALSE;
358
+ }
359
+
360
+ private function getMissingContentErrorMessage(?array $child_block): string|TranslatableMarkup {
361
+ $messageSuffix = t('content or attributes.');
362
+
363
+ if (!empty($child_block)) {
364
+ $messageParams = [
365
+ '%label' => $child_block['blockLabel'],
366
+ '@message_suffix' => $messageSuffix,
367
+ ];
368
+ return t('%label: block must contain @message_suffix', $messageParams);
369
+ }
370
+
371
+ return t('Block must contain @message_suffix', [
372
+ '@message_suffix' => $messageSuffix,
373
+ ]);
374
+ }
375
+
221
376
  }
@@ -71,7 +71,7 @@ class LinkProcessor {
71
71
  $this->cacheableMetadata->addCacheableDependency($other_object);
72
72
  }
73
73
 
74
- public function processLinks(string $html, string $direction, LanguageInterface $language = NULL) {
74
+ public function processLinks(string $html, string $direction, ?LanguageInterface $language = NULL) {
75
75
  if (!in_array($direction, ['inbound', 'outbound'], TRUE)) {
76
76
  throw new \Exception('Unknown direction: "' . $direction . '".');
77
77
  }
@@ -99,7 +99,7 @@ class LinkProcessor {
99
99
  return $processed;
100
100
  }
101
101
 
102
- protected function processBlocks(&$blocks, string $direction, LanguageInterface $language = NULL): void {
102
+ protected function processBlocks(&$blocks, string $direction, ?LanguageInterface $language = NULL): void {
103
103
  $processUrlCallback = fn(string $url) => $this->processUrl($url, $direction, $language);
104
104
  $processLinksCallback = fn(string $html) => $this->processLinks($html, $direction, $language);
105
105
  foreach ($blocks as &$block) {
@@ -203,7 +203,7 @@ class LinkProcessor {
203
203
  return $this->buildUrl($parts);
204
204
  }
205
205
 
206
- protected function processLink(\DOMElement $link, string $direction, LanguageInterface $language = NULL) {
206
+ protected function processLink(\DOMElement $link, string $direction, ?LanguageInterface $language = NULL) {
207
207
  if ($direction === 'outbound' && !$language) {
208
208
  throw new \Exception('$language is required for "outbound" direction.');
209
209
  }
@@ -229,7 +229,7 @@ class LinkProcessor {
229
229
 
230
230
  }
231
231
 
232
- public function processUrl(string $url, string $direction, LanguageInterface $language = NULL, array &$metadata = NULL): string {
232
+ public function processUrl(string $url, string $direction, ?LanguageInterface $language = NULL, ?array &$metadata = NULL): string {
233
233
  $metadata = [];
234
234
 
235
235
  if ($direction === 'outbound' && !$language) {
@@ -7,7 +7,7 @@ use Drupal\media_library\MediaLibraryState;
7
7
 
8
8
  class MediaService extends Original {
9
9
 
10
- public function renderDialog(array $media_types, array $media_bundles = NULL) {
10
+ public function renderDialog(array $media_types, ?array $media_bundles = NULL) {
11
11
  // Instead of guessing media types as the parent method does, use given
12
12
  // media types as media type IDs. So the blocks have full control over
13
13
  // allowed media types.
@@ -1534,4 +1534,289 @@ class BlockValidatorCardinalityTest extends UnitTestCase {
1534
1534
  );
1535
1535
  }
1536
1536
 
1537
+ public function testRequirePopulatedBlocksWithHtml() {
1538
+ $expectedChildren = [
1539
+ [
1540
+ 'blockName' => 'core/paragraph',
1541
+ 'blockLabel' => t('Paragraph'),
1542
+ 'min' => 1,
1543
+ 'max' => GutenbergCardinalityValidatorInterface::CARDINALITY_UNLIMITED,
1544
+ ],
1545
+ ];
1546
+
1547
+ $validBlock = [
1548
+ 'blockName' => 'core/column',
1549
+ 'innerBlocks' => [
1550
+ [
1551
+ 'blockName' => 'core/paragraph',
1552
+ 'innerHTML' => '<p>Content</p>',
1553
+ 'innerContent' => ['<p>Content</p>'],
1554
+ ],
1555
+ ],
1556
+ ];
1557
+ $invalidBlock = [
1558
+ 'blockName' => 'core/column',
1559
+ 'innerBlocks' => [
1560
+ [
1561
+ 'blockName' => 'core/paragraph',
1562
+ 'innerHTML' => '<p></p>',
1563
+ 'innerContent' => ['<p></p>'],
1564
+ ],
1565
+ ],
1566
+ ];
1567
+
1568
+ $this->assertEquals(
1569
+ [
1570
+ 'is_valid' => TRUE,
1571
+ 'message' => '',
1572
+ ],
1573
+ $this->validateCardinality($validBlock, $expectedChildren),
1574
+ );
1575
+ $this->assertEquals(
1576
+ [
1577
+ 'is_valid' => FALSE,
1578
+ 'message' => '<em class="placeholder">Paragraph</em>: block must contain content or attributes.',
1579
+ ],
1580
+ $this->validateCardinality($invalidBlock, $expectedChildren),
1581
+ );
1582
+ }
1583
+
1584
+ public function testMixedContentBlocksAreValid() {
1585
+ $expectedChildren = [
1586
+ [
1587
+ 'blockName' => 'core/paragraph',
1588
+ 'blockLabel' => t('Paragraph'),
1589
+ 'min' => 1,
1590
+ 'max' => 3,
1591
+ ],
1592
+ ];
1593
+
1594
+ $block = [
1595
+ 'blockName' => 'core/column',
1596
+ 'innerBlocks' => [
1597
+ [
1598
+ 'blockName' => 'core/paragraph',
1599
+ 'innerHTML' => '<p></p>',
1600
+ 'innerContent' => ['<p></p>'],
1601
+ ],
1602
+ [
1603
+ 'blockName' => 'core/paragraph',
1604
+ 'innerHTML' => '<p>Content</p>',
1605
+ 'innerContent' => ['<p>Content</p>'],
1606
+ ],
1607
+ ],
1608
+ ];
1609
+
1610
+ $this->assertEquals(
1611
+ [
1612
+ 'is_valid' => TRUE,
1613
+ 'message' => '',
1614
+ ],
1615
+ $this->validateCardinality($block, $expectedChildren),
1616
+ );
1617
+ }
1618
+
1619
+ public function testRequirePopulatedBlocksWithAttributes() {
1620
+ $expectedChildren = [
1621
+ [
1622
+ 'blockName' => 'core/embed',
1623
+ 'blockLabel' => t('Embed'),
1624
+ 'min' => 1,
1625
+ 'max' => 1,
1626
+ ],
1627
+ ];
1628
+
1629
+ $validBlock = [
1630
+ 'blockName' => 'core/column',
1631
+ 'innerBlocks' => [
1632
+ [
1633
+ 'blockName' => 'core/embed',
1634
+ 'innerHTML' => '',
1635
+ 'innerContent' => [],
1636
+ 'attrs' => [
1637
+ 'url' => 'https://example.com/video',
1638
+ ],
1639
+ ],
1640
+ ],
1641
+ ];
1642
+ $invalidBlock = [
1643
+ 'blockName' => 'core/column',
1644
+ 'innerBlocks' => [
1645
+ [
1646
+ 'blockName' => 'core/embed',
1647
+ 'innerHTML' => '',
1648
+ 'innerContent' => [],
1649
+ 'attrs' => [],
1650
+ ],
1651
+ ],
1652
+ ];
1653
+
1654
+ $this->assertEquals(
1655
+ [
1656
+ 'is_valid' => TRUE,
1657
+ 'message' => '',
1658
+ ],
1659
+ $this->validateCardinality($validBlock, $expectedChildren),
1660
+ );
1661
+ $this->assertEquals(
1662
+ [
1663
+ 'is_valid' => FALSE,
1664
+ 'message' => '<em class="placeholder">Embed</em>: block must contain content or attributes.',
1665
+ ],
1666
+ $this->validateCardinality($invalidBlock, $expectedChildren),
1667
+ );
1668
+ }
1669
+
1670
+ public function testAnyBlockTypeRequiresContent() {
1671
+ $expectedChildren = [
1672
+ 'validationType' => GutenbergCardinalityValidatorInterface::CARDINALITY_ANY,
1673
+ 'min' => 1,
1674
+ 'max' => 2,
1675
+ ];
1676
+
1677
+ $validBlock = [
1678
+ 'blockName' => 'core/group',
1679
+ 'innerBlocks' => [
1680
+ [
1681
+ 'blockName' => 'core/paragraph',
1682
+ 'innerHTML' => '<p>Content</p>',
1683
+ 'innerContent' => ['<p>Content</p>'],
1684
+ ],
1685
+ ],
1686
+ ];
1687
+ $invalidBlock = [
1688
+ 'blockName' => 'core/group',
1689
+ 'innerBlocks' => [
1690
+ [
1691
+ 'blockName' => 'core/paragraph',
1692
+ 'innerHTML' => '<p></p>',
1693
+ 'innerContent' => ['<p></p>'],
1694
+ ],
1695
+ ],
1696
+ ];
1697
+
1698
+ $this->assertEquals(
1699
+ [
1700
+ 'is_valid' => TRUE,
1701
+ 'message' => '',
1702
+ ],
1703
+ $this->validateCardinality($validBlock, $expectedChildren),
1704
+ );
1705
+ $this->assertEquals(
1706
+ [
1707
+ 'is_valid' => FALSE,
1708
+ 'message' => 'Block must contain content or attributes.',
1709
+ ],
1710
+ $this->validateCardinality($invalidBlock, $expectedChildren),
1711
+ );
1712
+ $this->assertEquals(
1713
+ [
1714
+ 'is_valid' => FALSE,
1715
+ 'message' => 'At least 1 block is required.',
1716
+ ],
1717
+ $this->validateCardinality([
1718
+ 'blockName' => 'core/group',
1719
+ 'innerBlocks' => [],
1720
+ ], $expectedChildren),
1721
+ );
1722
+ }
1723
+
1724
+ public function testAnyBlockTypeAcceptsNestedContent() {
1725
+ $expectedChildren = [
1726
+ 'validationType' => GutenbergCardinalityValidatorInterface::CARDINALITY_ANY,
1727
+ 'min' => 1,
1728
+ 'max' => 2,
1729
+ ];
1730
+
1731
+ $block = [
1732
+ 'blockName' => 'core/group',
1733
+ 'innerBlocks' => [
1734
+ [
1735
+ 'blockName' => 'custom/accordion-item-text',
1736
+ 'innerHTML' => '',
1737
+ 'innerContent' => [],
1738
+ 'innerBlocks' => [
1739
+ [
1740
+ 'blockName' => 'core/paragraph',
1741
+ 'innerHTML' => '<p>Content</p>',
1742
+ 'innerContent' => ['<p>Content</p>'],
1743
+ ],
1744
+ ],
1745
+ ],
1746
+ ],
1747
+ ];
1748
+
1749
+ $this->assertEquals(
1750
+ [
1751
+ 'is_valid' => TRUE,
1752
+ 'message' => '',
1753
+ ],
1754
+ $this->validateCardinality($block, $expectedChildren),
1755
+ );
1756
+ }
1757
+
1758
+ public function testBlockWithOnlyNestedContentSucceedsWhenDescendantHasContent() {
1759
+ $expectedChildren = [
1760
+ [
1761
+ 'blockName' => 'custom/accordion-item-text',
1762
+ 'blockLabel' => t('Accordion item'),
1763
+ 'min' => 1,
1764
+ 'max' => 1,
1765
+ ],
1766
+ ];
1767
+
1768
+ $validBlock = [
1769
+ 'blockName' => 'custom/accordion',
1770
+ 'innerBlocks' => [
1771
+ [
1772
+ 'blockName' => 'custom/accordion-item-text',
1773
+ 'innerHTML' => '',
1774
+ 'innerContent' => [],
1775
+ 'attrs' => [],
1776
+ 'innerBlocks' => [
1777
+ [
1778
+ 'blockName' => 'core/paragraph',
1779
+ 'innerHTML' => '<p>Some content</p>',
1780
+ 'innerContent' => ['<p>Some content</p>'],
1781
+ ],
1782
+ ],
1783
+ ],
1784
+ ],
1785
+ ];
1786
+
1787
+ $invalidBlock = [
1788
+ 'blockName' => 'custom/accordion',
1789
+ 'innerBlocks' => [
1790
+ [
1791
+ 'blockName' => 'custom/accordion-item-text',
1792
+ 'innerHTML' => '',
1793
+ 'innerContent' => [],
1794
+ 'attrs' => [],
1795
+ 'innerBlocks' => [
1796
+ [
1797
+ 'blockName' => 'core/paragraph',
1798
+ 'innerHTML' => '<p></p>',
1799
+ 'innerContent' => ['<p></p>'],
1800
+ ],
1801
+ ],
1802
+ ],
1803
+ ],
1804
+ ];
1805
+
1806
+ $this->assertEquals(
1807
+ [
1808
+ 'is_valid' => TRUE,
1809
+ 'message' => '',
1810
+ ],
1811
+ $this->validateCardinality($validBlock, $expectedChildren),
1812
+ );
1813
+ $this->assertEquals(
1814
+ [
1815
+ 'is_valid' => FALSE,
1816
+ 'message' => '<em class="placeholder">Accordion item</em>: block must contain content or attributes.',
1817
+ ],
1818
+ $this->validateCardinality($invalidBlock, $expectedChildren),
1819
+ );
1820
+ }
1821
+
1537
1822
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amazeelabs/silverback-gutenberg",
3
- "version": "2.6.3",
3
+ "version": "2.7.1",
4
4
  "scripts": {
5
5
  "precommit": "pnpm precommit:fix && pnpm precommit:check && pnpm test:unit",
6
6
  "precommit:fix": "pnpm --filter @custom/cms precommit:fix packages/@amazeelabs/silverback-gutenberg/drupal",
@@ -12,5 +12,5 @@
12
12
  "publishConfig": {
13
13
  "access": "public"
14
14
  },
15
- "gitHead": "00e375dc86982224214745341087636b28d971af"
15
+ "gitHead": "8e0638f5452e77742f99aebe4a65606aac751103"
16
16
  }