@platforma-sdk/workflow-tengo 3.0.3 → 3.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.
Files changed (51) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +2 -1
  3. package/dist/index.mjs +2 -1
  4. package/dist/tengo/lib/canonical.lib.tengo +73 -0
  5. package/dist/tengo/lib/exec.lib.tengo +4 -7
  6. package/dist/tengo/lib/exec.limits.lib.tengo +63 -1
  7. package/dist/tengo/lib/exec.monetization.lib.tengo +3 -1
  8. package/dist/tengo/lib/exec.monetization_internal.lib.tengo +2 -1
  9. package/dist/tengo/lib/exec.runcmd.lib.tengo +47 -7
  10. package/dist/tengo/lib/feats.lib.tengo +5 -4
  11. package/dist/tengo/lib/maps.lib.tengo +1 -1
  12. package/dist/tengo/lib/pframes.builder.lib.tengo +5 -1
  13. package/dist/tengo/lib/pframes.bundle.lib.tengo +544 -0
  14. package/dist/tengo/lib/pframes.constants.lib.tengo +5 -1
  15. package/dist/tengo/lib/pframes.lib.tengo +3 -0
  16. package/dist/tengo/lib/pframes.spec.lib.tengo +224 -1
  17. package/dist/tengo/lib/pframes.xsv-import-file.lib.tengo +3 -3
  18. package/dist/tengo/lib/pframes.xsv.lib.tengo +79 -4
  19. package/dist/tengo/lib/sets.lib.tengo +2 -0
  20. package/dist/tengo/lib/slices.lib.tengo +4 -1
  21. package/dist/tengo/lib/smart.lib.tengo +24 -6
  22. package/dist/tengo/lib/tpl.base.lib.tengo +582 -0
  23. package/dist/tengo/lib/tpl.lib.tengo +6 -567
  24. package/dist/tengo/lib/tpl.light.lib.tengo +9 -0
  25. package/dist/tengo/lib/workdir.lib.tengo +1 -1
  26. package/dist/tengo/lib/workdir.proc.lib.tengo +1 -1
  27. package/dist/tengo/lib/workflow.lib.tengo +41 -2
  28. package/dist/tengo/lib/workflow.model.lib.tengo +3 -2
  29. package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
  30. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  31. package/dist/tengo/tpl/exec.monetization_info.plj.gz +0 -0
  32. package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
  33. package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
  34. package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
  35. package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
  36. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  37. package/dist/tengo/tpl/pframes.export-pframe-for-ui.plj.gz +0 -0
  38. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  39. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  40. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  41. package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
  42. package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
  43. package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
  44. package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
  45. package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
  46. package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
  47. package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
  48. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  49. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  50. package/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
  51. package/package.json +2 -2
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  declare type TemplateFromFile = { readonly type: "from-file"; readonly path: string; };
2
- declare type TplName = "ll.get-field" | "workflow.query-anchored" | "pframes.export-single-pcolumn" | "pframes.import-dir" | "pframes.remove-nulls" | "waiter.waiter" | "workdir.save" | "workflow.build-ctx" | "exec.R.create-renv" | "exec.python.install-deps" | "pframes.export-multiple-pcolumns" | "exec.python.create-venv" | "exec.monetization_to_json" | "exec.exec" | "exec.monetization_info" | "pframes.xsv-export-pframe" | "pframes.xsv-import-if-not-null" | "pframes.xsv-import-map" | "pframes.process-pcolumn-data" | "pframes.export-pframe" | "pframes.export-pframe-for-ui";
2
+ declare type TplName = "ll.get-field" | "pframes.export-single-pcolumn" | "pframes.import-dir" | "pframes.remove-nulls" | "waiter.waiter" | "workdir.save" | "workflow.build-ctx" | "workflow.query-anchored" | "exec.R.create-renv" | "exec.python.install-deps" | "pframes.export-multiple-pcolumns" | "exec.python.create-venv" | "exec.monetization_to_json" | "exec.exec" | "exec.monetization_info" | "pframes.xsv-export-pframe" | "pframes.xsv-import-if-not-null" | "pframes.xsv-import-map" | "pframes.process-pcolumn-data" | "pframes.slice-data" | "pframes.export-pframe" | "pframes.export-pframe-for-ui";
3
3
  declare const Templates: Record<TplName, TemplateFromFile>;
