@milaboratories/pl-middle-layer 1.10.12
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/block_registry/index.d.ts +4 -0
- package/dist/block_registry/index.d.ts.map +1 -0
- package/dist/block_registry/registry.d.ts +37 -0
- package/dist/block_registry/registry.d.ts.map +1 -0
- package/dist/block_registry/registry_spec.d.ts +12 -0
- package/dist/block_registry/registry_spec.d.ts.map +1 -0
- package/dist/block_registry/watcher.d.ts +15 -0
- package/dist/block_registry/watcher.d.ts.map +1 -0
- package/dist/block_registry/well_known_registries.d.ts +4 -0
- package/dist/block_registry/well_known_registries.d.ts.map +1 -0
- package/dist/cfg_render/executor.d.ts +8 -0
- package/dist/cfg_render/executor.d.ts.map +1 -0
- package/dist/cfg_render/operation.d.ts +29 -0
- package/dist/cfg_render/operation.d.ts.map +1 -0
- package/dist/cfg_render/renderer.d.ts +6 -0
- package/dist/cfg_render/renderer.d.ts.map +1 -0
- package/dist/cfg_render/traverse.d.ts +3 -0
- package/dist/cfg_render/traverse.d.ts.map +1 -0
- package/dist/cfg_render/util.d.ts +5 -0
- package/dist/cfg_render/util.d.ts.map +1 -0
- package/dist/dev/index.d.ts +21 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/util.d.ts +3 -0
- package/dist/dev/util.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3587 -0
- package/dist/index.mjs.map +1 -0
- package/dist/js_render/context.d.ts +68 -0
- package/dist/js_render/context.d.ts.map +1 -0
- package/dist/js_render/index.d.ts +6 -0
- package/dist/js_render/index.d.ts.map +1 -0
- package/dist/middle_layer/active_cfg.d.ts +6 -0
- package/dist/middle_layer/active_cfg.d.ts.map +1 -0
- package/dist/middle_layer/block.d.ts +9 -0
- package/dist/middle_layer/block.d.ts.map +1 -0
- package/dist/middle_layer/block_ctx.d.ts +20 -0
- package/dist/middle_layer/block_ctx.d.ts.map +1 -0
- package/dist/middle_layer/block_ctx_unsafe.d.ts +16 -0
- package/dist/middle_layer/block_ctx_unsafe.d.ts.map +1 -0
- package/dist/middle_layer/driver_kit.d.ts +31 -0
- package/dist/middle_layer/driver_kit.d.ts.map +1 -0
- package/dist/middle_layer/frontend_path.d.ts +6 -0
- package/dist/middle_layer/frontend_path.d.ts.map +1 -0
- package/dist/middle_layer/index.d.ts +5 -0
- package/dist/middle_layer/index.d.ts.map +1 -0
- package/dist/middle_layer/middle_layer.d.ts +78 -0
- package/dist/middle_layer/middle_layer.d.ts.map +1 -0
- package/dist/middle_layer/navigation_states.d.ts +10 -0
- package/dist/middle_layer/navigation_states.d.ts.map +1 -0
- package/dist/middle_layer/ops.d.ts +64 -0
- package/dist/middle_layer/ops.d.ts.map +1 -0
- package/dist/middle_layer/project.d.ts +110 -0
- package/dist/middle_layer/project.d.ts.map +1 -0
- package/dist/middle_layer/project_list.d.ts +11 -0
- package/dist/middle_layer/project_list.d.ts.map +1 -0
- package/dist/middle_layer/project_overview.d.ts +8 -0
- package/dist/middle_layer/project_overview.d.ts.map +1 -0
- package/dist/middle_layer/render.d.ts +6 -0
- package/dist/middle_layer/render.d.ts.map +1 -0
- package/dist/middle_layer/types.d.ts +11 -0
- package/dist/middle_layer/types.d.ts.map +1 -0
- package/dist/middle_layer/util.d.ts +3 -0
- package/dist/middle_layer/util.d.ts.map +1 -0
- package/dist/model/args.d.ts +12 -0
- package/dist/model/args.d.ts.map +1 -0
- package/dist/model/block_pack.d.ts +8 -0
- package/dist/model/block_pack.d.ts.map +1 -0
- package/dist/model/block_pack_spec.d.ts +40 -0
- package/dist/model/block_pack_spec.d.ts.map +1 -0
- package/dist/model/frontend.d.ts +10 -0
- package/dist/model/frontend.d.ts.map +1 -0
- package/dist/model/index.d.ts +3 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/project_model.d.ts +67 -0
- package/dist/model/project_model.d.ts.map +1 -0
- package/dist/model/project_model_util.d.ts +29 -0
- package/dist/model/project_model_util.d.ts.map +1 -0
- package/dist/model/template_spec.d.ts +16 -0
- package/dist/model/template_spec.d.ts.map +1 -0
- package/dist/mutator/block-pack/block_pack.d.ts +17 -0
- package/dist/mutator/block-pack/block_pack.d.ts.map +1 -0
- package/dist/mutator/block-pack/frontend.d.ts +4 -0
- package/dist/mutator/block-pack/frontend.d.ts.map +1 -0
- package/dist/mutator/context_export.d.ts +9 -0
- package/dist/mutator/context_export.d.ts.map +1 -0
- package/dist/mutator/project.d.ts +121 -0
- package/dist/mutator/project.d.ts.map +1 -0
- package/dist/mutator/template/render_block.d.ts +32 -0
- package/dist/mutator/template/render_block.d.ts.map +1 -0
- package/dist/mutator/template/render_template.d.ts +12 -0
- package/dist/mutator/template/render_template.d.ts.map +1 -0
- package/dist/mutator/template/template_loading.d.ts +13 -0
- package/dist/mutator/template/template_loading.d.ts.map +1 -0
- package/dist/pool/data.d.ts +24 -0
- package/dist/pool/data.d.ts.map +1 -0
- package/dist/pool/driver.d.ts +22 -0
- package/dist/pool/driver.d.ts.map +1 -0
- package/dist/pool/index.d.ts +3 -0
- package/dist/pool/index.d.ts.map +1 -0
- package/dist/pool/p_object_collection.d.ts +29 -0
- package/dist/pool/p_object_collection.d.ts.map +1 -0
- package/dist/pool/ref_count_pool.d.ts +25 -0
- package/dist/pool/ref_count_pool.d.ts.map +1 -0
- package/dist/pool/result_pool.d.ts +25 -0
- package/dist/pool/result_pool.d.ts.map +1 -0
- package/dist/test/block_packs.d.ts +6 -0
- package/dist/test/block_packs.d.ts.map +1 -0
- package/dist/test/explicit_templates.d.ts +3 -0
- package/dist/test/explicit_templates.d.ts.map +1 -0
- package/dist/test/known_templates.d.ts +6 -0
- package/dist/test/known_templates.d.ts.map +1 -0
- package/package.json +55 -0
- package/src/block_registry/index.ts +3 -0
- package/src/block_registry/registry.test.ts +35 -0
- package/src/block_registry/registry.ts +180 -0
- package/src/block_registry/registry_spec.ts +13 -0
- package/src/block_registry/watcher.ts +72 -0
- package/src/block_registry/well_known_registries.ts +13 -0
- package/src/cfg_render/executor.test.ts +120 -0
- package/src/cfg_render/executor.ts +253 -0
- package/src/cfg_render/operation.ts +38 -0
- package/src/cfg_render/renderer.ts +540 -0
- package/src/cfg_render/traverse.ts +58 -0
- package/src/cfg_render/util.ts +29 -0
- package/src/dev/index.ts +89 -0
- package/src/dev/util.ts +13 -0
- package/src/index.ts +21 -0
- package/src/js_render/context.ts +768 -0
- package/src/js_render/index.ts +41 -0
- package/src/middle_layer/active_cfg.ts +56 -0
- package/src/middle_layer/block.ts +70 -0
- package/src/middle_layer/block_ctx.ts +90 -0
- package/src/middle_layer/block_ctx_unsafe.ts +29 -0
- package/src/middle_layer/driver_kit.ts +107 -0
- package/src/middle_layer/frontend_path.ts +83 -0
- package/src/middle_layer/index.ts +4 -0
- package/src/middle_layer/middle_layer.test.ts +720 -0
- package/src/middle_layer/middle_layer.ts +235 -0
- package/src/middle_layer/navigation_states.ts +48 -0
- package/src/middle_layer/ops.ts +147 -0
- package/src/middle_layer/project.ts +380 -0
- package/src/middle_layer/project_list.ts +59 -0
- package/src/middle_layer/project_overview.ts +220 -0
- package/src/middle_layer/render.test.ts +129 -0
- package/src/middle_layer/render.ts +19 -0
- package/src/middle_layer/types.ts +16 -0
- package/src/middle_layer/util.ts +22 -0
- package/src/model/args.ts +62 -0
- package/src/model/block_pack.ts +8 -0
- package/src/model/block_pack_spec.ts +52 -0
- package/src/model/frontend.ts +10 -0
- package/src/model/index.ts +2 -0
- package/src/model/project_model.test.ts +26 -0
- package/src/model/project_model.ts +142 -0
- package/src/model/project_model_util.test.ts +88 -0
- package/src/model/project_model_util.ts +169 -0
- package/src/model/template_spec.ts +18 -0
- package/src/mutator/block-pack/block_pack.test.ts +53 -0
- package/src/mutator/block-pack/block_pack.ts +187 -0
- package/src/mutator/block-pack/frontend.ts +29 -0
- package/src/mutator/context_export.ts +25 -0
- package/src/mutator/project.test.ts +272 -0
- package/src/mutator/project.ts +1112 -0
- package/src/mutator/template/render_block.ts +91 -0
- package/src/mutator/template/render_template.ts +40 -0
- package/src/mutator/template/template_loading.ts +77 -0
- package/src/mutator/template/template_render.test.ts +272 -0
- package/src/pool/data.ts +239 -0
- package/src/pool/driver.ts +325 -0
- package/src/pool/index.ts +2 -0
- package/src/pool/p_object_collection.ts +122 -0
- package/src/pool/ref_count_pool.ts +76 -0
- package/src/pool/result_pool.ts +284 -0
- package/src/test/block_packs.ts +23 -0
- package/src/test/explicit_templates.ts +8 -0
- package/src/test/known_templates.ts +24 -0
package/src/pool/data.ts
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { PObjectId, PObjectSpec } from '@platforma-sdk/model';
|
|
2
|
+
import { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
|
|
3
|
+
import { PlTreeNodeAccessor, ResourceInfo } from '@milaboratories/pl-tree';
|
|
4
|
+
import { assertNever } from '@milaboratories/ts-helpers';
|
|
5
|
+
import { createHash } from 'crypto';
|
|
6
|
+
import canonicalize from 'canonicalize';
|
|
7
|
+
import {
|
|
8
|
+
isNullResourceId,
|
|
9
|
+
resourceType,
|
|
10
|
+
resourceTypeToString,
|
|
11
|
+
resourceTypesEqual
|
|
12
|
+
} from '@milaboratories/pl-client';
|
|
13
|
+
import { Writable } from 'utility-types';
|
|
14
|
+
|
|
15
|
+
export function* allBlobs<B>(data: PFrameInternal.DataInfo<B>): Generator<B> {
|
|
16
|
+
switch (data.type) {
|
|
17
|
+
case 'Json':
|
|
18
|
+
return;
|
|
19
|
+
case 'JsonPartitioned':
|
|
20
|
+
for (const [, blob] of Object.entries(data.parts)) yield blob;
|
|
21
|
+
return;
|
|
22
|
+
case 'BinaryPartitioned':
|
|
23
|
+
for (const [, { index, values }] of Object.entries(data.parts)) {
|
|
24
|
+
yield index;
|
|
25
|
+
yield values;
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
default:
|
|
29
|
+
assertNever(data);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function mapValues<T extends object, TResult>(
|
|
34
|
+
obj: T,
|
|
35
|
+
callback: (v: T[keyof T], key: keyof T) => TResult
|
|
36
|
+
): { [P in keyof T]: TResult } {
|
|
37
|
+
return Object.fromEntries(
|
|
38
|
+
Object.entries(obj).map(([key, value]) => [key, callback(value, key as any)])
|
|
39
|
+
) as any;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function mapBlobs<B1, B2>(
|
|
43
|
+
data: PFrameInternal.DataInfo<B1>,
|
|
44
|
+
mapping: (blob: B1) => B2
|
|
45
|
+
): PFrameInternal.DataInfo<B2> {
|
|
46
|
+
switch (data.type) {
|
|
47
|
+
case 'Json':
|
|
48
|
+
return { ...data };
|
|
49
|
+
case 'JsonPartitioned':
|
|
50
|
+
return { ...data, parts: mapValues(data.parts, mapping) };
|
|
51
|
+
case 'BinaryPartitioned':
|
|
52
|
+
return {
|
|
53
|
+
...data,
|
|
54
|
+
parts: mapValues(data.parts, (v) => ({
|
|
55
|
+
index: mapping(v.index),
|
|
56
|
+
values: mapping(v.values)
|
|
57
|
+
}))
|
|
58
|
+
};
|
|
59
|
+
default:
|
|
60
|
+
assertNever(data);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const PColumnDataJsonPartitioned = resourceType('PColumnData/JsonPartitioned', '1');
|
|
65
|
+
export const PColumnDataJsonSuperPartitioned = resourceType(
|
|
66
|
+
'PColumnData/Partitioned/JsonPartitioned',
|
|
67
|
+
'1'
|
|
68
|
+
);
|
|
69
|
+
export const PColumnDataBinaryPartitioned = resourceType('PColumnData/BinaryPartitioned', '1');
|
|
70
|
+
export const PColumnDataBinarySuperPartitioned = resourceType(
|
|
71
|
+
'PColumnData/Partitioned/BinaryPartitioned',
|
|
72
|
+
'1'
|
|
73
|
+
);
|
|
74
|
+
export const PColumnDataJson = resourceType('PColumnData/Json', '1');
|
|
75
|
+
|
|
76
|
+
export type PColumnDataJsonResourceValue = {
|
|
77
|
+
keyLength: number;
|
|
78
|
+
data: Record<string, PFrameInternal.JsonDataValue>;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type PColumnDataPartitionedResourceValue = {
|
|
82
|
+
partitionKeyLength: number;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export type PColumnDataSuperPartitionedResourceValue = {
|
|
86
|
+
superPartitionKeyLength: number;
|
|
87
|
+
partitionKeyLength: number;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export function parseDataInfoResource(
|
|
91
|
+
data: PlTreeNodeAccessor
|
|
92
|
+
): PFrameInternal.DataInfo<ResourceInfo> {
|
|
93
|
+
if (!data.getIsReadyOrError()) throw new Error('Data not ready.');
|
|
94
|
+
|
|
95
|
+
const resourceData = data.getDataAsJson();
|
|
96
|
+
if (resourceData === undefined)
|
|
97
|
+
throw new Error('unexpected data info structure, no resource data');
|
|
98
|
+
|
|
99
|
+
if (resourceTypesEqual(data.resourceType, PColumnDataJson)) {
|
|
100
|
+
const dataContent = resourceData as PColumnDataJsonResourceValue;
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
type: 'Json',
|
|
104
|
+
keyLength: dataContent.keyLength,
|
|
105
|
+
data: dataContent.data
|
|
106
|
+
};
|
|
107
|
+
} else if (resourceTypesEqual(data.resourceType, PColumnDataJsonPartitioned)) {
|
|
108
|
+
const meta = resourceData as PColumnDataPartitionedResourceValue;
|
|
109
|
+
|
|
110
|
+
const parts = Object.fromEntries(
|
|
111
|
+
data
|
|
112
|
+
.listInputFields()
|
|
113
|
+
.map((field) => [field, data.traverse({ field, errorIfFieldNotSet: true }).resourceInfo])
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
type: 'JsonPartitioned',
|
|
118
|
+
partitionKeyLength: meta.partitionKeyLength,
|
|
119
|
+
parts
|
|
120
|
+
};
|
|
121
|
+
} else if (resourceTypesEqual(data.resourceType, PColumnDataJsonSuperPartitioned)) {
|
|
122
|
+
const meta = resourceData as PColumnDataSuperPartitionedResourceValue;
|
|
123
|
+
|
|
124
|
+
const parts: Record<string, ResourceInfo> = {};
|
|
125
|
+
for (const superKey of data.listInputFields()) {
|
|
126
|
+
const superPart = data.traverse({ field: superKey, errorIfFieldNotSet: true });
|
|
127
|
+
const keys = superPart.listInputFields();
|
|
128
|
+
if (keys === undefined) throw new Error(`no partition keys for super key ${superKey}`);
|
|
129
|
+
|
|
130
|
+
for (const key of keys) {
|
|
131
|
+
const partKey = superKey.slice(0, superKey.length - 1) + ',' + key.slice(1, key.length);
|
|
132
|
+
parts[partKey] = superPart.traverse({ field: key, errorIfFieldNotSet: true }).resourceInfo;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
type: 'JsonPartitioned',
|
|
138
|
+
partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,
|
|
139
|
+
parts
|
|
140
|
+
};
|
|
141
|
+
} else if (resourceTypesEqual(data.resourceType, PColumnDataBinaryPartitioned)) {
|
|
142
|
+
const meta = resourceData as PColumnDataPartitionedResourceValue;
|
|
143
|
+
|
|
144
|
+
const parts: Record<
|
|
145
|
+
string,
|
|
146
|
+
Partial<Writable<PFrameInternal.BinaryChunkInfo<ResourceInfo>>>
|
|
147
|
+
> = {};
|
|
148
|
+
|
|
149
|
+
// parsing the structure
|
|
150
|
+
for (const field of data.listInputFields()) {
|
|
151
|
+
if (field.endsWith('.index')) {
|
|
152
|
+
const partKey = field.slice(0, field.length - 6);
|
|
153
|
+
let part = parts[partKey];
|
|
154
|
+
if (part === undefined) {
|
|
155
|
+
part = {};
|
|
156
|
+
parts[partKey] = part;
|
|
157
|
+
}
|
|
158
|
+
part.index = data.traverse({ field, errorIfFieldNotSet: true }).resourceInfo;
|
|
159
|
+
} else if (field.endsWith('.values')) {
|
|
160
|
+
const partKey = field.slice(0, field.length - 7);
|
|
161
|
+
let part = parts[partKey];
|
|
162
|
+
if (part === undefined) {
|
|
163
|
+
part = {};
|
|
164
|
+
parts[partKey] = part;
|
|
165
|
+
}
|
|
166
|
+
part.values = data.traverse({ field, errorIfFieldNotSet: true }).resourceInfo;
|
|
167
|
+
} else throw new Error(`unrecognized part field name: ${field}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// structure validation
|
|
171
|
+
for (const [key, part] of Object.entries(parts)) {
|
|
172
|
+
if (part.index === undefined) throw new Error(`no index for part ${key}`);
|
|
173
|
+
if (part.values === undefined) throw new Error(`no values for part ${key}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
type: 'BinaryPartitioned',
|
|
178
|
+
partitionKeyLength: meta.partitionKeyLength,
|
|
179
|
+
parts: parts as Record<string, PFrameInternal.BinaryChunkInfo<ResourceInfo>>
|
|
180
|
+
};
|
|
181
|
+
} else if (resourceTypesEqual(data.resourceType, PColumnDataBinarySuperPartitioned)) {
|
|
182
|
+
const meta = resourceData as PColumnDataSuperPartitionedResourceValue;
|
|
183
|
+
|
|
184
|
+
const parts: Record<
|
|
185
|
+
string,
|
|
186
|
+
Partial<Writable<PFrameInternal.BinaryChunkInfo<ResourceInfo>>>
|
|
187
|
+
> = {};
|
|
188
|
+
for (const superKey of data.listInputFields()) {
|
|
189
|
+
const superData = data.traverse({ field: superKey, errorIfFieldNotSet: true });
|
|
190
|
+
const keys = superData.listInputFields();
|
|
191
|
+
if (keys === undefined) throw new Error(`no partition keys for super key ${superKey}`);
|
|
192
|
+
|
|
193
|
+
for (const field of keys) {
|
|
194
|
+
if (field.endsWith('.index')) {
|
|
195
|
+
const key = field.slice(0, field.length - 6);
|
|
196
|
+
|
|
197
|
+
const partKey = superKey.slice(0, superKey.length - 1) + ',' + key.slice(1, key.length);
|
|
198
|
+
let part = parts[partKey];
|
|
199
|
+
if (part === undefined) {
|
|
200
|
+
part = {};
|
|
201
|
+
parts[partKey] = part;
|
|
202
|
+
}
|
|
203
|
+
parts[partKey].index = superData.traverse({
|
|
204
|
+
field,
|
|
205
|
+
errorIfFieldNotSet: true
|
|
206
|
+
}).resourceInfo;
|
|
207
|
+
} else if (field.endsWith('.values')) {
|
|
208
|
+
const key = field.slice(0, field.length - 7);
|
|
209
|
+
|
|
210
|
+
const partKey = superKey.slice(0, superKey.length - 1) + ',' + key.slice(1, key.length);
|
|
211
|
+
let part = parts[partKey];
|
|
212
|
+
if (part === undefined) {
|
|
213
|
+
part = {};
|
|
214
|
+
parts[partKey] = part;
|
|
215
|
+
}
|
|
216
|
+
parts[partKey].values = superData.traverse({
|
|
217
|
+
field,
|
|
218
|
+
errorIfFieldNotSet: true
|
|
219
|
+
}).resourceInfo;
|
|
220
|
+
} else throw new Error(`unrecognized part field name: ${field}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
type: 'BinaryPartitioned',
|
|
226
|
+
partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,
|
|
227
|
+
parts: parts as Record<string, PFrameInternal.BinaryChunkInfo<ResourceInfo>>
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
throw new Error(`unsupported resource type: ${resourceTypeToString(data.resourceType)}`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function derivePObjectId(spec: PObjectSpec, data: PlTreeNodeAccessor): PObjectId {
|
|
235
|
+
const hash = createHash('sha256');
|
|
236
|
+
hash.update(canonicalize(spec)!);
|
|
237
|
+
hash.update(String(!isNullResourceId(data.originalId) ? data.originalId : data.id));
|
|
238
|
+
return hash.digest().toString('hex') as PObjectId;
|
|
239
|
+
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { DownloadDriver } from '@milaboratories/pl-drivers';
|
|
2
|
+
import type PFramesType from '@milaboratories/pframes-node';
|
|
3
|
+
import { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
|
|
4
|
+
import { PlTreeNodeAccessor, ResourceInfo } from '@milaboratories/pl-tree';
|
|
5
|
+
import { ComputableCtx, ComputableStableDefined } from '@milaboratories/computable';
|
|
6
|
+
import {
|
|
7
|
+
CalculateTableDataRequest,
|
|
8
|
+
CalculateTableDataResponse,
|
|
9
|
+
FindColumnsRequest,
|
|
10
|
+
FindColumnsResponse,
|
|
11
|
+
LocalBlobHandleAndSize,
|
|
12
|
+
PColumnIdAndSpec,
|
|
13
|
+
PColumnSpec,
|
|
14
|
+
PFrameHandle,
|
|
15
|
+
PObjectId,
|
|
16
|
+
PTableColumnSpec,
|
|
17
|
+
PTableHandle,
|
|
18
|
+
PTableShape,
|
|
19
|
+
PTableVector,
|
|
20
|
+
TableRange,
|
|
21
|
+
UniqueValuesRequest,
|
|
22
|
+
UniqueValuesResponse,
|
|
23
|
+
PFrameDriver as SdkPFrameDriver,
|
|
24
|
+
PColumn,
|
|
25
|
+
mapPObjectData,
|
|
26
|
+
PFrameDef,
|
|
27
|
+
JoinEntry,
|
|
28
|
+
PTableDef,
|
|
29
|
+
mapPTableDef
|
|
30
|
+
} from '@platforma-sdk/model';
|
|
31
|
+
import { RefCountResourcePool } from './ref_count_pool';
|
|
32
|
+
import { allBlobs, mapBlobs, parseDataInfoResource } from './data';
|
|
33
|
+
import { createHash } from 'crypto';
|
|
34
|
+
import { assertNever } from '@milaboratories/ts-helpers';
|
|
35
|
+
import canonicalize from 'canonicalize';
|
|
36
|
+
|
|
37
|
+
// special way of importing native node module
|
|
38
|
+
const PFrames: PFramesType = require('@milaboratories/pframes-node');
|
|
39
|
+
|
|
40
|
+
function blobKey(res: ResourceInfo): string {
|
|
41
|
+
return String(res.id);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type InternalPFrameData = PFrameDef<PFrameInternal.DataInfo<ResourceInfo>>;
|
|
45
|
+
|
|
46
|
+
class PFrameHolder implements PFrameInternal.PFrameDataSource, Disposable {
|
|
47
|
+
public readonly pFrame: PFrameInternal.PFrame = new PFrames.PFrame();
|
|
48
|
+
private readonly blobIdToResource = new Map<string, ResourceInfo>();
|
|
49
|
+
private readonly blobHandleComputables = new Map<
|
|
50
|
+
string,
|
|
51
|
+
ComputableStableDefined<LocalBlobHandleAndSize>
|
|
52
|
+
>();
|
|
53
|
+
|
|
54
|
+
constructor(
|
|
55
|
+
private readonly blobDriver: DownloadDriver,
|
|
56
|
+
private readonly columns: InternalPFrameData
|
|
57
|
+
) {
|
|
58
|
+
// pframe initialization
|
|
59
|
+
this.pFrame.setDataSource(this);
|
|
60
|
+
for (const column of columns) {
|
|
61
|
+
for (const blob of allBlobs(column.data)) this.blobIdToResource.set(blobKey(blob), blob);
|
|
62
|
+
const dataInfo = mapBlobs(column.data, blobKey);
|
|
63
|
+
try {
|
|
64
|
+
this.pFrame.addColumnSpec(column.id, column.spec);
|
|
65
|
+
this.pFrame.setColumnData(column.id, dataInfo);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Adding column ${column.id} to PFrame failed: ${err}; Spec: ${column.spec}, DataInfo: ${dataInfo}.`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private getOrCreateComputableForBlob(blobId: string) {
|
|
75
|
+
let computable = this.blobHandleComputables.get(blobId);
|
|
76
|
+
if (computable !== undefined) return computable;
|
|
77
|
+
|
|
78
|
+
const blobResource = this.blobIdToResource.get(blobId);
|
|
79
|
+
if (blobResource === undefined) throw new Error(`Blob with id ${blobId} not found.`);
|
|
80
|
+
|
|
81
|
+
// precalculation of value tree will trigger the download proecess right away
|
|
82
|
+
computable = this.blobDriver.getDownloadedBlob(blobResource).withPreCalculatedValueTree();
|
|
83
|
+
|
|
84
|
+
this.blobHandleComputables.set(blobId, computable);
|
|
85
|
+
|
|
86
|
+
return computable;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public readonly preloadBlob = async (blobIds: string[]): Promise<void> => {
|
|
90
|
+
const computables = blobIds.map((blobId) => this.getOrCreateComputableForBlob(blobId));
|
|
91
|
+
for (const computable of computables) await computable.awaitStableFullValue();
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
public readonly resolveBlob = async (blobId: string): Promise<string> => {
|
|
95
|
+
const computable = this.getOrCreateComputableForBlob(blobId);
|
|
96
|
+
return this.blobDriver.getLocalPath((await computable.awaitStableValue()).handle);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
[Symbol.dispose](): void {
|
|
100
|
+
for (const computable of this.blobHandleComputables.values()) computable.resetState();
|
|
101
|
+
this.pFrame.dispose();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
type FullPTableDef = {
|
|
106
|
+
pFrameHandle: PFrameHandle;
|
|
107
|
+
def: PTableDef<PObjectId>;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export class PFrameDriver implements SdkPFrameDriver {
|
|
111
|
+
private readonly pFrames: RefCountResourcePool<InternalPFrameData, PFrameHolder>;
|
|
112
|
+
private readonly pTables: RefCountResourcePool<FullPTableDef, Promise<PFrameInternal.PTable>>;
|
|
113
|
+
|
|
114
|
+
constructor(private readonly blobDriver: DownloadDriver) {
|
|
115
|
+
this.pFrames = new (class extends RefCountResourcePool<InternalPFrameData, PFrameHolder> {
|
|
116
|
+
constructor(private readonly blobDriver: DownloadDriver) {
|
|
117
|
+
super();
|
|
118
|
+
}
|
|
119
|
+
protected createNewResource(params: InternalPFrameData): PFrameHolder {
|
|
120
|
+
return new PFrameHolder(this.blobDriver, params);
|
|
121
|
+
}
|
|
122
|
+
protected calculateParamsKey(params: InternalPFrameData): string {
|
|
123
|
+
return stableKeyFromPFrameData(params);
|
|
124
|
+
}
|
|
125
|
+
})(this.blobDriver);
|
|
126
|
+
|
|
127
|
+
this.pTables = new (class extends RefCountResourcePool<
|
|
128
|
+
FullPTableDef,
|
|
129
|
+
Promise<PFrameInternal.PTable>
|
|
130
|
+
> {
|
|
131
|
+
constructor(
|
|
132
|
+
private readonly pFrames: RefCountResourcePool<InternalPFrameData, PFrameHolder>
|
|
133
|
+
) {
|
|
134
|
+
super();
|
|
135
|
+
}
|
|
136
|
+
protected async createNewResource(params: FullPTableDef): Promise<PFrameInternal.PTable> {
|
|
137
|
+
const pFrame = this.pFrames.getByKey(params.pFrameHandle);
|
|
138
|
+
const rawPTable = await pFrame.pFrame.createTable({
|
|
139
|
+
src: joinEntryToInternal(params.def.src),
|
|
140
|
+
filters: params.def.filters
|
|
141
|
+
});
|
|
142
|
+
return params.def.sorting.length !== 0 ? rawPTable.sort(params.def.sorting) : rawPTable;
|
|
143
|
+
}
|
|
144
|
+
protected calculateParamsKey(params: FullPTableDef): string {
|
|
145
|
+
return stableKeyFromFullPTableDef(params);
|
|
146
|
+
}
|
|
147
|
+
})(this.pFrames);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
//
|
|
151
|
+
// Internal / Config API Methods
|
|
152
|
+
//
|
|
153
|
+
|
|
154
|
+
public createPFrame(def: PFrameDef<PlTreeNodeAccessor>, ctx: ComputableCtx): PFrameHandle {
|
|
155
|
+
const internalData = def.map((c) => mapPObjectData(c, (d) => parseDataInfoResource(d)));
|
|
156
|
+
const res = this.pFrames.acquire(internalData);
|
|
157
|
+
ctx.addOnDestroy(res.unref);
|
|
158
|
+
return res.key as PFrameHandle;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
public createPTable(
|
|
162
|
+
def: PTableDef<PColumn<PlTreeNodeAccessor>>,
|
|
163
|
+
ctx: ComputableCtx
|
|
164
|
+
): PTableHandle {
|
|
165
|
+
const pFrameHandle = this.createPFrame(extractAllColumns(def.src), ctx);
|
|
166
|
+
const defIds = mapPTableDef(def, (c) => c.id);
|
|
167
|
+
const res = this.pTables.acquire({ def: defIds, pFrameHandle });
|
|
168
|
+
ctx.addOnDestroy(res.unref); // in addition to pframe unref added in createPFrame above
|
|
169
|
+
return res.key as PTableHandle;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
//
|
|
173
|
+
// PFrame istance methods
|
|
174
|
+
//
|
|
175
|
+
|
|
176
|
+
public async findColumns(
|
|
177
|
+
handle: PFrameHandle,
|
|
178
|
+
request: FindColumnsRequest
|
|
179
|
+
): Promise<FindColumnsResponse> {
|
|
180
|
+
const iRequest: PFrameInternal.FindColumnsRequest = {
|
|
181
|
+
...request,
|
|
182
|
+
compatibleWith:
|
|
183
|
+
request.compatibleWith.length !== 0
|
|
184
|
+
? [{ axesSpec: request.compatibleWith, qualifications: [] }]
|
|
185
|
+
: []
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
hits: (await this.pFrames.getByKey(handle).pFrame.findColumns(iRequest)).hits.map(
|
|
189
|
+
(h) => h.hit
|
|
190
|
+
)
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
public async getColumnSpec(handle: PFrameHandle, columnId: PObjectId): Promise<PColumnSpec> {
|
|
195
|
+
return this.pFrames.getByKey(handle).pFrame.getColumnSpec(columnId);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
public async listColumns(handle: PFrameHandle): Promise<PColumnIdAndSpec[]> {
|
|
199
|
+
return this.pFrames.getByKey(handle).pFrame.listColumns();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
public async calculateTableData(
|
|
203
|
+
handle: PFrameHandle,
|
|
204
|
+
request: CalculateTableDataRequest<PObjectId>
|
|
205
|
+
): Promise<CalculateTableDataResponse> {
|
|
206
|
+
let table = await this.pFrames.getByKey(handle).pFrame.createTable({
|
|
207
|
+
src: joinEntryToInternal(request.src),
|
|
208
|
+
filters: request.filters
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if (request.sorting.length > 0) {
|
|
212
|
+
const sortedTable = await table.sort(request.sorting);
|
|
213
|
+
table.dispose();
|
|
214
|
+
table = sortedTable;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const spec = table.getSpec();
|
|
218
|
+
const data = await table.getData([...spec.keys()]);
|
|
219
|
+
table.dispose();
|
|
220
|
+
|
|
221
|
+
return spec.map((spec, i) => ({
|
|
222
|
+
spec: spec,
|
|
223
|
+
data: data[i]
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
public async getUniqueValues(
|
|
228
|
+
handle: PFrameHandle,
|
|
229
|
+
request: UniqueValuesRequest
|
|
230
|
+
): Promise<UniqueValuesResponse> {
|
|
231
|
+
return await this.pFrames.getByKey(handle).pFrame.getUniqueValues(request);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
//
|
|
235
|
+
// PTable istance methods
|
|
236
|
+
//
|
|
237
|
+
|
|
238
|
+
public async getShape(handle: PTableHandle): Promise<PTableShape> {
|
|
239
|
+
const pTable = await this.pTables.getByKey(handle);
|
|
240
|
+
return pTable.getShape();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
public async getSpec(handle: PTableHandle): Promise<PTableColumnSpec[]> {
|
|
244
|
+
const pTable = await this.pTables.getByKey(handle);
|
|
245
|
+
return pTable.getSpec();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
public async getData(
|
|
249
|
+
handle: PTableHandle,
|
|
250
|
+
columnIndices: number[],
|
|
251
|
+
range?: TableRange
|
|
252
|
+
): Promise<PTableVector[]> {
|
|
253
|
+
const pTable = await this.pTables.getByKey(handle);
|
|
254
|
+
return pTable.getData(columnIndices, range);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function joinEntryToInternal(entry: JoinEntry<PObjectId>): PFrameInternal.JoinEntry {
|
|
259
|
+
switch (entry.type) {
|
|
260
|
+
case 'column':
|
|
261
|
+
return {
|
|
262
|
+
type: 'column',
|
|
263
|
+
columnId: entry.column,
|
|
264
|
+
qualifications: []
|
|
265
|
+
};
|
|
266
|
+
case 'inner':
|
|
267
|
+
case 'full':
|
|
268
|
+
return {
|
|
269
|
+
type: entry.type,
|
|
270
|
+
entries: entry.entries.map((col) => joinEntryToInternal(col))
|
|
271
|
+
};
|
|
272
|
+
case 'outer':
|
|
273
|
+
return {
|
|
274
|
+
type: 'outer',
|
|
275
|
+
primary: joinEntryToInternal(entry.primary),
|
|
276
|
+
secondary: entry.secondary.map((col) => joinEntryToInternal(col))
|
|
277
|
+
};
|
|
278
|
+
default:
|
|
279
|
+
assertNever(entry);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function stableKeyFromFullPTableDef(data: FullPTableDef): string {
|
|
284
|
+
const hash = createHash('sha256');
|
|
285
|
+
hash.update(data.pFrameHandle);
|
|
286
|
+
hash.update(canonicalize(data.def)!);
|
|
287
|
+
return hash.digest().toString('hex');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function stableKeyFromPFrameData(data: PColumn<unknown>[]): string {
|
|
291
|
+
// PObject IDs derived from the PObjects canonical identity, so represents the content
|
|
292
|
+
const ids = data.map((d) => d.id).sort();
|
|
293
|
+
const hash = createHash('sha256');
|
|
294
|
+
let previous = '';
|
|
295
|
+
for (const id of ids) {
|
|
296
|
+
if (previous === id) continue; // only unique ids
|
|
297
|
+
hash.update(id);
|
|
298
|
+
previous = id;
|
|
299
|
+
}
|
|
300
|
+
return hash.digest().toString('hex');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function extractAllColumns<D>(entry: JoinEntry<PColumn<D>>): PFrameDef<D> {
|
|
304
|
+
const columns = new Map<PObjectId, PColumn<D>>();
|
|
305
|
+
addAllColumns(entry, columns);
|
|
306
|
+
return [...columns.values()];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function addAllColumns<D>(entry: JoinEntry<PColumn<D>>, map: Map<PObjectId, PColumn<D>>): void {
|
|
310
|
+
switch (entry.type) {
|
|
311
|
+
case 'column':
|
|
312
|
+
map.set(entry.column.id, entry.column);
|
|
313
|
+
return;
|
|
314
|
+
case 'full':
|
|
315
|
+
case 'inner':
|
|
316
|
+
for (const e of entry.entries) addAllColumns(e, map);
|
|
317
|
+
return;
|
|
318
|
+
case 'outer':
|
|
319
|
+
addAllColumns(entry.primary, map);
|
|
320
|
+
for (const e of entry.secondary) addAllColumns(e, map);
|
|
321
|
+
return;
|
|
322
|
+
default:
|
|
323
|
+
assertNever(entry);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { PlError, PlTreeNodeAccessor } from '@milaboratories/pl-tree';
|
|
2
|
+
import { PObject, PObjectSpec, ValueOrError } from '@platforma-sdk/model';
|
|
3
|
+
import { notEmpty } from '@milaboratories/ts-helpers';
|
|
4
|
+
import assert from 'assert';
|
|
5
|
+
import { Writable } from 'utility-types';
|
|
6
|
+
import { derivePObjectId } from './data';
|
|
7
|
+
|
|
8
|
+
/** Represents specific staging or prod ctx data */
|
|
9
|
+
export interface RawPObjectCollection {
|
|
10
|
+
/** true if no new results are expected */
|
|
11
|
+
readonly locked: boolean;
|
|
12
|
+
/** results by name */
|
|
13
|
+
readonly results: Map<string, RawPObjectEntry>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Single result in a particular ctx */
|
|
17
|
+
export interface RawPObjectEntry {
|
|
18
|
+
/**
|
|
19
|
+
* true - means this result has data field, however it may still be not ready
|
|
20
|
+
* false - means it can be derived that this result incarnation is spec-only
|
|
21
|
+
* undefined - means that it is not yet known
|
|
22
|
+
* */
|
|
23
|
+
readonly hasData?: boolean;
|
|
24
|
+
|
|
25
|
+
/** Result may be added even if there is no data associated with it */
|
|
26
|
+
readonly spec?: PObjectSpec;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Returns data accessor, or error, or undefined if data not yet available.
|
|
30
|
+
* If data fuinction itself is not defined, this means that corresponding context
|
|
31
|
+
* was not rendered.
|
|
32
|
+
* */
|
|
33
|
+
data?(): ValueOrError<PlTreeNodeAccessor, string> | undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const BContextValuePrefix = 'values/';
|
|
37
|
+
const BContextValueSpecSuffix = '.spec';
|
|
38
|
+
const BContextValueDataSuffix = '.data';
|
|
39
|
+
|
|
40
|
+
export function parseRawPObjectCollection(
|
|
41
|
+
node: PlTreeNodeAccessor,
|
|
42
|
+
errorOnUnknownField: boolean = true,
|
|
43
|
+
ignoreFieldErrors: boolean = false,
|
|
44
|
+
prefix: string = ''
|
|
45
|
+
): RawPObjectCollection {
|
|
46
|
+
const entryPattern = /^(?<name>.*)\.(?<type>spec|data)$/;
|
|
47
|
+
const results = new Map<string, Writable<RawPObjectEntry>>();
|
|
48
|
+
for (const fieldName of node.listInputFields()) {
|
|
49
|
+
const match = fieldName.match(entryPattern);
|
|
50
|
+
if (!match) {
|
|
51
|
+
if (errorOnUnknownField) throw new Error(`unexpected field name ${fieldName}`);
|
|
52
|
+
else continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let name = notEmpty(match.groups?.name);
|
|
56
|
+
if (!name.startsWith(prefix)) {
|
|
57
|
+
if (errorOnUnknownField) throw new Error(`unexpected field name ${fieldName}`);
|
|
58
|
+
else continue;
|
|
59
|
+
}
|
|
60
|
+
name = name.slice(prefix.length);
|
|
61
|
+
|
|
62
|
+
const type = notEmpty(match.groups?.type) as 'spec' | 'data';
|
|
63
|
+
let result = results.get(name);
|
|
64
|
+
if (result === undefined) {
|
|
65
|
+
result = {};
|
|
66
|
+
results.set(name, result);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
switch (type) {
|
|
70
|
+
case 'spec':
|
|
71
|
+
result.spec = node
|
|
72
|
+
.traverse({
|
|
73
|
+
field: fieldName,
|
|
74
|
+
ignoreError: ignoreFieldErrors,
|
|
75
|
+
pureFieldErrorToUndefined: ignoreFieldErrors
|
|
76
|
+
})
|
|
77
|
+
?.getDataAsJson();
|
|
78
|
+
break;
|
|
79
|
+
case 'data':
|
|
80
|
+
result.hasData = true;
|
|
81
|
+
result.data = () =>
|
|
82
|
+
node.traverseOrError({
|
|
83
|
+
field: fieldName,
|
|
84
|
+
ignoreError: ignoreFieldErrors
|
|
85
|
+
});
|
|
86
|
+
default:
|
|
87
|
+
// other value types planned
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const locked = node.getInputsLocked();
|
|
93
|
+
if (locked)
|
|
94
|
+
for (const [, result] of results) if (result.data === undefined) result.hasData = false;
|
|
95
|
+
|
|
96
|
+
return { locked, results };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function parseFinalPObjectCollection(
|
|
100
|
+
node: PlTreeNodeAccessor,
|
|
101
|
+
errorOnUnknownField: boolean = true,
|
|
102
|
+
prefix: string = ''
|
|
103
|
+
): Record<string, PObject<PlTreeNodeAccessor>> {
|
|
104
|
+
if (!node.getIsReadyOrError()) throw new Error('resource is not ready');
|
|
105
|
+
const rawCollection = parseRawPObjectCollection(node, errorOnUnknownField, false, prefix);
|
|
106
|
+
assert(rawCollection.locked);
|
|
107
|
+
const collection: Record<string, PObject<PlTreeNodeAccessor>> = {};
|
|
108
|
+
for (const [exportName, result] of rawCollection.results) {
|
|
109
|
+
if (result.spec === undefined) throw new Error(`no spec for key ${exportName}`);
|
|
110
|
+
if (result.hasData !== true || result.data === undefined)
|
|
111
|
+
throw new Error(`no data for key ${exportName}`);
|
|
112
|
+
const data = result.data();
|
|
113
|
+
if (data === undefined) throw new Error(`no data for key ${exportName}`);
|
|
114
|
+
if (!data.ok) throw new PlError(data.error);
|
|
115
|
+
collection[exportName] = {
|
|
116
|
+
id: derivePObjectId(result.spec, data.value),
|
|
117
|
+
spec: result.spec,
|
|
118
|
+
data: data.value
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return collection;
|
|
122
|
+
}
|