@datocms/cma-client 5.1.11 → 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 (118) hide show
  1. package/README.md +40 -46
  2. package/dist/cjs/fieldTypes/file.js +6 -6
  3. package/dist/cjs/fieldTypes/gallery.js +7 -7
  4. package/dist/cjs/fieldTypes/rich_text.js +14 -12
  5. package/dist/cjs/fieldTypes/rich_text.js.map +1 -1
  6. package/dist/cjs/fieldTypes/schema.js +3 -0
  7. package/dist/cjs/fieldTypes/schema.js.map +1 -0
  8. package/dist/cjs/fieldTypes/single_block.js +13 -12
  9. package/dist/cjs/fieldTypes/single_block.js.map +1 -1
  10. package/dist/cjs/fieldTypes/structured_text.js +13 -12
  11. package/dist/cjs/fieldTypes/structured_text.js.map +1 -1
  12. package/dist/cjs/generated/Client.js +1 -1
  13. package/dist/cjs/generated/resources/Item.js.map +1 -1
  14. package/dist/cjs/generated/resources/ScheduledPublication.js.map +1 -1
  15. package/dist/cjs/generated/resources/ScheduledUnpublishing.js.map +1 -1
  16. package/dist/cjs/generated/resources/Upload.js.map +1 -1
  17. package/dist/cjs/index.js +1 -0
  18. package/dist/cjs/index.js.map +1 -1
  19. package/dist/cjs/utilities/buildBlockRecord.js +1 -46
  20. package/dist/cjs/utilities/buildBlockRecord.js.map +1 -1
  21. package/dist/cjs/utilities/duplicateBlockRecord.js +49 -0
  22. package/dist/cjs/utilities/duplicateBlockRecord.js.map +1 -0
  23. package/dist/cjs/utilities/inspectItem.js +50 -20
  24. package/dist/cjs/utilities/inspectItem.js.map +1 -1
  25. package/dist/cjs/utilities/nonRecursiveBlocks.js +1 -170
  26. package/dist/cjs/utilities/nonRecursiveBlocks.js.map +1 -1
  27. package/dist/cjs/utilities/recursiveBlocks.js +52 -112
  28. package/dist/cjs/utilities/recursiveBlocks.js.map +1 -1
  29. package/dist/esm/fieldTypes/file.d.ts +3 -3
  30. package/dist/esm/fieldTypes/file.js +3 -3
  31. package/dist/esm/fieldTypes/gallery.d.ts +5 -5
  32. package/dist/esm/fieldTypes/gallery.js +5 -5
  33. package/dist/esm/fieldTypes/rich_text.d.ts +8 -9
  34. package/dist/esm/fieldTypes/rich_text.js +9 -7
  35. package/dist/esm/fieldTypes/rich_text.js.map +1 -1
  36. package/dist/esm/fieldTypes/schema.d.ts +59 -0
  37. package/dist/esm/fieldTypes/schema.js +2 -0
  38. package/dist/esm/fieldTypes/schema.js.map +1 -0
  39. package/dist/esm/fieldTypes/single_block.d.ts +30 -15
  40. package/dist/esm/fieldTypes/single_block.js +8 -7
  41. package/dist/esm/fieldTypes/single_block.js.map +1 -1
  42. package/dist/esm/fieldTypes/structured_text.d.ts +14 -15
  43. package/dist/esm/fieldTypes/structured_text.js +8 -7
  44. package/dist/esm/fieldTypes/structured_text.js.map +1 -1
  45. package/dist/esm/generated/ApiTypes.d.ts +90 -980
  46. package/dist/esm/generated/Client.js +1 -1
  47. package/dist/esm/generated/RawApiTypes.d.ts +156 -999
  48. package/dist/esm/generated/resources/Field.d.ts +200 -200
  49. package/dist/esm/generated/resources/Item.d.ts +34 -34
  50. package/dist/esm/generated/resources/Item.js.map +1 -1
  51. package/dist/esm/generated/resources/ItemVersion.d.ts +3 -1
  52. package/dist/esm/generated/resources/ScheduledPublication.d.ts +3 -3
  53. package/dist/esm/generated/resources/ScheduledPublication.js.map +1 -1
  54. package/dist/esm/generated/resources/ScheduledUnpublishing.d.ts +3 -3
  55. package/dist/esm/generated/resources/ScheduledUnpublishing.js.map +1 -1
  56. package/dist/esm/generated/resources/Upload.d.ts +6 -6
  57. package/dist/esm/generated/resources/Upload.js.map +1 -1
  58. package/dist/esm/index.d.ts +1 -0
  59. package/dist/esm/index.js +1 -0
  60. package/dist/esm/index.js.map +1 -1
  61. package/dist/esm/utilities/buildBlockRecord.d.ts +3 -5
  62. package/dist/esm/utilities/buildBlockRecord.js +0 -44
  63. package/dist/esm/utilities/buildBlockRecord.js.map +1 -1
  64. package/dist/esm/utilities/duplicateBlockRecord.d.ts +6 -0
  65. package/dist/esm/utilities/duplicateBlockRecord.js +45 -0
  66. package/dist/esm/utilities/duplicateBlockRecord.js.map +1 -0
  67. package/dist/esm/utilities/inspectItem.d.ts +3 -3
  68. package/dist/esm/utilities/inspectItem.js +28 -21
  69. package/dist/esm/utilities/inspectItem.js.map +1 -1
  70. package/dist/esm/utilities/itemDefinition.d.ts +25 -27
  71. package/dist/esm/utilities/nonRecursiveBlocks.d.ts +10 -75
  72. package/dist/esm/utilities/nonRecursiveBlocks.js +1 -164
  73. package/dist/esm/utilities/nonRecursiveBlocks.js.map +1 -1
  74. package/dist/esm/utilities/recursiveBlocks.d.ts +53 -23
  75. package/dist/esm/utilities/recursiveBlocks.js +52 -112
  76. package/dist/esm/utilities/recursiveBlocks.js.map +1 -1
  77. package/dist/types/fieldTypes/file.d.ts +3 -3
  78. package/dist/types/fieldTypes/gallery.d.ts +5 -5
  79. package/dist/types/fieldTypes/rich_text.d.ts +8 -9
  80. package/dist/types/fieldTypes/schema.d.ts +59 -0
  81. package/dist/types/fieldTypes/single_block.d.ts +30 -15
  82. package/dist/types/fieldTypes/structured_text.d.ts +14 -15
  83. package/dist/types/generated/ApiTypes.d.ts +90 -980
  84. package/dist/types/generated/RawApiTypes.d.ts +156 -999
  85. package/dist/types/generated/resources/Field.d.ts +200 -200
  86. package/dist/types/generated/resources/Item.d.ts +34 -34
  87. package/dist/types/generated/resources/ItemVersion.d.ts +3 -1
  88. package/dist/types/generated/resources/ScheduledPublication.d.ts +3 -3
  89. package/dist/types/generated/resources/ScheduledUnpublishing.d.ts +3 -3
  90. package/dist/types/generated/resources/Upload.d.ts +6 -6
  91. package/dist/types/index.d.ts +1 -0
  92. package/dist/types/utilities/buildBlockRecord.d.ts +3 -5
  93. package/dist/types/utilities/duplicateBlockRecord.d.ts +6 -0
  94. package/dist/types/utilities/inspectItem.d.ts +3 -3
  95. package/dist/types/utilities/itemDefinition.d.ts +25 -27
  96. package/dist/types/utilities/nonRecursiveBlocks.d.ts +10 -75
  97. package/dist/types/utilities/recursiveBlocks.d.ts +53 -23
  98. package/package.json +3 -3
  99. package/src/fieldTypes/file.ts +6 -6
  100. package/src/fieldTypes/gallery.ts +10 -10
  101. package/src/fieldTypes/rich_text.ts +26 -24
  102. package/src/fieldTypes/schema.ts +657 -0
  103. package/src/fieldTypes/single_block.ts +61 -38
  104. package/src/fieldTypes/structured_text.ts +57 -51
  105. package/src/generated/ApiTypes.ts +217 -1880
  106. package/src/generated/Client.ts +1 -1
  107. package/src/generated/RawApiTypes.ts +272 -2113
  108. package/src/generated/resources/Item.ts +93 -187
  109. package/src/generated/resources/ScheduledPublication.ts +4 -15
  110. package/src/generated/resources/ScheduledUnpublishing.ts +4 -15
  111. package/src/generated/resources/Upload.ts +9 -32
  112. package/src/index.ts +1 -0
  113. package/src/utilities/buildBlockRecord.ts +4 -60
  114. package/src/utilities/duplicateBlockRecord.ts +52 -0
  115. package/src/utilities/inspectItem.ts +64 -52
  116. package/src/utilities/itemDefinition.ts +109 -98
  117. package/src/utilities/nonRecursiveBlocks.ts +25 -279
  118. package/src/utilities/recursiveBlocks.ts +337 -72
