@platforma-sdk/workflow-tengo 6.17.3 → 6.18.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 (59) hide show
  1. package/dist/index.cjs +2 -0
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.js +2 -0
  4. package/dist/tengo/lib/pframes.bundle.lib.tengo +30 -5
  5. package/dist/tengo/lib/pframes.table-builder.lib.tengo +58 -17
  6. package/dist/tengo/lib/tpl.base.lib.tengo +1 -0
  7. package/dist/tengo/lib/v2.bquery.lib.tengo +9 -0
  8. package/dist/tengo/lib/v2.exports.lib.tengo +141 -0
  9. package/dist/tengo/lib/v2.node-rules.lib.tengo +102 -0
  10. package/dist/tengo/lib/v2.workflow.lib.tengo +281 -0
  11. package/dist/tengo/lib/workflow.bquery.lib.tengo +218 -26
  12. package/dist/tengo/tpl/annotations.compute.plj.gz +0 -0
  13. package/dist/tengo/tpl/anonymize.deanonymize.plj.gz +0 -0
  14. package/dist/tengo/tpl/anonymize.impl.plj.gz +0 -0
  15. package/dist/tengo/tpl/check_network.create_workdir_from_storage.plj.gz +0 -0
  16. package/dist/tengo/tpl/check_network.download_blob.plj.gz +0 -0
  17. package/dist/tengo/tpl/check_network.run_hello_world.plj.gz +0 -0
  18. package/dist/tengo/tpl/check_network.run_hello_world_py.plj.gz +0 -0
  19. package/dist/tengo/tpl/check_network.upload_blob.plj.gz +0 -0
  20. package/dist/tengo/tpl/check_network.upload_template.plj.gz +0 -0
  21. package/dist/tengo/tpl/check_network.workdir_type_name_extractor.plj.gz +0 -0
  22. package/dist/tengo/tpl/exec.conda.create-env.plj.gz +0 -0
  23. package/dist/tengo/tpl/exec.delayed-compute-allocation.impl.plj.gz +0 -0
  24. package/dist/tengo/tpl/exec.exec-impl.plj.gz +0 -0
  25. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  26. package/dist/tengo/tpl/exec.load-software.impl.plj.gz +0 -0
  27. package/dist/tengo/tpl/exec.monetization_info.plj.gz +0 -0
  28. package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
  29. package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
  30. package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
  31. package/dist/tengo/tpl/exec.python.venv-bin-path.plj.gz +0 -0
  32. package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
  33. package/dist/tengo/tpl/pframes.build-table.plj.gz +0 -0
  34. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  35. package/dist/tengo/tpl/pframes.export-parquet-pcolumn-chunk.plj.gz +0 -0
  36. package/dist/tengo/tpl/pframes.export-pframe-for-ui.plj.gz +0 -0
  37. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  38. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  39. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  40. package/dist/tengo/tpl/pframes.merge-resource-maps.plj.gz +0 -0
  41. package/dist/tengo/tpl/pframes.parse-to-json.plj.gz +0 -0
  42. package/dist/tengo/tpl/pframes.process-pcolumn-batch-split.plj.gz +0 -0
  43. package/dist/tengo/tpl/pframes.process-pcolumn-batch.plj.gz +0 -0
  44. package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
  45. package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
  46. package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
  47. package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
  48. package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
  49. package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
  50. package/dist/tengo/tpl/pt.import-dir.plj.gz +0 -0
  51. package/dist/tengo/tpl/pt.workflow-run.plj.gz +0 -0
  52. package/dist/tengo/tpl/v2.export-collection-for-ui.plj.gz +0 -0
  53. package/dist/tengo/tpl/v2.export-for-ui.plj.gz +0 -0
  54. package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
  55. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  56. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  57. package/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
  58. package/dist/tengo/tpl/workflow.query-context-filter.plj.gz +0 -0
  59. package/package.json +2 -2
