@platforma-sdk/workflow-tengo 2.10.2 → 2.12.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.
@@ -28,6 +28,7 @@ RTYPE_QUERY_RESULT := { Name: "BQueryResult", Version: "1" }
28
28
  RTYPE_OBJECT_SPEC := { Name: "BObjectSpec", Version: "1" }
29
29
 
30
30
  RTYPE_WORKDIR_CREATE := { Name: "WorkdirCreate", Version: "1" }
31
+ RTYPE_WORKDIR_CREATE_2 := { Name: "WorkdirCreate", Version: "2" }
31
32
  RTYPE_WORKDIR_FILL := { Name: "WorkdirFill", Version: "1" }
32
33
  RTYPE_WORKDIR_FILL_RULES := { Name: "WorkdirFill/rules", Version: "1" }
33
34
  RTYPE_WORKDIR_SAVE := { Name: "WorkdirSave", Version: "1" }
@@ -67,6 +68,7 @@ export ll.toStrict({
67
68
  RTYPE_RENDER_EPH_TEMPLATE : RTYPE_RENDER_EPH_TEMPLATE,
68
69
  RTYPE_RENDER_TEMPLATE : RTYPE_RENDER_TEMPLATE,
69
70
  RTYPE_WORKDIR_CREATE : RTYPE_WORKDIR_CREATE,
71
+ RTYPE_WORKDIR_CREATE_2 : RTYPE_WORKDIR_CREATE_2,
70
72
  RTYPE_WORKDIR_FILL : RTYPE_WORKDIR_FILL,
71
73
  RTYPE_WORKDIR_FILL_RULES : RTYPE_WORKDIR_FILL_RULES,
72
74
  RTYPE_WORKDIR_SAVE : RTYPE_WORKDIR_SAVE,
@@ -17,6 +17,7 @@ enum := import("enum")
17
17
  oop := import("@platforma-sdk/workflow-tengo:oop")
18
18
  validation := import("@platforma-sdk/workflow-tengo:validation")
19
19
  constants := import("@platforma-sdk/workflow-tengo:constants")
20
+ limits := import("@platforma-sdk/workflow-tengo:exec.limits")
20
21
  monetization := import("@platforma-sdk/workflow-tengo:exec.monetization")
21
22
  execConstants := import("@platforma-sdk/workflow-tengo:exec.constants")
22
23
 
@@ -50,7 +51,14 @@ builder := func() {
50
51
  "either a command with arguments or a software were already set.")
51
52
  }
52
53
 
54
+ //
55
+ // Limits
56
+ //
53
57
  queue := _HEAVY_QUEUE
58
+ hasLimits := false
59
+ cpuRequest := undefined
60
+ ramRequest := undefined
61
+
54
62
  cache := 0
55
63
  nErrorLines := 200
56
64
 
@@ -262,6 +270,40 @@ builder := func() {
262
270
  return self
263
271
  },
264
272
 
273
+ /**
274
+ * Preserve CPUs in environments that plan CPU resources (like PBS or Slurm).
275
+ *
276
+ * @param amount: number - number of cores requested for command.
277
+ */
278
+ cpu: func(amount) {
279
+ validation.assertType(amount, "number", "exec.builder.cpu: amount of cores must be a number")
280
+ ll.assert(amount > 0, "exec.builder.cpu: amount of cores should be greater than 0")
281
+ hasLimits = true
282
+ cpuRequest = amount
283
+
284
+ return self
285
+ },
286
+
287
+ /**
288
+ * Amount of RAM to request.
289
+ *
290
+ * @param amount: number | string - amount of RAM in bytes or string with size suffix (case-insensitive):
291
+ * K, KB, M, MB, G, GB for base-10 sizes (x1000)
292
+ * Ki, KiB, Mi, MiB, Gi, GiB for base-2 sizes (x1024)
293
+ * when operating with bytes, you may use 'units' package for convenience:
294
+ * 120 * units.GiB
295
+ *
296
+ * @return builder
297
+ */
298
+ ram: func(amount) {
299
+ validation.assertType(amount, ["or", "number", "string"], "exec.builder.ram: RAM amount should be a number or string")
300
+ ll.assert(is_string(amount) || amount > 0, "exec.builder.ram: amount in bytes should be greater than 0")
301
+ hasLimits = true
302
+ ramRequest = amount
303
+
304
+ return self
305
+ },
306
+
265
307
  /**
266
308
  * Use cacheMillis, cacheSeconds, cacheMinutes, cacheHours and cacheDays to not make hidden mistakes in cache time ranges.
267
309
  * Sets the cache time.
@@ -684,7 +726,7 @@ builder := func() {
684
726
  nErrorLines: nErrorLines
685
727
  }
686
728
 
687
- execInputs := {
729
+ pureExecInputs := {
688
730
  filesToAdd: smart.createMapResource(filesToAdd),
689
731
  filesToWrite: smart.createMapResource(filesToWriteRefs),
690
732
  dirsToCreate: slices.fromSet(dirsToCreate),
@@ -701,30 +743,39 @@ builder := func() {
701
743
  assetCache: assetCache
702
744
  }
703
745
 
746
+ serviceExecInputs := {}
747
+ if hasLimits && feats.workdirLimits {
748
+ serviceExecInputs.quota = limits.quotaBuilder().
749
+ queue(queue).
750
+ cpu(cpuRequest).
751
+ ram(ramRequest).
752
+ build()
753
+ }
754
+
704
755
  // We add monetization only if it's enabled
705
756
  // to not break a deduplication of exec's
706
757
  // that were created before this feature existed.
707
758
  if monetization.shouldRun(mnz) {
708
- execInputs.runOptions.monetization = monetization.toJson(mnz)
759
+ pureExecInputs.runOptions.monetization = monetization.toJson(mnz)
709
760
  }
710
761
 
711
762
  // We add new argsTyped and envsTyped only when they were added
712
763
  // to not break a deduplication on old pl backend.
713
764
  if typedArgSet || typedEnvSet {
714
- execInputs.runOptions.argsTyped = argsTyped
715
- execInputs.runOptions.envsTyped = envsTyped
765
+ pureExecInputs.runOptions.argsTyped = argsTyped
766
+ pureExecInputs.runOptions.envsTyped = envsTyped
716
767
  }
717
768
 
718
769
  if !is_undefined(soft) {
719
- execInputs.softwareInfo = soft.resource
720
- execInputs.runOptions.software = {
770
+ pureExecInputs.softwareInfo = soft.resource
771
+ pureExecInputs.runOptions.software = {
721
772
  name: soft.name,
722
773
  version: soft.version,
723
774
  descriptor: ll.fromStrict(soft.descriptor)
724
775
  }
725
776
  }
726
777
 
727
- tpl := render.create(assets.importTemplate("@platforma-sdk/workflow-tengo:exec.exec"), execInputs)
778
+ tpl := render.create(assets.importTemplate("@platforma-sdk/workflow-tengo:exec.exec"), pureExecInputs, serviceExecInputs)
728
779
 
729
780
  self := undefined
730
781
  self = ll.toStrict(oop.inherit(tpl, {
@@ -0,0 +1,149 @@
1
+ /**
2
+ * A library that creates Quota resource.
3
+ * Shared with 'workdir' package.
4
+ *
5
+ * Was shaped as a separate lib to avoid recursive imports (exec -> workdir -> exec)
6
+ */
7
+
8
+ ll := import("@platforma-sdk/workflow-tengo:ll")
9
+ oop := import("@platforma-sdk/workflow-tengo:oop")
10
+ validation := import("@platforma-sdk/workflow-tengo:validation")
11
+
12
+ smart := import("@platforma-sdk/workflow-tengo:smart")
13
+ json := import("json")
14
+
15
+ _RTYPE_QUOTA := { Name: "Quota", Version: "1" }
16
+
17
+ /**
18
+ * Builder for quota resource.
19
+ *
20
+ * Creates 'Quota' resource used to request specific amount of CPU and RAM in a given queue
21
+ */
22
+ quotaBuilder := func() {
23
+ self := undefined
24
+
25
+ queue_name := undefined
26
+ cpu_request := undefined
27
+ ram_request := undefined
28
+
29
+
30
+ self = ll.toStrict({
31
+ /**
32
+ * Create quota resource for specific execution queue.
33
+ * This is the only method required for call before .build()
34
+ *
35
+ * @param name: string - name of the queue.
36
+ * @return builder
37
+ */
38
+ queue: func(name) {
39
+ validation.assertType(name, "string", "exec.quota.builder.queue: name of the queue should be a string")
40
+ queue_name = name
41
+
42
+ return self
43
+ },
44
+
45
+ /**
46
+ * Amount of cores to request in quota.
47
+ *
48
+ * @param amount: number - number of cores.
49
+ * @return builder
50
+ */
51
+ cpu: func(amount) {
52
+ validation.assertType(amount, ["or", "number", "null"], "exec.quota.builder.cpu: CPU amount should be a number")
53
+ ll.assert(is_undefined(amount) || amount > 0, "exec.quota.builder.cpu: amount of cores should be greater than 0")
54
+ cpu_request = amount
55
+
56
+ return self
57
+ },
58
+
59
+ /**
60
+ * Amount of RAM to request.
61
+ *
62
+ * @param amount: number | string - amount of RAM in bytes or string with size suffix (case-insensitive):
63
+ * K, KB, M, MB, G, GB for base-10 sizes (x1000)
64
+ * Ki, KiB, Mi, MiB, Gi, GiB for base-2 sizes (x1024)
65
+ *
66
+ * @return builder
67
+ */
68
+ ram: func(amount) {
69
+ validation.assertType(amount, ["or", "number", "string", "null"], "exec.quota.builder.ram: RAM amount should be a number or string")
70
+ ll.assert(is_undefined(amount) || is_string(amount) || amount > 0, "exec.quota.builder.ram: amount in bytes should be greater than 0")
71
+ ram_request = amount
72
+
73
+ return self
74
+ },
75
+
76
+ /**
77
+ * Create Quota resource.
78
+ *
79
+ * @return smart.resource - reference to the resulting value resource
80
+ */
81
+ build: func() {
82
+ ll.assert(queue_name != "", "exec.quota.builder: queue name is mandatory and could not be empty string")
83
+
84
+ data := {
85
+ queue: queue_name
86
+ }
87
+ if !is_undefined(cpu_request) {
88
+ data.cpuCores = cpu_request
89
+ }
90
+ if !is_undefined(ram_request) {
91
+ data.memory = ram_request
92
+ }
93
+
94
+ return smart.ephemeralBuilder(_RTYPE_QUOTA, json.encode(data)).lockAndBuild()
95
+ }
96
+ })
97
+
98
+ return self
99
+ }
100
+
101
+ _RTYPE_STORAGE_SPACE_REQUEST := { Name: "StorageSpaceRequest", Version: "1" }
102
+ _SSR_FIELD_REQUEST := "request" // input
103
+ _SSR_FIELD_ALLOCATION := "allocation" // output
104
+
105
+ storageSpaceRequestBuilder := func() {
106
+ self := undefined
107
+
108
+ quota := undefined
109
+
110
+ self = ll.toStrict({
111
+ /**
112
+ * Request for given resources
113
+ *
114
+ * @param ref: smart.reference - reference to Quota resource to be fulfilled by storage allocator
115
+ * @return spaceRequestBuilder
116
+ */
117
+ quota: func(ref) {
118
+ validation.assertType(ref, validation.reference, "workdir.limits.quota: quota must be a valid reference to Quota resource")
119
+ quota = ref
120
+
121
+ return self
122
+ },
123
+
124
+ /**
125
+ * Build StorageSpaceRequest resource
126
+ *
127
+ * @return StorageSpaceRequest
128
+ */
129
+ build: func() {
130
+ ll.assert(smart.isReference(quota), "workdir.limits.build: quota of StorageSpaceRequest must be a valid reference")
131
+
132
+ requestBuilder := smart.ephemeralBuilder(_RTYPE_STORAGE_SPACE_REQUEST)
133
+ requestBuilder.getField(_SSR_FIELD_REQUEST).set(quota)
134
+
135
+ return ll.toStrict(
136
+ oop.inherit(requestBuilder, {
137
+ allocation: requestBuilder.getField(_SSR_FIELD_ALLOCATION)
138
+ })
139
+ )
140
+ }
141
+ })
142
+
143
+ return self
144
+ }
145
+
146
+ export ll.toStrict({
147
+ quotaBuilder: quotaBuilder,
148
+ storageSpaceRequestBuilder: storageSpaceRequestBuilder
149
+ })
@@ -18,6 +18,9 @@ _RTYPE_RUN_COMMAND_ARGS := { Name: "RunCommandArgs", Version: "1" }
18
18
  _RTYPE_RUN_COMMAND_OPTIONS := { Name: "run-command/options", Version: "1" }
19
19
  _RTYPE_RUN_COMMAND_REFS := { Name: "RunCommandRefs", Version: "1" }
20
20
 
21
+ _FIELD_WORKDIR_IN := "workdirIn"
22
+ _FIELD_WORKDIR_OUT := "workdirOut"
23
+
21
24
  /**
22
25
  * Creates a simple argument for the command.
23
26
  *
@@ -64,7 +67,7 @@ _createExpressionArg := func(argTpl) {
64
67
  */
65
68
  builder := func(workdir) {
66
69
  res := smart.ephemeralBuilder(_RTYPE_RUN_COMMAND)
67
- res.getField("workdirIn").set(workdir)
70
+ res.getField(_FIELD_WORKDIR_IN).set(workdir)
68
71
 
69
72
  self := undefined
70
73
 
@@ -386,7 +389,7 @@ builder := func(workdir) {
386
389
  self.getField("args").set(argsRes)
387
390
 
388
391
  res := ll.toStrict(oop.inherit(self.lockAndBuild(), {
389
- "workdir": self.outputs().workdirOut,
392
+ "workdir": self.outputs()[_FIELD_WORKDIR_OUT],
390
393
  "options": options
391
394
  }))
392
395
 
@@ -11,5 +11,6 @@ if is_undefined(_api) {
11
11
 
12
12
  export ll.toStrict({
13
13
  pureFutureFields: _api >= 1,
14
- commandExpressions: _api >= 2
14
+ commandExpressions: _api >= 2,
15
+ workdirLimits: _api >= 4
15
16
  })
@@ -20,9 +20,14 @@ _SUBSCRIPTION_FIELD := "subscription"
20
20
  * @param tpl: template resource (see assets.importTemplate())
21
21
  * @param isEph: whether template is ephemeral
22
22
  * @param inputs: a map of inputs to be passed to the template
23
+ * @param serviceInputs: (optional) a map of service inputs to be passed to the template.
24
+ * Service inputs are not accounted in dedupliation and recovering.
25
+ * Two resources with the same <inputs> and different <serviceInputs>
26
+ * would be considered as copies of each-other.
23
27
  * @return renderer: a smart resource with additional output("name") method
24
28
  */
25
- createUniversal := func(tpl, isEph, inputs) {
29
+ createUniversal := func(tpl, isEph, inputs, ...opts) {
30
+ serviceInputs := opts[0]
26
31
 
27
32
  // build renderer resource
28
33
  rbuilder := undefined
@@ -45,6 +50,9 @@ createUniversal := func(tpl, isEph, inputs) {
45
50
  for name, value in inputs {
46
51
  inputsMap.add(name, value)
47
52
  }
53
+ for name, value in serviceInputs {
54
+ inputsMap.addService(name, value)
55
+ }
48
56
 
49
57
  rbuilder.createInputField(tplutil.INPUTS_MAP_FIELD).set(inputsMap.build())
50
58
  // build
@@ -76,10 +84,14 @@ createUniversal := func(tpl, isEph, inputs) {
76
84
  *
77
85
  * @param tpl: template resource (see assets.importTemplate())
78
86
  * @param inputs: a map of inputs to be passed to the template
87
+ * @param serviceInputs: (optional) a map of service inputs to be passed to the template.
88
+ * Service inputs are not accounted in dedupliation and recovering.
89
+ * Two resources with the same <inputs> and different <serviceInputs>
90
+ * would be considered as copies of each-other.
79
91
  * @return renderer: a smart resource with additional output("name") method
80
92
  */
81
- create := func(tpl, inputs) {
82
- return createUniversal(tpl, false, inputs)
93
+ create := func(tpl, inputs, ...opts) {
94
+ return createUniversal(tpl, false, inputs, opts...)
83
95
  }
84
96
 
85
97
  /**
@@ -87,10 +99,14 @@ create := func(tpl, inputs) {
87
99
  *
88
100
  * @param tpl: template resource (see assets.importTemplate())
89
101
  * @param inputs: a map of inputs to be passed to the template
102
+ * @param serviceInputs: (optional) a map of service inputs to be passed to the template.
103
+ * The split into <inputs> and <serviceInputs> has no
104
+ * effect on ephemeral templates, as they do not
105
+ * have deduplication.
90
106
  * @return renderer: a smart resource with additional output("name") method
91
107
  */
92
- createEphemeral := func(tpl, inputs) {
93
- return createUniversal(tpl, true, inputs)
108
+ createEphemeral := func(tpl, inputs, ...opts) {
109
+ return createUniversal(tpl, true, inputs, opts...)
94
110
  }
95
111
 
96
112
  export ll.toStrict({
@@ -929,7 +929,7 @@ resourceBuilder = func(r) {
929
929
  * Create new field with given name and field type.
930
930
  *
931
931
  * @param name field name
932
- * @param fieldType: field type - input, output, dynamic, service, OTW. Use FfTYPE_* constants.
932
+ * @param fieldType: field type - input, output, dynamic, service, OTW. Use FTYPE_* constants.
933
933
  */
934
934
  createField: func(name, fieldType) {
935
935
  assertNotBuilt()
@@ -938,6 +938,17 @@ resourceBuilder = func(r) {
938
938
  return field(fid)
939
939
  },
940
940
 
941
+ /**
942
+ * Create service field with given name.
943
+ * Service fields behave very like inputs, but with deduplication disabled.
944
+ * This means, that if you create 2 resources, each having 1 input field with value "A" and the service
945
+ * field with different values, they would be considered the same by backend.
946
+ */
947
+ createServiceField: func(name) {
948
+ assertNotBuilt()
949
+ return self.createField(name, constants.FTYPE_SERVICE)
950
+ },
951
+
941
952
  /**
942
953
  * Create new input field with given name.
943
954
  *
@@ -945,7 +956,7 @@ resourceBuilder = func(r) {
945
956
  */
946
957
  createInputField: func(name) {
947
958
  assertNotBuilt()
948
- return self.createField(name, "input")
959
+ return self.createField(name, constants.FTYPE_INPUT)
949
960
  },
950
961
 
951
962
  /**
@@ -955,7 +966,7 @@ resourceBuilder = func(r) {
955
966
  */
956
967
  createOutputField: func(name) {
957
968
  assertNotBuilt()
958
- return self.createField(name, "output")
969
+ return self.createField(name, constants.FTYPE_OUTPUT)
959
970
  },
960
971
 
961
972
  /**
@@ -1086,6 +1097,18 @@ _mapBuilder := func(isEph) {
1086
1097
  return self
1087
1098
  },
1088
1099
 
1100
+ /**
1101
+ * Adds object automatically converting non-reference objects into Json values.
1102
+ * The object is added into service field, meaning the value would not affect deduplication of the map.
1103
+ *
1104
+ * @param key: string - how the field will be named in the map
1105
+ * @param value: any - a reference or a primitive value
1106
+ * @return self: mapBuilder
1107
+ */
1108
+ addService: func(key, value) {
1109
+ builder.createServiceField(key).setRefOrJson(value)
1110
+ return self
1111
+ },
1089
1112
 
1090
1113
  /**
1091
1114
  * Creates a map in the platforma
@@ -0,0 +1,13 @@
1
+ ll := import("@platforma-sdk/workflow-tengo:ll")
2
+
3
+ export ll.toStrict({
4
+ // Base-2 size modifiers:
5
+ KiB: 1024,
6
+ MiB: 1024 * 1024,
7
+ GiB: 1024 * 1024 * 1024,
8
+
9
+ // Decimal modifiers:
10
+ KB: 1000,
11
+ MB: 1000000,
12
+ GB: 1000000000
13
+ })
@@ -399,7 +399,7 @@ resource := func(...args) {
399
399
  ops = args[0]
400
400
  }
401
401
 
402
- assertJsonSchema(ops, {
402
+ assertType(ops, {
403
403
  `type,?`: {
404
404
  Name: `string`,
405
405
  Version: `string`
@@ -5,6 +5,8 @@
5
5
  json := import("json")
6
6
 
7
7
  ll := import("@platforma-sdk/workflow-tengo:ll")
8
+ feats := import("@platforma-sdk/workflow-tengo:feats")
9
+
8
10
  assets := import("@platforma-sdk/workflow-tengo:assets")
9
11
  constants := import("@platforma-sdk/workflow-tengo:constants")
10
12
  smart := import("@platforma-sdk/workflow-tengo:smart")
@@ -16,9 +18,36 @@ enum := import("enum")
16
18
  validation := import("@platforma-sdk/workflow-tengo:validation")
17
19
  render := import("@platforma-sdk/workflow-tengo:render")
18
20
 
21
+ _WD_CREATE_FIELD_ALLOCATION := "allocation"
22
+ _WD_CREATE_FIELD_WORKDIR := "workdir"
23
+
24
+ _WD_FILL_FIELD_WORKDIR_IN := "workdirIn"
25
+ _WD_FILL_FIELD_WORKDIR_OUT := "workdirOut"
26
+
27
+ /**
28
+ * Create empty working directory
29
+ *
30
+ * @return smart.field - field where working directory will appear after creation
31
+ */
19
32
  create := func() {
20
- wd := smart.ephemeralBuilder(constants.RTYPE_WORKDIR_CREATE).lockAndBuild()
21
- return wd.getField("workdir")
33
+ // working directories without quotas (old-style)
34
+ wdCreate := smart.ephemeralBuilder(constants.RTYPE_WORKDIR_CREATE).lockAndBuild()
35
+ return wdCreate.getField(_WD_CREATE_FIELD_WORKDIR)
36
+ }
37
+
38
+ /**
39
+ * Create empry working directory for given allocation. This means, the WD would actually be created only
40
+ * when allocation is ready for usage.
41
+ *
42
+ * @param allocation: smart.reference - a reference to storage space allocation.
43
+ * It is usually obtained from StorageSpaceRequest
44
+ * (see exec.limits.storageSpaceRequestBuilder)
45
+ */
46
+ createV2 := func(allocation) {
47
+ wdCreate := smart.ephemeralBuilder(constants.RTYPE_WORKDIR_CREATE_2).lockAndBuild()
48
+ wdCreate.getField(_WD_CREATE_FIELD_ALLOCATION).set(allocation)
49
+
50
+ return wdCreate.getField(_WD_CREATE_FIELD_WORKDIR)
22
51
  }
23
52
 
24
53
  _getFileFillRule := func(filePath, extension) {
@@ -87,14 +116,12 @@ _getDirFillRule := func(dir) {
87
116
  /**
88
117
  * Fills a workdir with files, values, and directories.
89
118
  *
90
- * @param workdir: reference - a reference to the workdir to fill.
91
119
  * @return self: builder - a builder that adds files and directories to the workdir. */
92
- _fill := func(workdir) {
93
- ll.assert(smart.isReference(workdir), "workdir must be a reference to a resource or field")
94
-
120
+ _fill := func() {
95
121
  wd := smart.ephemeralBuilder(constants.RTYPE_WORKDIR_FILL)
96
- wd.getField("workdirIn").set(workdir)
97
122
 
123
+ /** @type reference - space allocation to be used for WD */
124
+ _allocation := undefined
98
125
  blobs := {}
99
126
  files := {}
100
127
  zipExtractions := {}
@@ -113,6 +140,16 @@ _fill := func(workdir) {
113
140
  sets.add(dirs, newDirs...)
114
141
  },
115
142
 
143
+ /**
144
+ * Use given storage allocation for working directory.
145
+ *
146
+ * @param allocation: reference
147
+ */
148
+ allocation: func(allocation) {
149
+ validation.assertType(allocation, validation.reference, "workdir.builder: allocation should be a valid reference")
150
+ _allocation = allocation
151
+ },
152
+
116
153
  /**
117
154
  * Adds files from given zip archive into working directory.
118
155
  *
@@ -223,6 +260,15 @@ _fill := func(workdir) {
223
260
  * @return wd: reference - a reference to the filled workdir.
224
261
  */
225
262
  build: func() {
263
+ emptyWorkdir := undefined
264
+ if feats.workdirLimits && smart.isReference(_allocation) {
265
+ emptyWorkdir = createV2(_allocation)
266
+ } else {
267
+ emptyWorkdir = create()
268
+ }
269
+
270
+ wd.getField(_WD_FILL_FIELD_WORKDIR_IN).set(emptyWorkdir)
271
+
226
272
  rules := []
227
273
  for dir, _ in dirs {
228
274
  rules = append(rules, _getDirFillRule(dir))
@@ -249,7 +295,7 @@ _fill := func(workdir) {
249
295
  self.getField("dataIn").set(smart.createBinaryMapResource(values))
250
296
  self.getField("blobsIn").set(smart.createBlobMapResource(blobs))
251
297
 
252
- return self.lockAndBuild().outputs().workdirOut
298
+ return self.lockAndBuild().outputs()[_WD_FILL_FIELD_WORKDIR_OUT]
253
299
  }
254
300
  }))
255
301
 
@@ -262,7 +308,7 @@ _fill := func(workdir) {
262
308
  * @return self: builder - a builder that adds files and directories to the workdir.
263
309
  */
264
310
  builder := func() {
265
- return _fill(create())
311
+ return _fill()
266
312
  }
267
313
 
268
314
  /**
@@ -368,5 +414,6 @@ save := func(workdir) {
368
414
  export ll.toStrict({
369
415
  builder: builder,
370
416
  create: create,
417
+ createV2: createV2,
371
418
  save: save
372
419
  })
@@ -214,13 +214,7 @@ resolve := func(ref, ...options) {
214
214
  // template for prerun
215
215
  _preRunTpl := undefined
216
216
 
217
- /**
218
- * Prepare additional resources for the workflow body. The body function of the workflow will be executed only when the references will be
219
- * returned by the prepare method will ve resolved into a ready resources. The resulting resources will be passed as an input to the body function.
220
- *
221
- * @param cb: a callback function returning a map of references to be resolved
222
- */
223
- prepare := func(cb) {
217
+ _prepare := func(cb) {
224
218
  self.prepare(func(_){
225
219
  raw := cb(args())
226
220
  prefixed := {}
@@ -232,6 +226,32 @@ prepare := func(cb) {
232
226
  })
233
227
  }
234
228
 
229
+ /**
230
+ * Prepare additional resources for the workflow body. The body function of the workflow will be executed only when the references will be
231
+ * returned by the prepare method will ve resolved into a ready resources. The resulting resources will be passed as an input to the body function.
232
+ *
233
+ * @param cb: a callback function returning a map of references to be resolved
234
+ */
235
+ prepare := func(cb) {
236
+ if !_isProduction() {
237
+ return
238
+ }
239
+ _prepare(cb)
240
+ }
241
+
242
+ /**
243
+ * Prepare additional resources for the workflow body in pre-run. The body function of the workflow will be executed only when the references will be
244
+ * returned by the prepare method will ve resolved into a ready resources. The resulting resources will be passed as an input to the body function.
245
+ *
246
+ * @param cb: a callback function returning a map of references to be resolved
247
+ */
248
+ preparePreRun := func(cb) {
249
+ if !_isStaging() {
250
+ return
251
+ }
252
+ _prepare(cb)
253
+ }
254
+
235
255
  /**
236
256
  * Defines a pre run entry point for a current workflow
237
257
  */
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/workflow-tengo",
3
- "version": "2.10.2",
3
+ "version": "2.12.0",
4
4
  "description": "Tengo SDK library",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",
@@ -20,7 +20,7 @@
20
20
  "@platforma-open/milaboratories.software-small-binaries": "^1.15.6"
21
21
  },
22
22
  "devDependencies": {
23
- "@platforma-sdk/tengo-builder": "^1.17.5"
23
+ "@platforma-sdk/tengo-builder": "^1.17.7"
24
24
  },
25
25
  "scripts": {
26
26
  "type-check": "pl-tengo check",