@platforma-sdk/workflow-tengo 1.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +6 -0
  2. package/dist/tengo/lib/assets.lib.tengo +64 -0
  3. package/dist/tengo/lib/constants.lib.tengo +75 -0
  4. package/dist/tengo/lib/exec.internal.lib.tengo +211 -0
  5. package/dist/tengo/lib/exec.lib.tengo +516 -0
  6. package/dist/tengo/lib/exec.package.lib.tengo +296 -0
  7. package/dist/tengo/lib/exec.runcmd.lib.tengo +275 -0
  8. package/dist/tengo/lib/exec.runenv.lib.tengo +221 -0
  9. package/dist/tengo/lib/exec.stream.lib.tengo +64 -0
  10. package/dist/tengo/lib/file.lib.tengo +112 -0
  11. package/dist/tengo/lib/ll.lib.tengo +233 -0
  12. package/dist/tengo/lib/maps.lib.tengo +102 -0
  13. package/dist/tengo/lib/objects.lib.tengo +22 -0
  14. package/dist/tengo/lib/oop.lib.tengo +29 -0
  15. package/dist/tengo/lib/path.lib.tengo +165 -0
  16. package/dist/tengo/lib/pframes.constants.lib.tengo +53 -0
  17. package/dist/tengo/lib/pframes.export-pcolumn.lib.tengo +47 -0
  18. package/dist/tengo/lib/pframes.lib.tengo +84 -0
  19. package/dist/tengo/lib/pframes.ll.lib.tengo +86 -0
  20. package/dist/tengo/lib/pframes.util.lib.tengo +244 -0
  21. package/dist/tengo/lib/pframes.xsv.lib.tengo +178 -0
  22. package/dist/tengo/lib/render.lib.tengo +100 -0
  23. package/dist/tengo/lib/sets.lib.tengo +93 -0
  24. package/dist/tengo/lib/slices.lib.tengo +202 -0
  25. package/dist/tengo/lib/smart.lib.tengo +1005 -0
  26. package/dist/tengo/lib/tpl.lib.tengo +535 -0
  27. package/dist/tengo/lib/tpl.state-await.lib.tengo +255 -0
  28. package/dist/tengo/lib/tpl.util.lib.tengo +19 -0
  29. package/dist/tengo/lib/validation.lib.tengo +428 -0
  30. package/dist/tengo/lib/workdir.lib.tengo +300 -0
  31. package/dist/tengo/lib/workdir.proc.lib.tengo +158 -0
  32. package/dist/tengo/lib/workdir.save.lib.tengo +158 -0
  33. package/dist/tengo/lib/workflow.bobject.lib.tengo +51 -0
  34. package/dist/tengo/lib/workflow.bquery.lib.tengo +208 -0
  35. package/dist/tengo/lib/workflow.constants.lib.tengo +8 -0
  36. package/dist/tengo/lib/workflow.context.lib.tengo +56 -0
  37. package/dist/tengo/lib/workflow.lib.tengo +298 -0
  38. package/dist/tengo/tpl/exec.create-python-venv.plj.gz +0 -0
  39. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  40. package/dist/tengo/tpl/pframes.aggregate.plj.gz +0 -0
  41. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  42. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  43. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  44. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  45. package/dist/tengo/tpl/pframes.import-xsv-map.plj.gz +0 -0
  46. package/dist/tengo/tpl/pframes.map-pframe.plj.gz +0 -0
  47. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  48. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  49. package/index.d.ts +4 -0
  50. package/index.js +13 -0
  51. package/package.json +25 -0
