@milaboratories/pl-middle-layer 1.65.8 → 1.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/js_render/column_registry.cjs +56 -0
  2. package/dist/js_render/column_registry.cjs.map +1 -0
  3. package/dist/js_render/column_registry.js +55 -0
  4. package/dist/js_render/column_registry.js.map +1 -0
  5. package/dist/js_render/computable_context.cjs +80 -12
  6. package/dist/js_render/computable_context.cjs.map +1 -1
  7. package/dist/js_render/computable_context.js +81 -13
  8. package/dist/js_render/computable_context.js.map +1 -1
  9. package/dist/js_render/service_injectors.cjs +43 -2
  10. package/dist/js_render/service_injectors.cjs.map +1 -1
  11. package/dist/js_render/service_injectors.js +43 -2
  12. package/dist/js_render/service_injectors.js.map +1 -1
  13. package/dist/js_render/spec_override_collapse.cjs +108 -0
  14. package/dist/js_render/spec_override_collapse.cjs.map +1 -0
  15. package/dist/js_render/spec_override_collapse.js +108 -0
  16. package/dist/js_render/spec_override_collapse.js.map +1 -0
  17. package/dist/middle_layer/block_ctx.cjs +2 -1
  18. package/dist/middle_layer/block_ctx.cjs.map +1 -1
  19. package/dist/middle_layer/block_ctx.js +2 -1
  20. package/dist/middle_layer/block_ctx.js.map +1 -1
  21. package/dist/mutator/template/template_cache.cjs.map +1 -1
  22. package/dist/mutator/template/template_cache.d.ts.map +1 -1
  23. package/dist/mutator/template/template_cache.js.map +1 -1
  24. package/dist/pool/data.cjs +2 -9
  25. package/dist/pool/data.cjs.map +1 -1
  26. package/dist/pool/data.d.ts.map +1 -1
  27. package/dist/pool/data.js +3 -10
  28. package/dist/pool/data.js.map +1 -1
  29. package/dist/pool/result_pool.cjs +0 -20
  30. package/dist/pool/result_pool.cjs.map +1 -1
  31. package/dist/pool/result_pool.js +1 -21
  32. package/dist/pool/result_pool.js.map +1 -1
  33. package/dist/pool/upstream_block_ctx.cjs +64 -0
  34. package/dist/pool/upstream_block_ctx.cjs.map +1 -0
  35. package/dist/pool/upstream_block_ctx.js +64 -0
  36. package/dist/pool/upstream_block_ctx.js.map +1 -0
  37. package/dist/service_factories.cjs +3 -1
  38. package/dist/service_factories.cjs.map +1 -1
  39. package/dist/service_factories.js +3 -1
  40. package/dist/service_factories.js.map +1 -1
  41. package/package.json +16 -15
  42. package/src/js_render/column_registry.ts +95 -0
  43. package/src/js_render/computable_context.ts +109 -26
  44. package/src/js_render/service_injectors.ts +106 -8
  45. package/src/js_render/spec_override_collapse.ts +211 -0
  46. package/src/middle_layer/block_ctx.ts +6 -1
  47. package/src/mutator/template/template_cache.test.ts +0 -14
  48. package/src/mutator/template/template_cache.ts +0 -14
  49. package/src/pool/data.ts +4 -3
  50. package/src/pool/result_pool.ts +1 -24
  51. package/src/pool/upstream_block_ctx.ts +85 -0
  52. package/src/service_factories.ts +2 -0
