map-transform 1.6.0-rc.7 → 1.6.0-rc.8

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 (72) hide show
  1. package/NEXT.md +123 -0
  2. package/README.md +187 -52
  3. package/WRITING_PIPELINES.md +279 -106
  4. package/dist/legacy/createPathMapper.d.ts +4 -0
  5. package/dist/legacy/createPathMapper.js +21 -2
  6. package/dist/legacy/createPathMapper.js.map +1 -1
  7. package/dist/legacy/operations/lookup.js +2 -2
  8. package/dist/legacy/operations/lookup.js.map +1 -1
  9. package/dist/legacy/transformers/bucket.js +2 -1
  10. package/dist/legacy/transformers/bucket.js.map +1 -1
  11. package/dist/legacy/transformers/compare.js +3 -3
  12. package/dist/legacy/transformers/compare.js.map +1 -1
  13. package/dist/legacy/transformers/get.js +2 -2
  14. package/dist/legacy/transformers/get.js.map +1 -1
  15. package/dist/legacy/transformers/project.js +2 -2
  16. package/dist/legacy/transformers/project.js.map +1 -1
  17. package/dist/legacy/transformers/sort.js +2 -2
  18. package/dist/legacy/transformers/sort.js.map +1 -1
  19. package/dist/mapTransform.d.ts +1 -0
  20. package/dist/mapTransform.js +14 -22
  21. package/dist/mapTransform.js.map +1 -1
  22. package/dist/prep/alt.js +6 -0
  23. package/dist/prep/alt.js.map +1 -1
  24. package/dist/prep/apply.js +15 -3
  25. package/dist/prep/apply.js.map +1 -1
  26. package/dist/prep/array.js +6 -0
  27. package/dist/prep/array.js.map +1 -1
  28. package/dist/prep/filter.js +4 -1
  29. package/dist/prep/filter.js.map +1 -1
  30. package/dist/prep/index.d.ts +1 -1
  31. package/dist/prep/index.js +33 -12
  32. package/dist/prep/index.js.map +1 -1
  33. package/dist/prep/iterate.d.ts +1 -1
  34. package/dist/prep/iterate.js +2 -1
  35. package/dist/prep/iterate.js.map +1 -1
  36. package/dist/prep/mutation.js +17 -6
  37. package/dist/prep/mutation.js.map +1 -1
  38. package/dist/prep/path.js +3 -9
  39. package/dist/prep/path.js.map +1 -1
  40. package/dist/prep/transform.d.ts +1 -0
  41. package/dist/prep/transform.js +5 -4
  42. package/dist/prep/transform.js.map +1 -1
  43. package/dist/prepareOptions.d.ts +9 -0
  44. package/dist/prepareOptions.js +24 -0
  45. package/dist/prepareOptions.js.map +1 -0
  46. package/dist/run/alt.js +7 -5
  47. package/dist/run/alt.js.map +1 -1
  48. package/dist/run/index.d.ts +0 -5
  49. package/dist/run/index.js +33 -27
  50. package/dist/run/index.js.map +1 -1
  51. package/dist/run/iterate.js +2 -22
  52. package/dist/run/iterate.js.map +1 -1
  53. package/dist/run/mutation.d.ts +2 -1
  54. package/dist/run/mutation.js +2 -2
  55. package/dist/run/mutation.js.map +1 -1
  56. package/dist/run/path.d.ts +2 -1
  57. package/dist/run/path.js +11 -6
  58. package/dist/run/path.js.map +1 -1
  59. package/dist/run/resolveParentSets.d.ts +2 -0
  60. package/dist/run/resolveParentSets.js +46 -0
  61. package/dist/run/resolveParentSets.js.map +1 -0
  62. package/dist/run/transform.d.ts +3 -1
  63. package/dist/run/transform.js +9 -1
  64. package/dist/run/transform.js.map +1 -1
  65. package/dist/state.d.ts +2 -0
  66. package/dist/state.js +2 -0
  67. package/dist/state.js.map +1 -1
  68. package/dist/typesNext.d.ts +1 -1
  69. package/dist/utils/is.d.ts +1 -0
  70. package/dist/utils/is.js +1 -0
  71. package/dist/utils/is.js.map +1 -1
  72. package/package.json +2 -2
@@ -1,10 +1,16 @@
1
1
  # Writing MapTransform Pipelines
2
2
 
3
- A guide for AI agents on how to write MapTransform definitions. MapTransform is a JavaScript/TypeScript library for transforming data between two shapes using declarative definitions. Definitions are plain JavaScript objects (JSON-compatible) and are bidirectional — the same definition transforms data forward and in reverse.
3
+ A guide for AI agents on how to write MapTransform definitions. MapTransform is
4
+ a JavaScript/TypeScript library for transforming data between two shapes using
5
+ declarative definitions. Definitions are plain JavaScript objects
6
+ (JSON-compatible) and are bidirectional — the same definition transforms data
7
+ forward and in reverse.
4
8
 
