@platforma-sdk/workflow-tengo 2.14.0 → 2.15.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.
Files changed (80) hide show
  1. package/README.md +17 -1
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.js +3 -1
  4. package/dist/index.mjs +3 -1
  5. package/dist/tengo/lib/assets.lib.tengo +28 -28
  6. package/dist/tengo/lib/assets.swinfo.lib.tengo +24 -24
  7. package/dist/tengo/lib/constants.lib.tengo +6 -3
  8. package/dist/tengo/lib/exec.R.internal.lib.tengo +2 -2
  9. package/dist/tengo/lib/exec.R.lib.tengo +41 -41
  10. package/dist/tengo/lib/exec.descriptor.lib.tengo +3 -3
  11. package/dist/tengo/lib/exec.internal.lib.tengo +41 -41
  12. package/dist/tengo/lib/exec.lib.tengo +289 -289
  13. package/dist/tengo/lib/exec.limits.lib.tengo +49 -49
  14. package/dist/tengo/lib/exec.monetization.lib.tengo +1 -1
  15. package/dist/tengo/lib/exec.monetization_internal.lib.tengo +38 -38
  16. package/dist/tengo/lib/exec.package.lib.tengo +53 -53
  17. package/dist/tengo/lib/exec.python.internal.lib.tengo +1 -1
  18. package/dist/tengo/lib/exec.python.lib.tengo +37 -37
  19. package/dist/tengo/lib/exec.runcmd.lib.tengo +138 -138
  20. package/dist/tengo/lib/exec.runenv.lib.tengo +18 -18
  21. package/dist/tengo/lib/exec.stream.lib.tengo +25 -25
  22. package/dist/tengo/lib/feats.lib.tengo +3 -3
  23. package/dist/tengo/lib/file.internal.lib.tengo +31 -31
  24. package/dist/tengo/lib/file.lib.tengo +29 -29
  25. package/dist/tengo/lib/ll.get-future-field-default.lib.tengo +137 -0
  26. package/dist/tengo/lib/ll.lib.tengo +129 -129
  27. package/dist/tengo/lib/maps.lib.tengo +38 -38
  28. package/dist/tengo/lib/objects.lib.tengo +6 -6
  29. package/dist/tengo/lib/oop.lib.tengo +11 -11
  30. package/dist/tengo/lib/path.lib.tengo +60 -60
  31. package/dist/tengo/lib/pframes.builder.lib.tengo +3 -3
  32. package/dist/tengo/lib/pframes.constants.lib.tengo +20 -20
  33. package/dist/tengo/lib/pframes.export-pcolumn.lib.tengo +5 -5
  34. package/dist/tengo/lib/pframes.lib.tengo +206 -198
  35. package/dist/tengo/lib/pframes.ll.lib.tengo +59 -59
  36. package/dist/tengo/lib/pframes.pcolumn.lib.tengo +18 -18
  37. package/dist/tengo/lib/pframes.remove-nulls.lib.tengo +36 -0
  38. package/dist/tengo/lib/pframes.spec.lib.tengo +83 -83
  39. package/dist/tengo/lib/pframes.util.lib.tengo +176 -176
  40. package/dist/tengo/lib/pframes.xsv-import-file.lib.tengo +160 -0
  41. package/dist/tengo/lib/pframes.xsv.lib.tengo +84 -167
  42. package/dist/tengo/lib/render.lib.tengo +100 -45
  43. package/dist/tengo/lib/sets.lib.tengo +44 -44
  44. package/dist/tengo/lib/slices.lib.tengo +126 -126
  45. package/dist/tengo/lib/smart.lib.tengo +654 -507
  46. package/dist/tengo/lib/test.lib.tengo +120 -120
  47. package/dist/tengo/lib/tpl.lib.tengo +206 -170
  48. package/dist/tengo/lib/tpl.state-await.lib.tengo +17 -17
  49. package/dist/tengo/lib/tpl.util.lib.tengo +5 -5
  50. package/dist/tengo/lib/units.lib.tengo +2 -2
  51. package/dist/tengo/lib/validation.lib.tengo +130 -130
  52. package/dist/tengo/lib/waiter.lib.tengo +13 -13
  53. package/dist/tengo/lib/workdir.lib.tengo +126 -126
  54. package/dist/tengo/lib/workdir.proc.lib.tengo +47 -47
  55. package/dist/tengo/lib/workdir.save.lib.tengo +53 -53
  56. package/dist/tengo/lib/workflow.bobject.lib.tengo +11 -11
  57. package/dist/tengo/lib/workflow.bquery.lib.tengo +40 -40
  58. package/dist/tengo/lib/workflow.constants.lib.tengo +1 -1
  59. package/dist/tengo/lib/workflow.context.lib.tengo +16 -16
  60. package/dist/tengo/lib/workflow.lib.tengo +76 -76
  61. package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
  62. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  63. package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
  64. package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
  65. package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
  66. package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
  67. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  68. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  69. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  70. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  71. package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
  72. package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
  73. package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
  74. package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
  75. package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
  76. package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
  77. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  78. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  79. package/package.json +2 -2
  80. package/dist/tengo/tpl/pframes.map-pframe.plj.gz +0 -0