@@ -0,0 +1,95 @@
1
+ import type { PlTreeNodeAccessor } from "@milaboratories/pl-tree";
2
+ import {
3
+ AccessorEntriesProvider,
4
+ applySpecOverrides,
5
+ ColumnRegistry,
6
+ extractPObjectId,
7
+ PColumnValues,
8
+ ResultPoolEntriesProvider,
9
+ SpecOverrides,
10
+ type ColumnEntriesProvider,
11
+ type PColumn,
12
+ type PColumnSpec,
13
+ type PObjectId,
14
+ type UpstreamBlockCtx,
15
+ } from "@milaboratories/pl-model-common";
16
+
17
+ export interface ColumnRegistryRoots {
18
+ /** Main `outputs` accessor (if available) — usually `MainAccessorName`. */
19
+ readonly outputs?: PlTreeNodeAccessor;
20
+ /** Prerun `staging` accessor (if available) — usually `StagingAccessorName`. */
21
+ readonly prerun?: PlTreeNodeAccessor;
22
+ /** Upstream-block ctx accessors (as `collectUpstreamBlockCtxes` returns). */
23
+ readonly upstreamBlockCtxes: ReadonlyArray<UpstreamBlockCtx<PlTreeNodeAccessor>>;
24
+ }
25
+
26
+ /**
27
+ * Build a host-side {@link ColumnRegistry} mirroring the sandbox layout:
28
+ * `outputs` precedes `prerun` precedes `rawResultPool`, first-wins on conflict.
29
+ *
30
+ * Uses raw {@link PlTreeNodeAccessor}s directly — both `TreeNodeAccessor`
31
+ * (sandbox) and `PlTreeNodeAccessor` (host) satisfy the `AccessorLike`
32
+ * traversal surface, so no host-side adapter is required.
33
+ */
34
+ export function buildColumnRegistry(
35
+ roots: ColumnRegistryRoots,
36
+ ): ColumnRegistry<PlTreeNodeAccessor> {
37
+ const providers: ColumnEntriesProvider<PlTreeNodeAccessor>[] = [];
38
+
39
+ if (roots.outputs !== undefined) {
40
+ providers.push(new AccessorEntriesProvider(roots.outputs, ["main"]));
41
+ }
42
+ if (roots.prerun !== undefined) {
43
+ providers.push(new AccessorEntriesProvider(roots.prerun, ["staging"]));
44
+ }
45
+
46
+ providers.push(new ResultPoolEntriesProvider<PlTreeNodeAccessor>(roots.upstreamBlockCtxes));
47
+
48
+ return new ColumnRegistry<PlTreeNodeAccessor>(providers);
49
+ }
50
+
51
+ /**
52
+ * Resolve a column id to a materialised {@link PColumn} backed by the
53
+ * underlying {@link PlTreeNodeAccessor} for the `.data` resource. Returns the
54
+ * bare leaf spec (via {@link extractPObjectId}): filters / overrides encoded in
55
+ * a rich id are NOT reconstructed here — they are applied by the query nodes
56
+ * (`sliceAxes` / `specOverride`) that wrap this leaf in the def, so reconstructing
57
+ * would double-apply them. Only the explicit `overrides` argument is folded in.
58
+ *
59
+ * Throws when the id is not in the registry or the column has no resolved spec.
60
+ * A `.data` field that has not yet materialised yields an empty value list — the
61
+ * column refills on a later recomputation, mirroring the sandbox-side
62
+ * `finalizePColumnData` behaviour rather than blocking the whole PFrame.
63
+ */
64
+ export function resolvePColumnById(
65
+ registry: ColumnRegistry<PlTreeNodeAccessor>,
66
+ id: PObjectId,
67
+ overrides?: SpecOverrides,
68
+ ): PColumn<PColumnValues | PlTreeNodeAccessor> {
69
+ const pid = extractPObjectId(id);
70
+ const leaf = registry.resolve(pid);
71
+ if (leaf === undefined) {
72
+ throw new Error(`column id ${String(pid)} not found in host column registry`);
73
+ }
74
+ const specNode = leaf.accessor.traverse({
75
+ field: `${leaf.name}.spec`,
76
+ assertFieldType: "Input",
77
+ ignoreError: true,
78
+ });
79
+ const spec = specNode?.getDataAsJson<PColumnSpec>();
80
+ if (spec === undefined) {
81
+ throw new Error(`column ${String(pid)} has no resolved spec`);
82
+ }
83
+ const data =
84
+ leaf.accessor.traverse({
85
+ field: `${leaf.name}.data`,
86
+ assertFieldType: "Input",
87
+ ignoreError: true,
88
+ }) ?? [];
89
+
90
+ return {
91
+ id: id as PObjectId,
92
+ spec: applySpecOverrides(spec, overrides),
93
+ data,
94
+ };
95
+ }
@@ -7,14 +7,12 @@ import type {
7
7
  CommonFieldTraverseOps as CommonFieldTraverseOpsFromSDK,
8
8
  DataInfo,
9
9
  FieldTraversalStep as FieldTraversalStepFromSDK,
10
- Option,
11
10
  PColumn,
12
11
  PColumnValues,
13
12
  PFrameDef,
14
13
  PFrameHandle,
15
14
  PObject,
16
15
  PObjectSpec,
17
- PSpecPredicate,
18
16
  PTableDef,
19
17
  PTableDefV2,
20
18
  PTableHandle,
@@ -42,6 +40,7 @@ import type { MiddleLayerEnvironment } from "../middle_layer/middle_layer";
42
40
  import type { Block } from "../model/project_model";
43
41
  import { parseFinalPObjectCollection } from "../pool/p_object_collection";
44
42
  import type { ResultPool } from "../pool/result_pool";
43
+ import { collectUpstreamBlockCtx, type UpstreamBlockCtx } from "../pool/upstream_block_ctx";
45
44
  import type { JsExecutionContext } from "./context";
46
45
  import type { VmFunctionImplementation } from "quickjs-emscripten";
47
46
  import { Scope, type QuickJSHandle } from "quickjs-emscripten";
@@ -54,10 +53,10 @@ import {
54
53
  ServiceMethodNotFoundError,
55
54
  } from "@milaboratories/pl-model-common";
56
55
  import { getServiceInjectors } from "./service_injectors";
57
-
58
- function bytesToBase64(data: Uint8Array | undefined): string | undefined {
59
- return data !== undefined ? Buffer.from(data).toString("base64") : undefined;
60
- }
56
+ import { MainAccessorName, StagingAccessorName } from "@platforma-sdk/model";
57
+ import { buildColumnRegistry, resolvePColumnById } from "./column_registry";
58
+ import type { ColumnRegistry, PObjectId } from "@milaboratories/pl-model-common";
59
+ import { collapseSpecOverrideNode } from "./spec_override_collapse";
61
60
 
62
61
  export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRenderCtxMethods<
63
62
  string,
@@ -92,6 +91,10 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
92
91
  public resetComputableCtx() {
93
92
  this.computableCtx = undefined;
94
93
  this.accessors.clear();
94
+ this._resultPool = undefined;
95
+ this._upstreamBlockCtx = undefined;
96
+ this._rawUpstreamBlockCtx = undefined;
97
+ this._columnRegistry = undefined;
95
98
  }
96
99
 
97
100
  private get requireComputableCtx(): ComputableCtx {
@@ -153,6 +156,10 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
153
156
  return this.getAccessor(handle).getIsReadyOrError();
154
157
  }
155
158
 
159
+ hasData(handle: string): boolean {
160
+ return this.getAccessor(handle).hasData();
161
+ }
162
+
156
163
  getIsFinal(handle: string): boolean {
157
164
  return this.getAccessor(handle).getIsFinal();
158
165
  }
@@ -350,10 +357,6 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
350
357
  return this._resultPool;
351
358
  }
