@platforma-sdk/workflow-tengo 2.8.1 → 2.8.3

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 (59) hide show
  1. package/dist/tengo/lib/assets.lib.tengo +76 -82
  2. package/dist/tengo/lib/constants.lib.tengo +28 -31
  3. package/dist/tengo/lib/exec.R.internal.lib.tengo +14 -14
  4. package/dist/tengo/lib/exec.R.lib.tengo +158 -167
  5. package/dist/tengo/lib/exec.internal.lib.tengo +222 -234
  6. package/dist/tengo/lib/exec.lib.tengo +647 -714
  7. package/dist/tengo/lib/exec.package.lib.tengo +229 -238
  8. package/dist/tengo/lib/exec.python.internal.lib.tengo +5 -6
  9. package/dist/tengo/lib/exec.python.lib.tengo +138 -146
  10. package/dist/tengo/lib/exec.runcmd.lib.tengo +234 -253
  11. package/dist/tengo/lib/exec.runenv.lib.tengo +88 -92
  12. package/dist/tengo/lib/exec.stream.lib.tengo +36 -40
  13. package/dist/tengo/lib/file.internal.lib.tengo +70 -77
  14. package/dist/tengo/lib/file.lib.tengo +54 -57
  15. package/dist/tengo/lib/ll.lib.tengo +217 -241
  16. package/dist/tengo/lib/maps.lib.tengo +98 -106
  17. package/dist/tengo/lib/objects.lib.tengo +11 -13
  18. package/dist/tengo/lib/oop.lib.tengo +16 -19
  19. package/dist/tengo/lib/path.lib.tengo +116 -125
  20. package/dist/tengo/lib/pframes.constants.lib.tengo +25 -28
  21. package/dist/tengo/lib/pframes.export-pcolumn.lib.tengo +28 -33
  22. package/dist/tengo/lib/pframes.lib.tengo +38 -43
  23. package/dist/tengo/lib/pframes.ll.lib.tengo +70 -73
  24. package/dist/tengo/lib/pframes.pcolumn.lib.tengo +45 -51
  25. package/dist/tengo/lib/pframes.spec.lib.tengo +70 -96
  26. package/dist/tengo/lib/pframes.util.lib.tengo +154 -289
  27. package/dist/tengo/lib/pframes.xsv.lib.tengo +260 -273
  28. package/dist/tengo/lib/render.lib.tengo +65 -76
  29. package/dist/tengo/lib/sets.lib.tengo +61 -68
  30. package/dist/tengo/lib/slices.lib.tengo +169 -180
  31. package/dist/tengo/lib/smart.lib.tengo +956 -1069
  32. package/dist/tengo/lib/test.lib.tengo +129 -161
  33. package/dist/tengo/lib/tpl.lib.tengo +448 -516
  34. package/dist/tengo/lib/tpl.state-await.lib.tengo +226 -241
  35. package/dist/tengo/lib/tpl.util.lib.tengo +4 -9
  36. package/dist/tengo/lib/validation.lib.tengo +334 -379
  37. package/dist/tengo/lib/workdir.lib.tengo +309 -332
  38. package/dist/tengo/lib/workdir.proc.lib.tengo +126 -144
  39. package/dist/tengo/lib/workdir.save.lib.tengo +113 -130
  40. package/dist/tengo/lib/workflow.bobject.lib.tengo +21 -26
  41. package/dist/tengo/lib/workflow.bquery.lib.tengo +140 -166
  42. package/dist/tengo/lib/workflow.constants.lib.tengo +1 -2
  43. package/dist/tengo/lib/workflow.context.lib.tengo +27 -32
  44. package/dist/tengo/lib/workflow.lib.tengo +167 -207
  45. package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
  46. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  47. package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
  48. package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
  49. package/dist/tengo/tpl/pframes.aggregate.plj.gz +0 -0
  50. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  51. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  52. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  53. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  54. package/dist/tengo/tpl/pframes.map-pframe.plj.gz +0 -0
  55. package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
  56. package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
  57. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  58. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  59. package/package.json +8 -8
@@ -1,255 +1,240 @@
1
- /**
2
- * Library implementing state machine to await certain resource states in
3
- * ephemeral templates.
4
- */
1
+
2
+
3
+
5
4
 
