@grnsft/if 0.2.1 → 0.3.1
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/.commitlintrc.js +4 -7
- package/README.md +5 -1
- package/Refactor-migration-guide.md +342 -0
- package/build/{models/export-csv.d.ts → builtins/export-csv-raw.d.ts} +1 -1
- package/build/builtins/export-csv-raw.js +129 -0
- package/build/builtins/export-csv.d.ts +10 -0
- package/build/builtins/export-csv.js +86 -0
- package/build/builtins/export-log.js +18 -0
- package/build/builtins/export-yaml.js +27 -0
- package/build/builtins/group-by.d.ts +5 -0
- package/build/builtins/group-by.js +56 -0
- package/build/{models → builtins}/index.js +1 -1
- package/build/{models → builtins}/time-sync.d.ts +2 -2
- package/build/builtins/time-sync.js +304 -0
- package/build/config/config.js +2 -2
- package/build/config/params.js +56 -1
- package/build/config/strings.d.ts +4 -2
- package/build/config/strings.js +7 -9
- package/build/index.js +3 -1
- package/build/lib/aggregate.d.ts +1 -1
- package/build/lib/aggregate.js +4 -3
- package/build/lib/compute.js +9 -10
- package/build/lib/exhaust.js +8 -5
- package/build/lib/initialize.d.ts +2 -2
- package/build/lib/initialize.js +9 -5
- package/build/lib/load.d.ts +33 -2
- package/build/lib/load.js +4 -4
- package/build/lib/parameterize.d.ts +1 -1
- package/build/lib/parameterize.js +6 -4
- package/build/types/aggregation.d.ts +0 -1
- package/build/types/aggregation.js +1 -1
- package/build/types/compute.d.ts +3 -3
- package/build/types/compute.js +1 -1
- package/build/types/interface.d.ts +14 -1
- package/build/types/interface.js +6 -1
- package/build/types/manifest.d.ts +9 -40
- package/build/types/manifest.js +1 -1
- package/build/types/parameters.d.ts +2 -12
- package/build/types/parameters.js +1 -1
- package/build/types/plugin-storage.d.ts +6 -0
- package/build/types/plugin-storage.js +3 -0
- package/build/util/aggregation-helper.d.ts +1 -1
- package/build/util/aggregation-helper.js +11 -12
- package/build/util/errors.d.ts +1 -1
- package/build/util/errors.js +2 -2
- package/build/util/json.js +25 -5
- package/build/util/log-memoize.d.ts +3 -0
- package/build/util/log-memoize.js +19 -0
- package/build/util/logger.d.ts +1 -1
- package/build/util/logger.js +2 -3
- package/build/util/plugin-storage.d.ts +14 -0
- package/build/util/plugin-storage.js +34 -0
- package/build/util/validations.d.ts +144 -11
- package/build/util/validations.js +19 -17
- package/examples/manifests/asim-demo.yml +74 -0
- package/examples/manifests/basic-demo.yml +4 -9
- package/examples/manifests/boavizta-cloud.yml +20 -0
- package/examples/manifests/cim.yml +3 -3
- package/examples/manifests/divide.yml +38 -0
- package/examples/manifests/generics.yml +4 -4
- package/examples/manifests/group-by.yml +13 -13
- package/examples/manifests/mock-observation.yml +2 -2
- package/examples/manifests/nesting-demo.yml +2 -2
- package/examples/manifests/nesting.yml +16 -16
- package/examples/manifests/pipeline-demo-1.yml +4 -4
- package/examples/manifests/pipeline-demo-2.yml +9 -9
- package/examples/manifests/pipeline-demo.yml +10 -10
- package/examples/manifests/pipeline-teads-sci.yml +4 -4
- package/examples/manifests/pipeline-with-generics.yml +8 -8
- package/examples/manifests/pipeline-with-mocks.yml +10 -10
- package/examples/manifests/regex.yml +23 -0
- package/package.json +6 -3
- package/src/builtins/README.md +852 -0
- package/build/models/export-csv.js +0 -129
- package/build/models/export-log.js +0 -18
- package/build/models/export-yaml.js +0 -24
- package/build/models/group-by.d.ts +0 -11
- package/build/models/group-by.js +0 -56
- package/build/models/time-sync.js +0 -304
- package/build/types/initialize.d.ts +0 -4
- package/build/types/initialize.js +0 -3
- package/build/types/load.d.ts +0 -7
- package/build/types/load.js +0 -3
- package/src/models/README.md +0 -268
- /package/build/{models → builtins}/export-log.d.ts +0 -0
- /package/build/{models → builtins}/export-yaml.d.ts +0 -0
- /package/build/{models → builtins}/index.d.ts +0 -0
package/.commitlintrc.js
CHANGED
|
@@ -31,15 +31,12 @@ module.exports = {
|
|
|
31
31
|
'.github',
|
|
32
32
|
'.husky',
|
|
33
33
|
'scripts',
|
|
34
|
-
'
|
|
34
|
+
'builtins',
|
|
35
35
|
'plugins',
|
|
36
36
|
'integration',
|
|
37
|
-
'doc'
|
|
38
|
-
]
|
|
37
|
+
'doc',
|
|
38
|
+
],
|
|
39
39
|
],
|
|
40
|
-
'scope-empty': [
|
|
41
|
-
2,
|
|
42
|
-
'never'
|
|
43
|
-
]
|
|
40
|
+
'scope-empty': [2, 'never'],
|
|
44
41
|
},
|
|
45
42
|
};
|
package/README.md
CHANGED
|
@@ -24,10 +24,14 @@
|
|
|
24
24
|
> [!IMPORTANT]
|
|
25
25
|
> Incubation Project: This project is an incubation project being run inside the Green Software Foundation; as such, we DON’T recommend using it in any critical use case. Incubation projects are experimental, offer no support guarantee, have minimal governance and process, and may be retired at any moment. This project may one day Graduate, in which case this disclaimer will be removed.
|
|
26
26
|
|
|
27
|
+
**Note** We have recently refactored the IF codebase and introduced some changes affecting both users and developers. You can read our migration guide [HERE](./Refactor-migration-guide.md) to help you update to the latest version!
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
27
31
|
[Impact Framework](https://greensoftwarefoundation.atlassian.net/wiki/spaces/~612dd45e45cd76006a84071a/pages/17072136/Opensource+Impact+Engine+Framework) (IF) is an [Incubation](https://oc.greensoftware.foundation/project-lifecycle.html#incubation) project from the [Open Source Working Group](https://greensoftwarefoundation.atlassian.net/wiki/spaces/~612dd45e45cd76006a84071a/pages/852049/Open+Source+Working+Group) in the [Green Software Foundation](https://greensoftware.foundation/).
|
|
28
32
|
|
|
29
33
|
|
|
30
|
-
**Our documentation is online at [if.greensoftware.foundation](
|
|
34
|
+
**Our documentation is online at [if.greensoftware.foundation](https://if.greensoftware.foundation/)**
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
**IF** is a framework to **M**odel, **M**easure, si**M**ulate and **M**onitor the environmental impacts of software
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# Managing the IF refactor
|
|
2
|
+
|
|
3
|
+
We recently completed a major refactor of the entire IF codebase, moving from an object oriented to functional programmign style.
|
|
4
|
+
|
|
5
|
+
If you are a IF user or developer, there are some changes you must be aware of resulting from a recent refactor of the IF codebase. This guide will help you to update your processes to integrate your work with the newly refactored IF.
|
|
6
|
+
|
|
7
|
+
## Running IF
|
|
8
|
+
|
|
9
|
+
There have been some name changes to the CLI, specifically:
|
|
10
|
+
|
|
11
|
+
- `impact-engine` --> `ie`
|
|
12
|
+
The command line tool has been renamed from `impact-engine` to simply `ie`. This means that to invoke the Impact Framework on the command line you simply use
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
ie ...
|
|
16
|
+
```
|
|
17
|
+
- `impl` --> `manifest`
|
|
18
|
+
We have deprecated the original `impl` and `ompl` terminology across all our repositories and on the command line. Now, to pass a manifest file to IF, you use the `--manifest` command, as follows:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
ie --manifest <path-to-manifest>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
- `ompl` --> `output`
|
|
26
|
+
|
|
27
|
+
We have deprecated the original `impl` and `ompl` terminology across all our repositories and on the command line. Now, to define a savepath for your output file, you use the `--output` command, as follows:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
ie --manifest <path-to-manifest> --output <savepath>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Outputs
|
|
34
|
+
|
|
35
|
+
We currently default to exporting output data to the console only. If you want to export to a file, you have to add a small piece of config inside your manifest, as follows:
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
initalize:
|
|
39
|
+
plugins: ...
|
|
40
|
+
outputs: ['yaml']
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
You then provide your savepatrh (without file extension) to the `--output` command on the CLI.
|
|
44
|
+
|
|
45
|
+
The available options are currently `csv`, `yaml` or `log`.
|
|
46
|
+
|
|
47
|
+
### Summary of how to run the refactored IF
|
|
48
|
+
|
|
49
|
+
As before, you can install IF from our npm package using
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
npm i @grnsft/if
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Then run IF using the following command:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
ie --manifest <path-to-manifest>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This will dump the output to the console. If you want to save the output to a yaml file, provide a savepath to the `--output` command:
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
ie --manifest <path-to-manifest> --output <savepath>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
If you want to clone the source code and install and run a local copy of IF, you can do so using:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
git clone https://github.com/Green-Software-Foundation/if &&
|
|
72
|
+
cd if &&
|
|
73
|
+
npm i
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Then run IF using the following command:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
npm run if -- --manifest <path-to-manifest>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## Manifest files
|
|
84
|
+
|
|
85
|
+
There have also been some changes to the structure of manifest files. Some of these are simple renaming changes, others are more functional.
|
|
86
|
+
|
|
87
|
+
- **Rename `graph` -> `tree`**
|
|
88
|
+
The `graph` section of the manifest file is now renamed to `tree`. This is just to help us stay consistent in our metaphors and provide a more familiar naming convention for the data beneath.
|
|
89
|
+
|
|
90
|
+
- **Use plugin name as key in `Initialize` block**
|
|
91
|
+
In the previous version of IF, the plugins were organized into an array each having a `name` key, with the plugin name as the value. In the refactored IF, we use the name as the key identifying the plugin. For example, this is the OLD way:
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
initialize:
|
|
95
|
+
plugins:
|
|
96
|
+
- name: ccf
|
|
97
|
+
model: CloudCarbonFootprint
|
|
98
|
+
path: if-plugins
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
This is the new way:
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
initialize:
|
|
105
|
+
plugins:
|
|
106
|
+
"sci-e":
|
|
107
|
+
path: "@grnsft/if-plugins"
|
|
108
|
+
method: SciE
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- **Rename `model` to `method` in `Initialize` block**
|
|
112
|
+
Each plugin in the initialize block
|
|
113
|
+
|
|
114
|
+
Each plugin in the `Initialize` block has a field where the name of the exported function representing that plugin is defined. Previously, these were class names and they were defined using the `model` key. Now, they are functions, and they are defined using the `method` key. We use `method` instead of `function` because `function` is a reserved keyword in Typescript.
|
|
115
|
+
|
|
116
|
+
For example:
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
"sci-m":
|
|
120
|
+
path: "@grnsft/if-plugins"
|
|
121
|
+
method: SciM
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- **Global config**
|
|
125
|
+
We have introduced the concept of global config to the plugins. This is truly global configuration data that should be kept constant regardless of where the plugin is invoked across the manifest file.
|
|
126
|
+
|
|
127
|
+
A good example is the `interpolation` method to use in the Teads curve plugin - this is not expected to vary from component to component and can therefore be defined in global config. The plugin code itself must expect the global config. Then, the config can be passed in the `Initialize` block, for example:
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
initialize:
|
|
131
|
+
plugins:
|
|
132
|
+
"time-sync":
|
|
133
|
+
method: TimeSync
|
|
134
|
+
path: "builtin"
|
|
135
|
+
global-config:
|
|
136
|
+
start-time: "2023-12-12T00:00:00.000Z"
|
|
137
|
+
end-time: "2023-12-12T00:01:00.000Z"
|
|
138
|
+
interval: 5
|
|
139
|
+
allow-padding: true
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- **Node level config**
|
|
143
|
+
|
|
144
|
+
We have also introduced the concept of node-level config. This is designed for pluin configuration that might vary between components in the tree. For example, for each child in the tree you might wish to use the `groupby` plugin to group the outputs according to a different set of keys.
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
tree:
|
|
148
|
+
children:
|
|
149
|
+
child-1:
|
|
150
|
+
pipeline:
|
|
151
|
+
- teads-curve
|
|
152
|
+
- sci-e
|
|
153
|
+
- sci-m
|
|
154
|
+
- sci-o
|
|
155
|
+
- time-sync
|
|
156
|
+
- sci
|
|
157
|
+
config:
|
|
158
|
+
group-by:
|
|
159
|
+
group:
|
|
160
|
+
- region
|
|
161
|
+
- cloud/instance-type
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
- **Defaults**
|
|
165
|
+
|
|
166
|
+
We have also introduced the concept of `defaults`. This is a section in each component's definition that can be used to provide fallbacks for missing input data. For example, perhaps you have a value arriving from an external API that should be present in every observation in your inputs array, but for soem reason the API fails to deliver a value for some timestamps. In this case, IF would fallback to the value provided for that metric in the `defaults` section of the manifest for that component.
|
|
167
|
+
|
|
168
|
+
You can also use `defaults` as a quick way to add values to everyobservation in your input array if those values are expected to be constant over time (e.g. some of the lifespan values for embodied carbon calculations could be appropriate to include in defaults). This saves you from having to enter the value in every observation in the input array, instead IF can automatically grab it from `defaults` for every timestamp.
|
|
169
|
+
|
|
170
|
+
```yaml
|
|
171
|
+
tree:
|
|
172
|
+
children:
|
|
173
|
+
child-1:
|
|
174
|
+
pipeline:
|
|
175
|
+
- teads-curve
|
|
176
|
+
- sci-e
|
|
177
|
+
- sci-m
|
|
178
|
+
- sci-o
|
|
179
|
+
- time-sync
|
|
180
|
+
- sci
|
|
181
|
+
defaults:
|
|
182
|
+
cpu/thermal-design-power: 100
|
|
183
|
+
grid/carbon-intensity: 800
|
|
184
|
+
device/emissions-embodied: 1533.120 # gCO2eq
|
|
185
|
+
time-reserved: 3600 # 1hr in seconds
|
|
186
|
+
device/expected-lifespan: 94608000 # 3 years in seconds
|
|
187
|
+
resources-reserved: 1
|
|
188
|
+
resources-total: 8
|
|
189
|
+
functional-unit-time: "1 min"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
## New IF features
|
|
195
|
+
|
|
196
|
+
### Time-sync
|
|
197
|
+
Technically time-sync is not a new feature as it was present in IF before the refactor, but there are some tweaks to how the plugin is configured that are worth explaining here. Time sync snaps all input arrays across an entire graph to a common time grid.
|
|
198
|
+
|
|
199
|
+
This means you have to define a global start time, end time and interval to use everywhere. There is also a boolean to toggle whether you should allow the time sync model to pad the start and end of your time series with zeroes. You should default to `true` unless you have a specific reason not to. In the refactored IF we expect this information to be provided in global config, as follows:
|
|
200
|
+
|
|
201
|
+
```yaml
|
|
202
|
+
initialize:
|
|
203
|
+
plugins:
|
|
204
|
+
"time-sync":
|
|
205
|
+
method: TimeSync
|
|
206
|
+
path: "builtin"
|
|
207
|
+
global-config:
|
|
208
|
+
start-time: "2023-12-12T00:00:00.000Z"
|
|
209
|
+
end-time: "2023-12-12T00:01:00.000Z"
|
|
210
|
+
interval: 5
|
|
211
|
+
allow-padding: true
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Aggregate
|
|
215
|
+
|
|
216
|
+
The aggregate plugin aggregates data in two ways: first it condenses individual time series down into a single value (in many cases, this will be the total across the observation period for each metric) and aggregating multiple time series from several components into a single time series (in many cases this means the sum of the metric across multiple componments for each timestep).
|
|
217
|
+
|
|
218
|
+
This is a builtin feature of IF, meaning it does not have to be initialized as a plugin. Instead, you just have to include a short config block in the top of the manifest file. There are two pieces of information required:
|
|
219
|
+
|
|
220
|
+
- `metrics`: which metrics do you want to aggregate? Every metric you provide here must exist in the output array.
|
|
221
|
+
|
|
222
|
+
- `type`: the options are `horizontal`, `vertical` or both. Horizontal aggregation is the type that condenses each time series into a single summary value. Vertical aggregation is aggregated across components.
|
|
223
|
+
|
|
224
|
+
Here's what the config block should look like:
|
|
225
|
+
|
|
226
|
+
```yaml
|
|
227
|
+
aggregation:
|
|
228
|
+
metrics:
|
|
229
|
+
- 'carbon'
|
|
230
|
+
type: 'both'
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
### Groupby
|
|
235
|
+
|
|
236
|
+
Groupby allows you to regroup your outputs according to keys you define. For example, maybe you want to group your outputs by region (show me all the outputs for applications run in `uk-south` etc). Groupby *is* a plugin that needs to be initialized in the manifest.
|
|
237
|
+
|
|
238
|
+
You can initialize the plugin as follows:
|
|
239
|
+
|
|
240
|
+
```yaml
|
|
241
|
+
initialize:
|
|
242
|
+
plugins:
|
|
243
|
+
'group-by':
|
|
244
|
+
path: builtin
|
|
245
|
+
method: GroupBy
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Then you configure groupby for each component in the node level config. In the following example we will regroup the outputs by the `region`:
|
|
249
|
+
|
|
250
|
+
```yaml
|
|
251
|
+
tree:
|
|
252
|
+
children:
|
|
253
|
+
child-1:
|
|
254
|
+
pipeline:
|
|
255
|
+
- teads-curve
|
|
256
|
+
- sci-e
|
|
257
|
+
- sci-m
|
|
258
|
+
- sci-o
|
|
259
|
+
- time-sync
|
|
260
|
+
- group-by
|
|
261
|
+
- sci
|
|
262
|
+
config:
|
|
263
|
+
group-by:
|
|
264
|
+
group:
|
|
265
|
+
- region
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Exhaust
|
|
269
|
+
|
|
270
|
+
We have introduced `exhaust` as an IF feature. This is a wrapper around export plugins and it allows community contributors to create plugins for exporting to different formats.
|
|
271
|
+
|
|
272
|
+
Details tbc...
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
## Plugins
|
|
276
|
+
|
|
277
|
+
The plugins themselves require some changes to keep them compatible with the refactored IF.
|
|
278
|
+
|
|
279
|
+
Instead of the old class-based model, plugins are now functions. They conform to the following interface:
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
export type PluginInterface = {
|
|
283
|
+
execute: (
|
|
284
|
+
inputs: PluginParams[],
|
|
285
|
+
config?: Record<string, any>
|
|
286
|
+
) => PluginParams[];
|
|
287
|
+
metadata: {
|
|
288
|
+
kind: string;
|
|
289
|
+
};
|
|
290
|
+
[key: string]: any;
|
|
291
|
+
};
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
The plugin still requires an execute function. This is where you implement the plugin logic.
|
|
295
|
+
|
|
296
|
+
Here's a minimal example for a plugin that sums some inputs defined in global config - see inline comments for some important notes:
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
|
|
300
|
+
// Here's the function definition - notice that global config is passed in here!
|
|
301
|
+
export const Sum = (globalConfig: SumConfig): PluginInterface => {
|
|
302
|
+
const inputParameters = globalConfig['input-parameters'] || [];
|
|
303
|
+
const outputParameter = globalConfig['output-parameter'];
|
|
304
|
+
|
|
305
|
+
// we also return metadata now too - you can add more or just use this default
|
|
306
|
+
const metadata = {
|
|
307
|
+
kind: 'execute',
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Calculate the sum of the input metrics for each timestamp.
|
|
312
|
+
*/
|
|
313
|
+
const execute = async (inputs: PluginParams[]): Promise<PluginParams[]> => {
|
|
314
|
+
inputs.map(input => {
|
|
315
|
+
return calculateSum(input, inputParameters, outputParameter);
|
|
316
|
+
});
|
|
317
|
+
return inputs;
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Calculates the sum of the energy components.
|
|
322
|
+
*/
|
|
323
|
+
const calculateSum = (
|
|
324
|
+
input: PluginParams,
|
|
325
|
+
inputParameters: string[],
|
|
326
|
+
outputParameter: string
|
|
327
|
+
) => {
|
|
328
|
+
input[outputParameter] = inputParameters.reduce(
|
|
329
|
+
(accumulator, metricToSum) => {
|
|
330
|
+
return accumulator + input[metricToSum];
|
|
331
|
+
},
|
|
332
|
+
0
|
|
333
|
+
);
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// return the metadata and the execute function
|
|
337
|
+
return {
|
|
338
|
+
metadata,
|
|
339
|
+
execute,
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
```
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { ExhaustPluginInterface } from '../types/exhaust-plugin-interface';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const ExportCSVRaw: () => ExhaustPluginInterface;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.ExportCSVRaw = void 0;
|
|
27
|
+
const fs = __importStar(require("fs/promises"));
|
|
28
|
+
const errors_1 = require("../util/errors");
|
|
29
|
+
const { WriteFileError, CliInputError } = errors_1.ERRORS;
|
|
30
|
+
const ExportCSVRaw = () => {
|
|
31
|
+
/**
|
|
32
|
+
* handle a tree leaf, where there are no child nodes, by adding it as key->value pair to the flat map
|
|
33
|
+
* and capturing key as a header
|
|
34
|
+
*/
|
|
35
|
+
const handleLeafValue = (value, fullPath, key, flatMap, headers) => {
|
|
36
|
+
if (fullPath.includes('outputs')) {
|
|
37
|
+
headers.add(key);
|
|
38
|
+
flatMap[fullPath] = value;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* handle a tree node, recursively traverse the children and append their results to the flat map and captured headers
|
|
43
|
+
*/
|
|
44
|
+
const handleNodeValue = (value, fullPath, flatMap, headers) => {
|
|
45
|
+
const [subFlatMap, subHeaders] = extractFlatMapAndHeaders(value, fullPath);
|
|
46
|
+
if (Object.keys(subFlatMap).length > 0) {
|
|
47
|
+
Object.entries(subFlatMap).forEach(([subKey, value]) => {
|
|
48
|
+
flatMap[subKey] = value;
|
|
49
|
+
});
|
|
50
|
+
subHeaders.forEach(subHeader => {
|
|
51
|
+
headers.add(subHeader);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Handles a key at the top level of the tree
|
|
57
|
+
*/
|
|
58
|
+
const handleKey = (value, key, prefix, flatMap, headers) => {
|
|
59
|
+
const fullPath = prefix ? `${prefix}.${key}` : key;
|
|
60
|
+
if (value !== null && typeof value === 'object') {
|
|
61
|
+
return handleNodeValue(value, fullPath, flatMap, headers);
|
|
62
|
+
}
|
|
63
|
+
return handleLeafValue(value, fullPath, key, flatMap, headers);
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Recursively extract a flat map and headers from the hierarcial tree.
|
|
67
|
+
*/
|
|
68
|
+
const extractFlatMapAndHeaders = (tree, prefix = '') => {
|
|
69
|
+
const headers = new Set();
|
|
70
|
+
const flatMap = [];
|
|
71
|
+
for (const key in tree) {
|
|
72
|
+
if (key in tree) {
|
|
73
|
+
handleKey(tree[key], key, prefix, flatMap, headers);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return [flatMap, headers];
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* extract the id of the key, that is removing the last token (which is the index).
|
|
80
|
+
* in this manner, multiple keys that identical besides their index share the same id.
|
|
81
|
+
*/
|
|
82
|
+
const extractIdHelper = (key) => {
|
|
83
|
+
const parts = key.split('.');
|
|
84
|
+
parts.pop();
|
|
85
|
+
return parts.join('.');
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* generate a CSV formatted string based on a flat key->value map, headers and ids
|
|
89
|
+
*/
|
|
90
|
+
const getCsvString = (map, headers, ids) => {
|
|
91
|
+
const csvRows = [];
|
|
92
|
+
csvRows.push(['id', ...headers].join(','));
|
|
93
|
+
ids.forEach(id => {
|
|
94
|
+
const rowData = [id];
|
|
95
|
+
headers.forEach(header => {
|
|
96
|
+
const value = map[`${id}.${header}`] ?? '';
|
|
97
|
+
rowData.push(value.toString());
|
|
98
|
+
});
|
|
99
|
+
csvRows.push(rowData.join(','));
|
|
100
|
+
});
|
|
101
|
+
return csvRows.join('\n');
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* write the given string content to a file at the provided path
|
|
105
|
+
*/
|
|
106
|
+
const writeOutputFile = async (content, outputPath) => {
|
|
107
|
+
try {
|
|
108
|
+
await fs.writeFile(`${outputPath}.csv`, content);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
throw new WriteFileError(`Failed to write CSV to ${outputPath}: ${error}`);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* export the provided tree content to a CSV file, represented in a flat structure
|
|
116
|
+
*/
|
|
117
|
+
const execute = async (tree, _context, outputPath) => {
|
|
118
|
+
if (!outputPath) {
|
|
119
|
+
throw new CliInputError('Output path is required.');
|
|
120
|
+
}
|
|
121
|
+
const [extractredFlatMap, extractedHeaders] = extractFlatMapAndHeaders(tree);
|
|
122
|
+
const ids = new Set(Object.keys(extractredFlatMap).map(key => extractIdHelper(key)));
|
|
123
|
+
const csvString = getCsvString(extractredFlatMap, extractedHeaders, ids);
|
|
124
|
+
writeOutputFile(csvString, outputPath);
|
|
125
|
+
};
|
|
126
|
+
return { execute };
|
|
127
|
+
};
|
|
128
|
+
exports.ExportCSVRaw = ExportCSVRaw;
|
|
129
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhwb3J0LWNzdi1yYXcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYnVpbHRpbnMvZXhwb3J0LWNzdi1yYXcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxnREFBa0M7QUFFbEMsMkNBQXNDO0FBS3RDLE1BQU0sRUFBQyxjQUFjLEVBQUUsYUFBYSxFQUFDLEdBQUcsZUFBTSxDQUFDO0FBRXhDLE1BQU0sWUFBWSxHQUFHLEdBQTJCLEVBQUU7SUFDdkQ7OztPQUdHO0lBQ0gsTUFBTSxlQUFlLEdBQUcsQ0FDdEIsS0FBVSxFQUNWLFFBQWdCLEVBQ2hCLEdBQVEsRUFDUixPQUE2QixFQUM3QixPQUFvQixFQUNwQixFQUFFO1FBQ0YsSUFBSSxRQUFRLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxFQUFFO1lBQ2hDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDakIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHLEtBQUssQ0FBQztTQUMzQjtJQUNILENBQUMsQ0FBQztJQUVGOztPQUVHO0lBQ0gsTUFBTSxlQUFlLEdBQUcsQ0FDdEIsS0FBVSxFQUNWLFFBQWdCLEVBQ2hCLE9BQTRCLEVBQzVCLE9BQW9CLEVBQ3BCLEVBQUU7UUFDRixNQUFNLENBQUMsVUFBVSxFQUFFLFVBQVUsQ0FBQyxHQUFHLHdCQUF3QixDQUFDLEtBQUssRUFBRSxRQUFRLENBQUMsQ0FBQztRQUUzRSxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtZQUN0QyxNQUFNLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxFQUFFLEVBQUU7Z0JBQ3JELE9BQU8sQ0FBQyxNQUFNLENBQUMsR0FBRyxLQUFLLENBQUM7WUFDMUIsQ0FBQyxDQUFDLENBQUM7WUFF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QUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsR0FBRyxVQUFVLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztTQUNsRDtRQUFDLE9BQU8sS0FBSyxFQUFFO1lBQ2QsTUFBTSxJQUFJLGNBQWMsQ0FDdEIsMEJBQTBCLFVBQVUsS0FBSyxLQUFLLEVBQUUsQ0FDakQsQ0FBQztTQUNIO0lBQ0gsQ0FBQyxDQUFDO0lBRUY7O09BRUc7SUFDSCxNQUFNLE9BQU8sR0FBRyxLQUFLLEVBQUUsSUFBUyxFQUFFLFFBQWlCLEVBQUUsVUFBa0IsRUFBRSxFQUFFO1FBQ3pFLElBQUksQ0FBQyxVQUFVLEVBQUU7WUFDZixNQUFNLElBQUksYUFBYSxDQUFDLDBCQUEwQixDQUFDLENBQUM7U0FDckQ7UUFFRCxNQUFNLENBQUMsaUJBQWlCLEVBQUUsZ0JBQWdCLENBQUMsR0FDekMsd0JBQXdCLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDakMsTUFBTSxHQUFHLEdBQUcsSUFBSSxHQUFHLENBQ2pCLE1BQU0sQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FDaEUsQ0FBQztRQUNGLE1BQU0sU0FBUyxHQUFHLFlBQVksQ0FBQyxpQkFBaUIsRUFBRSxnQkFBZ0IsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUV6RSxlQUFlLENBQUMsU0FBUyxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQ3pDLENBQUMsQ0FBQztJQUVGLE9BQU8sRUFBQyxPQUFPLEVBQUMsQ0FBQztBQUNuQixDQUFDLENBQUM7QUFqSlcsUUFBQSxZQUFZLGdCQWlKdkIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBmcyBmcm9tICdmcy9wcm9taXNlcyc7XG5cbmltcG9ydCB7RVJST1JTfSBmcm9tICcuLi91dGlsL2Vycm9ycyc7XG5cbmltcG9ydCB7RXhoYXVzdFBsdWdpbkludGVyZmFjZX0gZnJvbSAnLi4vdHlwZXMvZXhoYXVzdC1wbHVnaW4taW50ZXJmYWNlJztcbmltcG9ydCB7Q29udGV4dH0gZnJvbSAnLi4vdHlwZXMvbWFuaWZlc3QnO1xuXG5jb25zdCB7V3JpdGVGaWxlRXJyb3IsIENsaUlucHV0RXJyb3J9ID0gRVJST1JTO1xuXG5leHBvcnQgY29uc3QgRXhwb3J0Q1NWUmF3ID0gKCk6IEV4aGF1c3RQbHVnaW5JbnRlcmZhY2UgPT4ge1xuICAvKipcbiAgICogaGFuZGxlIGEgdHJlZSBsZWFmLCB3aGVyZSB0aGVyZSBhcmUgbm8gY2hpbGQgbm9kZXMsIGJ5IGFkZGluZyBpdCBhcyBrZXktPnZhbHVlIHBhaXIgdG8gdGhlIGZsYXQgbWFwXG4gICAqIGFuZCBjYXB0dXJpbmcga2V5IGFzIGEgaGVhZGVyXG4gICAqL1xuICBjb25zdCBoYW5kbGVMZWFmVmFsdWUgPSAoXG4gICAgdmFsdWU6IGFueSxcbiAgICBmdWxsUGF0aDogc3RyaW5nLFxuICAgIGtleTogYW55LFxuICAgIGZsYXRNYXA6IHtba2V5OiBzdHJpbmddOiBhbnl9LFxuICAgIGhlYWRlcnM6IFNldDxzdHJpbmc+XG4gICkgPT4ge1xuICAgIGlmIChmdWxsUGF0aC5pbmNsdWRlcygnb3V0cHV0cycpKSB7XG4gICAgICBoZWFkZXJzLmFkZChrZXkpO1xuICAgICAgZmxhdE1hcFtmdWxsUGF0aF0gPSB2YWx1ZTtcbiAgICB9XG4gIH07XG5cbiAgLyoqXG4gICAqIGhhbmRsZSBhIHRyZWUgbm9kZSwgcmVjdXJzaXZlbHkgdHJhdmVyc2UgdGhlIGNoaWxkcmVuIGFuZCBhcHBlbmQgdGhlaXIgcmVzdWx0cyB0byB0aGUgZmxhdCBtYXAgYW5kIGNhcHR1cmVkIGhlYWRlcnNcbiAgICovXG4gIGNvbnN0IGhhbmRsZU5vZGVWYWx1ZSA9IChcbiAgICB2YWx1ZTogYW55LFxuICAgIGZ1bGxQYXRoOiBzdHJpbmcsXG4gICAgZmxhdE1hcDogUmVjb3JkPHN0cmluZywgYW55PixcbiAgICBoZWFkZXJzOiBTZXQ8c3RyaW5nPlxuICApID0+IHtcbiAgICBjb25zdCBbc3ViRmxhdE1hcCwgc3ViSGVhZGVyc10gPSBleHRyYWN0RmxhdE1hcEFuZEhlYWRlcnModmFsdWUsIGZ1bGxQYXRoKTtcblxuICAgIGlmIChPYmplY3Qua2V5cyhzdWJGbGF0TWFwKS5sZW5ndGggPiAwKSB7XG4gICAgICBPYmplY3QuZW50cmllcyhzdWJGbGF0TWFwKS5mb3JFYWNoKChbc3ViS2V5LCB2YWx1ZV0pID0+IHtcbiAgICAgICAgZmxhdE1hcFtzdWJLZXldID0gdmFsdWU7XG4gICAgICB9KTtcblxuICAgICAgc3ViSGVhZGVycy5mb3JFYWNoKHN1YkhlYWRlciA9PiB7XG4gICAgICAgIGhlYWRlcnMuYWRkKHN1YkhlYWRlcik7XG4gICAgICB9KTtcbiAgICB9XG4gIH07XG5cbiAgLyoqXG4gICAqIEhhbmRsZXMgYSBrZXkgYXQgdGhlIHRvcCBsZXZlbCBvZiB0aGUgdHJlZVxuICAgKi9cbiAgY29uc3QgaGFuZGxlS2V5ID0gKFxuICAgIHZhbHVlOiBhbnksXG4gICAga2V5OiBhbnksXG4gICAgcHJlZml4OiBzdHJpbmcsXG4gICAgZmxhdE1hcDogUmVjb3JkPHN0cmluZywgYW55PixcbiAgICBoZWFkZXJzOiBTZXQ8c3RyaW5nPlxuICApID0+IHtcbiAgICBjb25zdCBmdWxsUGF0aCA9IHByZWZpeCA/IGAke3ByZWZpeH0uJHtrZXl9YCA6IGtleTtcblxuICAgIGlmICh2YWx1ZSAhPT0gbnVsbCAmJiB0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnKSB7XG4gICAgICByZXR1cm4gaGFuZGxlTm9kZVZhbHVlKHZhbHVlLCBmdWxsUGF0aCwgZmxhdE1hcCwgaGVhZGVycyk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGhhbmRsZUxlYWZWYWx1ZSh2YWx1ZSwgZnVsbFBhdGgsIGtleSwgZmxhdE1hcCwgaGVhZGVycyk7XG4gIH07XG5cbiAgLyoqXG4gICAqIFJlY3Vyc2l2ZWx5IGV4dHJhY3QgYSBmbGF0IG1hcCBhbmQgaGVhZGVycyBmcm9tIHRoZSBoaWVyYXJjaWFsIHRyZWUuXG4gICAqL1xuICBjb25zdCBleHRyYWN0RmxhdE1hcEFuZEhlYWRlcnMgPSAoXG4gICAgdHJlZTogYW55LFxuICAgIHByZWZpeCA9ICcnXG4gICk6IFtSZWNvcmQ8c3RyaW5nLCBhbnk+LCBTZXQ8c3RyaW5nPl0gPT4ge1xuICAgIGNvbnN0IGhlYWRlcnM6IFNldDxzdHJpbmc+ID0gbmV3IFNldCgpO1xuICAgIGNvbnN0IGZsYXRNYXA6IFJlY29yZDxzdHJpbmcsIGFueT4gPSBbXTtcblxuICAgIGZvciAoY29uc3Qga2V5IGluIHRyZWUpIHtcbiAgICAgIGlmIChrZXkgaW4gdHJlZSkge1xuICAgICAgICBoYW5kbGVLZXkodHJlZVtrZXldLCBrZXksIHByZWZpeCwgZmxhdE1hcCwgaGVhZGVycyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIFtmbGF0TWFwLCBoZWFkZXJzXTtcbiAgfTtcblxuICAvKipcbiAgICogZXh0cmFjdCB0aGUgaWQgb2YgdGhlIGtleSwgdGhhdCBpcyByZW1vdmluZyB0aGUgbGFzdCB0b2tlbiAod2hpY2ggaXMgdGhlIGluZGV4KS5cbiAgICogaW4gdGhpcyBtYW5uZXIsIG11bHRpcGxlIGtleXMgdGhhdCBpZGVudGljYWwgYmVzaWRlcyB0aGVpciBpbmRleCBzaGFyZSB0aGUgc2FtZSBpZC5cbiAgICovXG4gIGNvbnN0IGV4dHJhY3RJZEhlbHBlciA9IChrZXk6IHN0cmluZyk6IHN0cmluZyA9PiB7XG4gICAgY29uc3QgcGFydHMgPSBrZXkuc3BsaXQoJy4nKTtcbiAgICBwYXJ0cy5wb3AoKTtcblxuICAgIHJldHVybiBwYXJ0cy5qb2luKCcuJyk7XG4gIH07XG5cbiAgLyoqXG4gICAqIGdlbmVyYXRlIGEgQ1NWIGZvcm1hdHRlZCBzdHJpbmcgYmFzZWQgb24gYSBmbGF0IGtleS0+dmFsdWUgbWFwLCBoZWFkZXJzIGFuZCBpZHNcbiAgICovXG4gIGNvbnN0IGdldENzdlN0cmluZyA9IChcbiAgICBtYXA6IHtba2V5OiBzdHJpbmddOiBhbnl9LFxuICAgIGhlYWRlcnM6IFNldDxzdHJpbmc+LFxuICAgIGlkczogU2V0PHN0cmluZz5cbiAgKTogc3RyaW5nID0+IHtcbiAgICBjb25zdCBjc3ZSb3dzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGNzdlJvd3MucHVzaChbJ2lkJywgLi4uaGVhZGVyc10uam9pbignLCcpKTtcblxuICAgIGlkcy5mb3JFYWNoKGlkID0+IHtcbiAgICAgIGNvbnN0IHJvd0RhdGEgPSBbaWRdO1xuXG4gICAgICBoZWFkZXJzLmZvckVhY2goaGVhZGVyID0+IHtcbiAgICAgICAgY29uc3QgdmFsdWUgPSBtYXBbYCR7aWR9LiR7aGVhZGVyfWBdID8/ICcnO1xuICAgICAgICByb3dEYXRhLnB1c2godmFsdWUudG9TdHJpbmcoKSk7XG4gICAgICB9KTtcbiAgICAgIGNzdlJvd3MucHVzaChyb3dEYXRhLmpvaW4oJywnKSk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gY3N2Um93cy5qb2luKCdcXG4nKTtcbiAgfTtcblxuICAvKipcbiAgICogd3JpdGUgdGhlIGdpdmVuIHN0cmluZyBjb250ZW50IHRvIGEgZmlsZSBhdCB0aGUgcHJvdmlkZWQgcGF0aFxuICAgKi9cbiAgY29uc3Qgd3JpdGVPdXRwdXRGaWxlID0gYXN5bmMgKGNvbnRlbnQ6IHN0cmluZywgb3V0cHV0UGF0aDogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGZzLndyaXRlRmlsZShgJHtvdXRwdXRQYXRofS5jc3ZgLCBjb250ZW50KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhyb3cgbmV3IFdyaXRlRmlsZUVycm9yKFxuICAgICAgICBgRmFpbGVkIHRvIHdyaXRlIENTViB0byAke291dHB1dFBhdGh9OiAke2Vycm9yfWBcbiAgICAgICk7XG4gICAgfVxuICB9O1xuXG4gIC8qKlxuICAgKiBleHBvcnQgdGhlIHByb3ZpZGVkIHRyZWUgY29udGVudCB0byBhIENTViBmaWxlLCByZXByZXNlbnRlZCBpbiBhIGZsYXQgc3RydWN0dXJlXG4gICAqL1xuICBjb25zdCBleGVjdXRlID0gYXN5bmMgKHRyZWU6IGFueSwgX2NvbnRleHQ6IENvbnRleHQsIG91dHB1dFBhdGg6IHN0cmluZykgPT4ge1xuICAgIGlmICghb3V0cHV0UGF0aCkge1xuICAgICAgdGhyb3cgbmV3IENsaUlucHV0RXJyb3IoJ091dHB1dCBwYXRoIGlzIHJlcXVpcmVkLicpO1xuICAgIH1cblxuICAgIGNvbnN0IFtleHRyYWN0cmVkRmxhdE1hcCwgZXh0cmFjdGVkSGVhZGVyc10gPVxuICAgICAgZXh0cmFjdEZsYXRNYXBBbmRIZWFkZXJzKHRyZWUpO1xuICAgIGNvbnN0IGlkcyA9IG5ldyBTZXQoXG4gICAgICBPYmplY3Qua2V5cyhleHRyYWN0cmVkRmxhdE1hcCkubWFwKGtleSA9PiBleHRyYWN0SWRIZWxwZXIoa2V5KSlcbiAgICApO1xuICAgIGNvbnN0IGNzdlN0cmluZyA9IGdldENzdlN0cmluZyhleHRyYWN0cmVkRmxhdE1hcCwgZXh0cmFjdGVkSGVhZGVycywgaWRzKTtcblxuICAgIHdyaXRlT3V0cHV0RmlsZShjc3ZTdHJpbmcsIG91dHB1dFBhdGgpO1xuICB9O1xuXG4gIHJldHVybiB7ZXhlY3V0ZX07XG59O1xuIl19
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Context } from '../types/manifest';
|
|
2
|
+
/**
|
|
3
|
+
* Extension to IF that outputs the tree in a CSV format.
|
|
4
|
+
*/
|
|
5
|
+
export declare const ExportCSV: () => {
|
|
6
|
+
execute: (tree: any, context: Context, outputPath: string) => Promise<void>;
|
|
7
|
+
metadata: {
|
|
8
|
+
kind: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExportCSV = void 0;
|
|
4
|
+
const promises_1 = require("fs/promises");
|
|
5
|
+
const sync_1 = require("csv-stringify/sync");
|
|
6
|
+
const errors_1 = require("../util/errors");
|
|
7
|
+
const { CliInputError } = errors_1.ERRORS;
|
|
8
|
+
/**
|
|
9
|
+
* Extension to IF that outputs the tree in a CSV format.
|
|
10
|
+
*/
|
|
11
|
+
const ExportCSV = () => {
|
|
12
|
+
const parseOutputAndField = (outputPath) => {
|
|
13
|
+
if (!outputPath) {
|
|
14
|
+
throw new CliInputError('Output path is required.');
|
|
15
|
+
}
|
|
16
|
+
const paths = outputPath.split('#');
|
|
17
|
+
const output = paths.slice(0, paths.length - 1).join('');
|
|
18
|
+
const criteria = paths[paths.length - 1];
|
|
19
|
+
if (paths.length <= 1 || !criteria) {
|
|
20
|
+
throw new CliInputError('CSV export criteria is not found in output path. Please append it after --output <path>#.');
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
output,
|
|
24
|
+
criteria,
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Grabs output and criteria from cli args, then call tree walker to collect csv data.
|
|
29
|
+
*/
|
|
30
|
+
const execute = async (tree, context, outputPath) => {
|
|
31
|
+
const columns = ['Path'];
|
|
32
|
+
const matrix = [columns];
|
|
33
|
+
const { output, criteria } = parseOutputAndField(outputPath);
|
|
34
|
+
const aggregationIsEnabled = !!context.aggregation;
|
|
35
|
+
/**
|
|
36
|
+
* Walks through all tree branches and leaves, collecting the data
|
|
37
|
+
*/
|
|
38
|
+
const treeWalker = (node, criteria, path = 'tree') => {
|
|
39
|
+
/** Hmm aggregated, then checks if it's the first one. If so adds column, pushes the value. */
|
|
40
|
+
if (node.aggregated) {
|
|
41
|
+
if (path === 'tree') {
|
|
42
|
+
columns.push('Aggregated');
|
|
43
|
+
}
|
|
44
|
+
matrix.push([`${path}.${criteria}`, node.aggregated[criteria]]);
|
|
45
|
+
}
|
|
46
|
+
/** So it has outputs, whats then? checks if timestamp is not there, adds one. Then appends values to it. */
|
|
47
|
+
if (node.outputs) {
|
|
48
|
+
node.outputs.forEach((output) => {
|
|
49
|
+
const { timestamp } = output;
|
|
50
|
+
if (!columns.includes(timestamp)) {
|
|
51
|
+
columns.push(output.timestamp);
|
|
52
|
+
}
|
|
53
|
+
const lastRow = matrix[matrix.length - 1];
|
|
54
|
+
if (aggregationIsEnabled) {
|
|
55
|
+
lastRow.push(output[criteria]);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
/** Handle without aggregation export strategy. */
|
|
59
|
+
if (matrix.length === 1 || lastRow.length === columns.length) {
|
|
60
|
+
matrix.push([`${path}.${criteria}`, output[criteria]]);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
lastRow.push(output[criteria]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** Ohh children? then call every one and execute. */
|
|
69
|
+
if (node.children) {
|
|
70
|
+
for (const child in node.children) {
|
|
71
|
+
treeWalker(node.children[child], criteria, `${path}.children.${child}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
treeWalker(tree, criteria);
|
|
76
|
+
await (0, promises_1.writeFile)(`${output}.csv`, (0, sync_1.stringify)(matrix, { columns }));
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
execute,
|
|
80
|
+
metadata: {
|
|
81
|
+
kind: 'exhaust',
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
exports.ExportCSV = ExportCSV;
|
|
86
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhwb3J0LWNzdi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9idWlsdGlucy9leHBvcnQtY3N2LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDBDQUFzQztBQUN0Qyw2Q0FBNkM7QUFFN0MsMkNBQXNDO0FBS3RDLE1BQU0sRUFBQyxhQUFhLEVBQUMsR0FBRyxlQUFNLENBQUM7QUFFL0I7O0dBRUc7QUFDSSxNQUFNLFNBQVMsR0FBRyxHQUFHLEVBQUU7SUFDNUIsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLFVBQWtCLEVBQUUsRUFBRTtRQUNqRCxJQUFJLENBQUMsVUFBVSxFQUFFO1lBQ2YsTUFBTSxJQUFJLGFBQWEsQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO1NBQ3JEO1FBRUQsTUFBTSxLQUFLLEdBQUcsVUFBVSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNwQyxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN6RCxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQztRQUV6QyxJQUFJLEtBQUssQ0FBQyxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFO1lBQ2xDLE1BQU0sSUFBSSxhQUFhLENBQ3JCLDJGQUEyRixDQUM1RixDQUFDO1NBQ0g7UUFFRCxPQUFPO1lBQ0wsTUFBTTtZQUNOLFFBQVE7U0FDVCxDQUFDO0lBQ0osQ0FBQyxDQUFDO0lBRUY7O09BRUc7SUFDSCxNQUFNLE9BQU8sR0FBRyxLQUFLLEVBQUUsSUFBUyxFQUFFLE9BQWdCLEVBQUUsVUFBa0IsRUFBRSxFQUFFO1FBQ3hFLE1BQU0sT0FBTyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDekIsTUFBTSxNQUFNLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUN6QixNQUFNLEVBQUMsTUFBTSxFQUFFLFFBQVEsRUFBQyxHQUFHLG1CQUFtQixDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQzNELE1BQU0sb0JBQW9CLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUM7UUFFbkQ7O1dBRUc7UUFDSCxNQUFNLFVBQVUsR0FBRyxDQUFDLElBQVMsRUFBRSxRQUFnQixFQUFFLElBQUksR0FBRyxNQUFNLEVBQUUsRUFBRTtZQUNoRSw4RkFBOEY7WUFDOUYsSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFO2dCQUNuQixJQUFJLElBQUksS0FBSyxNQUFNLEVBQUU7b0JBQ25CLE9BQU8sQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7aUJBQzVCO2dCQUVELE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksSUFBSSxRQUFRLEVBQUUsRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQzthQUNqRTtZQUVELDRHQUE0RztZQUM1RyxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUU7Z0JBQ2hCLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBb0IsRUFBRSxFQUFFO29CQUM1QyxNQUFNLEVBQUMsU0FBUyxFQUFDLEdBQUcsTUFBTSxDQUFDO29CQUUzQixJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsRUFBRTt3QkFDaEMsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7cUJBQ2hDO29CQUVELE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDO29CQUUxQyxJQUFJLG9CQUFvQixFQUFFO3dCQUN4QixPQUFPLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO3FCQUNoQzt5QkFBTTt3QkFDTCxrREFBa0Q7d0JBQ2xELElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxPQUFPLENBQUMsTUFBTSxFQUFFOzRCQUM1RCxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLElBQUksUUFBUSxFQUFFLEVBQUUsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQzt5QkFDeEQ7NkJBQU07NEJBQ0wsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQzt5QkFDaEM7cUJBQ0Y7Z0JBQ0gsQ0FBQyxDQUFDLENBQUM7YUFDSjtZQUVELHFEQUFxRDtZQUNyRCxJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUU7Z0JBQ2pCLEtBQUssTUFBTSxLQUFLLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRTtvQkFDakMsVUFBVSxDQUNSLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEVBQ3BCLFFBQVEsRUFDUixHQUFHLElBQUksYUFBYSxLQUFLLEVBQUUsQ0FDNUIsQ0FBQztpQkFDSDthQUNGO1FBQ0gsQ0FBQyxDQUFDO1FBRUYsVUFBVSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsQ0FBQztRQUUzQixNQUFNLElBQUEsb0JBQVMsRUFBQyxHQUFHLE1BQU0sTUFBTSxFQUFFLElBQUEsZ0JBQVMsRUFBQyxNQUFNLEVBQUUsRUFBQyxPQUFPLEVBQUMsQ0FBQyxDQUFDLENBQUM7SUFDakUsQ0FBQyxDQUFDO0lBRUYsT0FBTztRQUNMLE9BQU87UUFDUCxRQUFRLEVBQUU7WUFDUixJQUFJLEVBQUUsU0FBUztTQUNoQjtLQUNGLENBQUM7QUFDSixDQUFDLENBQUM7QUEzRlcsUUFBQSxTQUFTLGFBMkZwQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7d3JpdGVGaWxlfSBmcm9tICdmcy9wcm9taXNlcyc7XG5pbXBvcnQge3N0cmluZ2lmeX0gZnJvbSAnY3N2LXN0cmluZ2lmeS9zeW5jJztcblxuaW1wb3J0IHtFUlJPUlN9IGZyb20gJy4uL3V0aWwvZXJyb3JzJztcblxuaW1wb3J0IHtDb250ZXh0fSBmcm9tICcuLi90eXBlcy9tYW5pZmVzdCc7XG5pbXBvcnQge1BsdWdpblBhcmFtc30gZnJvbSAnLi4vdHlwZXMvaW50ZXJmYWNlJztcblxuY29uc3Qge0NsaUlucHV0RXJyb3J9ID0gRVJST1JTO1xuXG4vKipcbiAqIEV4dGVuc2lvbiB0byBJRiB0aGF0IG91dHB1dHMgdGhlIHRyZWUgaW4gYSBDU1YgZm9ybWF0LlxuICovXG5leHBvcnQgY29uc3QgRXhwb3J0Q1NWID0gKCkgPT4ge1xuICBjb25zdCBwYXJzZU91dHB1dEFuZEZpZWxkID0gKG91dHB1dFBhdGg6IHN0cmluZykgPT4ge1xuICAgIGlmICghb3V0cHV0UGF0aCkge1xuICAgICAgdGhyb3cgbmV3IENsaUlucHV0RXJyb3IoJ091dHB1dCBwYXRoIGlzIHJlcXVpcmVkLicpO1xuICAgIH1cblxuICAgIGNvbnN0IHBhdGhzID0gb3V0cHV0UGF0aC5zcGxpdCgnIycpO1xuICAgIGNvbnN0IG91dHB1dCA9IHBhdGhzLnNsaWNlKDAsIHBhdGhzLmxlbmd0aCAtIDEpLmpvaW4oJycpO1xuICAgIGNvbnN0IGNyaXRlcmlhID0gcGF0aHNbcGF0aHMubGVuZ3RoIC0gMV07XG5cbiAgICBpZiAocGF0aHMubGVuZ3RoIDw9IDEgfHwgIWNyaXRlcmlhKSB7XG4gICAgICB0aHJvdyBuZXcgQ2xpSW5wdXRFcnJvcihcbiAgICAgICAgJ0NTViBleHBvcnQgY3JpdGVyaWEgaXMgbm90IGZvdW5kIGluIG91dHB1dCBwYXRoLiBQbGVhc2UgYXBwZW5kIGl0IGFmdGVyIC0tb3V0cHV0IDxwYXRoPiMuJ1xuICAgICAgKTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgb3V0cHV0LFxuICAgICAgY3JpdGVyaWEsXG4gICAgfTtcbiAgfTtcblxuICAvKipcbiAgICogR3JhYnMgb3V0cHV0IGFuZCBjcml0ZXJpYSBmcm9tIGNsaSBhcmdzLCB0aGVuIGNhbGwgdHJlZSB3YWxrZXIgdG8gY29sbGVjdCBjc3YgZGF0YS5cbiAgICovXG4gIGNvbnN0IGV4ZWN1dGUgPSBhc3luYyAodHJlZTogYW55LCBjb250ZXh0OiBDb250ZXh0LCBvdXRwdXRQYXRoOiBzdHJpbmcpID0+IHtcbiAgICBjb25zdCBjb2x1bW5zID0gWydQYXRoJ107XG4gICAgY29uc3QgbWF0cml4ID0gW2NvbHVtbnNdO1xuICAgIGNvbnN0IHtvdXRwdXQsIGNyaXRlcmlhfSA9IHBhcnNlT3V0cHV0QW5kRmllbGQob3V0cHV0UGF0aCk7XG4gICAgY29uc3QgYWdncmVnYXRpb25Jc0VuYWJsZWQgPSAhIWNvbnRleHQuYWdncmVnYXRpb247XG5cbiAgICAvKipcbiAgICAgKiBXYWxrcyB0aHJvdWdoIGFsbCB0cmVlIGJyYW5jaGVzIGFuZCBsZWF2ZXMsIGNvbGxlY3RpbmcgdGhlIGRhdGFcbiAgICAgKi9cbiAgICBjb25zdCB0cmVlV2Fsa2VyID0gKG5vZGU6IGFueSwgY3JpdGVyaWE6IHN0cmluZywgcGF0aCA9ICd0cmVlJykgPT4ge1xuICAgICAgLyoqIEhtbSBhZ2dyZWdhdGVkLCB0aGVuIGNoZWNrcyBpZiBpdCdzIHRoZSBmaXJzdCBvbmUuIElmIHNvIGFkZHMgY29sdW1uLCBwdXNoZXMgdGhlIHZhbHVlLiAqL1xuICAgICAgaWYgKG5vZGUuYWdncmVnYXRlZCkge1xuICAgICAgICBpZiAocGF0aCA9PT0gJ3RyZWUnKSB7XG4gICAgICAgICAgY29sdW1ucy5wdXNoKCdBZ2dyZWdhdGVkJyk7XG4gICAgICAgIH1cblxuICAgICAgICBtYXRyaXgucHVzaChbYCR7cGF0aH0uJHtjcml0ZXJpYX1gLCBub2RlLmFnZ3JlZ2F0ZWRbY3JpdGVyaWFdXSk7XG4gICAgICB9XG5cbiAgICAgIC8qKiBTbyBpdCBoYXMgb3V0cHV0cywgd2hhdHMgdGhlbj8gY2hlY2tzIGlmIHRpbWVzdGFtcCBpcyBub3QgdGhlcmUsIGFkZHMgb25lLiBUaGVuIGFwcGVuZHMgdmFsdWVzIHRvIGl0LiAqL1xuICAgICAgaWYgKG5vZGUub3V0cHV0cykge1xuICAgICAgICBub2RlLm91dHB1dHMuZm9yRWFjaCgob3V0cHV0OiBQbHVnaW5QYXJhbXMpID0+IHtcbiAgICAgICAgICBjb25zdCB7dGltZXN0YW1wfSA9IG91dHB1dDtcblxuICAgICAgICAgIGlmICghY29sdW1ucy5pbmNsdWRlcyh0aW1lc3RhbXApKSB7XG4gICAgICAgICAgICBjb2x1bW5zLnB1c2gob3V0cHV0LnRpbWVzdGFtcCk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3QgbGFzdFJvdyA9IG1hdHJpeFttYXRyaXgubGVuZ3RoIC0gMV07XG5cbiAgICAgICAgICBpZiAoYWdncmVnYXRpb25Jc0VuYWJsZWQpIHtcbiAgICAgICAgICAgIGxhc3RSb3cucHVzaChvdXRwdXRbY3JpdGVyaWFdKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLyoqIEhhbmRsZSB3aXRob3V0IGFnZ3JlZ2F0aW9uIGV4cG9ydCBzdHJhdGVneS4gKi9cbiAgICAgICAgICAgIGlmIChtYXRyaXgubGVuZ3RoID09PSAxIHx8IGxhc3RSb3cubGVuZ3RoID09PSBjb2x1bW5zLmxlbmd0aCkge1xuICAgICAgICAgICAgICBtYXRyaXgucHVzaChbYCR7cGF0aH0uJHtjcml0ZXJpYX1gLCBvdXRwdXRbY3JpdGVyaWFdXSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBsYXN0Um93LnB1c2gob3V0cHV0W2NyaXRlcmlhXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgLyoqIE9oaCBjaGlsZHJlbj8gdGhlbiBjYWxsIGV2ZXJ5IG9uZSBhbmQgZXhlY3V0ZS4gKi9cbiAgICAgIGlmIChub2RlLmNoaWxkcmVuKSB7XG4gICAgICAgIGZvciAoY29uc3QgY2hpbGQgaW4gbm9kZS5jaGlsZHJlbikge1xuICAgICAgICAgIHRyZWVXYWxrZXIoXG4gICAgICAgICAgICBub2RlLmNoaWxkcmVuW2NoaWxkXSxcbiAgICAgICAgICAgIGNyaXRlcmlhLFxuICAgICAgICAgICAgYCR7cGF0aH0uY2hpbGRyZW4uJHtjaGlsZH1gXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH07XG5cbiAgICB0cmVlV2Fsa2VyKHRyZWUsIGNyaXRlcmlhKTtcblxuICAgIGF3YWl0IHdyaXRlRmlsZShgJHtvdXRwdXR9LmNzdmAsIHN0cmluZ2lmeShtYXRyaXgsIHtjb2x1bW5zfSkpO1xuICB9O1xuXG4gIHJldHVybiB7XG4gICAgZXhlY3V0ZSxcbiAgICBtZXRhZGF0YToge1xuICAgICAga2luZDogJ2V4aGF1c3QnLFxuICAgIH0sXG4gIH07XG59O1xuIl19
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExportLog = void 0;
|
|
4
|
+
const ExportLog = () => {
|
|
5
|
+
/**
|
|
6
|
+
* Logs output manifest in console.
|
|
7
|
+
*/
|
|
8
|
+
const execute = async (tree, context) => {
|
|
9
|
+
const outputFile = {
|
|
10
|
+
...context,
|
|
11
|
+
tree,
|
|
12
|
+
};
|
|
13
|
+
console.log(JSON.stringify(outputFile, null, 2));
|
|
14
|
+
};
|
|
15
|
+
return { execute };
|
|
16
|
+
};
|
|
17
|
+
exports.ExportLog = ExportLog;
|
|
18
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhwb3J0LWxvZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9idWlsdGlucy9leHBvcnQtbG9nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVPLE1BQU0sU0FBUyxHQUFHLEdBQUcsRUFBRTtJQUM1Qjs7T0FFRztJQUNILE1BQU0sT0FBTyxHQUFHLEtBQUssRUFBRSxJQUFTLEVBQUUsT0FBZ0IsRUFBRSxFQUFFO1FBQ3BELE1BQU0sVUFBVSxHQUFHO1lBQ2pCLEdBQUcsT0FBTztZQUNWLElBQUk7U0FDTCxDQUFDO1FBRUYsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLFVBQVUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNuRCxDQUFDLENBQUM7SUFFRixPQUFPLEVBQUMsT0FBTyxFQUFDLENBQUM7QUFDbkIsQ0FBQyxDQUFDO0FBZFcsUUFBQSxTQUFTLGFBY3BCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtDb250ZXh0fSBmcm9tICcuLi90eXBlcy9tYW5pZmVzdCc7XG5cbmV4cG9ydCBjb25zdCBFeHBvcnRMb2cgPSAoKSA9PiB7XG4gIC8qKlxuICAgKiBMb2dzIG91dHB1dCBtYW5pZmVzdCBpbiBjb25zb2xlLlxuICAgKi9cbiAgY29uc3QgZXhlY3V0ZSA9IGFzeW5jICh0cmVlOiBhbnksIGNvbnRleHQ6IENvbnRleHQpID0+IHtcbiAgICBjb25zdCBvdXRwdXRGaWxlID0ge1xuICAgICAgLi4uY29udGV4dCxcbiAgICAgIHRyZWUsXG4gICAgfTtcblxuICAgIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KG91dHB1dEZpbGUsIG51bGwsIDIpKTtcbiAgfTtcblxuICByZXR1cm4ge2V4ZWN1dGV9O1xufTtcbiJdfQ==
|