@milaboratories/pl-middle-layer 1.38.0 → 1.39.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.
@@ -3,6 +3,54 @@ import { BlockCodeWithInfo, ConfigRenderLambda } from '@platforma-sdk/model';
3
3
  import { ComputableRenderingOps, Computable } from '@milaboratories/computable';
4
4
  import { QuickJSWASMModule } from 'quickjs-emscripten';
5
5
  import { BlockContextAny } from '../middle_layer/block_ctx';
6
+ /**
7
+ * Creates a Computable that executes a render function (`fh`) from a block's code in a QuickJS virtual machine.
8
+ * This function handles both synchronous and asynchronous execution patterns of the sandboxed JS code.
9
+ *
10
+ * The overall data flow is as follows:
11
+ * 1. A QuickJS VM is initialized.
12
+ * 2. A `JsExecutionContext` is created to bridge the host (TypeScript) and guest (QuickJS) environments. It injects a
13
+ * context object (`cfgRenderCtx`) into the VM, providing helper methods for the sandboxed code to interact with the
14
+ * platform (e.g., to request data).
15
+ * 3. The block's Javascript bundle is evaluated in the VM.
16
+ * 4. The specified render function (`fh.handle`) is executed.
17
+ *
18
+ * Two execution paths are possible depending on the behavior of the render function:
19
+ *
20
+ * ### Synchronous Path
21
+ * If the render function computes its result without requesting any external data requiring asynchronous calculations,
22
+ * it executes synchronously.
23
+ * - The `computablesToResolve` map in `JsExecutionContext` remains empty.
24
+ * - The function returns an object with an `ir` field holding the result (`{ ir: importedResult }`).
25
+ * Since `postprocessValue` is not specified, `ir` is treated as the final resolved value of the Computable.
26
+ * - The QuickJS VM is disposed of immediately as it's no longer needed.
27
+ *
28
+ * ### Asynchronous Path (with `postprocessValue`)
29
+ * If the render function needs external data requiring asynchronous calculations (e.g., fetching a file), it calls
30
+ * one of the injected helper methods. These methods don't return data directly. Instead, they:
31
+ * a. Create a new `Computable` for the data request (e.g., to fetch a blob).
32
+ * b. Register this new `Computable` in the `computablesToResolve` map.
33
+ * c. Return a handle (string) to the sandboxed code.
34
+ *
35
+ * In this case:
36
+ * - The initial execution of the render function returns a scaffold of the final result, which depends on the pending
37
+ * computables.
38
+ * - The `computablesToResolve` map is passed as the `ir` (initial result) to `Computable.makeRaw`.
39
+ * - The `postprocessValue` function is provided to handle the results once the computables are resolved.
40
+ * - The QuickJS VM is kept alive (`keepVmAlive = true`) because its state is needed in `postprocessValue`.
41
+ * - Once the `computable` framework resolves all dependencies, it calls `postprocessValue` with the resolved data.
42
+ * - `postprocessValue` feeds the resolved data back into the VM, allowing the sandboxed code to compute the final
43
+ * result.
44
+ * - The VM is eventually disposed of when the host Computable is destroyed.
45
+ *
46
+ * @param env The middle layer environment.
47
+ * @param ctx The block context.
48
+ * @param fh The config render lambda to execute.
49
+ * @param codeWithInfo The block's code and feature flags.
50
+ * @param configKey A key for the configuration, used for cache busting.
51
+ * @param ops Options for the computable.
52
+ * @returns A `Computable` that will resolve to the result of the lambda execution.
53
+ */
6
54
  export declare function computableFromRF(env: MiddleLayerEnvironment, ctx: BlockContextAny, fh: ConfigRenderLambda, codeWithInfo: BlockCodeWithInfo, configKey: string, ops?: Partial<ComputableRenderingOps>): Computable<unknown>;
7
55
  export declare function executeSingleLambda(quickJs: QuickJSWASMModule, fh: ConfigRenderLambda, codeWithInfo: BlockCodeWithInfo, ...args: unknown[]): unknown;