6
5
  tx := import("tx")
7
6
  ll := import("@platforma-sdk/workflow-tengo:ll")
8
- sets := import("@platforma-sdk/workflow-tengo:sets")
9
7
  text := import("text")
10
8
 
11
9
  createContext := func() {
12
- return {
13
- activeSubscriptions: {},
14
- resolvedStates: {}
15
- }
10
+ return {
11
+ activeSubscriptions: {},
12
+ resolvedStates: {}
13
+ }
16
14
  }
17
15
 
18
16
  awaitOneState := func(field, path, state) {
19
- if field.isErrorSet() && !field.isValueSet() {
20
- // Field is in pure error state (just error and no referenced resource).
21
- // This is a terminal state, so we stop any traversal here, and return value
22
- // the same as we would return if we achieved the desired state. It is
23
- // template's body responsibility to correctly handle the error state.
24
- return { type: "Ready" }
25
- }
26
-
27
- if len(path) > 0 {
28
- step := path[0]
29
- rPath := path[1:]
30
-
31
- if !field.isValueSet() {
32
- return {
33
- type: "Subscribe",
34
- subscriptions: [{
35
- resource: field.id.ResourceID,
36
- event: "InputSet"
37
- }]
38
- }
39
- }
40
-
41
- resource := field.getValue()
42
-
43
- if is_string(step) {
44
- nextField := resource.inputs()[step]
45
- if nextField == undefined {
46
- if resource.info().InputsLocked {
47
- ll.panic("field %v not found and inputs locked", step)
48
- }
49
- return {
50
- type: "Subscribe",
51
- subscriptions: [{
52
- resource: resource.id,
53
- event: "FieldCreated"
54
- },{
55
- resource: resource.id,
56
- event: "InputsLocked"
57
- }]
58
- }
59
- }
60
- return awaitOneState(nextField, rPath, state)
61
- } else if is_map(step) {
62
- if is_undefined(step.wildcard) == is_undefined(step.match) {
63
- ll.panic("Unknown step structure: %v", step)
64
- }
65
-
66
- matcher := undefined
67
-
68
- if !is_undefined(step.wildcard) {
69
- if step.wildcard != "*" {
70
- ll.panic("Complex wildcards are not supported, use { match: \"{regexp}\" }: %v", step.wildcard)
71
- }
72
- matcher = func(field) {
73
- return true
74
- }
75
- } else {
76
- regex := text.re_compile(step.match)
77
- if is_error(regex) {
78
- ll.panic("Error parsing regex %v: %v", step.match, regex)
79
- }
80
- matcher = func(field) {
81
- return regex.match(field)
82
- }
83
- }
84
-
85
- if !resource.info().InputsLocked {
86
- return {
87
- type: "Subscribe",
88
- subscriptions: [{
89
- resource: resource.id,
90
- event: "InputsLocked"
91
- }]
92
- }
93
- }
94
-
95
- subscriptions := []
96
- for nextFieldName, nextField in resource.inputs() {
97
- if !matcher(nextFieldName) {
98
- continue
99
- }
100
- res := awaitOneState(nextField, rPath, state)
101
- if res.type == "Ready" {
102
- continue
103
- } else if res.type == "Subscribe" {
104
- subscriptions = append(subscriptions, res.subscriptions...)
105
- } else {
106
- ll.panic("Unknown result type: %v", res.type)
107
- }
108
- }
109
- if len(subscriptions) == 0 {
110
- return { type: "Ready" }
111
- } else {
112
- return {
113
- type: "Subscribe",
114
- subscriptions: subscriptions
115
- }
116
- }
117
- } else {
118
- ll.panic("Malformed state await request path: %v", step)
119
- }
120
- } else {
121
- if state == "Exists" {
122
- return { type: "Ready" }
123
- } else {
124
- if !field.isSet() {
125
- return {
126
- type: "Subscribe",
127
- subscriptions: [{
128
- resource: field.id.ResourceID,
129
- event: "InputSet"
130
- }]
131
- }
132
- }
133
- resource := field.getValue()
134
- if state == "Set" {
135
- return { type: "Ready" }
136
- } else if state == "InputsLocked" {
137
- if resource.info().InputsLocked {
138
- return { type: "Ready" }
139
- } else {
140
- return {
141
- type: "Subscribe",
142
- subscriptions: [{
143
- resource: resource.id,
144
- event: "InputsLocked"
145
- }]
146
- }
147
- }
148
- } else if state == "AllInputsSet" {
149
- if resource.info().AllInputsSet {
150
- return { type: "Ready" }
151
- } else {
152
- return {
153
- type: "Subscribe",
154
- subscriptions: [{
155
- resource: resource.id,
156
- event: "AllInputsSet"
157
- }]
158
- }
159
- }
160
- } else if state == "ResourceReady" {
161
- if resource.info().ResourceReady {
162
- return { type: "Ready" }
163
- } else {
164
- return {
165
- type: "Subscribe",
166
- subscriptions: [{
167
- resource: resource.id,
168
- event: "ResourceReady"
169
- }]
170
- }
171
- }
172
- } else {
173
- ll.panic("Unknown await state: %v", state)
174
- }
175
- }
176
- }
177
- }
178
-
179
- /** Returns true if all requested states are reached */
17
+ if field.isErrorSet() && !field.isValueSet() {
18
+ return { type: "Ready" }
19
+ }
20
+
21
+ if len(path) > 0 {
22
+ step := path[0]
23
+ rPath := path[1:]
24
+
25
+ if !field.isValueSet() {
26
+ return {
27
+ type: "Subscribe",
28
+ subscriptions: [{
29
+ resource: field.id.ResourceID,
30
+ event: "InputSet"
31
+ }]
32
+ }
33
+ }
34
+
35
+ resource := field.getValue()
36
+
37
+ if is_string(step) {
38
+ nextField := resource.inputs()[step]
39
+ if nextField == undefined {
40
+ if resource.info().InputsLocked {
41
+ ll.panic("field %v not found and inputs locked", step)
42
+ }
43
+ return {
44
+ type: "Subscribe",
45
+ subscriptions: [{
46
+ resource: resource.id,
47
+ event: "FieldCreated"
48
+ },{
49
+ resource: resource.id,
50
+ event: "InputsLocked"
51
+ }]
52
+ }
53
+ }
54
+ return awaitOneState(nextField, rPath, state)
55
+ } else if is_map(step) {
56
+ if is_undefined(step.wildcard) == is_undefined(step.match) {
57
+ ll.panic("Unknown step structure: %v", step)
58
+ }
59
+
60
+ matcher := undefined
61
+
62
+ if !is_undefined(step.wildcard) {
63
+ if step.wildcard != "*" {
64
+ ll.panic("Complex wildcards are not supported, use { match: \"{regexp}\" }: %v", step.wildcard)
65
+ }
66
+ matcher = func(field) {
67
+ return true
68
+ }
69
+ } else {
70
+ regex := text.re_compile(step.match)
71
+ if is_error(regex) {
72
+ ll.panic("Error parsing regex %v: %v", step.match, regex)
73
+ }
74
+ matcher = func(field) {
75
+ return regex.match(field)
76
+ }
77
+ }
78
+
79
+ if !resource.info().InputsLocked {
80
+ return {
81
+ type: "Subscribe",
82
+ subscriptions: [{
83
+ resource: resource.id,
84
+ event: "InputsLocked"
85
+ }]
86
+ }
87
+ }
88
+
89
+ subscriptions := []
90
+ for nextFieldName, nextField in resource.inputs() {
91
+ if !matcher(nextFieldName) {
92
+ continue
93
+ }
94
+ res := awaitOneState(nextField, rPath, state)
95
+ if res.type == "Ready" {
96
+ continue
97
+ } else if res.type == "Subscribe" {
98
+ subscriptions = append(subscriptions, res.subscriptions...)
99
+ } else {
100
+ ll.panic("Unknown result type: %v", res.type)
101
+ }
102
+ }
103
+ if len(subscriptions) == 0 {
104
+ return { type: "Ready" }
105
+ } else {
106
+ return {
107
+ type: "Subscribe",
108
+ subscriptions: subscriptions
109
+ }
110
+ }
111
+ } else {
112
+ ll.panic("Malformed state await request path: %v", step)
113
+ }
114
+ } else {
115
+ if state == "Exists" {
116
+ return { type: "Ready" }
117
+ } else {
118
+ if !field.isSet() {
119
+ return {
120
+ type: "Subscribe",
121
+ subscriptions: [{
122
+ resource: field.id.ResourceID,
123
+ event: "InputSet"
124
+ }]
125
+ }
126
+ }
127
+ resource := field.getValue()
128
+ if state == "Set" {
129
+ return { type: "Ready" }
130
+ } else if state == "InputsLocked" {
131
+ if resource.info().InputsLocked {
132
+ return { type: "Ready" }
133
+ } else {
134
+ return {
135
+ type: "Subscribe",
136
+ subscriptions: [{
137
+ resource: resource.id,
138
+ event: "InputsLocked"
139
+ }]
140
+ }
141
+ }
142
+ } else if state == "AllInputsSet" {
143
+ if resource.info().AllInputsSet {
144
+ return { type: "Ready" }
145
+ } else {
146
+ return {
147
+ type: "Subscribe",
148
+ subscriptions: [{
149
+ resource: resource.id,
150
+ event: "AllInputsSet"
151
+ }]
152
+ }
153
+ }
154
+ } else if state == "ResourceReady" {
155
+ if resource.info().ResourceReady {
156
+ return { type: "Ready" }
157
+ } else {
158
+ return {
159
+ type: "Subscribe",
160
+ subscriptions: [{
161
+ resource: resource.id,
162
+ event: "ResourceReady"
163
+ }]
164
+ }
165
+ }
166
+ } else {
167
+ ll.panic("Unknown await state: %v", state)
168
+ }
169
+ }
170
+ }
171
+ }
172
+
180
173
  await := func(rootField, ctxStorageResource, requests) {
181
- pssKey := "await:" + string(rootField.id.ResourceID) + ":" + rootField.id.Name
182
- prevSubscriptions := ctxStorageResource.kvGetAsJson(pssKey)
183
-
184
- if !is_undefined(prevSubscriptions) && len(prevSubscriptions) == 0 {
185
- // initialization was finished before, in a previous template execution
186
- ll.panic("Unexpected state, desired state was reached in the previous call")
187
- }
188
-
189
- if is_undefined(prevSubscriptions) {
190
- // initializing previous subscriptions set
191
- prevSubscriptions = {}
192
- }
193
-
194
- subscriptions := {}
195
-
196
- addSubscription := func(subscription) {
197
- key := string(subscription.resource) + ":" + subscription.event
198
- if !is_undefined(subscriptions[key]) {
199
- return false
200
- } else {
201
- subscriptions[key] = subscription
202
- return true
203
- }
204
- }
205
-
206
- for _, request in requests {
207
- if !is_map(request) || !is_array(request.path) || !is_string(request.state) {
208
- ll.panic("Malformed state await request: %v", request)
209
- }
210
- res := awaitOneState(rootField, request.path, request.state)
211
- if res.type == "Ready" {
212
- continue
213
- } else if res.type == "Subscribe" {
214
- for subscription in res.subscriptions {
215
- addSubscription(subscription)
216
- }
217
- } else {
218
- ll.panic("Unknown result type %v", res.type)
219
- }
220
- }
221
-
222
- // creating new subscriptions
223
- for key, sub in subscriptions {
224
- if !is_undefined(prevSubscriptions[key]) {
225
- // don't create subscription, if it was created in the past
226
- continue
227
- }
228
- e := {}
229
- e[sub.event] = true
230
- tx.subscribeTo(sub.resource, pssKey + ":" + key, e)
231
- }
232
-
233
- // unsubscribing from subscriptions we don't need anymore
234
- for key, _ in prevSubscriptions {
235
- if !is_undefined(subscriptions[key]) {
236
- // don't unsubscribe from subscriptions, we still need
237
- continue
238
- }
239
- tx.unsubscribeFrom(pssKey + ":" + key)
240
- }
241
-
242
- // creating and saving new context; savong only keys
243
- newPrevSubscriptions := {}
244
- for key, _ in subscriptions {
245
- newPrevSubscriptions[key] = true
246
- }
247
- // saving the context
248
- ctxStorageResource.kvSetJson(pssKey, newPrevSubscriptions)
249
-
250
- return len(subscriptions) == 0
174
+ pssKey := "await:" + string(rootField.id.ResourceID) + ":" + rootField.id.Name
175
+ prevSubscriptions := ctxStorageResource.kvGetAsJson(pssKey)
176
+
177
+ if !is_undefined(prevSubscriptions) && len(prevSubscriptions) == 0 {
178
+ ll.panic("Unexpected state, desired state was reached in the previous call")
179
+ }
180
+
181
+ if is_undefined(prevSubscriptions) {
182
+ prevSubscriptions = {}
183
+ }
184
+
185
+ subscriptions := {}
186
+
187
+ addSubscription := func(subscription) {
188
+ key := string(subscription.resource) + ":" + subscription.event
189
+ if !is_undefined(subscriptions[key]) {
190
+ return false
191
+ } else {
192
+ subscriptions[key] = subscription
193
+ return true
194
+ }
195
+ }
196
+
197
+ for _, request in requests {
198
+ if !is_map(request) || !is_array(request.path) || !is_string(request.state) {
199
+ ll.panic("Malformed state await request: %v", request)
200
+ }
201
+ res := awaitOneState(rootField, request.path, request.state)
202
+ if res.type == "Ready" {
203
+ continue
204
+ } else if res.type == "Subscribe" {
205
+ for subscription in res.subscriptions {
206
+ addSubscription(subscription)
207
+ }
208
+ } else {
209
+ ll.panic("Unknown result type %v", res.type)
210
+ }
211
+ }
212
+
213
+ for key, sub in subscriptions {
214
+ if !is_undefined(prevSubscriptions[key]) {
215
+ continue
216
+ }
217
+ e := {}
218
+ e[sub.event] = true
219
+ tx.subscribeTo(sub.resource, pssKey + ":" + key, e)
220
+ }
221
+
222
+ for key, _ in prevSubscriptions {
223
+ if !is_undefined(subscriptions[key]) {
224
+ continue
225
+ }
226
+ tx.unsubscribeFrom(pssKey + ":" + key)
227
+ }
228
+
229
+ newPrevSubscriptions := {}
230
+ for key, _ in subscriptions {
231
+ newPrevSubscriptions[key] = true
232
+ }
233
+ ctxStorageResource.kvSetJson(pssKey, newPrevSubscriptions)
234
+
235
+ return len(subscriptions) == 0
251
236
  }
