@narrative.io/jsonforms-provider-protocols 1.2.0-beta.2 → 1.2.0
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/README.md +88 -0
- package/dist/core/transforms.d.ts +6 -1
- package/dist/core/transforms.d.ts.map +1 -1
- package/dist/core/transforms.js +14 -0
- package/dist/core/transforms.js.map +1 -1
- package/package.json +1 -1
- package/src/core/transforms.ts +31 -1
package/README.md
CHANGED
|
@@ -141,6 +141,94 @@ const CustomProtocol: Protocol = {
|
|
|
141
141
|
}
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
+
### Data Transforms
|
|
145
|
+
Transform API response data before mapping to form items using a pipeline of transforms:
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"provider": {
|
|
150
|
+
"protocol": "rest_api",
|
|
151
|
+
"config": {
|
|
152
|
+
"url": "https://api.example.com/data",
|
|
153
|
+
"items": "$.data[*]",
|
|
154
|
+
"transforms": [
|
|
155
|
+
{
|
|
156
|
+
"name": "flatten",
|
|
157
|
+
"key": "children",
|
|
158
|
+
"labelFormat": "{parent.name} → {name}"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"name": "filter",
|
|
162
|
+
"key": "active",
|
|
163
|
+
"values": [true]
|
|
164
|
+
}
|
|
165
|
+
],
|
|
166
|
+
"map": {
|
|
167
|
+
"label": "$.name",
|
|
168
|
+
"value": "$.id"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### Built-in Transforms
|
|
176
|
+
|
|
177
|
+
**Flatten Transform**
|
|
178
|
+
Recursively flattens nested tree structures into a single-level array:
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{
|
|
182
|
+
"name": "flatten",
|
|
183
|
+
"key": "children",
|
|
184
|
+
"labelFormat": "{parent.name} → {name}"
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
- `key`: The property containing nested children arrays
|
|
189
|
+
- `labelFormat` (optional): Template for formatting labels using parent and child properties
|
|
190
|
+
- Adds `_depth`, `_parent`, and `_formattedLabel` metadata to items
|
|
191
|
+
|
|
192
|
+
**Filter Transform**
|
|
193
|
+
Filters items based on property values:
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"name": "filter",
|
|
198
|
+
"key": "category",
|
|
199
|
+
"values": ["A", "B"]
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
- `key`: The property to check
|
|
204
|
+
- `values` (optional): Array of values to match. If omitted, filters by key existence
|
|
205
|
+
|
|
206
|
+
**Combining Transforms**
|
|
207
|
+
Transforms are applied sequentially in pipeline order:
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"transforms": [
|
|
212
|
+
{ "name": "flatten", "key": "children" },
|
|
213
|
+
{ "name": "filter", "key": "type", "values": ["product"] }
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Custom Transforms**
|
|
219
|
+
Register custom transforms for your specific needs:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { registerTransform } from '@narrative.io/jsonforms-provider-protocols'
|
|
223
|
+
|
|
224
|
+
registerTransform('uppercase', (items, config) => {
|
|
225
|
+
return items.map(item => ({
|
|
226
|
+
...item,
|
|
227
|
+
name: item.name.toUpperCase()
|
|
228
|
+
}))
|
|
229
|
+
})
|
|
230
|
+
```
|
|
231
|
+
|
|
144
232
|
### Template Variables
|
|
145
233
|
Create dynamic URLs using form data:
|
|
146
234
|
|
|
@@ -11,7 +11,12 @@ export interface FlattenTransform extends Transform {
|
|
|
11
11
|
key: string;
|
|
12
12
|
labelFormat?: string;
|
|
13
13
|
}
|
|
14
|
-
export
|
|
14
|
+
export interface FilterTransform extends Transform {
|
|
15
|
+
name: "filter";
|
|
16
|
+
key: string;
|
|
17
|
+
values?: unknown[];
|
|
18
|
+
}
|
|
19
|
+
export type TransformStep = FlattenTransform | FilterTransform;
|
|
15
20
|
export type TransformPipeline = TransformStep[];
|
|
16
21
|
/**
|
|
17
22
|
* Registry of transform functions
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transforms.d.ts","sourceRoot":"","sources":["../../src/core/transforms.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,aAAa,GAAG,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"transforms.d.ts","sourceRoot":"","sources":["../../src/core/transforms.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAgB,SAAQ,SAAS;IAChD,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG,gBAAgB,GAAG,eAAe,CAAC;AAE/D,MAAM,MAAM,iBAAiB,GAAG,aAAa,EAAE,CAAC;AAEhD;;GAEG;AACH,KAAK,iBAAiB,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,EAAE,CAAC;AAI5E;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAE3E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,EAAE,EAChB,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,EAAE,CAYX"}
|
package/dist/core/transforms.js
CHANGED
|
@@ -59,7 +59,21 @@ function flattenTransform(items, config) {
|
|
|
59
59
|
}
|
|
60
60
|
return flattened;
|
|
61
61
|
}
|
|
62
|
+
function filterTransform(items, config) {
|
|
63
|
+
const filterConfig = config;
|
|
64
|
+
const { key, values } = filterConfig;
|
|
65
|
+
return items.filter((item) => {
|
|
66
|
+
if (typeof item !== "object" || item === null) return false;
|
|
67
|
+
const itemObj = item;
|
|
68
|
+
if (!values || values.length === 0) {
|
|
69
|
+
return key in itemObj;
|
|
70
|
+
}
|
|
71
|
+
const itemValue = itemObj[key];
|
|
72
|
+
return values.includes(itemValue);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
62
75
|
registerTransform("flatten", flattenTransform);
|
|
76
|
+
registerTransform("filter", filterTransform);
|
|
63
77
|
export {
|
|
64
78
|
applyTransformPipeline,
|
|
65
79
|
registerTransform
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transforms.js","sources":["../../src/core/transforms.ts"],"sourcesContent":["/**\n * Transform pipeline system for manipulating API response data\n * Transforms are applied sequentially in the order they appear in the pipeline\n */\n\nexport interface Transform {\n name: string;\n [key: string]: unknown;\n}\n\nexport interface FlattenTransform extends Transform {\n name: \"flatten\";\n key: string; // The key containing the nested array to flatten\n labelFormat?: string; // Optional format string like \"{parent.name} → {name}\"\n}\n\nexport type TransformStep = FlattenTransform;\n\nexport type TransformPipeline = TransformStep[];\n\n/**\n * Registry of transform functions\n */\ntype TransformFunction = (items: unknown[], config: Transform) => unknown[];\n\nconst transformRegistry: Record<string, TransformFunction> = {};\n\n/**\n * Register a transform function\n */\nexport function registerTransform(name: string, fn: TransformFunction): void {\n transformRegistry[name] = fn;\n}\n\n/**\n * Apply a pipeline of transforms to data\n */\nexport function applyTransformPipeline(\n items: unknown[],\n pipeline: TransformPipeline,\n): unknown[] {\n let result = items;\n\n for (const transform of pipeline) {\n const fn = transformRegistry[transform.name];\n if (!fn) {\n throw new Error(`Unknown transform: ${transform.name}`);\n }\n result = fn(result, transform);\n }\n\n return result;\n}\n\n/**\n * Flatten transform - recursively flattens nested arrays into a single level\n */\nfunction flattenTransform(items: unknown[], config: Transform): unknown[] {\n const flattenConfig = config as FlattenTransform;\n const { key, labelFormat } = flattenConfig;\n const flattened: unknown[] = [];\n\n function flattenRecursive(\n item: unknown,\n parent: Record<string, unknown> | null = null,\n depth: number = 0,\n ): void {\n if (typeof item !== \"object\" || item === null) return;\n\n const itemObj = item as Record<string, unknown>;\n\n // Add the current item\n if (labelFormat && parent) {\n const formattedItem = { ...itemObj };\n\n // Replace placeholders like {parent.name} and {name}\n let formattedLabel = labelFormat;\n formattedLabel = formattedLabel.replace(/\\{parent\\.(\\w+)\\}/g, (_, prop) =>\n String(parent[prop] ?? \"\"),\n );\n formattedLabel = formattedLabel.replace(/\\{(\\w+)\\}/g, (_, prop) =>\n String(itemObj[prop] ?? \"\"),\n );\n\n formattedItem._formattedLabel = formattedLabel;\n formattedItem._parent = parent;\n formattedItem._depth = depth;\n flattened.push(formattedItem);\n } else if (parent) {\n // Child node with parent reference\n flattened.push({\n ...itemObj,\n _parent: parent,\n _depth: depth,\n });\n } else {\n // Root node\n flattened.push({\n ...itemObj,\n _depth: depth,\n });\n }\n\n // Recursively flatten children\n const children = itemObj[key];\n if (Array.isArray(children)) {\n for (const child of children) {\n flattenRecursive(child, itemObj, depth + 1);\n }\n }\n }\n\n // Start flattening from root items\n for (const item of items) {\n flattenRecursive(item, null, 0);\n }\n\n return flattened;\n}\n\n// Register built-in transforms\nregisterTransform(\"flatten\", flattenTransform);\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transforms.js","sources":["../../src/core/transforms.ts"],"sourcesContent":["/**\n * Transform pipeline system for manipulating API response data\n * Transforms are applied sequentially in the order they appear in the pipeline\n */\n\nexport interface Transform {\n name: string;\n [key: string]: unknown;\n}\n\nexport interface FlattenTransform extends Transform {\n name: \"flatten\";\n key: string; // The key containing the nested array to flatten\n labelFormat?: string; // Optional format string like \"{parent.name} → {name}\"\n}\n\nexport interface FilterTransform extends Transform {\n name: \"filter\";\n key: string; // The key to check\n values?: unknown[]; // Optional array of values to match against\n}\n\nexport type TransformStep = FlattenTransform | FilterTransform;\n\nexport type TransformPipeline = TransformStep[];\n\n/**\n * Registry of transform functions\n */\ntype TransformFunction = (items: unknown[], config: Transform) => unknown[];\n\nconst transformRegistry: Record<string, TransformFunction> = {};\n\n/**\n * Register a transform function\n */\nexport function registerTransform(name: string, fn: TransformFunction): void {\n transformRegistry[name] = fn;\n}\n\n/**\n * Apply a pipeline of transforms to data\n */\nexport function applyTransformPipeline(\n items: unknown[],\n pipeline: TransformPipeline,\n): unknown[] {\n let result = items;\n\n for (const transform of pipeline) {\n const fn = transformRegistry[transform.name];\n if (!fn) {\n throw new Error(`Unknown transform: ${transform.name}`);\n }\n result = fn(result, transform);\n }\n\n return result;\n}\n\n/**\n * Flatten transform - recursively flattens nested arrays into a single level\n */\nfunction flattenTransform(items: unknown[], config: Transform): unknown[] {\n const flattenConfig = config as FlattenTransform;\n const { key, labelFormat } = flattenConfig;\n const flattened: unknown[] = [];\n\n function flattenRecursive(\n item: unknown,\n parent: Record<string, unknown> | null = null,\n depth: number = 0,\n ): void {\n if (typeof item !== \"object\" || item === null) return;\n\n const itemObj = item as Record<string, unknown>;\n\n // Add the current item\n if (labelFormat && parent) {\n const formattedItem = { ...itemObj };\n\n // Replace placeholders like {parent.name} and {name}\n let formattedLabel = labelFormat;\n formattedLabel = formattedLabel.replace(/\\{parent\\.(\\w+)\\}/g, (_, prop) =>\n String(parent[prop] ?? \"\"),\n );\n formattedLabel = formattedLabel.replace(/\\{(\\w+)\\}/g, (_, prop) =>\n String(itemObj[prop] ?? \"\"),\n );\n\n formattedItem._formattedLabel = formattedLabel;\n formattedItem._parent = parent;\n formattedItem._depth = depth;\n flattened.push(formattedItem);\n } else if (parent) {\n // Child node with parent reference\n flattened.push({\n ...itemObj,\n _parent: parent,\n _depth: depth,\n });\n } else {\n // Root node\n flattened.push({\n ...itemObj,\n _depth: depth,\n });\n }\n\n // Recursively flatten children\n const children = itemObj[key];\n if (Array.isArray(children)) {\n for (const child of children) {\n flattenRecursive(child, itemObj, depth + 1);\n }\n }\n }\n\n // Start flattening from root items\n for (const item of items) {\n flattenRecursive(item, null, 0);\n }\n\n return flattened;\n}\n\n/**\n * Filter transform - filters items based on a key and optional values\n */\nfunction filterTransform(items: unknown[], config: Transform): unknown[] {\n const filterConfig = config as FilterTransform;\n const { key, values } = filterConfig;\n\n return items.filter((item) => {\n if (typeof item !== \"object\" || item === null) return false;\n\n const itemObj = item as Record<string, unknown>;\n\n // If no values array provided, just check if the key exists\n if (!values || values.length === 0) {\n return key in itemObj;\n }\n\n // If values array provided, check if item[key] matches any of the values\n const itemValue = itemObj[key];\n return values.includes(itemValue);\n });\n}\n\n// Register built-in transforms\nregisterTransform(\"flatten\", flattenTransform);\nregisterTransform(\"filter\", filterTransform);\n"],"names":[],"mappings":"AA+BA,MAAM,oBAAuD,CAAA;AAKtD,SAAS,kBAAkB,MAAc,IAA6B;AAC3E,oBAAkB,IAAI,IAAI;AAC5B;AAKO,SAAS,uBACd,OACA,UACW;AACX,MAAI,SAAS;AAEb,aAAW,aAAa,UAAU;AAChC,UAAM,KAAK,kBAAkB,UAAU,IAAI;AAC3C,QAAI,CAAC,IAAI;AACP,YAAM,IAAI,MAAM,sBAAsB,UAAU,IAAI,EAAE;AAAA,IACxD;AACA,aAAS,GAAG,QAAQ,SAAS;AAAA,EAC/B;AAEA,SAAO;AACT;AAKA,SAAS,iBAAiB,OAAkB,QAA8B;AACxE,QAAM,gBAAgB;AACtB,QAAM,EAAE,KAAK,YAAA,IAAgB;AAC7B,QAAM,YAAuB,CAAA;AAE7B,WAAS,iBACP,MACA,SAAyC,MACzC,QAAgB,GACV;AACN,QAAI,OAAO,SAAS,YAAY,SAAS,KAAM;AAE/C,UAAM,UAAU;AAGhB,QAAI,eAAe,QAAQ;AACzB,YAAM,gBAAgB,EAAE,GAAG,QAAA;AAG3B,UAAI,iBAAiB;AACrB,uBAAiB,eAAe;AAAA,QAAQ;AAAA,QAAsB,CAAC,GAAG,SAChE,OAAO,OAAO,IAAI,KAAK,EAAE;AAAA,MAAA;AAE3B,uBAAiB,eAAe;AAAA,QAAQ;AAAA,QAAc,CAAC,GAAG,SACxD,OAAO,QAAQ,IAAI,KAAK,EAAE;AAAA,MAAA;AAG5B,oBAAc,kBAAkB;AAChC,oBAAc,UAAU;AACxB,oBAAc,SAAS;AACvB,gBAAU,KAAK,aAAa;AAAA,IAC9B,WAAW,QAAQ;AAEjB,gBAAU,KAAK;AAAA,QACb,GAAG;AAAA,QACH,SAAS;AAAA,QACT,QAAQ;AAAA,MAAA,CACT;AAAA,IACH,OAAO;AAEL,gBAAU,KAAK;AAAA,QACb,GAAG;AAAA,QACH,QAAQ;AAAA,MAAA,CACT;AAAA,IACH;AAGA,UAAM,WAAW,QAAQ,GAAG;AAC5B,QAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,iBAAW,SAAS,UAAU;AAC5B,yBAAiB,OAAO,SAAS,QAAQ,CAAC;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AAGA,aAAW,QAAQ,OAAO;AACxB,qBAAiB,MAAM,MAAM,CAAC;AAAA,EAChC;AAEA,SAAO;AACT;AAKA,SAAS,gBAAgB,OAAkB,QAA8B;AACvE,QAAM,eAAe;AACrB,QAAM,EAAE,KAAK,OAAA,IAAW;AAExB,SAAO,MAAM,OAAO,CAAC,SAAS;AAC5B,QAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AAEtD,UAAM,UAAU;AAGhB,QAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,aAAO,OAAO;AAAA,IAChB;AAGA,UAAM,YAAY,QAAQ,GAAG;AAC7B,WAAO,OAAO,SAAS,SAAS;AAAA,EAClC,CAAC;AACH;AAGA,kBAAkB,WAAW,gBAAgB;AAC7C,kBAAkB,UAAU,eAAe;"}
|
package/package.json
CHANGED
package/src/core/transforms.ts
CHANGED
|
@@ -14,7 +14,13 @@ export interface FlattenTransform extends Transform {
|
|
|
14
14
|
labelFormat?: string; // Optional format string like "{parent.name} → {name}"
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export
|
|
17
|
+
export interface FilterTransform extends Transform {
|
|
18
|
+
name: "filter";
|
|
19
|
+
key: string; // The key to check
|
|
20
|
+
values?: unknown[]; // Optional array of values to match against
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type TransformStep = FlattenTransform | FilterTransform;
|
|
18
24
|
|
|
19
25
|
export type TransformPipeline = TransformStep[];
|
|
20
26
|
|
|
@@ -118,5 +124,29 @@ function flattenTransform(items: unknown[], config: Transform): unknown[] {
|
|
|
118
124
|
return flattened;
|
|
119
125
|
}
|
|
120
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Filter transform - filters items based on a key and optional values
|
|
129
|
+
*/
|
|
130
|
+
function filterTransform(items: unknown[], config: Transform): unknown[] {
|
|
131
|
+
const filterConfig = config as FilterTransform;
|
|
132
|
+
const { key, values } = filterConfig;
|
|
133
|
+
|
|
134
|
+
return items.filter((item) => {
|
|
135
|
+
if (typeof item !== "object" || item === null) return false;
|
|
136
|
+
|
|
137
|
+
const itemObj = item as Record<string, unknown>;
|
|
138
|
+
|
|
139
|
+
// If no values array provided, just check if the key exists
|
|
140
|
+
if (!values || values.length === 0) {
|
|
141
|
+
return key in itemObj;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// If values array provided, check if item[key] matches any of the values
|
|
145
|
+
const itemValue = itemObj[key];
|
|
146
|
+
return values.includes(itemValue);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
121
150
|
// Register built-in transforms
|
|
122
151
|
registerTransform("flatten", flattenTransform);
|
|
152
|
+
registerTransform("filter", filterTransform);
|