8
56
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/js_render/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAI5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAGjE,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,sBAAsB,EAC3B,GAAG,EAAE,eAAe,EACpB,EAAE,EAAE,kBAAkB,EACtB,YAAY,EAAE,iBAAiB,EAC/B,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,OAAO,CAAC,sBAAsB,CAAM,GACxC,UAAU,CAAC,OAAO,CAAC,CAiErB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,iBAAiB,EAC1B,EAAE,EAAE,kBAAkB,EACtB,YAAY,EAAE,iBAAiB,EAC/B,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CA4BT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/js_render/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAI5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAYjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,sBAAsB,EAC3B,GAAG,EAAE,eAAe,EACpB,EAAE,EAAE,kBAAkB,EACtB,YAAY,EAAE,iBAAiB,EAC/B,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,OAAO,CAAC,sBAAsB,CAAM,GACxC,UAAU,CAAC,OAAO,CAAC,CA4ErB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,iBAAiB,EAC1B,EAAE,EAAE,kBAAkB,EACtB,YAAY,EAAE,iBAAiB,EAC/B,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CA4BT"}
@@ -3,6 +3,7 @@ import { PlTreeNodeAccessor } from '@milaboratories/pl-tree';
3
3
  import { ComputableCtx } from '@milaboratories/computable';
4
4
  import { CalculateTableDataRequest, CalculateTableDataResponse, FindColumnsRequest, FindColumnsResponse, PColumnIdAndSpec, PColumnSpec, PFrameHandle, PObjectId, PTableColumnSpec, PTableHandle, PTableShape, PTableVector, TableRange, UniqueValuesRequest, UniqueValuesResponse, PFrameDriver as SdkPFrameDriver, PColumn, PFrameDef, PTableDef, PColumnValues, DataInfo } from '@platforma-sdk/model';
5
5
  import { MiLogger } from '@milaboratories/ts-helpers';
6
+ export type PColumnDataUniversal = PlTreeNodeAccessor | DataInfo<PlTreeNodeAccessor> | PColumnValues;
6
7
  /**
7
8
  * Extends public and safe SDK's driver API with methods used internally in the middle
8
9
  * layer and in tests.
@@ -17,9 +18,9 @@ export interface InternalPFrameDriver extends SdkPFrameDriver {
17
18
  */
18
19
  pprofDump(): Promise<Uint8Array>;
19
20
  /** Create a new PFrame */
20
- createPFrame(def: PFrameDef<PlTreeNodeAccessor | PColumnValues | DataInfo<PlTreeNodeAccessor>>, ctx: ComputableCtx): PFrameHandle;
21
+ createPFrame(def: PFrameDef<PColumnDataUniversal>, ctx: ComputableCtx): PFrameHandle;
21
22
  /** Create a new PTable */
22
- createPTable(def: PTableDef<PColumn<PlTreeNodeAccessor | PColumnValues | DataInfo<PlTreeNodeAccessor>>>, ctx: ComputableCtx): PTableHandle;
23
+ createPTable(def: PTableDef<PColumn<PColumnDataUniversal>>, ctx: ComputableCtx): PTableHandle;
23
24
  /** Calculates data for the table and returns complete data representation of it */
24
25
  calculateTableData(handle: PFrameHandle, request: CalculateTableDataRequest<PObjectId>, range: TableRange | undefined, signal?: AbortSignal): Promise<CalculateTableDataResponse>;
25
26
  /** Calculate set of unique values for a specific axis for the filtered set of records */
