@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,129 @@
1
+ import { awaitBlockDone, withMl } from './middle_layer.test';
2
+ import { getQuickJS, Scope, shouldInterruptAfterDeadline } from 'quickjs-emscripten';
3
+ import * as tp from 'node:timers/promises';
4
+
5
+ test('test JS render enter numbers', async () => {
6
+ await withMl(async (ml) => {
7
+ const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
8
+ await ml.openProject(pRid1);
9
+ const prj = ml.getOpenedProject(pRid1);
10
+
11
+ const block1Id = await prj.addBlock('Block 1', {
12
+ type: 'from-registry-v1',
13
+ registryUrl: 'https://block.registry.platforma.bio/releases',
14
+ organization: 'milaboratory',
15
+ package: 'enter-numbers',
16
+ version: '1.1.1'
17
+ });
18
+
19
+ await prj.setBlockArgs(block1Id, { numbers: [1, 2, 3] });
20
+ await prj.runBlock(block1Id);
21
+ await awaitBlockDone(prj, block1Id);
22
+ const blockState = prj.getBlockState(block1Id);
23
+ await blockState.awaitStableValue();
24
+ const stateSnapshot = await blockState.getValue();
25
+ expect((stateSnapshot.outputs!['dependsOnBlocks1'] as any).value.length).toBeGreaterThan(5);
26
+ });
27
+ });
28
+
29
+ test.skip('test JS render options', async () => {
30
+ await withMl(async (ml) => {
31
+ const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
32
+ await ml.openProject(pRid1);
33
+ const prj = ml.getOpenedProject(pRid1);
34
+
35
+ const block1Id = await prj.addBlock('Block 1', {
36
+ type: 'from-registry-v1',
37
+ registryUrl: 'https://block.registry.platforma.bio/releases',
38
+ organization: 'milaboratory',
39
+ package: 'enter-numbers',
40
+ version: '1.1.1'
41
+ });
42
+ await prj.setBlockArgs(block1Id, { numbers: [1, 2, 3] });
43
+
44
+ const block2Id = await prj.addBlock('Block 2', {
45
+ type: 'dev',
46
+ folder: '../../blocks-beta/block-beta-sum-numbers'
47
+ });
48
+
49
+ const block2State = prj.getBlockState(block2Id);
50
+
51
+ await tp.setTimeout(2000);
52
+ const block2StateSnapshot1 = await block2State.getValue();
53
+ console.dir(block2StateSnapshot1, { depth: 7 });
54
+
55
+ // expect((stateSnapshot.outputs!['contentAsString1'] as any).value.length).toBeGreaterThan(5);
56
+
57
+ // await prj.runBlock(block1Id);
58
+ // await awaitBlockDone(prj, block1Id);
59
+ // const blockState = prj.getBlockState(block1Id);
60
+ // await blockState.awaitStableValue();
61
+ // const stateSnapshot = await blockState.getValue();
62
+ // console.dir(stateSnapshot, { depth: 5 });
63
+ // expect((stateSnapshot.outputs!['contentAsString1'] as any).value.length).toBeGreaterThan(5);
64
+ });
65
+ });
66
+
67
+ test.skip('test JS render download', async () => {
68
+ await withMl(async (ml) => {
69
+ const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
70
+ await ml.openProject(pRid1);
71
+ const prj = ml.getOpenedProject(pRid1);
72
+
73
+ const block1Id = await prj.addBlock('Block 1', {
74
+ type: 'from-registry-v1',
75
+ registryUrl: 'https://block.registry.platforma.bio/releases',
76
+ organization: 'milaboratory',
77
+ package: 'download-file',
78
+ version: '1.2.0'
79
+ });
80
+
81
+ await prj.setBlockArgs(block1Id, {
82
+ storageId: 'library',
83
+ filePath: 'answer_to_the_ultimate_question.txt'
84
+ });
85
+
86
+ await prj.runBlock(block1Id);
87
+ await awaitBlockDone(prj, block1Id);
88
+ const blockState = prj.getBlockState(block1Id);
89
+ await blockState.awaitStableValue();
90
+ const stateSnapshot = await blockState.getValue();
91
+ console.dir(stateSnapshot, { depth: 5 });
92
+ expect((stateSnapshot.outputs!['contentAsString1'] as any).value.length).toBeGreaterThan(5);
93
+ });
94
+ });
95
+
96
+ test.skip('basic quickjs code', async () => {
97
+ const qJs = await getQuickJS();
98
+ let state = 10;
99
+
100
+ const start = Date.now();
101
+ // const n = 1000;
102
+ // for (let i = 0; i < n; ++i) {
103
+ try {
104
+ Scope.withScope((scope) => {
105
+ const rt = scope.manage(qJs.newRuntime());
106
+ rt.setInterruptHandler(shouldInterruptAfterDeadline(Date.now() + 100));
107
+ rt.setMemoryLimit(1024 * 640);
108
+ rt.setMaxStackSize(1024 * 320);
109
+ const vm = scope.manage(rt.newContext({ intrinsics: { TypedArrays: false } }));
110
+ vm.newFunction('nextId', () => {
111
+ return vm.newNumber(12); // vm.newArrayBuffer(new Uint8Array([1, 2]));
112
+ }).consume((fn) => vm.setProp(vm.global, 'nextId', fn));
113
+ console.log('asdasdas');
114
+
115
+ const nextId = vm.getString(
116
+ scope.manage(
117
+ vm.unwrapResult(
118
+ vm.evalCode(`nextId(); nextId(); i = 0; while (1) { i++ } /* Buffer.from(nextId()); */`)
119
+ )
120
+ )
121
+ );
122
+ console.log(`${nextId}us per iteration`);
123
+ });
124
+ } catch (e: any) {
125
+ console.log(e);
126
+ }
127
+ // }
128
+ console.log(`${Date.now() - start}us per iteration`);
129
+ });
@@ -0,0 +1,19 @@
1
+ import { Code, isFunctionHandle, TypedConfigOrFunctionHandle } from '@platforma-sdk/model';
2
+ import { Computable, ComputableRenderingOps } from '@milaboratories/computable';
3
+ import { computableFromCfg } from '../cfg_render/executor';
4
+ import { MiddleLayerEnvironment } from './middle_layer';
5
+ import { computableFromRF } from '../js_render';
6
+ import { BlockContextAny } from './block_ctx';
7
+
8
+ export function computableFromCfgOrRF(
9
+ env: MiddleLayerEnvironment,
10
+ ctx: BlockContextAny,
11
+ cfgOrFh: TypedConfigOrFunctionHandle,
12
+ code: Code | undefined,
13
+ ops: Partial<ComputableRenderingOps> = {}
14
+ ): Computable<unknown> {
15
+ if (isFunctionHandle(cfgOrFh)) {
16
+ if (code === undefined) throw new Error('No code bundle.');
17
+ return computableFromRF(env, ctx, cfgOrFh, code, ops);
18
+ } else return computableFromCfg(env.driverKit, ctx, cfgOrFh, ops);
19
+ }
@@ -0,0 +1,16 @@
1
+ import { SynchronizedTreeOps, SynchronizedTreeState } from '@milaboratories/pl-tree';
2
+ import { Computable } from '@milaboratories/computable';
3
+
4
+ export type TemporalSynchronizedTreeOps = Pick<
5
+ SynchronizedTreeOps,
6
+ 'pollingInterval' | 'stopPollingDelay'
7
+ >;
8
+
9
+ export interface TreeAndComputable<T, ST extends T = T> {
10
+ tree: SynchronizedTreeState;
11
+ computable: Computable<T, ST>;
12
+ }
13
+
14
+ /** Helper type to express a TreeAndComputable with computable
15
+ * with standard non-undefined-stable type signature. */
16
+ export type TreeAndComputableU<T> = TreeAndComputable<T | undefined, T>;
@@ -0,0 +1,22 @@
1
+ import { PlTreeNodeAccessor } from '@milaboratories/pl-tree';
2
+ import { projectFieldName } from '../model/project_model';
3
+ import { Pl } from '@milaboratories/pl-client';
4
+ import { ifNotUndef } from '../cfg_render/util';
5
+ import { BlockPackInfo } from '../model/block_pack';
6
+ import { normalizeBlockConfig } from '@platforma-sdk/model';
7
+
8
+ export function getBlockCfg(prj: PlTreeNodeAccessor, blockId: string) {
9
+ return ifNotUndef(
10
+ prj
11
+ .traverse(
12
+ {
13
+ field: projectFieldName(blockId, 'blockPack'),
14
+ assertFieldType: 'Dynamic',
15
+ errorIfFieldNotSet: true
16
+ },
17
+ { field: Pl.HolderRefField, assertFieldType: 'Input', errorIfFieldNotFound: true }
18
+ )
19
+ ?.getDataAsJson<BlockPackInfo>()?.config,
20
+ (cfg) => normalizeBlockConfig(cfg)
21
+ );
22
+ }
@@ -0,0 +1,62 @@
1
+ import { assertNever } from '@milaboratories/ts-helpers';
2
+ import { Ref } from '@platforma-sdk/model';
3
+
4
+ export function outputRef(blockId: string, name: string): Ref {
5
+ return { __isRef: true, blockId, name };
6
+ }
7
+
8
+ export function isBlockOutputReference(obj: any): obj is Ref {
9
+ return (
10
+ typeof obj === 'object' &&
11
+ obj !== null &&
12
+ obj.__isRef === true &&
13
+ 'blockId' in obj &&
14
+ 'name' in obj
15
+ );
16
+ }
17
+
18
+ function addAllReferencedBlocks(result: BlockUpstreams, node: any, allowed?: Set<string>) {
19
+ const type = typeof node;
20
+ switch (type) {
21
+ case 'function':
22
+ case 'bigint':
23
+ case 'number':
24
+ case 'string':
25
+ case 'boolean':
26
+ case 'symbol':
27
+ case 'undefined':
28
+ return;
29
+
30
+ case 'object':
31
+ if (node === null) return;
32
+
33
+ if (isBlockOutputReference(node)) {
34
+ if (allowed === undefined || allowed.has(node.blockId)) result.upstreams.add(node.blockId);
35
+ else result.missingReferences = true;
36
+ } else if (Array.isArray(node)) {
37
+ for (const child of node) addAllReferencedBlocks(result, child, allowed);
38
+ } else {
39
+ for (const [, child] of Object.entries(node as object))
40
+ addAllReferencedBlocks(result, child, allowed);
41
+ }
42
+
43
+ return;
44
+
45
+ default:
46
+ assertNever(type);
47
+ }
48
+ }
49
+
50
+ export interface BlockUpstreams {
51
+ /** Direct block dependencies */
52
+ upstreams: Set<string>;
53
+ /** True if not-allowed references was encountered */
54
+ missingReferences: boolean;
55
+ }
56
+
57
+ /** Extracts all resource ids referenced by args object. */
58
+ export function inferAllReferencedBlocks(args: any, allowed?: Set<string>): BlockUpstreams {
59
+ const result = { upstreams: new Set<string>(), missingReferences: false };
60
+ addAllReferencedBlocks(result, args, allowed);
61
+ return result;
62
+ }
@@ -0,0 +1,8 @@
1
+ import { BlockConfigUniversal } from '@platforma-sdk/model';
2
+ import { BlockPackSpec } from '@milaboratories/pl-model-middle-layer';
3
+
4
+ /** Define structure of block-pack data section */
5
+ export interface BlockPackInfo {
6
+ readonly source: BlockPackSpec;
7
+ readonly config: BlockConfigUniversal;
8
+ }
@@ -0,0 +1,52 @@
1
+ import { ExplicitTemplate } from './template_spec';
2
+ import { ResourceType } from '@milaboratories/pl-client';
3
+ import { BlockConfig } from '@platforma-sdk/model';
4
+ import { BlockPackSpec } from '@milaboratories/pl-model-middle-layer';
5
+
6
+ export type FrontendSpec = FrontendFromUrl | FrontendFromFolder;
7
+
8
+ export interface FrontendFromUrlData {
9
+ url: string;
10
+ }
11
+
12
+ export const FrontendFromUrlResourceType: ResourceType = { name: 'Frontend/FromUrl', version: '1' };
13
+
14
+ /** Directs user of the block pack to download the contents from the URL
15
+ * outside the pl infrastructure. */
16
+ export interface FrontendFromUrl extends FrontendFromUrlData {
17
+ type: 'url';
18
+ }
19
+
20
+ export interface FrontendFromFolderData {
21
+ path: string;
22
+ /** HMAC signature of the path using local secret encoded as hex. */
23
+ signature: string;
24
+ }
25
+
26
+ export const FrontendFromFolderResourceType: ResourceType = {
27
+ name: 'Frontend/FromFolder',
28
+ version: '1'
29
+ };
30
+
31
+ /** Directs user of the block pack to load frontend from specific local
32
+ * folder. Signature allows to confirm that this is the same client who
33
+ * added the resource. */
34
+ export interface FrontendFromFolder extends FrontendFromFolderData {
35
+ type: 'local';
36
+ }
37
+
38
+ /** Direct instructions to create block-pack from client. Currently, this
39
+ * is the only block-pack spec that can be directly materialized. */
40
+ export interface BlockPackExplicit {
41
+ type: 'explicit';
42
+ template: ExplicitTemplate;
43
+ config: BlockConfig;
44
+ frontend: FrontendSpec;
45
+ source: BlockPackSpec;
46
+ }
47
+
48
+ /** Block-pack spec that can be materialized in pl. */
49
+ export type BlockPackSpecPrepared = BlockPackExplicit;
50
+
51
+ /** All block-pack specs. */
52
+ export type BlockPackSpecAny = BlockPackSpecPrepared | BlockPackSpec;
@@ -0,0 +1,10 @@
1
+ /** Middle layer provides information about where frontend for a specific block
2
+ * was unpacked and basic meta information about the enclosed UI
3
+ * (i.e. SDK version). */
4
+ export interface FrontendData {
5
+ /** Path in local file system where frontend was unpacked */
6
+ readonly path: string;
7
+
8
+ /** SDK version used by the UI */
9
+ readonly sdkVersion: string;
10
+ }
@@ -0,0 +1,2 @@
1
+ export * from './block_pack_spec';
2
+ export { type ProjectListEntry } from './project_model';
@@ -0,0 +1,26 @@
1
+ import {
2
+ blockFrontendStateKey,
3
+ parseBlockFrontendStateKey,
4
+ parseProjectField,
5
+ ProjectField,
6
+ projectFieldName,
7
+ ProjectStructureKey
8
+ } from './project_model';
9
+ import { randomUUID } from 'node:crypto';
10
+
11
+ test('project field parsing test', () => {
12
+ const field: ProjectField = { fieldName: 'stagingCtx', blockId: randomUUID() };
13
+ const fieldName = projectFieldName(field.blockId, field.fieldName);
14
+ expect(parseProjectField(fieldName)).toStrictEqual(field);
15
+ });
16
+
17
+ test('frontend state key parsing success', () => {
18
+ const frontendStateKey = blockFrontendStateKey('the-block-id');
19
+ const blockId = parseBlockFrontendStateKey(frontendStateKey);
20
+ expect(blockId).toEqual('the-block-id');
21
+ });
22
+
23
+ test('frontend state key parsing failure', () => {
24
+ expect(parseBlockFrontendStateKey('some-random-key')).toBeUndefined();
25
+ expect(parseBlockFrontendStateKey(ProjectStructureKey)).toBeUndefined();
26
+ });
@@ -0,0 +1,142 @@
1
+ import { ResourceId, ResourceType } from '@milaboratories/pl-client';
2
+ import {
3
+ ProjectListEntry as ProjectListEntryFromModel,
4
+ ProjectMeta
5
+ } from '@milaboratories/pl-model-middle-layer';
6
+ import { BlockRenderingMode } from '@platforma-sdk/model';
7
+
8
+ export interface ProjectListEntry extends ProjectListEntryFromModel {
9
+ /** Project resource ID. */
10
+ rid: ResourceId;
11
+ }
12
+
13
+ /** Entry representing a single block in block structure */
14
+ export interface Block {
15
+ /** Unique block id */
16
+ readonly id: string;
17
+
18
+ /** Label shown to the user */
19
+ label: string;
20
+
21
+ /** How to approach block rendering */
22
+ renderingMode: BlockRenderingMode;
23
+ }
24
+
25
+ /** Block group in block structure */
26
+ export interface BlockGroup {
27
+ /** Unique group id */
28
+ readonly id: string;
29
+
30
+ /** Label shown to the user */
31
+ label: string;
32
+
33
+ /** Blocks */
34
+ blocks: Block[];
35
+ }
36
+
37
+ /** Root of block structure value */
38
+ export interface ProjectStructure {
39
+ /** The structure */
40
+ readonly groups: BlockGroup[];
41
+ }
42
+
43
+ export const InitialBlockStructure: ProjectStructure = {
44
+ groups: [{ id: 'default', label: 'Default', blocks: [] }]
45
+ };
46
+
47
+ /** Root of project rendering state */
48
+ export interface ProjectRenderingState {
49
+ /** Timestamp of last staging rendering event */
50
+ stagingRefreshTimestamp: number;
51
+
52
+ /** Blocks with detached production state, i.e. some of the dependencies of
53
+ * such blocks were re-rendered, but their state was left "as is". */
54
+ blocksInLimbo: string[];
55
+ }
56
+
57
+ export const InitialProjectRenderingState: ProjectRenderingState = {
58
+ stagingRefreshTimestamp: 0,
59
+ blocksInLimbo: []
60
+ };
61
+
62
+ export const InitialBlockMeta: ProjectMeta = {
63
+ label: 'New Project'
64
+ };
65
+
66
+ //
67
+ // Pl Model
68
+ //
69
+
70
+ export const ProjectResourceType: ResourceType = { name: 'UserProject', version: '2' };
71
+
72
+ export const SchemaVersionKey = 'SchemaVersion';
73
+ export const SchemaVersionCurrent = '1';
74
+
75
+ export const ProjectCreatedTimestamp = 'ProjectCreated';
76
+ export const ProjectLastModifiedTimestamp = 'ProjectLastModified';
77
+ export const ProjectMetaKey = 'ProjectMeta';
78
+ export const ProjectStructureKey = 'ProjectStructure';
79
+ export const BlockRenderingStateKey = 'BlockRenderingState';
80
+
81
+ export const BlockFrontendStateKeyPrefix = 'BlockFrontendState/';
82
+ export const BlockFrontendStateKeyPattern = /^BlockFrontendState\/(?<blockid>.*)$/;
83
+
84
+ export function blockFrontendStateKey(blockId: string): string {
85
+ return `${BlockFrontendStateKeyPrefix}${blockId}`;
86
+ }
87
+
88
+ export const BlockArgsAuthorKeyPrefix = 'BlockArgsAuthor/';
89
+ export const BlockArgsAuthorKeyPattern = /^BlockArgsAuthor\/(?<blockid>.*)$/;
90
+
91
+ export function blockArgsAuthorKey(blockId: string): string {
92
+ return `${BlockArgsAuthorKeyPrefix}${blockId}`;
93
+ }
94
+
95
+ export const ProjectStructureAuthorKey = 'ProjectStructureAuthor';
96
+
97
+ /** Returns block id, or undefined if key does not match the pattern. */
98
+ export function parseBlockFrontendStateKey(key: string): string | undefined {
99
+ const match = key.match(BlockFrontendStateKeyPattern);
100
+ if (match === null) return undefined;
101
+ return match.groups!['blockid'];
102
+ }
103
+
104
+ export const ServiceTemplateCacheFieldPrefix = '__serviceTemplate_';
105
+
106
+ export function getServiceTemplateField(hash: string): string {
107
+ return `${ServiceTemplateCacheFieldPrefix}${hash}`;
108
+ }
109
+
110
+ export interface ProjectField {
111
+ blockId: string;
112
+ fieldName:
113
+ | 'blockPack'
114
+ | 'prodArgs'
115
+ | 'currentArgs'
116
+ | 'prodCtx'
117
+ | 'prodUiCtx'
118
+ | 'prodOutput'
119
+ | 'prodCtxPrevious'
120
+ | 'prodUiCtxPrevious'
121
+ | 'prodOutputPrevious'
122
+ | 'stagingCtx'
123
+ | 'stagingUiCtx'
124
+ | 'stagingOutput'
125
+ | 'stagingCtxPrevious'
126
+ | 'stagingUiCtxPrevious'
127
+ | 'stagingOutputPrevious';
128
+ }
129
+
130
+ export function projectFieldName(blockId: string, fieldName: ProjectField['fieldName']): string {
131
+ return `${blockId}-${fieldName}`;
132
+ }
133
+
134
+ const projectFieldPattern =
135
+ /^(?<blockId>.*)-(?<fieldName>blockPack|prodArgs|currentArgs|prodCtx|prodUiCtx|prodOutput|prodCtxPrevious|prodUiCtxPrevious|prodOutputPrevious|stagingCtx|stagingUiCtx|stagingOutput|stagingCtxPrevious|stagingUiCtxPrevious|stagingOutputPrevious)$/;
136
+
137
+ export function parseProjectField(name: string): ProjectField | undefined {
138
+ const match = name.match(projectFieldPattern);
139
+ if (match === null) return undefined;
140
+ const { blockId, fieldName } = match.groups!;
141
+ return { blockId, fieldName } as ProjectField;
142
+ }
@@ -0,0 +1,88 @@
1
+ import { ProjectStructure } from './project_model';
2
+ import { graphDiff, productionGraph, stagingGraph } from './project_model_util';
3
+ import { outputRef } from './args';
4
+ import { Ref } from '@platforma-sdk/model';
5
+
6
+ function toRefs(...ids: string[]): Ref[] {
7
+ return ids.map((id) => outputRef(id, ''));
8
+ }
9
+
10
+ function simpleStructure(...ids: string[]): ProjectStructure {
11
+ return {
12
+ groups: [
13
+ {
14
+ id: 'g1',
15
+ label: 'G1',
16
+ blocks: ids.map((id) => ({ id: id, label: id, renderingMode: 'Heavy' }))
17
+ }
18
+ ]
19
+ };
20
+ }
21
+
22
+ describe('simple traverse', () => {
23
+ const struct1: ProjectStructure = simpleStructure('b1', 'b2', 'b3', 'b4', 'b5', 'b6');
24
+ const inputs = new Map<string, Ref[]>();
25
+ inputs.set('b2', toRefs('b1'));
26
+ inputs.set('b4', toRefs('b3'));
27
+ inputs.set('b5', toRefs('b4'));
28
+ inputs.set('b6', toRefs('b2', 'b4'));
29
+ const pGraph1 = productionGraph(struct1, (id) => inputs.get(id) ?? null);
30
+
31
+ test.each([
32
+ { roots: ['b2'], expectedUpstreams: ['b1'], expectedDownstreams: ['b6'] },
33
+ { roots: ['b4'], expectedUpstreams: ['b3'], expectedDownstreams: ['b5', 'b6'] },
34
+ { roots: ['b5'], expectedUpstreams: ['b3', 'b4'], expectedDownstreams: [] },
35
+ { roots: ['b6'], expectedUpstreams: ['b1', 'b2', 'b3', 'b4'], expectedDownstreams: [] }
36
+ ])('$roots', ({ roots, expectedUpstreams, expectedDownstreams }) => {
37
+ const upstreams = pGraph1.traverseIdsExcludingRoots('upstream', ...roots);
38
+ const downstreams = pGraph1.traverseIdsExcludingRoots('downstream', ...roots);
39
+ expect([...upstreams].sort()).toEqual(expectedUpstreams.sort());
40
+ expect([...downstreams].sort()).toEqual(expectedDownstreams.sort());
41
+ });
42
+ });
43
+
44
+ describe('simple diff', () => {
45
+ const struct1: ProjectStructure = simpleStructure('b1', 'b2', 'b3', 'b4');
46
+ const inputs = new Map<string, Ref[]>();
47
+ inputs.set('b2', toRefs('b1'));
48
+ inputs.set('b4', toRefs('b3'));
49
+ const sGraph1 = stagingGraph(struct1);
50
+ const pGraph1 = productionGraph(struct1, (id) => inputs.get(id) ?? null);
51
+
52
+ test.each([
53
+ { struct2a: ['b1', 'b2', 'b3', 'b4'], expectedS: [], expectedP: [] },
54
+ { struct2a: ['b1', 'b2', 'b4'], expectedS: ['b4'], expectedP: ['b4'], onlyA: ['b3'] },
55
+ { struct2a: ['b1', 'b2', 'b4', 'b3'], expectedS: ['b3', 'b4'], expectedP: ['b4'] },
56
+ { struct2a: ['b1', 'b4', 'b2', 'b3'], expectedS: ['b2', 'b3', 'b4'], expectedP: ['b4'] },
57
+ { struct2a: ['b4', 'b1', 'b2', 'b3'], expectedS: ['b1', 'b2', 'b3', 'b4'], expectedP: ['b4'] },
58
+ {
59
+ struct2a: ['b4', 'b2', 'b1', 'b3'],
60
+ expectedS: ['b1', 'b2', 'b3', 'b4'],
61
+ expectedP: ['b2', 'b4']
62
+ },
63
+ {
64
+ struct2a: ['b1', 'b2', 'b4', 'b3', 'b5'],
65
+ expectedS: ['b3', 'b4'],
66
+ expectedP: ['b4'],
67
+ onlyB: ['b5']
68
+ },
69
+ {
70
+ struct2a: ['b2', 'b1', 'b3'],
71
+ expectedS: ['b1', 'b2', 'b3'],
72
+ expectedP: ['b2'],
73
+ onlyA: ['b4']
74
+ }
75
+ ])('$struct2a', ({ struct2a, expectedS, expectedP, onlyA, onlyB }) => {
76
+ const struct2: ProjectStructure = simpleStructure(...struct2a);
77
+ const sGraph2 = stagingGraph(struct2);
78
+ const pGraph2 = productionGraph(struct2, (id) => inputs.get(id) ?? null);
79
+ const sDiff = graphDiff(sGraph1, sGraph2);
80
+ const pDiff = graphDiff(pGraph1, pGraph2);
81
+ expect(sDiff.onlyInA).toEqual(new Set(onlyA ?? []));
82
+ expect(sDiff.onlyInB).toEqual(new Set(onlyB ?? []));
83
+ expect(sDiff.different).toEqual(new Set(expectedS));
84
+ expect(pDiff.onlyInA).toEqual(new Set(onlyA ?? []));
85
+ expect(pDiff.onlyInB).toEqual(new Set(onlyB ?? []));
86
+ expect(pDiff.different).toEqual(new Set(expectedP));
87
+ });
88
+ });