@@ -1,8 +1,18 @@
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';
15
+ import type { ExtractNestedBlocksFromFieldValue } from './itemDefinition';
6
16
  import {
7
17
  nonRecursiveFilterBlocksInNonLocalizedFieldValueAsync,
8
18
  nonRecursiveFindAllBlocksInNonLocalizedFieldValueAsync,
@@ -13,28 +23,63 @@ import {
13
23
  } from './nonRecursiveBlocks';
14
24
  import type { SchemaRepository } from './schemaRepository';
15
25
 
26
+ type RecognizableFieldValue =
27
+ | RichTextFieldValueInNestedResponse
28
+ | SingleBlockFieldValueInNestedResponse
29
+ | StructuredTextFieldValueInNestedResponse
30
+ | RichTextFieldValueInRequest
31
+ | SingleBlockFieldValueInRequest
32
+ | StructuredTextFieldValueInRequest
33
+ | RichTextFieldValue
34
+ | SingleBlockFieldValue
35
+ | StructuredTextFieldValue;
36
+
16
37
  /**
17
38
  * Path through a non-localized field value (ie. ['content', 0, 'attributes', 'title'])
18
39
  */
19
40
  export type TreePath = readonly (string | number)[];
20
41
 
42
+ /**
43
+ * Traversal direction for recursive operations
44
+ */
45
+ export type TraversalDirection = 'top-down' | 'bottom-up';
46
+
21
47
  /**
22
48
  * Recursively visit every block in a non-localized field value and all nested blocks within those blocks.
23
49
  * This function traverses not only the direct blocks in the non-localized field value but also recursively
24
50
  * visits blocks contained within the attributes of each block, creating a complete traversal
25
51
  * of the entire block hierarchy.
26
52
  *
27
- * @param schemaRepository - Repository for accessing DatoCMS schema information to resolve block structures
28
- * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
29
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)
30
56
  * @param visitor - Asynchronous function called for each block found, including nested blocks
31
57
  * @returns Promise that resolves when all blocks and nested blocks have been visited
32
58
  */
33
- export async function visitBlocksInNonLocalizedFieldValue(
59
+ export async function visitBlocksInNonLocalizedFieldValue<
60
+ T extends RecognizableFieldValue,
61
+ >(
62
+ nonLocalizedFieldValue: T,
63
+ fieldType: ApiTypes.Field['field_type'],
34
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,
35
73
  fieldType: ApiTypes.Field['field_type'],
74
+ schemaRepository: SchemaRepository,
75
+ visitor: (item: BlockInRequest, path: TreePath) => void | Promise<void>,
76
+ path?: TreePath,
77
+ ): Promise<void>;
78
+ export async function visitBlocksInNonLocalizedFieldValue(
36
79
  nonLocalizedFieldValue: unknown,
37
- visitor: (item: BlockItemInARequest, path: TreePath) => void | Promise<void>,
80
+ fieldType: ApiTypes.Field['field_type'],
81
+ schemaRepository: SchemaRepository,
82
+ visitor: (item: BlockInRequest, path: TreePath) => void | Promise<void>,
38
83
  path: TreePath = [],
39
84
  ): Promise<void> {
40
85
  await nonRecursiveVisitBlocksInNonLocalizedFieldValueAsync(
@@ -55,9 +100,9 @@ export async function visitBlocksInNonLocalizedFieldValue(
55
100
 
56
101
  for (const field of fields) {
57
102
  await visitBlocksInNonLocalizedFieldValue(
58
- schemaRepository,
59
- fieldType,
60
103
  block.attributes[field.attributes.api_key],
104
+ field.attributes.field_type,
105
+ schemaRepository,
61
106
  visitor,
62
107
  [...path, ...innerPath, 'attributes', field.attributes.api_key],
63
108
  );
@@ -71,23 +116,47 @@ export async function visitBlocksInNonLocalizedFieldValue(
71
116
  * Searches through all direct blocks and recursively through nested blocks within
72
117
  * the attributes of each block, returning all matches found throughout the hierarchy.
73
118
  *
74
- * @param schemaRepository - Repository for accessing DatoCMS schema information to resolve block structures
75
- * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
76
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)
77
122
  * @param predicate - Asynchronous function that tests each block, including nested ones
78
123
  * @returns Promise that resolves to an array of objects, each containing a matching block and its full path
79
124
  */
80
- export async function findAllBlocksInNonLocalizedFieldValue(
125
+ export async function findAllBlocksInNonLocalizedFieldValue<
126
+ T extends RecognizableFieldValue,
127
+ >(
128
+ nonLocalizedFieldValue: T,
129
+ fieldType: ApiTypes.Field['field_type'],
81
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,
82
141
  fieldType: ApiTypes.Field['field_type'],
142
+ schemaRepository: SchemaRepository,
143
+ predicate: (
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(
83
150
  nonLocalizedFieldValue: unknown,
151
+ fieldType: ApiTypes.Field['field_type'],
152
+ schemaRepository: SchemaRepository,
84
153
  predicate: (
85
- item: BlockItemInARequest,
154
+ item: BlockInRequest,
86
155
  path: TreePath,
87
156
  ) => boolean | Promise<boolean>,
88
157
  path: TreePath = [],
89
- ): Promise<Array<{ item: BlockItemInARequest; path: TreePath }>> {
90
- const results: Array<{ item: BlockItemInARequest; path: TreePath }> = [];
158
+ ): Promise<Array<{ item: BlockInRequest; path: TreePath }>> {
159
+ const results: Array<{ item: BlockInRequest; path: TreePath }> = [];
91
160
 
92
161
  const directMatches =
93
162
  await nonRecursiveFindAllBlocksInNonLocalizedFieldValueAsync(
@@ -120,9 +189,9 @@ export async function findAllBlocksInNonLocalizedFieldValue(
120
189
 
121
190
  for (const field of fields) {
122
191
  const nestedResults = await findAllBlocksInNonLocalizedFieldValue(
123
- schemaRepository,
124
- fieldType,
125
192
  block.attributes[field.attributes.api_key],
193
+ field.attributes.field_type,
194
+ schemaRepository,
126
195
  predicate,
127
196
  [...path, ...innerPath, 'attributes', field.attributes.api_key],
128
197
  );
@@ -140,55 +209,112 @@ export async function findAllBlocksInNonLocalizedFieldValue(
140
209
  * including recursive filtering of nested blocks within block attributes. The filtering
141
210
  * preserves the original non-localized field value structure and hierarchy.
142
211
  *
143
- * @param schemaRepository - Repository for accessing DatoCMS schema information to resolve block structures
144
- * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
145
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)
146
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
147
218
  * @returns Promise that resolves to the new non-localized field value with recursively filtered blocks
148
219
  */
149
- export async function filterBlocksInNonLocalizedFieldValue(
220
+ export async function filterBlocksInNonLocalizedFieldValue<
221
+ T extends RecognizableFieldValue,
222
+ >(
223
+ nonLocalizedFieldValue: T,
224
+ fieldType: ApiTypes.Field['field_type'],
150
225
  schemaRepository: SchemaRepository,
226
+ predicate: (
227
+ item: ExtractNestedBlocksFromFieldValue<T>,
228
+ path: TreePath,
229
+ ) => boolean | Promise<boolean>,
230
+ options?: { traversalDirection?: TraversalDirection },
231
+ path?: TreePath,
232
+ ): Promise<T>;
233
+ export async function filterBlocksInNonLocalizedFieldValue(
234
+ nonLocalizedFieldValue: unknown,
151
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(
152
245
  nonLocalizedFieldValue: unknown,
246
+ fieldType: ApiTypes.Field['field_type'],
247
+ schemaRepository: SchemaRepository,
153
248
  predicate: (
154
- item: BlockItemInARequest,
249
+ item: BlockInRequest,
155
250
  path: TreePath,
156
251
  ) => boolean | Promise<boolean>,
252
+ options: { traversalDirection?: TraversalDirection } = {},
157
253
  path: TreePath = [],
158
254
  ): Promise<unknown> {
159
- return nonRecursiveFilterBlocksInNonLocalizedFieldValueAsync(
160
- fieldType,
161
- nonLocalizedFieldValue,
162
- async (block, innerPath) => {
163
- const blockPath = [...path, ...innerPath];
164
- const passes = await predicate(block, blockPath);
255
+ const { traversalDirection = 'top-down' } = options;
165
256
 
166
- if (!passes) {
167
- return false;
168
- }
257
+ const mapperFunc = async (block: BlockInRequest, innerPath: TreePath) => {
258
+ const blockPath = [...path, ...innerPath];
169
259
 
170
- if (!isItemWithOptionalIdAndMeta(block)) {
171
- return true;
172
- }
260
+ if (!isItemWithOptionalIdAndMeta(block)) {
261
+ return block;
262
+ }
173
263
 
174
- const itemType = await schemaRepository.getRawItemTypeById(
175
- block.relationships.item_type.data.id,
176
- );
264
+ const itemType = await schemaRepository.getRawItemTypeById(
265
+ block.relationships.item_type.data.id,
266
+ );
177
267
 
178
- 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 } };
179
272
 
180
273
  for (const field of fields) {
181
- block.attributes[field.attributes.api_key] =
274
+ blockCopy.attributes[field.attributes.api_key] =
182
275
  await filterBlocksInNonLocalizedFieldValue(
276
+ blockCopy.attributes[field.attributes.api_key],
277
+ field.attributes.field_type,
183
278
  schemaRepository,
184
- fieldType,
185
- block.attributes[field.attributes.api_key],
279
+
186
280
  predicate,
281
+ options,
187
282
  [...blockPath, 'attributes', field.attributes.api_key],
188
283
  );
189
284
  }
190
285
 
191
- 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);
192
318
  },
193
319
  );
194
320
  }
@@ -199,20 +325,44 @@ export async function filterBlocksInNonLocalizedFieldValue(
199
325
  * accumulating results from the entire block hierarchy into a single value.
200
326
  *
201
327
  * @template R - The type of the accumulated result
202
- * @param schemaRepository - Repository for accessing DatoCMS schema information to resolve block structures
203
- * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
204
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)
205
331
  * @param reducer - Asynchronous function that processes each block and updates the accumulator
206
332
  * @param initialNonLocalizedFieldValue - The initial value for the accumulator
207
333
  * @returns Promise that resolves to the final accumulated value from all blocks in the hierarchy
208
334
  */
209
- export async function reduceBlocksInNonLocalizedFieldValue<R>(
335
+ export async function reduceBlocksInNonLocalizedFieldValue<T, R>(
336
+ nonLocalizedFieldValue: T,
337
+ fieldType: ApiTypes.Field['field_type'],
210
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,
211
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>(
212
360
  nonLocalizedFieldValue: unknown,
361
+ fieldType: ApiTypes.Field['field_type'],
362
+ schemaRepository: SchemaRepository,
213
363
  reducer: (
214
364
  accumulator: R,
215
- item: BlockItemInARequest,
365
+ item: BlockInRequest,
216
366
  path: TreePath,
217
367
  ) => R | Promise<R>,
218
368
  initialValue: R,
@@ -242,9 +392,9 @@ export async function reduceBlocksInNonLocalizedFieldValue<R>(
242
392
 
243
393
  for (const field of fields) {
244
394
  accumulator = await reduceBlocksInNonLocalizedFieldValue(
245
- schemaRepository,
246
- fieldType,
247
395
  block.attributes[field.attributes.api_key],
396
+ field.attributes.field_type,
397
+ schemaRepository,
248
398
  reducer,
249
399
  accumulator,
250
400
  [...path, ...innerPath, 'attributes', field.attributes.api_key],
@@ -262,18 +412,40 @@ export async function reduceBlocksInNonLocalizedFieldValue<R>(
262
412
  * Returns true as soon as the first matching block is found anywhere in the hierarchy
263
413
  * (short-circuit evaluation).
264
414
  *
265
- * @param schemaRepository - Repository for accessing DatoCMS schema information to resolve block structures
266
- * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
267
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)
268
418
  * @param predicate - Asynchronous function that tests each block, including nested ones
269
419
  * @returns Promise that resolves to true if any block in the hierarchy matches, false otherwise
270
420
  */
271
- export async function someBlocksInNonLocalizedFieldValue(
421
+ export async function someBlocksInNonLocalizedFieldValue<
422
+ T extends RecognizableFieldValue,
423
+ >(
424
+ nonLocalizedFieldValue: T,
425
+ fieldType: ApiTypes.Field['field_type'],
272
426
  schemaRepository: SchemaRepository,
427
+ predicate: (
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,
273
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(
274
444
  nonLocalizedFieldValue: unknown,
445
+ fieldType: ApiTypes.Field['field_type'],
446
+ schemaRepository: SchemaRepository,
275
447
  predicate: (
276
- item: BlockItemInARequest,
448
+ item: BlockInRequest,
277
449
  path: TreePath,
278
450
  ) => boolean | Promise<boolean>,
279
451
  path: TreePath = [],
@@ -307,9 +479,9 @@ export async function someBlocksInNonLocalizedFieldValue(
307
479
  if (found) break;
308
480
 
309
481
  const nestedMatch = await someBlocksInNonLocalizedFieldValue(
310
- schemaRepository,
311
- fieldType,
312
482
  block.attributes[field.attributes.api_key],
483
+ field.attributes.field_type,
484
+ schemaRepository,
313
485
  predicate,
314
486
  [...path, ...innerPath, 'attributes', field.attributes.api_key],
315
487
  );
@@ -330,81 +502,174 @@ export async function someBlocksInNonLocalizedFieldValue(
330
502
  * Returns false as soon as the first non-matching block is found anywhere in the hierarchy
331
503
  * (short-circuit evaluation).
332
504
  *
333
- * @param schemaRepository - Repository for accessing DatoCMS schema information to resolve block structures
334
- * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
335
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)
336
508
  * @param predicate - Asynchronous function that tests each block, including nested ones
337
509
  * @returns Promise that resolves to true if all blocks in the hierarchy match, false otherwise
338
510
  */
339
- export async function everyBlockInNonLocalizedFieldValue(
511
+ export async function everyBlockInNonLocalizedFieldValue<
512
+ T extends RecognizableFieldValue,
513
+ >(
514
+ nonLocalizedFieldValue: T,
515
+ fieldType: ApiTypes.Field['field_type'],
340
516
  schemaRepository: SchemaRepository,
517
+ predicate: (
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,
341
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(
342
534
  nonLocalizedFieldValue: unknown,
535
+ fieldType: ApiTypes.Field['field_type'],
536
+ schemaRepository: SchemaRepository,
343
537
  predicate: (
344
- item: BlockItemInARequest,
538
+ item: BlockInRequest,
345
539
  path: TreePath,
346
540
  ) => boolean | Promise<boolean>,
347
541
  path: TreePath = [],
348
542
  ): Promise<boolean> {
349
543
  return !(await someBlocksInNonLocalizedFieldValue(
350
- schemaRepository,
351
- fieldType,
352
544
  nonLocalizedFieldValue,
545
+ fieldType,
546
+ schemaRepository,
353
547
  async (item, path) => !(await predicate(item, path)),
354
548
  path,
355
549
  ));
356
550
  }
357
551
 
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
+
358
568
  /**
359
569
  * Recursively transform blocks in a non-localized field value by applying a mapping function to each block.
360
570
  * Creates a new non-localized field value structure with transformed blocks while preserving the original
361
571
  * structure. Applies the mapping function to both direct blocks and recursively to nested
362
572
  * blocks within block attributes throughout the entire hierarchy.
363
573
  *
364
- * @param schemaRepository - Repository for accessing DatoCMS schema information to resolve block structures
365
- * @param fieldType - The type of DatoCMS field definition that determines how the value is processed
366
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)
367
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
368
580
  * @returns Promise that resolves to the new non-localized field value with recursively transformed blocks
369
581
  */
370
- export async function mapBlocksInNonLocalizedFieldValue(
582
+ export async function mapBlocksInNonLocalizedFieldValue<
583
+ T extends RecognizableFieldValue,
584
+ >(
585
+ nonLocalizedFieldValue: T,
586
+ fieldType: ApiTypes.Field['field_type'],
371
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,
372
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(
373
607
  nonLocalizedFieldValue: unknown,
608
+ fieldType: ApiTypes.Field['field_type'],
609
+ schemaRepository: SchemaRepository,
374
610
  mapper: (
375
- item: BlockItemInARequest,
611
+ item: BlockInRequest,
376
612
  path: TreePath,
377
- ) => BlockItemInARequest | Promise<BlockItemInARequest>,
613
+ ) => BlockInRequest | Promise<BlockInRequest>,
614
+ options: { traversalDirection?: TraversalDirection } = {},
378
615
  path: TreePath = [],
379
- ) {
616
+ ): Promise<unknown> {
617
+ const { traversalDirection = 'top-down' } = options;
618
+
380
619
  return nonRecursiveMapBlocksInNonLocalizedFieldValueAsync(
381
620
  fieldType,
382
621
  nonLocalizedFieldValue,
383
622
  async (block, innerPath) => {
384
- const newBlock = await mapper(block, [...path, ...innerPath]);
623
+ const blockPath = [...path, ...innerPath];
385
624
 
386
- if (!isItemWithOptionalIdAndMeta(newBlock)) {
387
- return newBlock;
625
+ if (!isItemWithOptionalIdAndMeta(block)) {
626
+ return await mapper(block, blockPath);
388
627
  }
389
628
 
390
629
  const itemType = await schemaRepository.getRawItemTypeById(
391
- newBlock.relationships.item_type.data.id,
630
+ block.relationships.item_type.data.id,
392
631
  );
393
632
 
394
633
  const fields = await schemaRepository.getRawItemTypeFields(itemType);
395
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
+
396
659
  for (const field of fields) {
397
- newBlock.attributes[field.attributes.api_key] =
660
+ blockCopy.attributes[field.attributes.api_key] =
398
661
  await mapBlocksInNonLocalizedFieldValue(
662
+ blockCopy.attributes[field.attributes.api_key],
663
+ field.attributes.field_type,
399
664
  schemaRepository,
400
- fieldType,
401
- newBlock.attributes[field.attributes.api_key],
665
+
402
666
  mapper,
403
- [...path, ...innerPath, 'attributes', field.attributes.api_key],
667
+ options,
668
+ [...blockPath, 'attributes', field.attributes.api_key],
404
669
  );
405
670
  }
406
671
 
407
- return newBlock;
672
+ return await mapper(blockCopy, blockPath);
408
673
  },
409
674
  );
410
675
  }