352
359
 
353
- public calculateOptions(predicate: PSpecPredicate): Option[] {
354
- return this.resultPool.calculateOptions(predicate);
355
- }
356
-
357
360
  public getDataFromResultPool(): ResultCollection<PObject<string>> {
358
361
  const collection = this.resultPool.getData();
359
362
  if (collection.instabilityMarker !== undefined)
@@ -409,28 +412,79 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
409
412
  );
410
413
  }
411
414
 
415
+ /**
416
+ * Single source-of-truth cache of upstream-block ctx pairs as raw
417
+ * `PlTreeNodeAccessor`s. Both the sandbox-facing `getUpstreamBlockCtx()`
418
+ * (string-handle view) and the host column registry are derived from this.
419
+ */
420
+ private _rawUpstreamBlockCtx: UpstreamBlockCtx<PlTreeNodeAccessor>[] | undefined = undefined;
421
+ /**
422
+ * Raw upstream-block ctx accessors for the current render. Public so the
423
+ * `ColumnsCollection` service injector (`service_injectors.ts`) can build
424
+ * `ColumnsCollectionDriverHost.getUpstreamBlockCtxes` bindings without
425
+ * round-tripping through the handle-shape view.
426
+ */
427
+ public getRawUpstreamBlockCtx(): UpstreamBlockCtx<PlTreeNodeAccessor>[] {
428
+ if (this._rawUpstreamBlockCtx === undefined) {
429
+ const prjEntry = notEmpty(this.blockCtx.projectEntry, "projectEntry");
430
+ this._rawUpstreamBlockCtx = collectUpstreamBlockCtx(
431
+ this.requireComputableCtx,
432
+ prjEntry,
433
+ this.blockCtx.blockId,
434
+ );
435
+ }
436
+ return this._rawUpstreamBlockCtx;
437
+ }
438
+
439
+ private _upstreamBlockCtx: UpstreamBlockCtx<string>[] | undefined = undefined;
440
+ public getUpstreamBlockCtx(): UpstreamBlockCtx<string>[] {
441
+ if (this._upstreamBlockCtx === undefined) {
442
+ this._upstreamBlockCtx = this.getRawUpstreamBlockCtx().map((b) => ({
443
+ blockId: b.blockId,
444
+ prodCtx: this.wrapAccessor(b.prodCtx),
445
+ stagingCtx: this.wrapAccessor(b.stagingCtx),
446
+ prodIncomplete: b.prodIncomplete,
447
+ stagingIncomplete: b.stagingIncomplete,
448
+ }));
449
+ }
450
+ return this._upstreamBlockCtx;
451
+ }
452
+
453
+ //
454
+ // Host-side column registry — id → LeafEntry index over outputs/prerun/raw pool.
455
+ // Mirrors the sandbox `ColumnRegistry` so `createPFrame` / `createPTable` can
456
+ // accept `PObjectId` in addition to materialised `PColumn`s.
457
+ //
458
+ private _columnRegistry: ColumnRegistry<PlTreeNodeAccessor> | undefined = undefined;
459
+ public getColumnRegistry(): ColumnRegistry<PlTreeNodeAccessor> {
460
+ if (this._columnRegistry !== undefined) return this._columnRegistry;
461
+ return (this._columnRegistry = buildColumnRegistry({
462
+ outputs: this.accessors.get(MainAccessorName) ?? undefined,
463
+ prerun: this.accessors.get(StagingAccessorName) ?? undefined,
464
+ upstreamBlockCtxes: this.getRawUpstreamBlockCtx(),
465
+ }));
466
+ }
467
+
412
468
  //