5
9
  ## Core Concepts
6
10
 
7
- **MapTransform** takes a definition and returns a mapper function. The mapper transforms source data into target data. Run it with `{ rev: true }` to reverse the transformation.
11
+ **MapTransform** takes a definition and returns a mapper function. The mapper
12
+ transforms source data into target data. Run it with `{ rev: true }` to reverse
13
+ the transformation.
8
14
 
9
15
  ```js
10
16
  import { mapTransformSync } from 'map-transform'
@@ -12,84 +18,149 @@ import { mapTransformSync } from 'map-transform'
12
18
  const def = { title: 'content.heading' }
13
19
  const mapper = mapTransformSync(def, options)
14
20
 
15
- const target = mapper(sourceData) // Forward
16
- const source = mapper(targetData, { rev: true }) // Reverse
21
+ const target = mapper(sourceData) // Forward
22
+ const source = mapper(targetData, { rev: true }) // Reverse
17
23
  ```
18
24
 
19
- There are three exports: `mapTransform` (default, async, legacy), `mapTransformSync` (sync), and `mapTransformAsync` (async). Prefer `mapTransformSync` or `mapTransformAsync`.
25
+ There are three exports: `mapTransform` (default, async, legacy),
26
+ `mapTransformSync` (sync), and `mapTransformAsync` (async). Prefer
27
+ `mapTransformSync` or `mapTransformAsync`.
20
28
 
21
29
  ## Pipelines
22
30
 
23
- A **pipeline** is an array of steps. Data flows through each step in order. Steps can be paths, mutation objects, or operation objects.
31
+ A **pipeline** is an array of steps. Data flows through each step in order.
32
+ Steps can be paths, mutation objects, or operation objects.
24
33
 
25
34
  ```js
26
35
  const def = [
27
- 'data.items[]', // Get path
28
- { // Mutation object
36
+ 'data.items[]', // Get path
37
+ {
38
+ // Mutation object
29
39
  $iterate: true,
30
40
  id: 'articleNo',
31
41
  title: ['headline', { $transform: 'maxLength', length: 20 }],
32
42
  },
33
- { $filter: 'onlyActive' }, // Operation object
43
+ { $filter: 'onlyActive' }, // Operation object
34
44
  ]
35
45
  ```
36
46
 
37
- If a pipeline has only one step, the array can be omitted. A single path string is a valid pipeline.
47
+ If a pipeline has only one step, the array can be omitted. A single path string
48
+ is a valid pipeline.
38
49
 
39
- In **reverse mode**, the pipeline runs backwards: the last step runs first, and get/set paths swap roles.
50
+ In **reverse mode**, the pipeline runs backwards: the last step runs first, and
51
+ get/set paths swap roles.
40
52
 
41
53
  ## Dot Notation Paths
42
54
 
43
55
  Paths navigate into objects using dot-separated keys.
44
56
 
45
- | Path | Forward | Reverse|
46
- |---|---|---|
47
- | `'content.title'` | Gets `content.title` | Sets value at `content.title` |
48
- | `'>'` prefix, e.g. `'>content.title'` | Sets value at `content.title` | Gets `content.title` |
49
- | `'.'` | Returns current value as-is | Returns current value as-is |
57
+ | Path | Forward | Reverse |
58
+ | ------------------------------------- | ----------------------------- | ----------------------------- |
59
+ | `'content.title'` | Gets `content.title` | Sets value at `content.title` |
60
+ | `'>'` prefix, e.g. `'>content.title'` | Sets value at `content.title` | Gets `content.title` |
61
+ | `'.'` | Returns current value as-is | Returns current value as-is |
50
62
 
51
63
  ### Arrays in Paths
52
64
 
53
- - `'tags[].id'` — iterates `tags` array, gets `id` from each item. In reverse, wraps values back into `{ id: ... }` objects in a `tags` array.
54
- - `'tags.id[]'` gets `id` from each item in `tags`, ensures result is an array. In reverse, array stays at `id` level.
55
- - `'items[0]'` — gets first item from `items` array. Negative indices count from end.
65
+ - `'tags[].id'` — iterates `tags` array, gets `id` from each item. In reverse,
66
+ wraps values back into `{ id: ... }` objects in a `tags` array.
67
+ - `'tags.id[]'` — gets `id` from each item in `tags`, ensures result is an
68
+ array. In reverse, array stays at `id` level.
69
+ - `'items[0]'` — gets first item from `items` array. Negative indices count from
70
+ end.
56
71
  - `'content[]'` — ensures result is always an array, even for a single value.
57
72
 
58
- The position of `[]` matters for reverse mapping. Place `[]` where the array actually is in the data structure.
73
+ The position of `[]` matters for reverse mapping. Place `[]` where the array
74
+ actually is in the data structure.
59
75
 