@@ -0,0 +1,244 @@
1
+ ll := import("@platforma-sdk/workflow-tengo:ll")
2
+ validation := import("@platforma-sdk/workflow-tengo:validation")
3
+ constants := import("@platforma-sdk/workflow-tengo:pframes.constants")
4
+ objects := import("@platforma-sdk/workflow-tengo:objects")
5
+
6
+ _PRE_PROCESS_STEP_SCHEMA := [`or`,
7
+ {
8
+ // Replace cell value with the first full RegEx match
9
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
10
+ `type`: `string,regex=regexpExtract`,
11
+ // String representing ECMAScript RegEx
12
+ // If there are no or more than one matches - result would become NA
13
+ `pattern`: `string`
14
+ },
15
+ {
16
+ // Perform JS String.prototype.replace() operation
17
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
18
+ `type`: `string,regex=regexpReplace`,
19
+ // String representing ECMAScript RegEx with at least one capturing group.
20
+ // If you need to reorder capturing groups - use RegExp matching the whole string
21
+ // (must start with string begin anchor ^, end with string end anchor $).
22
+ // Use regex playground https://regexr.com/ to test your ideas.
23
+ `pattern`: `string`,
24
+ // Replacement pattern used to construct result string from captured groups
25
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#specifying_a_string_as_the_replacement
26
+ // Empty string as result would become NA.
27
+ `replacement`: `string`
28
+ }]
29
+
30
+ _SPEC_AXES_SCHEMA := {
31
+ // Column label from XSV
32
+ `column`: `string`,
33
+ // Pre-processing operations to be performed just after reading XSV, before any other operations
34
+ `preProcess,omitempty`: [_PRE_PROCESS_STEP_SCHEMA],
35
+ // Should the N/A values (or empty rows) be allowed;
36
+ // default: false
37
+ `allowNA,omitempty`: `bool`,
38
+ // Specification of the axis
39
+ `spec`: {
40
+ // Type of the axis value
41
+ `type`: `string,regex=Int|Long|Float|Double|String|Bytes`,
42
+ // Name of the axis to be used in spec;
43
+ // default: column label
44
+ `name,omitempty`: `string`,
45
+ // Auxiliary information to the axis name, type and parents to form a unique identifier;
46
+ // default: empty
47
+ `domain,omitempty`: { any: `string` },
48
+ // Any additional information attached to the axis that does not affect its identifier;
49
+ // default: "pl7.app/label": Column label from XSV
50
+ `annotations,omitempty`: { any: `string` },
51
+ // A list of zero-based indices of parent axes in the overall axes specification;
52
+ // default: empty
53
+ `parentAxes,omitempty`: [`number`]
54
+ }
55
+ }
56
+
57
+ _SPEC_COLUMN_SCHEMA := {
58
+ // Column label from XSV
59
+ `column`: `string`,
60
+ // ID of the column to be used in spec and as a saved filename;
61
+ // default: column label with all special characters replaced with '_'
62
+ `id,omitempty`: `string`,
63
+ // Pre-processing operations to be performed just after reading XSV, before any other operations;
64
+ // warning: when pre-processing is used, spec valueType must be set
65
+ `preProcess,omitempty`: [_PRE_PROCESS_STEP_SCHEMA],
66
+ // Should the N/A values (or empty rows) be allowed;
67
+ // default: true
68
+ `allowNA,omitempty`: `bool`,
69
+ // Specification of the column
70
+ `spec,omitempty`: {
71
+ // Type of column values;
72
+ // default: automatically detected to be one of Long, Double, String
73
+ `type,omitempty`: `string,regex=Int|Long|Float|Double|String|Bytes`,
74
+ // Name of the column to be used in spec;
75
+ // default: column label
76
+ `name,omitempty`: `string`,
77
+ // Auxiliary information to the column name, type and parents to form a unique identifier;
78
+ // default: empty
79
+ `domain,omitempty`: { any: `string` },
80
+ // Any additional information attached to the axis that does not affect its identifier;
81
+ // default: "pl7.app/label": Column label from XSV
82
+ `annotations,omitempty`: { any: `string` },
83
+ // A list of zero-based indices of parent axes in the overall axes specification;
84
+ // default: empty
85
+ `parentAxes,omitempty`: [ `number` ]
86
+ }
87
+ }
88
+
89
+ _SPEC_INDEX_SCHEMA := {
90
+ // Name of the axis
91
+ `name`: `string`,
92
+ // Auxiliary information to the column name, type and parents to form a unique identifier;
93
+ // default: empty
94
+ `domain,omitempty`: { any: `string` },
95
+ // Any additional information attached to the axis that does not affect its identifier;
96
+ // default: "pl7.app/label": name
97
+ `annotations,omitempty`: { any: `string` },
98
+ // A list of zero-based indices of parent axes in the overall axes specification;
99
+ // default: empty
100
+ `parentAxes,omitempty`: [`number`]
101
+ }
102
+
103
+ PFCONV_CFG_SCHEMA := {
104
+ // Columns would be stored using specified format;
105
+ // default: Binary
106
+ `storageFormat,omitempty` : `string,regex=Binary|Json`,
107
+ // Partitioning key length;
108
+ // default: 0
109
+ `partitionKeyLength,omitempty` : `number`,
110
+ // XSV columns to use as PColumn axes, order of axes in resulting columns
111
+ // would match the order of spec entries provided here
112
+ `axes`: [_SPEC_AXES_SCHEMA],
113
+ // XSV columns to use as PColumn values, each exported individually
114
+ `columns`: [_SPEC_COLUMN_SCHEMA],
115
+ // Axis of type Int with XSV row numbers (would be the last one);
116
+ // default: do not create additional axis with indexes
117
+ `index,omitempty`: _SPEC_INDEX_SCHEMA,
118
+ // String representing NA values of types Int, Long, Float, Double
119
+ // default: NaN
120
+ `naStr,omitempty`: `string`,
121
+ // String representing NA values of types String, Bytes
122
+ // default: null
123
+ `nullStr,omitempty`: `string`,
124
+ // Single ASCII character to be used as separator;
125
+ // default: ',' for .csv, '\t' for .tsv
126
+ `separator,omitempty`: `string`,
127
+ // Interpret empty string as NA value
128
+ // default: true
129
+ `emptyStrAsNA,omitempty`: `bool`,
130
+ // Prefix all column names with given string;
131
+ // default: names would be preserved
132
+ `columnNamePrefix,omitempty`: `string`,
133
+ // Resolve duplicates by adding sequential suffixes to column labels;
134
+ // default: true
135
+ `allowColumnLabelDuplicates,omitempty`: `bool`,
136
+ // When columns spec is provided but no such column was found in XSV - create such column
137
+ // filled with NA values instead of failing;
138
+ // default: false
139
+ `allowArtificialColumns,omitempty`: `bool`
140
+ }
141
+
142
+
143
+ /**
144
+ * Get column id from the column spec
145
+ */
146
+ xsvColumnId := func(c) {
147
+ id := c.id
148
+ if is_undefined(id) {
149
+ id = c.column
150
+ }
151
+ return id
152
+ }
153
+
154
+ /**
155
+ * Removes all annotations & domain information from spec, to pass it to pfconv
156
+ */
157
+ purifySpec := func(spec) {
158
+ newSpec := copy(spec)
159
+
160
+ newAxes := []
161
+ for ax in spec.axes {
162
+ newAxes = append(newAxes, {
163
+ column: ax.column,
164
+ spec: { type: ax.spec.type }
165
+ })
166
+ }
167
+ newSpec.axes = newAxes
168
+
169
+ newCols := []
170
+ for col in spec.columns {
171
+ newCols = append(newCols, objects.deleteUndefined({
172
+ column: col.column,
173
+ id: col.id,
174
+ allowNA: col.allowNA,
175
+ spec: { valueType: col.spec.valueType }
176
+ }))
177
+ }
178
+ newSpec.columns = newCols
179
+
180
+ validation.assertJsonSchema(newSpec, PFCONV_CFG_SCHEMA)
181
+
182
+ return newSpec
183
+ }
184
+
185
+ /**
186
+ * Split given pfconv config into:
187
+ *
188
+ * (1) purified cfg with minimal required settings to be passed to the pfconv binary,
189
+ * to maximize caching retention when domains and annotations changes
190
+ * (2) complete columns specs, that can be directly rendered into BObjectSpecs and
191
+ * returned as workflow exports
192
+ *
193
+ * This method comes in handy in situations where actual csv -> pframe export is broadcasted
194
+ * onto multiple objects, and then converges, so that each export operation only requires purified
195
+ * config, and only data is saved for such operations, and actual specs are added somewhere
196
+ * upstream.
197
+ *
198
+ * @param cfg full pfconv config object
199
+ * @param ops additional options
200
+ * {
201
+ * additionalAxesSpec - array of additional axes spec to prepend to each column spec
202
+ * }
203
+ *
204
+ * @return {
205
+ * purifiedCfg - purified pfconv config
206
+ * columnsSpec - map of full column specs by ids from the original cfg
207
+ * }
208
+ */
209
+ decomposePfconvCfg := func(cfg, ...ops) {
210
+ ll.assert(len(ops) <= 1, "too many options for decomposePfconvCfg: %v", len(ops))
211
+
212
+ axesSpec := []
213
+ if len(ops) == 1 && !is_undefined(ops[0].additionalAxesSpec) {
214
+ axesSpec = ops[0].additionalAxesSpec
215
+ }
216
+
217
+ for axis in cfg.axes {
218
+ axesSpec = append(axesSpec, axis.spec)
219
+ }
220
+
221
+ columnsSpec := {}
222
+
223
+ for column in cfg.columns {
224
+ id := xsvColumnId(column)
225
+ ll.assert(is_undefined(columnsSpec[id]), "repeated column spec id in pfconv cfg: %v", id)
226
+ columnsSpec[id] = column.spec
227
+ columnsSpec[id].axesSpec = axesSpec
228
+ // make sure spec contain correct kind value
229
+ columnsSpec[id].kind = "PColumn"
230
+ }
231
+
232
+ return {
233
+ purifiedCfg: purifySpec(cfg),
234
+ columnsSpec: columnsSpec
235
+ }
236
+ }
237
+
238
+ export ll.toStrict({
239
+ PFCONV_CFG_SCHEMA: PFCONV_CFG_SCHEMA,
240
+
241
+ xsvColumnId: xsvColumnId,
242
+ purifySpec: purifySpec,
243
+ decomposePfconvCfg: decomposePfconvCfg
244
+ })
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Library providing xsv <> p-frames conversion utils.
3
+ */
4
+
5
+ validation := import("@platforma-sdk/workflow-tengo:validation")
6
+ constants := import("@platforma-sdk/workflow-tengo:pframes.constants")
7
+ objects := import("@platforma-sdk/workflow-tengo:objects")
8
+ render := import("@platforma-sdk/workflow-tengo:render")
9
+ exec := import("@platforma-sdk/workflow-tengo:exec")
10
+ json := import("json")
11
+ util := import("@platforma-sdk/workflow-tengo:pframes.util")
12
+ ll := import("@platforma-sdk/workflow-tengo:ll")
13
+ assets := import("@platforma-sdk/workflow-tengo:assets")
14
+
15
+ pfconvSw := assets.importSoftware("@milaboratories/software-pframes-conv:main")
16
+
17
+ /**
18
+ * Calculate axes spec from pfconv spec
19
+ */
20
+ getAxesSpec := func(spec) {
21
+ axes := []
22
+ for ax in spec.axes {
23
+ axes = append(axes, ax.spec)
24
+ }
25
+ return axes
26
+ }
27
+
28
+ renderOps := func(defaults, ...ops) {
29
+ o := { }
30
+
31
+ for k, v in defaults {
32
+ o[k] = v
33
+ }
34
+
35
+ if len(ops) > 0 {
36
+ for k, v in ops[0] {
37
+ o[k] = v
38
+ }
39
+ }
40
+ return o
41
+ }
42
+
43
+ getColumnSpec := func(axesSpec, col) {
44
+ spec := {
45
+ kind: constants.KIND_P_COLUMN,
46
+ axesSpec: axesSpec
47
+ }
48
+
49
+ for k, v in col.spec {
50
+ spec[k] = v
51
+ }
52
+
53
+ return objects.deleteUndefined(spec)
54
+ }
55
+
56
+ /**
57
+ * Imports xsv data into p-frame. The resulting map resource contains all columns specified in the params (column identifiers as
58
+ * provided by the spec used as map keys). Resulting p-columns will be always single-partitioned at most.
59
+ *
60
+ * @param xsvFile: reference - a reference to a file
61
+ * @param xsvType: string - either csv or tsv
62
+ * @param spec: object - xsv conversion specification
63
+ * @param ops: dict - additional params (internal)
64
+ * @return map: reference - a reference to a map resource storing imported data.
65
+ */
66
+ importFile := func(xsvFile, xsvType, spec, ...ops) {
67
+ ll.assert(xsvType == "csv" || xsvType == "tsv", "expected one of [tsv, csv] types")
68
+
69
+ validation.assertJsonSchema(spec, util.PFCONV_CFG_SCHEMA)
70
+
71
+ xsvFileName := "file." + xsvType
72
+
73
+ // import p-frame from directory tpl
74
+ importDirTpl := assets.importTemplate("@platforma-sdk/workflow-tengo:pframes.import-dir")
75
+
76
+ // spec without any metadata (for caching purposes)
77
+ pureSpec := util.purifySpec(spec)
78
+
79
+ // convert csv to p-frame and read resulting data
80
+ pfconv := exec.builder().
81
+ software(pfconvSw).
82
+ arg("importCsv").
83
+ arg(xsvFileName).
84
+ arg("-p").arg("spec.json").
85
+ arg("-o").arg("out").
86
+ addFile(xsvFileName, xsvFile).
87
+ writeFile("spec.json", json.encode(pureSpec)).
88
+ processWorkdir("pf", importDirTpl, pureSpec).
89
+ run()
90
+
91
+ ops := renderOps({ dataOnly: false }, ops...)
92
+
93
+ // p-columns data
94
+ pf := pfconv.getProcessorResult("pf")
95
+ if ops.dataOnly {
96
+ return pf
97
+ }
98
+
99
+ axesSpec := getAxesSpec(spec)
100
+
101
+ result := {}
102
+ for col in spec.columns {
103
+ id := util.xsvColumnId(col)
104
+
105
+ result[id + ".data"] = pf.getFutureInputField(id)
106
+ result[id + ".spec"] = getColumnSpec(axesSpec, col)
107
+ }
108
+
109
+ return result
110
+ }
111
+
112
+ /**
113
+ * Imports a map with xsv files into a p-frame. The resulting map resource contains all columns specified in the params (column identifiers as
114
+ * provided by the spec used as map keys). Resulting p-columns may be double-partitioned.
115
+ *
116
+ * @param xsvFile: reference - a reference to a file
117
+ * @param xsvType: string - either csv or tsv
118
+ * @param spec: object - xsv conversion specification
119
+ * @param ops: object - additional options
120
+ * {
121
+ * dataOnly: boolean - set to true to completely skip creation of specs
122
+ * splitDataAndSpec: boolean - if true, resulting map will have nested structures,
123
+ * with "spec" + "data" fields in nested maps
124
+ * additionalAxesSpec: AxisSpec[] - array of additional axes spec to prepend to
125
+ * each column spec
126
+ * }
127
+ * @return map: reference - a reference to a map resource storing imported data.
128
+ */
129
+ importFileMap := func(xsvMap, xsvType, spec, ...ops) {
130
+
131
+ importXsvMapTpl := assets.importTemplate("@platforma-sdk/workflow-tengo:pframes.import-xsv-map")
132
+
133
+ ops := renderOps({
134
+ dataOnly: false,
135
+ splitDataAndSpec: false,
136
+ additionalAxesSpec: []
137
+ }, ops...)
138
+
139
+ // spec without any metadata (for caching purposes)
140
+ decomposedSpec := util.decomposePfconvCfg(spec, { additionalAxesSpec: ops.additionalAxesSpec })
141
+ pureSpec := decomposedSpec.purifiedCfg
142
+ columnsSpec := decomposedSpec.columnsSpec
143
+
144
+ r := render.createEphemeral(importXsvMapTpl, {
145
+ xsvMap: xsvMap,
146
+ xsvType: xsvType,
147
+ spec: pureSpec,
148
+ ops: { dataOnly: true }
149
+ })
150
+
151
+ if ops.dataOnly && ops.splitDataAndSpec {
152
+ ll.panic("Can't use both options dataOnly and splitDataAndSpec at the same time.")
153
+ }
154
+
155
+ result := {}
156
+ axesSpec := getAxesSpec(spec)
157
+ for c in spec.columns {
158
+ id := util.xsvColumnId(c)
159
+ if ops.dataOnly {
160
+ result[id] = r.output(id)
161
+ } else if ops.splitDataAndSpec {
162
+ result[id] = {
163
+ data: r.output(id),
164
+ spec: columnsSpec[id]
165
+ }
166
+ } else {
167
+ result[id + ".data"] = r.output(id)
168
+ result[id + ".spec"] = columnsSpec[id]
169
+ }
170
+ }
171
+
172
+ return result
173
+ }
174
+
175
+ export ll.toStrict({
176
+ importFile: importFile,
177
+ importFileMap: importFileMap
178
+ })
@@ -0,0 +1,100 @@
1
+ /*
2
+ * Create renderer objects to render templates from the code
3
+ */
4
+
5
+ ll := import("@platforma-sdk/workflow-tengo:ll")
6
+ assets := import("@platforma-sdk/workflow-tengo:assets")
7
+ smart := import("@platforma-sdk/workflow-tengo:smart")
8
+ tplutil := import("@platforma-sdk/workflow-tengo:tpl.util")
9
+ constants := import("@platforma-sdk/workflow-tengo:constants")
10
+ oop := import("@platforma-sdk/workflow-tengo:oop")
11
+ validation := import("@platforma-sdk/workflow-tengo:validation")
12
+ tx := import("tx")
13
+
14
+
15
+ _SUBSCRIPTION_FIELD := "subscription"
16
+
17
+ /**
18
+ * Creates a render template resource.
19
+ *
20
+ * @param tpl: template resource (see assets.importTemplate())
21
+ * @param isEph: whether template is ephemeral
22
+ * @param inputs: a map of inputs to be passed to the template
23
+ * @return renderer: a smart resource with additional output("name") method
24
+ */
25
+ createUniversal := func(tpl, isEph, inputs) {
26
+
27
+ // build renderer resource
28
+ rbuilder := undefined
29
+ if isEph {
30
+ rbuilder = smart.ephemeralBuilder(constants.RTYPE_RENDER_EPH_TEMPLATE)
31
+ } else {
32
+ rbuilder = smart.structBuilder(constants.RTYPE_RENDER_TEMPLATE)
33
+ }
34
+
35
+ if isEph {
36
+ // create internal subscription field
37
+ rbuilder.createField(_SUBSCRIPTION_FIELD, "OTW")
38
+ }
39
+
40
+ // template input
41
+ rbuilder.createInputField(tplutil.TEMPLATE_FIELD).set(tpl)
42
+
43
+ // inputs input
44
+ inputsMap := isEph ? smart.ephemeralMapBuilder() : smart.mapBuilder()
45
+ for name, value in inputs {
46
+ inputsMap.add(name, value)
47
+ }
48
+
49
+ rbuilder.createInputField(tplutil.INPUTS_MAP_FIELD).set(inputsMap.build())
50
+ // build
51
+ renderer := rbuilder.lockInputsAndBuild()
52
+
53
+ self := undefined
54
+ self = ll.toStrict(oop.inherit(renderer, {
55
+
56
+ /**
57
+ * Returns an output field with a given name
58
+ */
59
+ output: func(name, ...cache) {
60
+ ll.assert(len(cache) == 0 || is_int(cache[0]), "cache should be a number (milliseconds)")
61
+ out := renderer.getFutureOutputField(tplutil.TEMPLATE_OUTPUT_PREFIX + name)
62
+ if len(cache) > 0 && cache[0] > 0 {
63
+ cache = cache[0]
64
+ tx.cacheSetToField(out.id, cache)
65
+ }
66
+
67
+ return out
68
+ }
69
+ }))
70
+
71
+ return self
72
+ }
73
+
74
+ /**
75
+ * Creates a render template resource.
76
+ *
77
+ * @param tpl: template resource (see assets.importTemplate())
78
+ * @param inputs: a map of inputs to be passed to the template
79
+ * @return renderer: a smart resource with additional output("name") method
80
+ */
81
+ create := func(tpl, inputs) {
82
+ return createUniversal(tpl, false, inputs)
83
+ }
84
+
85
+ /**
86
+ * Creates ephemeral render template resource.
87
+ *
88
+ * @param tpl: template resource (see assets.importTemplate())
89
+ * @param inputs: a map of inputs to be passed to the template
90
+ * @return renderer: a smart resource with additional output("name") method
91
+ */
92
+ createEphemeral := func(tpl, inputs) {
93
+ return createUniversal(tpl, true, inputs)
94
+ }
95
+
96
+ export ll.toStrict({
97
+ createUniversal: createUniversal,
98
+ create: create,
99
+ createEphemeral: createEphemeral
100
+ })
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Functions for working with sets of elements,
3
+ * in Tengo they are just maps with truthy values.
4
+ * Keys of the map can be only either strings or numbers.
5
+ */
6
+
7
+ ll := import("@platforma-sdk/workflow-tengo:ll")
8
+ maps := import("@platforma-sdk/workflow-tengo:maps")
9
+
10
+ /**
11
+ * Turns a slice to a set of values.
12
+ *
13
+ * @param slice: slice<string|number>
14
+ * @return result: set<string|number>
15
+ */
16
+ fromSlice := func(slice) {
17
+ set := {}
18
+ for _, elem in slice {
19
+ set[elem] = true
20
+ }
21
+ return set
22
+ }
23
+
24
+ /**
25
+ * Convert given set to slice
26
+ *
27
+ * @param set: set - a set of values to be converted to slice
28
+ * @return slice: []<string|number>
29
+ */
30
+ toSlice := func(set) {
31
+ sl := []
32
+ for v, _ in set {
33
+ sl = append(sl, v)
34
+ }
35
+ return sl
36
+ }
37
+
38
+ /**
39
+ * Checks if a set has an element.
40
+ *
41
+ * @param set a set of any elements
42
+ * @param element any
43
+ * @return found: bool
44
+ */
45
+ hasElement := func(set, element) {
46
+ return !is_undefined(set[element])
47
+ }
48
+
49
+ /**
50
+ * Adds elements to the set.
51
+ *
52
+ * @param set a set
53
+ * @param elements a slice of elements
54
+ * @return: result: set<string|number> - a changed old set with all old and new elements.
55
+ */
56
+ add := func(set, ...elements) {
57
+ for _, elem in elements {
58
+ set[elem] = true
59
+ }
60
+
61
+ return set
62
+ }
63
+
64
+ /**
65
+ * Returns a set of key of the map.
66
+ *
67
+ * @param map a map
68
+ * @return result: set<string|number>
69
+ */
70
+ fromMapKeys := func(map) {
71
+ keys := maps.getKeys(map)
72
+ return fromSlice(keys)
73
+ }
74
+
75
+ /**
76
+ * Transforms all keys of the map with a function and returns a new map with these keys.
77
+ *
78
+ * @param set: set
79
+ * @param fn: func(elem): newElem - a function that gets an element and returns a new element.
80
+ * @return new set: map[newElem]any - a transformed set
81
+ */
82
+ map := func(set, fn) {
83
+ return maps.mapKeys(set, fn)
84
+ }
85
+
86
+ export ll.toStrict({
87
+ fromSlice: fromSlice,
88
+ toSlice: toSlice,
89
+ hasElement: hasElement,
90
+ add: add,
91
+ fromMapKeys: fromMapKeys,
92
+ map: map
93
+ })