413
469
  // PFrames / PTables
414
470
  //
415
471
 
416
472
  public createPFrame(
417
- def: PFrameDef<PColumn<string | PColumnValues | DataInfo<string>>>,
473
+ def: PFrameDef<PObjectId | PColumn<string | PColumnValues | DataInfo<string>>>,
418
474
  ): PFrameHandle {
419
475
  using guard = new PoolEntryGuard(
420
- this.env.driverKit.pFrameDriver.createPFrame(
421
- def.map((c) => mapPObjectData(c, (d) => this.transformInputPData(d))),
422
- ),
476
+ this.env.driverKit.pFrameDriver.createPFrame(def.map((c) => this.resolvePColumn(c))),
423
477
  );
424
478
  this.requireComputableCtx.addOnDestroy(guard.entry.unref);
425
479
  return guard.keep().key;
426
480
  }
427
481
 
428
482
  public createPTable(
429
- def: PTableDef<PColumn<string | PColumnValues | DataInfo<string>>>,
483
+ def: PTableDef<PObjectId | PColumn<string | PColumnValues | DataInfo<string>>>,
430
484
  ): PTableHandle {
431
485
  using guard = new PoolEntryGuard(
432
486
  this.env.driverKit.pFrameDriver.createPTable(
433
- mapPTableDef(def, (c) => mapPObjectData(c, (d) => this.transformInputPData(d))),
487
+ mapPTableDef(def, (c) => this.resolvePColumn(c)),
434
488
  ),
435
489
  );
436
490
  this.requireComputableCtx.addOnDestroy(guard.entry.unref);
@@ -438,17 +492,35 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
438
492
  }