package/dist/index.cjs CHANGED
@@ -43,5 +43,7 @@ module.exports = { Templates: {
43
43
  'pframes.process-pcolumn-batch': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.process-pcolumn-batch.plj.gz') },
44
44
  'pframes.process-pcolumn-data': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.process-pcolumn-data.plj.gz') },
45
45
  'pframes.export-pframe': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.export-pframe.plj.gz') },
46
+ 'v2.export-collection-for-ui': { type: 'from-file', path: require.resolve('./tengo/tpl/v2.export-collection-for-ui.plj.gz') },
47
+ 'v2.export-for-ui': { type: 'from-file', path: require.resolve('./tengo/tpl/v2.export-for-ui.plj.gz') },
46
48
  'pframes.export-pframe-for-ui': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.export-pframe-for-ui.plj.gz') }
47
49
  }};
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" | "pframes.export-parquet-pcolumn-chunk" | "pframes.export-single-pcolumn" | "pframes.import-dir" | "pframes.merge-resource-maps" | "pframes.remove-nulls" | "pt.import-dir" | "waiter.waiter" | "workdir.save" | "workflow.build-ctx" | "workflow.query-context-filter" | "anonymize.deanonymize" | "anonymize.impl" | "check_network.workdir_type_name_extractor" | "exec.delayed-compute-allocation.impl" | "exec.monetization_to_json" | "exec.python.install-deps" | "exec.python.venv-bin-path" | "pframes.export-multiple-pcolumns" | "workflow.query-anchored" | "check_network.create_workdir_from_storage" | "exec.conda.create-env" | "exec.exec-impl" | "exec.monetization_info" | "exec.python.create-venv" | "pframes.slice-data" | "check_network.download_blob" | "check_network.upload_blob" | "check_network.upload_template" | "exec.load-software.impl" | "pframes.parse-to-json" | "exec.exec" | "pt.workflow-run" | "check_network.run_hello_world" | "check_network.run_hello_world_py" | "pframes.build-table" | "pframes.xsv-export-pframe" | "pframes.xsv-import-if-not-null" | "pframes.xsv-import-map" | "annotations.compute" | "pframes.process-pcolumn-batch-split" | "pframes.process-pcolumn-batch" | "pframes.process-pcolumn-data" | "pframes.export-pframe" | "pframes.export-pframe-for-ui";
2
+ declare type TplName = "ll.get-field" | "pframes.export-parquet-pcolumn-chunk" | "pframes.export-single-pcolumn" | "pframes.import-dir" | "pframes.merge-resource-maps" | "pframes.remove-nulls" | "pt.import-dir" | "waiter.waiter" | "workdir.save" | "workflow.build-ctx" | "workflow.query-context-filter" | "anonymize.deanonymize" | "anonymize.impl" | "check_network.workdir_type_name_extractor" | "exec.delayed-compute-allocation.impl" | "exec.monetization_to_json" | "exec.python.install-deps" | "exec.python.venv-bin-path" | "pframes.export-multiple-pcolumns" | "workflow.query-anchored" | "check_network.create_workdir_from_storage" | "exec.conda.create-env" | "exec.exec-impl" | "exec.monetization_info" | "exec.python.create-venv" | "pframes.slice-data" | "check_network.download_blob" | "check_network.upload_blob" | "check_network.upload_template" | "exec.load-software.impl" | "pframes.parse-to-json" | "exec.exec" | "pt.workflow-run" | "check_network.run_hello_world" | "check_network.run_hello_world_py" | "pframes.build-table" | "pframes.xsv-export-pframe" | "pframes.xsv-import-if-not-null" | "pframes.xsv-import-map" | "annotations.compute" | "pframes.process-pcolumn-batch-split" | "pframes.process-pcolumn-batch" | "pframes.process-pcolumn-data" | "pframes.export-pframe" | "v2.export-collection-for-ui" | "v2.export-for-ui" | "pframes.export-pframe-for-ui";
3
3
  declare const Templates: Record<TplName, TemplateFromFile>;
