@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,91 @@
1
+ import {
2
+ AnyRef,
3
+ field,
4
+ PlTransaction,
5
+ ResourceRef,
6
+ ResourceType,
7
+ Pl
8
+ } from '@milaboratories/pl-client';
9
+ import { randomUUID } from 'node:crypto';
10
+ import { createRenderTemplate } from './render_template';
11
+
12
+ export const BContextEnd: ResourceType = { name: 'BContextEnd', version: '1' };
13
+ export const BContext: ResourceType = { name: 'BContext', version: '1' };
14
+ export const BContextId = 'id';
15
+ export const BContextParent = 'parent';
16
+ export const BContextMultiParentPrefix = 'parent/';
17
+
18
+ // TODO: add implementation for dual context heavy block.
19
+ // export type BlockType =
20
+ // | 'LightBlock'
21
+ // | 'HeavyBlock'
22
+ // | 'DualContextHeavyBlock';
23
+ // moved to project model ==>>>
24
+
25
+ export type HeavyBlockInputs = {
26
+ args: AnyRef;
27
+ blockId: AnyRef;
28
+ isProduction: AnyRef;
29
+ context: AnyRef;
30
+ };
31
+
32
+ export type HeavyBlockOutputs = {
33
+ context: AnyRef;
34
+ result: AnyRef;
35
+ };
36
+
37
+ export const HeavyBlockOutputNames: (keyof HeavyBlockOutputs)[] = ['context', 'result'];
38
+
39
+ export function createRenderHeavyBlock(
40
+ tx: PlTransaction,
41
+ tpl: AnyRef,
42
+ inputs: HeavyBlockInputs
43
+ ): HeavyBlockOutputs {
44
+ return createRenderTemplate(tx, tpl, true, inputs, HeavyBlockOutputNames);
45
+ }
46
+
47
+ export type LightBlockInputs = {
48
+ args: AnyRef;
49
+ blockId: AnyRef;
50
+ stagingContext: AnyRef;
51
+ productionContext: AnyRef;
52
+ };
53
+
54
+ export type LightBlockOutput = {
55
+ result: AnyRef;
56
+ };
57
+
58
+ export const LightBlockOutputNames: (keyof LightBlockOutput)[] = ['result'];
59
+
60
+ export function createRenderLightBlock(
61
+ tx: PlTransaction,
62
+ tpl: ResourceRef,
63
+ inputs: LightBlockInputs
64
+ ): LightBlockOutput {
65
+ return createRenderTemplate(tx, tpl, true, inputs, LightBlockOutputNames);
66
+ }
67
+
68
+ export function createBContextEnd(tx: PlTransaction): ResourceRef {
69
+ const ctx = tx.createEphemeral(BContextEnd);
70
+ tx.lock(ctx);
71
+ return ctx;
72
+ }
73
+
74
+ export function createBContextFromUpstreams(tx: PlTransaction, upstreamCtxs: AnyRef[]): AnyRef {
75
+ if (upstreamCtxs.length === 0) return createBContextEnd(tx);
76
+
77
+ if (upstreamCtxs.length === 1) return upstreamCtxs[0];
78
+
79
+ const ctx = tx.createEphemeral(BContext);
80
+
81
+ // setting id
82
+ tx.createField(field(ctx, BContextId), 'Input', Pl.createPlString(tx, randomUUID()));
83
+
84
+ // setting parents
85
+ for (let i = 0; i < upstreamCtxs.length; i++)
86
+ tx.createField(field(ctx, `${BContextMultiParentPrefix}${i}`), 'Input', upstreamCtxs[i]);
87
+
88
+ tx.lock(ctx);
89
+
90
+ return ctx;
91
+ }
@@ -0,0 +1,40 @@
1
+ import { AnyRef, field, Pl, PlTransaction } from '@milaboratories/pl-client';
2
+ import { ResourceType } from '@platforma-sdk/model';
3
+
4
+ const EphRenderTemplate: ResourceType = {
5
+ name: 'EphRenderTemplate',
6
+ version: '1'
7
+ };
8
+ const RenderTemplate: ResourceType = {
9
+ name: 'RenderTemplate',
10
+ version: '1'
11
+ };
12
+
13
+ /**
14
+ * Creates render template operation, for a given remplate and inputs.
15
+ *
16
+ * @param tx transaction to use during rendering creation
17
+ * @param tpl reference to the template resource (see {@link loadTemplate})
18
+ * @param ephemeral true for ephemeral templates, false for pure templates
19
+ * @param inputs map of inputs pointing to other resources or fields
20
+ * @param outputNames names of the outputs to collect and return from this method
21
+ */
22
+ export function createRenderTemplate<O extends string>(
23
+ tx: PlTransaction,
24
+ tpl: AnyRef,
25
+ ephemeral: boolean,
26
+ inputs: Pl.PlRecord,
27
+ outputNames: O[]
28
+ ): Record<O, AnyRef> {
29
+ if (outputNames.length === 0) throw new Error('Zero output names provided');
30
+ const rId = ephemeral ? tx.createEphemeral(EphRenderTemplate) : tx.createStruct(RenderTemplate);
31
+
32
+ const tplField = field(rId, 'template');
33
+ const inputsField = field(rId, 'inputs');
34
+
35
+ tx.createField(tplField, 'Input', tpl);
36
+ tx.createField(inputsField, 'Input', Pl.createPlMap(tx, inputs, ephemeral));
37
+ tx.lockInputs(rId);
38
+
39
+ return Pl.futureRecord(tx, rId, outputNames, 'Output', 'outputs/');
40
+ }
@@ -0,0 +1,77 @@
1
+ import { AnyRef, field, Pl, PlTransaction, ResourceType } from '@milaboratories/pl-client';
2
+ import fs from 'node:fs';
3
+ import {
4
+ ExplicitTemplate,
5
+ TemplateFromRegistry,
6
+ TemplateSpecAny,
7
+ TemplateSpecPrepared
8
+ } from '../../model/template_spec';
9
+ import { assertNever } from '@milaboratories/ts-helpers';
10
+
11
+ //
12
+ // Resource schema
13
+ //
14
+
15
+ export const TengoTemplateGet: ResourceType = { name: 'TengoTemplateGet', version: '1' };
16
+ export const TengoTemplateGetRegistry = 'registry';
17
+ export const TengoTemplateGetTemplateURI = 'templateURI';
18
+ export const TengoTemplateGetTemplate = 'template';
19
+
20
+ export const TengoTemplatePack: ResourceType = { name: 'TengoTemplatePack', version: '1' };
21
+ export const TengoTemplatePackConvert: ResourceType = {
22
+ name: 'TengoTemplatePackConvert',
23
+ version: '1'
24
+ };
25
+ export const TengoTemplatePackConvertTemplatePack = 'templatePack';
26
+ export const TengoTemplatePackConvertTemplate = 'template';
27
+
28
+ export async function prepareTemplateSpec(tpl: TemplateSpecAny): Promise<TemplateSpecPrepared> {
29
+ switch (tpl.type) {
30
+ case 'from-file':
31
+ return {
32
+ type: 'explicit',
33
+ content: await fs.promises.readFile(tpl.path)
34
+ };
35
+ case 'from-registry':
36
+ case 'explicit':
37
+ return tpl;
38
+ default:
39
+ return assertNever(tpl);
40
+ }
41
+ }
42
+
43
+ function loadTemplateFromRegistry(tx: PlTransaction, spec: TemplateFromRegistry): AnyRef {
44
+ const getTemplate = tx.createStruct(TengoTemplateGet);
45
+ const registry = field(getTemplate, TengoTemplateGetRegistry);
46
+ const uri = field(getTemplate, TengoTemplateGetTemplateURI);
47
+ const templateFromRegistry = field(getTemplate, TengoTemplateGetTemplate);
48
+
49
+ // Note: it has a resource schema, so platforma creates fields by itself.
50
+
51
+ tx.setField(registry, tx.createValue(Pl.JsonString, Buffer.from(JSON.stringify(spec.registry))));
52
+ tx.setField(uri, tx.createValue(Pl.JsonString, Buffer.from(JSON.stringify(spec.path))));
53
+
54
+ return templateFromRegistry;
55
+ }
56
+
57
+ function loadTemplateFromExplicit(tx: PlTransaction, spec: ExplicitTemplate): AnyRef {
58
+ const templatePack = tx.createValue(TengoTemplatePack, spec.content);
59
+ const templatePackConvert = tx.createStruct(TengoTemplatePackConvert);
60
+ const templatePackField = field(templatePackConvert, TengoTemplatePackConvertTemplatePack);
61
+ const template = field(templatePackConvert, TengoTemplatePackConvertTemplate);
62
+
63
+ tx.setField(templatePackField, templatePack);
64
+
65
+ return template;
66
+ }
67
+
68
+ export function loadTemplate(tx: PlTransaction, spec: TemplateSpecPrepared): AnyRef {
69
+ switch (spec.type) {
70
+ case 'from-registry':
71
+ return loadTemplateFromRegistry(tx, spec);
72
+ case 'explicit':
73
+ return loadTemplateFromExplicit(tx, spec);
74
+ default:
75
+ return assertNever(spec);
76
+ }
77
+ }
@@ -0,0 +1,272 @@
1
+ import {
2
+ AnyRef,
3
+ AnyResourceRef,
4
+ field,
5
+ isNotNullResourceId,
6
+ isNullResourceId,
7
+ PlClient,
8
+ PlTransaction,
9
+ ResourceData,
10
+ ResourceId,
11
+ TestHelpers,
12
+ getField,
13
+ valErr,
14
+ Pl
15
+ } from '@milaboratories/pl-client';
16
+ import { loadTemplate } from './template_loading';
17
+ import { createBContextEnd, createRenderHeavyBlock, HeavyBlockOutputs } from './render_block';
18
+ import { notEmpty, sleep } from '@milaboratories/ts-helpers';
19
+ import { TemplateSpecPrepared } from '../../model/template_spec';
20
+ import {
21
+ TplSpecEnterExplicit,
22
+ TplSpecEnterFromRegistry,
23
+ TplSpecSumExplicit,
24
+ TplSpecSumFromRegistry
25
+ } from '../../test/known_templates';
26
+ import { outputRef } from '../../model/args';
27
+
28
+ describe.each([
29
+ { name: 'explicit', specEnter: TplSpecEnterExplicit, specSum: TplSpecSumExplicit },
30
+ { name: 'from registry', specEnter: TplSpecEnterFromRegistry, specSum: TplSpecSumFromRegistry }
31
+ ])('test render $name', ({ specEnter, specSum }) => {
32
+ const args: {
33
+ name: string;
34
+ createBlocksFn: (tx: PlTransaction) => Promise<HeavyBlockOutputs>;
35
+ expect: (pl: PlClient, tx: PlTransaction) => Promise<boolean>;
36
+ }[] = [
37
+ {
38
+ name: 'test render template staging',
39
+ createBlocksFn: async (tx) => {
40
+ return createEnterNumbers(tx, specEnter, false, 'block1', createBContextEnd(tx), undefined);
41
+ },
42
+
43
+ expect: async (pl, tx) => {
44
+ const { ctx, result, ok } = await expectResultAndContext(tx, pl.clientRoot);
45
+ if (!ok) return false;
46
+
47
+ expectFields(result!, ['dependsOnBlocks']);
48
+ expectFields(ctx!, ['id', 'parent', 'values/column.spec']);
49
+ expect(await expectData(tx, result!, 'dependsOnBlocks')).toStrictEqual([]);
50
+ expect(await expectData(tx, ctx!, 'values/column.spec')).not.toBeUndefined();
51
+
52
+ return true;
53
+ }
54
+ },
55
+ {
56
+ name: 'test render template production',
57
+ createBlocksFn: async (tx) => {
58
+ return createEnterNumbers(
59
+ tx,
60
+ specEnter,
61
+ true,
62
+ 'block1',
63
+ createBContextEnd(tx),
64
+ [42, 2, 3, 7]
65
+ );
66
+ },
67
+ expect: async (pl, tx) => {
68
+ const { ctx, result, ok } = await expectResultAndContext(tx, pl.clientRoot);
69
+ if (!ok) return false;
70
+
71
+ expectFields(result!, ['dependsOnBlocks']);
72
+ expectFields(ctx!, ['id', 'parent', 'values/column.spec', 'values/column.data']);
73
+ expect(await expectData(tx, result!, 'dependsOnBlocks')).toStrictEqual([]);
74
+ expect(await expectData(tx, ctx!, 'values/column.data')).toStrictEqual([42, 2, 3, 7]);
75
+
76
+ return true;
77
+ }
78
+ },
79
+ {
80
+ name: 'test render chain staging',
81
+ createBlocksFn: async (tx) => {
82
+ const enter1 = createEnterNumbers(
83
+ tx,
84
+ specEnter,
85
+ false,
86
+ 'block1',
87
+ createBContextEnd(tx),
88
+ undefined
89
+ );
90
+ const enter2 = createEnterNumbers(
91
+ tx,
92
+ specEnter,
93
+ false,
94
+ 'block2',
95
+ enter1.context,
96
+ undefined
97
+ );
98
+ return createSumNumbers(tx, specSum, false, 'block3', enter2.context, undefined);
99
+ },
100
+
101
+ expect: async (pl, tx) => {
102
+ const { ctx, result, ok } = await expectResultAndContext(tx, pl.clientRoot);
103
+ if (!ok) return false;
104
+
105
+ expectFields(result!, ['dependsOnBlocks', 'opts']);
106
+ expectFields(ctx!, ['id', 'parent']);
107
+ expect(await expectData(tx, result!, 'opts')).toStrictEqual([
108
+ {
109
+ label: 'block1 / column',
110
+ ref: { blockId: 'block1', name: 'column' }
111
+ },
112
+ {
113
+ label: 'block2 / column',
114
+ ref: { blockId: 'block2', name: 'column' }
115
+ }
116
+ ]);
117
+
118
+ return true;
119
+ }
120
+ },
121
+ {
122
+ name: 'test render chain production',
123
+ createBlocksFn: async (tx) => {
124
+ const enter1 = createEnterNumbers(tx, specEnter, true, 'block1', createBContextEnd(tx), [
125
+ 21
126
+ ]);
127
+ const enter2 = createEnterNumbers(tx, specEnter, true, 'block2', enter1.context, [10, 11]);
128
+ return createSumNumbers(tx, specSum, true, 'block3', enter2.context, [
129
+ outputRef('block1', 'column'), //{ blockId: 'block1', name: 'column' },
130
+ outputRef('block2', 'column') //{ blockId: 'block2', name: 'column' }
131
+ ]);
132
+ },
133
+
134
+ expect: async (pl, tx) => {
135
+ const { ctx, result, ok } = await expectResultAndContext(tx, pl.clientRoot);
136
+ if (!ok) return false;
137
+
138
+ expectFields(result!, ['dependsOnBlocks', 'sum']);
139
+ expectFields(ctx!, ['id', 'parent']);
140
+ expect(await expectData(tx, result!, 'sum')).toBe(42);
141
+
142
+ return true;
143
+ }
144
+ }
145
+ ];
146
+
147
+ for (const arg of args) {
148
+ test(
149
+ arg.name,
150
+ async () => {
151
+ await TestHelpers.withTempRoot(async (pl) => {
152
+ const f0 = field(pl.clientRoot, 'result');
153
+ const f1 = field(pl.clientRoot, 'context');
154
+
155
+ await pl.withWriteTx('test', async (tx) => {
156
+ const b = await arg.createBlocksFn(tx);
157
+ tx.createField(f0, 'Dynamic', b.result);
158
+ tx.createField(f1, 'Dynamic', b.context);
159
+ await tx.commit();
160
+ });
161
+
162
+ while (true) {
163
+ if (await pl.withReadTx('test', (tx) => arg.expect(pl, tx))) break;
164
+
165
+ await sleep(30);
166
+ }
167
+ });
168
+ },
169
+ 5000
170
+ );
171
+ }
172
+ });
173
+
174
+ function createEnterNumbers(
175
+ tx: PlTransaction,
176
+ template: TemplateSpecPrepared,
177
+ isProd: boolean,
178
+ blockIdData: string,
179
+ ctx: AnyRef,
180
+ numbers?: number[]
181
+ ) {
182
+ const tpl = loadTemplate(tx, template);
183
+
184
+ const args = tx.createValue(Pl.JsonObject, jsonToData({ numbers: numbers ?? [] }));
185
+ const blockId = tx.createValue(Pl.JsonString, jsonToData(blockIdData));
186
+ const isProduction = Pl.createPlBool(tx, isProd);
187
+
188
+ return createRenderHeavyBlock(tx, tpl, {
189
+ args: args,
190
+ blockId: blockId,
191
+ isProduction: isProduction,
192
+ context: ctx
193
+ });
194
+ }
195
+
196
+ interface Ref {
197
+ blockId: string;
198
+ name: string;
199
+ }
200
+
201
+ function createSumNumbers(
202
+ tx: PlTransaction,
203
+ template: TemplateSpecPrepared,
204
+ isProd: boolean,
205
+ blockIdData: string,
206
+ ctx: AnyRef,
207
+ sources?: Ref[]
208
+ ) {
209
+ const tpl = loadTemplate(tx, template);
210
+
211
+ const args = tx.createValue(Pl.JsonObject, jsonToData({ sources: sources }));
212
+ const blockId = tx.createValue(Pl.JsonString, jsonToData(blockIdData));
213
+ const isProduction = Pl.createPlBool(tx, isProd);
214
+
215
+ return createRenderHeavyBlock(tx, tpl, {
216
+ args: args,
217
+ blockId: blockId,
218
+ isProduction: isProduction,
219
+ context: ctx
220
+ });
221
+ }
222
+
223
+ async function expectResultAndContext(
224
+ tx: PlTransaction,
225
+ root: AnyResourceRef
226
+ ): Promise<{
227
+ ctx?: ResourceData;
228
+ result?: ResourceData;
229
+ ok: boolean;
230
+ }> {
231
+ const fieldData = await tx.getResourceData(root, true);
232
+ expect(isNullResourceId(fieldData.error)).toBe(true);
233
+ expectFields(fieldData, ['context', 'result']);
234
+
235
+ const ctxField = await valErr(tx, getField(fieldData, 'context'));
236
+ expect(ctxField.error).toHaveLength(0);
237
+ const resultField = await valErr(tx, getField(fieldData, 'result'));
238
+ expect(resultField.error).toHaveLength(0);
239
+
240
+ const ctxId = ctxField.valueId;
241
+ const resultId = resultField.valueId;
242
+
243
+ if (isNullResourceId(ctxId) || isNullResourceId(resultId)) return { ok: false };
244
+
245
+ const result = await tx.getResourceData(resultId, true);
246
+ const ctx = await tx.getResourceData(ctxId, true);
247
+
248
+ if (!result.final || !ctx.final) return { ok: false };
249
+
250
+ return { ctx, result, ok: true };
251
+ }
252
+
253
+ async function expectResource(tx: PlTransaction, res: ResourceData, fieldName: string) {
254
+ const f = getField(res, fieldName);
255
+ const ve = await valErr(tx, f);
256
+ expect(ve.error).toHaveLength(0);
257
+ expect(isNotNullResourceId(ve.valueId)).toBeTruthy();
258
+ return await tx.getResourceData(ve.valueId as ResourceId, true);
259
+ }
260
+
261
+ async function expectData(tx: PlTransaction, result: ResourceData, fieldName: string) {
262
+ return expectResource(tx, result, fieldName).then(resDataToJson);
263
+ }
264
+
265
+ function expectFields(res: ResourceData, fields: string[]) {
266
+ const names = res.fields.map((f) => f.name);
267
+ expect(names.sort()).toStrictEqual(fields.sort());
268
+ }
269
+
270
+ const jsonToData = (data: unknown) => Buffer.from(JSON.stringify(data));
271
+
272
+ const resDataToJson = (res: ResourceData) => JSON.parse(notEmpty(res.data).toString());