439
493
 
440
494
  public createPTableV2(
441
- def: PTableDefV2<PColumn<string | PColumnValues | DataInfo<string>>>,
495
+ def: PTableDefV2<PObjectId | PColumn<string | PColumnValues | DataInfo<string>>>,
442
496
  ): PTableHandle {
443
497
  using guard = new PoolEntryGuard(
444
498
  this.env.driverKit.pFrameDriver.createPTableV2(
445
- mapPTableDefV2(def, (c) => mapPObjectData(c, (d) => this.transformInputPData(d))),
499
+ mapPTableDefV2(def, {
500
+ column: (c) => this.resolvePColumn(c),
501
+ node: collapseSpecOverrideNode,
502
+ }),
446
503
  ),
447
504
  );
448
505
  this.requireComputableCtx.addOnDestroy(guard.entry.unref);
449
506
  return guard.keep().key;
450
507
  }
451
508
 
509
+ /**
510
+ * Normalise a def element: pass-through for materialised columns (after
511
+ * mapping their `data` payload via {@link transformInputPData}), or resolve
512
+ * a bare id through the host column registry. `resolvePColumnById` extracts
513
+ * the underlying {@link PObjectId} from any id encoding
514
+ * ({@link ColumnOverriddenId}, etc.).
515
+ */
516
+ private resolvePColumn(
517
+ item: PObjectId | PColumn<string | PColumnValues | DataInfo<string>>,
518
+ ): PColumn<PlTreeNodeAccessor | PColumnValues | DataInfo<PlTreeNodeAccessor>> {
519
+ return typeof item === "string"
520
+ ? resolvePColumnById(this.getColumnRegistry(), item)
521
+ : mapPObjectData(item, (d) => this.transformInputPData(d));
522
+ }
523
+
452
524
  /**
453
525
  * Transforms input data for PFrame/PTable creation
454
526
  * - Converts string handles to accessors
@@ -495,7 +567,13 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
495
567
  // Helpers
496
568
  //
497
569
 
498
- private getAccessor(handle: string): PlTreeNodeAccessor {
570
+ /**
571
+ * Resolve a sandbox-side `AccessorHandle` string back to its concrete
572
+ * host accessor. Used internally by every `traverse`/`getX(handle)`
573
+ * method and externally by the `ColumnsCollection` service injector
574
+ * for `SerializedColumnsSource` of kind `"accessor"`.
575
+ */
576
+ public getAccessor(handle: string): PlTreeNodeAccessor {
499
577
  const accessor = this.accessors.get(handle);
500
578
  if (accessor === undefined) throw new Error("No such accessor");
501
579
  return accessor;
@@ -649,6 +727,10 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
649
727
  return parent.exportSingleValue(this.getIsFinal(vm.getString(handle)), undefined);
650
728
  });
651
729
 
730
+ exportCtxFunction("hasData", (handle) => {
731
+ return parent.exportSingleValue(this.hasData(vm.getString(handle)), undefined);
732
+ });
733
+
652
734
  exportCtxFunction("getError", (handle) => {
653
735
  return parent.exportSingleValue(this.getError(vm.getString(handle)), undefined);
654
736
  });
@@ -816,13 +898,6 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
816
898
  return parent.exportObjectUniversal(this.getSpecsFromResultPool(), undefined);
817
899
  });
818
900
 
819
- exportCtxFunction("calculateOptions", (predicate) => {
820
- return parent.exportObjectUniversal(
821
- this.calculateOptions(parent.importObjectViaJson(predicate) as PSpecPredicate),
822
- undefined,
823
- );
824
- });
825
-
826
901
  exportCtxFunction("getSpecFromResultPoolByRef", (blockId, exportName) => {
827
902
  return parent.exportObjectUniversal(
828
903
  this.getSpecFromResultPoolByRef(vm.getString(blockId), vm.getString(exportName)),
@@ -837,6 +912,10 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
837
912
  );
838
913
  });
