@datocms/cma-client 5.1.10 → 5.1.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 (253) hide show
  1. package/README.md +569 -0
  2. package/dist/cjs/fieldTypes/boolean.js +2 -2
  3. package/dist/cjs/fieldTypes/boolean.js.map +1 -1
  4. package/dist/cjs/fieldTypes/color.js +2 -2
  5. package/dist/cjs/fieldTypes/color.js.map +1 -1
  6. package/dist/cjs/fieldTypes/date.js +2 -3
  7. package/dist/cjs/fieldTypes/date.js.map +1 -1
  8. package/dist/cjs/fieldTypes/date_time.js +2 -2
  9. package/dist/cjs/fieldTypes/date_time.js.map +1 -1
  10. package/dist/cjs/fieldTypes/file.js +33 -5
  11. package/dist/cjs/fieldTypes/file.js.map +1 -1
  12. package/dist/cjs/fieldTypes/float.js +2 -2
  13. package/dist/cjs/fieldTypes/float.js.map +1 -1
  14. package/dist/cjs/fieldTypes/gallery.js +20 -5
  15. package/dist/cjs/fieldTypes/gallery.js.map +1 -1
  16. package/dist/cjs/fieldTypes/index.js +2 -2
  17. package/dist/cjs/fieldTypes/index.js.map +1 -1
  18. package/dist/cjs/fieldTypes/integer.js +2 -2
  19. package/dist/cjs/fieldTypes/integer.js.map +1 -1
  20. package/dist/cjs/fieldTypes/json.js +2 -3
  21. package/dist/cjs/fieldTypes/json.js.map +1 -1
  22. package/dist/cjs/fieldTypes/lat_lon.js +2 -2
  23. package/dist/cjs/fieldTypes/lat_lon.js.map +1 -1
  24. package/dist/cjs/fieldTypes/link.js +4 -4
  25. package/dist/cjs/fieldTypes/link.js.map +1 -1
  26. package/dist/cjs/fieldTypes/links.js +5 -3
  27. package/dist/cjs/fieldTypes/links.js.map +1 -1
  28. package/dist/cjs/fieldTypes/rich_text.js +18 -18
  29. package/dist/cjs/fieldTypes/rich_text.js.map +1 -1
  30. package/dist/cjs/fieldTypes/schema.js +3 -0
  31. package/dist/cjs/fieldTypes/schema.js.map +1 -0
  32. package/dist/cjs/fieldTypes/seo.js +2 -3
  33. package/dist/cjs/fieldTypes/seo.js.map +1 -1
  34. package/dist/cjs/fieldTypes/single_block.js +20 -19
  35. package/dist/cjs/fieldTypes/single_block.js.map +1 -1
  36. package/dist/cjs/fieldTypes/slug.js +2 -3
  37. package/dist/cjs/fieldTypes/slug.js.map +1 -1
  38. package/dist/cjs/fieldTypes/string.js +2 -2
  39. package/dist/cjs/fieldTypes/string.js.map +1 -1
  40. package/dist/cjs/fieldTypes/structured_text.js +17 -18
  41. package/dist/cjs/fieldTypes/structured_text.js.map +1 -1
  42. package/dist/cjs/fieldTypes/text.js +2 -3
  43. package/dist/cjs/fieldTypes/text.js.map +1 -1
  44. package/dist/cjs/fieldTypes/video.js +2 -2
  45. package/dist/cjs/fieldTypes/video.js.map +1 -1
  46. package/dist/cjs/generated/Client.js +1 -1
  47. package/dist/cjs/generated/resources/Item.js.map +1 -1
  48. package/dist/cjs/generated/resources/ScheduledPublication.js.map +1 -1
  49. package/dist/cjs/generated/resources/ScheduledUnpublishing.js.map +1 -1
  50. package/dist/cjs/generated/resources/Upload.js.map +1 -1
  51. package/dist/cjs/index.js +5 -4
  52. package/dist/cjs/index.js.map +1 -1
  53. package/dist/cjs/utilities/buildBlockRecord.js +1 -45
  54. package/dist/cjs/utilities/buildBlockRecord.js.map +1 -1
  55. package/dist/cjs/utilities/duplicateBlockRecord.js +49 -0
  56. package/dist/cjs/utilities/duplicateBlockRecord.js.map +1 -0
  57. package/dist/cjs/utilities/inspectItem.js +427 -0
  58. package/dist/cjs/utilities/inspectItem.js.map +1 -0
  59. package/dist/cjs/utilities/{blocks.js → nonRecursiveBlocks.js} +60 -234
  60. package/dist/cjs/utilities/nonRecursiveBlocks.js.map +1 -0
  61. package/dist/cjs/utilities/{fieldValue.js → normalizedFieldValues.js} +53 -41
  62. package/dist/cjs/utilities/normalizedFieldValues.js.map +1 -0
  63. package/dist/cjs/utilities/recursiveBlocks.js +73 -92
  64. package/dist/cjs/utilities/recursiveBlocks.js.map +1 -1
  65. package/dist/cjs/utilities/schemaRepository.js +2 -2
  66. package/dist/esm/fieldTypes/boolean.d.ts +1 -1
  67. package/dist/esm/fieldTypes/boolean.js +2 -2
  68. package/dist/esm/fieldTypes/boolean.js.map +1 -1
  69. package/dist/esm/fieldTypes/color.d.ts +1 -1
  70. package/dist/esm/fieldTypes/color.js +2 -2
  71. package/dist/esm/fieldTypes/color.js.map +1 -1
  72. package/dist/esm/fieldTypes/date.d.ts +1 -1
  73. package/dist/esm/fieldTypes/date.js +2 -3
  74. package/dist/esm/fieldTypes/date.js.map +1 -1
  75. package/dist/esm/fieldTypes/date_time.d.ts +1 -1
  76. package/dist/esm/fieldTypes/date_time.js +2 -2
  77. package/dist/esm/fieldTypes/date_time.js.map +1 -1
  78. package/dist/esm/fieldTypes/file.d.ts +68 -1
  79. package/dist/esm/fieldTypes/file.js +29 -3
  80. package/dist/esm/fieldTypes/file.js.map +1 -1
  81. package/dist/esm/fieldTypes/float.d.ts +1 -1
  82. package/dist/esm/fieldTypes/float.js +2 -2
  83. package/dist/esm/fieldTypes/float.js.map +1 -1
  84. package/dist/esm/fieldTypes/gallery.d.ts +27 -11
  85. package/dist/esm/fieldTypes/gallery.js +17 -4
  86. package/dist/esm/fieldTypes/gallery.js.map +1 -1
  87. package/dist/esm/fieldTypes/index.d.ts +2 -2
  88. package/dist/esm/fieldTypes/index.js +2 -2
  89. package/dist/esm/fieldTypes/index.js.map +1 -1
  90. package/dist/esm/fieldTypes/integer.d.ts +1 -1
  91. package/dist/esm/fieldTypes/integer.js +2 -2
  92. package/dist/esm/fieldTypes/integer.js.map +1 -1
  93. package/dist/esm/fieldTypes/json.d.ts +1 -1
  94. package/dist/esm/fieldTypes/json.js +2 -3
  95. package/dist/esm/fieldTypes/json.js.map +1 -1
  96. package/dist/esm/fieldTypes/lat_lon.d.ts +1 -1
  97. package/dist/esm/fieldTypes/lat_lon.js +2 -2
  98. package/dist/esm/fieldTypes/lat_lon.js.map +1 -1
  99. package/dist/esm/fieldTypes/link.d.ts +1 -1
  100. package/dist/esm/fieldTypes/link.js +4 -4
  101. package/dist/esm/fieldTypes/link.js.map +1 -1
  102. package/dist/esm/fieldTypes/links.d.ts +1 -1
  103. package/dist/esm/fieldTypes/links.js +5 -3
  104. package/dist/esm/fieldTypes/links.js.map +1 -1
  105. package/dist/esm/fieldTypes/rich_text.d.ts +9 -10
  106. package/dist/esm/fieldTypes/rich_text.js +13 -13
  107. package/dist/esm/fieldTypes/rich_text.js.map +1 -1
  108. package/dist/esm/fieldTypes/schema.d.ts +59 -0
  109. package/dist/esm/fieldTypes/schema.js +2 -0
  110. package/dist/esm/fieldTypes/schema.js.map +1 -0
  111. package/dist/esm/fieldTypes/seo.d.ts +1 -1
  112. package/dist/esm/fieldTypes/seo.js +2 -3
  113. package/dist/esm/fieldTypes/seo.js.map +1 -1
  114. package/dist/esm/fieldTypes/single_block.d.ts +31 -16
  115. package/dist/esm/fieldTypes/single_block.js +15 -14
  116. package/dist/esm/fieldTypes/single_block.js.map +1 -1
  117. package/dist/esm/fieldTypes/slug.d.ts +1 -1
  118. package/dist/esm/fieldTypes/slug.js +2 -3
  119. package/dist/esm/fieldTypes/slug.js.map +1 -1
  120. package/dist/esm/fieldTypes/string.d.ts +1 -1
  121. package/dist/esm/fieldTypes/string.js +2 -2
  122. package/dist/esm/fieldTypes/string.js.map +1 -1
  123. package/dist/esm/fieldTypes/structured_text.d.ts +15 -16
  124. package/dist/esm/fieldTypes/structured_text.js +12 -13
  125. package/dist/esm/fieldTypes/structured_text.js.map +1 -1
  126. package/dist/esm/fieldTypes/text.d.ts +1 -1
  127. package/dist/esm/fieldTypes/text.js +2 -3
  128. package/dist/esm/fieldTypes/text.js.map +1 -1
  129. package/dist/esm/fieldTypes/video.d.ts +1 -1
  130. package/dist/esm/fieldTypes/video.js +2 -2
  131. package/dist/esm/fieldTypes/video.js.map +1 -1
  132. package/dist/esm/generated/ApiTypes.d.ts +90 -980
  133. package/dist/esm/generated/Client.js +1 -1
  134. package/dist/esm/generated/RawApiTypes.d.ts +156 -999
  135. package/dist/esm/generated/resources/Field.d.ts +200 -200
  136. package/dist/esm/generated/resources/Item.d.ts +34 -34
  137. package/dist/esm/generated/resources/Item.js.map +1 -1
  138. package/dist/esm/generated/resources/ItemVersion.d.ts +3 -1
  139. package/dist/esm/generated/resources/ScheduledPublication.d.ts +3 -3
  140. package/dist/esm/generated/resources/ScheduledPublication.js.map +1 -1
  141. package/dist/esm/generated/resources/ScheduledUnpublishing.d.ts +3 -3
  142. package/dist/esm/generated/resources/ScheduledUnpublishing.js.map +1 -1
  143. package/dist/esm/generated/resources/Upload.d.ts +6 -6
  144. package/dist/esm/generated/resources/Upload.js.map +1 -1
  145. package/dist/esm/index.d.ts +5 -4
  146. package/dist/esm/index.js +5 -4
  147. package/dist/esm/index.js.map +1 -1
  148. package/dist/esm/utilities/buildBlockRecord.d.ts +3 -5
  149. package/dist/esm/utilities/buildBlockRecord.js +0 -43
  150. package/dist/esm/utilities/buildBlockRecord.js.map +1 -1
  151. package/dist/esm/utilities/duplicateBlockRecord.d.ts +6 -0
  152. package/dist/esm/utilities/duplicateBlockRecord.js +45 -0
  153. package/dist/esm/utilities/duplicateBlockRecord.js.map +1 -0
  154. package/dist/esm/utilities/inspectItem.d.ts +8 -0
  155. package/dist/esm/utilities/inspectItem.js +400 -0
  156. package/dist/esm/utilities/inspectItem.js.map +1 -0
  157. package/dist/esm/utilities/itemDefinition.d.ts +45 -23
  158. package/dist/esm/utilities/nonRecursiveBlocks.d.ts +114 -0
  159. package/dist/esm/utilities/{blocks.js → nonRecursiveBlocks.js} +52 -220
  160. package/dist/esm/utilities/nonRecursiveBlocks.js.map +1 -0
  161. package/dist/{types/utilities/fieldValue.d.ts → esm/utilities/normalizedFieldValues.d.ts} +15 -14
  162. package/dist/esm/utilities/{fieldValue.js → normalizedFieldValues.js} +39 -28
  163. package/dist/esm/utilities/normalizedFieldValues.js.map +1 -0
  164. package/dist/esm/utilities/recursiveBlocks.d.ts +124 -11
  165. package/dist/esm/utilities/recursiveBlocks.js +65 -84
  166. package/dist/esm/utilities/recursiveBlocks.js.map +1 -1
  167. package/dist/esm/utilities/schemaRepository.d.ts +2 -2
  168. package/dist/esm/utilities/schemaRepository.js +2 -2
  169. package/dist/types/fieldTypes/boolean.d.ts +1 -1
  170. package/dist/types/fieldTypes/color.d.ts +1 -1
  171. package/dist/types/fieldTypes/date.d.ts +1 -1
  172. package/dist/types/fieldTypes/date_time.d.ts +1 -1
  173. package/dist/types/fieldTypes/file.d.ts +68 -1
  174. package/dist/types/fieldTypes/float.d.ts +1 -1
  175. package/dist/types/fieldTypes/gallery.d.ts +27 -11
  176. package/dist/types/fieldTypes/index.d.ts +2 -2
  177. package/dist/types/fieldTypes/integer.d.ts +1 -1
  178. package/dist/types/fieldTypes/json.d.ts +1 -1
  179. package/dist/types/fieldTypes/lat_lon.d.ts +1 -1
  180. package/dist/types/fieldTypes/link.d.ts +1 -1
  181. package/dist/types/fieldTypes/links.d.ts +1 -1
  182. package/dist/types/fieldTypes/rich_text.d.ts +9 -10
  183. package/dist/types/fieldTypes/schema.d.ts +59 -0
  184. package/dist/types/fieldTypes/seo.d.ts +1 -1
  185. package/dist/types/fieldTypes/single_block.d.ts +31 -16
  186. package/dist/types/fieldTypes/slug.d.ts +1 -1
  187. package/dist/types/fieldTypes/string.d.ts +1 -1
  188. package/dist/types/fieldTypes/structured_text.d.ts +15 -16
  189. package/dist/types/fieldTypes/text.d.ts +1 -1
  190. package/dist/types/fieldTypes/video.d.ts +1 -1
  191. package/dist/types/generated/ApiTypes.d.ts +90 -980
  192. package/dist/types/generated/RawApiTypes.d.ts +156 -999
  193. package/dist/types/generated/resources/Field.d.ts +200 -200
  194. package/dist/types/generated/resources/Item.d.ts +34 -34
  195. package/dist/types/generated/resources/ItemVersion.d.ts +3 -1
  196. package/dist/types/generated/resources/ScheduledPublication.d.ts +3 -3
  197. package/dist/types/generated/resources/ScheduledUnpublishing.d.ts +3 -3
  198. package/dist/types/generated/resources/Upload.d.ts +6 -6
  199. package/dist/types/index.d.ts +5 -4
  200. package/dist/types/utilities/buildBlockRecord.d.ts +3 -5
  201. package/dist/types/utilities/duplicateBlockRecord.d.ts +6 -0
  202. package/dist/types/utilities/inspectItem.d.ts +8 -0
  203. package/dist/types/utilities/itemDefinition.d.ts +45 -23
  204. package/dist/types/utilities/nonRecursiveBlocks.d.ts +114 -0
  205. package/dist/{esm/utilities/fieldValue.d.ts → types/utilities/normalizedFieldValues.d.ts} +15 -14
  206. package/dist/types/utilities/recursiveBlocks.d.ts +124 -11
  207. package/dist/types/utilities/schemaRepository.d.ts +2 -2
  208. package/package.json +4 -4
  209. package/src/fieldTypes/boolean.ts +5 -3
  210. package/src/fieldTypes/color.ts +5 -3
  211. package/src/fieldTypes/date.ts +5 -4
  212. package/src/fieldTypes/date_time.ts +5 -3
  213. package/src/fieldTypes/file.ts +98 -4
  214. package/src/fieldTypes/float.ts +5 -3
  215. package/src/fieldTypes/gallery.ts +53 -20
  216. package/src/fieldTypes/index.ts +2 -3
  217. package/src/fieldTypes/integer.ts +5 -3
  218. package/src/fieldTypes/json.ts +5 -4
  219. package/src/fieldTypes/lat_lon.ts +5 -3
  220. package/src/fieldTypes/link.ts +7 -5
  221. package/src/fieldTypes/links.ts +8 -4
  222. package/src/fieldTypes/rich_text.ts +33 -31
  223. package/src/fieldTypes/schema.ts +657 -0
  224. package/src/fieldTypes/seo.ts +5 -4
  225. package/src/fieldTypes/single_block.ts +71 -46
  226. package/src/fieldTypes/slug.ts +5 -4
  227. package/src/fieldTypes/string.ts +5 -3
  228. package/src/fieldTypes/structured_text.ts +64 -58
  229. package/src/fieldTypes/text.ts +5 -4
  230. package/src/fieldTypes/video.ts +5 -3
  231. package/src/generated/ApiTypes.ts +217 -1880
  232. package/src/generated/Client.ts +1 -1
  233. package/src/generated/RawApiTypes.ts +272 -2113
  234. package/src/generated/resources/Item.ts +93 -187
  235. package/src/generated/resources/ScheduledPublication.ts +4 -15
  236. package/src/generated/resources/ScheduledUnpublishing.ts +4 -15
  237. package/src/generated/resources/Upload.ts +9 -32
  238. package/src/index.ts +5 -4
  239. package/src/utilities/buildBlockRecord.ts +4 -59
  240. package/src/utilities/duplicateBlockRecord.ts +52 -0
  241. package/src/utilities/inspectItem.ts +602 -0
  242. package/src/utilities/itemDefinition.ts +130 -92
  243. package/src/utilities/nonRecursiveBlocks.ts +406 -0
  244. package/src/utilities/{fieldValue.ts → normalizedFieldValues.ts} +70 -30
  245. package/src/utilities/recursiveBlocks.ts +484 -204
  246. package/src/utilities/schemaRepository.ts +2 -2
  247. package/dist/cjs/utilities/blocks.js.map +0 -1
  248. package/dist/cjs/utilities/fieldValue.js.map +0 -1
  249. package/dist/esm/utilities/blocks.d.ts +0 -180
  250. package/dist/esm/utilities/blocks.js.map +0 -1
  251. package/dist/esm/utilities/fieldValue.js.map +0 -1
  252. package/dist/types/utilities/blocks.d.ts +0 -180
  253. package/src/utilities/blocks.ts +0 -626
