@syncbridge/common 0.5.10 → 0.5.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.
@@ -28,6 +28,7 @@ export declare namespace BaseElement {
28
28
  status: ServiceStatus,
29
29
  statusMessage: string
30
30
  ];
31
+ 'values-updated': [string[]];
31
32
  }
32
33
  interface InitArgs extends Runnable.InitArgs {
33
34
  logger?: ILogger;
@@ -48,7 +48,9 @@ export class BaseElement extends Runnable {
48
48
  await Promise.all(Object.values(this.components).map(component => component.init()));
49
49
  }
50
50
  async _start(abortSignal) {
51
- await Promise.all(Object.values(this.components).map(component => component.start(abortSignal)));
51
+ if (this.status === ServiceStatus.starting ||
52
+ this.status === ServiceStatus.started)
53
+ await Promise.all(Object.values(this.components).map(component => component.start(abortSignal)));
52
54
  }
53
55
  async _stop() {
54
56
  await Promise.all(Object.values(this.components)
package/constants.js CHANGED
@@ -1,4 +1,4 @@
1
- export const version = '0.5.10';
1
+ export const version = '0.5.12';
2
2
  export const OWN_ELEMENT_METADATA = Symbol.for('OWN_ELEMENT_METADATA');
3
3
  export const COMPONENT_OPTIONS = Symbol.for('COMPONENT_OPTIONS');
4
4
  export const PROCESSOR_OPTIONS = Symbol.for('PROCESSOR_OPTIONS');
@@ -9,6 +9,7 @@ export declare namespace DefineVariable {
9
9
  variables?: TVariable | Promise<TVariable>;
10
10
  enumValues?: TEnumValue | Promise<TEnumValue>;
11
11
  choice?: string[] | Promise<string[]>;
12
+ index?: number;
12
13
  }
13
14
  }
14
15
  export {};
@@ -10,47 +10,49 @@ export function DefineVariable(options) {
10
10
  const designType = Reflect.getMetadata('design:type', target, propertyKey);
11
11
  if (designType === Array)
12
12
  options.isArray = true;
13
+ const base = Object.getPrototypeOf(target);
14
+ const baseMetadata = Reflect.hasMetadata(VARIABLE_CONTAINER, base)
15
+ ? deepClone(Reflect.getMetadata(VARIABLE_CONTAINER, base))
16
+ : {};
17
+ let metadata = merge(options, baseMetadata[propertyKey] || {}, {
18
+ deep: 'full',
19
+ ignoreNulls: false,
20
+ ignoreUndefined: false,
21
+ keepExisting: true,
22
+ });
13
23
  if (typeof options.variables === 'function') {
14
- options.type = VariableType.Nested;
15
- options.variables = Reflect.getMetadata(VARIABLE_CONTAINER, options.variables.prototype);
24
+ metadata.type = VariableType.Nested;
25
+ metadata.variables = Reflect.getMetadata(VARIABLE_CONTAINER, options.variables.prototype);
16
26
  }
17
- if (options.enumValues && !options.type) {
18
- options.type = VariableType.Enum;
27
+ if (metadata.enumValues && !metadata.type) {
28
+ metadata.type = VariableType.Enum;
19
29
  }
20
- if (options.type !== VariableType.Enum)
21
- delete options.enumValues;
22
- if (!options.type) {
30
+ if (metadata.type !== VariableType.Enum)
31
+ delete metadata.enumValues;
32
+ if (!metadata.type) {
23
33
  if (designType === String)
24
- options.type = VariableType.String;
34
+ metadata.type = VariableType.String;
25
35
  else if (designType === Boolean)
26
- options.type = VariableType.Boolean;
36
+ metadata.type = VariableType.Boolean;
27
37
  else if (designType === Number)
28
- options.type = VariableType.Number;
38
+ metadata.type = VariableType.Number;
29
39
  else if (typeof designType === 'function' &&
30
40
  Reflect.hasMetadata(VARIABLE_CONTAINER, designType.prototype)) {
31
- options.type = VariableType.Nested;
32
- options.variables = Reflect.getMetadata(VARIABLE_CONTAINER, designType.prototype);
41
+ metadata.type = VariableType.Nested;
42
+ metadata.variables = Reflect.getMetadata(VARIABLE_CONTAINER, designType.prototype);
33
43
  }
34
44
  }
35
- if (options.isArray && !options.type)
45
+ if (metadata.isArray && !metadata.type)
36
46
  throw new TypeError('Array variables must have type defined. ' +
37
47
  (target.constructor.name +
38
48
  '.' +
39
49
  propertyKey +
40
50
  ' is an array variable'));
41
- const base = Object.getPrototypeOf(target);
42
- const baseMetadata = Reflect.hasMetadata(VARIABLE_CONTAINER, base)
43
- ? deepClone(Reflect.getMetadata(VARIABLE_CONTAINER, base))
44
- : {};
45
- let metadata = merge(baseMetadata, options, {
46
- deep: 'full',
47
- ignoreNulls: false,
48
- ignoreUndefined: false,
49
- });
50
51
  metadata = omitUndefined(metadata, 'full');
51
52
  const metadataRecord = Reflect.hasOwnMetadata(VARIABLE_CONTAINER, target)
52
53
  ? Reflect.getMetadata(VARIABLE_CONTAINER, target)
53
54
  : {};
55
+ merge(metadataRecord, baseMetadata);
54
56
  metadataRecord[propertyKey] = metadata;
55
57
  Reflect.defineMetadata(VARIABLE_CONTAINER, metadataRecord, target);
56
58
  };
@@ -5,6 +5,7 @@ export declare enum LogLevel {
5
5
  error = "error",
6
6
  warn = "warn",
7
7
  info = "info",
8
+ verbose = "verbose",
8
9
  trace = "trace",
9
10
  debug = "debug"
10
11
  }
@@ -7,6 +7,7 @@ export var LogLevel;
7
7
  LogLevel["error"] = "error";
8
8
  LogLevel["warn"] = "warn";
9
9
  LogLevel["info"] = "info";
10
+ LogLevel["verbose"] = "verbose";
10
11
  LogLevel["trace"] = "trace";
11
12
  LogLevel["debug"] = "debug";
12
13
  })(LogLevel || (LogLevel = {}));
@@ -11,6 +11,7 @@ export declare class VariableMetadataRecord {
11
11
  */
12
12
  export declare class VariableMetadata {
13
13
  ignored?: boolean;
14
+ index?: number;
14
15
  hotPlug?: boolean;
15
16
  label?: string;
16
17
  description?: string;
@@ -21,6 +21,12 @@ let VariableMetadata = class VariableMetadata {
21
21
  ignored;
22
22
  hotPlug;
23
23
  };
24
+ __decorate([
25
+ ApiField({
26
+ description: 'Index of the variable. Variables with higher index will be displayed first',
27
+ }),
28
+ __metadata("design:type", Number)
29
+ ], VariableMetadata.prototype, "index", void 0);
24
30
  __decorate([
25
31
  ApiField({
26
32
  description: 'Determine if the variable can be changed while worker running',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncbridge/common",
3
- "version": "0.5.10",
3
+ "version": "0.5.12",
4
4
  "description": "SyncBridge Common utilities",
5
5
  "author": "Panates Inc",
6
6
  "license": "MIT",
@@ -6,12 +6,14 @@ export declare namespace ProcessorFactory {
6
6
  const METADATA_KEY: unique symbol;
7
7
  const PROFILE_KEY: unique symbol;
8
8
  interface ProcessorOptions {
9
- logger: ILogger;
9
+ logger?: ILogger;
10
10
  dataDirectory?: string;
11
11
  }
12
12
  function createProcessor<T extends ProcessorBase>(profile: Profile, options?: ProcessorOptions): Promise<{
13
13
  processor: T;
14
14
  issues: StackExecutor.Issue[];
15
15
  }>;
16
- function updateProcessor(processor: ProcessorBase, profile: Profile, options?: ProcessorOptions): Promise<StackExecutor.Issue[]>;
16
+ function updateProcessor(processor: ProcessorBase, profile: Profile, options?: ProcessorOptions): Promise<{
17
+ issues: StackExecutor.Issue[];
18
+ }>;
17
19
  }
@@ -16,10 +16,17 @@ export var ProcessorFactory;
16
16
  values: profile.values || {},
17
17
  dataDirectory: options?.dataDirectory,
18
18
  });
19
- const issues = await _configureProcessor(processor, profile);
19
+ const ctx = {
20
+ stackExecutor: new StackExecutor(),
21
+ processor,
22
+ profile,
23
+ options,
24
+ valueUpdates: new Map(),
25
+ };
26
+ await _configureProcessor(ctx);
20
27
  return {
21
28
  processor: processor,
22
- issues,
29
+ issues: ctx.stackExecutor.issues,
23
30
  };
24
31
  }
25
32
  ProcessorFactory.createProcessor = createProcessor;
@@ -28,69 +35,160 @@ export var ProcessorFactory;
28
35
  processor.logger = options.logger;
29
36
  if (options?.dataDirectory)
30
37
  processor.dataDirectory = options.dataDirectory;
31
- return _configureProcessor(processor, profile);
38
+ const ctx = {
39
+ stackExecutor: new StackExecutor(),
40
+ processor,
41
+ profile,
42
+ options,
43
+ valueUpdates: new Map(),
44
+ };
45
+ await _configureProcessor(ctx);
46
+ const signalUpdates = async (obj) => {
47
+ if (obj.components) {
48
+ for (const component of Object.values(obj.components)) {
49
+ await signalUpdates(component);
50
+ }
51
+ }
52
+ if (ctx.valueUpdates.has(obj)) {
53
+ await obj.emitAsyncSafe('values-updated', ctx.valueUpdates.get(obj));
54
+ }
55
+ };
56
+ signalUpdates(processor);
57
+ return {
58
+ issues: ctx.stackExecutor.issues,
59
+ };
32
60
  }
33
61
  ProcessorFactory.updateProcessor = updateProcessor;
34
- async function _configureProcessor(processor, profile) {
35
- const processorMetadata = materializeMetadata(profile);
36
- const stackExecutor = new StackExecutor();
37
- async function configureComponents(owner, curPath, newMetadata, newProfile, oldMetadata, oldProfile) {
38
- if (!newMetadata.components)
39
- return;
40
- // Stack: components
41
- await stackExecutor.executeAsync('components', async () => {
42
- for (const [key, childMetadata] of Object.entries(newMetadata.components)) {
43
- // Stack: components/componentName
44
- await stackExecutor.executeAsync(key, async () => {
45
- const oldChildMetadata = oldMetadata?.components?.[key];
46
- const oldChildProfile = oldProfile?.components?.[key];
47
- const childProfile = newProfile.components[key];
48
- const componentPath = curPath ? curPath + '/' + key : key;
49
- let componentInstance = owner.components?.[key];
50
- /** Create component only if processor is stopped */
51
- if (processor.stopped) {
52
- const compReg = ExtensionRegistry.getComponent(childMetadata.className);
53
- /** Create the component instance */
54
- const compClass = await compReg.getClass();
55
- componentInstance = new compClass({
56
- processor: processor,
57
- parent: owner,
58
- name: key,
59
- path: componentPath,
60
- values: childProfile.values || {},
61
- });
62
- owner.components = owner.components || {};
63
- owner.components[key] = componentInstance;
64
- if (childMetadata.propertyKey)
65
- owner[childMetadata.propertyKey] = componentInstance;
66
- }
67
- else if (oldMetadata?.className &&
68
- oldMetadata?.className !== newMetadata.className) {
69
- throw new Error('Can not change component class while processor running. You should stop it first.');
70
- }
71
- else {
72
- // todo update only hot-plug values
73
- componentInstance.values = childProfile.values || {};
74
- }
75
- /** Create the child logger */
76
- componentInstance.logger = owner.logger?.child({
77
- component: componentPath,
62
+ async function _configureProcessor(ctx) {
63
+ const { processor, profile } = ctx;
64
+ const cmpCtx = {
65
+ ...ctx,
66
+ owner: processor,
67
+ curPath: '',
68
+ newMetadata: materializeMetadata(profile),
69
+ newProfile: profile,
70
+ oldMetadata: processor[ProcessorFactory.METADATA_KEY],
71
+ oldProfile: processor[ProcessorFactory.PROFILE_KEY],
72
+ };
73
+ _validateComponents(cmpCtx);
74
+ await _configureComponents(cmpCtx);
75
+ processor[ProcessorFactory.METADATA_KEY] = cmpCtx.newMetadata;
76
+ processor[ProcessorFactory.PROFILE_KEY] = profile;
77
+ }
78
+ function _validateComponents(ctx) {
79
+ const { newMetadata, newProfile, oldMetadata, oldProfile, curPath, owner, stackExecutor, processor, } = ctx;
80
+ if (!newMetadata.components)
81
+ return;
82
+ // Stack: components
83
+ stackExecutor.execute('components', () => {
84
+ for (const [key, childMetadata] of Object.entries(newMetadata.components)) {
85
+ // Stack: components/componentName
86
+ stackExecutor.execute(key, () => {
87
+ const oldChildMetadata = oldMetadata?.components?.[key];
88
+ const oldChildProfile = oldProfile?.components?.[key];
89
+ const childProfile = newProfile.components[key];
90
+ const componentPath = curPath ? curPath + '/' + key : key;
91
+ const componentInstance = owner.components?.[key];
92
+ if (processor.stopped &&
93
+ oldMetadata?.className &&
94
+ oldMetadata?.className !== newMetadata.className)
95
+ throw new Error('Can not change component class while processor running. You should stop it first.');
96
+ ExtensionRegistry.getComponent(childMetadata.className);
97
+ /** Validate sub components */
98
+ if (childMetadata.components) {
99
+ _validateComponents({
100
+ ...ctx,
101
+ owner: componentInstance,
102
+ curPath: componentPath,
103
+ newMetadata: childMetadata,
104
+ newProfile: childProfile,
105
+ oldMetadata: oldChildMetadata,
106
+ oldProfile: oldChildProfile,
78
107
  });
79
- if (childProfile.settings?.logs?.level) {
80
- componentInstance.logger.level =
81
- childProfile.settings?.logs?.level;
82
- }
83
- /** Initialize sub components */
84
- if (childMetadata.components) {
85
- configureComponents(componentInstance, componentPath, childMetadata, childProfile, oldChildMetadata, oldChildProfile);
86
- }
87
- });
108
+ }
109
+ });
110
+ }
111
+ });
112
+ }
113
+ async function _configureComponents(ctx) {
114
+ const { newMetadata, stackExecutor } = ctx;
115
+ if (!newMetadata.components)
116
+ return;
117
+ // Stack: components
118
+ await stackExecutor.executeAsync('components', async () => {
119
+ for (const [key, childMetadata] of Object.entries(newMetadata.components)) {
120
+ // Stack: components/componentName
121
+ await stackExecutor.executeAsync(key, async () => {
122
+ await _configureComponent(ctx, key, childMetadata);
123
+ });
124
+ }
125
+ });
126
+ }
127
+ async function _configureComponent(ctx, key, childMetadata) {
128
+ const { newMetadata, newProfile, oldMetadata, oldProfile, curPath, owner, processor, } = ctx;
129
+ const oldChildMetadata = oldMetadata?.components?.[key];
130
+ const oldChildProfile = oldProfile?.components?.[key];
131
+ const childProfile = newProfile.components[key];
132
+ const componentPath = curPath ? curPath + '/' + key : key;
133
+ let componentInstance = owner.components?.[key];
134
+ /** Create the component only if the processor stopped */
135
+ if (processor.stopped) {
136
+ const compReg = ExtensionRegistry.getComponent(childMetadata.className);
137
+ /** Create the component instance */
138
+ const compClass = await compReg.getClass();
139
+ componentInstance = new compClass({
140
+ processor: processor,
141
+ parent: owner,
142
+ name: key,
143
+ path: componentPath,
144
+ values: childProfile.values || {},
145
+ });
146
+ owner.components = owner.components || {};
147
+ owner.components[key] = componentInstance;
148
+ if (childMetadata.propertyKey)
149
+ owner[childMetadata.propertyKey] = componentInstance;
150
+ }
151
+ else {
152
+ componentInstance.values = {};
153
+ const patchValues = (target, valPath, newValues, newVariables, oldVariables, oldValues) => {
154
+ Object.assign(target, newValues);
155
+ const updatedValues = [];
156
+ for (const [varKey, varMeta] of Object.entries(newMetadata)) {
157
+ const keyPath = valPath ? valPath + '.' + varKey : varKey;
158
+ /** Update only hotplug variables */
159
+ if (varMeta?.hotPlug !== true)
160
+ continue;
161
+ if (varMeta.type === 'nested') {
162
+ target[varKey] = {};
163
+ patchValues(target[varKey], keyPath, newValues[varKey], newVariables[varKey]?.variables || {}, oldVariables?.[varKey]?.variables, oldValues?.[varKey]);
164
+ continue;
165
+ }
166
+ updatedValues.push(keyPath);
167
+ target[varKey] = newValues[varKey];
88
168
  }
169
+ if (updatedValues.length)
170
+ ctx.valueUpdates.set(componentInstance, updatedValues);
171
+ };
172
+ patchValues(componentInstance.values, '', childProfile.values || {}, childMetadata.variables || {}, oldChildMetadata?.variables, oldProfile?.values);
173
+ }
174
+ /** Create the child logger */
175
+ componentInstance.logger = owner.logger?.child({
176
+ component: componentPath,
177
+ });
178
+ if (childProfile.settings?.logs?.level) {
179
+ componentInstance.logger.level = childProfile.settings?.logs?.level;
180
+ }
181
+ /** Initialize sub components */
182
+ if (childMetadata.components) {
183
+ await _configureComponents({
184
+ ...ctx,
185
+ owner: componentInstance,
186
+ curPath: componentPath,
187
+ newMetadata: childMetadata,
188
+ newProfile: childProfile,
189
+ oldMetadata: oldChildMetadata,
190
+ oldProfile: oldChildProfile,
89
191
  });
90
192
  }
91
- await configureComponents(processor, '', processorMetadata, profile, processor[ProcessorFactory.METADATA_KEY], processor[ProcessorFactory.PROFILE_KEY]);
92
- processor[ProcessorFactory.METADATA_KEY] = processorMetadata;
93
- processor[ProcessorFactory.PROFILE_KEY] = profile;
94
- return stackExecutor.issues;
95
193
  }
96
194
  })(ProcessorFactory || (ProcessorFactory = {}));
@@ -1,11 +1,12 @@
1
1
  import { deepClone, merge } from '@jsopen/objects';
2
- import { ComponentMetadata, ProcessorMetadata } from '../models/index.js';
2
+ import { ComponentMetadata, ProcessorMetadata, } from '../models/index.js';
3
3
  import { initializeModelsDocument } from '../models-document.js';
4
4
  import { resolvePromisesDeep } from '../utils/resolve-promises.js';
5
5
  export async function normalizeProcessorMetadata(metadata) {
6
6
  const decoder = await getProcMetadataTypeDecoder();
7
7
  let out = await resolvePromisesDeep(metadata);
8
- out = deepClone({
8
+ out = deepClone(out);
9
+ out = decoder({
9
10
  className: out.className,
10
11
  displayName: out.displayName,
11
12
  description: out.description,
@@ -13,35 +14,18 @@ export async function normalizeProcessorMetadata(metadata) {
13
14
  author: out.author,
14
15
  tags: out.tags,
15
16
  abstract: out.abstract,
16
- variables: out.variables,
17
- components: out.components,
17
+ variables: normalizeVariables(out.variables),
18
+ components: normalizeComponents(out.components),
18
19
  ...out,
19
20
  });
20
- if (out.variables) {
21
- /** Convert object enumValues to arrays */
22
- for (const v of Object.values(out.variables)) {
23
- if (v.enumValues &&
24
- typeof v.enumValues === 'object' &&
25
- !Array.isArray(v.enumValues)) {
26
- let values = v.enumValues;
27
- const keys = Object.keys(values).filter(k => !/^\d+$/.test(k));
28
- values = keys.reduce((a, k) => {
29
- if (values[k] != null)
30
- a.push(values[k]);
31
- return a;
32
- }, []);
33
- v.enumValues = values;
34
- }
35
- }
36
- }
37
- out = decoder(out);
38
21
  merge(out, metadata, { keepExisting: true, deep: 'full' });
39
22
  return out;
40
23
  }
41
24
  export async function normalizeComponentMetadata(metadata) {
42
25
  const decoder = await getCompMetadataTypeDecoder();
43
26
  let out = await resolvePromisesDeep(metadata);
44
- out = deepClone({
27
+ out = deepClone(out);
28
+ out = decoder({
45
29
  className: out.className,
46
30
  displayName: out.displayName,
47
31
  description: out.description,
@@ -50,32 +34,44 @@ export async function normalizeComponentMetadata(metadata) {
50
34
  interfaces: out.interfaces,
51
35
  tags: out.tags,
52
36
  abstract: out.abstract,
53
- variables: out.variables,
54
- components: out.components,
37
+ variables: normalizeVariables(out.variables),
38
+ components: normalizeComponents(out.components),
55
39
  ...out,
56
40
  });
57
- if (out.variables) {
58
- /** Convert object enumValues to arrays */
59
- for (const v of Object.values(out.variables)) {
60
- if (v.enumValues &&
61
- typeof v.enumValues === 'object' &&
62
- !Array.isArray(v.enumValues)) {
63
- let values = v.enumValues;
64
- const keys = Object.keys(values).filter(k => !/^\d+$/.test(k));
65
- values = keys.reduce((a, k) => {
66
- if (values[k] != null)
67
- a.push(values[k]);
68
- return a;
69
- // v => !(typeof v === 'number' && !keys.includes(String(v))),
70
- }, []);
71
- v.enumValues = values;
72
- }
73
- }
74
- }
75
- out = decoder(out);
76
41
  merge(out, metadata, { keepExisting: true, deep: 'full' });
77
42
  return out;
78
43
  }
44
+ function normalizeComponents(components) {
45
+ if (!components)
46
+ return;
47
+ for (const component of Object.values(components)) {
48
+ if (component.variables)
49
+ component.variables = normalizeVariables(component.variables);
50
+ if (component.components)
51
+ component.components = normalizeComponents(component.components);
52
+ }
53
+ return components;
54
+ }
55
+ function normalizeVariables(variables) {
56
+ if (!variables)
57
+ return;
58
+ /** Convert object enumValues to arrays */
59
+ for (const v of Object.values(variables)) {
60
+ if (v.enumValues &&
61
+ typeof v.enumValues === 'object' &&
62
+ !Array.isArray(v.enumValues)) {
63
+ let values = v.enumValues;
64
+ const keys = Object.keys(values).filter(k => !/^\d+$/.test(k));
65
+ values = keys.reduce((a, k) => {
66
+ if (values[k] != null)
67
+ a.push(values[k]);
68
+ return a;
69
+ }, []);
70
+ v.enumValues = values;
71
+ }
72
+ }
73
+ return variables;
74
+ }
79
75
  // ************************************************************************
80
76
  // ************************************************************************
81
77
  let _compMetadataTypeDecoder;
@@ -1,6 +1,12 @@
1
1
  import { StackExecutor } from '../classes/stack-executor.js';
2
2
  import { ProcessorMetadata, Profile } from '../models/index.js';
3
+ /**
4
+ * Materializes metadata from profile.
5
+ */
3
6
  export declare function materializeMetadata(profile: Profile): ProcessorMetadata;
7
+ /**
8
+ *
9
+ */
4
10
  export declare function materializeMetadataSilent(profile: Profile): {
5
11
  metadata: ProcessorMetadata;
6
12
  issues?: StackExecutor.Issue[];
@@ -1,7 +1,11 @@
1
1
  import { clone, merge, omitUndefined } from '@jsopen/objects';
2
2
  import { SbError } from '../classes/sb-error.js';
3
3
  import { StackExecutor } from '../classes/stack-executor.js';
4
+ import { VariableType, } from '../models/index.js';
4
5
  import { ExtensionRegistry } from '../registry/extension-registry.js';
6
+ /**
7
+ * Materializes metadata from profile.
8
+ */
5
9
  export function materializeMetadata(profile) {
6
10
  const { metadata, issues } = materializeMetadataSilent(profile);
7
11
  if (issues?.length) {
@@ -16,6 +20,9 @@ export function materializeMetadata(profile) {
16
20
  }
17
21
  return metadata;
18
22
  }
23
+ /**
24
+ *
25
+ */
19
26
  export function materializeMetadataSilent(profile) {
20
27
  const stackExecutor = new StackExecutor();
21
28
  const processorRec = ExtensionRegistry.getProcessor(profile.processorClass);
@@ -69,6 +76,7 @@ function _materializeComponents(stackExecutor, metadata, profile) {
69
76
  const reg = ExtensionRegistry.getComponent(profileComponent.className);
70
77
  materializedComponent.className = profileComponent.className;
71
78
  materializedComponent.variables = merge({}, [reg.metadata.variables || {}, metadataComponent.variables || {}], { deep: 'full' });
79
+ materializedComponent.variables = sortVariables(materializedComponent.variables);
72
80
  if (metadataComponent.className) {
73
81
  if (materializedComponent.className &&
74
82
  materializedComponent.className !== metadataComponent.className)
@@ -97,3 +105,29 @@ function _materializeComponents(stackExecutor, metadata, profile) {
97
105
  }
98
106
  return out;
99
107
  }
108
+ function sortVariables(variables) {
109
+ if (!variables)
110
+ return;
111
+ const keys = Object.keys(variables);
112
+ const sorted = [...keys]
113
+ .sort((a, b) => {
114
+ const va = variables[a];
115
+ const vb = variables[b];
116
+ const indexA = va.index ?? Number.MAX_SAFE_INTEGER;
117
+ const indexB = vb.index ?? Number.MAX_SAFE_INTEGER;
118
+ if (indexA > indexB)
119
+ return 1;
120
+ if (indexA < indexB)
121
+ return -1;
122
+ if (va.type === VariableType.Nested && vb.type !== VariableType.Nested)
123
+ return 1;
124
+ if (vb.type === VariableType.Nested && va.type !== VariableType.Nested)
125
+ return -1;
126
+ return keys.indexOf(a) > keys.indexOf(b) ? 1 : -1;
127
+ })
128
+ .reduce((a, k) => {
129
+ a[k] = variables[k];
130
+ return a;
131
+ }, {});
132
+ return sorted;
133
+ }