@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.
Files changed (179) hide show
  1. package/dist/block_registry/index.d.ts +4 -0
  2. package/dist/block_registry/index.d.ts.map +1 -0
  3. package/dist/block_registry/registry.d.ts +37 -0
  4. package/dist/block_registry/registry.d.ts.map +1 -0
  5. package/dist/block_registry/registry_spec.d.ts +12 -0
  6. package/dist/block_registry/registry_spec.d.ts.map +1 -0
  7. package/dist/block_registry/watcher.d.ts +15 -0
  8. package/dist/block_registry/watcher.d.ts.map +1 -0
  9. package/dist/block_registry/well_known_registries.d.ts +4 -0
  10. package/dist/block_registry/well_known_registries.d.ts.map +1 -0
  11. package/dist/cfg_render/executor.d.ts +8 -0
  12. package/dist/cfg_render/executor.d.ts.map +1 -0
  13. package/dist/cfg_render/operation.d.ts +29 -0
  14. package/dist/cfg_render/operation.d.ts.map +1 -0
  15. package/dist/cfg_render/renderer.d.ts +6 -0
  16. package/dist/cfg_render/renderer.d.ts.map +1 -0
  17. package/dist/cfg_render/traverse.d.ts +3 -0
  18. package/dist/cfg_render/traverse.d.ts.map +1 -0
  19. package/dist/cfg_render/util.d.ts +5 -0
  20. package/dist/cfg_render/util.d.ts.map +1 -0
  21. package/dist/dev/index.d.ts +21 -0
  22. package/dist/dev/index.d.ts.map +1 -0
  23. package/dist/dev/util.d.ts +3 -0
  24. package/dist/dev/util.d.ts.map +1 -0
  25. package/dist/index.d.ts +13 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +2 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/index.mjs +3587 -0
  30. package/dist/index.mjs.map +1 -0
  31. package/dist/js_render/context.d.ts +68 -0
  32. package/dist/js_render/context.d.ts.map +1 -0
  33. package/dist/js_render/index.d.ts +6 -0
  34. package/dist/js_render/index.d.ts.map +1 -0
  35. package/dist/middle_layer/active_cfg.d.ts +6 -0
  36. package/dist/middle_layer/active_cfg.d.ts.map +1 -0
  37. package/dist/middle_layer/block.d.ts +9 -0
  38. package/dist/middle_layer/block.d.ts.map +1 -0
  39. package/dist/middle_layer/block_ctx.d.ts +20 -0
  40. package/dist/middle_layer/block_ctx.d.ts.map +1 -0
  41. package/dist/middle_layer/block_ctx_unsafe.d.ts +16 -0
  42. package/dist/middle_layer/block_ctx_unsafe.d.ts.map +1 -0
  43. package/dist/middle_layer/driver_kit.d.ts +31 -0
  44. package/dist/middle_layer/driver_kit.d.ts.map +1 -0
  45. package/dist/middle_layer/frontend_path.d.ts +6 -0
  46. package/dist/middle_layer/frontend_path.d.ts.map +1 -0
  47. package/dist/middle_layer/index.d.ts +5 -0
  48. package/dist/middle_layer/index.d.ts.map +1 -0
  49. package/dist/middle_layer/middle_layer.d.ts +78 -0
  50. package/dist/middle_layer/middle_layer.d.ts.map +1 -0
  51. package/dist/middle_layer/navigation_states.d.ts +10 -0
  52. package/dist/middle_layer/navigation_states.d.ts.map +1 -0
  53. package/dist/middle_layer/ops.d.ts +64 -0
  54. package/dist/middle_layer/ops.d.ts.map +1 -0
  55. package/dist/middle_layer/project.d.ts +110 -0
  56. package/dist/middle_layer/project.d.ts.map +1 -0
  57. package/dist/middle_layer/project_list.d.ts +11 -0
  58. package/dist/middle_layer/project_list.d.ts.map +1 -0
  59. package/dist/middle_layer/project_overview.d.ts +8 -0
  60. package/dist/middle_layer/project_overview.d.ts.map +1 -0
  61. package/dist/middle_layer/render.d.ts +6 -0
  62. package/dist/middle_layer/render.d.ts.map +1 -0
  63. package/dist/middle_layer/types.d.ts +11 -0
  64. package/dist/middle_layer/types.d.ts.map +1 -0
  65. package/dist/middle_layer/util.d.ts +3 -0
  66. package/dist/middle_layer/util.d.ts.map +1 -0
  67. package/dist/model/args.d.ts +12 -0
  68. package/dist/model/args.d.ts.map +1 -0
  69. package/dist/model/block_pack.d.ts +8 -0
  70. package/dist/model/block_pack.d.ts.map +1 -0
  71. package/dist/model/block_pack_spec.d.ts +40 -0
  72. package/dist/model/block_pack_spec.d.ts.map +1 -0
  73. package/dist/model/frontend.d.ts +10 -0
  74. package/dist/model/frontend.d.ts.map +1 -0
  75. package/dist/model/index.d.ts +3 -0
  76. package/dist/model/index.d.ts.map +1 -0
  77. package/dist/model/project_model.d.ts +67 -0
  78. package/dist/model/project_model.d.ts.map +1 -0
  79. package/dist/model/project_model_util.d.ts +29 -0
  80. package/dist/model/project_model_util.d.ts.map +1 -0
  81. package/dist/model/template_spec.d.ts +16 -0
  82. package/dist/model/template_spec.d.ts.map +1 -0
  83. package/dist/mutator/block-pack/block_pack.d.ts +17 -0
  84. package/dist/mutator/block-pack/block_pack.d.ts.map +1 -0
  85. package/dist/mutator/block-pack/frontend.d.ts +4 -0
  86. package/dist/mutator/block-pack/frontend.d.ts.map +1 -0
  87. package/dist/mutator/context_export.d.ts +9 -0
  88. package/dist/mutator/context_export.d.ts.map +1 -0
  89. package/dist/mutator/project.d.ts +121 -0
  90. package/dist/mutator/project.d.ts.map +1 -0
  91. package/dist/mutator/template/render_block.d.ts +32 -0
  92. package/dist/mutator/template/render_block.d.ts.map +1 -0
  93. package/dist/mutator/template/render_template.d.ts +12 -0
  94. package/dist/mutator/template/render_template.d.ts.map +1 -0
  95. package/dist/mutator/template/template_loading.d.ts +13 -0
  96. package/dist/mutator/template/template_loading.d.ts.map +1 -0
  97. package/dist/pool/data.d.ts +24 -0
  98. package/dist/pool/data.d.ts.map +1 -0
  99. package/dist/pool/driver.d.ts +22 -0
  100. package/dist/pool/driver.d.ts.map +1 -0
  101. package/dist/pool/index.d.ts +3 -0
  102. package/dist/pool/index.d.ts.map +1 -0
  103. package/dist/pool/p_object_collection.d.ts +29 -0
  104. package/dist/pool/p_object_collection.d.ts.map +1 -0
  105. package/dist/pool/ref_count_pool.d.ts +25 -0
  106. package/dist/pool/ref_count_pool.d.ts.map +1 -0
  107. package/dist/pool/result_pool.d.ts +25 -0
  108. package/dist/pool/result_pool.d.ts.map +1 -0
  109. package/dist/test/block_packs.d.ts +6 -0
  110. package/dist/test/block_packs.d.ts.map +1 -0
  111. package/dist/test/explicit_templates.d.ts +3 -0
  112. package/dist/test/explicit_templates.d.ts.map +1 -0
  113. package/dist/test/known_templates.d.ts +6 -0
  114. package/dist/test/known_templates.d.ts.map +1 -0
  115. package/package.json +55 -0
  116. package/src/block_registry/index.ts +3 -0
  117. package/src/block_registry/registry.test.ts +35 -0
  118. package/src/block_registry/registry.ts +180 -0
  119. package/src/block_registry/registry_spec.ts +13 -0
  120. package/src/block_registry/watcher.ts +72 -0
  121. package/src/block_registry/well_known_registries.ts +13 -0
  122. package/src/cfg_render/executor.test.ts +120 -0
  123. package/src/cfg_render/executor.ts +253 -0
  124. package/src/cfg_render/operation.ts +38 -0
  125. package/src/cfg_render/renderer.ts +540 -0
  126. package/src/cfg_render/traverse.ts +58 -0
  127. package/src/cfg_render/util.ts +29 -0
  128. package/src/dev/index.ts +89 -0
  129. package/src/dev/util.ts +13 -0
  130. package/src/index.ts +21 -0
  131. package/src/js_render/context.ts +768 -0
  132. package/src/js_render/index.ts +41 -0
  133. package/src/middle_layer/active_cfg.ts +56 -0
  134. package/src/middle_layer/block.ts +70 -0
  135. package/src/middle_layer/block_ctx.ts +90 -0
  136. package/src/middle_layer/block_ctx_unsafe.ts +29 -0
  137. package/src/middle_layer/driver_kit.ts +107 -0
  138. package/src/middle_layer/frontend_path.ts +83 -0
  139. package/src/middle_layer/index.ts +4 -0
  140. package/src/middle_layer/middle_layer.test.ts +720 -0
  141. package/src/middle_layer/middle_layer.ts +235 -0
  142. package/src/middle_layer/navigation_states.ts +48 -0
  143. package/src/middle_layer/ops.ts +147 -0
  144. package/src/middle_layer/project.ts +380 -0
  145. package/src/middle_layer/project_list.ts +59 -0
  146. package/src/middle_layer/project_overview.ts +220 -0
  147. package/src/middle_layer/render.test.ts +129 -0
  148. package/src/middle_layer/render.ts +19 -0
  149. package/src/middle_layer/types.ts +16 -0
  150. package/src/middle_layer/util.ts +22 -0
  151. package/src/model/args.ts +62 -0
  152. package/src/model/block_pack.ts +8 -0
  153. package/src/model/block_pack_spec.ts +52 -0
  154. package/src/model/frontend.ts +10 -0
  155. package/src/model/index.ts +2 -0
  156. package/src/model/project_model.test.ts +26 -0
  157. package/src/model/project_model.ts +142 -0
  158. package/src/model/project_model_util.test.ts +88 -0
  159. package/src/model/project_model_util.ts +169 -0
  160. package/src/model/template_spec.ts +18 -0
  161. package/src/mutator/block-pack/block_pack.test.ts +53 -0
  162. package/src/mutator/block-pack/block_pack.ts +187 -0
  163. package/src/mutator/block-pack/frontend.ts +29 -0
  164. package/src/mutator/context_export.ts +25 -0
  165. package/src/mutator/project.test.ts +272 -0
  166. package/src/mutator/project.ts +1112 -0
  167. package/src/mutator/template/render_block.ts +91 -0
  168. package/src/mutator/template/render_template.ts +40 -0
  169. package/src/mutator/template/template_loading.ts +77 -0
  170. package/src/mutator/template/template_render.test.ts +272 -0
  171. package/src/pool/data.ts +239 -0
  172. package/src/pool/driver.ts +325 -0
  173. package/src/pool/index.ts +2 -0
  174. package/src/pool/p_object_collection.ts +122 -0
  175. package/src/pool/ref_count_pool.ts +76 -0
  176. package/src/pool/result_pool.ts +284 -0
  177. package/src/test/block_packs.ts +23 -0
  178. package/src/test/explicit_templates.ts +8 -0
  179. package/src/test/known_templates.ts +24 -0
@@ -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,2 @@
1
+ export * from './driver';
2
+ export * from './result_pool';
@@ -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
+ }