@@ -0,0 +1,406 @@
1
+ /**
2
+ * DatoCMS Block Field Value Processing Utilities
3
+ *
4
+ * This utility provides a unified interface for working with blocks embedded within DatoCMS field values.
5
+ * DatoCMS supports three field types that can contain blocks:
6
+ * - Modular Content fields: arrays of blocks
7
+ * - Single Block fields: a single block
8
+ * - Structured Text fields: complex document structures with embedded blocks
9
+ *
10
+ * The challenge this solves: Each field type stores blocks differently and requires different
11
+ * traversal logic, making it complex to perform operations like transformations, filtering,
12
+ * or searching across blocks regardless of their containing field type.
13
+ *
14
+ * This utility abstracts away these differences, providing a consistent API to:
15
+ * - Visit/iterate through all blocks in any field type
16
+ * - Transform blocks while preserving field structure
17
+ * - Filter blocks based on conditions
18
+ * - Search for specific blocks
19
+ * - Perform functional operations (map, reduce, some, every)
20
+ *
21
+ * All functions come in both sync and async variants to support different use cases,
22
+ * particularly useful when block transformations require async operations like API calls.
23
+ */
24
+
25
+ import {
26
+ type TreePath,
27
+ collectNodesAsync,
28
+ filterNodesAsync,
29
+ mapNodes,
30
+ mapNodesAsync,
31
+ } from 'datocms-structured-text-utils';
32
+ import type {
33
+ BlockInRequest,
34
+ RichTextFieldValue,
35
+ RichTextFieldValueInNestedResponse,
36
+ RichTextFieldValueInRequest,
37
+ SingleBlockFieldValue,
38
+ SingleBlockFieldValueInNestedResponse,
39
+ SingleBlockFieldValueInRequest,
40
+ StructuredTextFieldValue,
41
+ StructuredTextFieldValueInNestedResponse,
42
+ StructuredTextFieldValueInRequest,
43
+ } from '../fieldTypes';
44
+ import type * as ApiTypes from '../generated/ApiTypes';
45
+
46
+ type PossibleRichTextValue =
47
+ | RichTextFieldValue
48
+ | RichTextFieldValueInRequest
49
+ | RichTextFieldValueInNestedResponse;
50
+ type PossibleSingleBlockValue =
51
+ | SingleBlockFieldValue
52
+ | SingleBlockFieldValueInRequest
53
+ | SingleBlockFieldValueInNestedResponse;
54
+ type PossibleStructuredTextValue =
55
+ | StructuredTextFieldValue
56
+ | StructuredTextFieldValueInRequest
57
+ | StructuredTextFieldValueInNestedResponse;
58
+
59
+ async function* iterateBlocksAsync(
60
+ fieldType: ApiTypes.Field['field_type'],
61
+ nonLocalizedFieldValue: unknown,
62
+ ): AsyncGenerator<{ item: BlockInRequest; path: TreePath }> {
63
+ if (fieldType === 'rich_text') {
64
+ const richTextValue = nonLocalizedFieldValue as PossibleRichTextValue;
65
+ if (richTextValue) {
66
+ for (let index = 0; index < richTextValue.length; index++) {
67
+ const item = richTextValue[index]!;
68
+ yield { item, path: [index] };
69
+ }
70
+ }
71
+ return;
72
+ }
73
+
74
+ if (fieldType === 'single_block') {
75
+ const singleBlockValue = nonLocalizedFieldValue as PossibleSingleBlockValue;
76
+ if (singleBlockValue) {
77
+ yield { item: singleBlockValue, path: [] };
78
+ }
79
+ return;
80
+ }
81
+
82
+ if (fieldType === 'structured_text') {
83
+ const structuredTextValue =
84
+ nonLocalizedFieldValue as PossibleStructuredTextValue;
85
+ if (structuredTextValue) {
86
+ const foundNodes = await collectNodesAsync(
87
+ structuredTextValue.document,
88
+ async (node) => node.type === 'block' || node.type === 'inlineBlock',
89
+ );
90
+
91
+ for (const { node, path } of foundNodes) {
92
+ if (node.type === 'block' || node.type === 'inlineBlock') {
93
+ yield { item: node.item, path };
94
+ }
95
+ }
96
+ }
97
+ return;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Visit every block in a field value, calling the visitor function for each block found.
103
+ * Supports rich text, single block, and structured text field types.
104
+ *
105
+ * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
106
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to visit
107
+ * @param visitor - Asynchronous function called for each block. Receives the block item and its path
108
+ * @returns Promise that resolves when all blocks have been visited
109
+ */
110
+ export async function nonRecursiveVisitBlocksInNonLocalizedFieldValueAsync(
111
+ fieldType: ApiTypes.Field['field_type'],
112
+ nonLocalizedFieldValue: unknown,
113
+ visitor: (item: BlockInRequest, path: TreePath) => Promise<void>,
114
+ ): Promise<void> {
115
+ for await (const { item, path } of iterateBlocksAsync(
116
+ fieldType,
117
+ nonLocalizedFieldValue,
118
+ )) {
119
+ await visitor(item, path);
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Transform blocks in a field value by applying a mapping function to each block.
125
+ * Creates a new field value structure with transformed blocks while preserving the original structure.
126
+ * Supports rich text, single block, and structured text field types.
127
+ *
128
+ * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
129
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to transform
130
+ * @param mapper - Synchronous function that transforms each block. Receives block item and path, returns new block
131
+ * @returns The new field value with transformed blocks
132
+ */
133
+ export function nonRecursiveMapBlocksInNonLocalizedFieldValue(
134
+ fieldType: ApiTypes.Field['field_type'],
135
+ nonLocalizedFieldValue: unknown,
136
+ mapper: (item: BlockInRequest, path: TreePath) => BlockInRequest,
137
+ ): unknown {
138
+ if (fieldType === 'rich_text') {
139
+ const richTextValue = nonLocalizedFieldValue as PossibleRichTextValue;
140
+ return richTextValue
141
+ ? richTextValue.map((item, index) => mapper(item, [index]))
142
+ : richTextValue;
143
+ }
144
+
145
+ if (fieldType === 'single_block') {
146
+ const singleBlockValue = nonLocalizedFieldValue as PossibleSingleBlockValue;
147
+ return singleBlockValue ? mapper(singleBlockValue, []) : null;
148
+ }
149
+
150
+ if (fieldType === 'structured_text') {
151
+ const structuredTextValue =
152
+ nonLocalizedFieldValue as PossibleStructuredTextValue;
153
+
154
+ if (!structuredTextValue) {
155
+ return null;
156
+ }
157
+
158
+ return {
159
+ schema: 'dast',
160
+ document: mapNodes(
161
+ structuredTextValue.document,
162
+ (node, _parent, path) => {
163
+ if (node.type === 'block' || node.type === 'inlineBlock') {
164
+ return { ...node, item: mapper(node.item, path) };
165
+ }
166
+
167
+ return node;
168
+ },
169
+ ),
170
+ };
171
+ }
172
+
173
+ return nonLocalizedFieldValue;
174
+ }
175
+
176
+ /**
177
+ * Transform blocks in a field value by applying a mapping function to each block.
178
+ * Creates a new field value structure with transformed blocks while preserving the original structure.
179
+ * Supports rich text, single block, and structured text field types.
180
+ *
181
+ * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
182
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to transform
183
+ * @param mapper - Asynchronous function that transforms each block. Receives block item and path, returns new block
184
+ * @returns Promise that resolves to the new field value with transformed blocks
185
+ */
186
+ export async function nonRecursiveMapBlocksInNonLocalizedFieldValueAsync(
187
+ fieldType: ApiTypes.Field['field_type'],
188
+ nonLocalizedFieldValue: unknown,
189
+ mapper: (item: BlockInRequest, path: TreePath) => Promise<BlockInRequest>,
190
+ ): Promise<unknown> {
191
+ if (fieldType === 'rich_text') {
192
+ const richTextValue = nonLocalizedFieldValue as PossibleRichTextValue;
193
+ return richTextValue
194
+ ? await Promise.all(
195
+ richTextValue.map((item, index) => mapper(item, [index])),
196
+ )
197
+ : richTextValue;
198
+ }
199
+
200
+ if (fieldType === 'single_block') {
201
+ const singleBlockValue = nonLocalizedFieldValue as PossibleSingleBlockValue;
202
+ return singleBlockValue ? await mapper(singleBlockValue, []) : null;
203
+ }
204
+
205
+ if (fieldType === 'structured_text') {
206
+ const structuredTextValue =
207
+ nonLocalizedFieldValue as PossibleStructuredTextValue;
208
+
209
+ if (!structuredTextValue) {
210
+ return null;
211
+ }
212
+
213
+ return {
214
+ schema: 'dast',
215
+ document: await mapNodesAsync(
216
+ structuredTextValue.document,
217
+ async (node, _parent, path) => {
218
+ if (node.type === 'block' || node.type === 'inlineBlock') {
219
+ return { ...node, item: await mapper(node.item, path) };
220
+ }
221
+
222
+ return node;
223
+ },
224
+ ),
225
+ };
226
+ }
227
+
228
+ return nonLocalizedFieldValue;
229
+ }
230
+
231
+ /**
232
+ * Find all blocks that match the predicate function.
233
+ * Searches through all blocks in the non-localized field value and returns all matches.
234
+ *
235
+ * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
236
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to search
237
+ * @param predicate - Asynchronous function that tests each block. Should return true for matching blocks
238
+ * @returns Promise that resolves to an array of objects, each containing a matching block and its path
239
+ */
240
+ export async function nonRecursiveFindAllBlocksInNonLocalizedFieldValueAsync(
241
+ fieldType: ApiTypes.Field['field_type'],
242
+ nonLocalizedFieldValue: unknown,
243
+ predicate: (item: BlockInRequest, path: TreePath) => Promise<boolean>,
244
+ ): Promise<Array<{ item: BlockInRequest; path: TreePath }>> {
245
+ const results: Array<{ item: BlockInRequest; path: TreePath }> = [];
246
+
247
+ for await (const { item, path } of iterateBlocksAsync(
248
+ fieldType,
249
+ nonLocalizedFieldValue,
250
+ )) {
251
+ if (await predicate(item, path)) {
252
+ results.push({ item, path });
253
+ }
254
+ }
255
+
256
+ return results;
257
+ }
258
+
259
+ /**
260
+ * Filter blocks in a field value, removing those that don't match the predicate.
261
+ * Creates a new field value containing only blocks that pass the predicate test.
262
+ * Preserves the original field value structure and hierarchy.
263
+ *
264
+ * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
265
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to filter
266
+ * @param predicate - Asynchronous function that tests each block. Blocks returning false are removed
267
+ * @returns Promise that resolves to the new field value with filtered blocks
268
+ */
269
+ export async function nonRecursiveFilterBlocksInNonLocalizedFieldValueAsync(
270
+ fieldType: ApiTypes.Field['field_type'],
271
+ nonLocalizedFieldValue: unknown,
272
+ predicate: (item: BlockInRequest, path: TreePath) => Promise<boolean>,
273
+ ): Promise<unknown> {
274
+ if (fieldType === 'rich_text') {
275
+ const filteredItems: BlockInRequest[] = [];
276
+
277
+ for await (const { item, path } of iterateBlocksAsync(
278
+ fieldType,
279
+ nonLocalizedFieldValue,
280
+ )) {
281
+ if (await predicate(item, path)) {
282
+ filteredItems.push(item);
283
+ }
284
+ }
285
+
286
+ return filteredItems;
287
+ }
288
+
289
+ if (fieldType === 'single_block') {
290
+ for await (const { item, path } of iterateBlocksAsync(
291
+ fieldType,
292
+ nonLocalizedFieldValue,
293
+ )) {
294
+ if (await predicate(item, path)) {
295
+ return item;
296
+ }
297
+ }
298
+ return null;
299
+ }
300
+
301
+ if (fieldType === 'structured_text') {
302
+ const structuredTextValue =
303
+ nonLocalizedFieldValue as PossibleStructuredTextValue;
304
+
305
+ if (!structuredTextValue) {
306
+ return null;
307
+ }
308
+
309
+ const filteredDocument = await filterNodesAsync(
310
+ structuredTextValue.document,
311
+ async (node, _parent, path) => {
312
+ if (node.type === 'block' || node.type === 'inlineBlock') {
313
+ return await predicate(node.item, path);
314
+ }
315
+ return true;
316
+ },
317
+ );
318
+
319
+ return filteredDocument
320
+ ? {
321
+ schema: 'dast',
322
+ document: filteredDocument,
323
+ }
324
+ : null;
325
+ }
326
+
327
+ return nonLocalizedFieldValue;
328
+ }
329
+
330
+ /**
331
+ * Reduce all blocks in a field value to a single value by applying a reducer function.
332
+ * Processes each block in the non-localized field value and accumulates the results into a single value.
333
+ *
334
+ * @template R - The type of the accumulated result
335
+ * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
336
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to reduce
337
+ * @param reducer - Asynchronous function that processes each block and updates the accumulator
338
+ * @param initialValue - The initial value for the accumulator
339
+ * @returns Promise that resolves to the final accumulated value
340
+ */
341
+ export async function nonRecursiveReduceBlocksInNonLocalizedFieldValueAsync<R>(
342
+ fieldType: ApiTypes.Field['field_type'],
343
+ nonLocalizedFieldValue: unknown,
344
+ reducer: (accumulator: R, item: BlockInRequest, path: TreePath) => Promise<R>,
345
+ initialValue: R,
346
+ ): Promise<R> {
347
+ let accumulator = initialValue;
348
+
349
+ for await (const { item, path } of iterateBlocksAsync(
350
+ fieldType,
351
+ nonLocalizedFieldValue,
352
+ )) {
353
+ accumulator = await reducer(accumulator, item, path);
354
+ }
355
+
356
+ return accumulator;
357
+ }
358
+
359
+ /**
360
+ * Check if any block in the non-localized field value matches the predicate function.
361
+ * Returns true as soon as the first matching block is found (short-circuit evaluation).
362
+ *
363
+ * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
364
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to test
365
+ * @param predicate - Asynchronous function that tests each block. Should return true for matching blocks
366
+ * @returns Promise that resolves to true if any block matches, false otherwise
367
+ */
368
+ export async function nonRecursiveSomeBlocksInNonLocalizedFieldValueAsync(
369
+ fieldType: ApiTypes.Field['field_type'],
370
+ nonLocalizedFieldValue: unknown,
371
+ predicate: (item: BlockInRequest, path: TreePath) => Promise<boolean>,
372
+ ): Promise<boolean> {
373
+ for await (const { item, path } of iterateBlocksAsync(
374
+ fieldType,
375
+ nonLocalizedFieldValue,
376
+ )) {
377
+ if (await predicate(item, path)) {
378
+ return true;
379
+ }
380
+ }
381
+
382
+ return false;
383
+ }
384
+
385
+ /**
386
+ * Check if every block in the non-localized field value matches the predicate function.
387
+ * Returns false as soon as the first non-matching block is found (short-circuit evaluation).
388
+ *
389
+ * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
390
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to test
391
+ * @param predicate - Asynchronous function that tests each block. Should return true for valid blocks
392
+ * @returns Promise that resolves to true if all blocks match, false otherwise
393
+ */
394
+ export async function nonRecursiveEveryBlockInNonLocalizedFieldValueAsync(
395
+ fieldType: ApiTypes.Field['field_type'],
396
+ nonLocalizedFieldValue: unknown,
397
+ predicate: (item: BlockInRequest, path: TreePath) => Promise<boolean>,
398
+ ): Promise<boolean> {
399
+ return !(await nonRecursiveSomeBlocksInNonLocalizedFieldValueAsync(
400
+ fieldType,
401
+ nonLocalizedFieldValue,
402
+ async (item, path) => {
403
+ return !(await predicate(item, path));
404
+ },
405
+ ));
406
+ }
@@ -47,6 +47,25 @@ export function isLocalized(
47
47
  return 'attributes' in field ? field.attributes.localized : field.localized;
48
48
  }
49
49
 
50
+ export function isLocalizedFieldValue<T = unknown, L extends string = string>(
51
+ value: unknown,
52
+ ): value is LocalizedFieldValue<T, L> {
53
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
54
+ return false;
55
+ }
56
+
57
+ const keys = Object.keys(value);
58
+
59
+ if (keys.length === 0) {
60
+ return false;
61
+ }
62
+
63
+ const localePattern =
64
+ /^[A-Za-z]{2,4}(-[A-Za-z]{4})?(-([A-Za-z]{2}|[0-9]{3}))?$/;
65
+
66
+ return keys.every((key) => localePattern.test(key));
67
+ }
68
+
50
69
  /**
51
70
  * A normalized entry that represents a single value from either a localized or non-localized field.
52
71
  *
@@ -55,7 +74,10 @@ export function isLocalized(
55
74
  *
56
75
  * This uniform structure allows the same processing logic to work with both field types.
57
76
  */
58
- export type FieldValueEntry<T = unknown, L extends string = string> = {
77
+ export type NormalizedFieldValueEntry<
78
+ T = unknown,
79
+ L extends string = string,
80
+ > = {
59
81
  locale: L | undefined;
60
82
  value: T;
61
83
  };
@@ -70,10 +92,13 @@ export type FieldValueEntry<T = unknown, L extends string = string> = {
70
92
  * @param value - The field value to convert (either a localized object or direct value)
71
93
  * @returns Array of entries where each entry contains a locale (string for localized, undefined for non-localized) and the corresponding value
72
94
  */
73
- export function fieldValueToEntries<T = unknown, L extends string = string>(
95
+ export function toNormalizedFieldValueEntries<
96
+ T = unknown,
97
+ L extends string = string,
98
+ >(
74
99
  field: RawApiTypes.Field | ApiTypes.Field,
75
100
  value: T | LocalizedFieldValue<T, L>,
76
- ): FieldValueEntry<T, L>[] {
101
+ ): NormalizedFieldValueEntry<T, L>[] {
77
102
  if (isLocalized(field)) {
78
103
  const localizedValue = value as LocalizedFieldValue<T, L>;
79
104
 
@@ -89,7 +114,7 @@ export function fieldValueToEntries<T = unknown, L extends string = string>(
89
114
  /**
90
115
  * Converts an array of possibly localized entries back into the appropriate field value format.
91
116
  *
92
- * This function is the inverse of `fieldValueToEntries`. It takes a uniform
117
+ * This function is the inverse of `toNormalizedFieldValueEntries`. It takes a uniform
93
118
  * array of entries and converts them back to either a localized object or a direct value,
94
119
  * depending on the field's localization setting.
95
120
  *
@@ -97,9 +122,12 @@ export function fieldValueToEntries<T = unknown, L extends string = string>(
97
122
  * @param entries - Array of entries to convert back to field value format
98
123
  * @returns Either a localized object (for localized fields) or the direct value (for non-localized fields)
99
124
  */
100
- export function entriesToFieldValue<T = unknown, L extends string = string>(
125
+ export function fromNormalizedFieldValueEntries<
126
+ T = unknown,
127
+ L extends string = string,
128
+ >(
101
129
  field: RawApiTypes.Field | ApiTypes.Field,
102
- entries: FieldValueEntry<T, L>[],
130
+ entries: NormalizedFieldValueEntry<T, L>[],
103
131
  ): T | LocalizedFieldValue<T, L> {
104
132
  if (isLocalized(field)) {
105
133
  return Object.fromEntries(
@@ -125,7 +153,7 @@ export function entriesToFieldValue<T = unknown, L extends string = string>(
125
153
  * @param mapFn - The function to apply to each locale value or the direct value
126
154
  * @returns The mapped value with the same structure as the input
127
155
  */
128
- export function mapFieldValue<
156
+ export function mapNormalizedFieldValues<
129
157
  TInput = unknown,
130
158
  TOutput = unknown,
131
159
  L extends string = string,
@@ -134,12 +162,12 @@ export function mapFieldValue<
134
162
  value: TInput | LocalizedFieldValue<TInput, L>,
135
163
  mapFn: (locale: L | undefined, localeValue: TInput) => TOutput,
136
164
  ): TOutput | LocalizedFieldValue<TOutput, L> {
137
- const entries = fieldValueToEntries<TInput, L>(field, value);
165
+ const entries = toNormalizedFieldValueEntries<TInput, L>(field, value);
138
166
  const mappedEntries = entries.map(({ locale, value }) => ({
139
167
  locale,
140
168
  value: mapFn(locale, value),
141
169
  }));
142
- return entriesToFieldValue<TOutput, L>(field, mappedEntries);
170
+ return fromNormalizedFieldValueEntries<TOutput, L>(field, mappedEntries);
143
171
  }
144
172
 
145
173
  /**
@@ -153,7 +181,7 @@ export function mapFieldValue<
153
181
  * @param mapFn - The function to apply to each locale value or the direct value
154
182
  * @returns The mapped value with the same structure as the input
155
183
  */
156
- export async function mapFieldValueAsync<
184
+ export async function mapNormalizedFieldValuesAsync<
157
185
  TInput = unknown,
158
186
  TOutput = unknown,
159
187
  L extends string = string,
@@ -162,14 +190,14 @@ export async function mapFieldValueAsync<
162
190
  value: TInput | LocalizedFieldValue<TInput, L>,
163
191
  mapFn: (locale: L | undefined, localeValue: TInput) => Promise<TOutput>,
164
192
  ): Promise<TOutput | LocalizedFieldValue<TOutput, L>> {
165
- const entries = fieldValueToEntries<TInput, L>(field, value);
193
+ const entries = toNormalizedFieldValueEntries<TInput, L>(field, value);
166
194
  const mappedEntries = await Promise.all(
167
195
  entries.map(async ({ locale, value }) => ({
168
196
  locale,
169
197
  value: await mapFn(locale, value),
170
198
  })),
171
199
  );
172
- return entriesToFieldValue<TOutput, L>(field, mappedEntries);
200
+ return fromNormalizedFieldValueEntries<TOutput, L>(field, mappedEntries);
173
201
  }
174
202
 
175
203
  /**
@@ -182,18 +210,21 @@ export async function mapFieldValueAsync<
182
210
  * @param filterFn - The function to test each locale value or the direct value
183
211
  * @returns The filtered value with the same structure as the input
184
212
  */
185
- export function filterFieldValue<T = unknown, L extends string = string>(
213
+ export function filterNormalizedFieldValues<
214
+ T = unknown,
215
+ L extends string = string,
216
+ >(
186
217
  field: RawApiTypes.Field | ApiTypes.Field,
187
218
  value: T | LocalizedFieldValue<T, L>,
188
219
  filterFn: (locale: L | undefined, localeValue: T) => boolean,
189
220
  ): T | LocalizedFieldValue<T, L> | undefined {
190
- const entries = fieldValueToEntries<T, L>(field, value);
221
+ const entries = toNormalizedFieldValueEntries<T, L>(field, value);
191
222
  const filteredEntries = entries.filter((entry) =>
192
223
  filterFn(entry.locale, entry.value),
193
224
  );
194
225
 
195
226
  if (isLocalized(field)) {
196
- return entriesToFieldValue<T, L>(field, filteredEntries);
227
+ return fromNormalizedFieldValueEntries<T, L>(field, filteredEntries);
197
228
  }
198
229
 
199
230
  return filteredEntries.length > 0 ? filteredEntries[0]?.value : undefined;
@@ -209,7 +240,7 @@ export function filterFieldValue<T = unknown, L extends string = string>(
209
240
  * @param filterFn - The function to test each locale value or the direct value
210
241
  * @returns The filtered value with the same structure as the input
211
242
  */
212
- export async function filterFieldValueAsync<
243
+ export async function filterNormalizedFieldValuesAsync<
213
244
  T = unknown,
214
245
  L extends string = string,
215
246
  >(
@@ -217,7 +248,7 @@ export async function filterFieldValueAsync<
217
248
  value: T | LocalizedFieldValue<T, L>,
218
249
  filterFn: (locale: L | undefined, localeValue: T) => Promise<boolean>,
219
250
  ): Promise<T | LocalizedFieldValue<T, L> | undefined> {
220
- const entries = fieldValueToEntries<T, L>(field, value);
251
+ const entries = toNormalizedFieldValueEntries<T, L>(field, value);
221
252
  const results = await Promise.all(
222
253
  entries.map(async ({ locale, value }) => ({
223
254
  locale,
@@ -231,7 +262,7 @@ export async function filterFieldValueAsync<
231
262
  .map(({ locale, value }) => ({ locale, value }));
232
263
 
233
264
  if (isLocalized(field)) {
234
- return entriesToFieldValue<T, L>(field, filteredEntries);
265
+ return fromNormalizedFieldValueEntries<T, L>(field, filteredEntries);
235
266
  }
236
267
 
237
268
  return filteredEntries.length > 0 ? filteredEntries[0]?.value : undefined;
@@ -247,12 +278,15 @@ export async function filterFieldValueAsync<
247
278
  * @param testFn - The function to test each locale value or the direct value
248
279
  * @returns true if at least one value passes the test, false otherwise
249
280
  */
250
- export function someFieldValue<T = unknown, L extends string = string>(
281
+ export function someNormalizedFieldValues<
282
+ T = unknown,
283
+ L extends string = string,
284
+ >(
251
285
  field: RawApiTypes.Field | ApiTypes.Field,
252
286
  value: T | LocalizedFieldValue<T, L>,
253
287
  testFn: (locale: L | undefined, localeValue: T) => boolean,
254
288
  ): boolean {
255
- const entries = fieldValueToEntries<T, L>(field, value);
289
+ const entries = toNormalizedFieldValueEntries<T, L>(field, value);
256
290
  return entries.some(({ locale, value }) => testFn(locale, value));
257
291
  }
258
292
 
@@ -266,7 +300,7 @@ export function someFieldValue<T = unknown, L extends string = string>(
266
300
  * @param testFn - The function to test each locale value or the direct value
267
301
  * @returns true if at least one value passes the test, false otherwise
268
302
  */
269
- export async function someFieldValueAsync<
303
+ export async function someNormalizedFieldValuesAsync<
270
304
  T = unknown,
271
305
  L extends string = string,
272
306
  >(
@@ -274,7 +308,7 @@ export async function someFieldValueAsync<
274
308
  value: T | LocalizedFieldValue<T, L>,
275
309
  testFn: (locale: L | undefined, localeValue: T) => Promise<boolean>,
276
310
  ): Promise<boolean> {
277
- const entries = fieldValueToEntries<T, L>(field, value);
311
+ const entries = toNormalizedFieldValueEntries<T, L>(field, value);
278
312
  const results = await Promise.all(
279
313
  entries.map(({ locale, value }) => testFn(locale, value)),
280
314
  );
@@ -291,12 +325,15 @@ export async function someFieldValueAsync<
291
325
  * @param testFn - The function to test each locale value or the direct value
292
326
  * @returns true if all values pass the test, false otherwise
293
327
  */
294
- export function everyFieldValue<T = unknown, L extends string = string>(
328
+ export function everyNormalizedFieldValue<
329
+ T = unknown,
330
+ L extends string = string,
331
+ >(
295
332
  field: RawApiTypes.Field | ApiTypes.Field,
296
333
  value: T | LocalizedFieldValue<T, L>,
297
334
  testFn: (locale: L | undefined, localeValue: T) => boolean,
298
335
  ): boolean {
299
- return !someFieldValue(
336
+ return !someNormalizedFieldValues(
300
337
  field,
301
338
  value,
302
339
  (locale, localeValue) => !testFn(locale, localeValue),
@@ -313,7 +350,7 @@ export function everyFieldValue<T = unknown, L extends string = string>(
313
350
  * @param testFn - The function to test each locale value or the direct value
314
351
  * @returns true if all values pass the test, false otherwise
315
352
  */
316
- export async function everyFieldValueAsync<
353
+ export async function everyNormalizedFieldValueAsync<
317
354
  T = unknown,
318
355
  L extends string = string,
319
356
  >(
@@ -321,7 +358,7 @@ export async function everyFieldValueAsync<
321
358
  value: T | LocalizedFieldValue<T, L>,
322
359
  testFn: (locale: L | undefined, localeValue: T) => Promise<boolean>,
323
360
  ): Promise<boolean> {
324
- return !(await someFieldValueAsync(
361
+ return !(await someNormalizedFieldValuesAsync(
325
362
  field,
326
363
  value,
327
364
  async (locale, localeValue) => !(await testFn(locale, localeValue)),
@@ -337,12 +374,15 @@ export async function everyFieldValueAsync<
337
374
  * @param value - The field value (either localized object or direct value)
338
375
  * @param visitFn - The function to call for each locale value or the direct value
339
376
  */
340
- export function visitFieldValue<T = unknown, L extends string = string>(
377
+ export function visitNormalizedFieldValues<
378
+ T = unknown,
379
+ L extends string = string,
380
+ >(
341
381
  field: RawApiTypes.Field | ApiTypes.Field,
342
382
  value: T | LocalizedFieldValue<T, L>,
343
383
  visitFn: (locale: L | undefined, localeValue: T) => void,
344
384
  ): void {
345
- const entries = fieldValueToEntries<T, L>(field, value);
385
+ const entries = toNormalizedFieldValueEntries<T, L>(field, value);
346
386
  for (const { locale, value } of entries) {
347
387
  visitFn(locale, value);
348
388
  }
@@ -357,7 +397,7 @@ export function visitFieldValue<T = unknown, L extends string = string>(
357
397
  * @param value - The field value (either localized object or direct value)
358
398
  * @param visitFn - The function to call for each locale value or the direct value
359
399
  */
360
- export async function visitFieldValueAsync<
400
+ export async function visitNormalizedFieldValuesAsync<
361
401
  T = unknown,
362
402
  L extends string = string,
363
403
  >(
@@ -365,6 +405,6 @@ export async function visitFieldValueAsync<
365
405
  value: T | LocalizedFieldValue<T, L>,
366
406
  visitFn: (locale: L | undefined, localeValue: T) => Promise<void>,
367
407
  ): Promise<void> {
368
- const entries = fieldValueToEntries<T, L>(field, value);
408
+ const entries = toNormalizedFieldValueEntries<T, L>(field, value);
369
409
  await Promise.all(entries.map(({ locale, value }) => visitFn(locale, value)));
370
410
  }