252
237
 
253
238
  export ll.toStrict({
254
- await: await
239
+ await: await
255
240
  })
@@ -1,19 +1,14 @@
1
- /* Templates/renderers utils */
2
1
 
3
2
  ll := import("@platforma-sdk/workflow-tengo:ll")
4
3
 
5
- // template field name in renderer resource
6
4
  TEMPLATE_FIELD := "template"
7
- // field name of a resource map with the inputs in renderer resource
8
5
  INPUTS_MAP_FIELD := "inputs"
9
- // prefix of output fields template results in renderer resource
10
6
  TEMPLATE_OUTPUT_PREFIX := "outputs/"
11
- // input field containing a map with resources to prepare for the body
12
7
  PREPARE_MAP_FIELD_NAME := "__prepare__"
13
8
 
14
9
  export ll.toStrict({
15
- TEMPLATE_FIELD : TEMPLATE_FIELD,
16
- INPUTS_MAP_FIELD : INPUTS_MAP_FIELD,
17
- TEMPLATE_OUTPUT_PREFIX : TEMPLATE_OUTPUT_PREFIX,
18
- PREPARE_MAP_FIELD_NAME : PREPARE_MAP_FIELD_NAME
10
+ TEMPLATE_FIELD : TEMPLATE_FIELD,
11
+ INPUTS_MAP_FIELD : INPUTS_MAP_FIELD,
12
+ TEMPLATE_OUTPUT_PREFIX : TEMPLATE_OUTPUT_PREFIX,
13
+ PREPARE_MAP_FIELD_NAME : PREPARE_MAP_FIELD_NAME
19
14
  })