839
914
 
915
+ exportCtxFunction("getUpstreamBlockCtx", () => {
916
+ return parent.exportObjectUniversal(this.getUpstreamBlockCtx(), undefined);
917
+ });
918
+
840
919
  exportCtxFunction("createPFrame", (def) => {
841
920
  return parent.exportSingleValue(
842
921
  this.createPFrame(
@@ -938,3 +1017,7 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
938
1017
  });
939
1018
  }
940
1019
  }
1020
+
1021
+ function bytesToBase64(data: Uint8Array | undefined): string | undefined {
1022
+ return data !== undefined ? Buffer.from(data).toString("base64") : undefined;
1023
+ }
@@ -1,6 +1,15 @@
1
1
  import type { QuickJSHandle, VmFunctionImplementation } from "quickjs-emscripten";
2
- import type { InferServiceModel, ServiceBrand } from "@milaboratories/pl-model-common";
2
+ import type {
3
+ CollectionHandle,
4
+ ColumnsCollectionDriverHost,
5
+ DiscoverColumnsOptions,
6
+ InferServiceModel,
7
+ PoolEntry,
8
+ SerializedColumnsSource,
9
+ ServiceBrand,
10
+ } from "@milaboratories/pl-model-common";
3
11
  import { Services, ServiceNotRegisteredError } from "@milaboratories/pl-model-common";