60
- When a path with `[]` meets `undefined` (or any nonvalue), an empty array is returned (unless `noDefaults` is true).
76
+ When a path with `[]` meets `undefined` (or any nonvalue), an empty array is
77
+ returned (unless `noDefaults` is true).
61
78
 
62
79
  ### Parent and Root Paths
63
80
 
64
81
  - `'^.prop'` — goes up one level (like `../` in file paths)
65
82
  - `'^.^.prop'` — goes up two levels
66
- - `'^^.prop'` — goes to root level (the current pipeline's root, which may be mutated by earlier steps)
67
- - `'^^^.prop'` goes to the original root level (always the original source data, regardless of prior transformations)
83
+ - `'^^.prop'` — goes to the top of the tree you're in (the mutated root when a
84
+ mutation object has run at the top level, otherwise the original data)
85
+ - `'^^^.prop'` — goes to the original source data, regardless of prior
86
+ transformations
87
+
88
+ The number of carets follows from the structure of the definition alone:
89
+
90
+ 1. **Only drilling down adds a level.** A key segment adds one, `[0]` adds one,
91
+ and entering an array item adds one — whether by iterating a path with `[]`,
92
+ by a mutation object iterating an array, or by the `$iterate` operation. A
93
+ bare `[]` adds nothing. So from inside iterating `tags[]`, use `'^.^.id'` to
94
+ reach the object above the array. One `^` reaches the array itself, which is
95
+ useful for things like `^.[0].id`.
96
+ 2. **Operations are opaque.** After `$transform`, a mutation object, `$alt`,
97
+ `$if`, `$filter`, `$apply`, or `$array`, the levels are what they were before
98
+ the operation. Inside an operation, you inherit the levels from where it is
99
+ used.
100
+ 3. **Set paths don't move.** `^` after `>prop` is the same as `^` before it.
101
+ 4. **`^^` is the top of the tree, `^^^` is the original data.** When you write a
102
+ named pipeline for `$apply` and don't know how deep it will be applied, `^^`
103
+ gets you to the top of the tree it is applied in.
104
+ 5. **In reverse, parent and root paths set on the parent or root level** of the
105
+ target, with the same number of carets. A missing parent level drops the
106
+ value.
107
+
108
+ You move up in the data you moved down in. After a mutation object, a path into
109
+ the result and a parent path back up walks the mutated data:
68
110
 
69
- Inside an iteration, the array counts as one level. So from inside iterating `tags[]`, use `'^.^.id'` to reach the object above the array. You reach the array with one `^`, which can be useful to reach other items, like `^.[0].id` to get the id of the first item in the parent array.
111
+ ```js
112
+ const def = [
113
+ { id: 'key', item: { title: 'content.heading' } },
114
+ { title: 'item.title', id: ['item.title', '^.^.id'] },
115
+ ]
116
+ // { key: 'ent1', content: { heading: 'The heading' } } → { title: 'The heading', id: 'ent1' }
117
+ ```
70
118
 
71
- **`^^` vs `^^^`:** In a multi-step pipeline, `^^` refers to the root as it exists at the current point in the pipeline — if a previous step mutated it, `^^` sees the mutated version. Use `^^^` when you need the original, unmutated source data:
119
+ **`^^` vs `^^^`:** In a multi-step pipeline, `^^` refers to the root as it
120
+ exists at the current point in the pipeline — if a previous step mutated it,
121
+ `^^` sees the mutated version. Use `^^^` when you need the original, unmutated
122
+ source data:
72
123
 
73
124
  ```js
74
125
  const def = [
75
- { title: 'content.heading', type: { $value: 'article' } }, // Step 1: transforms root
126
+ { title: 'content.heading', type: { $value: 'article' } }, // Step 1: transforms root
76
127
  {
77
128
  $modify: true,
78
129
  meta: {
79
- mutatedType: '^^.type', // 'article' (from step 1 output)
130
+ mutatedType: '^^.type', // 'article' (from step 1 output)
80
131
  originalType: '^^^.type', // original value from source data
81
132
  },
82
133
  },
83
134
  ]
84
135
  ```
85
136
 
137
+ **Setting on parent and root:** In reverse, `section: '^.^.meta.section'` inside
138
+ an iteration sets `meta.section` on the object holding the array, which is where
139
+ it was read from going forward:
140
+
141
+ ```js
142
+ const def = [
143
+ 'items[]',
144
+ { $iterate: true, id: 'key', section: '^.^.meta.section' },
145
+ ]
146
+ // Forward: { meta: { section: 'news' }, items: [{ key: 'ent1' }] } → [{ id: 'ent1', section: 'news' }]
147
+ // Reverse: [{ id: 'ent1', section: 'news' }] → { meta: { section: 'news' }, items: [{ key: 'ent1' }] }
148
+ ```
149
+
150
+ The same works going forward with a set path, like the key
151
+ `'^.^.meta.section': 'section'` on a mutation object. A parent _within_ a path,
152
+ like `item.^.count`, just cancels the step before it and is the same as `count`
153
+ in both directions.
154
+
86
155
  ### Escaped Keys
