@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
@@ -1,35 +1,90 @@
1
1
  import {
2
- type BlockItemInARequest,
2
+ type BlockInRequest,
3
+ type RichTextFieldValue,
4
+ type RichTextFieldValueInNestedResponse,
5
+ type RichTextFieldValueInRequest,
6
+ type SingleBlockFieldValue,
7
+ type SingleBlockFieldValueInNestedResponse,
8
+ type SingleBlockFieldValueInRequest,
9
+ type StructuredTextFieldValue,
10
+ type StructuredTextFieldValueInNestedResponse,
11
+ type StructuredTextFieldValueInRequest,
3
12
  isItemWithOptionalIdAndMeta,
4
13
  } from '../fieldTypes';
5
14
  import type * as ApiTypes from '../generated/ApiTypes';
6
- import type * as RawApiTypes from '../generated/RawApiTypes';
15
+ import type { ExtractNestedBlocksFromFieldValue } from './itemDefinition';
7
16
  import {
8
- nonRecursiveFilterBlocksInFieldValueAsync,
9
- nonRecursiveFindAllBlocksInFieldValueAsync,
10
- nonRecursiveMapBlocksInFieldValueAsync,
11
- nonRecursiveReduceBlocksInFieldValueAsync,
12
- nonRecursiveSomeBlocksInFieldValueAsync,
13
- nonRecursiveVisitBlocksInFieldValueAsync,
14
- } from './blocks';
15
- import { mapFieldValueAsync, visitFieldValueAsync } from './fieldValue';
17
+ nonRecursiveFilterBlocksInNonLocalizedFieldValueAsync,
18
+ nonRecursiveFindAllBlocksInNonLocalizedFieldValueAsync,
19
+ nonRecursiveMapBlocksInNonLocalizedFieldValueAsync,
20
+ nonRecursiveReduceBlocksInNonLocalizedFieldValueAsync,
21
+ nonRecursiveSomeBlocksInNonLocalizedFieldValueAsync,
22
+ nonRecursiveVisitBlocksInNonLocalizedFieldValueAsync,
23
+ } from './nonRecursiveBlocks';
16
24
  import type { SchemaRepository } from './schemaRepository';
17
25
 
26
+ type RecognizableFieldValue =
27
+ | RichTextFieldValueInNestedResponse
28
+ | SingleBlockFieldValueInNestedResponse
29
+ | StructuredTextFieldValueInNestedResponse
30
+ | RichTextFieldValueInRequest
31
+ | SingleBlockFieldValueInRequest
32
+ | StructuredTextFieldValueInRequest
33
+ | RichTextFieldValue
34
+ | SingleBlockFieldValue
35
+ | StructuredTextFieldValue;
36
+
18
37
  /**
19
- * Path through a field value (ie. ['content', 0, 'attributes', 'title'])
38
+ * Path through a non-localized field value (ie. ['content', 0, 'attributes', 'title'])
20
39
  */
21
40
  export type TreePath = readonly (string | number)[];
22
41
 
