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.
- package/NEXT.md +123 -0
- package/README.md +187 -52
- package/WRITING_PIPELINES.md +279 -106
- package/dist/legacy/createPathMapper.d.ts +4 -0
- package/dist/legacy/createPathMapper.js +21 -2
- package/dist/legacy/createPathMapper.js.map +1 -1
- package/dist/legacy/operations/lookup.js +2 -2
- package/dist/legacy/operations/lookup.js.map +1 -1
- package/dist/legacy/transformers/bucket.js +2 -1
- package/dist/legacy/transformers/bucket.js.map +1 -1
- package/dist/legacy/transformers/compare.js +3 -3
- package/dist/legacy/transformers/compare.js.map +1 -1
- package/dist/legacy/transformers/get.js +2 -2
- package/dist/legacy/transformers/get.js.map +1 -1
- package/dist/legacy/transformers/project.js +2 -2
- package/dist/legacy/transformers/project.js.map +1 -1
- package/dist/legacy/transformers/sort.js +2 -2
- package/dist/legacy/transformers/sort.js.map +1 -1
- package/dist/mapTransform.d.ts +1 -0
- package/dist/mapTransform.js +14 -22
- package/dist/mapTransform.js.map +1 -1
- package/dist/prep/alt.js +6 -0
- package/dist/prep/alt.js.map +1 -1
- package/dist/prep/apply.js +15 -3
- package/dist/prep/apply.js.map +1 -1
- package/dist/prep/array.js +6 -0
- package/dist/prep/array.js.map +1 -1
- package/dist/prep/filter.js +4 -1
- package/dist/prep/filter.js.map +1 -1
- package/dist/prep/index.d.ts +1 -1
- package/dist/prep/index.js +33 -12
- package/dist/prep/index.js.map +1 -1
- package/dist/prep/iterate.d.ts +1 -1
- package/dist/prep/iterate.js +2 -1
- package/dist/prep/iterate.js.map +1 -1
- package/dist/prep/mutation.js +17 -6
- package/dist/prep/mutation.js.map +1 -1
- package/dist/prep/path.js +3 -9
- package/dist/prep/path.js.map +1 -1
- package/dist/prep/transform.d.ts +1 -0
- package/dist/prep/transform.js +5 -4
- package/dist/prep/transform.js.map +1 -1
- package/dist/prepareOptions.d.ts +9 -0
- package/dist/prepareOptions.js +24 -0
- package/dist/prepareOptions.js.map +1 -0
- package/dist/run/alt.js +7 -5
- package/dist/run/alt.js.map +1 -1
- package/dist/run/index.d.ts +0 -5
- package/dist/run/index.js +33 -27
- package/dist/run/index.js.map +1 -1
- package/dist/run/iterate.js +2 -22
- package/dist/run/iterate.js.map +1 -1
- package/dist/run/mutation.d.ts +2 -1
- package/dist/run/mutation.js +2 -2
- package/dist/run/mutation.js.map +1 -1
- package/dist/run/path.d.ts +2 -1
- package/dist/run/path.js +11 -6
- package/dist/run/path.js.map +1 -1
- package/dist/run/resolveParentSets.d.ts +2 -0
- package/dist/run/resolveParentSets.js +46 -0
- package/dist/run/resolveParentSets.js.map +1 -0
- package/dist/run/transform.d.ts +3 -1
- package/dist/run/transform.js +9 -1
- package/dist/run/transform.js.map +1 -1
- package/dist/state.d.ts +2 -0
- package/dist/state.js +2 -0
- package/dist/state.js.map +1 -1
- package/dist/typesNext.d.ts +1 -1
- package/dist/utils/is.d.ts +1 -0
- package/dist/utils/is.js +1 -0
- package/dist/utils/is.js.map +1 -1
- package/package.json +2 -2
package/WRITING_PIPELINES.md
CHANGED
|
@@ -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
|
|
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
|
|
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)
|
|
16
|
-
const source = mapper(targetData, { rev: true })
|
|
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),
|
|
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.
|
|
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[]',
|
|
28
|
-
{
|
|
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' },
|
|
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
|
|
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
|
|
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
|
|
46
|
-
|
|
47
|
-
| `'content.title'`
|
|
48
|
-
| `'>'` prefix, e.g. `'>content.title'` | Sets value at `content.title` | Gets `content.title`
|
|
49
|
-
| `'.'`
|
|
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,
|
|
54
|
-
|
|
55
|
-
- `'
|
|
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
|
|
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
|
|
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
|
|
67
|
-
|
|
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
|
-
|
|
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
|
|
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' } },
|
|
126
|
+
{ title: 'content.heading', type: { $value: 'article' } }, // Step 1: transforms root
|
|
76
127
|
{
|
|
77
128
|
$modify: true,
|
|
78
129
|
meta: {
|
|
79
|
-
mutatedType: '^^.type',
|
|
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:
|
|
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
|
|
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
|
|
124
|
-
|
|
125
|
-
| `$iterate:
|
|
126
|
-
| `$modify: true`
|
|
127
|
-
| `$modify: 'path'`
|
|
128
|
-
| `$
|
|
129
|
-
| `$
|
|
130
|
-
| `$direction: '
|
|
131
|
-
| `$
|
|
132
|
-
| `$
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
{
|
|
302
|
+
{
|
|
303
|
+
$iterate: '>content'
|
|
304
|
+
}
|
|
207
305
|
```
|
|
208
306
|
|
|
209
|
-
Wraps a pipeline so it applies to each item in an array individually, rather
|
|
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
|
|
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
|
-
{
|
|
318
|
+
{
|
|
319
|
+
$apply: 'pipelineName'
|
|
320
|
+
}
|
|
217
321
|
```
|
|
218
322
|
|
|
219
|
-
Applies a named pipeline from `options.pipelines`. Supports `$iterate: true` and
|
|
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
|
-
{
|
|
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
|
|
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
|
|
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).
|
|
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
|
-
{
|
|
360
|
+
{
|
|
361
|
+
$concat: ['data.users', 'data.admins']
|
|
362
|
+
}
|
|
249
363
|
```
|
|
250
364
|
|
|
251
|
-
Flattens results of all pipelines into one array. `undefined` values are
|
|
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
|
-
{
|
|
372
|
+
{
|
|
373
|
+
$merge: ['original', 'updated', 'final']
|
|
374
|
+
}
|
|
257
375
|
```
|
|
258
376
|
|
|
259
|
-
Deep-merges results of all pipelines. Rightmost values win on conflicts.
|
|
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
|
|
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,
|
|
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
|
-
{
|
|
403
|
+
{
|
|
404
|
+
$value: 'Anonymous'
|
|
405
|
+
}
|
|
281
406
|
```
|
|
282
407
|
|
|
283
|
-
Shorthand for `{ $transform: 'value', value: 'Anonymous' }`. Sets a fixed value
|
|
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
|
-
{
|
|
289
|
-
|
|
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,
|
|
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
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
|
301
|
-
| `
|
|
302
|
-
| `
|
|
303
|
-
| `
|
|
304
|
-
| `
|
|
305
|
-
| `
|
|
306
|
-
| `
|
|
307
|
-
| `
|
|
308
|
-
| `
|
|
309
|
-
| `
|
|
310
|
-
| `
|
|
311
|
-
| `
|
|
312
|
-
| `
|
|
313
|
-
| `
|
|
314
|
-
| `
|
|
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: [
|
|
465
|
+
statusCodes: [
|
|
466
|
+
[200, 'ok'],
|
|
467
|
+
[404, 'notfound'],
|
|
468
|
+
['*', 'error'],
|
|
469
|
+
],
|
|
330
470
|
},
|
|
331
|
-
nonvalues: [undefined, null],
|
|
332
|
-
fwdAlias: 'from',
|
|
333
|
-
revAlias: 'to',
|
|
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
|
|
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
|
|
358
|
-
|
|
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
|
|
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
|
|
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
|
|
381
|
-
|
|
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
|
|
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
|
|
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
|
-
{
|
|
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
|
-
{
|
|
567
|
+
{
|
|
568
|
+
date: ['meta.date', { $transform: 'formatDate' }]
|
|
569
|
+
}
|
|
413
570
|
```
|
|
414
571
|
|
|
415
572
|
### Default values
|
|
573
|
+
|
|
416
574
|
```js
|
|
417
|
-
{
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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
|
|
630
|
+
const def = ['data.users', { $apply: 'userShape' }]
|
|
458
631
|
```
|