@@ -47,8 +48,8 @@ export declare class PFrameDriver implements InternalPFrameDriver {
47
48
  pprofDump(): Promise<Uint8Array>;
48
49
  static init(blobDriver: DownloadDriver, logger: MiLogger, spillPath: string): Promise<PFrameDriver>;
49
50
  private constructor();
50
- createPFrame(def: PFrameDef<PlTreeNodeAccessor | PColumnValues | DataInfo<PlTreeNodeAccessor>>, ctx: ComputableCtx): PFrameHandle;
51
- createPTable(rawDef: PTableDef<PColumn<PlTreeNodeAccessor | PColumnValues | DataInfo<PlTreeNodeAccessor>>>, ctx: ComputableCtx): PTableHandle;
51
+ createPFrame(def: PFrameDef<PColumnDataUniversal>, ctx: ComputableCtx): PFrameHandle;
52
+ createPTable(rawDef: PTableDef<PColumn<PColumnDataUniversal>>, ctx: ComputableCtx): PTableHandle;
52
53
  findColumns(handle: PFrameHandle, request: FindColumnsRequest): Promise<FindColumnsResponse>;
53
54
  getColumnSpec(handle: PFrameHandle, columnId: PObjectId): Promise<PColumnSpec>;
54
55
  listColumns(handle: PFrameHandle): Promise<PColumnIdAndSpec[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/pool/driver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/pool/driver.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-middle-layer",
3
- "version": "1.38.0",
3
+ "version": "1.39.0",
4
4
  "description": "Pl Middle Layer",
5
5
  "engines": {
6
6
  "node": ">=20.16.0"
@@ -22,7 +22,7 @@
22
22
  "keywords": [],
23
23
  "license": "UNLICENSED",
24
24
  "dependencies": {
25
- "@milaboratories/pframes-rs-node": "1.0.47",
25
+ "@milaboratories/pframes-rs-node": "1.0.49",
26
26
  "canonicalize": "~2.1.0",
27
27
  "denque": "^2.1.0",
28
28
  "lru-cache": "^11.1.0",
@@ -33,21 +33,21 @@
33
33
  "zod": "~3.23.8",
34
34
  "remeda": "^2.22.6",
35
35
  "@milaboratories/pl-http": "^1.1.4",
36
+ "@platforma-sdk/block-tools": "^2.5.63",
36
37
  "@milaboratories/resolve-helper": "^1.1.0",
37
38
  "@milaboratories/pl-client": "^2.11.2",
38
39
  "@milaboratories/pl-drivers": "^1.6.0",
39
- "@milaboratories/computable": "^2.6.0",
40
- "@platforma-sdk/block-tools": "^2.5.62",
41
- "@milaboratories/pl-model-middle-layer": "~1.7.44",
40
+ "@platforma-sdk/model": "~1.39.0",
42
41
  "@milaboratories/pl-model-common": "~1.16.0",
42
+ "@milaboratories/computable": "^2.6.0",
43
43
  "@milaboratories/pl-tree": "~1.7.0",
44
- "@platforma-sdk/model": "~1.39.0",
45
- "@milaboratories/pl-config": "^1.6.1",
46
- "@milaboratories/ts-helpers": "^1.4.1",
47
- "@milaboratories/pl-errors": "^1.1.9",
48
44
  "@platforma-sdk/workflow-tengo": "4.9.2",
49
45
  "@milaboratories/pl-model-backend": "~1.1.2",
50
- "@milaboratories/pl-deployments": "^2.4.3"
46
+ "@milaboratories/pl-model-middle-layer": "~1.7.45",
47
+ "@milaboratories/pl-errors": "^1.1.9",
48
+ "@milaboratories/pl-config": "^1.6.1",
49
+ "@milaboratories/pl-deployments": "^2.4.3",
50
+ "@milaboratories/ts-helpers": "^1.4.1"
51
51
  },
52
52
  "devDependencies": {
53
53
  "semver": "^7.7.2",
@@ -9,6 +9,63 @@ import { JsExecutionContext } from './context';
9
9
  import type { BlockContextAny } from '../middle_layer/block_ctx';
10
10
  import { getDebugFlags } from '../debug';
11
11
 
12
+ function logOutputStatus(handle: string, renderedResult: unknown, stable: boolean, recalculationCounter: number, unstableMarker?: string) {
13
+ if (getDebugFlags().logOutputStatus && (getDebugFlags().logOutputStatus === 'any' || !stable)) {
14
+ if (stable)
15
+ console.log(`Stable output ${handle} calculated ${renderedResult !== undefined ? 'defined' : 'undefined'}; (#${recalculationCounter})`);
16
+ else
17
+ console.log(`Unstable output ${handle}; marker = ${unstableMarker}; ${renderedResult !== undefined ? 'defined' : 'undefined'} (#${recalculationCounter})`);
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Creates a Computable that executes a render function (`fh`) from a block's code in a QuickJS virtual machine.
23
+ * This function handles both synchronous and asynchronous execution patterns of the sandboxed JS code.
24
+ *
25
+ * The overall data flow is as follows:
26
+ * 1. A QuickJS VM is initialized.
27
+ * 2. A `JsExecutionContext` is created to bridge the host (TypeScript) and guest (QuickJS) environments. It injects a
28
+ * context object (`cfgRenderCtx`) into the VM, providing helper methods for the sandboxed code to interact with the
29
+ * platform (e.g., to request data).
30
+ * 3. The block's Javascript bundle is evaluated in the VM.
31
+ * 4. The specified render function (`fh.handle`) is executed.
32
+ *
33
+ * Two execution paths are possible depending on the behavior of the render function:
34
+ *
35
+ * ### Synchronous Path
36
+ * If the render function computes its result without requesting any external data requiring asynchronous calculations,
37
+ * it executes synchronously.
38
+ * - The `computablesToResolve` map in `JsExecutionContext` remains empty.
39
+ * - The function returns an object with an `ir` field holding the result (`{ ir: importedResult }`).
40
+ * Since `postprocessValue` is not specified, `ir` is treated as the final resolved value of the Computable.
41
+ * - The QuickJS VM is disposed of immediately as it's no longer needed.
42
+ *
43
+ * ### Asynchronous Path (with `postprocessValue`)
44
+ * If the render function needs external data requiring asynchronous calculations (e.g., fetching a file), it calls
45
+ * one of the injected helper methods. These methods don't return data directly. Instead, they:
46
+ * a. Create a new `Computable` for the data request (e.g., to fetch a blob).
47
+ * b. Register this new `Computable` in the `computablesToResolve` map.
48
+ * c. Return a handle (string) to the sandboxed code.
49
+ *
50
+ * In this case:
51
+ * - The initial execution of the render function returns a scaffold of the final result, which depends on the pending
52
+ * computables.
53
+ * - The `computablesToResolve` map is passed as the `ir` (initial result) to `Computable.makeRaw`.
54
+ * - The `postprocessValue` function is provided to handle the results once the computables are resolved.
55
+ * - The QuickJS VM is kept alive (`keepVmAlive = true`) because its state is needed in `postprocessValue`.
56
+ * - Once the `computable` framework resolves all dependencies, it calls `postprocessValue` with the resolved data.
57
+ * - `postprocessValue` feeds the resolved data back into the VM, allowing the sandboxed code to compute the final
58
+ * result.
59
+ * - The VM is eventually disposed of when the host Computable is destroyed.
60
+ *
61
+ * @param env The middle layer environment.
62
+ * @param ctx The block context.
63
+ * @param fh The config render lambda to execute.
64
+ * @param codeWithInfo The block's code and feature flags.
65
+ * @param configKey A key for the configuration, used for cache busting.
66
+ * @param ops Options for the computable.
67
+ * @returns A `Computable` that will resolve to the result of the lambda execution.
68
+ */
12
69
  export function computableFromRF(
13
70
  env: MiddleLayerEnvironment,
14
71
  ctx: BlockContextAny,
@@ -27,59 +84,70 @@ export function computableFromRF(
27
84
  console.log(`Block lambda recalculation : ${key} (${cCtx.changeSourceMarker}; ${cCtx.bodyInvocations} invocations)`);
28
85
 
29
86
  const scope = new Scope();
30
- cCtx.addOnDestroy(() => scope.dispose());
31
-
32
- const runtime = scope.manage(env.quickJs.newRuntime());
33
- runtime.setMemoryLimit(1024 * 1024 * 8);
34
- runtime.setMaxStackSize(1024 * 320);
35
-
36
- let deadlineSettings: DeadlineSettings | undefined;
37
- runtime.setInterruptHandler(() => {
38
- if (deadlineSettings === undefined) return false;
39
- if (Date.now() > deadlineSettings.deadline) return true;
40
- return false;
87
+ let keepVmAlive = false;
88
+ cCtx.addOnDestroy(() => {
89
+ // If keepVmAlive is false, the scope will be disposed by the finally block,
90
+ // no need to dispose it here.
91
+ if (keepVmAlive) scope.dispose();
41
92
  });
42
- const vm = scope.manage(runtime.newContext());
43
- const rCtx = new JsExecutionContext(scope, vm,
44
- (s) => { deadlineSettings = s; },
45
- featureFlags,
46
- { computableCtx: cCtx, blockCtx: ctx, mlEnv: env });
47
-
48
- rCtx.evaluateBundle(code.content);
49
- const result = rCtx.runCallback(fh.handle);
50
-
51
- rCtx.resetComputableCtx();
52
-
53
- let recalculationCounter = 0;
54
-
55
- if (getDebugFlags().logOutputStatus === 'any')
56
- console.log(`Output ${fh.handle} scaffold calculated.`);
57
93
 
58
- return {
59
- ir: rCtx.computableHelper!.computablesToResolve,
60
- postprocessValue: (resolved: Record<string, unknown>, { unstableMarker, stable }) => {
94
+ try {
95
+ const runtime = scope.manage(env.quickJs.newRuntime());
96
+ runtime.setMemoryLimit(1024 * 1024 * 8);
97
+ runtime.setMaxStackSize(1024 * 320);
98
+
99
+ let deadlineSettings: DeadlineSettings | undefined;
100
+ runtime.setInterruptHandler(() => {
101
+ if (deadlineSettings === undefined) return false;
102
+ if (Date.now() > deadlineSettings.deadline) return true;
103
+ return false;
104
+ });
105
+ const vm = scope.manage(runtime.newContext());
106
+ const rCtx = new JsExecutionContext(scope, vm,
107
+ (s) => { deadlineSettings = s; },
108
+ featureFlags,
109
+ { computableCtx: cCtx, blockCtx: ctx, mlEnv: env });
110
+
111
+ rCtx.evaluateBundle(code.content);
112
+ const result = rCtx.runCallback(fh.handle);
113
+
114
+ rCtx.resetComputableCtx();
115
+
116
+ const toBeResolved = rCtx.computableHelper!.computablesToResolve;
117
+
118
+ if (Object.keys(toBeResolved).length === 0) {
119
+ const importedResult = rCtx.importObjectUniversal(result);
120
+ logOutputStatus(fh.handle, importedResult, cCtx.unstableMarker === undefined, -1, cCtx.unstableMarker);
121
+ return { ir: importedResult };
122
+ }
123
+
124
+ let recalculationCounter = 0;
125
+ if (getDebugFlags().logOutputStatus)
126
+ console.log(`Output ${fh.handle} scaffold calculated (not all computables resolved yet).`);
127
+ keepVmAlive = true;
128
+
129
+ return {
130
+ ir: toBeResolved,
131
+ postprocessValue: (resolved: Record<string, unknown>, { unstableMarker, stable }) => {
61
132
  // resolving futures
62
- for (const [handle, value] of Object.entries(resolved)) rCtx.runCallback(handle, value);
63
-
64
- // rendering result
65
- const renderedResult = rCtx.importObjectUniversal(result);
66
-
67
- // logging
68
- recalculationCounter++;
69
- if (getDebugFlags().logOutputStatus && (getDebugFlags().logOutputStatus === 'any' || !stable)) {
70
- if (stable)
71
- console.log(
72
- `Stable output ${fh.handle} calculated ${renderedResult !== undefined ? 'defined' : 'undefined'}; (#${recalculationCounter})`,
73
- );
74
- else
75
- console.log(
76
- `Unstable output ${fh.handle}; marker = ${unstableMarker}; ${renderedResult !== undefined ? 'defined' : 'undefined'} (#${recalculationCounter})`,
77
- );
78
- }
79
-
80
- return renderedResult;
81
- },
82
- };
133
+ for (const [handle, value] of Object.entries(resolved)) rCtx.runCallback(handle, value);
134
+
135
+ // rendering result
136
+ const renderedResult = rCtx.importObjectUniversal(result);
137
+
138
+ // logging
139
+ recalculationCounter++;
140
+ logOutputStatus(fh.handle, renderedResult, stable, recalculationCounter, unstableMarker);
141
+
142
+ return renderedResult;
143
+ },
144
+ };
145
+ } catch (e) {
146
+ keepVmAlive = false;
147
+ throw e;
148
+ } finally {
149
+ if (!keepVmAlive) scope.dispose();
150
+ }
83
151
  }, ops);
84
152
  }
85
153