@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.
- package/dist/block_registry/index.d.ts +4 -0
- package/dist/block_registry/index.d.ts.map +1 -0
- package/dist/block_registry/registry.d.ts +37 -0
- package/dist/block_registry/registry.d.ts.map +1 -0
- package/dist/block_registry/registry_spec.d.ts +12 -0
- package/dist/block_registry/registry_spec.d.ts.map +1 -0
- package/dist/block_registry/watcher.d.ts +15 -0
- package/dist/block_registry/watcher.d.ts.map +1 -0
- package/dist/block_registry/well_known_registries.d.ts +4 -0
- package/dist/block_registry/well_known_registries.d.ts.map +1 -0
- package/dist/cfg_render/executor.d.ts +8 -0
- package/dist/cfg_render/executor.d.ts.map +1 -0
- package/dist/cfg_render/operation.d.ts +29 -0
- package/dist/cfg_render/operation.d.ts.map +1 -0
- package/dist/cfg_render/renderer.d.ts +6 -0
- package/dist/cfg_render/renderer.d.ts.map +1 -0
- package/dist/cfg_render/traverse.d.ts +3 -0
- package/dist/cfg_render/traverse.d.ts.map +1 -0
- package/dist/cfg_render/util.d.ts +5 -0
- package/dist/cfg_render/util.d.ts.map +1 -0
- package/dist/dev/index.d.ts +21 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/util.d.ts +3 -0
- package/dist/dev/util.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3587 -0
- package/dist/index.mjs.map +1 -0
- package/dist/js_render/context.d.ts +68 -0
- package/dist/js_render/context.d.ts.map +1 -0
- package/dist/js_render/index.d.ts +6 -0
- package/dist/js_render/index.d.ts.map +1 -0
- package/dist/middle_layer/active_cfg.d.ts +6 -0
- package/dist/middle_layer/active_cfg.d.ts.map +1 -0
- package/dist/middle_layer/block.d.ts +9 -0
- package/dist/middle_layer/block.d.ts.map +1 -0
- package/dist/middle_layer/block_ctx.d.ts +20 -0
- package/dist/middle_layer/block_ctx.d.ts.map +1 -0
- package/dist/middle_layer/block_ctx_unsafe.d.ts +16 -0
- package/dist/middle_layer/block_ctx_unsafe.d.ts.map +1 -0
- package/dist/middle_layer/driver_kit.d.ts +31 -0
- package/dist/middle_layer/driver_kit.d.ts.map +1 -0
- package/dist/middle_layer/frontend_path.d.ts +6 -0
- package/dist/middle_layer/frontend_path.d.ts.map +1 -0
- package/dist/middle_layer/index.d.ts +5 -0
- package/dist/middle_layer/index.d.ts.map +1 -0
- package/dist/middle_layer/middle_layer.d.ts +78 -0
- package/dist/middle_layer/middle_layer.d.ts.map +1 -0
- package/dist/middle_layer/navigation_states.d.ts +10 -0
- package/dist/middle_layer/navigation_states.d.ts.map +1 -0
- package/dist/middle_layer/ops.d.ts +64 -0
- package/dist/middle_layer/ops.d.ts.map +1 -0
- package/dist/middle_layer/project.d.ts +110 -0
- package/dist/middle_layer/project.d.ts.map +1 -0
- package/dist/middle_layer/project_list.d.ts +11 -0
- package/dist/middle_layer/project_list.d.ts.map +1 -0
- package/dist/middle_layer/project_overview.d.ts +8 -0
- package/dist/middle_layer/project_overview.d.ts.map +1 -0
- package/dist/middle_layer/render.d.ts +6 -0
- package/dist/middle_layer/render.d.ts.map +1 -0
- package/dist/middle_layer/types.d.ts +11 -0
- package/dist/middle_layer/types.d.ts.map +1 -0
- package/dist/middle_layer/util.d.ts +3 -0
- package/dist/middle_layer/util.d.ts.map +1 -0
- package/dist/model/args.d.ts +12 -0
- package/dist/model/args.d.ts.map +1 -0
- package/dist/model/block_pack.d.ts +8 -0
- package/dist/model/block_pack.d.ts.map +1 -0
- package/dist/model/block_pack_spec.d.ts +40 -0
- package/dist/model/block_pack_spec.d.ts.map +1 -0
- package/dist/model/frontend.d.ts +10 -0
- package/dist/model/frontend.d.ts.map +1 -0
- package/dist/model/index.d.ts +3 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/project_model.d.ts +67 -0
- package/dist/model/project_model.d.ts.map +1 -0
- package/dist/model/project_model_util.d.ts +29 -0
- package/dist/model/project_model_util.d.ts.map +1 -0
- package/dist/model/template_spec.d.ts +16 -0
- package/dist/model/template_spec.d.ts.map +1 -0
- package/dist/mutator/block-pack/block_pack.d.ts +17 -0
- package/dist/mutator/block-pack/block_pack.d.ts.map +1 -0
- package/dist/mutator/block-pack/frontend.d.ts +4 -0
- package/dist/mutator/block-pack/frontend.d.ts.map +1 -0
- package/dist/mutator/context_export.d.ts +9 -0
- package/dist/mutator/context_export.d.ts.map +1 -0
- package/dist/mutator/project.d.ts +121 -0
- package/dist/mutator/project.d.ts.map +1 -0
- package/dist/mutator/template/render_block.d.ts +32 -0
- package/dist/mutator/template/render_block.d.ts.map +1 -0
- package/dist/mutator/template/render_template.d.ts +12 -0
- package/dist/mutator/template/render_template.d.ts.map +1 -0
- package/dist/mutator/template/template_loading.d.ts +13 -0
- package/dist/mutator/template/template_loading.d.ts.map +1 -0
- package/dist/pool/data.d.ts +24 -0
- package/dist/pool/data.d.ts.map +1 -0
- package/dist/pool/driver.d.ts +22 -0
- package/dist/pool/driver.d.ts.map +1 -0
- package/dist/pool/index.d.ts +3 -0
- package/dist/pool/index.d.ts.map +1 -0
- package/dist/pool/p_object_collection.d.ts +29 -0
- package/dist/pool/p_object_collection.d.ts.map +1 -0
- package/dist/pool/ref_count_pool.d.ts +25 -0
- package/dist/pool/ref_count_pool.d.ts.map +1 -0
- package/dist/pool/result_pool.d.ts +25 -0
- package/dist/pool/result_pool.d.ts.map +1 -0
- package/dist/test/block_packs.d.ts +6 -0
- package/dist/test/block_packs.d.ts.map +1 -0
- package/dist/test/explicit_templates.d.ts +3 -0
- package/dist/test/explicit_templates.d.ts.map +1 -0
- package/dist/test/known_templates.d.ts +6 -0
- package/dist/test/known_templates.d.ts.map +1 -0
- package/package.json +55 -0
- package/src/block_registry/index.ts +3 -0
- package/src/block_registry/registry.test.ts +35 -0
- package/src/block_registry/registry.ts +180 -0
- package/src/block_registry/registry_spec.ts +13 -0
- package/src/block_registry/watcher.ts +72 -0
- package/src/block_registry/well_known_registries.ts +13 -0
- package/src/cfg_render/executor.test.ts +120 -0
- package/src/cfg_render/executor.ts +253 -0
- package/src/cfg_render/operation.ts +38 -0
- package/src/cfg_render/renderer.ts +540 -0
- package/src/cfg_render/traverse.ts +58 -0
- package/src/cfg_render/util.ts +29 -0
- package/src/dev/index.ts +89 -0
- package/src/dev/util.ts +13 -0
- package/src/index.ts +21 -0
- package/src/js_render/context.ts +768 -0
- package/src/js_render/index.ts +41 -0
- package/src/middle_layer/active_cfg.ts +56 -0
- package/src/middle_layer/block.ts +70 -0
- package/src/middle_layer/block_ctx.ts +90 -0
- package/src/middle_layer/block_ctx_unsafe.ts +29 -0
- package/src/middle_layer/driver_kit.ts +107 -0
- package/src/middle_layer/frontend_path.ts +83 -0
- package/src/middle_layer/index.ts +4 -0
- package/src/middle_layer/middle_layer.test.ts +720 -0
- package/src/middle_layer/middle_layer.ts +235 -0
- package/src/middle_layer/navigation_states.ts +48 -0
- package/src/middle_layer/ops.ts +147 -0
- package/src/middle_layer/project.ts +380 -0
- package/src/middle_layer/project_list.ts +59 -0
- package/src/middle_layer/project_overview.ts +220 -0
- package/src/middle_layer/render.test.ts +129 -0
- package/src/middle_layer/render.ts +19 -0
- package/src/middle_layer/types.ts +16 -0
- package/src/middle_layer/util.ts +22 -0
- package/src/model/args.ts +62 -0
- package/src/model/block_pack.ts +8 -0
- package/src/model/block_pack_spec.ts +52 -0
- package/src/model/frontend.ts +10 -0
- package/src/model/index.ts +2 -0
- package/src/model/project_model.test.ts +26 -0
- package/src/model/project_model.ts +142 -0
- package/src/model/project_model_util.test.ts +88 -0
- package/src/model/project_model_util.ts +169 -0
- package/src/model/template_spec.ts +18 -0
- package/src/mutator/block-pack/block_pack.test.ts +53 -0
- package/src/mutator/block-pack/block_pack.ts +187 -0
- package/src/mutator/block-pack/frontend.ts +29 -0
- package/src/mutator/context_export.ts +25 -0
- package/src/mutator/project.test.ts +272 -0
- package/src/mutator/project.ts +1112 -0
- package/src/mutator/template/render_block.ts +91 -0
- package/src/mutator/template/render_template.ts +40 -0
- package/src/mutator/template/template_loading.ts +77 -0
- package/src/mutator/template/template_render.test.ts +272 -0
- package/src/pool/data.ts +239 -0
- package/src/pool/driver.ts +325 -0
- package/src/pool/index.ts +2 -0
- package/src/pool/p_object_collection.ts +122 -0
- package/src/pool/ref_count_pool.ts +76 -0
- package/src/pool/result_pool.ts +284 -0
- package/src/test/block_packs.ts +23 -0
- package/src/test/explicit_templates.ts +8 -0
- package/src/test/known_templates.ts +24 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { Block, ProjectStructure } from './project_model';
|
|
2
|
+
import { Optional, Writable } from 'utility-types';
|
|
3
|
+
import { inferAllReferencedBlocks } from './args';
|
|
4
|
+
|
|
5
|
+
export function allBlocks(structure: ProjectStructure): Iterable<Block> {
|
|
6
|
+
return {
|
|
7
|
+
*[Symbol.iterator]() {
|
|
8
|
+
for (const g of structure.groups) for (const b of g.blocks) yield b;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface BlockGraphNode {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly missingReferences: boolean;
|
|
16
|
+
/** Direct upstreams */
|
|
17
|
+
readonly upstream: Set<string>;
|
|
18
|
+
/** Direct downstreams */
|
|
19
|
+
readonly downstream: Set<string>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class BlockGraph {
|
|
23
|
+
/** Nodes are stored in the map in topological order */
|
|
24
|
+
public readonly nodes: Map<string, BlockGraphNode>;
|
|
25
|
+
|
|
26
|
+
constructor(nodes: Map<string, BlockGraphNode>) {
|
|
27
|
+
this.nodes = nodes;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public traverseIds(direction: 'upstream' | 'downstream', ...rootBlockIds: string[]): Set<string> {
|
|
31
|
+
const all = new Set<string>();
|
|
32
|
+
this.traverse(direction, rootBlockIds, (node) => all.add(node.id));
|
|
33
|
+
return all;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public traverseIdsExcludingRoots(
|
|
37
|
+
direction: 'upstream' | 'downstream',
|
|
38
|
+
...rootBlockIds: string[]
|
|
39
|
+
): Set<string> {
|
|
40
|
+
const result = this.traverseIds(direction, ...rootBlockIds);
|
|
41
|
+
for (const r of rootBlockIds) result.delete(r);
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public traverse(
|
|
46
|
+
direction: 'upstream' | 'downstream',
|
|
47
|
+
rootBlockIds: string[],
|
|
48
|
+
cb: (node: BlockGraphNode) => void
|
|
49
|
+
): void {
|
|
50
|
+
let unprocessed = [...rootBlockIds];
|
|
51
|
+
// used to deduplicate possible downstream / upstream blocks and process them only once
|
|
52
|
+
const queued = new Set<string>(unprocessed);
|
|
53
|
+
while (unprocessed.length > 0) {
|
|
54
|
+
let nextUnprocessed: string[] = [];
|
|
55
|
+
for (const blockId of unprocessed) {
|
|
56
|
+
const info = this.nodes.get(blockId)!;
|
|
57
|
+
cb(info);
|
|
58
|
+
info[direction]!.forEach((v) => {
|
|
59
|
+
if (!queued.has(v)) {
|
|
60
|
+
queued.add(v);
|
|
61
|
+
nextUnprocessed.push(v);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
unprocessed = nextUnprocessed;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function stagingGraph(structure: ProjectStructure) {
|
|
71
|
+
type WNode = Optional<Writable<BlockGraphNode>, 'upstream' | 'downstream'>;
|
|
72
|
+
const result = new Map<string, WNode>();
|
|
73
|
+
|
|
74
|
+
// Simple dependency graph from previous to next
|
|
75
|
+
//
|
|
76
|
+
// more complicated patterns to be implemented later
|
|
77
|
+
// (i.e. groups with specific behaviours for total outputs and inputs)
|
|
78
|
+
//
|
|
79
|
+
let previous: WNode | undefined = undefined;
|
|
80
|
+
for (const { id } of allBlocks(structure)) {
|
|
81
|
+
const current: WNode = {
|
|
82
|
+
id: id,
|
|
83
|
+
missingReferences: false
|
|
84
|
+
};
|
|
85
|
+
result.set(id, current);
|
|
86
|
+
if (previous === undefined) {
|
|
87
|
+
current.upstream = new Set<string>();
|
|
88
|
+
} else {
|
|
89
|
+
current.upstream = new Set<string>([previous.id]);
|
|
90
|
+
previous.downstream = new Set<string>([current.id]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
previous = current;
|
|
94
|
+
}
|
|
95
|
+
if (previous !== undefined) previous.downstream = new Set<string>();
|
|
96
|
+
|
|
97
|
+
return new BlockGraph(result as Map<string, BlockGraphNode>);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function productionGraph(
|
|
101
|
+
structure: ProjectStructure,
|
|
102
|
+
argsProvider: (blockId: string) => any | undefined
|
|
103
|
+
): BlockGraph {
|
|
104
|
+
const result = new Map<string, BlockGraphNode>();
|
|
105
|
+
|
|
106
|
+
// traversing blocks in topological order defined by the project structure
|
|
107
|
+
// and keeping possibleUpstreams set on each step, to consider only
|
|
108
|
+
// those dependencies that are possible under current topology
|
|
109
|
+
const possibleUpstreams = new Set<string>();
|
|
110
|
+
for (const { id } of allBlocks(structure)) {
|
|
111
|
+
const args = argsProvider(id);
|
|
112
|
+
|
|
113
|
+
// skipping those blocks for which we don't have args
|
|
114
|
+
if (args === undefined) continue;
|
|
115
|
+
|
|
116
|
+
const upstreams = inferAllReferencedBlocks(args, possibleUpstreams);
|
|
117
|
+
const node: BlockGraphNode = {
|
|
118
|
+
id,
|
|
119
|
+
missingReferences: upstreams.missingReferences,
|
|
120
|
+
upstream: upstreams.upstreams,
|
|
121
|
+
downstream: new Set<string>() // will be populated from downstream blocks
|
|
122
|
+
};
|
|
123
|
+
result.set(id, node);
|
|
124
|
+
upstreams.upstreams.forEach((dep) => result.get(dep)!.downstream.add(id));
|
|
125
|
+
possibleUpstreams.add(id);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return new BlockGraph(result);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function setsEqual<T>(a: Set<T>, b: Set<T>): boolean {
|
|
132
|
+
if (a.size !== b.size) return false;
|
|
133
|
+
for (const e of a) if (!b.has(e)) return false;
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function intersects<T>(a: Set<T>, b: Set<T>): boolean {
|
|
138
|
+
if (a.size > b.size) return intersects(b, a);
|
|
139
|
+
for (const e of a) if (b.has(e)) return true;
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface GraphDiff {
|
|
144
|
+
onlyInA: Set<string>;
|
|
145
|
+
/** Nodes that changed the list of their upstreams,
|
|
146
|
+
* and all their downstreams */
|
|
147
|
+
different: Set<string>;
|
|
148
|
+
onlyInB: Set<string>;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function graphDiff(a: BlockGraph, b: BlockGraph): GraphDiff {
|
|
152
|
+
let matched = 0;
|
|
153
|
+
const onlyInA = new Set<string>();
|
|
154
|
+
const onlyInB = new Set<string>();
|
|
155
|
+
const different = new Set<string>();
|
|
156
|
+
a.nodes.forEach((fromA) => {
|
|
157
|
+
const fromB = b.nodes.get(fromA.id);
|
|
158
|
+
if (fromB === undefined) onlyInA.add(fromA.id);
|
|
159
|
+
else if (!setsEqual(fromA.upstream, fromB.upstream) || intersects(fromA.upstream, different))
|
|
160
|
+
different.add(fromA.id);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
b.nodes.forEach((fromB) => {
|
|
164
|
+
if (!a.nodes.has(fromB.id)) onlyInB.add(fromB.id);
|
|
165
|
+
else if (intersects(fromB.upstream, different)) different.add(fromB.id);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return { onlyInA, onlyInB, different };
|
|
169
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface TemplateFromRegistry {
|
|
2
|
+
readonly type: 'from-registry';
|
|
3
|
+
registry: string;
|
|
4
|
+
path: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface ExplicitTemplate {
|
|
8
|
+
readonly type: 'explicit';
|
|
9
|
+
content: Uint8Array;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface TemplateFromFile {
|
|
13
|
+
readonly type: 'from-file';
|
|
14
|
+
path: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type TemplateSpecPrepared = TemplateFromRegistry | ExplicitTemplate;
|
|
18
|
+
export type TemplateSpecAny = TemplateSpecPrepared | TemplateFromFile;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isNullResourceId,
|
|
3
|
+
poll,
|
|
4
|
+
TestHelpers,
|
|
5
|
+
toGlobalResourceId
|
|
6
|
+
} from '@milaboratories/pl-client';
|
|
7
|
+
import { BlockPackPreparer, createBlockPack } from './block_pack';
|
|
8
|
+
import { BlockPackSpecAny } from '../../model';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { HmacSha256Signer } from '@milaboratories/ts-helpers';
|
|
11
|
+
|
|
12
|
+
const preparation = new BlockPackPreparer(new HmacSha256Signer(HmacSha256Signer.generateSecret()));
|
|
13
|
+
|
|
14
|
+
test.each([
|
|
15
|
+
{
|
|
16
|
+
spec: {
|
|
17
|
+
type: 'from-registry-v1',
|
|
18
|
+
registryUrl: 'https://block.registry.platforma.bio/releases',
|
|
19
|
+
organization: 'milaboratory',
|
|
20
|
+
package: 'enter-numbers',
|
|
21
|
+
version: '0.4.1'
|
|
22
|
+
} as BlockPackSpecAny
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
spec: {
|
|
26
|
+
type: 'dev',
|
|
27
|
+
folder: path.resolve('integration', 'block-beta-sum-numbers')
|
|
28
|
+
} as BlockPackSpecAny
|
|
29
|
+
}
|
|
30
|
+
])('test load template from $spec.type', async ({ spec }) => {
|
|
31
|
+
const config = await preparation.getBlockConfig(spec);
|
|
32
|
+
expect(config).toBeDefined();
|
|
33
|
+
expect(config.renderingMode).toEqual('Heavy');
|
|
34
|
+
|
|
35
|
+
const specPrepared = await preparation.prepare(spec);
|
|
36
|
+
|
|
37
|
+
await TestHelpers.withTempRoot(async (pl) => {
|
|
38
|
+
const f0 = { resourceId: pl.clientRoot, fieldName: 'test0' };
|
|
39
|
+
|
|
40
|
+
const bp = await pl.withWriteTx('test', async (tx) => {
|
|
41
|
+
tx.createField(f0, 'Dynamic');
|
|
42
|
+
const bp = createBlockPack(tx, specPrepared);
|
|
43
|
+
tx.setField(f0, bp);
|
|
44
|
+
await tx.commit();
|
|
45
|
+
return await toGlobalResourceId(bp);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await poll(pl, async (a) => {
|
|
49
|
+
const r = await a.get(bp).then((r) => r.final()); // this will await final state
|
|
50
|
+
expect(isNullResourceId(r.data.error)).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { AnyResourceRef, field, PlTransaction, ResourceType } from '@milaboratories/pl-client';
|
|
2
|
+
import { loadTemplate } from '../template/template_loading';
|
|
3
|
+
import { BlockPackExplicit, BlockPackSpecAny, BlockPackSpecPrepared } from '../../model';
|
|
4
|
+
import { assertNever, Signer } from '@milaboratories/ts-helpers';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import { Dispatcher, request } from 'undici';
|
|
7
|
+
import { createFrontend } from './frontend';
|
|
8
|
+
import { BlockConfig } from '@platforma-sdk/model';
|
|
9
|
+
import { loadPackDescription, RegistryV1 } from '@platforma-sdk/block-tools';
|
|
10
|
+
import { BlockPackInfo } from '../../model/block_pack';
|
|
11
|
+
import { resolveDevPacket } from '../../dev';
|
|
12
|
+
import { getDevV2PacketMtime } from '../../block_registry';
|
|
13
|
+
|
|
14
|
+
export const BlockPackCustomType: ResourceType = { name: 'BlockPackCustom', version: '1' };
|
|
15
|
+
export const BlockPackTemplateField = 'template';
|
|
16
|
+
export const BlockPackFrontendField = 'frontend';
|
|
17
|
+
|
|
18
|
+
/** Ensure trailing slash */
|
|
19
|
+
function tSlash(str: string): string {
|
|
20
|
+
if (str.endsWith('/')) return str;
|
|
21
|
+
else return `${str}/`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class BlockPackPreparer {
|
|
25
|
+
constructor(
|
|
26
|
+
private readonly signer: Signer,
|
|
27
|
+
private readonly http?: Dispatcher
|
|
28
|
+
) {}
|
|
29
|
+
|
|
30
|
+
public async getBlockConfig(spec: BlockPackSpecAny): Promise<BlockConfig> {
|
|
31
|
+
switch (spec.type) {
|
|
32
|
+
case 'explicit':
|
|
33
|
+
return spec.config;
|
|
34
|
+
|
|
35
|
+
case 'dev':
|
|
36
|
+
case 'dev-v1': {
|
|
37
|
+
const devPaths = await resolveDevPacket(spec.folder, false);
|
|
38
|
+
const configContent = await fs.promises.readFile(devPaths.config, { encoding: 'utf-8' });
|
|
39
|
+
return JSON.parse(configContent) as BlockConfig;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
case 'dev-v2': {
|
|
43
|
+
const description = await loadPackDescription(spec.folder);
|
|
44
|
+
const configContent = await fs.promises.readFile(description.components.model.file, {
|
|
45
|
+
encoding: 'utf-8'
|
|
46
|
+
});
|
|
47
|
+
return JSON.parse(configContent) as BlockConfig;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
case 'from-registry-v1': {
|
|
51
|
+
const httpOptions = this.http !== undefined ? { dispatcher: this.http } : {};
|
|
52
|
+
|
|
53
|
+
const urlPrefix = `${tSlash(spec.registryUrl)}${RegistryV1.packageContentPrefix(spec)}`;
|
|
54
|
+
|
|
55
|
+
const configResponse = await request(`${urlPrefix}/config.json`, httpOptions);
|
|
56
|
+
|
|
57
|
+
return (await configResponse.body.json()) as BlockConfig;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
default:
|
|
61
|
+
return assertNever(spec);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public async prepare(spec: BlockPackSpecAny): Promise<BlockPackSpecPrepared> {
|
|
66
|
+
switch (spec.type) {
|
|
67
|
+
case 'explicit':
|
|
68
|
+
return spec;
|
|
69
|
+
|
|
70
|
+
case 'dev':
|
|
71
|
+
case 'dev-v1': {
|
|
72
|
+
const devPaths = await resolveDevPacket(spec.folder, false);
|
|
73
|
+
|
|
74
|
+
// template
|
|
75
|
+
const templateContent = await fs.promises.readFile(devPaths.workflow);
|
|
76
|
+
|
|
77
|
+
// config
|
|
78
|
+
const config = JSON.parse(
|
|
79
|
+
await fs.promises.readFile(devPaths.config, 'utf-8')
|
|
80
|
+
) as BlockConfig;
|
|
81
|
+
|
|
82
|
+
// frontend
|
|
83
|
+
const frontendPath = devPaths.ui;
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
type: 'explicit',
|
|
87
|
+
template: {
|
|
88
|
+
type: 'explicit',
|
|
89
|
+
content: templateContent
|
|
90
|
+
},
|
|
91
|
+
config,
|
|
92
|
+
frontend: {
|
|
93
|
+
type: 'local',
|
|
94
|
+
path: frontendPath,
|
|
95
|
+
signature: this.signer.sign(frontendPath)
|
|
96
|
+
},
|
|
97
|
+
source: spec
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
case 'dev-v2': {
|
|
102
|
+
const description = await loadPackDescription(spec.folder);
|
|
103
|
+
const config = JSON.parse(
|
|
104
|
+
await fs.promises.readFile(description.components.model.file, {
|
|
105
|
+
encoding: 'utf-8'
|
|
106
|
+
})
|
|
107
|
+
) as BlockConfig;
|
|
108
|
+
const workflowContent = await fs.promises.readFile(description.components.workflow.file);
|
|
109
|
+
const frontendPath = description.components.ui.folder;
|
|
110
|
+
const source = { ...spec };
|
|
111
|
+
if (spec.mtime === undefined)
|
|
112
|
+
// if absent, calculating the mtime here, so the block will correctly show whether it can be updated
|
|
113
|
+
source.mtime = await getDevV2PacketMtime(description);
|
|
114
|
+
return {
|
|
115
|
+
type: 'explicit',
|
|
116
|
+
template: {
|
|
117
|
+
type: 'explicit',
|
|
118
|
+
content: workflowContent
|
|
119
|
+
},
|
|
120
|
+
config,
|
|
121
|
+
frontend: {
|
|
122
|
+
type: 'local',
|
|
123
|
+
path: frontendPath,
|
|
124
|
+
signature: this.signer.sign(frontendPath)
|
|
125
|
+
},
|
|
126
|
+
source
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
case 'from-registry-v1': {
|
|
131
|
+
const httpOptions = this.http !== undefined ? { dispatcher: this.http } : {};
|
|
132
|
+
|
|
133
|
+
const urlPrefix = `${tSlash(spec.registryUrl)}${RegistryV1.packageContentPrefix(spec)}`;
|
|
134
|
+
|
|
135
|
+
const templateUrl = `${urlPrefix}/template.plj.gz`;
|
|
136
|
+
// template
|
|
137
|
+
const templateResponse = await request(templateUrl, httpOptions);
|
|
138
|
+
if (templateResponse.statusCode !== 200)
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Block not found in registry (url = ${templateUrl} ; code = ${templateResponse.statusCode}): ` +
|
|
141
|
+
JSON.stringify(spec)
|
|
142
|
+
);
|
|
143
|
+
const templateContent = new Uint8Array(await templateResponse.body.arrayBuffer());
|
|
144
|
+
|
|
145
|
+
// config
|
|
146
|
+
const configResponse = await request(`${urlPrefix}/config.json`, httpOptions);
|
|
147
|
+
const config = (await configResponse.body.json()) as BlockConfig;
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
type: 'explicit',
|
|
151
|
+
template: {
|
|
152
|
+
type: 'explicit',
|
|
153
|
+
content: templateContent
|
|
154
|
+
},
|
|
155
|
+
config,
|
|
156
|
+
frontend: {
|
|
157
|
+
type: 'url',
|
|
158
|
+
url: `${urlPrefix}/frontend.tgz`
|
|
159
|
+
},
|
|
160
|
+
source: spec
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
default:
|
|
165
|
+
return assertNever(spec);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function createCustomBlockPack(tx: PlTransaction, spec: BlockPackExplicit): AnyResourceRef {
|
|
171
|
+
const blockPackInfo: BlockPackInfo = { config: spec.config, source: spec.source };
|
|
172
|
+
const bp = tx.createStruct(BlockPackCustomType, JSON.stringify(blockPackInfo));
|
|
173
|
+
tx.createField(field(bp, BlockPackTemplateField), 'Input', loadTemplate(tx, spec.template));
|
|
174
|
+
tx.createField(field(bp, BlockPackFrontendField), 'Input', createFrontend(tx, spec.frontend));
|
|
175
|
+
tx.lock(bp);
|
|
176
|
+
|
|
177
|
+
return bp;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function createBlockPack(tx: PlTransaction, spec: BlockPackSpecPrepared): AnyResourceRef {
|
|
181
|
+
switch (spec.type) {
|
|
182
|
+
case 'explicit':
|
|
183
|
+
return createCustomBlockPack(tx, spec);
|
|
184
|
+
default:
|
|
185
|
+
return assertNever(spec.type);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AnyResourceRef, PlTransaction } from '@milaboratories/pl-client';
|
|
2
|
+
import {
|
|
3
|
+
FrontendFromFolderData,
|
|
4
|
+
FrontendFromFolderResourceType,
|
|
5
|
+
FrontendFromUrlData,
|
|
6
|
+
FrontendFromUrlResourceType,
|
|
7
|
+
FrontendSpec
|
|
8
|
+
} from '../../model';
|
|
9
|
+
import { assertNever } from '@milaboratories/ts-helpers';
|
|
10
|
+
|
|
11
|
+
export function createFrontend(tx: PlTransaction, spec: FrontendSpec): AnyResourceRef {
|
|
12
|
+
switch (spec.type) {
|
|
13
|
+
case 'url':
|
|
14
|
+
return tx.createValue(
|
|
15
|
+
FrontendFromUrlResourceType,
|
|
16
|
+
JSON.stringify({ url: spec.url } as FrontendFromUrlData)
|
|
17
|
+
);
|
|
18
|
+
case 'local':
|
|
19
|
+
return tx.createValue(
|
|
20
|
+
FrontendFromFolderResourceType,
|
|
21
|
+
JSON.stringify({
|
|
22
|
+
path: spec.path,
|
|
23
|
+
signature: spec.signature
|
|
24
|
+
} as FrontendFromFolderData)
|
|
25
|
+
);
|
|
26
|
+
default:
|
|
27
|
+
return assertNever(spec);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AnyRef, PlTransaction } from '@milaboratories/pl-client';
|
|
2
|
+
import { Templates as SdkTemplates } from '@platforma-sdk/workflow-tengo';
|
|
3
|
+
import { createRenderTemplate } from './template/render_template';
|
|
4
|
+
import { prepareTemplateSpec } from './template/template_loading';
|
|
5
|
+
import { TemplateSpecPrepared } from '../model/template_spec';
|
|
6
|
+
import { createHash } from 'crypto';
|
|
7
|
+
|
|
8
|
+
export type TemplateEnvelop = { spec: TemplateSpecPrepared; hash: string };
|
|
9
|
+
|
|
10
|
+
let preparedTemplateEnvelop: TemplateEnvelop | undefined;
|
|
11
|
+
|
|
12
|
+
export async function getPreparedExportTemplateEnvelope(): Promise<TemplateEnvelop> {
|
|
13
|
+
if (preparedTemplateEnvelop === undefined) {
|
|
14
|
+
const preparedTemplate = await prepareTemplateSpec(SdkTemplates['pframes.export-pframe']);
|
|
15
|
+
if (preparedTemplate.type !== 'explicit') throw new Error('Unexpected prepared template type.');
|
|
16
|
+
const hash = createHash('sha256').update(preparedTemplate.content).digest('hex');
|
|
17
|
+
preparedTemplateEnvelop = { spec: preparedTemplate, hash };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return preparedTemplateEnvelop;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function exportContext(tx: PlTransaction, exportTpl: AnyRef, ctx: AnyRef) {
|
|
24
|
+
return createRenderTemplate(tx, exportTpl, true, { pf: ctx }, ['result']).result;
|
|
25
|
+
}
|