87
156
 
88
- Keys starting with `$` have special meaning. Escape with backslash: `'data.\\$type'`.
157
+ Keys starting with `$` have special meaning. Escape with backslash:
158
+ `'data.\\$type'`.
89
159
 
90
160
  ## Mutation Objects
91
161
 
92
- A mutation object describes the target object shape. Each key becomes a key on the result, and each value is a pipeline describing how to get the data.
162
+ A mutation object describes the target object shape. Each key becomes a key on
163
+ the result, and each value is a pipeline describing how to get the data.
93
164
 
94
165
  ```js
95
166
  const def = {
@@ -120,20 +191,22 @@ const def = { 'articles[]': 'content.items' }
120
191
 
121
192
  ### Special Properties on Mutation Objects
122
193
 
123
- | Property | Description |
124
- |---|---|
125
- | `$iterate: true` | Apply mutation to each item in an array. Will still apply to single value |
126
- | `$modify: true` | Modify existing object instead of replacing it |
127
- | `$modify: 'path'` | Modify the object at the given path |
128
- | `$flip: true` | Definition is written from reverse perspective |
129
- | `$direction: 'fwd'` | Only apply this mutation going forward |
130
- | `$direction: 'rev'` | Only apply this mutation going in reverse |
131
- | `$noDefaults: true` | Don't include default values or undefined props in this mutation object or its children |
132
- | `$alwaysApply: true` | Apply mutation even when input is a nonvalue |
194
+ | Property | Description |
195
+ | -------------------- | --------------------------------------------------------------------------------------- |
196
+ | `$iterate: false` | Apply mutation to an array as a whole. By default, it's applied to each item |
197
+ | `$modify: true` | Modify existing object instead of replacing it |
198
+ | `$modify: 'path'` | Modify the object at the given path |
199
+ | `$modify: '$modify'` | Modify existing object in both directions |
200
+ | `$flip: true` | Definition is written from reverse perspective |
201
+ | `$direction: 'fwd'` | Only apply this mutation going forward |
202
+ | `$direction: 'rev'` | Only apply this mutation going in reverse |
203
+ | `$noDefaults: true` | Don't include default values or undefined props in this mutation object or its children |
204
+ | `$alwaysApply: true` | Apply mutation even when input is a nonvalue |
133
205
 
134
206
  ### `$modify`
135
207
 
136
- Normally a mutation replaces the pipeline value. With `$modify: true`, it merges with the existing object (keeping properties not set by the mutation).
208
+ Normally a mutation replaces the pipeline value. With `$modify: true`, it merges
209
+ with the existing object (keeping properties not set by the mutation).
137
210
 
138
211
  ```js
