@platforma-sdk/workflow-tengo 4.6.0 → 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.
- package/dist/tengo/lib/pframes.constants.lib.tengo +2 -2
- package/dist/tengo/lib/pframes.data.lib.tengo +628 -0
- package/dist/tengo/lib/pframes.lib.tengo +2 -0
- package/dist/tengo/lib/pt.lib.tengo +23 -3
- package/dist/tengo/lib/smart.lib.tengo +4 -1
- package/dist/tengo/lib/tpl.base.lib.tengo +2 -2
- package/dist/tengo/tpl/check_network.create_workdir_from_storage.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.download_blob.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.run_hello_world.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.run_hello_world_py.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.upload_blob.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.upload_template.plj.gz +0 -0
- package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
- package/dist/tengo/tpl/exec.monetization_info.plj.gz +0 -0
- package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.venv-bin-path.plj.gz +0 -0
- package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-pframe-for-ui.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
- package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
- package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
- package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
- package/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
- 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
|
|
61
|
-
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,
|
|
@@ -141,6 +141,7 @@ workflow := func() {
|
|
|
141
141
|
finalFileName := undefined
|
|
142
142
|
finalXsvType := undefined
|
|
143
143
|
inferSchemaOpt := undefined
|
|
144
|
+
optionsSchema := undefined
|
|
144
145
|
|
|
145
146
|
isStructuralInput := is_map(frameInput) && !is_undefined(frameInput.file)
|
|
146
147
|
|
|
@@ -149,6 +150,9 @@ workflow := func() {
|
|
|
149
150
|
inputXsvType = frameInput.xsvType
|
|
150
151
|
|
|
151
152
|
if !is_undefined(frameInput.schema) {
|
|
153
|
+
if !is_array(frameInput.schema) {
|
|
154
|
+
ll.panic("frameInput.schema (for structural input) must be an array if provided. Got: %T", frameInput.schema)
|
|
155
|
+
}
|
|
152
156
|
inputSchema = frameInput.schema
|
|
153
157
|
}
|
|
154
158
|
|
|
@@ -190,6 +194,14 @@ workflow := func() {
|
|
|
190
194
|
inferSchemaOpt = opts.inferSchema
|
|
191
195
|
}
|
|
192
196
|
|
|
197
|
+
|
|
198
|
+
if !is_undefined(opts.schema) {
|
|
199
|
+
if !is_array(opts.schema) {
|
|
200
|
+
ll.panic("'schema' option (from options map) must be an array. Got: %T", opts.schema)
|
|
201
|
+
}
|
|
202
|
+
optionsSchema = opts.schema
|
|
203
|
+
}
|
|
204
|
+
|
|
193
205
|
if !is_undefined(fileRef) && !smart.isReference(fileRef) {
|
|
194
206
|
ll.panic("frameInput.file (for structural input) or frameInput (for direct reference) must be a valid resource reference. Got: %v", fileRef)
|
|
195
207
|
} else if !is_undefined(fileContent) && !is_string(fileContent) {
|
|
@@ -214,14 +226,22 @@ workflow := func() {
|
|
|
214
226
|
readCsvStep.delimiter = "\t"
|
|
215
227
|
}
|
|
216
228
|
|
|
217
|
-
|
|
218
|
-
|
|
229
|
+
|
|
230
|
+
finalSchemaToUse := undefined
|
|
231
|
+
if !is_undefined(optionsSchema) {
|
|
232
|
+
finalSchemaToUse = optionsSchema
|
|
233
|
+
} else {
|
|
234
|
+
finalSchemaToUse = inputSchema
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if !is_undefined(finalSchemaToUse) {
|
|
238
|
+
readCsvStep.schema = finalSchemaToUse
|
|
219
239
|
}
|
|
220
240
|
|
|
221
241
|
|
|
222
242
|
|
|
223
243
|
if !is_undefined(inferSchemaOpt) && inferSchemaOpt == false {
|
|
224
|
-
readCsvStep.
|
|
244
|
+
readCsvStep.inferSchema = false
|
|
225
245
|
}
|
|
226
246
|
|
|
227
247
|
self.addRawStep(readCsvStep)
|
|
@@ -366,7 +366,10 @@ resource = func(r) {
|
|
|
366
366
|
|
|
367
367
|
|
|
368
368
|
info: func() {
|
|
369
|
-
|
|
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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|