@platforma-sdk/workflow-tengo 4.6.1 → 4.6.2

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 (35) hide show
  1. package/dist/tengo/lib/pframes.constants.lib.tengo +2 -2
  2. package/dist/tengo/lib/pframes.data.lib.tengo +628 -0
  3. package/dist/tengo/lib/pframes.lib.tengo +2 -0
  4. package/dist/tengo/lib/smart.lib.tengo +4 -1
  5. package/dist/tengo/lib/tpl.base.lib.tengo +2 -2
  6. package/dist/tengo/tpl/check_network.create_workdir_from_storage.plj.gz +0 -0
  7. package/dist/tengo/tpl/check_network.download_blob.plj.gz +0 -0
  8. package/dist/tengo/tpl/check_network.run_hello_world.plj.gz +0 -0
  9. package/dist/tengo/tpl/check_network.run_hello_world_py.plj.gz +0 -0
  10. package/dist/tengo/tpl/check_network.upload_blob.plj.gz +0 -0
  11. package/dist/tengo/tpl/check_network.upload_template.plj.gz +0 -0
  12. package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
  13. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  14. package/dist/tengo/tpl/exec.monetization_info.plj.gz +0 -0
  15. package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
  16. package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
  17. package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
  18. package/dist/tengo/tpl/exec.python.venv-bin-path.plj.gz +0 -0
  19. package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
  20. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  21. package/dist/tengo/tpl/pframes.export-pframe-for-ui.plj.gz +0 -0
  22. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  23. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  24. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  25. package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
  26. package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
  27. package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
  28. package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
  29. package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
  30. package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
  31. package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
  32. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  33. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  34. package/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
  35. package/package.json +1 -1
