@datocms/cma-client 5.1.12 → 5.1.14

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 (36) hide show
  1. package/README.md +362 -167
  2. package/dist/cjs/generated/Client.js +1 -1
  3. package/dist/cjs/utilities/buildBlockRecord.js.map +1 -1
  4. package/dist/cjs/utilities/duplicateBlockRecord.js +2 -1
  5. package/dist/cjs/utilities/duplicateBlockRecord.js.map +1 -1
  6. package/dist/cjs/utilities/schemaRepository.js +107 -0
  7. package/dist/cjs/utilities/schemaRepository.js.map +1 -1
  8. package/dist/esm/fieldTypes/schema.d.ts +1 -1
  9. package/dist/esm/fieldTypes/single_block.d.ts +1 -1
  10. package/dist/esm/generated/ApiTypes.d.ts +4 -0
  11. package/dist/esm/generated/Client.js +1 -1
  12. package/dist/esm/generated/RawApiTypes.d.ts +4 -0
  13. package/dist/esm/generated/resources/Field.d.ts +100 -100
  14. package/dist/esm/utilities/buildBlockRecord.d.ts +10 -2
  15. package/dist/esm/utilities/buildBlockRecord.js.map +1 -1
  16. package/dist/esm/utilities/duplicateBlockRecord.js +2 -1
  17. package/dist/esm/utilities/duplicateBlockRecord.js.map +1 -1
  18. package/dist/esm/utilities/schemaRepository.d.ts +40 -0
  19. package/dist/esm/utilities/schemaRepository.js +107 -0
  20. package/dist/esm/utilities/schemaRepository.js.map +1 -1
  21. package/dist/types/fieldTypes/schema.d.ts +1 -1
  22. package/dist/types/fieldTypes/single_block.d.ts +1 -1
  23. package/dist/types/generated/ApiTypes.d.ts +4 -0
  24. package/dist/types/generated/RawApiTypes.d.ts +4 -0
  25. package/dist/types/generated/resources/Field.d.ts +100 -100
  26. package/dist/types/utilities/buildBlockRecord.d.ts +10 -2
  27. package/dist/types/utilities/schemaRepository.d.ts +40 -0
  28. package/package.json +4 -4
  29. package/src/fieldTypes/schema.ts +1 -1
  30. package/src/fieldTypes/single_block.ts +1 -1
  31. package/src/generated/ApiTypes.ts +4 -0
  32. package/src/generated/Client.ts +1 -1
  33. package/src/generated/RawApiTypes.ts +4 -0
  34. package/src/utilities/buildBlockRecord.ts +18 -2
  35. package/src/utilities/duplicateBlockRecord.ts +2 -1
  36. package/src/utilities/schemaRepository.ts +141 -0