4
4
  export { Templates };
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  module.exports = { Templates: {
2
2
  'll.get-field': { type: 'from-file', path: require.resolve('./tengo/tpl/ll.get-field.plj.gz') },
3
- 'workflow.query-anchored': { type: 'from-file', path: require.resolve('./tengo/tpl/workflow.query-anchored.plj.gz') },
4
3
  'pframes.export-single-pcolumn': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.export-single-pcolumn.plj.gz') },
5
4
  'pframes.import-dir': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.import-dir.plj.gz') },
6
5
  'pframes.remove-nulls': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.remove-nulls.plj.gz') },
7
6
  'waiter.waiter': { type: 'from-file', path: require.resolve('./tengo/tpl/waiter.waiter.plj.gz') },
8
7
  'workdir.save': { type: 'from-file', path: require.resolve('./tengo/tpl/workdir.save.plj.gz') },
9
8
  'workflow.build-ctx': { type: 'from-file', path: require.resolve('./tengo/tpl/workflow.build-ctx.plj.gz') },
9
+ 'workflow.query-anchored': { type: 'from-file', path: require.resolve('./tengo/tpl/workflow.query-anchored.plj.gz') },
10
10
  'exec.R.create-renv': { type: 'from-file', path: require.resolve('./tengo/tpl/exec.R.create-renv.plj.gz') },
11
11
  'exec.python.install-deps': { type: 'from-file', path: require.resolve('./tengo/tpl/exec.python.install-deps.plj.gz') },
12
12
  'pframes.export-multiple-pcolumns': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.export-multiple-pcolumns.plj.gz') },
@@ -18,6 +18,7 @@ module.exports = { Templates: {
18
18
  'pframes.xsv-import-if-not-null': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.xsv-import-if-not-null.plj.gz') },
19
19
  'pframes.xsv-import-map': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.xsv-import-map.plj.gz') },
20
20
  'pframes.process-pcolumn-data': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.process-pcolumn-data.plj.gz') },
21
+ 'pframes.slice-data': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.slice-data.plj.gz') },
21
22
  'pframes.export-pframe': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.export-pframe.plj.gz') },
22
23
  'pframes.export-pframe-for-ui': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.export-pframe-for-ui.plj.gz') }
23
24
  }};
package/dist/index.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  import { resolve } from 'node:path';
2
2
  export const Templates = {
3
3
  'll.get-field': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/ll.get-field.plj.gz') },
4
- 'workflow.query-anchored': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/workflow.query-anchored.plj.gz') },
5
4
  'pframes.export-single-pcolumn': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.export-single-pcolumn.plj.gz') },
6
5
  'pframes.import-dir': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.import-dir.plj.gz') },
7
6
  'pframes.remove-nulls': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.remove-nulls.plj.gz') },
8
7
  'waiter.waiter': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/waiter.waiter.plj.gz') },
9
8
  'workdir.save': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/workdir.save.plj.gz') },
10
9
  'workflow.build-ctx': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/workflow.build-ctx.plj.gz') },
10
+ 'workflow.query-anchored': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/workflow.query-anchored.plj.gz') },
11
11
  'exec.R.create-renv': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/exec.R.create-renv.plj.gz') },
12
12
  'exec.python.install-deps': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/exec.python.install-deps.plj.gz') },
13
13
  'pframes.export-multiple-pcolumns': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.export-multiple-pcolumns.plj.gz') },
@@ -19,6 +19,7 @@ export const Templates = {
19
19
  'pframes.xsv-import-if-not-null': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.xsv-import-if-not-null.plj.gz') },
20
20
  'pframes.xsv-import-map': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.xsv-import-map.plj.gz') },
21
21
  'pframes.process-pcolumn-data': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.process-pcolumn-data.plj.gz') },
22
+ 'pframes.slice-data': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.slice-data.plj.gz') },
22
23
  'pframes.export-pframe': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.export-pframe.plj.gz') },
23
24
  'pframes.export-pframe-for-ui': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.export-pframe-for-ui.plj.gz') }
24
25
  };
@@ -0,0 +1,73 @@
1
+ ll := import("@platforma-sdk/workflow-tengo:ll")
2
+ maps := import("@platforma-sdk/workflow-tengo:maps")
3
+ json := import("json")
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+ encode := func(obj) {
13
+ if is_map(obj) || ll.isStrict(obj) {
14
+
15
+ _obj := obj
16
+ if ll.isStrict(obj) {
17
+ _obj = ll.fromStrict(obj)
18
+ }
19
+
20
+
21
+ sortedKeys := maps.getKeys(_obj)
22
+
23
+
24
+ result := "{"
25
+
26
+ for i, key in sortedKeys {
27
+ value := _obj[key]
28
+
29
+
30
+ if i > 0 {
31
+ result = result + ","
32
+ }
33
+
34
+
35
+ result = result + json.encode(key) + ":"
36
+
37
+
38
+ if is_map(value) || is_array(value) || ll.isStrict(value) {
39
+ result = result + encode(value)
40
+ } else {
41
+ result = result + json.encode(value)
42
+ }
43
+ }
44
+
45
+ return result + "}"
46
+ } else if is_array(obj) {
47
+
48
+ result := "["
49
+
50
+ for i, value in obj {
51
+
52
+ if i > 0 {
53
+ result = result + ","
54
+ }
55
+
56
+
57
+ if is_map(value) || is_array(value) || ll.isStrict(value) {
58
+ result = result + encode(value)
59
+ } else {
60
+ result = result + json.encode(value)
61
+ }
62
+ }
63
+
64
+ return result + "]"
65
+ }
66
+
67
+
68
+ return json.encode(obj)
69
+ }
70
+
71
+ export ll.toStrict({
72
+ encode: encode
73
+ })
@@ -4,6 +4,7 @@
4
4
 
5
5
  ll := import("@platforma-sdk/workflow-tengo:ll")
6
6
  feats := import("@platforma-sdk/workflow-tengo:feats")
7
+ validation := import("@platforma-sdk/workflow-tengo:validation")
7
8
 
8
9
  assets := import("@platforma-sdk/workflow-tengo:assets")
9
10
  smart := import("@platforma-sdk/workflow-tengo:smart")
@@ -15,7 +16,6 @@ path := import("@platforma-sdk/workflow-tengo:path")
15
16
  render := import("@platforma-sdk/workflow-tengo:render")
16
17
  enum := import("enum")
17
18
  oop := import("@platforma-sdk/workflow-tengo:oop")
18
- validation := import("@platforma-sdk/workflow-tengo:validation")
19
19
  constants := import("@platforma-sdk/workflow-tengo:constants")
20
20
  limits := import("@platforma-sdk/workflow-tengo:exec.limits")
21
21
  monetization := import("@platforma-sdk/workflow-tengo:exec.monetization_internal")
@@ -50,7 +50,6 @@ builder := func() {
50
50
 
51
51
 
52
52
  queue := execConstants.HEAVY_QUEUE
53
- hasLimits := false
54
53
  cpuRequest := undefined
55
54
  ramRequest := undefined
56
55
 
@@ -254,7 +253,6 @@ builder := func() {
254
253
  cpu: func(amount) {
255
254
  validation.assertType(amount, "number", "exec.builder.cpu: amount of cores must be a number")
256
255
  ll.assert(amount > 0, "exec.builder.cpu: amount of cores should be greater than 0")
257
- hasLimits = true
258
256
  cpuRequest = amount
259
257
 
260
258
  return self
@@ -274,7 +272,6 @@ builder := func() {
274
272
  ram: func(amount) {
275
273
  validation.assertType(amount, ["or", "number", "string"], "exec.builder.ram: RAM amount should be a number or string")
276
274
  ll.assert(is_string(amount) || amount > 0, "exec.builder.ram: amount in bytes should be greater than 0")
277
- hasLimits = true
278
275
  ramRequest = amount
279
276
 
280
277
  return self
@@ -728,11 +725,11 @@ builder := func() {
728
725
  }
729
726
 
730
727
  serviceExecInputs := {}
731
- if hasLimits && feats.workdirLimits {
728
+ if feats.workdirLimits || feats.computeLimits {
732
729
  serviceExecInputs.quota = limits.quotaBuilder().
733
730
  queue(queue).
734
- cpu(cpuRequest).
735
- ram(ramRequest).
731
+ cpu(cpuRequest). // 'undefined' value is OK and makes runner to apply default queue limits
732
+ ram(ramRequest). // 'undefined' value is OK and makes runner to apply default queue limits
736
733
  build()
737
734
  }
738
735
 
@@ -143,7 +143,69 @@ storageSpaceRequestBuilder := func() {
143
143
  return self
144
144
  }
145
145
 
146
+ _RTYPE_COMPUTE_REQUEST := func(executor) { return { Name: "ComputeRequest/" + executor, Version: "1" } }
147
+ _CR_DEFAULT_EXECUTOR_ID := "executor"
148
+ _CR_FIELD_REQUEST := "request" // input
149
+ _CR_FIELD_ALLOCATION := "allocation" // output
150
+
151
+ computeRequestBuilder := func() {
152
+ self := undefined
153
+
154
+ quota := undefined
155
+ executor := _CR_DEFAULT_EXECUTOR_ID
156
+
157
+ self = ll.toStrict({
158
+
159
+
160
+
161
+
162
+
163
+
164
+ quota: func(ref) {
165
+ validation.assertType(ref, validation.reference, "exec.limits.quota: quota must be a valid reference to Quota resource")
166
+ quota = ref
167
+
168
+ return self
169
+ },
170
+
171
+
172
+
173
+
174
+
175
+
176
+
177
+ executor: func(execID) {
178
+ validation.assertType(execID, "string", "exec.limits.executor: executor ID should be a string")
179
+ executor = execID
180
+
181
+ return self
182
+ },
183
+
184
+
185
+
186
+
187
+
188
+
189
+ build: func() {
190
+ ll.assert(smart.isReference(quota), "exec.limits.build: quota of ComputeRequest must be a valid reference")
191
+
192
+ rType := _RTYPE_COMPUTE_REQUEST(executor)
193
+ requestBuilder := smart.ephemeralBuilder(rType)
194
+ requestBuilder.getField(_CR_FIELD_REQUEST).set(quota)
195
+
196
+ return ll.toStrict(
197
+ oop.inherit(requestBuilder, {
198
+ allocation: requestBuilder.getField(_CR_FIELD_ALLOCATION)
199
+ })
200
+ )
201
+ }
202
+ })
203
+
204
+ return self
205
+ }
206
+
146
207
  export ll.toStrict({
147
208
  quotaBuilder: quotaBuilder,
148
- storageSpaceRequestBuilder: storageSpaceRequestBuilder
209
+ storageSpaceRequestBuilder: storageSpaceRequestBuilder,
210
+ computeRequestBuilder: computeRequestBuilder
149
211
  })
@@ -7,6 +7,8 @@ render := import("@platforma-sdk/workflow-tengo:render")
7
7
  smart := import("@platforma-sdk/workflow-tengo:smart")
8
8
  validation := import("@platforma-sdk/workflow-tengo:validation")
9
9
 
10
+ mznInfoTpl := assets.importTemplate("@platforma-sdk/workflow-tengo:exec.monetization_info")
11
+
10
12
 
11
13
 
12
14
 
@@ -119,7 +121,7 @@ builder := func(productKey) {
119
121
 
120
122
 
121
123
  result := render.create(
122
- assets.importTemplate("@platforma-sdk/workflow-tengo:exec.monetization_info"),
124
+ mznInfoTpl,
123
125
  {
124
126
  date: date, // solely for deduplication
125
127
  monetization: monetization.toJson(mnz),
@@ -218,9 +218,10 @@ setUrl := func(mnz, url) {
218
218
  run := func(mnz, wd, queue) {
219
219
  _validateIsReady(mnz)
220
220
 
221
+ mnzSw := assets.importSoftware("@platforma-open/milaboratories.software-small-binaries:mnz-client")
222
+
221
223
  exprType := feats.commandExpressions ? constants.ARG_TYPE_EXPRESSION : constants.ARG_TYPE_VAR
222
224
 
223
- mnzSw := assets.importSoftware("@platforma-open/milaboratories.software-small-binaries:mnz-client")
224
225
  builder := runcmd.builder(wd)
225
226
  builder.envTyped("MI_LICENSE", exprType, "{secrets.MI_LICENSE}")
226
227
 
@@ -3,22 +3,33 @@
3
3
 
4
4
 
5
5
  ll := import("@platforma-sdk/workflow-tengo:ll")
6
+ validation := import("@platforma-sdk/workflow-tengo:validation")
6
7
  feats := import("@platforma-sdk/workflow-tengo:feats")
8
+ oop := import("@platforma-sdk/workflow-tengo:oop")
7
9
 
8
10
  maps := import("@platforma-sdk/workflow-tengo:maps")
9
- oop := import("@platforma-sdk/workflow-tengo:oop")
10
11
  smart := import("@platforma-sdk/workflow-tengo:smart")
11
12
  json := import("json")
12
13
  constants := import("@platforma-sdk/workflow-tengo:exec.constants")
13
14
  slices := import("@platforma-sdk/workflow-tengo:slices")
15
+ limits := import("@platforma-sdk/workflow-tengo:exec.limits")
14
16
 
15
- _RTYPE_RUN_COMMAND := { Name: "RunCommand/executor", Version: "1" }
17
+ _RTYPE_RUN_COMMAND_V1 := { Name: "RunCommand/executor", Version: "1" }
18
+ _RTYPE_RUN_COMMAND_V2 := { Name: "RunCommand/executor", Version: "2" }
16
19
  _RTYPE_RUN_COMMAND_CMD := { Name: "RunCommandCmd", Version: "1" }
17
20
  _RTYPE_RUN_COMMAND_ARGS := { Name: "RunCommandArgs", Version: "1" }
18
21
  _RTYPE_RUN_COMMAND_OPTIONS := { Name: "run-command/options", Version: "1" }
19
22
  _RTYPE_RUN_COMMAND_REFS := { Name: "RunCommandRefs", Version: "1" }
20
23
 
24
+
25
+ _FIELD_ALLOCATION := "allocation" // not in v1
26
+ _FIELD_REFS := "refs"
27
+ _FIELD_CMD := "cmd"
28
+ _FIELD_ARGS := "args"
29
+ _FIELD_OPTIONS := "options"
21
30
  _FIELD_WORKDIR_IN := "workdirIn"
31
+
32
+
22
33
  _FIELD_WORKDIR_OUT := "workdirOut"
23
34
 
24
35
 
@@ -66,7 +77,14 @@ _createExpressionArg := func(argTpl) {
66
77
 
67
78
 
68
79
  builder := func(workdir) {
69
- res := smart.ephemeralBuilder(_RTYPE_RUN_COMMAND)
80
+ res := undefined
81
+
82
+ if feats.computeLimits {
83
+ res = smart.ephemeralBuilder(_RTYPE_RUN_COMMAND_V2)
84
+ } else {
85
+ res = smart.ephemeralBuilder(_RTYPE_RUN_COMMAND_V1)
86
+ }
87
+
70
88
  res.getField(_FIELD_WORKDIR_IN).set(workdir)
71
89
 
72
90
  self := undefined
@@ -76,6 +94,7 @@ builder := func(workdir) {
76
94
  envs := {}
77
95
  queue := ""
78
96
  refs := {}
97
+ _allocation := undefined
79
98
 
80
99
  substRules := {}
81
100
  hasPatternArgs := false // TODO: deprecated, remove after a month of expressionRefs in prod.
@@ -92,6 +111,17 @@ builder := func(workdir) {
92
111
 
93
112
 
94
113
 
114
+ allocation: func(allocation) {
115
+ validation.assertType(allocation, validation.reference, "runcmd.builder: allocation should be a valid reference")
116
+ _allocation = allocation
117
+ return self
118
+ },
119
+
120
+
121
+
122
+
123
+
124
+
95
125
  cmd: func(commandName) {
96
126
  cmd = _createSimpleArg(commandName)
97
127
  return self
@@ -366,11 +396,21 @@ builder := func(workdir) {
366
396
  "runcmd.builder().build: expression arguments must be enabled only if platforma backend supports expressions, found: %v",
367
397
  hasExpressionArgs)
368
398
 
399
+ if feats.computeLimits {
400
+ if !smart.isReference(_allocation) {
401
+
402
+
403
+ _quota := limits.quotaBuilder().queue(queue).build()
404
+ _allocation = limits.computeRequestBuilder().quota(_quota).build().allocation
405
+ }
406
+ self.getField(_FIELD_ALLOCATION).set(_allocation)
407
+ }
408
+
369
409
  refsRes := smart.createMapResourceWithType(_RTYPE_RUN_COMMAND_REFS, refs)
370
- self.getField("refs").set(refsRes)
410
+ self.getField(_FIELD_REFS).set(refsRes)
371
411
 
372
412
  cmdRes := smart.createValueResource(_RTYPE_RUN_COMMAND_CMD, json.encode(cmd))
373
- self.getField("cmd").set(cmdRes)
413
+ self.getField(_FIELD_CMD).set(cmdRes)
374
414
 
375
415
  options := {
376
416
  queueName: queue,
@@ -383,10 +423,10 @@ builder := func(workdir) {
383
423
  }
384
424
 
385
425
  optionsRes := smart.createValueResource(_RTYPE_RUN_COMMAND_OPTIONS, json.encode(options))
386
- self.getField("options").set(optionsRes)
426
+ self.getField(_FIELD_OPTIONS).set(optionsRes)
387
427
 
388
428
  argsRes := smart.createValueResource(_RTYPE_RUN_COMMAND_ARGS, json.encode(args))
389
- self.getField("args").set(argsRes)
429
+ self.getField(_FIELD_ARGS).set(argsRes)
390
430
 
391
431
  res := ll.toStrict(oop.inherit(self.lockAndBuild(), {
392
432
  "workdir": self.outputs()[_FIELD_WORKDIR_OUT],
@@ -6,11 +6,12 @@ ll := import("@platforma-sdk/workflow-tengo:ll")
6
6
 
7
7
  _api := plapi.apiVersion
8
8
  if is_undefined(_api) {
9
- _api = 0
9
+ _api = 0
10
10
  }
11
11
 
12
12
  export ll.toStrict({
13
- pureFutureFields: _api >= 1,
14
- commandExpressions: _api >= 2,
15
- workdirLimits: _api >= 4
13
+ pureFutureFields: _api >= 1, // 'pure' future fields magic that provides deduplication
14
+ commandExpressions: _api >= 2, // rich expressions in command arguments ( {{ system.ram_bytes / 20 }} )
15
+ workdirLimits: _api >= 4, // workdir storage controller can limit concurrency
16
+ computeLimits: _api >= 8 // runner can plan CPU and RAM
16
17
  })
@@ -13,7 +13,7 @@ getKeys := func(map) {
13
13
  res = append(res, k)
14
14
  }
15
15
 
16
- return slices.quickSort(res)
16
+ return slices.quickSortInPlace(res)
17
17
  }
18
18
 
19
19
 
@@ -2,7 +2,8 @@ ll := import("@platforma-sdk/workflow-tengo:ll")
2
2
  oop := import("@platforma-sdk/workflow-tengo:oop")
3
3
  smart := import("@platforma-sdk/workflow-tengo:smart")
4
4
  pConstants := import("@platforma-sdk/workflow-tengo:pframes.constants")
5
-
5
+ pSpec := import("@platforma-sdk/workflow-tengo:pframes.spec")
6
+ validation := import("@platforma-sdk/workflow-tengo:validation")
6
7
 
7
8
 
8
9
 
@@ -15,6 +16,9 @@ pFrameBuilder := func() {
15
16
 
16
17
  add: func(column, spec, data) {
17
18
  if (!is_undefined(spec)) {
19
+ if !smart.isReference(spec) {
20
+ validation.assertType(spec, pSpec.P_OBJECT_SPEC_SCHEMA)
21
+ }
18
22
  r.createInputField(column + ".spec").setRefOrJson(spec)
19
23
  }
20
24
  if (!is_undefined(data)) {