4
4
  export { Templates };
package/dist/index.js CHANGED
@@ -44,5 +44,7 @@ export const Templates = {
44
44
  'pframes.process-pcolumn-batch': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.process-pcolumn-batch.plj.gz') },
45
45
  'pframes.process-pcolumn-data': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.process-pcolumn-data.plj.gz') },
46
46
  'pframes.export-pframe': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.export-pframe.plj.gz') },
47
+ 'v2.export-collection-for-ui': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/v2.export-collection-for-ui.plj.gz') },
48
+ 'v2.export-for-ui': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/v2.export-for-ui.plj.gz') },
47
49
  'pframes.export-pframe-for-ui': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.export-pframe-for-ui.plj.gz') }
48
50
  };
@@ -65,7 +65,8 @@ render := import("@platforma-sdk/workflow-tengo:render")
65
65
 
66
66
 
67
67
 
68
- createBuilder := func(ctx) {
68
+
69
+ createBuilderWith := func(q, ctx) {
69
70
 
70
71
 
71
72
 
@@ -117,6 +118,9 @@ createBuilder := func(ctx) {
117
118
 
118
119
 
119
120
  isGlobalRef := is_map(decodedId) && decodedId.__isRef == true
121
+ if isGlobalRef && maps.containsKey(q, "isRef") {
122
+ ll.panic("serialized value ids are not supported by this query API; add a ref {block, workflow, name} with addRef, got %v", id)
123
+ }
120
124
  if isGlobalRef {
121
125
  refKey := is_string(id) ? id : canonical.encode(decodedId)
122
126
  if !is_undefined(queryKey) {
@@ -305,7 +309,7 @@ createBuilder := func(ctx) {
305
309
  build: func() {
306
310
 
307
311
 
308
- resolveResult := bquery.anchoredQuery(ctx, anchors, queries, anchoredQueryOpts)
312
+ resolveResult := q.anchoredQuery(ctx, anchors, queries, anchoredQueryOpts)
309
313
 
310
314
  bundle := smart.mapBuilder(pConstants.RTYPE_P_COLUMN_BUNDLE)
311
315
 
@@ -314,7 +318,7 @@ createBuilder := func(ctx) {
314
318
  bundle.addRef(refKey, resolveResult.getAnchor(ref))
315
319
  bundle.addRef(ref, resolveResult.getAnchor(ref))
316
320
  } else {
317
- bundle.addRef(refKey, bquery.resolve(ref, ctx))
321
+ bundle.addRef(refKey, q.resolve(ref, ctx))
318
322
  }
319
323
  }
320
324
 
@@ -334,6 +338,15 @@ createBuilder := func(ctx) {
334
338
 
335
339
 
336
340
 
341
+ createBuilder := func(ctx) {
342
+ return createBuilderWith(bquery, ctx)
343
+ }
344
+
345
+
346
+
347
+
348
+
349
+
337
350
 
338
351
 
339
352
 
@@ -392,6 +405,17 @@ pColumnBundleUnmarshaller := func() {
392
405
 
393
406
  result := bResultUnmarshaller.parse(value.getValue(), parser)
394
407
 
408
+
409
+
410
+
411
+ dataOf := func(r) {
412
+ if !ll.isMap(r) || maps.containsKey(r, "data") || !maps.containsKey(r, "ref") ||
413
+ !ll.isMap(r.ref) || !maps.containsKey(r.ref, "block") {
414
+ return r.data
415
+ }
416
+ return undefined
417
+ }
418
+
395
419
  if is_array(result) {
396
420
  arr := []
397
421
  for i, r in result {
@@ -400,7 +424,7 @@ pColumnBundleUnmarshaller := func() {
400
424
  el := {
401
425
  key: k,
402
426
  spec: r.spec,
403
- data: r.data
427
+ data: dataOf(r)
404
428
  }
405
429
  pool[k] = el
406
430
  arr = append(arr, el)
@@ -410,7 +434,7 @@ pColumnBundleUnmarshaller := func() {
410
434
  pool[resultKey] = {
411
435
  key: resultKey,
412
436
  spec: result.spec,
413
- data: result.data
437
+ data: dataOf(result)
414
438
  }
415
439
  }
416
440
  }
@@ -611,5 +635,6 @@ pColumnBundleUnmarshaller := func() {
611
635
 
612
636
  export ll.toStrict({
613
637
  createBuilder: createBuilder,
638
+ createBuilderWith: createBuilderWith,
614
639
  pColumnBundleUnmarshaller: pColumnBundleUnmarshaller
615
640
  })
@@ -22,8 +22,6 @@
22
22
 
23
23
 
24
24
 
25
-
26
-
27
25
 
28
26
 
29
27
 
@@ -148,6 +146,29 @@ isQuerySpec := func(q) {
148
146
 
149
147
 
150
148
 
149
+ holdsRef := func(value, isRef) {
150
+ stack := [value]
151
+ si := 0
152
+ for si < len(stack) {
153
+ v := stack[si]
154
+ si += 1
155
+ if isRef(v) { return true }
156
+ if ll.isMap(v) {
157
+ if isResolvedColumn(v) { continue }
158
+ for _, child in v {
159
+ stack = append(stack, child)
160
+ }
161
+ } else if is_array(v) {
162
+ for _, child in v {
163
+ stack = append(stack, child)
164
+ }
165
+ }
166
+ }
167
+ return false
168
+ }
169
+
170
+
171
+
151
172
 
152
173
 
153
174
 
@@ -157,7 +178,16 @@ isQuerySpec := func(q) {
157
178
 
158
179
 
159
180
 
160
- tableBuilder := func(format, ...ctxArg) {
181
+ tableBuilderWith := func(q, format, ...ctxArg) {
182
+
183
+ unresolved := isUnresolved
184
+ if maps.containsKey(q, "isRef") {
185
+ unresolved = func(value) { return holdsRef(value, q.isRef) }
186
+ }
187
+ querySpec := func(v) {
188
+ return !unresolved(v) && !isResolvedColumn(v) && !isEnrichmentRef(v)
189
+ }
190
+
161
191
  ll.assert(!is_undefined(VALID_FORMATS[format]),
162
192
  "format must be one of [tsv, csv, parquet, ndjson], got '%s'", format)
163
193
 
@@ -194,7 +224,7 @@ tableBuilder := func(format, ...ctxArg) {
194
224
  header = opts[0].header
195
225
  }
196
226
 
197
- unresolved := isUnresolved(value)
227
+ unresolved := unresolved(value)
198
228
  if !unresolved {
199
229
  ll.assert(
200
230
  isResolvedColumn(value) ||
@@ -232,7 +262,7 @@ tableBuilder := func(format, ...ctxArg) {
232
262
  header = opts[0].header
233
263
  }
234
264
  join := columnJoinOpt(opts)
235
- if isUnresolved(query) || isEnrichmentRef(query) {
265
+ if unresolved(query) || isEnrichmentRef(query) {
236
266
  _hasUnresolved = true
237
267
  }
238
268
  _columns = append(_columns, {
@@ -283,7 +313,7 @@ tableBuilder := func(format, ...ctxArg) {
283
313
  entrySuffix = undefined
284
314
  }
285
315
 
286
- if isUnresolved(entryQuery) || isEnrichmentRef(entryQuery) {
316
+ if unresolved(entryQuery) || isEnrichmentRef(entryQuery) {
287
317
  _hasUnresolved = true
288
318
  }
289
319
  _columns = append(_columns, {
@@ -298,7 +328,7 @@ tableBuilder := func(format, ...ctxArg) {
298
328
  return self
299
329
  }
300
330
 
301
- if isUnresolved(query) || isEnrichmentRef(query) {
331
+ if unresolved(query) || isEnrichmentRef(query) {
302
332
  _hasUnresolved = true
303
333
  }
304
334
  _columns = append(_columns, {
@@ -406,7 +436,7 @@ tableBuilder := func(format, ...ctxArg) {
406
436
 
407
437
  hasQuerySpec := false
408
438
  for _, c in _columns {
409
- if isQuerySpec(c.query) {
439
+ if querySpec(c.query) {
410
440
  hasQuerySpec = true
411
441
  break
412
442
  }
@@ -421,15 +451,18 @@ tableBuilder := func(format, ...ctxArg) {
421
451
 
422
452
  resolveColumn := func(value) {
423
453
  if is_string(value) {
454
+ if maps.containsKey(q, "isRef") {
455
+ ll.panic("serialized value ids are not supported by this query API; pass a ref {block, workflow, name}, got %v", value)
456
+ }
424
457
  ref := decodeGlobalPObjectId(value)
425
- r := bquery.resolve(ref, _ctx)
458
+ r := q.resolve(ref, _ctx)
426
459
  return {
427
460
  spec: r.getFutureInputField("spec"),
428
461
  data: r.getFutureInputField("data")
429
462
  }
430
463
  }
431
- if isUnresolved(value) {
432
- r := bquery.resolve(value, _ctx)
464
+ if unresolved(value) {
465
+ r := q.resolve(value, _ctx)
433
466
  return {
434
467
  spec: r.getFutureInputField("spec"),
435
468
  data: r.getFutureInputField("data")
@@ -462,7 +495,7 @@ tableBuilder := func(format, ...ctxArg) {
462
495
  data: resolvedCol.data,
463
496
  filters: []
464
497
  }
465
- if isUnresolved(column) {
498
+ if unresolved(column) {
466
499
  primaryPlRefs[p.name] = column
467
500
  }
468
501
 
@@ -487,7 +520,7 @@ tableBuilder := func(format, ...ctxArg) {
487
520
  multiQueryResultByIdx := {}
488
521
  qMap := {}
489
522
  for idx, c in _columns {
490
- if isQuerySpec(c.query) {
523
+ if querySpec(c.query) {
491
524
  strategy := "expectSingle"
492
525
  if c.mode == "multi" { strategy = "expectMultiple" }
493
526
  qMap["q" + string(idx)] = maps.deepMerge(c.query, {
@@ -500,9 +533,9 @@ tableBuilder := func(format, ...ctxArg) {
500
533
  "column query specs require at least one primary added as a PlRef " +
501
534
  "or PrimaryRef (pre-resolved primaries cannot anchor column queries)")
502
535
 
503
- qResult := bquery.anchoredQuery(_ctx, primaryPlRefs, qMap)
536
+ qResult := q.anchoredQuery(_ctx, primaryPlRefs, qMap)
504
537
  for idx, c in _columns {
505
- if isQuerySpec(c.query) {
538
+ if querySpec(c.query) {
506
539
  r := qResult.getResult("q" + string(idx))
507
540
  if c.mode == "multi" {
508
541
 
@@ -552,7 +585,7 @@ tableBuilder := func(format, ...ctxArg) {
552
585
  } else if isResolvedColumn(c.query) {
553
586
  entry.spec = c.query.spec
554
587
  entry.data = c.query.data
555
- } else if isUnresolved(c.query) {
588
+ } else if unresolved(c.query) {
556
589
  resolved := resolveColumn(c.query)
557
590
  entry.spec = resolved.spec
558
591
  entry.data = resolved.data
@@ -646,6 +679,14 @@ tableBuilder := func(format, ...ctxArg) {
646
679
  return ll.toStrict(self)
647
680
  }
648
681
 
682
+
683
+
684
+
685
+ tableBuilder := func(format, ...ctxArg) {
686
+ return tableBuilderWith(bquery, format, ctxArg...)
687
+ }
688
+
649
689
  export ll.toStrict({
650
- tableBuilder: tableBuilder
690
+ tableBuilder: tableBuilder,
691
+ tableBuilderWith: tableBuilderWith
651
692
  })
@@ -739,6 +739,7 @@ tpl := func(ops) {
739
739
  hasInput : self.hasInput,
740
740
  inputs : self.inputs,
741
741
  defineOutputs : self.defineOutputs,
742
+ definedOutputs : func() { return copy(self.definedOutputs) },
742
743
  setOutputCache : self.setOutputCache,
743
744
  setOutputCacheMillis : self.setOutputCacheMillis,
744
745
  setOutputCacheSeconds : self.setOutputCacheSeconds,
@@ -0,0 +1,9 @@
1
+
2
+
3
+
4
+
5
+
6
+
7
+ bquery := import("@platforma-sdk/workflow-tengo:workflow.bquery")
8
+
9
+ export bquery.forFamily(2)
@@ -0,0 +1,141 @@
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+ ll := import("@platforma-sdk/workflow-tengo:ll")
13
+ smart := import("@platforma-sdk/workflow-tengo:smart")
14
+ maps := import("@platforma-sdk/workflow-tengo:maps")
15
+ constants := import("@platforma-sdk/workflow-tengo:constants")
16
+ bobject := import("@platforma-sdk/workflow-tengo:workflow.bobject")
17
+ rules := import("@platforma-sdk/workflow-tengo:v2.node-rules")
18
+
19
+ _CONSTRUCTORS_HINT := "build it with v2.exports.folder or v2.exports.collection (or pframes.pFrameBuilder)"
20
+
21
+
22
+ _see := func(value) {
23
+ if smart.isField(value) {
24
+ info := value.info()
25
+ if info.IsSet && info.Value != 0 {
26
+ return { kind: "resource", type: value.getValue().info().Type }
27
+ }
28
+ return { kind: "pending" }
29
+ }
30
+ if smart.isResource(value) {
31
+ return { kind: "resource", type: value.info().Type }
32
+ }
33
+ if ll.isMap(value) {
34
+ return { kind: "map", keys: maps.getKeys(value) }
35
+ }
36
+ return { kind: "other", typeName: type_name(value) }
37
+ }
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+ assertNode := func(value, what) {
48
+ got := rules.refusal(_see(value))
49
+ if !is_undefined(got) {
50
+ ll.panic("%s must be a typed node — a folder or a collection — or a reference to one; got %s; %s",
51
+ what, got, _CONSTRUCTORS_HINT)
52
+ }
53
+ }
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+ folder := func(entries) {
63
+ ll.assert(ll.isMap(entries) && !smart.isReference(entries),
64
+ "v2.exports.folder: expected a map of entry name to node, got %v", entries)
65
+
66
+ b := smart.mapBuilder()
67
+ for name in maps.getKeys(entries) {
68
+ rules.assertName(name, "folder entry name")
69
+ node := entries[name]
70
+ if !smart.isReference(node) && ll.isMap(node) && maps.containsKey(node, "spec") {
71
+ ll.panic("folder entry %q is not a node; items belong in a collection (v2.exports.collection)", name)
72
+ }
73
+ assertNode(node, "folder entry \"" + name + "\"")
74
+ b.add(name, node)
75
+ }
76
+ return b.build()
77
+ }
78
+
79
+
80
+ _isNoResult := func(data) {
81
+ if smart.isField(data) {
82
+ info := data.info()
83
+ if !(info.IsSet && info.Value != 0) {
84
+ return false
85
+ }
86
+ data = data.getValue()
87
+ }
88
+ return smart.isResource(data) && data.checkResourceType(constants.RTYPE_BRESOLVE_NO_RESULT)
89
+ }
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+ collection := func(items) {
104
+ ll.assert(ll.isMap(items) && !smart.isReference(items),
105
+ "v2.exports.collection: expected a map of item name to {spec, data}, got %v", items)
106
+
107
+ b := smart.structBuilder(rules.COLLECTION_TYPE)
108
+ for name in maps.getKeys(items) {
109
+ rules.assertName(name, "item name")
110
+ item := items[name]
111
+ if smart.isReference(item) || !ll.isMap(item) {
112
+ ll.panic("item %q must be a map {spec, data}, got %v", name, item)
113
+ }
114
+ for key in maps.getKeys(item) {
115
+ if key != "spec" && key != "data" && key != "ref" {
116
+ ll.panic("item %q has key %q; an item has only spec and data", name, key)
117
+ }
118
+ }
119
+
120
+ if !maps.containsKey(item, "spec") || is_undefined(item.spec) {
121
+ ll.panic("item %q has no spec", name)
122
+ }
123
+ spec := item.spec
124
+ if !smart.isReference(spec) {
125
+ spec = bobject.createSpec(spec)
126
+ }
127
+ b.createInputField(name + ".spec").set(spec)
128
+
129
+ data := maps.containsKey(item, "data") ? item.data : undefined
130
+ if !is_undefined(data) && !_isNoResult(data) {
131
+ b.createInputField(name + ".data").setRefOrJson(data)
132
+ }
133
+ }
134
+ return b.lockAndBuild()
135
+ }
136
+
137
+ export ll.toStrict({
138
+ folder: folder,
139
+ collection: collection,
140
+ assertNode: assertNode
141
+ })
@@ -0,0 +1,102 @@
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+ ll := import("@platforma-sdk/workflow-tengo:ll")
10
+ text := import("text")
11
+
12
+ FOLDER_TYPE := { Name: "std/map", Version: "1" }
13
+ COLLECTION_TYPE := { Name: "PFrame", Version: "1" }
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+ nameProblem := func(name) {
27
+ if !is_string(name) {
28
+ return "is not a string"
29
+ }
30
+ if name == "" {
31
+ return "is empty"
32
+ }
33
+ if text.contains(name, ".") {
34
+ return "contains \".\", which joins the names of a path"
35
+ }
36
+ if name == "resourceError" {
37
+ return "is reserved by the backend"
38
+ }
39
+ if text.has_prefix(name, "recovery/") {
40
+ return "starts with \"recovery/\", which the backend reserves"
41
+ }
42
+ return undefined
43
+ }
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+ assertName := func(name, what) {
52
+ problem := nameProblem(name)
53
+ if !is_undefined(problem) {
54
+ ll.panic("%s %q %s", what, name, problem)
55
+ }
56
+ }
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+ isNodeType := func(type) {
65
+ return (type.Name == FOLDER_TYPE.Name && type.Version == FOLDER_TYPE.Version) ||
66
+ (type.Name == COLLECTION_TYPE.Name && type.Version == COLLECTION_TYPE.Version)
67
+ }
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ refusal := func(seen) {
80
+ if seen.kind == "pending" {
81
+ return undefined
82
+ }
83
+ if seen.kind == "resource" {
84
+ if isNodeType(seen.type) {
85
+ return undefined
86
+ }
87
+ return "a resource of type " + seen.type.Name + "/" + seen.type.Version
88
+ }
89
+ if seen.kind == "map" {
90
+ return "a map with keys [" + text.join(seen.keys, ", ") + "]"
91
+ }
92
+ return "a value of type " + seen.typeName
93
+ }
94
+
95
+ export ll.toStrict({
96
+ FOLDER_TYPE: FOLDER_TYPE,
97
+ COLLECTION_TYPE: COLLECTION_TYPE,
98
+ nameProblem: nameProblem,
99
+ assertName: assertName,
100
+ isNodeType: isNodeType,
101
+ refusal: refusal
102
+ })