12
+ import type { PlTreeNodeAccessor } from "@milaboratories/pl-tree";
4
13
  import type {
5
14
  AxesId,
6
15
  AxesSpec,
@@ -8,6 +17,7 @@ import type {
8
17
  PColumn,
9
18
  PColumnSpec,
10
19
  PColumnValues,
20
+ PObjectId,
11
21
  PTableColumnId,
12
22
  PTableColumnSpec,
13
23
  PTableRecordFilter,
@@ -141,16 +151,12 @@ export function getServiceInjectors(): ServiceInjectorMap {
141
151
  };
142
152
  },
143
153
 
144
- // Dialog has no model-side surface — workflow scripts cannot open save dialogs.
145
- // Declared as an empty injector to satisfy the exhaustive ServiceInjectorMap.
146
- Dialog: () => ({}) as Record<never, VmMethod>,
147
-
148
154
  PFrame: ({ host, vm }: ServiceInjectorContext) => ({
149
155
  createPFrame: (def: QuickJSHandle) =>
150
156
  vm.exportSingleValue(
151
157
  host.createPFrame(
152
158
  vm.importObjectViaJson(def) as PFrameDef<
153
- PColumn<string | PColumnValues | DataInfo<string>>
159
+ PObjectId | PColumn<string | PColumnValues | DataInfo<string>>
154
160
  >,
155
161
  ),
156
162
  ),
@@ -159,7 +165,7 @@ export function getServiceInjectors(): ServiceInjectorMap {
159
165
  vm.exportSingleValue(
160
166
  host.createPTable(
161
167
  vm.importObjectViaJson(def) as PTableDef<
162
- PColumn<string | PColumnValues | DataInfo<string>>
168
+ PObjectId | PColumn<string | PColumnValues | DataInfo<string>>
163
169
  >,
164
170
  ),
165
171
  ),
@@ -168,10 +174,102 @@ export function getServiceInjectors(): ServiceInjectorMap {
168
174
  vm.exportSingleValue(
169
175
  host.createPTableV2(
170
176
  vm.importObjectViaJson(def) as PTableDefV2<
171
- PColumn<string | PColumnValues | DataInfo<string>>
177
+ PObjectId | PColumn<string | PColumnValues | DataInfo<string>>
172
178
  >,
173
179
  ),
174
180
  ),
175
181
  }),
182
+
183
+ // Dialog has no model-side surface — workflow scripts cannot open save dialogs.
184
+ // Declared as an empty injector to satisfy the exhaustive ServiceInjectorMap.
185
+ Dialog: () => ({}) as Record<never, VmMethod>,
186
+
187
+ ColumnsCollection: ({ host, vm }: ServiceInjectorContext) => {
188
+ const driver = host.serviceRegistry.get(Services.ColumnsCollection);
189
+ if (!driver)
190
+ throw new ServiceNotRegisteredError(
191
+ `Service "${Services.ColumnsCollection}" has no factory in ModelServiceRegistry. Provide a non-null factory.`,
192
+ );
193
+ const specDriver = host.serviceRegistry.get(Services.PFrameSpec);
194
+ if (!specDriver)
195
+ throw new ServiceNotRegisteredError(
196
+ `Service "${Services.PFrameSpec}" has no factory in ModelServiceRegistry. Provide a non-null factory.`,
197
+ );
198
+
199
+ const bindings: ColumnsCollectionDriverHost<PlTreeNodeAccessor> = {
200
+ resolveAccessor: (h) => host.getAccessor(h),
201
+ getUpstreamBlockCtxes: () => host.getRawUpstreamBlockCtx(),
202
+ getSpecDriver: () => specDriver,
203
+ resolveSpec: (id: PObjectId) => {
204
+ const leaf = host.getColumnRegistry().resolve(id);
205
+ if (leaf === undefined) return undefined;
206
+ const specNode = leaf.accessor.traverse({
207
+ field: `${leaf.name}.spec`,
208
+ assertFieldType: "Input",
209
+ ignoreError: true,
210
+ });
211
+ return specNode?.getDataAsJson<PColumnSpec>();
212
+ },
213
+ };
214
+
215
+ const pinHandle = (entry: PoolEntry<CollectionHandle>): CollectionHandle => {
216
+ using guard = new PoolEntryGuard(entry);
217
+ host.addOnDestroy(guard.entry.unref);
218
+ return guard.keep().key;
219
+ };
220
+
221
+ return {
222
+ create: (sources: QuickJSHandle) =>
223
+ vm.exportSingleValue(
224
+ pinHandle(
225
+ driver.create(vm.importObjectViaJson(sources) as SerializedColumnsSource[], bindings),
226
+ ),
227
+ ),
228
+
229
+ isEmpty: (handle: QuickJSHandle) =>
230
+ vm.exportSingleValue(driver.isEmpty(vm.vm.getString(handle) as CollectionHandle)),
231
+
232
+ isFinal: (handle: QuickJSHandle) =>
233
+ vm.exportSingleValue(driver.isFinal(vm.vm.getString(handle) as CollectionHandle)),
234
+
235
+ getColumns: (handle: QuickJSHandle) =>
236
+ vm.exportObjectViaJson(
237
+ driver.getColumns(vm.vm.getString(handle) as CollectionHandle, bindings),
238
+ ),
239
+
240
+ addSource: (handle: QuickJSHandle, sources: QuickJSHandle) =>
241
+ vm.exportSingleValue(
242
+ pinHandle(
243
+ driver.addSource(
244
+ vm.vm.getString(handle) as CollectionHandle,
245
+ vm.importObjectViaJson(sources) as SerializedColumnsSource[],
246
+ bindings,
247
+ ),
248
+ ),
249
+ ),
250
+
251
+ discover: (handle: QuickJSHandle, opts: QuickJSHandle) =>
252
+ vm.exportSingleValue(
253
+ pinHandle(
254
+ driver.discover(
255
+ vm.vm.getString(handle) as CollectionHandle,
256
+ vm.importObjectViaJson(opts) as DiscoverColumnsOptions,
257
+ bindings,
258
+ ),
259
+ ),
260
+ ),
261
+
262
+ filter: (handle: QuickJSHandle, opts: QuickJSHandle) =>
263
+ vm.exportSingleValue(
264
+ pinHandle(
265
+ driver.filter(
266
+ vm.vm.getString(handle) as CollectionHandle,
267
+ vm.importObjectViaJson(opts) as DiscoverColumnsOptions,
268
+ bindings,
269
+ ),
270
+ ),
271
+ ),
272
+ };
273
+ },
176
274
  };
177
275
  }