139
212
  const def = {
@@ -143,13 +216,29 @@ const def = {
143
216
  // Keeps all existing top-level props, only overwrites `data`
144
217
  ```
145
218
 
146
- `$modify` can also be a path: `$modify: 'response'` merges with the object at `response`.
219
+ `$modify` can also be a path: `$modify: 'response'` merges with the object at
220
+ `response`.
221
+
222
+ `$modify` is a path segment that merges instead of replacing, and it takes
223
+ effect on the side being set: going forward when it's in a key, and in reverse
224
+ when it's in a value, e.g. `{ response: '$modify', ... }`.
147
225
 
148
- For reverse, `$modify` is expressed as a value: `{ response: '$modify', ... }`.
226
+ Put `$modify` on both sides to modify in both directions:
227
+
228
+ ```js
229
+ const def = {
230
+ $modify: 'response.$modify',
231
+ data: 'response.data.deeply.placed.items',
232
+ }
233
+ // Merges with the object at `response` going forward, and sets the merged
234
+ // object on `response` in reverse
235
+ ```
149
236
 
150
237
  ### `$flip`
151
238
 
152
- When the reverse transformation is more complex, define the mutation from the reverse perspective and set `$flip: true`. In forward mode, keys become get paths and values become set paths (the reverse of normal).
239
+ When the reverse transformation is more complex, define the mutation from the
240
+ reverse perspective and set `$flip: true`. In forward mode, keys become get
241
+ paths and values become set paths (the reverse of normal).
153
242
 
154
243
  The mutation object is still run as it would if it was defined "the right way".
155
244
 
@@ -164,11 +253,15 @@ const def = {
164
253
  }
165
254
  ```
166
255
 
167
- The `/1` variant is skipped in forward mode. In reverse, both `name` and `name/1` pipelines run. Use this when one source value maps to multiple target properties. Escaped slashes (`\\/`) in keys are treated as literal slashes.
256
+ The `/1` variant is skipped in forward mode. In reverse, both `name` and
257
+ `name/1` pipelines run. Use this when one source value maps to multiple target
258
+ properties. Escaped slashes (`\\/`) in keys are treated as literal slashes.
168
259
 
169
260
  ## Operation Objects
170
261
 
171
- Operation objects are steps in a pipeline that perform specific actions. The presence of any of these `$` prefixed keys makes this an operation object -- as opposed to a mutation object.
262
+ Operation objects are steps in a pipeline that perform specific actions. The
263
+ presence of any of these `$` prefixed keys makes this an operation object -- as
264
+ opposed to a mutation object.
172
265
 
173
266
  ### `$transform` — Apply a Transformer
174
267
 
@@ -176,7 +269,8 @@ Operation objects are steps in a pipeline that perform specific actions. The pre
176
269
  { $transform: 'transformerName', ...props }
177
270
  ```
178
271
 
179
- Runs the named transformer on the pipeline value. Extra properties are passed to the transformer. Supports `$iterate: true` and `$direction`.
272
+ Runs the named transformer on the pipeline value. Extra properties are passed to
273
+ the transformer. Supports `$iterate: true` and `$direction`.
180
274
 
181
275
  ### `$filter` — Filter Values
182
276
 
@@ -186,7 +280,9 @@ Runs the named transformer on the pipeline value. Extra properties are passed to
186
280
  { $filter: ['path', { $transform: 'check' }] }
187
281
  ```
188
282
 
189
- Uses a transformer or pipeline as a predicate. For arrays, removes items where the predicate is falsy. For non-arrays, replaces value with `undefined` when falsy. Supports `$direction`.
283
+ Uses a transformer or pipeline as a predicate. For arrays, removes items where
284
+ the predicate is falsy. For non-arrays, replaces value with `undefined` when
285
+ falsy. Supports `$direction`.
190
286
 
191
287
  ### `$if` — Conditional
192
288
 
@@ -203,30 +299,43 @@ Uses a transformer or pipeline as a predicate. For arrays, removes items where t
203
299
  ### `$iterate` — Iterate a Pipeline
204
300
 
205
301
  ```js
206
- { $iterate: '>content' }
302
+ {
303
+ $iterate: '>content'
304
+ }
207
305
  ```
208
306
 
209
- Wraps a pipeline so it applies to each item in an array individually, rather than the array as a whole.
307
+ Wraps a pipeline so it applies to each item in an array individually, rather
308
+ than the array as a whole.
210
309
 
211
- Note that the presence of `$iterate` by itself does not turn this into an operation object, it will just make a mutation object iterate.
310
+ Note that `$iterate` is also used as a flag on mutation objects and other
311
+ operators, to control if they are iterated or not. In these cases, the value is
312
+ either `true` or `false`, while `$iterate: <pipeline>` iterates the given
313
+ pipeline.
212
314
 
213
315
  ### `$apply` — Named Pipelines
214
316
 
215
317
  ```js
216
- { $apply: 'pipelineName' }
318
+ {
319
+ $apply: 'pipelineName'
320
+ }
217
321
  ```
218
322
 
219
- Applies a named pipeline from `options.pipelines`. Supports `$iterate: true` and `$direction`.
323
+ Applies a named pipeline from `options.pipelines`. Supports `$iterate: true` and
324
+ `$direction`.
220
325
 
221
326
  ### `$alt` — Alternatives / Defaults
222
327
 
223
328
  ```js
224
- { $alt: ['data.name', 'data.username', { $value: 'Anonymous' }] }
329
+ {
330
+ $alt: ['data.name', 'data.username', { $value: 'Anonymous' }]
331
+ }
225
332
  ```
226
333
 
227
- Tries each pipeline in order until one returns a non-`undefined` value. Common pattern for default values. Supports `$iterate: true` and `$direction`.
334
+ Tries each pipeline in order until one returns a non-`undefined` value. Common
335
+ pattern for default values. Supports `$iterate: true` and `$direction`.
228
336
 
229
- In reverse, alternatives run in reverse order, but the first pipeline always runs (as it typically acts as the set path).
337
+ In reverse, alternatives run in reverse order, but the first pipeline always
338
+ runs (as it typically acts as the set path).
230
339
 
231
340
  ### `$array` — Build an Array
232
341
 
@@ -240,23 +349,33 @@ In reverse, alternatives run in reverse order, but the first pipeline always run
240
349
  }
241
350
  ```
242
351
 
243
- Runs each pipeline and collects results into an array (maintaining positions). In reverse, each pipeline receives the item at its position from the input array. Supports `$flip: true` to reverse the direction (create array in reverse instead of forward). Supports `$iterate` and `$direction`.
352
+ Runs each pipeline and collects results into an array (maintaining positions).
353
+ In reverse, each pipeline receives the item at its position from the input
354
+ array. Supports `$flip: true` to reverse the direction (create array in reverse
355
+ instead of forward). Supports `$iterate` and `$direction`.
244
356
 
245
357
  ### `$concat` — Concatenate Arrays
246
358
 
247
359
  ```js
248
- { $concat: ['data.users', 'data.admins'] }
360
+ {
361
+ $concat: ['data.users', 'data.admins']
362
+ }
249
363
  ```
250
364
 
251
- Flattens results of all pipelines into one array. `undefined` values are filtered out. Destructive in reverse (all data goes to first pipeline). There is also `$concatRev` which reverses the direction.
365
+ Flattens results of all pipelines into one array. `undefined` values are
366
+ filtered out. Destructive in reverse (all data goes to first pipeline). There is
367
+ also `$concatRev` which reverses the direction.
252
368
 
253
369
  ### `$merge` — Merge Objects (Shorthand)
254
370
 
255
371
  ```js
256
- { $merge: ['original', 'updated', 'final'] }
372
+ {
373
+ $merge: ['original', 'updated', 'final']
374
+ }
257
375
  ```
258
376
 
259
- Deep-merges results of all pipelines. Rightmost values win on conflicts. `undefined` never overwrites. Destructive in reverse.
377
+ Deep-merges results of all pipelines. Rightmost values win on conflicts.
378
+ `undefined` never overwrites. Destructive in reverse.
260
379
 
261
380
  ### `$lookup` — Look Up in Array
262
381
 
@@ -264,7 +383,10 @@ Deep-merges results of all pipelines. Rightmost values win on conflicts. `undefi
264
383
  { $lookup: '^^.users[]', path: 'id' }
265
384
  ```
266
385
 
267
- Replaces the pipeline value with the first matching object from the array at `$lookup`, matched by the `path` property. Set `matchSeveral: true` to get all matches. In reverse, extracts the `path` value from each object. Honors `$flip` mode.
386
+ Replaces the pipeline value with the first matching object from the array at
387
+ `$lookup`, matched by the `path` property. Set `matchSeveral: true` to get all
388
+ matches. In reverse, extracts the `path` value from each object. Honors `$flip`
389
+ mode.
268
390
 
269
391
  ### `$lookdown` — Reverse Lookup
270
392
 
@@ -272,46 +394,58 @@ Replaces the pipeline value with the first matching object from the array at `$l
272
394
  { $lookdown: '^^.users[]', path: 'id' }
273
395
  ```
274
396
 
275
- Same as `$lookup` but in the opposite direction. Looks up going in reverse, extracts going forward.
397
+ Same as `$lookup` but in the opposite direction. Looks up going in reverse,
398
+ extracts going forward.
276
399
 
277
400
  ### `$value` — Set a Value (Shorthand)
278
401
 
279
402
  ```js
280
- { $value: 'Anonymous' }
403
+ {
404
+ $value: 'Anonymous'
405
+ }
281
406
  ```
282
407
 
283
- Shorthand for `{ $transform: 'value', value: 'Anonymous' }`. Sets a fixed value in the pipeline. Respects `noDefaults` (returns `undefined` when `noDefaults` is true). Use `{ $transform: 'fixed', value: ... }` for values that should persist even with `noDefaults`.
408
+ Shorthand for `{ $transform: 'value', value: 'Anonymous' }`. Sets a fixed value
409
+ in the pipeline. Respects `noDefaults` (returns `undefined` when `noDefaults` is
410
+ true). Use `{ $transform: 'fixed', value: ... }` for values that should persist
411
+ even with `noDefaults`.
284
412
 
285
413
  ### `$and` / `$or` — Logical Operations (Shorthand)
286
414
 
287
415
  ```js
288
- { $and: ['active', 'authorized'] }
289
- { $or: ['active', 'draft'] }
416
+ {
417
+ $and: ['active', 'authorized']
418
+ }
419
+ {
420
+ $or: ['active', 'draft']
421
+ }
290
422
  ```
291
423
 
292
- Shorthands for the `logical` transformer. Runs pipelines, forces to boolean, applies AND/OR logic. Typically used with `$if`.
424
+ Shorthands for the `logical` transformer. Runs pipelines, forces to boolean,
425
+ applies AND/OR logic. Typically used with `$if`.
293
426
 
294
427
  ## Built-in Transformers
295
428
 
296
- Provide these via `options.transformers` or use them by name in `$transform`/`$filter` operations.
297
-
298
- | Transformer | Description |
299
- |---|---|
300
- | `bucket` | Splits array into named buckets by condition, size, or `groupByPath` |
301
- | `compare` | Compares values. Props: `path`, `match`/`matchPath`, `operator` (`=`, `!=`, `>`, `>=`, `<`, `<=`, `in`, `exists`), `not` |
302
- | `explode` | Object array of `{ key, value }` pairs. In reverse, array `{ key: index, value }` |
303
- | `implode` | Opposite of `explode`. Implodes forward, explodes in reverse. |
304
- | `fixed` | Sets a value. Like `value` but ignores `noDefaults`. |
305
- | `flatten` | Flattens nested arrays. Props: `depth` (default: 1) |
306
- | `index` | Returns current iteration index (0 outside iteration) |
307
- | `logical` | AND/OR logic on pipelines. Props: `operator` (`AND`/`OR`), `pipelines` |
308
- | `map` | Dictionary mapping with array of `[from, to]` tuples. Props: `dictionary` (a dictionary or the id of a dictionary), `flip`. Wildcard: `'*'` |
309
- | `merge` | Deep-merges objects from pipelines in `path`. |
310
- | `mergeRev` | Opposite direction of `merge`. |
311
- | `not` | Boolean negation of the value |
312
- | `project` | Keeps/removes object props. Props: `include`/`exclude` (arrays of strings), `includePath`/`excludePath` |
313
- | `sort` | Sorts arrays. Props: `path` (dot notation), `asc` (default: true) |
314
- | `value` | Sets a fixed value. Skipped when `noDefaults` is true. |
429
+ Provide these via `options.transformers` or use them by name in
430
+ `$transform`/`$filter` operations.
431
+
432
+ | Transformer | Description |
433
+ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
434
+ | `bucket` | Splits array into named buckets by condition, size, or `groupByPath` |
435
+ | `compare` | Compares values. Props: `path`, `match`/`matchPath`, `operator` (`=`, `!=`, `>`, `>=`, `<`, `<=`, `in`, `exists`), `not` |
436
+ | `explode` | Object → array of `{ key, value }` pairs. In reverse, array → `{ key: index, value }` |
437
+ | `implode` | Opposite of `explode`. Implodes forward, explodes in reverse. |
438
+ | `fixed` | Sets a value. Like `value` but ignores `noDefaults`. |
439
+ | `flatten` | Flattens nested arrays. Props: `depth` (default: 1) |
440
+ | `index` | Returns current iteration index (0 outside iteration) |
441
+ | `logical` | AND/OR logic on pipelines. Props: `operator` (`AND`/`OR`), `pipelines` |
442
+ | `map` | Dictionary mapping with array of `[from, to]` tuples. Props: `dictionary` (a dictionary or the id of a dictionary), `flip`. Wildcard: `'*'` |
443
+ | `merge` | Deep-merges objects from pipelines in `path`. |
444
+ | `mergeRev` | Opposite direction of `merge`. |
445
+ | `not` | Boolean negation of the value |
446
+ | `project` | Keeps/removes object props. Props: `include`/`exclude` (arrays of strings), `includePath`/`excludePath` |
447
+ | `sort` | Sorts arrays. Props: `path` (dot notation), `asc` (default: true) |
448
+ | `value` | Sets a fixed value. Skipped when `noDefaults` is true. |
315
449
 
316
450
  ## Options Object
317
451
 
@@ -320,17 +454,23 @@ Passed as second argument to `mapTransformSync(def, options)`:
320
454
  ```js
321
455
  const options = {
322
456
  transformers: {
323
- myTransformer: (props) => (options) => (data, state) => { /* ... */ },
457
+ myTransformer: (props) => (options) => (data, state) => {
458
+ /* ... */
459
+ },
324
460
  },
325
461
  pipelines: {
326
462
  castEntry: { title: ['title', { $transform: 'ensureString' }] },
327
463
  },
328
464
  dictionaries: {
329
- statusCodes: [[200, 'ok'], [404, 'notfound'], ['*', 'error']],
465
+ statusCodes: [
466
+ [200, 'ok'],
467
+ [404, 'notfound'],
468
+ ['*', 'error'],
469
+ ],
330
470
  },
331
- nonvalues: [undefined, null], // Values treated as "no value" (default: [undefined])
332
- fwdAlias: 'from', // Alias for 'fwd' in $direction
333
- revAlias: 'to', // Alias for 'rev' in $direction
471
+ nonvalues: [undefined, null], // Values treated as "no value" (default: [undefined])
472
+ fwdAlias: 'from', // Alias for 'fwd' in $direction
473
+ revAlias: 'to', // Alias for 'rev' in $direction
334
474
  }
335
475
  ```
336
476
 
@@ -346,7 +486,8 @@ const myTransformer = (props) => (options) => (data, state) => {
346
486
  }
347
487
  ```
348
488
 
349
- Transformers should be pure functions: don't mutate `data`, don't rely on external state.
489
+ Transformers should be pure functions: don't mutate `data`, don't rely on
490
+ external state.
350
491
 
351
492
  ## Reverse Mapping
352
493
 
@@ -354,8 +495,10 @@ Every definition implicitly defines a reverse transformation:
354
495
 
355
496
  1. **Paths swap roles**: get paths become set paths, set paths become get paths.
356
497
  2. **Pipeline order reverses**: steps run last-to-first.
357
- 3. **Mutation objects reverse**: keys become get sources, values become set targets.
358
- 4. **Transformers run in both directions** by default, but may have the opposite effect in rev.
498
+ 3. **Mutation objects reverse**: keys become get sources, values become set
499
+ targets.
500
+ 4. **Transformers run in both directions** by default, but may have the opposite
501
+ effect in rev.
359
502
 
360
503
  ```js
361
504
  const def = {
@@ -369,22 +512,29 @@ const def = {
369
512
 
370
513
  ### Data Loss
371
514
 
372
- Reverse mapping can only reconstruct data that was mapped. Properties not included in the definition are lost.
515
+ Reverse mapping can only reconstruct data that was mapped. Properties not
516
+ included in the definition are lost.
373
517
 
374
518
  ### Direction Control
375
519
 
376
- Use `$direction: 'fwd'` or `$direction: 'rev'` on operation and mutation objects to limit them to one direction. Use slashed keys (`name/1`) for reverse-only mutation properties.
520
+ Use `$direction: 'fwd'` or `$direction: 'rev'` on operation and mutation objects
521
+ to limit them to one direction. Use slashed keys (`name/1`) for reverse-only
522
+ mutation properties.
377
523
 
378
524
  ## Nonvalues and `undefined`
379
525
 
380
- - `undefined` is treated as a "nonvalue": it triggers `$alt` alternatives, is excluded with `noDefaults`, and `[]` paths return empty arrays for it.
381
- - `null` is treated as a value by default. Set `nonvalues: [undefined, null]` in options to treat `null` as a nonvalue too.
526
+ - `undefined` is treated as a "nonvalue": it triggers `$alt` alternatives, is
527
+ excluded with `noDefaults`, and `[]` paths return empty arrays for it.
528
+ - `null` is treated as a value by default. Set `nonvalues: [undefined, null]` in
529
+ options to treat `null` as a nonvalue too.
382
530
  - In JSON definitions, use `'**undefined**'` to represent `undefined`.
383
- - A mutation object is skipped entirely when its input is a nonvalue (unless `$alwaysApply: true`).
531
+ - A mutation object is skipped entirely when its input is a nonvalue (unless
532
+ `$alwaysApply: true`).
384
533
 
385
534
  ## Providing a Target
386
535
 
387
- You can provide an initial target object that gets merged with the transformation result:
536
+ You can provide an initial target object that gets merged with the
537
+ transformation result:
388
538
 
389
539
  ```js
390
540
  const target = { id: '12345', title: 'Default title' }
@@ -394,11 +544,15 @@ mapper(sourceData, { target })
394
544
  ## Common Patterns
395
545
 
396
546
  ### Simple field mapping
547
+
397
548
  ```js
398
- { targetField: 'source.path' }
549
+ {
550
+ targetField: 'source.path'
551
+ }
399
552
  ```
400
553
 
401
554
  ### Iterating an array of objects
555
+
402
556
  ```js
403
557
  {
404
558
  $iterate: true,
@@ -408,16 +562,25 @@ mapper(sourceData, { target })
408
562
  ```
409
563
 
410
564
  ### Pipeline with path + transform
565
+
411
566
  ```js
412
- { date: ['meta.date', { $transform: 'formatDate' }] }
567
+ {
568
+ date: ['meta.date', { $transform: 'formatDate' }]
569
+ }
413
570
  ```
414
571
 
415
572
  ### Default values
573
+
416
574
  ```js
417
- { name: { $alt: ['fullName', { $value: 'Unknown' }] } }
575
+ {
576
+ name: {
577
+ $alt: ['fullName', { $value: 'Unknown' }]
578
+ }
579
+ }
418
580
  ```
419
581
 
420
582
  ### Conditional mapping
583
+
421
584
  ```js
422
585
  {
423
586
  $if: 'isActive',
@@ -427,16 +590,25 @@ mapper(sourceData, { target })
427
590
  ```
428
591
 
429
592
  ### Dictionary mapping
593
+
430
594
  ```js
431
- { status: ['statusCode', { $transform: 'map', dictionary: 'statusCodes' }] }
595
+ {
596
+ status: ['statusCode', { $transform: 'map', dictionary: 'statusCodes' }]
597
+ }
432
598
  ```
433
599
 
434
600
  ### Concatenating arrays from multiple sources
601
+
435
602
  ```js
436
- { allUsers: { $concat: ['data.users', 'data.admins'] } }
603
+ {
604
+ allUsers: {
605
+ $concat: ['data.users', 'data.admins']
606
+ }
607
+ }
437
608
  ```
438
609
 
439
610
  ### Nested mutation objects
611
+
440
612
  ```js
441
613
  {
442
614
  user: {
@@ -450,9 +622,10 @@ mapper(sourceData, { target })
450
622
  ```
451
623
 
452
624
  ### Named pipelines with `$apply`
625
+
453
626
  ```js
454
627
  // In options.pipelines:
455
628
  // userShape: { id: 'userId', name: 'fullName' }
456
629
 
457
- const def = ['data.users[]', { $iterate: true }, { $apply: 'userShape' }]
630
+ const def = ['data.users', { $apply: 'userShape' }]
458
631
  ```