@milaboratories/pl-middle-layer 1.53.3 → 1.54.1
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/js_render/computable_context.cjs +92 -85
- package/dist/js_render/computable_context.cjs.map +1 -1
- package/dist/js_render/computable_context.js +92 -84
- package/dist/js_render/computable_context.js.map +1 -1
- package/dist/js_render/context.cjs.map +1 -1
- package/dist/js_render/context.js.map +1 -1
- package/dist/js_render/service_injectors.cjs +49 -0
- package/dist/js_render/service_injectors.cjs.map +1 -0
- package/dist/js_render/service_injectors.js +48 -0
- package/dist/js_render/service_injectors.js.map +1 -0
- package/dist/middle_layer/middle_layer.cjs +10 -0
- package/dist/middle_layer/middle_layer.cjs.map +1 -1
- package/dist/middle_layer/middle_layer.d.ts +4 -0
- package/dist/middle_layer/middle_layer.js +10 -0
- package/dist/middle_layer/middle_layer.js.map +1 -1
- package/dist/pool/driver.cjs +1 -0
- package/dist/pool/driver.cjs.map +1 -1
- package/dist/pool/driver.js +1 -0
- package/dist/pool/driver.js.map +1 -1
- package/dist/service_factories.cjs +22 -0
- package/dist/service_factories.cjs.map +1 -0
- package/dist/service_factories.js +21 -0
- package/dist/service_factories.js.map +1 -0
- package/package.json +20 -20
- package/src/js_render/computable_context.ts +105 -171
- package/src/js_render/context.ts +4 -4
- package/src/js_render/service_injectors.ts +153 -0
- package/src/middle_layer/middle_layer.ts +18 -0
- package/src/pool/driver.ts +1 -1
- package/src/service_factories.ts +22 -0
- package/dist/js_render/spec_driver.cjs +0 -2
- package/dist/js_render/spec_driver.js +0 -3
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
mapValueInVOE,
|
|
35
35
|
} from "@platforma-sdk/model";
|
|
36
36
|
import { notEmpty } from "@milaboratories/ts-helpers";
|
|
37
|
+
import { PoolEntryGuard } from "@milaboratories/pl-model-common";
|
|
37
38
|
import { randomUUID } from "node:crypto";
|
|
38
39
|
import type { Optional } from "utility-types";
|
|
39
40
|
import type { BlockContextAny } from "../middle_layer/block_ctx";
|
|
@@ -44,18 +45,15 @@ import type { ResultPool } from "../pool/result_pool";
|
|
|
44
45
|
import type { JsExecutionContext } from "./context";
|
|
45
46
|
import type { VmFunctionImplementation } from "quickjs-emscripten";
|
|
46
47
|
import { Scope, type QuickJSHandle } from "quickjs-emscripten";
|
|
47
|
-
import
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
PTableColumnSpec,
|
|
55
|
-
SingleAxisSelector,
|
|
56
|
-
SpecFrameHandle,
|
|
48
|
+
import {
|
|
49
|
+
resolveRequiredServices,
|
|
50
|
+
serviceFnKey,
|
|
51
|
+
Services,
|
|
52
|
+
ModelServiceRegistry,
|
|
53
|
+
ServiceInjectionError,
|
|
54
|
+
ServiceMethodNotFoundError,
|
|
57
55
|
} from "@milaboratories/pl-model-common";
|
|
58
|
-
import {
|
|
56
|
+
import { getServiceInjectors } from "./service_injectors";
|
|
59
57
|
|
|
60
58
|
function bytesToBase64(data: Uint8Array | undefined): string | undefined {
|
|
61
59
|
return data !== undefined ? Buffer.from(data).toString("base64") : undefined;
|
|
@@ -69,18 +67,18 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
69
67
|
|
|
70
68
|
private computableCtx: ComputableCtx | undefined;
|
|
71
69
|
private readonly accessors = new Map<string, PlTreeNodeAccessor | undefined>();
|
|
72
|
-
private readonly specDriver = new SpecDriver();
|
|
73
|
-
|
|
74
70
|
private _meta: Map<string, Block> | undefined;
|
|
75
71
|
private get meta(): Map<string, Block> {
|
|
76
72
|
if (this._meta === undefined) {
|
|
77
|
-
|
|
78
|
-
throw new Error("blockMeta can't be resolved in this context");
|
|
79
|
-
this._meta = this.blockCtx.blockMeta(this.computableCtx);
|
|
73
|
+
this._meta = this.blockCtx.blockMeta(this.requireComputableCtx);
|
|
80
74
|
}
|
|
81
75
|
return this._meta;
|
|
82
76
|
}
|
|
83
77
|
|
|
78
|
+
public get serviceRegistry(): ModelServiceRegistry {
|
|
79
|
+
return this.env.serviceRegistry;
|
|
80
|
+
}
|
|
81
|
+
|
|
84
82
|
constructor(
|
|
85
83
|
private readonly parent: JsExecutionContext,
|
|
86
84
|
private readonly blockCtx: BlockContextAny,
|
|
@@ -96,21 +94,29 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
96
94
|
this.accessors.clear();
|
|
97
95
|
}
|
|
98
96
|
|
|
97
|
+
private get requireComputableCtx(): ComputableCtx {
|
|
98
|
+
if (this.computableCtx === undefined)
|
|
99
|
+
throw new Error("computableCtx not available (called from future mapper?)");
|
|
100
|
+
return this.computableCtx;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public addOnDestroy(callback: () => void): void {
|
|
104
|
+
this.requireComputableCtx.addOnDestroy(callback);
|
|
105
|
+
}
|
|
106
|
+
|
|
99
107
|
//
|
|
100
108
|
// Methods for injected ctx object
|
|
101
109
|
//
|
|
102
110
|
|
|
103
111
|
getAccessorHandleByName(name: string): string | undefined {
|
|
104
|
-
|
|
105
|
-
throw new Error("Accessors can't be used in this context");
|
|
112
|
+
const cCtx = this.requireComputableCtx;
|
|
106
113
|
const wellKnownAccessor = (name: string, ctxKey: "staging" | "prod"): string | undefined => {
|
|
107
114
|
if (!this.accessors.has(name)) {
|
|
108
115
|
const lambda = this.blockCtx[ctxKey];
|
|
109
116
|
if (lambda === undefined) throw new Error("Staging context not available");
|
|
110
|
-
const entry = lambda(
|
|
117
|
+
const entry = lambda(cCtx);
|
|
111
118
|
if (!entry) this.accessors.set(name, undefined);
|
|
112
|
-
else
|
|
113
|
-
this.accessors.set(name, this.computableCtx!.accessor(entry).node({ ignoreError: true }));
|
|
119
|
+
else this.accessors.set(name, cCtx.accessor(entry).node({ ignoreError: true }));
|
|
114
120
|
}
|
|
115
121
|
return this.accessors.get(name) ? name : undefined;
|
|
116
122
|
};
|
|
@@ -336,14 +342,10 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
336
342
|
private _resultPool: ResultPool | undefined = undefined;
|
|
337
343
|
private get resultPool(): ResultPool {
|
|
338
344
|
if (this._resultPool === undefined) {
|
|
339
|
-
if (this.computableCtx === undefined)
|
|
340
|
-
throw new Error(
|
|
341
|
-
"can't use result pool in this context (most porbably called from the future mapper)",
|
|
342
|
-
);
|
|
343
345
|
this._resultPool = notEmpty(
|
|
344
346
|
this.blockCtx.getResultsPool,
|
|
345
347
|
"getResultsPool",
|
|
346
|
-
)(this.
|
|
348
|
+
)(this.requireComputableCtx);
|
|
347
349
|
}
|
|
348
350
|
return this._resultPool;
|
|
349
351
|
}
|
|
@@ -355,7 +357,9 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
355
357
|
public getDataFromResultPool(): ResultCollection<PObject<string>> {
|
|
356
358
|
const collection = this.resultPool.getData();
|
|
357
359
|
if (collection.instabilityMarker !== undefined)
|
|
358
|
-
this.
|
|
360
|
+
this.requireComputableCtx.markUnstable(
|
|
361
|
+
`incomplete_result_pool:${collection.instabilityMarker}`,
|
|
362
|
+
);
|
|
359
363
|
return {
|
|
360
364
|
isComplete: collection.isComplete,
|
|
361
365
|
entries: collection.entries.map((e) => ({
|
|
@@ -370,7 +374,9 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
370
374
|
> {
|
|
371
375
|
const collection = this.resultPool.getDataWithErrors();
|
|
372
376
|
if (collection.instabilityMarker !== undefined)
|
|
373
|
-
this.
|
|
377
|
+
this.requireComputableCtx.markUnstable(
|
|
378
|
+
`incomplete_result_pool:${collection.instabilityMarker}`,
|
|
379
|
+
);
|
|
374
380
|
return {
|
|
375
381
|
isComplete: collection.isComplete,
|
|
376
382
|
entries: collection.entries.map((e) => ({
|
|
@@ -387,7 +393,9 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
387
393
|
public getSpecsFromResultPool(): ResultCollection<PObjectSpec> {
|
|
388
394
|
const specs = this.resultPool.getSpecs();
|
|
389
395
|
if (specs.instabilityMarker !== undefined)
|
|
390
|
-
this.
|
|
396
|
+
this.requireComputableCtx.markUnstable(
|
|
397
|
+
`specs_from_pool_incomplete:${specs.instabilityMarker}`,
|
|
398
|
+
);
|
|
391
399
|
return specs;
|
|
392
400
|
}
|
|
393
401
|
|
|
@@ -408,79 +416,37 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
408
416
|
public createPFrame(
|
|
409
417
|
def: PFrameDef<PColumn<string | PColumnValues | DataInfo<string>>>,
|
|
410
418
|
): PFrameHandle {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
)
|
|
415
|
-
const { key, unref } = this.env.driverKit.pFrameDriver.createPFrame(
|
|
416
|
-
def.map((c) => mapPObjectData(c, (d) => this.transformInputPData(d))),
|
|
419
|
+
using guard = new PoolEntryGuard(
|
|
420
|
+
this.env.driverKit.pFrameDriver.createPFrame(
|
|
421
|
+
def.map((c) => mapPObjectData(c, (d) => this.transformInputPData(d))),
|
|
422
|
+
),
|
|
417
423
|
);
|
|
418
|
-
this.
|
|
419
|
-
return key;
|
|
424
|
+
this.requireComputableCtx.addOnDestroy(guard.entry.unref);
|
|
425
|
+
return guard.keep().key;
|
|
420
426
|
}
|
|
421
427
|
|
|
422
428
|
public createPTable(
|
|
423
429
|
def: PTableDef<PColumn<string | PColumnValues | DataInfo<string>>>,
|
|
424
430
|
): PTableHandle {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
)
|
|
429
|
-
const { key, unref } = this.env.driverKit.pFrameDriver.createPTable(
|
|
430
|
-
mapPTableDef(def, (c) => mapPObjectData(c, (d) => this.transformInputPData(d))),
|
|
431
|
+
using guard = new PoolEntryGuard(
|
|
432
|
+
this.env.driverKit.pFrameDriver.createPTable(
|
|
433
|
+
mapPTableDef(def, (c) => mapPObjectData(c, (d) => this.transformInputPData(d))),
|
|
434
|
+
),
|
|
431
435
|
);
|
|
432
|
-
this.
|
|
433
|
-
return key;
|
|
436
|
+
this.requireComputableCtx.addOnDestroy(guard.entry.unref);
|
|
437
|
+
return guard.keep().key;
|
|
434
438
|
}
|
|
439
|
+
|
|
435
440
|
public createPTableV2(
|
|
436
441
|
def: PTableDefV2<PColumn<string | PColumnValues | DataInfo<string>>>,
|
|
437
442
|
): PTableHandle {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
)
|
|
442
|
-
const { key, unref } = this.env.driverKit.pFrameDriver.createPTableV2(
|
|
443
|
-
mapPTableDefV2(def, (c) => mapPObjectData(c, (d) => this.transformInputPData(d))),
|
|
443
|
+
using guard = new PoolEntryGuard(
|
|
444
|
+
this.env.driverKit.pFrameDriver.createPTableV2(
|
|
445
|
+
mapPTableDefV2(def, (c) => mapPObjectData(c, (d) => this.transformInputPData(d))),
|
|
446
|
+
),
|
|
444
447
|
);
|
|
445
|
-
this.
|
|
446
|
-
return key;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
//
|
|
450
|
-
// Spec Frames
|
|
451
|
-
//
|
|
452
|
-
|
|
453
|
-
public createSpecFrame(specs: Record<string, PColumnSpec>): SpecFrameHandle {
|
|
454
|
-
const handle = this.specDriver.createSpecFrame(specs);
|
|
455
|
-
this.computableCtx?.addOnDestroy(() => this.specDriver.disposeSpecFrame(handle));
|
|
456
|
-
return handle;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
public specFrameDiscoverColumns(
|
|
460
|
-
handle: SpecFrameHandle,
|
|
461
|
-
request: DiscoverColumnsRequest,
|
|
462
|
-
): DiscoverColumnsResponse {
|
|
463
|
-
return this.specDriver.specFrameDiscoverColumns(handle as SpecFrameHandle, request);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
public disposeSpecFrame(handle: SpecFrameHandle): void {
|
|
467
|
-
this.specDriver.disposeSpecFrame(handle as SpecFrameHandle);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
public expandAxes(spec: AxesSpec): AxesId {
|
|
471
|
-
return this.specDriver.expandAxes(spec);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
public collapseAxes(ids: AxesId): AxesSpec {
|
|
475
|
-
return this.specDriver.collapseAxes(ids);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
public findAxis(spec: AxesSpec, selector: SingleAxisSelector): number {
|
|
479
|
-
return this.specDriver.findAxis(spec, selector);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
public findTableColumn(tableSpec: PTableColumnSpec[], selector: PTableColumnId): number {
|
|
483
|
-
return this.specDriver.findTableColumn(tableSpec, selector);
|
|
448
|
+
this.requireComputableCtx.addOnDestroy(guard.entry.unref);
|
|
449
|
+
return guard.keep().key;
|
|
484
450
|
}
|
|
485
451
|
|
|
486
452
|
/**
|
|
@@ -597,49 +563,32 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
597
563
|
if (checkBlockFlag(this.featureFlags, "supportsLazyState")) {
|
|
598
564
|
// injecting lazy state functions
|
|
599
565
|
exportCtxFunction("args", () => {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
`Add dummy call to ctx.args outside the future lambda. Can't be directly used in this context.`,
|
|
603
|
-
);
|
|
604
|
-
const args = this.blockCtx.args(this.computableCtx);
|
|
566
|
+
const cCtx = this.requireComputableCtx;
|
|
567
|
+
const args = this.blockCtx.args(cCtx);
|
|
605
568
|
return args === undefined ? vm.undefined : vm.newString(args);
|
|
606
569
|
});
|
|
607
570
|
exportCtxFunction("blockStorage", () => {
|
|
608
|
-
|
|
609
|
-
throw new Error(
|
|
610
|
-
`Add dummy call to ctx.blockStorage outside the future lambda. Can't be directly used in this context.`,
|
|
611
|
-
);
|
|
612
|
-
return vm.newString(this.blockCtx.blockStorage(this.computableCtx) ?? "{}");
|
|
571
|
+
return vm.newString(this.blockCtx.blockStorage(this.requireComputableCtx) ?? "{}");
|
|
613
572
|
});
|
|
614
573
|
exportCtxFunction("data", () => {
|
|
615
|
-
|
|
616
|
-
throw new Error(
|
|
617
|
-
`Add dummy call to ctx.data outside the future lambda. Can't be directly used in this context.`,
|
|
618
|
-
);
|
|
619
|
-
return vm.newString(this.blockCtx.data(this.computableCtx) ?? "{}");
|
|
574
|
+
return vm.newString(this.blockCtx.data(this.requireComputableCtx) ?? "{}");
|
|
620
575
|
});
|
|
621
576
|
exportCtxFunction("activeArgs", () => {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
`Add dummy call to ctx.activeArgs outside the future lambda. Can't be directly used in this context.`,
|
|
625
|
-
);
|
|
626
|
-
const res = this.blockCtx.activeArgs(this.computableCtx);
|
|
577
|
+
const cCtx = this.requireComputableCtx;
|
|
578
|
+
const res = this.blockCtx.activeArgs(cCtx);
|
|
627
579
|
return res === undefined ? vm.undefined : vm.newString(res);
|
|
628
580
|
});
|
|
629
581
|
// For v1/v2 blocks, also inject uiState (extracted from state.uiState)
|
|
630
582
|
if (isLegacyBlock) {
|
|
631
583
|
exportCtxFunction("uiState", () => {
|
|
632
|
-
|
|
633
|
-
throw new Error(
|
|
634
|
-
`Add dummy call to ctx.uiState outside the future lambda. Can't be directly used in this context.`,
|
|
635
|
-
);
|
|
636
|
-
return vm.newString(extractUiState(this.blockCtx.data(this.computableCtx)));
|
|
584
|
+
return vm.newString(extractUiState(this.blockCtx.data(this.requireComputableCtx)));
|
|
637
585
|
});
|
|
638
586
|
}
|
|
639
587
|
} else {
|
|
640
|
-
const
|
|
641
|
-
const
|
|
642
|
-
const
|
|
588
|
+
const cCtx = this.requireComputableCtx;
|
|
589
|
+
const args = this.blockCtx.args(cCtx);
|
|
590
|
+
const activeArgs = this.blockCtx.activeArgs(cCtx);
|
|
591
|
+
const data = this.blockCtx.data(cCtx);
|
|
643
592
|
if (args !== undefined) {
|
|
644
593
|
vm.setProp(configCtx, "args", localScope.manage(vm.newString(args)));
|
|
645
594
|
}
|
|
@@ -888,10 +837,6 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
888
837
|
);
|
|
889
838
|
});
|
|
890
839
|
|
|
891
|
-
//
|
|
892
|
-
// PFrames / PTables
|
|
893
|
-
//
|
|
894
|
-
|
|
895
840
|
exportCtxFunction("createPFrame", (def) => {
|
|
896
841
|
return parent.exportSingleValue(
|
|
897
842
|
this.createPFrame(
|
|
@@ -920,63 +865,52 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
920
865
|
});
|
|
921
866
|
|
|
922
867
|
//
|
|
923
|
-
//
|
|
868
|
+
// Services
|
|
924
869
|
//
|
|
925
870
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
undefined,
|
|
930
|
-
);
|
|
931
|
-
});
|
|
932
|
-
|
|
933
|
-
exportCtxFunction("specFrameDiscoverColumns", (handle, request) => {
|
|
934
|
-
return parent.exportObjectViaJson(
|
|
935
|
-
this.specFrameDiscoverColumns(
|
|
936
|
-
vm.getString(handle) as SpecFrameHandle,
|
|
937
|
-
parent.importObjectViaJson(request) as DiscoverColumnsRequest,
|
|
938
|
-
),
|
|
939
|
-
undefined,
|
|
940
|
-
);
|
|
941
|
-
});
|
|
942
|
-
|
|
943
|
-
exportCtxFunction("disposeSpecFrame", (handle) => {
|
|
944
|
-
this.disposeSpecFrame(vm.getString(handle) as SpecFrameHandle);
|
|
945
|
-
});
|
|
871
|
+
const requiredServiceNames = new Set(resolveRequiredServices(this.featureFlags) as string[]);
|
|
872
|
+
const serviceFunctions = new Map<string, VmFunctionImplementation<QuickJSHandle>>();
|
|
873
|
+
const injectors = getServiceInjectors();
|
|
946
874
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
875
|
+
for (const [key, serviceId] of Object.entries(Services)) {
|
|
876
|
+
if (!requiredServiceNames.has(serviceId)) continue;
|
|
877
|
+
const injector = injectors[key as keyof typeof injectors];
|
|
878
|
+
try {
|
|
879
|
+
const methods = injector({ host: this, vm: parent });
|
|
880
|
+
for (const [method, fn] of Object.entries(methods)) {
|
|
881
|
+
serviceFunctions.set(serviceFnKey(serviceId, method), fn);
|
|
882
|
+
}
|
|
883
|
+
} catch (e) {
|
|
884
|
+
throw new ServiceInjectionError(`Failed to inject service "${serviceId}"`, { cause: e });
|
|
885
|
+
}
|
|
886
|
+
}
|
|
953
887
|
|
|
954
|
-
exportCtxFunction("
|
|
955
|
-
return parent.exportObjectViaJson(
|
|
956
|
-
this.collapseAxes(parent.importObjectViaJson(ids) as AxesId),
|
|
957
|
-
undefined,
|
|
958
|
-
);
|
|
888
|
+
exportCtxFunction("getServiceNames", () => {
|
|
889
|
+
return parent.exportObjectViaJson([...requiredServiceNames]);
|
|
959
890
|
});
|
|
960
891
|
|
|
961
|
-
exportCtxFunction("
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
)
|
|
967
|
-
|
|
968
|
-
);
|
|
892
|
+
exportCtxFunction("getServiceMethods", (serviceIdHandle) => {
|
|
893
|
+
const serviceId = vm.getString(serviceIdHandle);
|
|
894
|
+
const pfx = serviceFnKey(serviceId);
|
|
895
|
+
const methods = [...serviceFunctions.keys()]
|
|
896
|
+
.filter((k) => k.startsWith(pfx))
|
|
897
|
+
.map((k) => k.slice(pfx.length));
|
|
898
|
+
return parent.exportObjectViaJson(methods, undefined);
|
|
969
899
|
});
|
|
970
900
|
|
|
971
|
-
exportCtxFunction(
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
)
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
901
|
+
exportCtxFunction(
|
|
902
|
+
"callServiceMethod",
|
|
903
|
+
function (this: QuickJSHandle, serviceIdHandle, methodHandle, ...argHandles) {
|
|
904
|
+
const serviceId = vm.getString(serviceIdHandle);
|
|
905
|
+
const method = vm.getString(methodHandle);
|
|
906
|
+
const fn = serviceFunctions.get(serviceFnKey(serviceId, method));
|
|
907
|
+
if (!fn)
|
|
908
|
+
throw new ServiceMethodNotFoundError(
|
|
909
|
+
`Method "${method}" not found on service "${serviceId}"`,
|
|
910
|
+
);
|
|
911
|
+
return fn.call(this, ...argHandles);
|
|
912
|
+
},
|
|
913
|
+
);
|
|
980
914
|
|
|
981
915
|
//
|
|
982
916
|
// Computable
|
package/src/js_render/context.ts
CHANGED
|
@@ -200,7 +200,7 @@ export class JsExecutionContext {
|
|
|
200
200
|
|
|
201
201
|
public exportSingleValue(
|
|
202
202
|
obj: boolean | number | string | null | ArrayBuffer | undefined,
|
|
203
|
-
scope
|
|
203
|
+
scope?: Scope,
|
|
204
204
|
): QuickJSHandle {
|
|
205
205
|
const result = this.tryExportSingleValue(obj, scope);
|
|
206
206
|
if (result === undefined) {
|
|
@@ -211,7 +211,7 @@ export class JsExecutionContext {
|
|
|
211
211
|
return result;
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
public tryExportSingleValue(obj: unknown, scope
|
|
214
|
+
public tryExportSingleValue(obj: unknown, scope?: Scope): QuickJSHandle | undefined {
|
|
215
215
|
let handle: QuickJSHandle;
|
|
216
216
|
let manage = false;
|
|
217
217
|
switch (typeof obj) {
|
|
@@ -244,13 +244,13 @@ export class JsExecutionContext {
|
|
|
244
244
|
return manage && scope != undefined ? scope.manage(handle) : handle;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
public exportObjectUniversal(obj: unknown, scope
|
|
247
|
+
public exportObjectUniversal(obj: unknown, scope?: Scope): QuickJSHandle {
|
|
248
248
|
const simpleHandle = this.tryExportSingleValue(obj, scope);
|
|
249
249
|
if (simpleHandle !== undefined) return simpleHandle;
|
|
250
250
|
return this.exportObjectViaJson(obj, scope);
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
public exportObjectViaJson(obj: unknown, scope
|
|
253
|
+
public exportObjectViaJson(obj: unknown, scope?: Scope): QuickJSHandle {
|
|
254
254
|
const t0 = performance.now();
|
|
255
255
|
const json = JSON.stringify(obj);
|
|
256
256
|
this.stats.serInBytes += json.length;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { QuickJSHandle, VmFunctionImplementation } from "quickjs-emscripten";
|
|
2
|
+
import type { InferServiceModel, ServiceBrand } from "@milaboratories/pl-model-common";
|
|
3
|
+
import { Services, ServiceNotRegisteredError } from "@milaboratories/pl-model-common";
|
|
4
|
+
import type {
|
|
5
|
+
AxesId,
|
|
6
|
+
AxesSpec,
|
|
7
|
+
DataInfo,
|
|
8
|
+
PColumn,
|
|
9
|
+
PColumnSpec,
|
|
10
|
+
PColumnValues,
|
|
11
|
+
PTableColumnId,
|
|
12
|
+
PTableColumnSpec,
|
|
13
|
+
SingleAxisSelector,
|
|
14
|
+
DeleteColumnRequest,
|
|
15
|
+
DiscoverColumnsRequest,
|
|
16
|
+
PFrameDef,
|
|
17
|
+
SpecQuery,
|
|
18
|
+
PTableDef,
|
|
19
|
+
PTableDefV2,
|
|
20
|
+
SpecFrameHandle,
|
|
21
|
+
} from "@milaboratories/pl-model-common";
|
|
22
|
+
import { PoolEntryGuard } from "@milaboratories/pl-model-common";
|
|
23
|
+
import type { JsExecutionContext } from "./context";
|
|
24
|
+
import type { ComputableContextHelper } from "./computable_context";
|
|
25
|
+
|
|
26
|
+
type VmMethod = VmFunctionImplementation<QuickJSHandle>;
|
|
27
|
+
|
|
28
|
+
export type ServiceInjectorContext = {
|
|
29
|
+
host: ComputableContextHelper;
|
|
30
|
+
vm: JsExecutionContext;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Each injector returns a record of method name -> VM function implementation.
|
|
34
|
+
// The framework automatically registers them with serviceFnKey(serviceId, methodName).
|
|
35
|
+
export type ServiceInjector<Methods extends string = string> = (
|
|
36
|
+
ctx: ServiceInjectorContext,
|
|
37
|
+
) => Record<Methods, VmMethod>;
|
|
38
|
+
|
|
39
|
+
// Type-safe injector for a specific service — must return all methods from the model interface.
|
|
40
|
+
type ServiceInjectorFor<S extends keyof typeof Services> = ServiceInjector<
|
|
41
|
+
string & keyof InferServiceModel<ServiceBrand<(typeof Services)[S]>>
|
|
42
|
+
>;
|
|
43
|
+
|
|
44
|
+
// Complete, type-checked injector map.
|
|
45
|
+
// Adding a service to Services without an entry here is a compile-time error.
|
|
46
|
+
// Missing a method from the interface is also a compile-time error.
|
|
47
|
+
type ServiceInjectorMap = { [K in keyof typeof Services]: ServiceInjectorFor<K> };
|
|
48
|
+
|
|
49
|
+
export function getServiceInjectors(): ServiceInjectorMap {
|
|
50
|
+
return {
|
|
51
|
+
PFrameSpec: ({ host, vm }: ServiceInjectorContext) => {
|
|
52
|
+
const driver = host.serviceRegistry.get(Services.PFrameSpec);
|
|
53
|
+
if (!driver)
|
|
54
|
+
throw new ServiceNotRegisteredError(
|
|
55
|
+
`Service "${Services.PFrameSpec}" has no factory in ModelServiceRegistry. Provide a non-null factory.`,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
createSpecFrame: (specs: QuickJSHandle) => {
|
|
60
|
+
using guard = new PoolEntryGuard(
|
|
61
|
+
driver.createSpecFrame(vm.importObjectViaJson(specs) as Record<string, PColumnSpec>),
|
|
62
|
+
);
|
|
63
|
+
host.addOnDestroy(guard.entry.unref);
|
|
64
|
+
const entry = guard.keep();
|
|
65
|
+
// TODO: add [Symbol.dispose] once QuickJS supports ES2024 explicit resource management
|
|
66
|
+
const obj = vm.vm.newObject();
|
|
67
|
+
vm.vm.newString(entry.key).consume((k) => vm.vm.setProp(obj, "key", k));
|
|
68
|
+
vm.vm
|
|
69
|
+
.newFunction("unref", () => {
|
|
70
|
+
entry.unref();
|
|
71
|
+
})
|
|
72
|
+
.consume((fn) => vm.vm.setProp(obj, "unref", fn));
|
|
73
|
+
return obj;
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
discoverColumns: (handle: QuickJSHandle, request: QuickJSHandle) =>
|
|
77
|
+
vm.exportObjectViaJson(
|
|
78
|
+
driver.discoverColumns(
|
|
79
|
+
vm.vm.getString(handle) as SpecFrameHandle,
|
|
80
|
+
vm.importObjectViaJson(request) as DiscoverColumnsRequest,
|
|
81
|
+
),
|
|
82
|
+
),
|
|
83
|
+
|
|
84
|
+
deleteColumn: (handle: QuickJSHandle, request: QuickJSHandle) =>
|
|
85
|
+
vm.exportObjectViaJson(
|
|
86
|
+
driver.deleteColumn(
|
|
87
|
+
vm.vm.getString(handle) as SpecFrameHandle,
|
|
88
|
+
vm.importObjectViaJson(request) as DeleteColumnRequest,
|
|
89
|
+
),
|
|
90
|
+
),
|
|
91
|
+
|
|
92
|
+
evaluateQuery: (handle: QuickJSHandle, request: QuickJSHandle) =>
|
|
93
|
+
vm.exportObjectViaJson(
|
|
94
|
+
driver.evaluateQuery(
|
|
95
|
+
vm.vm.getString(handle) as SpecFrameHandle,
|
|
96
|
+
vm.importObjectViaJson(request) as SpecQuery,
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
|
|
100
|
+
expandAxes: (spec: QuickJSHandle) =>
|
|
101
|
+
vm.exportObjectViaJson(driver.expandAxes(vm.importObjectViaJson(spec) as AxesSpec)),
|
|
102
|
+
|
|
103
|
+
collapseAxes: (ids: QuickJSHandle) =>
|
|
104
|
+
vm.exportObjectViaJson(driver.collapseAxes(vm.importObjectViaJson(ids) as AxesId)),
|
|
105
|
+
|
|
106
|
+
findAxis: (spec: QuickJSHandle, selector: QuickJSHandle) =>
|
|
107
|
+
vm.exportSingleValue(
|
|
108
|
+
driver.findAxis(
|
|
109
|
+
vm.importObjectViaJson(spec) as AxesSpec,
|
|
110
|
+
vm.importObjectViaJson(selector) as SingleAxisSelector,
|
|
111
|
+
),
|
|
112
|
+
),
|
|
113
|
+
|
|
114
|
+
findTableColumn: (tableSpec: QuickJSHandle, selector: QuickJSHandle) =>
|
|
115
|
+
vm.exportSingleValue(
|
|
116
|
+
driver.findTableColumn(
|
|
117
|
+
vm.importObjectViaJson(tableSpec) as PTableColumnSpec[],
|
|
118
|
+
vm.importObjectViaJson(selector) as PTableColumnId,
|
|
119
|
+
),
|
|
120
|
+
),
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
PFrame: ({ host, vm }: ServiceInjectorContext) => ({
|
|
125
|
+
createPFrame: (def: QuickJSHandle) =>
|
|
126
|
+
vm.exportSingleValue(
|
|
127
|
+
host.createPFrame(
|
|
128
|
+
vm.importObjectViaJson(def) as PFrameDef<
|
|
129
|
+
PColumn<string | PColumnValues | DataInfo<string>>
|
|
130
|
+
>,
|
|
131
|
+
),
|
|
132
|
+
),
|
|
133
|
+
|
|
134
|
+
createPTable: (def: QuickJSHandle) =>
|
|
135
|
+
vm.exportSingleValue(
|
|
136
|
+
host.createPTable(
|
|
137
|
+
vm.importObjectViaJson(def) as PTableDef<
|
|
138
|
+
PColumn<string | PColumnValues | DataInfo<string>>
|
|
139
|
+
>,
|
|
140
|
+
),
|
|
141
|
+
),
|
|
142
|
+
|
|
143
|
+
createPTableV2: (def: QuickJSHandle) =>
|
|
144
|
+
vm.exportSingleValue(
|
|
145
|
+
host.createPTableV2(
|
|
146
|
+
vm.importObjectViaJson(def) as PTableDefV2<
|
|
147
|
+
PColumn<string | PColumnValues | DataInfo<string>>
|
|
148
|
+
>,
|
|
149
|
+
),
|
|
150
|
+
),
|
|
151
|
+
}),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -32,6 +32,11 @@ import type { MiddleLayerDriverKit } from "./driver_kit";
|
|
|
32
32
|
import { initDriverKit } from "./driver_kit";
|
|
33
33
|
import type { BlockCodeFeatureFlags, DriverKit, SupportedRequirement } from "@platforma-sdk/model";
|
|
34
34
|
import { RuntimeCapabilities } from "@platforma-sdk/model";
|
|
35
|
+
import {
|
|
36
|
+
type ModelServiceRegistry,
|
|
37
|
+
registerServiceCapabilities,
|
|
38
|
+
} from "@milaboratories/pl-model-common";
|
|
39
|
+
import { createModelServiceRegistry } from "../service_factories";
|
|
35
40
|
import type { DownloadUrlDriver } from "@milaboratories/pl-drivers";
|
|
36
41
|
import { V2RegistryProvider } from "../block_registry";
|
|
37
42
|
import type { Dispatcher } from "undici";
|
|
@@ -54,6 +59,7 @@ export interface MiddleLayerEnvironment {
|
|
|
54
59
|
readonly blockUpdateWatcher: BlockUpdateWatcher;
|
|
55
60
|
readonly quickJs: QuickJSWASMModule;
|
|
56
61
|
readonly driverKit: MiddleLayerDriverKit;
|
|
62
|
+
readonly serviceRegistry: ModelServiceRegistry;
|
|
57
63
|
readonly projectHelper: ProjectHelper;
|
|
58
64
|
}
|
|
59
65
|
|
|
@@ -115,6 +121,11 @@ export class MiddleLayer {
|
|
|
115
121
|
return this.env.driverKit;
|
|
116
122
|
}
|
|
117
123
|
|
|
124
|
+
/** Returns the service registry for service introspection. */
|
|
125
|
+
public get serviceRegistry(): ModelServiceRegistry {
|
|
126
|
+
return this.env.serviceRegistry;
|
|
127
|
+
}
|
|
128
|
+
|
|
118
129
|
//
|
|
119
130
|
// Project List Manipulation
|
|
120
131
|
//
|
|
@@ -335,8 +346,13 @@ export class MiddleLayer {
|
|
|
335
346
|
runtimeCapabilities.addSupportedRequirement("requiresModelAPIVersion", 1);
|
|
336
347
|
runtimeCapabilities.addSupportedRequirement("requiresModelAPIVersion", 2);
|
|
337
348
|
runtimeCapabilities.addSupportedRequirement("requiresCreatePTable", 2);
|
|
349
|
+
registerServiceCapabilities((flag, value) =>
|
|
350
|
+
runtimeCapabilities.addSupportedRequirement(flag, value),
|
|
351
|
+
);
|
|
338
352
|
// runtime capabilities of the desktop are to be added by the desktop app / test framework
|
|
339
353
|
|
|
354
|
+
const serviceRegistry = createModelServiceRegistry({ logger });
|
|
355
|
+
|
|
340
356
|
const env: MiddleLayerEnvironment = {
|
|
341
357
|
pl,
|
|
342
358
|
blockEventDispatcher: new BlockEventDispatcher(),
|
|
@@ -354,9 +370,11 @@ export class MiddleLayer {
|
|
|
354
370
|
preferredUpdateChannel: ops.preferredUpdateChannel,
|
|
355
371
|
}),
|
|
356
372
|
runtimeCapabilities,
|
|
373
|
+
serviceRegistry,
|
|
357
374
|
quickJs,
|
|
358
375
|
projectHelper: new ProjectHelper(quickJs, logger),
|
|
359
376
|
dispose: async () => {
|
|
377
|
+
await serviceRegistry.dispose();
|
|
360
378
|
await retryHttpDispatcher.destroy();
|
|
361
379
|
await driverKit.dispose();
|
|
362
380
|
},
|
package/src/pool/driver.ts
CHANGED
|
@@ -219,7 +219,6 @@ class BlobStore extends PFrameInternal.BaseObjectStore {
|
|
|
219
219
|
type: "Ok",
|
|
220
220
|
size: blob.size,
|
|
221
221
|
range: translatedRange,
|
|
222
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
223
222
|
data: Readable.fromWeb(data),
|
|
224
223
|
});
|
|
225
224
|
},
|
|
@@ -268,6 +267,7 @@ class RemoteBlobProviderImpl implements RemoteBlobProvider<PlTreeEntry> {
|
|
|
268
267
|
|
|
269
268
|
async [Symbol.asyncDispose](): Promise<void> {
|
|
270
269
|
await this.server.stop();
|
|
270
|
+
await this.pool[Symbol.asyncDispose]();
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
|