23
- export async function visitBlocksInFieldValues(
42
+ /**
43
+ * Traversal direction for recursive operations
44
+ */
45
+ export type TraversalDirection = 'top-down' | 'bottom-up';
46
+
47
+ /**
48
+ * Recursively visit every block in a non-localized field value and all nested blocks within those blocks.
49
+ * This function traverses not only the direct blocks in the non-localized field value but also recursively
50
+ * visits blocks contained within the attributes of each block, creating a complete traversal
51
+ * of the entire block hierarchy.
52
+ *
53
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to visit
54
+ * @param fieldType - The type field (determines how the value is processed)
55
+ * @param schemaRepository - Repository for accessing DatoCMS schema information (to resolve block structures)
56
+ * @param visitor - Asynchronous function called for each block found, including nested blocks
57
+ * @returns Promise that resolves when all blocks and nested blocks have been visited
58
+ */
59
+ export async function visitBlocksInNonLocalizedFieldValue<
60
+ T extends RecognizableFieldValue,
61
+ >(
62
+ nonLocalizedFieldValue: T,
63
+ fieldType: ApiTypes.Field['field_type'],
64
+ schemaRepository: SchemaRepository,
65
+ visitor: (
66
+ item: ExtractNestedBlocksFromFieldValue<T>,
67
+ path: TreePath,
68
+ ) => void | Promise<void>,
69
+ path?: TreePath,
70
+ ): Promise<void>;
71
+ export async function visitBlocksInNonLocalizedFieldValue(
72
+ nonLocalizedFieldValue: unknown,
73
+ fieldType: ApiTypes.Field['field_type'],
24
74
  schemaRepository: SchemaRepository,
25
- field: RawApiTypes.Field | ApiTypes.Field,
26
- value: unknown,
27
- visitor: (item: BlockItemInARequest, path: TreePath) => void | Promise<void>,
75
+ visitor: (item: BlockInRequest, path: TreePath) => void | Promise<void>,
76
+ path?: TreePath,
77
+ ): Promise<void>;
78
+ export async function visitBlocksInNonLocalizedFieldValue(
79
+ nonLocalizedFieldValue: unknown,
80
+ fieldType: ApiTypes.Field['field_type'],
81
+ schemaRepository: SchemaRepository,
82
+ visitor: (item: BlockInRequest, path: TreePath) => void | Promise<void>,
28
83
  path: TreePath = [],
29
84
  ): Promise<void> {
30
- await nonRecursiveVisitBlocksInFieldValueAsync(
31
- field,
32
- value,
85
+ await nonRecursiveVisitBlocksInNonLocalizedFieldValueAsync(
86
+ fieldType,
87
+ nonLocalizedFieldValue,
33
88
  async (block, innerPath) => {
34
89
  await visitor(block, [...path, ...innerPath]);
35
90
 
@@ -44,46 +99,72 @@ export async function visitBlocksInFieldValues(
44
99
  const fields = await schemaRepository.getRawItemTypeFields(itemType);
45
100
 
46
101
  for (const field of fields) {
47
- await visitFieldValueAsync(
48
- field,
102
+ await visitBlocksInNonLocalizedFieldValue(
49
103
  block.attributes[field.attributes.api_key],
50
- (locale, valueForLocale) =>
51
- visitBlocksInFieldValues(
52
- schemaRepository,
53
- field,
54
- valueForLocale,
55
- visitor,
56
- [
57
- ...path,
58
- ...innerPath,
59
- 'attributes',
60
- field.attributes.api_key,
61
- ...(locale ? [locale] : []),
62
- ],
63
- ),
104
+ field.attributes.field_type,
105
+ schemaRepository,
106
+ visitor,
107
+ [...path, ...innerPath, 'attributes', field.attributes.api_key],
64
108
  );
65
109
  }
66
110
  },
67
111
  );
68
112
  }
69
113
 
70
- export async function findAllBlocksInFieldValues(
114
+ /**
115
+ * Recursively find all blocks that match the predicate function in a non-localized field value.
116
+ * Searches through all direct blocks and recursively through nested blocks within
117
+ * the attributes of each block, returning all matches found throughout the hierarchy.
118
+ *
119
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to search
120
+ * @param fieldType - The type field (determines how the value is processed)
121
+ * @param schemaRepository - Repository for accessing DatoCMS schema information (to resolve block structures)
122
+ * @param predicate - Asynchronous function that tests each block, including nested ones
123
+ * @returns Promise that resolves to an array of objects, each containing a matching block and its full path
124
+ */
125
+ export async function findAllBlocksInNonLocalizedFieldValue<
126
+ T extends RecognizableFieldValue,
127
+ >(
128
+ nonLocalizedFieldValue: T,
129
+ fieldType: ApiTypes.Field['field_type'],
130
+ schemaRepository: SchemaRepository,
131
+ predicate: (
132
+ item: ExtractNestedBlocksFromFieldValue<T>,
133
+ path: TreePath,
134
+ ) => boolean | Promise<boolean>,
135
+ path?: TreePath,
136
+ ): Promise<
137
+ Array<{ item: ExtractNestedBlocksFromFieldValue<T>; path: TreePath }>
138
+ >;
139
+ export async function findAllBlocksInNonLocalizedFieldValue(
140
+ nonLocalizedFieldValue: unknown,
141
+ fieldType: ApiTypes.Field['field_type'],
71
142
  schemaRepository: SchemaRepository,
72
- field: RawApiTypes.Field | ApiTypes.Field,
73
- value: unknown,
74
143
  predicate: (
75
- item: BlockItemInARequest,
144
+ item: BlockInRequest,
145
+ path: TreePath,
146
+ ) => boolean | Promise<boolean>,
147
+ path?: TreePath,
148
+ ): Promise<Array<{ item: BlockInRequest; path: TreePath }>>;
149
+ export async function findAllBlocksInNonLocalizedFieldValue(
150
+ nonLocalizedFieldValue: unknown,
151
+ fieldType: ApiTypes.Field['field_type'],
152
+ schemaRepository: SchemaRepository,
153
+ predicate: (
154
+ item: BlockInRequest,
76
155
  path: TreePath,
77
156
  ) => boolean | Promise<boolean>,
78
157
  path: TreePath = [],
79
- ): Promise<Array<{ item: BlockItemInARequest; path: TreePath }>> {
80
- const results: Array<{ item: BlockItemInARequest; path: TreePath }> = [];
158
+ ): Promise<Array<{ item: BlockInRequest; path: TreePath }>> {
159
+ const results: Array<{ item: BlockInRequest; path: TreePath }> = [];
81
160
 
82
- const directMatches = await nonRecursiveFindAllBlocksInFieldValueAsync(
83
- field,
84
- value,
85
- async (block, innerPath) => await predicate(block, [...path, ...innerPath]),
86
- );
161
+ const directMatches =
162
+ await nonRecursiveFindAllBlocksInNonLocalizedFieldValueAsync(
163
+ fieldType,
164
+ nonLocalizedFieldValue,
165
+ async (block, innerPath) =>
166
+ await predicate(block, [...path, ...innerPath]),
167
+ );
87
168
 
88
169
  results.push(
89
170
  ...directMatches.map(({ item, path: innerPath }) => ({
@@ -92,9 +173,9 @@ export async function findAllBlocksInFieldValues(
92
173
  })),
93
174
  );
94
175
 
95
- await nonRecursiveVisitBlocksInFieldValueAsync(
96
- field,
97
- value,
176
+ await nonRecursiveVisitBlocksInNonLocalizedFieldValueAsync(
177
+ fieldType,
178
+ nonLocalizedFieldValue,
98
179
  async (block, innerPath) => {
99
180
  if (!isItemWithOptionalIdAndMeta(block)) {
100
181
  return;
@@ -107,26 +188,14 @@ export async function findAllBlocksInFieldValues(
107
188
  const fields = await schemaRepository.getRawItemTypeFields(itemType);
108
189
 
109
190
  for (const field of fields) {
110
- await visitFieldValueAsync(
111
- field,
191
+ const nestedResults = await findAllBlocksInNonLocalizedFieldValue(
112
192
  block.attributes[field.attributes.api_key],
113
- async (locale, valueForLocale) => {
114
- const nestedResults = await findAllBlocksInFieldValues(
115
- schemaRepository,
116
- field,
117
- valueForLocale,
118
- predicate,
119
- [
120
- ...path,
121
- ...innerPath,
122
- 'attributes',
123
- field.attributes.api_key,
124
- ...(locale ? [locale] : []),
125
- ],
126
- );
127
- results.push(...nestedResults);
128
- },
193
+ field.attributes.field_type,
194
+ schemaRepository,
195
+ predicate,
196
+ [...path, ...innerPath, 'attributes', field.attributes.api_key],
129
197
  );
198
+ results.push(...nestedResults);
130
199
  }
131
200
  },
132
201
  );
@@ -134,85 +203,182 @@ export async function findAllBlocksInFieldValues(
134
203
  return results;
135
204
  }
136
205
 
137
- export async function filterBlocksInFieldValues(
206
+ /**
207
+ * Recursively filter blocks in a non-localized field value, removing those that don't match the predicate.
208
+ * Creates a new non-localized field value structure containing only blocks that pass the predicate test,
209
+ * including recursive filtering of nested blocks within block attributes. The filtering
210
+ * preserves the original non-localized field value structure and hierarchy.
211
+ *
212
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to filter
213
+ * @param fieldType - The type field (determines how the value is processed)
214
+ * @param schemaRepository - Repository for accessing DatoCMS schema information (to resolve block structures)
215
+ * @param predicate - Asynchronous function that tests each block, including nested ones
216
+ * @param options - Optional configuration object
217
+ * @param options.traversalDirection - Direction of traversal: 'top-down' (default) applies predicate before processing children, 'bottom-up' processes children first
218
+ * @returns Promise that resolves to the new non-localized field value with recursively filtered blocks
219
+ */
220
+ export async function filterBlocksInNonLocalizedFieldValue<
221
+ T extends RecognizableFieldValue,
222
+ >(
223
+ nonLocalizedFieldValue: T,
224
+ fieldType: ApiTypes.Field['field_type'],
138
225
  schemaRepository: SchemaRepository,
139
- field: RawApiTypes.Field | ApiTypes.Field,
140
- value: unknown,
141
226
  predicate: (
142
- item: BlockItemInARequest,
227
+ item: ExtractNestedBlocksFromFieldValue<T>,
143
228
  path: TreePath,
144
229
  ) => boolean | Promise<boolean>,
230
+ options?: { traversalDirection?: TraversalDirection },
231
+ path?: TreePath,
232
+ ): Promise<T>;
233
+ export async function filterBlocksInNonLocalizedFieldValue(
234
+ nonLocalizedFieldValue: unknown,
235
+ fieldType: ApiTypes.Field['field_type'],
236
+ schemaRepository: SchemaRepository,
237
+ predicate: (
238
+ item: BlockInRequest,
239
+ path: TreePath,
240
+ ) => boolean | Promise<boolean>,
241
+ options?: { traversalDirection?: TraversalDirection },
242
+ path?: TreePath,
243
+ ): Promise<unknown>;
244
+ export async function filterBlocksInNonLocalizedFieldValue(
245
+ nonLocalizedFieldValue: unknown,
246
+ fieldType: ApiTypes.Field['field_type'],
247
+ schemaRepository: SchemaRepository,
248
+ predicate: (
249
+ item: BlockInRequest,
250
+ path: TreePath,
251
+ ) => boolean | Promise<boolean>,
252
+ options: { traversalDirection?: TraversalDirection } = {},
145
253
  path: TreePath = [],
146
254
  ): Promise<unknown> {
147
- return nonRecursiveFilterBlocksInFieldValueAsync(
148
- field,
149
- value,
150
- async (block, innerPath) => {
151
- const blockPath = [...path, ...innerPath];
152
- const passes = await predicate(block, blockPath);
255
+ const { traversalDirection = 'top-down' } = options;
153
256
 
154
- if (!passes) {
155
- return false;
156
- }
257
+ const mapperFunc = async (block: BlockInRequest, innerPath: TreePath) => {
258
+ const blockPath = [...path, ...innerPath];
157
259
 
158
- if (!isItemWithOptionalIdAndMeta(block)) {
159
- return true;
160
- }
260
+ if (!isItemWithOptionalIdAndMeta(block)) {
261
+ return block;
262
+ }
161
263
 
162
- const itemType = await schemaRepository.getRawItemTypeById(
163
- block.relationships.item_type.data.id,
164
- );
264
+ const itemType = await schemaRepository.getRawItemTypeById(
265
+ block.relationships.item_type.data.id,
266
+ );
165
267
 
166
- const fields = await schemaRepository.getRawItemTypeFields(itemType);
268
+ const fields = await schemaRepository.getRawItemTypeFields(itemType);
269
+
270
+ if (traversalDirection === 'top-down') {
271
+ const blockCopy = { ...block, attributes: { ...block.attributes } };
167
272
 
168
273
  for (const field of fields) {
169
- block.attributes[field.attributes.api_key] = await mapFieldValueAsync(
170
- field,
171
- block.attributes[field.attributes.api_key],
172
- (locale, valueForLocale) =>
173
- filterBlocksInFieldValues(
174
- schemaRepository,
175
- field,
176
- valueForLocale,
177
- predicate,
178
- [
179
- ...blockPath,
180
- 'attributes',
181
- field.attributes.api_key,
182
- ...(locale ? [locale] : []),
183
- ],
184
- ),
185
- );
274
+ blockCopy.attributes[field.attributes.api_key] =
275
+ await filterBlocksInNonLocalizedFieldValue(
276
+ blockCopy.attributes[field.attributes.api_key],
277
+ field.attributes.field_type,
278
+ schemaRepository,
279
+
280
+ predicate,
281
+ options,
282
+ [...blockPath, 'attributes', field.attributes.api_key],
283
+ );
186
284
  }
187
285
 
188
- return true;
286
+ return blockCopy;
287
+ }
288
+
289
+ const blockCopy = { ...block, attributes: { ...block.attributes } };
290
+
291
+ for (const field of fields) {
292
+ blockCopy.attributes[field.attributes.api_key] =
293
+ await filterBlocksInNonLocalizedFieldValue(
294
+ blockCopy.attributes[field.attributes.api_key],
295
+ field.attributes.field_type,
296
+ schemaRepository,
297
+ predicate,
298
+ options,
299
+ [...blockPath, 'attributes', field.attributes.api_key],
300
+ );
301
+ }
302
+
303
+ return blockCopy;
304
+ };
305
+
306
+ const mappedValue = await nonRecursiveMapBlocksInNonLocalizedFieldValueAsync(
307
+ fieldType,
308
+ nonLocalizedFieldValue,
309
+ mapperFunc,
310
+ );
311
+
312
+ return nonRecursiveFilterBlocksInNonLocalizedFieldValueAsync(
313
+ fieldType,
314
+ mappedValue,
315
+ async (block, innerPath) => {
316
+ const blockPath = [...path, ...innerPath];
317
+ return await predicate(block, blockPath);
189
318
  },
190
319
  );
191
320
  }
192
321
 
193
- export async function reduceBlocksInFieldValues<R>(
322
+ /**
323
+ * Recursively reduce all blocks in a non-localized field value to a single value by applying a reducer function.
324
+ * Processes each direct block and recursively processes nested blocks within block attributes,
325
+ * accumulating results from the entire block hierarchy into a single value.
326
+ *
327
+ * @template R - The type of the accumulated result
328
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to reduce
329
+ * @param fieldType - The type field (determines how the value is processed)
330
+ * @param schemaRepository - Repository for accessing DatoCMS schema information (to resolve block structures)
331
+ * @param reducer - Asynchronous function that processes each block and updates the accumulator
332
+ * @param initialNonLocalizedFieldValue - The initial value for the accumulator
333
+ * @returns Promise that resolves to the final accumulated value from all blocks in the hierarchy
334
+ */
335
+ export async function reduceBlocksInNonLocalizedFieldValue<T, R>(
336
+ nonLocalizedFieldValue: T,
337
+ fieldType: ApiTypes.Field['field_type'],
338
+ schemaRepository: SchemaRepository,
339
+ reducer: (
340
+ accumulator: R,
341
+ item: ExtractNestedBlocksFromFieldValue<T>,
342
+ path: TreePath,
343
+ ) => R | Promise<R>,
344
+ initialValue: R,
345
+ path?: TreePath,
346
+ ): Promise<R>;
347
+ export async function reduceBlocksInNonLocalizedFieldValue<R>(
348
+ nonLocalizedFieldValue: unknown,
349
+ fieldType: ApiTypes.Field['field_type'],
350
+ schemaRepository: SchemaRepository,
351
+ reducer: (
352
+ accumulator: R,
353
+ item: BlockInRequest,
354
+ path: TreePath,
355
+ ) => R | Promise<R>,
356
+ initialValue: R,
357
+ path?: TreePath,
358
+ ): Promise<R>;
359
+ export async function reduceBlocksInNonLocalizedFieldValue<R>(
360
+ nonLocalizedFieldValue: unknown,
361
+ fieldType: ApiTypes.Field['field_type'],
194
362
  schemaRepository: SchemaRepository,
195
- field: RawApiTypes.Field | ApiTypes.Field,
196
- value: unknown,
197
363
  reducer: (
198
364
  accumulator: R,
199
- item: BlockItemInARequest,
365
+ item: BlockInRequest,
200
366
  path: TreePath,
201
367
  ) => R | Promise<R>,
202
368
  initialValue: R,
203
369
  path: TreePath = [],
204
370
  ): Promise<R> {
205
- let accumulator = await nonRecursiveReduceBlocksInFieldValueAsync(
206
- field,
207
- value,
371
+ let accumulator = await nonRecursiveReduceBlocksInNonLocalizedFieldValueAsync(
372
+ fieldType,
373
+ nonLocalizedFieldValue,
208
374
  async (acc, block, innerPath) =>
209
375
  await reducer(acc, block, [...path, ...innerPath]),
210
376
  initialValue,
211
377
  );
212
378
 
213
- await nonRecursiveVisitBlocksInFieldValueAsync(
214
- field,
215
- value,
379
+ await nonRecursiveVisitBlocksInNonLocalizedFieldValueAsync(
380
+ fieldType,
381
+ nonLocalizedFieldValue,
216
382
  async (block, innerPath) => {
217
383
  if (!isItemWithOptionalIdAndMeta(block)) {
218
384
  return;
@@ -225,25 +391,13 @@ export async function reduceBlocksInFieldValues<R>(
225
391
  const fields = await schemaRepository.getRawItemTypeFields(itemType);
226
392
 
227
393
  for (const field of fields) {
228
- await visitFieldValueAsync(
229
- field,
394
+ accumulator = await reduceBlocksInNonLocalizedFieldValue(
230
395
  block.attributes[field.attributes.api_key],
231
- async (locale, valueForLocale) => {
232
- accumulator = await reduceBlocksInFieldValues(
233
- schemaRepository,
234
- field,
235
- valueForLocale,
236
- reducer,
237
- accumulator,
238
- [
239
- ...path,
240
- ...innerPath,
241
- 'attributes',
242
- field.attributes.api_key,
243
- ...(locale ? [locale] : []),
244
- ],
245
- );
246
- },
396
+ field.attributes.field_type,
397
+ schemaRepository,
398
+ reducer,
399
+ accumulator,
400
+ [...path, ...innerPath, 'attributes', field.attributes.api_key],
247
401
  );
248
402
  }
249
403
  },
@@ -252,19 +406,53 @@ export async function reduceBlocksInFieldValues<R>(
252
406
  return accumulator;
253
407
  }
254
408
 
255
- export async function someBlocksInFieldValues(
409
+ /**
410
+ * Recursively check if any block in the non-localized field value matches the predicate function.
411
+ * Tests both direct blocks and recursively tests nested blocks within block attributes.
412
+ * Returns true as soon as the first matching block is found anywhere in the hierarchy
413
+ * (short-circuit evaluation).
414
+ *
415
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to test
416
+ * @param fieldType - The type field (determines how the value is processed)
417
+ * @param schemaRepository - Repository for accessing DatoCMS schema information (to resolve block structures)
418
+ * @param predicate - Asynchronous function that tests each block, including nested ones
419
+ * @returns Promise that resolves to true if any block in the hierarchy matches, false otherwise
420
+ */
421
+ export async function someBlocksInNonLocalizedFieldValue<
422
+ T extends RecognizableFieldValue,
423
+ >(
424
+ nonLocalizedFieldValue: T,
425
+ fieldType: ApiTypes.Field['field_type'],
256
426
  schemaRepository: SchemaRepository,
257
- field: RawApiTypes.Field | ApiTypes.Field,
258
- value: unknown,
259
427
  predicate: (
260
- item: BlockItemInARequest,
428
+ item: ExtractNestedBlocksFromFieldValue<T>,
429
+ path: TreePath,
430
+ ) => boolean | Promise<boolean>,
431
+ path?: TreePath,
432
+ ): Promise<boolean>;
433
+ export async function someBlocksInNonLocalizedFieldValue(
434
+ nonLocalizedFieldValue: unknown,
435
+ fieldType: ApiTypes.Field['field_type'],
436
+ schemaRepository: SchemaRepository,
437
+ predicate: (
438
+ item: BlockInRequest,
439
+ path: TreePath,
440
+ ) => boolean | Promise<boolean>,
441
+ path?: TreePath,
442
+ ): Promise<boolean>;
443
+ export async function someBlocksInNonLocalizedFieldValue(
444
+ nonLocalizedFieldValue: unknown,
445
+ fieldType: ApiTypes.Field['field_type'],
446
+ schemaRepository: SchemaRepository,
447
+ predicate: (
448
+ item: BlockInRequest,
261
449
  path: TreePath,
262
450
  ) => boolean | Promise<boolean>,
263
451
  path: TreePath = [],
264
452
  ): Promise<boolean> {
265
- const directMatch = await nonRecursiveSomeBlocksInFieldValueAsync(
266
- field,
267
- value,
453
+ const directMatch = await nonRecursiveSomeBlocksInNonLocalizedFieldValueAsync(
454
+ fieldType,
455
+ nonLocalizedFieldValue,
268
456
  async (block, innerPath) => await predicate(block, [...path, ...innerPath]),
269
457
  );
270
458
 
@@ -273,9 +461,9 @@ export async function someBlocksInFieldValues(
273
461
  }
274
462
 
275
463
  let found = false;
276
- await nonRecursiveVisitBlocksInFieldValueAsync(
277
- field,
278
- value,
464
+ await nonRecursiveVisitBlocksInNonLocalizedFieldValueAsync(
465
+ fieldType,
466
+ nonLocalizedFieldValue,
279
467
  async (block, innerPath) => {
280
468
  if (found || !isItemWithOptionalIdAndMeta(block)) {
281
469
  return;
@@ -290,31 +478,17 @@ export async function someBlocksInFieldValues(
290
478
  for (const field of fields) {
291
479
  if (found) break;
292
480
 
293
- await visitFieldValueAsync(
294
- field,
481
+ const nestedMatch = await someBlocksInNonLocalizedFieldValue(
295
482
  block.attributes[field.attributes.api_key],
296
- async (locale, valueForLocale) => {
297
- if (found) return;
298
-
299
- const nestedMatch = await someBlocksInFieldValues(
300
- schemaRepository,
301
- field,
302
- valueForLocale,
303
- predicate,
304
- [
305
- ...path,
306
- ...innerPath,
307
- 'attributes',
308
- field.attributes.api_key,
309
- ...(locale ? [locale] : []),
310
- ],
311
- );
312
-
313
- if (nestedMatch) {
314
- found = true;
315
- }
316
- },
483
+ field.attributes.field_type,
484
+ schemaRepository,
485
+ predicate,
486
+ [...path, ...innerPath, 'attributes', field.attributes.api_key],
317
487
  );
488
+
489
+ if (nestedMatch) {
490
+ found = true;
491
+ }
318
492
  }
319
493
  },
320
494
  );
@@ -322,74 +496,180 @@ export async function someBlocksInFieldValues(
322
496
  return found;
323
497
  }
324
498
 
325
- export async function everyBlockInFieldValues(
499
+ /**
500
+ * Recursively check if every block in the non-localized field value matches the predicate function.
501
+ * Tests both direct blocks and recursively tests nested blocks within block attributes.
502
+ * Returns false as soon as the first non-matching block is found anywhere in the hierarchy
503
+ * (short-circuit evaluation).
504
+ *
505
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to test
506
+ * @param fieldType - The type field (determines how the value is processed)
507
+ * @param schemaRepository - Repository for accessing DatoCMS schema information (to resolve block structures)
508
+ * @param predicate - Asynchronous function that tests each block, including nested ones
509
+ * @returns Promise that resolves to true if all blocks in the hierarchy match, false otherwise
510
+ */
511
+ export async function everyBlockInNonLocalizedFieldValue<
512
+ T extends RecognizableFieldValue,
513
+ >(
514
+ nonLocalizedFieldValue: T,
515
+ fieldType: ApiTypes.Field['field_type'],
326
516
  schemaRepository: SchemaRepository,
327
- field: RawApiTypes.Field | ApiTypes.Field,
328
- value: unknown,
329
517
  predicate: (
330
- item: BlockItemInARequest,
518
+ item: ExtractNestedBlocksFromFieldValue<T>,
519
+ path: TreePath,
520
+ ) => boolean | Promise<boolean>,
521
+ path?: TreePath,
522
+ ): Promise<boolean>;
523
+ export async function everyBlockInNonLocalizedFieldValue(
524
+ nonLocalizedFieldValue: unknown,
525
+ fieldType: ApiTypes.Field['field_type'],
526
+ schemaRepository: SchemaRepository,
527
+ predicate: (
528
+ item: BlockInRequest,
529
+ path: TreePath,
530
+ ) => boolean | Promise<boolean>,
531
+ path?: TreePath,
532
+ ): Promise<boolean>;
533
+ export async function everyBlockInNonLocalizedFieldValue(
534
+ nonLocalizedFieldValue: unknown,
535
+ fieldType: ApiTypes.Field['field_type'],
536
+ schemaRepository: SchemaRepository,
537
+ predicate: (
538
+ item: BlockInRequest,
331
539
  path: TreePath,
332
540
  ) => boolean | Promise<boolean>,
333
541
  path: TreePath = [],
334
542
  ): Promise<boolean> {
335
- return !(await someBlocksInFieldValues(
543
+ return !(await someBlocksInNonLocalizedFieldValue(
544
+ nonLocalizedFieldValue,
545
+ fieldType,
336
546
  schemaRepository,
337
- field,
338
- value,
339
547
  async (item, path) => !(await predicate(item, path)),
340
548
  path,
341
549
  ));
342
550
  }
343
551
 
344
- export async function mapBlocksInFieldValues(
552
+ // Converts fields
553
+ // RichTextFieldValueInNestedResponse<Block1>
554
+ // into their as-request variant
555
+ // RichTextFieldValueInRequest<Block1>
556
+ type FieldValueInRequest<T> = T extends RichTextFieldValueInNestedResponse<
557
+ infer D
558
+ >
559
+ ? RichTextFieldValueInRequest<D>
560
+ : T extends SingleBlockFieldValueInNestedResponse<infer D>
561
+ ? SingleBlockFieldValueInRequest<D>
562
+ : T extends StructuredTextFieldValueInNestedResponse<infer DB, infer DI>
563
+ ? StructuredTextFieldValueInRequest<DB, DI>
564
+ : T extends StructuredTextFieldValueInNestedResponse<infer DB>
565
+ ? StructuredTextFieldValueInRequest<DB>
566
+ : T;
567
+
568
+ /**
569
+ * Recursively transform blocks in a non-localized field value by applying a mapping function to each block.
570
+ * Creates a new non-localized field value structure with transformed blocks while preserving the original
571
+ * structure. Applies the mapping function to both direct blocks and recursively to nested
572
+ * blocks within block attributes throughout the entire hierarchy.
573
+ *
574
+ * @param nonLocalizedFieldValue - The non-localized field value containing blocks to transform
575
+ * @param fieldType - The type field (determines how the value is processed)
576
+ * @param schemaRepository - Repository for accessing DatoCMS schema information (to resolve block structures)
577
+ * @param mapper - Asynchronous function that transforms each block, including nested ones
578
+ * @param options - Optional configuration object
579
+ * @param options.traversalDirection - Direction of traversal: 'top-down' (default) applies mapper before processing children, 'bottom-up' processes children first
580
+ * @returns Promise that resolves to the new non-localized field value with recursively transformed blocks
581
+ */
582
+ export async function mapBlocksInNonLocalizedFieldValue<
583
+ T extends RecognizableFieldValue,
584
+ >(
585
+ nonLocalizedFieldValue: T,
586
+ fieldType: ApiTypes.Field['field_type'],
587
+ schemaRepository: SchemaRepository,
588
+ mapper: (
589
+ item: ExtractNestedBlocksFromFieldValue<T>,
590
+ path: TreePath,
591
+ ) => BlockInRequest | Promise<BlockInRequest>,
592
+ options?: { traversalDirection?: TraversalDirection },
593
+ path?: TreePath,
594
+ ): Promise<FieldValueInRequest<T>>;
595
+ export async function mapBlocksInNonLocalizedFieldValue(
596
+ nonLocalizedFieldValue: unknown,
597
+ fieldType: ApiTypes.Field['field_type'],
598
+ schemaRepository: SchemaRepository,
599
+ mapper: (
600
+ item: BlockInRequest,
601
+ path: TreePath,
602
+ ) => BlockInRequest | Promise<BlockInRequest>,
603
+ options?: { traversalDirection?: TraversalDirection },
604
+ path?: TreePath,
605
+ ): Promise<unknown>;
606
+ export async function mapBlocksInNonLocalizedFieldValue(
607
+ nonLocalizedFieldValue: unknown,
608
+ fieldType: ApiTypes.Field['field_type'],
345
609
  schemaRepository: SchemaRepository,
346
- field: RawApiTypes.Field | ApiTypes.Field,
347
- value: unknown,
348
610
  mapper: (
349
- item: BlockItemInARequest,
611
+ item: BlockInRequest,
350
612
  path: TreePath,
351
- ) => BlockItemInARequest | Promise<BlockItemInARequest>,
613
+ ) => BlockInRequest | Promise<BlockInRequest>,
614
+ options: { traversalDirection?: TraversalDirection } = {},
352
615
  path: TreePath = [],
353
- ) {
354
- return nonRecursiveMapBlocksInFieldValueAsync(
355
- field,
356
- value,
616
+ ): Promise<unknown> {
617
+ const { traversalDirection = 'top-down' } = options;
618
+
619
+ return nonRecursiveMapBlocksInNonLocalizedFieldValueAsync(
620
+ fieldType,
621
+ nonLocalizedFieldValue,
357
622
  async (block, innerPath) => {
358
- const newBlock = await mapper(block, [...path, ...innerPath]);
623
+ const blockPath = [...path, ...innerPath];
359
624
 
360
- if (!isItemWithOptionalIdAndMeta(newBlock)) {
361
- return newBlock;
625
+ if (!isItemWithOptionalIdAndMeta(block)) {
626
+ return await mapper(block, blockPath);
362
627
  }
363
628
 
364
629
  const itemType = await schemaRepository.getRawItemTypeById(
365
- newBlock.relationships.item_type.data.id,
630
+ block.relationships.item_type.data.id,
366
631
  );
367
632
 
368
633
  const fields = await schemaRepository.getRawItemTypeFields(itemType);
369
634
 
635
+ if (traversalDirection === 'top-down') {
636
+ const newBlock = await mapper(block, blockPath);
637
+
638
+ if (!isItemWithOptionalIdAndMeta(newBlock)) {
639
+ return newBlock;
640
+ }
641
+
642
+ for (const field of fields) {
643
+ newBlock.attributes[field.attributes.api_key] =
644
+ await mapBlocksInNonLocalizedFieldValue(
645
+ newBlock.attributes[field.attributes.api_key],
646
+ field.attributes.field_type,
647
+ schemaRepository,
648
+ mapper,
649
+ options,
650
+ [...blockPath, 'attributes', field.attributes.api_key],
651
+ );
652
+ }
653
+
654
+ return newBlock;
655
+ }
656
+
657
+ const blockCopy = { ...block };
658
+
370
659
  for (const field of fields) {
371
- newBlock.attributes[field.attributes.api_key] =
372
- await mapFieldValueAsync(
373
- field,
374
- newBlock.attributes[field.attributes.api_key],
375
- (locale, valueForLocale) =>
376
- mapBlocksInFieldValues(
377
- schemaRepository,
378
- field,
379
- valueForLocale,
380
- mapper,
381
- [
382
- ...path,
383
- ...innerPath,
384
- 'attributes',
385
- field.attributes.api_key,
386
- ...(locale ? [locale] : []),
387
- ],
388
- ),
660
+ blockCopy.attributes[field.attributes.api_key] =
661
+ await mapBlocksInNonLocalizedFieldValue(
662
+ blockCopy.attributes[field.attributes.api_key],
663
+ field.attributes.field_type,
664
+ schemaRepository,
665
+
666
+ mapper,
667
+ options,
668
+ [...blockPath, 'attributes', field.attributes.api_key],
389
669
  );
390
670
  }
391
671
 
392
- return newBlock;
672
+ return await mapper(blockCopy, blockPath);
393
673
  },
394
674
  );
395
675
  }