@@ -57,8 +57,8 @@ export ll.toStrict({
57
57
  KIND_P_COLUMN : KIND_P_COLUMN,
58
58
  KEY_FIELD_NAME : KEY_FIELD_NAME,
59
59
  VALUE_FIELD_NAME : VALUE_FIELD_NAME,
60
- AGGREGATION_AXES_SPEC_FIELD_NAME : AGGREGATION_AXES_SPEC_FIELD_NAME,
61
- AGGREGATION_AXES_NAMES_FIELD_NAME : AGGREGATION_AXES_NAMES_FIELD_NAME,
60
+ AGGREGATION_AXES_SPEC_FIELD_NAME : AGGREGATION_AXES_SPEC_FIELD_NAME,
61
+ AGGREGATION_AXES_NAMES_FIELD_NAME : AGGREGATION_AXES_NAMES_FIELD_NAME,
62
62
  DISTILLED_AGGREGATION_SPEC_FIELD_NAME : DISTILLED_AGGREGATION_SPEC_FIELD_NAME,
63
63
  P_COLUMN_DATA_TYPE_PREFIX : P_COLUMN_DATA_TYPE_PREFIX,
64
64
  P_COLUMN_SUPER_PARTITIONED_DATA_TYPE_PREFIX : P_COLUMN_SUPER_PARTITIONED_DATA_TYPE_PREFIX,
@@ -0,0 +1,628 @@
1
+ ll := import("@platforma-sdk/workflow-tengo:ll")
2
+ smart := import("@platforma-sdk/workflow-tengo:smart")
3
+ json := import("json")
4
+ text := import("text")
5
+
6
+ slices := import("@platforma-sdk/workflow-tengo:slices")
7
+ maps := import("@platforma-sdk/workflow-tengo:maps")
8
+ pConstants := import("@platforma-sdk/workflow-tengo:pframes.constants")
9
+
10
+ _createPColumnDataInstance := undefined // Forward declaration
11
+
12
+ _createDecodeKeyString := func(expectedLength) {
13
+ return func(keyStr) {
14
+ decoded := json.decode(keyStr)
15
+ if !is_array(decoded) {
16
+ ll.panic("Decoded key '%v' from string '%s' is not an array(expected JSON array string).", decoded, keyStr)
17
+ }
18
+ if len(decoded) != expectedLength {
19
+ ll.panic("Decoded key array '%v' (from string '%s') length %d does not match expected top-layer key length %d.", decoded, keyStr, len(decoded), expectedLength)
20
+ }
21
+ return decoded
22
+ }
23
+ }
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+ parseData := func(inputParam, ...optsRaw) {
38
+ opts := {}
39
+ if len(optsRaw) > 0 {
40
+ if len(optsRaw) == 1 && is_map(optsRaw[0]) {
41
+ opts = optsRaw[0]
42
+ } else {
43
+ ll.panic("parseData opts must be a single map argument. Got: %v", optsRaw)
44
+ }
45
+ }
46
+
47
+ parse := opts.parse != false
48
+ recursive := opts.recursive == true
49
+
50
+ actualResource := undefined
51
+ initialSpec := undefined
52
+
53
+
54
+ isResourceDirectly := smart.isResource(inputParam)
55
+
56
+ if !isResourceDirectly && is_map(inputParam) && !is_undefined(inputParam.data) {
57
+ actualResource = inputParam.data
58
+ if parse && !smart.isResource(actualResource) {
59
+ ll.panic("The 'data' field in inputParam must be a valid resource. Got: %v", actualResource)
60
+ }
61
+ if !is_undefined(inputParam.spec) {
62
+ initialSpec = inputParam.spec
63
+ }
64
+ } else if isResourceDirectly {
65
+ actualResource = inputParam
66
+ } else {
67
+ ll.panic(
68
+ "parseData input must be a resource (a map with an .info() method) or a map of the form {data: resource, spec: map}. Got: %v",
69
+ inputParam
70
+ )
71
+ }
72
+
73
+ state := {
74
+ resource: actualResource,
75
+ resourceType: actualResource.info().Type,
76
+ parsed: parse,
77
+ keyLength: undefined, // For PColumnData/Json and PColumnData/ResourceMap
78
+ partitionKeyLength: undefined, // For partitioned types (top or only level for this instance)
79
+ superPartitionKeyLength: undefined, // For super-partitioned types
80
+ relevantTopLayerKeyLength: 0, // Calculated after parsing type
81
+ data: {}, // The main data store: map[stringKey] -> value | ResourceRef | PColumnDataInstance
82
+ spec: !is_undefined(opts.spec) ? opts.spec : initialSpec
83
+ }
84
+
85
+ if !parse {
86
+ return _createPColumnDataInstance(state)
87
+ }
88
+
89
+ resourceData := actualResource.getDataAsJson()
90
+ if is_undefined(resourceData) {
91
+ ll.panic("Failed to get JSON data (metadata) from resource of type %s", state.resourceType)
92
+ }
93
+
94
+
95
+ if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON ||
96
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP {
97
+ state.keyLength = resourceData.keyLength
98
+ state.relevantTopLayerKeyLength = state.keyLength
99
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_PARTITIONED ||
100
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED ||
101
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP_PARTITIONED {
102
+ state.partitionKeyLength = resourceData.partitionKeyLength
103
+ state.relevantTopLayerKeyLength = state.partitionKeyLength
104
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED ||
105
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED {
106
+ state.superPartitionKeyLength = resourceData.superPartitionKeyLength
107
+ state.partitionKeyLength = resourceData.partitionKeyLength // This is PKL of *nested* structures
108
+ state.relevantTopLayerKeyLength = state.superPartitionKeyLength
109
+ } else {
110
+ ll.panic("Unsupported PColumn data resource type for initial key length determination: %s", state.resourceType)
111
+ }
112
+
113
+ _decodeKeyString := _createDecodeKeyString(state.relevantTopLayerKeyLength)
114
+
115
+
116
+ if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON {
117
+ state.data = resourceData.data
118
+ for key, _ in state.data { _decodeKeyString(key) }
119
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_PARTITIONED ||
120
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP {
121
+ for key, fieldRef in actualResource.inputs() {
122
+ _decodeKeyString(key) // Validate key structure early
123
+ state.data[key] = fieldRef
124
+ }
125
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED {
126
+ tempData := {}
127
+ for key, fieldRef in actualResource.inputs() {
128
+ partKey := undefined
129
+ isIndex := false
130
+ if text.has_suffix(key, ".index") {
131
+ partKey = key[:len(key)-6]
132
+ isIndex = true
133
+ } else if text.has_suffix(key, ".values") {
134
+ partKey = key[:len(key)-7]
135
+ } else {
136
+ ll.warn("Unexpected file in BinaryPartitioned resource: %s", key)
137
+ continue
138
+ }
139
+ _decodeKeyString(partKey) // Validate key structure
140
+ if is_undefined(tempData[partKey]) { tempData[partKey] = {} }
141
+ if isIndex {
142
+ tempData[partKey].index = fieldRef
143
+ } else {
144
+ tempData[partKey].values = fieldRef
145
+ }
146
+ }
147
+ for pk, parts in tempData { // Ensure all parts have both index and values
148
+ if is_undefined(parts.index) || is_undefined(parts.values) {
149
+ ll.panic("Binary partition key %s is incomplete (missing index or values).", pk)
150
+ }
151
+ }
152
+ state.data = tempData
153
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED ||
154
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED ||
155
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP_PARTITIONED {
156
+ nestedOpts := { parse: recursive } // if not recursive, the nested resource will not be parsed
157
+ if !is_undefined(state.spec) {
158
+ nestedOpts.spec = maps.deepTransform(state.spec, {
159
+ axesSpec: func(axesSpec) {
160
+ return axesSpec[state.superPartitionKeyLength:]
161
+ }
162
+ });
163
+ }
164
+ for key, fieldRef in actualResource.inputs() {
165
+ _decodeKeyString(key)
166
+ state.data[key] = parseData(recursive ? fieldRef.getValue() : fieldRef, nestedOpts)
167
+ }
168
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP {
169
+ for key, fieldRef in actualResource.inputs() {
170
+ _decodeKeyString(key)
171
+ state.data[key] = fieldRef
172
+ }
173
+ } else {
174
+ ll.panic("Unsupported resource type for parseData data population: %s", state.resourceType)
175
+ }
176
+
177
+ return _createPColumnDataInstance(state)
178
+ }
179
+
180
+ _createPColumnDataInstance = func(state) {
181
+ _decodeKeyString := state.parsed ? _createDecodeKeyString(state.relevantTopLayerKeyLength) : undefined
182
+
183
+ self := undefined
184
+
185
+ self = ll.toStrict({
186
+ resource: state.resource,
187
+
188
+ parsed: state.parsed,
189
+
190
+ getSpec: func() {
191
+ return state.spec
192
+ },
193
+
194
+ getResourceType: func() {
195
+ return state.resourceType
196
+ },
197
+
198
+ keyLength: state.keyLength,
199
+ partitionKeyLength: state.partitionKeyLength,
200
+ superPartitionKeyLength: state.superPartitionKeyLength,
201
+
202
+ data: state.data,
203
+
204
+ parseKey: _createDecodeKeyString,
205
+
206
+ uniqueKeyValues: func(axisIdx) {
207
+ if !state.parsed {
208
+ ll.panic("uniqueKeyValues: resource is not parsed.")
209
+ }
210
+ ll.assert(is_int(axisIdx) && axisIdx >= 0, "axisIdx must be a non-negative integer.")
211
+ ll.assert(axisIdx < state.relevantTopLayerKeyLength, "axisIdx %d is out of bounds for the top-layer key structure (length %d).", axisIdx, state.relevantTopLayerKeyLength)
212
+
213
+ uniqueValsMap := {}
214
+ for keyStr, _ in state.data {
215
+ keyArray := _decodeKeyString(keyStr)
216
+ val := keyArray[axisIdx]
217
+ uniqueValsMap[string(val)] = val
218
+ }
219
+ return maps.values(uniqueValsMap)
220
+ },
221
+
222
+ createDataResource: func() {
223
+ if !is_undefined(state.resource) {
224
+
225
+ return state.resource
226
+ }
227
+
228
+ if !state.parsed {
229
+ ll.panic("createDataResource: resource is not parsed and no original resource is set.")
230
+ }
231
+
232
+ builder := undefined
233
+ if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON {
234
+ builder = smart.structBuilder(state.resourceType, json.encode({ keyLength: state.keyLength, data: state.data }))
235
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_PARTITIONED ||
236
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP_PARTITIONED {
237
+ builder = smart.structBuilder(state.resourceType, json.encode({ partitionKeyLength: state.partitionKeyLength }))
238
+ for key, resourceRef in state.data {
239
+ builder.createInputField(key).set(resourceRef)
240
+ }
241
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED {
242
+ builder = smart.structBuilder(state.resourceType, json.encode({ partitionKeyLength: state.partitionKeyLength }))
243
+ for key, binaryParts in state.data {
244
+ builder.createInputField(key + ".index").set(binaryParts.index)
245
+ builder.createInputField(key + ".values").set(binaryParts.values)
246
+ }
247
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED ||
248
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED ||
249
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP_PARTITIONED {
250
+ builder = smart.structBuilder(state.resourceType, json.encode({
251
+ superPartitionKeyLength: state.superPartitionKeyLength,
252
+ partitionKeyLength: state.partitionKeyLength
253
+ }))
254
+ for key, item in state.data {
255
+ builder.createInputField(key).set(item.createDataResource())
256
+ }
257
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP {
258
+ builder = smart.structBuilder(state.resourceType, json.encode({ keyLength: state.keyLength }))
259
+ for key, resRef in state.data {
260
+ builder.createInputField(key).set(resRef)
261
+ }
262
+ } else {
263
+ ll.panic("Unsupported resource type for createDataResource: %s", state.resourceType)
264
+ }
265
+
266
+ return builder.lockAndBuild()
267
+ },
268
+
269
+ filter: func(slicingParams) {
270
+ if len(slicingParams) == 0 {
271
+ return self
272
+ }
273
+
274
+ if !state.parsed {
275
+ ll.panic("filter: resource is not parsed.")
276
+ }
277
+
278
+ slicingParams = maps.clone(slicingParams)
279
+ slices.quickSortInPlaceFn(slicingParams, func(a, b) {
280
+ return a[0] < b[0]
281
+ })
282
+
283
+ _validateSlicingParamFormat := func(sp) {
284
+ if !is_array(sp) || len(sp) != 2 { ll.panic("Invalid slicing param: %v", sp) }
285
+ if !is_int(sp[0]) || sp[0] < 0 { ll.panic("Invalid axisIdx: %v", sp[0]) }
286
+ }
287
+
288
+ fixedAxesGlobal := {}
289
+ for param in slicingParams {
290
+ _validateSlicingParamFormat(param) // Corrected variable name from p to param
291
+ if fixedAxesGlobal[param[0]] { ll.panic("Duplicate axisIdx %d", param[0]) }
292
+ if param[0] >= state.relevantTopLayerKeyLength {
293
+ ll.panic("Slicing axis %d out of bounds for RTLKL %d", param[0], state.relevantTopLayerKeyLength)
294
+ }
295
+ fixedAxesGlobal[param[0]] = true
296
+ }
297
+
298
+ _matchesFilter := func(keyArray) {
299
+ for param in slicingParams {
300
+ axisIdx := param[0]
301
+ value := param[1]
302
+ if keyArray[axisIdx] != value {
303
+ return false
304
+ }
305
+ }
306
+ return true
307
+ }
308
+
309
+ _transformKey := func(keyArray) {
310
+ newKeyArray := []
311
+ for i, value in keyArray { if !fixedAxesGlobal[i] { newKeyArray = append(newKeyArray, value) } }
312
+ return newKeyArray
313
+ }
314
+
315
+ newSpec := undefined
316
+
317
+ if !is_undefined(state.spec) {
318
+ newSpec = maps.deepTransform(state.spec, {
319
+ axesSpec: func(axesSpec) {
320
+ updatedAxesSpec := []
321
+ for i, axisSpecEntry in axesSpec { if !fixedAxesGlobal[i] { updatedAxesSpec = append(updatedAxesSpec, axisSpecEntry) } }
322
+ return updatedAxesSpec
323
+ }
324
+ })
325
+ }
326
+
327
+ if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED ||
328
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED ||
329
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP_PARTITIONED {
330
+
331
+ newData := {}
332
+ for superKeyStr, item in state.data {
333
+ superKeyArray := _decodeKeyString(superKeyStr)
334
+ if _matchesFilter(superKeyArray) {
335
+ newSuperKeyArray := _transformKey(superKeyArray)
336
+ newData[string(json.encode(newSuperKeyArray))] = item // Nested item taken as is
337
+ }
338
+ }
339
+
340
+ newSuperPartitionKeyLength := state.superPartitionKeyLength - len(slicingParams)
341
+
342
+ if newSuperPartitionKeyLength < 0 {
343
+ ll.panic("Internal filter error: newSuperPartitionKeyLength is negative.")
344
+ }
345
+
346
+ if newSuperPartitionKeyLength == 0 {
347
+
348
+
349
+ if len(newData) > 1 {
350
+ ll.panic("Filter error: too many items (%d) after reducing super-partition key length to 0. Expected 0 or 1.", len(newData))
351
+ }
352
+
353
+ newInstanceOpts := {
354
+ parsed: true,
355
+ spec: newSpec
356
+ }
357
+
358
+ if len(newData) == 0 {
359
+
360
+ newInstanceOpts.data = {}
361
+
362
+ if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED {
363
+ partitionKeyLength := state.partitionKeyLength
364
+ newInstanceOpts.resourceType = pConstants.RTYPE_P_COLUMN_DATA_JSON_PARTITIONED
365
+ newInstanceOpts.partitionKeyLength = partitionKeyLength
366
+ newInstanceOpts.relevantTopLayerKeyLength = partitionKeyLength
367
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED {
368
+ partitionKeyLength := state.partitionKeyLength
369
+ newInstanceOpts.resourceType = pConstants.RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED
370
+ newInstanceOpts.partitionKeyLength = partitionKeyLength
371
+ newInstanceOpts.relevantTopLayerKeyLength = partitionKeyLength
372
+ } else { // RTYPE_P_COLUMN_DATA_RESOURCE_MAP_PARTITIONED (PColumnData/Partitioned/ResourceMap)
373
+ keyLength := state.keyLength
374
+ newInstanceOpts.resourceType = pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP
375
+ newInstanceOpts.keyLength = keyLength
376
+ newInstanceOpts.relevantTopLayerKeyLength = keyLength
377
+ }
378
+
379
+ return _createPColumnDataInstance(newInstanceOpts)
380
+ } else { // len(newData) == 1
381
+ singleNestedItem := newData["[]"] // This is a PColumnData instance
382
+ if is_undefined(singleNestedItem) || is_undefined(singleNestedItem.getResourceType()) {
383
+ ll.panic("Filter error: expected a PColumnData instance for key '[]' when unwrapping, got %v", singleNestedItem)
384
+ }
385
+ return singleNestedItem
386
+ }
387
+ } else { // newSuperPartitionKeyLength > 0, still super-partitioned
388
+ return _createPColumnDataInstance({
389
+ parsed: true,
390
+ resourceType: state.resourceType,
391
+ superPartitionKeyLength: newSuperPartitionKeyLength,
392
+ partitionKeyLength: state.partitionKeyLength, // This is PKL of nested structures or KL of nested ResourceMap. It doesn't change by slicing super keys.
393
+ relevantTopLayerKeyLength: newSuperPartitionKeyLength,
394
+ data: newData,
395
+ spec: newSpec,
396
+ isRecursive: state.isRecursive,
397
+ originalResource: undefined
398
+ })
399
+ }
400
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_PARTITIONED ||
401
+ state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED {
402
+ newData := {}
403
+ for keyStr, item in state.data {
404
+ keyArray := _decodeKeyString(keyStr)
405
+ if _matchesFilter(keyArray) {
406
+ newKeyArray := _transformKey(keyArray)
407
+ newData[string(json.encode(newKeyArray))] = item
408
+ }
409
+ }
410
+ newPartitionKeyLength := state.partitionKeyLength - len(slicingParams)
411
+ return _createPColumnDataInstance({
412
+ parsed: true,
413
+ resourceType: state.resourceType,
414
+ partitionKeyLength: newPartitionKeyLength,
415
+ relevantTopLayerKeyLength: newPartitionKeyLength,
416
+ data: newData,
417
+ spec: newSpec,
418
+ isRecursive: state.isRecursive
419
+ })
420
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP {
421
+ newData := {}
422
+ for keyStr, item in state.data {
423
+ keyArray := _decodeKeyString(keyStr)
424
+ if _matchesFilter(keyArray) {
425
+ newKeyArray := _transformKey(keyArray)
426
+ newData[string(json.encode(newKeyArray))] = item
427
+ }
428
+ }
429
+ newKeyLength := state.keyLength - len(slicingParams)
430
+ return _createPColumnDataInstance({
431
+ parsed: true,
432
+ resourceType: state.resourceType,
433
+ keyLength: newKeyLength,
434
+ relevantTopLayerKeyLength: newKeyLength,
435
+ data: newData,
436
+ spec: newSpec,
437
+ isRecursive: state.isRecursive
438
+ })
439
+ } else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON {
440
+ newData := {}
441
+ for keyStr, val in state.data {
442
+ keyArray := _decodeKeyString(keyStr)
443
+ if _matchesFilter(keyArray) {
444
+ newKeyArray := _transformKey(keyArray)
445
+ newData[string(json.encode(newKeyArray))] = val
446
+ }
447
+ }
448
+ newKeyLength := state.keyLength - len(slicingParams)
449
+ return _createPColumnDataInstance({
450
+ parsed: true,
451
+ resourceType: state.resourceType,
452
+ keyLength: newKeyLength,
453
+ relevantTopLayerKeyLength: newKeyLength,
454
+ data: newData,
455
+ spec: newSpec,
456
+ isRecursive: state.isRecursive,
457
+ originalResource: undefined
458
+ })
459
+ } else {
460
+ ll.panic("Filtering not supported for type %s", state.resourceType)
461
+ }
462
+ },
463
+
464
+ partition: func(...axesToPartitionBy) {
465
+ if !state.parsed {
466
+ ll.panic("partition: resource is not parsed.")
467
+ }
468
+ if len(axesToPartitionBy) == 0 {
469
+ ll.panic("partition: at least one axis index must be provided.")
470
+ }
471
+
472
+
473
+ seenAxesValidation := {}
474
+ for _, axisIdx in axesToPartitionBy {
475
+ if !is_int(axisIdx) || axisIdx < 0 {
476
+ ll.panic("partition: axis index '%v' must be a non-negative integer.", axisIdx)
477
+ }
478
+ if axisIdx >= state.relevantTopLayerKeyLength {
479
+ ll.panic("partition: axis index %d is out of bounds for the top-layer key structure (length %d). Current type: %s", axisIdx, state.relevantTopLayerKeyLength, state.resourceType)
480
+ }
481
+ if seenAxesValidation[axisIdx] {
482
+ ll.panic("partition: duplicate axis index %d provided.", axisIdx)
483
+ }
484
+ seenAxesValidation[axisIdx] = true
485
+ }
486
+
487
+ resultPartitions := {}
488
+
489
+ for keyStr, _ in state.data {
490
+ keyArray := _decodeKeyString(keyStr)
491
+
492
+ currentPartitionTuple := []
493
+ for _, axisIdx in axesToPartitionBy {
494
+ currentPartitionTuple = append(currentPartitionTuple, keyArray[axisIdx])
495
+ }
496
+ partitionMapKeyStr := string(json.encode(currentPartitionTuple))
497
+
498
+ if is_undefined(resultPartitions[partitionMapKeyStr]) {
499
+ slicingParamsForThisPartition := []
500
+ for i, axisIdx in axesToPartitionBy {
501
+ slicingParamsForThisPartition = append(slicingParamsForThisPartition, [axisIdx, currentPartitionTuple[i]])
502
+ }
503
+ resultPartitions[partitionMapKeyStr] = self.filter(slicingParamsForThisPartition)
504
+ }
505
+ }
506
+ return resultPartitions
507
+ },
508
+
509
+ forEach: func(userCallback) {
510
+ if !state.parsed {
511
+ ll.panic("forEach: resource is not parsed.")
512
+ }
513
+ if !is_callable(userCallback) {
514
+ ll.panic("forEach: provided argument is not a function.")
515
+ }
516
+
517
+ maps.forEach(state.data, func(keyStr, value) {
518
+ decodedKey := _decodeKeyString(keyStr)
519
+ userCallback(decodedKey, value)
520
+ })
521
+ },
522
+
523
+ flatten: func() {
524
+ if !state.parsed {
525
+ ll.panic("flatten: resource is not parsed.")
526
+ }
527
+
528
+ isJsonSuper := state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED
529
+ isBinarySuper := state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED
530
+ isResourceMapSuper := state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP_SUPER_PARTITIONED
531
+
532
+ if !(isJsonSuper || isBinarySuper || isResourceMapSuper) {
533
+ return self // Not a type that can be flattened
534
+ }
535
+
536
+ finalResourceType := undefined
537
+ usePartitionKeyLengthForFinal := false
538
+ if isJsonSuper {
539
+ finalResourceType = pConstants.RTYPE_P_COLUMN_DATA_JSON_PARTITIONED
540
+ usePartitionKeyLengthForFinal = true
541
+ } else if isBinarySuper {
542
+ finalResourceType = pConstants.RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED
543
+ usePartitionKeyLengthForFinal = true
544
+ } else { // isResourceMapSuper
545
+ finalResourceType = pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP
546
+
547
+ }
548
+
549
+ newCombinedKeyLength := -1
550
+ if usePartitionKeyLengthForFinal {
551
+ if is_undefined(state.superPartitionKeyLength) || is_undefined(state.partitionKeyLength) {
552
+ ll.panic(
553
+ "Flatten setup error: superPartitionKeyLength (%v) or partitionKeyLength (%v) is undefined for super-type %s. These are needed to determine combined key length.",
554
+ state.superPartitionKeyLength, state.partitionKeyLength, state.resourceType
555
+ )
556
+ }
557
+ newCombinedKeyLength = state.superPartitionKeyLength + state.partitionKeyLength
558
+ } else {
559
+ if is_undefined(state.partitionKeyLength) || is_undefined(state.keyLength) {
560
+ ll.panic(
561
+ "Flatten setup error: partitionKeyLength (%v) or keyLength (%v) is undefined for super-type %s. These are needed to determine combined key length.",
562
+ state.partitionKeyLength, state.keyLength, state.resourceType
563
+ )
564
+ }
565
+ newCombinedKeyLength = state.partitionKeyLength + state.keyLength
566
+ }
567
+
568
+ newData := {}
569
+
570
+ self.forEach(func(superKeyArray, nestedPCDInstance) {
571
+ if !nestedPCDInstance.parsed {
572
+ ll.panic("Flatten error: Nested PCD for superKey '%s' (superType %s) is not parsed or missing resourceType.", superKeyArray, state.resourceType)
573
+ }
574
+ if nestedPCDInstance.getResourceType() != finalResourceType {
575
+ ll.panic("Flatten error: For superKey '%s' (superType %s), expected nested type %s, got %s.",
576
+ superKeyArray, state.resourceType, finalResourceType, nestedPCDInstance.getResourceType())
577
+ }
578
+
579
+ if !usePartitionKeyLengthForFinal { // Nested is ResourceMap
580
+ if is_undefined(nestedPCDInstance.keyLength) {
581
+ ll.panic("Flatten error: Nested ResourceMap PCD for superKey '%s' (superType %s) is missing its 'keyLength'.",
582
+ superKeyArray, state.resourceType)
583
+ }
584
+ if nestedPCDInstance.keyLength != state.keyLength {
585
+ ll.panic("Flatten error: Nested ResourceMap PCD for superKey '%s' (superType %s) has keyLength %d, expected %d.",
586
+ superKeyArray, state.resourceType, nestedPCDInstance.keyLength, state.keyLength)
587
+ }
588
+ } else { // Nested is JsonPartitioned or BinaryPartitioned
589
+ if is_undefined(nestedPCDInstance.partitionKeyLength) {
590
+ ll.panic("Flatten error: Nested Partitioned PCD for superKey '%s' (superType %s) is missing its 'partitionKeyLength'.",
591
+ superKeyArray, state.resourceType)
592
+ }
593
+ if nestedPCDInstance.partitionKeyLength != state.partitionKeyLength {
594
+ ll.panic("Flatten error: Nested Partitioned PCD for superKey '%s' (superType %s) has partitionKeyLength %d, expected %d.",
595
+ superKeyArray, state.resourceType, nestedPCDInstance.partitionKeyLength, state.partitionKeyLength)
596
+ }
597
+ }
598
+
599
+ nestedPCDInstance.forEach(func(nestedKeyArray, valueOrParts) {
600
+ combinedKeyArray := superKeyArray + nestedKeyArray
601
+ newData[string(json.encode(combinedKeyArray))] = valueOrParts
602
+ })
603
+ })
604
+
605
+ newInstanceOpts := {
606
+ parsed: true,
607
+ resourceType: finalResourceType,
608
+ relevantTopLayerKeyLength: newCombinedKeyLength,
609
+ data: newData,
610
+ spec: state.spec // User's comment update incorporated
611
+ }
612
+
613
+ if usePartitionKeyLengthForFinal {
614
+ newInstanceOpts.partitionKeyLength = newCombinedKeyLength
615
+ } else { // For ResourceMap, the length field is 'keyLength'
616
+ newInstanceOpts.keyLength = newCombinedKeyLength
617
+ }
618
+
619
+ return _createPColumnDataInstance(newInstanceOpts)
620
+ }
621
+ })
622
+
623
+ return self
624
+ }
625
+
626
+ export ll.toStrict({
627
+ parseData: parseData
628
+ })
@@ -14,6 +14,7 @@ pSpec := import("@platforma-sdk/workflow-tengo:pframes.spec")
14
14
  validation := import("@platforma-sdk/workflow-tengo:validation")
15
15
  constants := import("@platforma-sdk/workflow-tengo:pframes.constants")
16
16
  xsvBuilder := import("@platforma-sdk/workflow-tengo:pframes.xsv-builder")
17
+ pColumnData := import("@platforma-sdk/workflow-tengo:pframes.data")
17
18
 
18
19
  llProcessTpl := assets.importTemplate("@platforma-sdk/workflow-tengo:pframes.process-pcolumn-data")
19
20
 
@@ -607,6 +608,7 @@ processColumn := func(input, bodyTpl, outputs, ...opts) {
607
608
  }
608
609
 
609
610
  export ll.toStrict({
611
+ parseData: pColumnData.parseData,
610
612
  processColumn: processColumn,
611
613
  exportFrame: exportFrame,
612
614
  pFrameBuilder: builder.pFrameBuilder,
@@ -366,7 +366,10 @@ resource = func(r) {
366
366
 
367
367
 
368
368
  info: func() {
369
- return tx.getResource(self.id)
369
+ _resourceInfo := tx.getResource(self.id)
370
+
371
+ _resourceInfo.Type = { Name: _resourceInfo.Type.Name, Version: _resourceInfo.Type.Version }
372
+ return _resourceInfo
370
373
  },
371
374
 
372
375
 
@@ -96,11 +96,11 @@ AWAIT_TEMPLATES := {
96
96
  ["spec", "ResourceReady"]
97
97
  ],
98
98
  "PColumnBundle": [
99
- [{ wildcard: "*" }, "ref", "ResourceReady"],
99
+ [{ wildcard: "*" }, { match: "^ref$" }, "ResourceReady"], // optional ref field
100
100
  [{ wildcard: "*" }, "spec", "ResourceReady"]
101
101
  ],
102
102
  "PColumnBundleWithPartitions": [
103
- [{ wildcard: "*" }, "ref", "ResourceReady"],
103
+ [{ wildcard: "*" }, { match: "^ref$" }, "ResourceReady"], // optional ref field
104
104
  [{ wildcard: "*" }, "spec", "ResourceReady"],
105
105
  [{ wildcard: "*" }, "data", "InputsLocked"]
106
106
  ]
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/workflow-tengo",
3
- "version": "4.6.1",
3
+ "version": "4.6.2",
4
4
  "description": "Tengo SDK library",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",