@@ -1,4 +1,4 @@
1
- /* A library exposing template functional components */
1
+
2
2
 
3
3
  tx := import("tx")
4
4
  ll := import("@platforma-sdk/workflow-tengo:ll")
@@ -13,21 +13,21 @@ validation := import("@platforma-sdk/workflow-tengo:validation")
13
13
 
14
14
  bquery := import("@platforma-sdk/workflow-tengo:workflow.bquery")
15
15
 
16
- /**
17
- * Unmarshal resource according by:
18
- * - converting all json resources (potentially nested) into a tengo objects (maps)
19
- * - converting all pl.maps (resource maps) into tengo maps
20
- *
21
- * @param resource: smart resource to unmarshal
22
- * @param preprocessors: input preprocessors
23
- * @return object: unmarshaled resource
24
- */
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
25
  _unmarshal := func(resource, preprocessors) {
26
26
  ll.assert(smart.isResource(resource), "expected resource, found ", resource)
27
27
 
28
- // instance of this
28
+
29
29
  self := func(resource) { return _unmarshal(resource, preprocessors) }
30
- // try preprocessors first
30
+
31
31
  for preproc in preprocessors {
32
32
  if preproc.canParse(resource) {
33
33
  return preproc.parse(resource, self)
@@ -38,13 +38,17 @@ _unmarshal := func(resource, preprocessors) {
38
38
  ll.panic("input resource has errors: ", resource, resource.getError())
39
39
  }
40
40
 
41
+ if smart.isResource(resource) && resource.checkResourceType(constants.RTYPE_NULL) {
42
+ return undefined
43
+ }
44
+
41
45
  if smart.isJsonResource(resource) || resource.checkResourceType(constants.RTYPE_BOBJECT_SPEC) {
42
46
 
43
- // parse json data of the resource
47
+
44
48
  return resource.getDataAsJson()
45
49
  } else if smart.isMapResource(resource) || smart.isLockedEphMapResource(resource) {
46
50
 
47
- // unmarshal map
51
+
48
52
  result := {}
49
53
  for field, value in resource.inputs() {
50
54
  if value.isErrorSet() {
@@ -58,14 +62,14 @@ _unmarshal := func(resource, preprocessors) {
58
62
  }
59
63
  return result
60
64
  } else {
61
- // nothing todo
65
+
62
66
  return resource
63
67
  }
64
68
  }
65
69
 
66
- /**
67
- * A wrapper object around template functionality
68
- */
70
+
71
+
72
+
69
73
  tpl := func() {
70
74
  self := undefined
71
75
 
@@ -86,56 +90,56 @@ tpl := func() {
86
90
 
87
91
  self = {
88
92
 
89
- /**
90
- * Current renderer smart resource
91
- */
93
+
94
+
95
+
92
96
  renderer: func() {
93
97
  return _renderer
94
98
  },
95
99
 
96
- /**
97
- * Whether this template is ephemeral (rendered by ephemeral template router)
98
- */
100
+
101
+
102
+
99
103
  isEphemeral: func() {
100
104
  return _isEphemeral
101
105
  },
102
106
 
103
- /**
104
- * Current template resource
105
- */
107
+
108
+
109
+
106
110
  template: func() {
107
111
  return smart.resource(_templateId)
108
112
  },
109
113
 
110
114
  _unmarshallers: [bquery.resultUnmarshaller()],
111
115
 
112
- /**
113
- * Register a preprocessor of input resource. The preprocessor must have two
114
- * methods: `canParse(resource)` (bool) and `parse(resource)` (object). Registered
115
- * preprocessors will be used when unmarshaling resources.
116
- */
116
+
117
+
118
+
119
+
120
+
117
121
  registerUnmarshaller: func(preproc) {
118
122
  ll.assert(is_undefined(self._inputs), "input preprocessors must be registered before getting inputs")
119
123
  self._unmarshallers = append(self._unmarshallers, preproc)
120
124
  },
121
125
 
122
- /**
123
- * @return map[string]field: - a map of inputs to the fields of the input map.
124
- */
126
+
127
+
128
+
125
129
  rawInputs: func() {
126
130
  return self.renderer().get(tplutil.INPUTS_MAP_FIELD).inputs()
127
131
  },
128
132
 
129
133
  _inputs: undefined,
130
134
 
131
- /**
132
- * @return map: a map of the template inputs
133
- */
135
+
136
+
137
+
134
138
  inputs: func() {
135
139
  if self._inputs == undefined {
136
140
  inputs := _unmarshal(self.renderer().get(tplutil.INPUTS_MAP_FIELD), self._unmarshallers)
137
141
 
138
- // if we have prepared inputs, flatten them
142
+
139
143
  pp := inputs[tplutil.PREPARE_MAP_FIELD_NAME]
140
144
  if !is_undefined(pp) {
141
145
  delete(inputs, tplutil.PREPARE_MAP_FIELD_NAME)
@@ -150,22 +154,22 @@ tpl := func() {
150
154
  return self._inputs
151
155
  },
152
156
 
153
- /**
154
- * @return bool: whether there is input with a given name
155
- */
157
+
158
+
159
+
156
160
  hasInput: func(name) {
157
161
  return self.renderer().get(tplutil.INPUTS_MAP_FIELD).hasField(name)
158
162
  },
159
163
 
160
- // accumulated output definitions
164
+
161
165
  definedOutputs : [],
162
166
 
163
- /**
164
- * Defines current template output(s) names.
165
- *
166
- * For pure templates, all outputs must be defined before the template body, and only registered outputs can be used
167
- * inside. Optional for ephemeral templates.
168
- */
167
+
168
+
169
+
170
+
171
+
172
+
169
173
  defineOutputs : func(...name) {
170
174
  if len(name) == 1 && is_array(name[0]) {
171
175
  for v in name[0] {
@@ -176,84 +180,115 @@ tpl := func() {
176
180
  }
177
181
  },
178
182
 
179
- /**
180
- * In ephemeral template, body will only be executed when corresponding inputs state is
181
- * reached.
182
- *
183
- * Signature:
184
- * awaitState(path..., requestedState)
185
- *
186
- * Usage examples:
187
- * awaitState("AllInputsSet") - all inputs of the inputs map must be set
188
- * awaitState("inputName1", "ResourceReady") - resource in the input inputName1 must be ready
189
- * awaitState("inputName1", "nestedField", "Set") - nested input field in the input
190
- * inputName1 must exist and point to some resource
191
- * awaitState("inputName1", { wildcard: "*" }, "InputsLocked") - nested resource by the
192
- * inputName1 field must exist and have locked inputs and all
193
- * resources referenced by those fields must have all inputs
194
- * locked
195
- * awaitState("inputName1", { match: "^some_prefix\\." }, "InputsLocked") - nested resource
196
- * by the inputName1 field must exist and have locked inputs and
197
- * all resources referenced by inputs with "some_prefix." prefix
198
- * must have all inputs locked
199
- *
200
- * Example, the following two expressions are equivalent:
201
- * awaitState("inputName1", { wildcard: "*" }, "Set")
202
- * awaitState("inputName1", { match: "^some_prefix\\." }, "Set")
203
- * awaitState("inputName1", "AllInputsSet")
204
- *
205
- * Allowed states:
206
- * Exists - field exists
207
- * Set - field points to existing resource
208
- * InputsLocked - field points to resource with locked inputs
209
- * AllInputsSet - field points to resource with all inputs set
210
- * ResourceReady - field points to ready resource
211
- */
183
+
184
+
185
+
186
+
187
+
188
+
189
+
190
+
191
+
192
+
193
+
194
+
195
+
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
212
216
  awaitState: func(...pathAndState) {
213
217
  if !self.isEphemeral() {
214
218
  ll.panic("awaitState allowed only in ephemeral templates")
215
219
  }
220
+ self.awaitStateIfEphemeral(pathAndState...)
221
+ },
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+
256
+ awaitStateIfEphemeral: func(...pathAndState) {
257
+ if !self.isEphemeral() {
258
+ return
259
+ }
216
260
  path := pathAndState[:len(pathAndState) - 1]
217
261
  state := pathAndState[len(pathAndState) - 1]
218
- // if len(self.prepareQueue) == 0 {
219
262
  _stateRequests = append(_stateRequests, {
220
263
  path: path,
221
264
  state: state
222
265
  })
223
- // } else if len(self.prepareQueue) == 1 {
224
- // _stateRequestsAfterPrepare = append(_stateRequestsAfterPrepare, {
225
- // path: path,
226
- // state: state
227
- // })
228
- // } else {
229
- // ll.panic("Multiple prepare are not supported")
230
- // }
231
266
  },
232
267
 
233
- /**
234
- * True means we are in the inner render cycle, when all "to be prepared" fields are passed
235
- * already to inputs and no need to call prepare callbacks again.
236
- */
268
+
269
+
270
+
271
+
237
272
  ignorePrepare: _hasPrepared,
238
273
 
239
- /**
240
- * Resource map with the resources passed
241
- */
274
+
275
+
276
+
242
277
  preparedMapResource: func() {
243
278
  return _preparedMap
244
279
  },
245
280
 
246
- /**
247
- * A queue of 'prepare' callbacks
248
- */
281
+
282
+
283
+
249
284
  prepareQueue : [],
250
285
 
251
- /**
252
- * Prepare additional resources for the template body. The body function of the workflow will be executed only when the references
253
- * returned from prepare method will be resolved into a ready resources. The resulting resources will be passed as an input to the body function.
254
- *
255
- * @param cb: a callback function returning a map of references to be resolved
256
- */
286
+
287
+
288
+
289
+
290
+
291
+
257
292
  prepare : func(codeFn) {
258
293
  if self.ignorePrepare {
259
294
  return
@@ -264,19 +299,19 @@ tpl := func() {
264
299
  self.prepareQueue = append(self.prepareQueue, codeFn)
265
300
  },
266
301
 
267
- /**
268
- * Creates renderer which is re-rendering the current template additionally passing
269
- * resolve references as extra inputs.
270
- */
302
+
303
+
304
+
305
+
271
306
  renderPrepare: func() {
272
307
  ll.assert(!self.ignorePrepare, "expected !ignorePrepare")
273
308
 
274
309
  inputs := self.inputs()
275
310
 
276
- // create new inputs map
311
+
277
312
  newInputs := copy(inputs)
278
313
 
279
- // build a prepare map
314
+
280
315
  builder := undefined
281
316
  if self.isEphemeral() {
282
317
  builder = smart.ephemeralMapBuilder()
@@ -284,10 +319,10 @@ tpl := func() {
284
319
  builder = smart.mapBuilder()
285
320
  }
286
321
 
287
- // validate inputs first if needed
322
+
288
323
  self._validateInputs()
289
324
  for cb in self.prepareQueue {
290
- // run callback
325
+
291
326
  r := cb(inputs)
292
327
 
293
328
  ll.assert(is_map(r), "expected map, got ", r)
@@ -300,27 +335,27 @@ tpl := func() {
300
335
  }
301
336
  newInputs[tplutil.PREPARE_MAP_FIELD_NAME] = builder.build()
302
337
 
303
- // re-render self
338
+
304
339
  renderer := undefined
305
340
  if self.isEphemeral() {
306
341
  renderer = render.createEphemeral(self.template(), newInputs)
307
342
  } else {
308
343
  renderer = render.create(self.template(), newInputs)
309
344
  }
310
- // set outputs
345
+
311
346
  for output in self.definedOutputs {
312
347
  self.getOutputField(output).set(renderer.output(output))
313
348
  }
314
349
  },
315
350
 
316
- /**
317
- * Sets results map to resource output fields. Non-resource objects in the result values
318
- * will be automatically converted into Json resources.
319
- */
351
+
352
+
353
+
354
+
320
355
  setOutputs : func(resultMap) {
321
356
  ll.assert(ll.isMap(resultMap), "expected map, got ", resultMap)
322
357
 
323
- // set outputs
358
+
324
359
  for name, value in resultMap {
325
360
  f := self.getOutputField(name)
326
361
  if !f.isSet() {
@@ -329,16 +364,16 @@ tpl := func() {
329
364
  }
330
365
  },
331
366
 
332
- /**
333
- * Returns renderer's output field with a given name
334
- */
367
+
368
+
369
+
335
370
  getOutputField: func(name) {
336
371
  return self.renderer().getField(tplutil.TEMPLATE_OUTPUT_PREFIX + name)
337
372
  },
338
373
 
339
- /**
340
- * Creates relevant output fields and locks renderer's outputs
341
- */
374
+
375
+
376
+
342
377
  createOutputsAndLock: func(outputs) {
343
378
  for name in outputs {
344
379
  self.renderer().createOutputField(tplutil.TEMPLATE_OUTPUT_PREFIX + name)
@@ -348,9 +383,9 @@ tpl := func() {
348
383
 
349
384
  _inputsSchema: undefined,
350
385
 
351
- /**
352
- * Validate inputs schema before passing inputs to body.
353
- */
386
+
387
+
388
+
354
389
  validateInputs: func(schema) {
355
390
  ll.assert(is_undefined(self._inputsSchema), "validateInputs was already invoked")
356
391
  self._inputsSchema = schema
@@ -364,9 +399,9 @@ tpl := func() {
364
399
  validation.assertType(self.inputs(), schema)
365
400
  },
366
401
 
367
- /**
368
- * Routes template.
369
- */
402
+
403
+
404
+
370
405
  body: func(bodyFn){
371
406
  if self.isEphemeral() {
372
407
 
@@ -377,40 +412,40 @@ tpl := func() {
377
412
  }
378
413
  },
379
414
 
380
- /**
381
- * Routes pure template.
382
- */
415
+
416
+
417
+
383
418
  routePure: func(bodyFn){
384
- // Checks
419
+
385
420
  if len(self.definedOutputs) == 0 {
386
421
  ll.panic("please define outputs before template body")
387
422
  }
388
423
 
389
- //
390
- // Rules of pl guarantees that pure template code will be executed twice:
391
- //
392
- // (1) once for initialization, at this point it must initialize all outputs, for the
393
- // deduplication and recovery mechanisms to pick them up and, if possible, populate them
394
- // with results from equivalent previous invocations
395
- //
396
- // (2) after initialization is done and all deduplication and recovery algorithms did their
397
- // magic, if some (or all) of the outputs, our consumers demand are still absent, main
398
- // body of the template is executed, to calculate them
399
- //
424
+
425
+
426
+
427
+
428
+
429
+
430
+
431
+
432
+
433
+
434
+
400
435
 
401
436
  if ll.isInitializing() {
402
437
 
403
- // Initialization
438
+
404
439
  self.createOutputsAndLock(self.definedOutputs)
405
440
  } else {
406
441
 
407
- // processing resolves
442
+
408
443
  if len(self.prepareQueue) != 0 {
409
444
 
410
- // re-render self to
445
+
411
446
  self.renderPrepare()
412
447
  } else {
413
- // run body func
448
+
414
449
  self._validateInputs()
415
450
  result := bodyFn(self.inputs())
416
451
 
@@ -424,15 +459,15 @@ tpl := func() {
424
459
  "tpl.body() callback must return the same list of outputs as were given to 'defineOutputs()'. Want %s, got %s",
425
460
  self.definedOutputs, sets.toSlice(outputsWithValues))
426
461
 
427
- // set result to self outputs
462
+
428
463
  self.setOutputs(result)
429
464
  }
430
465
  }
431
466
  },
432
467
 
433
- /**
434
- * Routes ephemeral template.
435
- */
468
+
469
+
470
+
436
471
  routeEph: func(bodyFn) {
437
472
 
438
473
  if len(_stateRequests) == 0 {
@@ -465,7 +500,7 @@ tpl := func() {
465
500
  self.renderPrepare()
466
501
  } else {
467
502
 
468
- // run body func
503
+
469
504
  self._validateInputs()
470
505
  result := bodyFn(self.inputs())
471
506
  ll.assert(ll.isMap(result),
@@ -473,20 +508,20 @@ tpl := func() {
473
508
 
474
509
  self.createOutputsAndLock(maps.getKeys(result))
475
510
 
476
- // set result to self outputs
511
+
477
512
  self.setOutputs(result)
478
513
  }
479
514
  }
480
515
  },
481
516
 
482
- /**
483
- * Delegates itself to the specified template, i.e. runs specified template and
484
- * saves its outputs to this.
485
- *
486
- * @param tpl: template - specified template
487
- * @param extraInputs: map - additional inputs to pass to the template
488
- * @param outputs: array - array of expected outputs to save
489
- */
517
+
518
+
519
+
520
+
521
+
522
+
523
+
524
+
490
525
  routeDelegate: func(tpl, extraInputs, outputs) {
491
526
  _delegatedOutputs = outputs
492
527
 
@@ -514,9 +549,9 @@ tpl := func() {
514
549
  return ll.toStrict(self)
515
550
  }
516
551
 
517
- /**
518
- * Singleton instance of this resource
519
- */
552
+
553
+
554
+
520
555
  instance := tpl()
521
556
 
522
557
  export ll.toStrict({
@@ -528,6 +563,7 @@ export ll.toStrict({
528
563
  defineOutputs : instance.defineOutputs,
529
564
  prepare : instance.prepare,
530
565
  awaitState : instance.awaitState,
566
+ awaitStateIfEphemeral : instance.awaitStateIfEphemeral,
531
567
  delegate : instance.routeDelegate,
532
568
  validateInputs : instance.validateInputs,
533
569
  body : instance.body,
@@ -1,7 +1,7 @@
1
- /**
2
- * Library implementing state machine to await certain resource states in
3
- * ephemeral templates.
4
- */
1
+
2
+
3
+
4
+
5
5
 
6
6
  tx := import("tx")
7
7
  ll := import("@platforma-sdk/workflow-tengo:ll")
@@ -16,10 +16,10 @@ createContext := func() {
16
16
 
17
17
  awaitOneState := func(field, path, state) {
18
18
  if field.isErrorSet() && !field.isValueSet() {
19
- // Field is in pure error state (just error and no referenced resource).
20
- // This is a terminal state, so we stop any traversal here, and return value
21
- // the same as we would return if we achieved the desired state. It is
22
- // template's body responsibility to correctly handle the error state.
19
+
20
+
21
+
22
+
23
23
  return { type: "Ready" }
24
24
  }
25
25
 
@@ -175,18 +175,18 @@ awaitOneState := func(field, path, state) {
175
175
  }
176
176
  }
177
177
 
178
- /** Returns true if all requested states are reached */
178
+
179
179
  await := func(rootField, ctxStorageResource, requests) {
180
180
  pssKey := "await:" + string(rootField.id.ResourceID) + ":" + rootField.id.Name
181
181
  prevSubscriptions := ctxStorageResource.kvGetAsJson(pssKey)
182
182
 
183
183
  if !is_undefined(prevSubscriptions) && len(prevSubscriptions) == 0 {
184
- // initialization was finished before, in a previous template execution
184
+
185
185
  ll.panic("Unexpected state, desired state was reached in the previous call")
186
186
  }
187
187
 
188
188
  if is_undefined(prevSubscriptions) {
189
- // initializing previous subscriptions set
189
+
190
190
  prevSubscriptions = {}
191
191
  }
192
192
 
@@ -218,10 +218,10 @@ await := func(rootField, ctxStorageResource, requests) {
218
218
  }
219
219
  }
220
220
 
221
- // creating new subscriptions
221
+
222
222
  for key, sub in subscriptions {
223
223
  if !is_undefined(prevSubscriptions[key]) {
224
- // don't create subscription, if it was created in the past
224
+
225
225
  continue
226
226
  }
227
227
  e := {}
@@ -229,21 +229,21 @@ await := func(rootField, ctxStorageResource, requests) {
229
229
  tx.subscribeTo(sub.resource, pssKey + ":" + key, e)
230
230
  }
231
231
 
232
- // unsubscribing from subscriptions we don't need anymore
232
+
233
233
  for key, _ in prevSubscriptions {
234
234
  if !is_undefined(subscriptions[key]) {
235
- // don't unsubscribe from subscriptions, we still need
235
+
236
236
  continue
237
237
  }
238
238
  tx.unsubscribeFrom(pssKey + ":" + key)
239
239
  }
240
240
 
241
- // creating and saving new context; savong only keys
241
+
242
242
  newPrevSubscriptions := {}
243
243
  for key, _ in subscriptions {
244
244
  newPrevSubscriptions[key] = true
245
245
  }
246
- // saving the context
246
+
247
247
  ctxStorageResource.kvSetJson(pssKey, newPrevSubscriptions)
248
248
 
249
249
  return len(subscriptions) == 0