package/README.md CHANGED
@@ -1,28 +1,221 @@
1
- [![Node.js CI](https://github.com/datocms/js-rest-api-clients/actions/workflows/node.js.yml/badge.svg)](https://github.com/datocms/js-rest-api-clients/actions/workflows/node.js.yml)
1
+ # DatoCMS Content Management API Utilities
2
2
 
3
- # DatoCMS Content Management JS API Client (Common)
3
+ Take a look at the full [API documentation](https://www.datocms.com/docs/content-management-api) for examples!
4
4
 
5
- API client for [DatoCMS](https://www.datocms.com). Take a look at the full [API documentation](https://www.datocms.com/docs/content-management-api) for examples.
5
+ ## Field Types
6
6
 
7
- <br /><br />
8
- <a href="https://www.datocms.com/">
9
- <img src="https://www.datocms.com/images/full_logo.svg" height="60">
10
- </a>
11
- <br /><br />
7
+ This library provides comprehensive TypeScript type definitions and utilities for all DatoCMS field types. Each field type includes type guards, validation functions, localization support, and editor appearance configurations.
12
8
 
13
- ## Utility Functions
9
+ ### What's available
14
10
 
15
- This library provides a comprehensive set of utility functions to work with DatoCMS records, blocks, and field values. These utilities make it easier to manipulate, traverse, and inspect your content programmatically.
11
+ Every field type follows a consistent pattern providing:
16
12
 
17
- ### 1. Record Inspection Utilities
13
+ - **Field value types**: TypeScript definitions for the field's data structure
14
+ - **Type guards**: Functions to validate field values at runtime
15
+ - **Localization support**: Utilities for handling localized field variants
16
+ - **Validation types**: Supported validators for the field type
17
+ - **Appearance configuration**: Editor types and their configuration options
18
18
 
19
- The `inspectItem()` function provides a visual, tree-structured representation of DatoCMS records in the console, making it easier to debug and understand complex content structures.
19
+ **Example: `lat_lon` Field Type**
20
+
21
+ <details>
22
+ <summary>View example</summary>
23
+
24
+ ```typescript
25
+ import { isLatLonFieldValue, isLocalizedLatLonFieldValue } from '@datocms/cma-client';
26
+ import type { LatLonFieldValue, LatLonFieldValidators, LatLonFieldAppearance } from '@datocms/cma-client';
27
+
28
+ // Field value type - object with latitude/longitude or null
29
+ const value: LatLonFieldValue = { latitude: 45.4642, longitude: 9.1900 };
30
+
31
+ // Type guard functions for validation
32
+ if (isLatLonFieldValue(someValue)) {
33
+ // someValue is guaranteed to be { latitude: number; longitude: number } | null
34
+ }
35
+
36
+ if (isLocalizedLatLonFieldValue(localizedValue)) {
37
+ // localizedValue is a localized lat/lon field
38
+ }
39
+
40
+ // Validator and appearance types available for type-safe configuration
41
+ type Validators = LatLonFieldValidators;
42
+ type Appearance = LatLonFieldAppearance;
43
+ ```
44
+ </details>
45
+
46
+ ### Context-Dependent field types
47
+
48
+ Some field types have different value formats depending on the API context (request vs response) or query parameters:
49
+
50
+ #### Request vs Response variations
51
+
52
+ **File and Gallery fields** have different type requirements for API requests versus responses:
53
+
54
+ <details>
55
+ <summary>View example</summary>
56
+
57
+ ```typescript
58
+ import {
59
+ FileFieldValue,
60
+ FileFieldValueInRequest,
61
+ GalleryFieldValue,
62
+ GalleryFieldValueInRequest,
63
+ // Type guards for runtime validation
64
+ isFileFieldValue,
65
+ isFileFieldValueInRequest,
66
+ isGalleryFieldValue,
67
+ isGalleryFieldValueInRequest
68
+ } from '@datocms/cma-client';
69
+
70
+ // API Response format - all metadata fields present with defaults
71
+ const fileResponse: FileFieldValue = {
72
+ upload_id: "12345",
73
+ alt: null, // Always present (default: null)
74
+ title: null, // Always present (default: null)
75
+ custom_data: {}, // Always present (default: {})
76
+ focal_point: null // Always present (default: null)
77
+ };
78
+
79
+ // API Request format - metadata fields are optional
80
+ const fileRequest: FileFieldValueInRequest = {
81
+ upload_id: "12345"
82
+ // alt, title, custom_data, focal_point are optional
83
+ };
84
+
85
+ // Runtime validation for different contexts
86
+ if (isFileFieldValueInRequest(someFileValue)) {
87
+ // someFileValue has optional metadata fields
88
+ }
89
+
90
+ if (isGalleryFieldValue(someGalleryValue)) {
91
+ // someGalleryValue is array of files with all metadata present
92
+ }
93
+ ```
94
+ </details>
95
+
96
+ #### "Nested Mode" Response variations
97
+
98
+ **Block-containing fields** (`structured_text`, `single_block`, `rich_text`) support different block representations for regular responses, for ["Nested Mode" responses](https://www.datocms.com/docs/content-management-api/resources/item#api-response-modes-regular-vs-nested), and for requests:
99
+
100
+ <details>
101
+ <summary>View example</summary>
102
+
103
+ ```typescript
104
+ import {
105
+ StructuredTextFieldValue,
106
+ StructuredTextFieldValueInRequest,
107
+ StructuredTextFieldValueInNestedResponse,
108
+ // Type guards for all variations (also available for single_block and rich_text)
109
+ isStructuredTextFieldValue,
110
+ isStructuredTextFieldValueInRequest,
111
+ isStructuredTextFieldValueInNestedResponse
112
+ } from '@datocms/cma-client';
113
+
114
+ // Regular response - blocks as string IDs
115
+ const standard: StructuredTextFieldValue = {
116
+ document: {
117
+ type: "root",
118
+ children: [
119
+ {
120
+ type: "block",
121
+ // String ID reference
122
+ item: "IdMLV2GJTXyQ0Bfns7R4IQ"
123
+ }
124
+ ]
125
+ }
126
+ };
127
+
128
+ // Nested Mode response (?nested=true) - blocks as full objects
129
+ const nested: StructuredTextFieldValueInNestedResponse = {
130
+ document: {
131
+ type: "root",
132
+ children: [
133
+ {
134
+ type: "block",
135
+ // Always full block object
136
+ item: {
137
+ id: "IdMLV2GJTXyQ0Bfns7R4IQ",
138
+ type: "item",
139
+ attributes: { /* ... */ },
140
+ relationships: { /* ... */ }
141
+ }
142
+ }
143
+ ]
144
+ }
145
+ };
146
+
147
+ // Request format - flexible block representation
148
+ const request: StructuredTextFieldValueInRequest = {
149
+ document: {
150
+ type: "root",
151
+ children: [
152
+ {
153
+ type: "block",
154
+ // Can be string ID, to keep block unchanged...
155
+ item: "FicV5CxCSQ6yOrgfwRoiKA"
156
+ },
157
+ {
158
+ type: "block",
159
+ // ...or full block object (to create new blocks or update existing ones)
160
+ item: {
161
+ type: "item",
162
+ attributes: { /* ... */ },
163
+ relationships: { /* ... */ }
164
+ }
165
+ }
166
+ ]
167
+ }
168
+ };
169
+
170
+ // Runtime validation for different contexts
171
+ if (isStructuredTextFieldValueInNestedResponse(someStructuredText)) {
172
+ // someStructuredText has blocks as full objects
173
+ }
174
+
175
+ if (isStructuredTextFieldValueInRequest(requestData)) {
176
+ // requestData allows flexible block representations
177
+ }
178
+ ```
179
+ </details>
180
+
181
+ These variants ensure type safety across different API contexts while maintaining the same conceptual data structure. All localized variants also have corresponding type guards (e.g., `isLocalizedStructuredTextFieldValueInRequest`, `isLocalizedStructuredTextFieldValueInNestedResponse`, etc.).
182
+
183
+ **TypeScript Generics Support:** For maximum type safety, all field value types and type guards for block-containing fields accept [`ItemTypeDefinition` generics](https://www.datocms.com/docs/content-management-api/resources/item#type-safe-development-with-typescript) to provide precise typing for your specific schema:
20
184
 
21
185
  <details>
22
- <summary><strong>inspectItem()</strong> - Display records with visual appeal</summary>
186
+ <summary>View example</summary>
187
+
188
+ ```typescript
189
+ import type { MyArticle, MyArticleSection } from './schema';
190
+
191
+ // Fully typed structured text with specific block types
192
+ const content: StructuredTextFieldValueInRequest<MyArticleSection> = {
193
+ document: {
194
+ type: "root",
195
+ children: [/* ... */]
196
+ }
197
+ };
198
+
199
+ // Type guard with generic for precise validation
200
+ if (isStructuredTextFieldValueInNestedResponse<MyArticleSection>(value)) {
201
+ // value is now typed with your specific block schema
202
+ }
203
+ ```
204
+ </details>
205
+
206
+ ## Block Processing Utilities
207
+
208
+ ### Inspecting Records and Blocks
209
+
210
+ The `inspectItem()` function provides a visual, tree-structured representation of DatoCMS records in the console, making it easier to debug and understand complex content structures.
211
+
212
+ #### inspectItem()
23
213
 
24
214
  Formats a DatoCMS item (record or block) as a visual tree structure, showing all fields with proper formatting for each field type. Particularly useful for debugging nested structures like modular content and structured text.
25
215
 
216
+ <details>
217
+ <summary>View details</summary>
218
+
26
219
  **TypeScript Signature:**
27
220
  ```typescript
28
221
  function inspectItem(
@@ -59,19 +252,61 @@ console.log(inspectItem(record));
59
252
  ```
60
253
  </details>
61
254
 
62
- ### 2. Block Processing Utilities
255
+ ### Creating and Duplicating Blocks
256
+
257
+ #### buildBlockRecord()
258
+
259
+ Converts a block data object into the proper format for API requests.
260
+
261
+ <details>
262
+ <summary>View details</summary>
263
+
264
+ **TypeScript Signature:**
265
+ ```typescript
266
+ function buildBlockRecord<D extends ItemTypeDefinition>(
267
+ body: ItemUpdateSchema<ToItemDefinitionInRequest<D>>
268
+ ): NewBlockInRequest<ToItemDefinitionInRequest<D>>
269
+ ```
63
270
 
64
- These utilities provide a unified interface for working with blocks embedded within DatoCMS field values. DatoCMS supports three field types that can contain blocks: Modular Content (arrays of blocks), Single Block fields, and Structured Text (with embedded blocks). These functions abstract away the differences between field types, providing consistent APIs for common operations.
271
+ **Parameters:**
272
+ - `body`: Block data in update schema format
65
273
 
66
- #### Recursive Block Operations
274
+ **Returns:** Formatted block record ready for API requests
275
+ </details>
67
276
 
68
- These functions traverse blocks recursively, processing nested blocks within blocks. They require a `SchemaRepository` instance to look up field definitions for nested blocks.
277
+ #### duplicateBlockRecord()
278
+
279
+ Creates a deep copy of a block record, including all nested blocks, removing IDs to create new instances.
69
280
 
70
281
  <details>
71
- <summary><strong>visitBlocksInNonLocalizedFieldValue()</strong> - Recursively visit all blocks</summary>
282
+ <summary>View details</summary>
283
+
284
+ **TypeScript Signature:**
285
+ ```typescript
286
+ async function duplicateBlockRecord<D extends ItemTypeDefinition>(
287
+ existingBlock: ItemWithOptionalIdAndMeta<ToItemDefinitionInNestedResponse<D>>,
288
+ schemaRepository: SchemaRepository
289
+ ): Promise<NewBlockInRequest<ToItemDefinitionInRequest<D>>>
290
+ ```
291
+
292
+ **Parameters:**
293
+ - `existingBlock`: The block to duplicate
294
+ - `schemaRepository`: Repository for schema lookups
295
+
296
+ **Returns:** New block record without IDs, ready to be created
297
+ </details>
298
+
299
+ ### Recursive Block Operations
300
+
301
+ DatoCMS supports three field types that can contain blocks: Modular Content (arrays of blocks), Single Block fields, and Structured Text (rich-text with embedded blocks). These functions abstract away the differences between field types and can traverse blocks recursively, processing nested blocks within blocks. They require a `SchemaRepository` instance to look up field definitions for nested blocks.
302
+
303
+ #### visitBlocksInNonLocalizedFieldValue()
72
304
 
73
305
  Visit every block in a non-localized field value recursively, including blocks nested within other blocks.
74
306
 
307
+ <details>
308
+ <summary>View details</summary>
309
+
75
310
  **TypeScript Signature:**
76
311
  ```typescript
77
312
  async function visitBlocksInNonLocalizedFieldValue(
@@ -79,24 +314,23 @@ async function visitBlocksInNonLocalizedFieldValue(
79
314
  fieldType: string,
80
315
  schemaRepository: SchemaRepository,
81
316
  visitor: (item: BlockInRequest, path: TreePath) => void | Promise<void>,
82
- path?: TreePath
83
317
  ): Promise<void>
84
318
  ```
85
319
 
86
320
  **Parameters:**
87
321
  - `nonLocalizedFieldValue`: The non-localized field value
88
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
322
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
89
323
  - `schemaRepository`: Repository for caching schema lookups
90
324
  - `visitor`: Function called for each block (including nested)
91
- - `path`: Optional base path for tracking location
92
- ```
93
325
  </details>
94
326
 
95
- <details>
96
- <summary><strong>mapBlocksInNonLocalizedFieldValue()</strong> - Recursively transform all blocks</summary>
327
+ #### mapBlocksInNonLocalizedFieldValue()
97
328
 
98
329
  Transform all blocks in a non-localized field value recursively, including nested blocks.
99
330
 
331
+ <details>
332
+ <summary>View details</summary>
333
+
100
334
  **TypeScript Signature:**
101
335
  ```typescript
102
336
  async function mapBlocksInNonLocalizedFieldValue(
@@ -104,24 +338,25 @@ async function mapBlocksInNonLocalizedFieldValue(
104
338
  fieldType: string,
105
339
  schemaRepository: SchemaRepository,
106
340
  mapper: (item: BlockInRequest, path: TreePath) => BlockInRequest | Promise<BlockInRequest>,
107
- path?: TreePath
108
341
  ): Promise<unknown>
109
342
  ```
110
343
 
111
344
  **Parameters:**
112
345
  - `nonLocalizedFieldValue`: The non-localized field value
113
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
346
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
114
347
  - `schemaRepository`: Repository for caching schema lookups
115
348
  - `mapper`: Function that transforms each block
116
349
 
117
350
  **Returns:** New field value
118
351
  </details>
119
352
 
120
- <details>
121
- <summary><strong>filterBlocksInNonLocalizedFieldValue()</strong> - Recursively filter blocks</summary>
353
+ #### filterBlocksInNonLocalizedFieldValue()
122
354
 
123
355
  Filter blocks recursively, removing blocks at any nesting level that don't match the predicate.
124
356
 
357
+ <details>
358
+ <summary>View details</summary>
359
+
125
360
  **TypeScript Signature:**
126
361
  ```typescript
127
362
  async function filterBlocksInNonLocalizedFieldValue(
@@ -129,13 +364,12 @@ async function filterBlocksInNonLocalizedFieldValue(
129
364
  fieldType: string,
130
365
  schemaRepository: SchemaRepository,
131
366
  predicate: (item: BlockInRequest, path: TreePath) => boolean | Promise<boolean>,
132
- path?: TreePath
133
367
  ): Promise<unknown>
134
368
  ```
135
369
 
136
370
  **Parameters:**
137
371
  - `nonLocalizedFieldValue`: The non-localized field value to filter
138
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
372
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
139
373
  - `schemaRepository`: Repository for caching schema lookups
140
374
  - `predicate`: Function that tests each block
141
375
 
@@ -153,11 +387,13 @@ const noVideos = await filterBlocksInNonLocalizedFieldValue(
153
387
  ```
154
388
  </details>
155
389
 
156
- <details>
157
- <summary><strong>findAllBlocksInNonLocalizedFieldValue()</strong> - Recursively search for blocks</summary>
390
+ #### findAllBlocksInNonLocalizedFieldValue()
158
391
 
159
392
  Find all blocks that match the predicate, searching recursively through nested blocks.
160
393
 
394
+ <details>
395
+ <summary>View details</summary>
396
+
161
397
  **TypeScript Signature:**
162
398
  ```typescript
163
399
  async function findAllBlocksInNonLocalizedFieldValue(
@@ -165,24 +401,25 @@ async function findAllBlocksInNonLocalizedFieldValue(
165
401
  fieldType: string,
166
402
  schemaRepository: SchemaRepository,
167
403
  predicate: (item: BlockInRequest, path: TreePath) => boolean | Promise<boolean>,
168
- path?: TreePath
169
404
  ): Promise<Array<{ item: BlockInRequest; path: TreePath }>>
170
405
  ```
171
406
 
172
407
  **Parameters:**
173
408
  - `nonLocalizedFieldValue`: The non-localized field value to search
174
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
409
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
175
410
  - `schemaRepository`: Repository for caching schema lookups
176
411
  - `predicate`: Function that tests each block
177
412
 
178
413
  **Returns:** Array of all matching blocks with their paths
179
414
  </details>
180
415
 
181
- <details>
182
- <summary><strong>reduceBlocksInNonLocalizedFieldValue()</strong> - Recursively reduce blocks</summary>
416
+ #### reduceBlocksInNonLocalizedFieldValue()
183
417
 
184
418
  Reduce all blocks recursively to a single value.
185
419
 
420
+ <details>
421
+ <summary>View details</summary>
422
+
186
423
  **TypeScript Signature:**
187
424
  ```typescript
188
425
  async function reduceBlocksInNonLocalizedFieldValue<R>(
@@ -191,26 +428,26 @@ async function reduceBlocksInNonLocalizedFieldValue<R>(
191
428
  schemaRepository: SchemaRepository,
192
429
  reducer: (accumulator: R, item: BlockInRequest, path: TreePath) => R | Promise<R>,
193
430
  initialValue: R,
194
- path?: TreePath
195
431
  ): Promise<R>
196
432
  ```
197
433
 
198
434
  **Parameters:**
199
435
  - `nonLocalizedFieldValue`: The non-localized field value to reduce
200
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
436
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
201
437
  - `schemaRepository`: Repository for caching schema lookups
202
438
  - `reducer`: Function that processes each block
203
439
  - `initialValue`: Initial accumulator value
204
440
 
205
441
  **Returns:** The final accumulated value
206
- ```
207
442
  </details>
208
443
 
209
- <details>
210
- <summary><strong>someBlocksInNonLocalizedFieldValue()</strong> - Recursively test for any match</summary>
444
+ #### someBlocksInNonLocalizedFieldValue()
211
445
 
212
446
  Check if any block (including nested) matches the predicate.
213
447
 
448
+ <details>
449
+ <summary>View details</summary>
450
+
214
451
  **TypeScript Signature:**
215
452
  ```typescript
216
453
  async function someBlocksInNonLocalizedFieldValue(
@@ -218,25 +455,25 @@ async function someBlocksInNonLocalizedFieldValue(
218
455
  fieldType: string,
219
456
  schemaRepository: SchemaRepository,
220
457
  predicate: (item: BlockInRequest, path: TreePath) => boolean | Promise<boolean>,
221
- path?: TreePath
222
458
  ): Promise<boolean>
223
459
  ```
224
460
 
225
461
  **Parameters:**
226
462
  - `nonLocalizedFieldValue`: The non-localized field value to test
227
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
463
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
228
464
  - `schemaRepository`: Repository for caching schema lookups
229
465
  - `predicate`: Function that tests each block
230
466
 
231
467
  **Returns:** True if any block matches
232
- ```
233
468
  </details>
234
469
 
235
- <details>
236
- <summary><strong>everyBlockInNonLocalizedFieldValue()</strong> - Recursively test if all match</summary>
470
+ #### everyBlockInNonLocalizedFieldValue()
237
471
 
238
472
  Check if every block (including nested) matches the predicate.
239
473
 
474
+ <details>
475
+ <summary>View details</summary>
476
+
240
477
  **TypeScript Signature:**
241
478
  ```typescript
242
479
  async function everyBlockInNonLocalizedFieldValue(
@@ -244,83 +481,28 @@ async function everyBlockInNonLocalizedFieldValue(
244
481
  fieldType: string,
245
482
  schemaRepository: SchemaRepository,
246
483
  predicate: (item: BlockInRequest, path: TreePath) => boolean | Promise<boolean>,
247
- path?: TreePath
248
484
  ): Promise<boolean>
249
485
  ```
250
486
 
251
487
  **Parameters:**
252
488
  - `nonLocalizedFieldValue`: The non-localized field value to test
253
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
489
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
254
490
  - `schemaRepository`: Repository for caching schema lookups
255
491
  - `predicate`: Function that tests each block
256
492
 
257
493
  **Returns:** True if all blocks match
258
494
  </details>
259
495
 
260
- #### Block Record Management
261
-
262
- <details>
263
- <summary><strong>buildBlockRecord()</strong> - Create block records from data</summary>
264
-
265
- Converts a block data object into the proper format for API requests.
266
-
267
- **TypeScript Signature:**
268
- ```typescript
269
- function buildBlockRecord<D extends ItemTypeDefinition>(
270
- body: ItemUpdateSchema<ToItemDefinitionInRequest<D>>
271
- ): NewBlockInRequest<ToItemDefinitionInRequest<D>>
272
- ```
273
-
274
- **Parameters:**
275
- - `body`: Block data in update schema format
276
-
277
- **Returns:** Formatted block record ready for API requests
278
- </details>
279
-
280
- <details>
281
- <summary><strong>duplicateBlockRecord()</strong> - Deep clone blocks with nested content</summary>
282
-
283
- Creates a deep copy of a block record, including all nested blocks, removing IDs to create new instances.
284
-
285
- **TypeScript Signature:**
286
- ```typescript
287
- async function duplicateBlockRecord<D extends ItemTypeDefinition>(
288
- existingBlock: ItemWithOptionalIdAndMeta<ToItemDefinitionInNestedResponse<D>>,
289
- schemaRepository: SchemaRepository
290
- ): Promise<NewBlockInRequest<ToItemDefinitionInRequest<D>>>
291
- ```
292
-
293
- **Parameters:**
294
- - `existingBlock`: The block to duplicate
295
- - `schemaRepository`: Repository for schema lookups
296
-
297
- **Returns:** New block record without IDs, ready to be created
298
- </details>
299
-
300
- ### 3. Localization-Aware Field Utilities
496
+ ## Unified Field Processing (Localized & Non-Localized)
301
497
 
302
498
  These utilities provide a unified interface for working with DatoCMS field values that may or may not be localized. They eliminate the need for conditional logic when processing fields that could be either localized or non-localized.
303
499
 
304
- <details>
305
- <summary><strong>isLocalized()</strong> - Check if a field is localized</summary>
306
-
307
- Determines whether a DatoCMS field is configured for localization.
500
+ #### mapNormalizedFieldValues() / mapNormalizedFieldValuesAsync()
308
501
 
309
- **TypeScript Signature:**
310
- ```typescript
311
- function isLocalized(field: Field): boolean
312
- ```
313
-
314
- **Parameters:**
315
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
316
-
317
- **Returns:** True if the field is localized, false otherwise
318
- </details>
502
+ Apply a transformation function to field values, handling both localized and non-localized fields uniformly.
319
503
 
320
504
  <details>
321
- <summary><strong>mapNormalizedFieldValues() / mapNormalizedFieldValuesAsync()</strong> - Transform field values</summary>
322
-
323
- Apply a transformation function to field values, handling both localized and non-localized fields uniformly.
505
+ <summary>View details</summary>
324
506
 
325
507
  **TypeScript Signatures:**
326
508
  ```typescript
@@ -338,18 +520,20 @@ async function mapNormalizedFieldValuesAsync<TInput, TOutput>(
338
520
  ```
339
521
 
340
522
  **Parameters:**
341
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
523
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
342
524
  - `nonLocalizedFieldValue`: The non-localized field value (localized or non-localized)
343
525
  - `mapFn`: Function to transform each value (receives locale for localized fields, undefined for non-localized)
344
526
 
345
527
  **Returns:** Transformed value maintaining the same structure
346
528
  </details>
347
529
 
348
- <details>
349
- <summary><strong>filterNormalizedFieldValues() / filterNormalizedFieldValuesAsync()</strong> - Filter field values</summary>
530
+ #### filterNormalizedFieldValues() / filterNormalizedFieldValuesAsync()
350
531
 
351
532
  Filter field values based on a predicate, handling both localized and non-localized fields.
352
533
 
534
+ <details>
535
+ <summary>View details</summary>
536
+
353
537
  **TypeScript Signatures:**
354
538
  ```typescript
355
539
  function filterNormalizedFieldValues<T>(
@@ -366,18 +550,20 @@ async function filterNormalizedFieldValuesAsync<T>(
366
550
  ```
367
551
 
368
552
  **Parameters:**
369
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
553
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
370
554
  - `nonLocalizedFieldValue`: The non-localized field value to filter
371
555
  - `filterFn`: Predicate function for filtering
372
556
 
373
557
  **Returns:** Filtered value or undefined if all filtered out
374
558
  </details>
375
559
 
376
- <details>
377
- <summary><strong>visitNormalizedFieldValues() / visitNormalizedFieldValuesAsync()</strong> - Iterate over field values</summary>
560
+ #### visitNormalizedFieldValues() / visitNormalizedFieldValuesAsync()
378
561
 
379
562
  Visit each value in a field, handling both localized and non-localized fields.
380
563
 
564
+ <details>
565
+ <summary>View details</summary>
566
+
381
567
  **TypeScript Signatures:**
382
568
  ```typescript
383
569
  function visitNormalizedFieldValues<T>(
@@ -394,16 +580,18 @@ async function visitNormalizedFieldValuesAsync<T>(
394
580
  ```
395
581
 
396
582
  **Parameters:**
397
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
583
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
398
584
  - `nonLocalizedFieldValue`: The non-localized field value to visit
399
585
  - `visitFn`: Function called for each value
400
586
  </details>
401
587
 
402
- <details>
403
- <summary><strong>someNormalizedFieldValues() / someNormalizedFieldValuesAsync()</strong> - Test if any value matches</summary>
588
+ #### someNormalizedFieldValues() / someNormalizedFieldValuesAsync()
404
589
 
405
590
  Check if at least one field value passes the test.
406
591
 
592
+ <details>
593
+ <summary>View details</summary>
594
+
407
595
  **TypeScript Signatures:**
408
596
  ```typescript
409
597
  function someNormalizedFieldValues<T>(
@@ -420,18 +608,20 @@ async function someNormalizedFieldValuesAsync<T>(
420
608
  ```
421
609
 
422
610
  **Parameters:**
423
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
611
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
424
612
  - `nonLocalizedFieldValue`: The non-localized field value to test
425
613
  - `testFn`: Predicate function
426
614
 
427
615
  **Returns:** True if any value passes the test
428
616
  </details>
429
617
 
430
- <details>
431
- <summary><strong>everyNormalizedFieldValue() / everyNormalizedFieldValueAsync()</strong> - Test if all values match</summary>
618
+ #### everyNormalizedFieldValue() / everyNormalizedFieldValueAsync()
432
619
 
433
620
  Check if all field values pass the test.
434
621
 
622
+ <details>
623
+ <summary>View details</summary>
624
+
435
625
  **TypeScript Signatures:**
436
626
  ```typescript
437
627
  function everyNormalizedFieldValue<T>(
@@ -448,18 +638,20 @@ async function everyNormalizedFieldValueAsync<T>(
448
638
  ```
449
639
 
450
640
  **Parameters:**
451
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
641
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
452
642
  - `nonLocalizedFieldValue`: The non-localized field value to test
453
643
  - `testFn`: Predicate function
454
644
 
455
645
  **Returns:** True if all values pass the test
456
646
  </details>
457
647
 
458
- <details>
459
- <summary><strong>toNormalizedFieldValueEntries() / fromNormalizedFieldValueEntries()</strong> - Convert between formats</summary>
648
+ #### toNormalizedFieldValueEntries() / fromNormalizedFieldValueEntries()
460
649
 
461
650
  Convert field values to/from a normalized entry format for uniform processing.
462
651
 
652
+ <details>
653
+ <summary>View details</summary>
654
+
463
655
  **TypeScript Signatures:**
464
656
  ```typescript
465
657
  function toNormalizedFieldValueEntries<T>(
@@ -479,7 +671,7 @@ type NormalizedFieldValueEntry<T> = {
479
671
  ```
480
672
 
481
673
  **Parameters:**
482
- - `fieldType`: The typeo of DatoCMS field (ie. `string`, `rich_text`, etc.)
674
+ - `fieldType`: The type of DatoCMS field (ie. `string`, `rich_text`, etc.)
483
675
  - `nonLocalizedFieldValue`/`entries non-localized`: Value to convert from/to
484
676
 
485
677
  **Returns:** Normalized entries array or reconstructed field value
@@ -500,22 +692,60 @@ const result = fromNormalizedFieldValueEntries(field, processed);
500
692
  ```
501
693
  </details>
502
694
 
503
- ### 4. Schema Repository
695
+ ## SchemaRepository
696
+
697
+ The `SchemaRepository` class provides a lightweight, in-memory cache for DatoCMS schema entities (item types, fields, fieldsets, and plugins). It helps avoid redundant API calls when working across multiple functions or utilities that require schema lookups.
698
+
699
+ **Why use it?**
504
700
 
505
- The `SchemaRepository` class provides an in-memory caching system for DatoCMS schema entities, significantly improving performance when working with complex content structures.
701
+ - **Cache once, reuse everywhere**: The first API call stores results in memory; all subsequent lookups are instant.
702
+ - **Efficient schema access**: Retrieve entities by ID, API key, or package name without re-fetching.
703
+ - **Optimized for block processing**: Essential for utilities like `mapBlocksInNonLocalizedFieldValue`.
704
+ - **Fewer API calls**: Dramatically speeds up bulk operations and complex traversals.
705
+
706
+ **Usage Example:**
506
707
 
507
708
  <details>
508
- <summary><strong>SchemaRepository</strong> - Cache schema entities for performance</summary>
709
+ <summary>View example</summary>
509
710
 
510
- Repository for DatoCMS schema entities including item types, fields, fieldsets, and plugins. Provides caching and efficient lookup functionality for schema-related operations.
711
+ ```typescript
712
+ const schemaRepository = new SchemaRepository(client);
511
713
 
512
- **Key Features:**
513
- - Automatic caching of schema entities after first fetch
514
- - Efficient lookups by ID, API key, or package name
515
- - Essential for recursive block operations
516
- - Dramatically reduces API calls during complex traversals
714
+ // First call: fetches from API and caches result
715
+ const blogPost = await schemaRepository.getItemTypeByApiKey('blog_post');
716
+ const fields = await schemaRepository.getItemTypeFields(blogPost);
717
+
718
+ // Next calls: resolved instantly from cache (no API calls)
719
+ const sameBlogPost = await schemaRepository.getItemTypeByApiKey('blog_post');
720
+ const sameFields = await schemaRepository.getItemTypeFields(blogPost);
721
+
722
+ // Works seamlessly with block-processing utilities
723
+ await mapBlocksInNonLocalizedFieldValue(
724
+ fieldValue,
725
+ fieldType,
726
+ schemaRepository, // share cached lookups
727
+ async (block) => {
728
+ // transform block here
729
+ }
730
+ );
731
+ ```
732
+ </details>
733
+
734
+ **When to Use**
735
+
736
+ * Traversing relationships that repeatedly query schema
737
+ * Bulk record processing scripts
738
+ * Block-processing utilities that need frequent lookups
739
+ * Any script where reducing API calls matters
740
+
741
+ **When Not to Use**
742
+
743
+ * Scripts that modify schema (models, fields, etc.)
744
+ * Long-running applications (cache never expires)
745
+ * Situations where the schema might change during execution
746
+
747
+ <details><summary><strong>Class signature</strong></summary>
517
748
 
518
- **TypeScript Class:**
519
749
  ```typescript
520
750
  class SchemaRepository {
521
751
  constructor(client: GenericClient)
@@ -542,41 +772,6 @@ class SchemaRepository {
542
772
  // ... and more raw variants
543
773
  }
544
774
  ```
545
-
546
- **When to Use:**
547
- - Complex traversal operations that repeatedly lookup schema
548
- - Bulk operations processing multiple records
549
- - Scripts that need efficient access to schema information
550
- - Working with recursive block utilities
551
-
552
- **When NOT to Use:**
553
- - Scripts that modify schema (models, fields, etc.)
554
- - Long-running applications (no cache expiration)
555
- - When schema might change during execution
556
-
557
- **Usage Example:**
558
- ```typescript
559
- const schemaRepository = new SchemaRepository(client);
560
-
561
- // First call fetches from API and caches
562
- const blogPost = await schemaRepository.getItemTypeByApiKey('blog_post');
563
- const fields = await schemaRepository.getItemTypeFields(blogPost);
564
-
565
- // Subsequent calls use cached data (no API calls)
566
- const sameBlogPost = await schemaRepository.getItemTypeByApiKey('blog_post');
567
- const sameFields = await schemaRepository.getItemTypeFields(blogPost);
568
-
569
- // Use with recursive utilities for optimal performance
570
- await mapBlocksInNonLocalizedFieldValue(
571
- schemaRepository, // Pass repository for efficient lookups
572
- field,
573
- fieldValue,
574
- async (block) => { /* transform */ }
575
- );
576
- ```
577
-
578
- **Performance Impact:**
579
- Without SchemaRepository, a script processing structured text with nested blocks might make the same API calls dozens of times. SchemaRepository ensures each unique schema request is made only once, dramatically improving performance for complex operations.
580
775
  </details>
581
776
 
582
777
  ## Contributing