@mat3ra/wode 2026.6.29-0 → 2026.7.3-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,13 +7,11 @@ import { type ComputedEntityMixin } from "@mat3ra/ide/dist/js/compute";
7
7
  import type { Material } from "@mat3ra/made";
8
8
  import { Model, ModelFactory } from "@mat3ra/mode";
9
9
  import type { MetaPropertyHolder } from "@mat3ra/prode";
10
- import type { MaterialExternalContext } from "./context/mixins/MaterialContextMixin";
11
- import type { MaterialsExternalContext } from "./context/mixins/MaterialsContextMixin";
12
- import type { MaterialsSetExternalContext } from "./context/mixins/MaterialsSetContextMixin";
13
- import type { JobExternalContext, WorkflowExternalContext } from "./context/providers/by_application/espresso/QEPWXInputDataManager";
10
+ import type { WorkflowExternalContext } from "./context/providers/by_application/espresso/QEPWXInputDataManager";
14
11
  import { type SubworkflowSchemaMixin } from "./generated/SubworkflowSchemaMixin";
15
12
  import { SubworkflowUnit } from "./units";
16
13
  import type { AnySubworkflowUnit } from "./units/factory";
14
+ import type { WorkflowRenderContext } from "./Workflow";
17
15
  type ConvergenceConfig = {
18
16
  parameter: "N_k" | "N_k_nonuniform";
19
17
  parameterInitial: number | [number, number, number];
@@ -28,7 +26,7 @@ type ConvergenceConfig = {
28
26
  };
29
27
  interface Subworkflow extends DefaultableInMemoryEntity, NamedInMemoryEntity, SubworkflowSchemaMixin, HashedEntity, Omit<ComputedEntityMixin, "compute"> {
30
28
  }
31
- type SubworkflowExternalContext = MaterialExternalContext & MaterialsExternalContext & MaterialsSetExternalContext & WorkflowExternalContext & JobExternalContext;
29
+ type SubworkflowExternalContext = WorkflowRenderContext & WorkflowExternalContext;
32
30
  declare class Subworkflow extends InMemoryEntity implements SubworkflowSchema {
33
31
  private ModelFactory;
34
32
  private applicationInstance;
@@ -21,8 +21,10 @@ import type { WorkflowSchema } from "./workflows/types";
21
21
  interface Workflow extends Defaultable, NamedInMemoryEntity, WorkflowSchemaMixin, Taggable, HashedEntity, ComputedEntityMixin, HasDescription {
22
22
  compute: WorkflowSchema["compute"];
23
23
  }
24
- /** Context passed to Workflow.render() before `workflowHasRelaxation` is injected for subworkflows. */
25
- export type WorkflowRenderContext = MaterialExternalContext & MaterialsExternalContext & MaterialsSetExternalContext & JobExternalContext;
24
+ /** Context passed to Workflow.render(); subworkflows also receive `workflowHasRelaxation`. */
25
+ export type WorkflowRenderContext = MaterialExternalContext & MaterialsExternalContext & MaterialsSetExternalContext & JobExternalContext & {
26
+ scopeGlobal?: Record<string, unknown>;
27
+ };
26
28
  declare class Workflow extends InMemoryEntity implements WorkflowSchema {
27
29
  createDefault: () => Workflow;
28
30
  static readonly defaultConfig: WorkflowSchema;
@@ -66,6 +66,10 @@ DataForRendering = S["data"]> {
66
66
  getDataForRendering(): DataForRendering;
67
67
  getContextItemData(): S;
68
68
  getContextItemDataForRendering(): ContextItemForRendering<S, DataForRendering>;
69
+ /**
70
+ * Resolves Jinja placeholders in persisted context `data` from `scope.global`.
71
+ */
72
+ renderContext(scopeGlobal: Record<string, unknown>): boolean;
69
73
  /**
70
74
  * Helper method to find a context item from a unit context array by name.
71
75
  * Returns a partial schema object that can be safely passed to constructors.
@@ -1,6 +1,18 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ const standata_1 = require("@mat3ra/standata");
3
7
  const utils_1 = require("@mat3ra/utils");
8
+ const nunjucks_1 = __importDefault(require("nunjucks"));
9
+ const nunjucksEnvironment = (0, standata_1.setupNunjucksEnvironment)(new nunjucks_1.default.Environment());
10
+ const parseNumericStrings = (_key, value) => {
11
+ if (typeof value === "string" && value.trim() !== "" && !Number.isNaN(Number(value))) {
12
+ return Number(value);
13
+ }
14
+ return value;
15
+ };
4
16
  /**
5
17
  * Context providers expose three data layers. Keep them separate.
6
18
  *
@@ -75,6 +87,21 @@ class ContextProvider {
75
87
  data: this.getDataForRendering(),
76
88
  };
77
89
  }
90
+ /**
91
+ * Resolves Jinja placeholders in persisted context `data` from `scope.global`.
92
+ */
93
+ renderContext(scopeGlobal) {
94
+ const data = this.getData();
95
+ const dataJson = JSON.stringify(data);
96
+ const renderedJson = nunjucksEnvironment.renderString(dataJson, scopeGlobal);
97
+ const resolved = JSON.parse(renderedJson, parseNumericStrings);
98
+ if (JSON.stringify(data) === JSON.stringify(resolved)) {
99
+ return false;
100
+ }
101
+ this.setData(resolved);
102
+ this.setIsEdited(true);
103
+ return true;
104
+ }
78
105
  /**
79
106
  * Helper method to find a context item from a unit context array by name.
80
107
  * Returns a partial schema object that can be safely passed to constructors.
@@ -52,7 +52,7 @@ class QEPWXInputDataManager extends JSONSchemaDataProvider_1.default {
52
52
  const pseudo = (((_b = this.methodData) === null || _b === void 0 ? void 0 : _b.pseudo) || []).find((p) => p.element === symbolWithoutLabel);
53
53
  return {
54
54
  X: `${symbolWithoutLabel}${label}`,
55
- Mass_X: periodic_table_js_1.PERIODIC_TABLE[symbol].atomic_mass,
55
+ Mass_X: periodic_table_js_1.PERIODIC_TABLE[symbolWithoutLabel].atomic_mass,
56
56
  PseudoPot_X: (pseudo === null || pseudo === void 0 ? void 0 : pseudo.filename) || path_1.default.basename((pseudo === null || pseudo === void 0 ? void 0 : pseudo.path) || ""),
57
57
  };
58
58
  });
@@ -65,11 +65,14 @@ export type AssignmentContext = Record<AssignmentUnitSchema["operand"], Assignme
65
65
  export type SubworkflowContext = {
66
66
  subworkflowContext: AssignmentContext;
67
67
  };
68
+ export type ScopeGlobalExternalContext = {
69
+ scopeGlobal?: Record<string, unknown>;
70
+ };
68
71
  /**
69
72
  * External context type used by ExecutionUnitInput when creating providers.
70
73
  * This type is always expected to be present when providers are instantiated.
71
74
  */
72
- export type ExternalContext = ApplicationExternalContext & WorkflowExternalContext & JobExternalContext & MaterialsExternalContext & MethodDataExternalContext & MaterialsSetExternalContext & MaterialExternalContext & JinjaExternalContext & SubworkflowContext;
75
+ export type ExternalContext = ApplicationExternalContext & WorkflowExternalContext & JobExternalContext & MaterialsExternalContext & MethodDataExternalContext & MaterialsSetExternalContext & MaterialExternalContext & JinjaExternalContext & SubworkflowContext & ScopeGlobalExternalContext;
73
76
  /**
74
77
  * Type for provider names as they appear in templates.
75
78
  */
@@ -43,8 +43,9 @@ declare class ExecutionUnit extends ExecutionUnit_base implements Schema {
43
43
  render(externalContext: ExternalContext, convergence?: ConvergenceParameter): void;
44
44
  private getContextProvidersInstances;
45
45
  savePersistentContext(): void;
46
+ renderContext(scopeGlobal: Record<string, unknown>): void;
46
47
  saveRenderingContext(externalContext: ExternalContext): void;
47
- saveContext(externalContext: ExternalContext): void;
48
+ saveContext({ scopeGlobal, ...externalContext }: ExternalContext): void;
48
49
  getHashObject(): {
49
50
  application: {
50
51
  _id?: string;
@@ -130,7 +130,7 @@ class ExecutionUnit extends BaseUnit_1.default {
130
130
  .flat()),
131
131
  ];
132
132
  // TODO: kgrid should be abstracted and selected by user
133
- const parameterToContextProviderMap = {
133
+ const parameterToProviderMap = {
134
134
  N_k: "kgrid",
135
135
  N_k_nonuniform: "kgrid",
136
136
  };
@@ -139,8 +139,7 @@ class ExecutionUnit extends BaseUnit_1.default {
139
139
  return (0, providers_1.createProvider)(name, this.context, externalContext);
140
140
  })
141
141
  .map((provider) => {
142
- if (convergence &&
143
- provider.name === parameterToContextProviderMap[convergence.name]) {
142
+ if (convergence && provider.name === parameterToProviderMap[convergence.name]) {
144
143
  provider.applyConvergenceParameter(convergence);
145
144
  }
146
145
  return provider;
@@ -150,17 +149,28 @@ class ExecutionUnit extends BaseUnit_1.default {
150
149
  const persistentItems = this.contextProvidersInstances.map((p) => p.getContextItemData());
151
150
  this.context = persistentItems.filter((c) => c.isEdited);
152
151
  }
152
+ renderContext(scopeGlobal) {
153
+ this.contextProvidersInstances.forEach((provider) => {
154
+ provider.renderContext(scopeGlobal);
155
+ });
156
+ }
153
157
  saveRenderingContext(externalContext) {
158
+ // scopeGlobal resolves provider data only; do not pass it to input Jinja templates.
159
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- omitted from Jinja context
160
+ const { scopeGlobal, ...renderingExternalContext } = externalContext;
154
161
  const renderingItems = this.contextProvidersInstances.map((p) => p.getContextItemDataForRendering());
155
162
  this.renderingContext = {
156
163
  ...Object.fromEntries(renderingItems.map((context) => [context.name, context.data])),
157
- ...externalContext,
164
+ ...renderingExternalContext,
158
165
  };
159
166
  this.input = this.inputInstances.map((input) => {
160
167
  return input.render(this.renderingContext).toJSON();
161
168
  });
162
169
  }
163
- saveContext(externalContext) {
170
+ saveContext({ scopeGlobal, ...externalContext }) {
171
+ if (scopeGlobal) {
172
+ this.renderContext(scopeGlobal);
173
+ }
164
174
  this.savePersistentContext();
165
175
  this.saveRenderingContext(externalContext);
166
176
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mat3ra/wode",
3
- "version": "2026.6.29-0",
3
+ "version": "2026.7.3-0",
4
4
  "description": "WOrkflow DEfinitions",
5
5
  "scripts": {
6
6
  "test": "mocha --recursive --bail --require ts-node/register 'tests/js/**/*.ts'",
@@ -20,14 +20,8 @@ import type { MetaPropertyHolder } from "@mat3ra/prode";
20
20
  import { ApplicationRegistry, setUnitLinks } from "@mat3ra/standata";
21
21
  import { Utils } from "@mat3ra/utils";
22
22
 
23
- import type { MaterialExternalContext } from "./context/mixins/MaterialContextMixin";
24
- import type { MaterialsExternalContext } from "./context/mixins/MaterialsContextMixin";
25
- import type { MaterialsSetExternalContext } from "./context/mixins/MaterialsSetContextMixin";
26
23
  import type { AssignmentContext, ExternalContext } from "./context/providers";
27
- import type {
28
- JobExternalContext,
29
- WorkflowExternalContext,
30
- } from "./context/providers/by_application/espresso/QEPWXInputDataManager";
24
+ import type { WorkflowExternalContext } from "./context/providers/by_application/espresso/QEPWXInputDataManager";
31
25
  import { createConvergenceParameter } from "./convergence/factory";
32
26
  import { UnitTag, UnitType } from "./enums";
33
27
  import {
@@ -36,6 +30,7 @@ import {
36
30
  } from "./generated/SubworkflowSchemaMixin";
37
31
  import { AssignmentUnit, ConditionUnit, SubworkflowUnit, UnitFactory } from "./units";
38
32
  import type { AnySubworkflowUnit } from "./units/factory";
33
+ import type { WorkflowRenderContext } from "./Workflow";
39
34
 
40
35
  type ConvergenceConfig = {
41
36
  parameter: "N_k" | "N_k_nonuniform";
@@ -57,11 +52,7 @@ interface Subworkflow
57
52
  HashedEntity,
58
53
  Omit<ComputedEntityMixin, "compute"> {}
59
54
 
60
- type SubworkflowExternalContext = MaterialExternalContext &
61
- MaterialsExternalContext &
62
- MaterialsSetExternalContext &
63
- WorkflowExternalContext &
64
- JobExternalContext;
55
+ type SubworkflowExternalContext = WorkflowRenderContext & WorkflowExternalContext;
65
56
 
66
57
  class Subworkflow extends InMemoryEntity implements SubworkflowSchema {
67
58
  private ModelFactory: typeof ModelFactory;
@@ -55,11 +55,13 @@ interface Workflow
55
55
  compute: WorkflowSchema["compute"];
56
56
  }
57
57
 
58
- /** Context passed to Workflow.render() before `workflowHasRelaxation` is injected for subworkflows. */
58
+ /** Context passed to Workflow.render(); subworkflows also receive `workflowHasRelaxation`. */
59
59
  export type WorkflowRenderContext = MaterialExternalContext &
60
60
  MaterialsExternalContext &
61
61
  MaterialsSetExternalContext &
62
- JobExternalContext;
62
+ JobExternalContext & {
63
+ scopeGlobal?: Record<string, unknown>;
64
+ };
63
65
 
64
66
  class Workflow extends InMemoryEntity implements WorkflowSchema {
65
67
  declare createDefault: () => Workflow;
@@ -1,5 +1,7 @@
1
1
  import { type ContextItemSchema } from "@mat3ra/esse/dist/js/types";
2
+ import { setupNunjucksEnvironment } from "@mat3ra/standata";
2
3
  import { Utils } from "@mat3ra/utils";
4
+ import nunjucks from "nunjucks";
3
5
 
4
6
  export type UnitContext = ContextItemSchema[];
5
7
  export type ContextName = ContextItemSchema["name"];
@@ -28,6 +30,16 @@ export type EntityName = "unit" | "subworkflow";
28
30
  /** Minimal bound for provider external context; the full contract is ExternalContext in context/providers/index.ts */
29
31
  export type BaseExternalContext = object;
30
32
 
33
+ const nunjucksEnvironment = setupNunjucksEnvironment(new nunjucks.Environment());
34
+
35
+ const parseNumericStrings = (_key: string, value: unknown) => {
36
+ if (typeof value === "string" && value.trim() !== "" && !Number.isNaN(Number(value))) {
37
+ return Number(value);
38
+ }
39
+
40
+ return value;
41
+ };
42
+
31
43
  /**
32
44
  * Context providers expose three data layers. Keep them separate.
33
45
  *
@@ -134,6 +146,25 @@ abstract class ContextProvider<
134
146
  };
135
147
  }
136
148
 
149
+ /**
150
+ * Resolves Jinja placeholders in persisted context `data` from `scope.global`.
151
+ */
152
+ renderContext(scopeGlobal: Record<string, unknown>): boolean {
153
+ const data = this.getData();
154
+ const dataJson = JSON.stringify(data);
155
+ const renderedJson = nunjucksEnvironment.renderString(dataJson, scopeGlobal);
156
+ const resolved = JSON.parse(renderedJson, parseNumericStrings) as S["data"];
157
+
158
+ if (JSON.stringify(data) === JSON.stringify(resolved)) {
159
+ return false;
160
+ }
161
+
162
+ this.setData(resolved);
163
+ this.setIsEdited(true);
164
+
165
+ return true;
166
+ }
167
+
137
168
  /**
138
169
  * Helper method to find a context item from a unit context array by name.
139
170
  * Returns a partial schema object that can be safely passed to constructors.
@@ -117,7 +117,7 @@ class QEPWXInputDataManager extends JSONSchemaDataProvider<Schema, ExternalConte
117
117
  );
118
118
  return {
119
119
  X: `${symbolWithoutLabel}${label}`,
120
- Mass_X: PERIODIC_TABLE[symbol].atomic_mass,
120
+ Mass_X: PERIODIC_TABLE[symbolWithoutLabel].atomic_mass,
121
121
  PseudoPot_X: pseudo?.filename || path.basename(pseudo?.path || ""),
122
122
  };
123
123
  });
@@ -77,6 +77,10 @@ export type SubworkflowContext = {
77
77
  subworkflowContext: AssignmentContext;
78
78
  };
79
79
 
80
+ export type ScopeGlobalExternalContext = {
81
+ scopeGlobal?: Record<string, unknown>;
82
+ };
83
+
80
84
  /**
81
85
  * External context type used by ExecutionUnitInput when creating providers.
82
86
  * This type is always expected to be present when providers are instantiated.
@@ -89,7 +93,8 @@ export type ExternalContext = ApplicationExternalContext &
89
93
  MaterialsSetExternalContext &
90
94
  MaterialExternalContext &
91
95
  JinjaExternalContext &
92
- SubworkflowContext;
96
+ SubworkflowContext &
97
+ ScopeGlobalExternalContext;
93
98
 
94
99
  /**
95
100
  * Type for provider names as they appear in templates.
@@ -207,7 +207,7 @@ class ExecutionUnit extends (BaseUnit as Base) implements Schema {
207
207
  ];
208
208
 
209
209
  // TODO: kgrid should be abstracted and selected by user
210
- const parameterToContextProviderMap = {
210
+ const parameterToProviderMap = {
211
211
  N_k: "kgrid",
212
212
  N_k_nonuniform: "kgrid",
213
213
  } as const;
@@ -217,10 +217,7 @@ class ExecutionUnit extends (BaseUnit as Base) implements Schema {
217
217
  return createProvider(name, this.context, externalContext);
218
218
  })
219
219
  .map((provider) => {
220
- if (
221
- convergence &&
222
- provider.name === parameterToContextProviderMap[convergence.name]
223
- ) {
220
+ if (convergence && provider.name === parameterToProviderMap[convergence.name]) {
224
221
  provider.applyConvergenceParameter(convergence);
225
222
  }
226
223
  return provider;
@@ -232,20 +229,33 @@ class ExecutionUnit extends (BaseUnit as Base) implements Schema {
232
229
  this.context = persistentItems.filter((c) => c.isEdited);
233
230
  }
234
231
 
232
+ renderContext(scopeGlobal: Record<string, unknown>) {
233
+ this.contextProvidersInstances.forEach((provider) => {
234
+ provider.renderContext(scopeGlobal);
235
+ });
236
+ }
237
+
235
238
  saveRenderingContext(externalContext: ExternalContext) {
239
+ // scopeGlobal resolves provider data only; do not pass it to input Jinja templates.
240
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- omitted from Jinja context
241
+ const { scopeGlobal, ...renderingExternalContext } = externalContext;
236
242
  const renderingItems = this.contextProvidersInstances.map((p) =>
237
243
  p.getContextItemDataForRendering(),
238
244
  );
239
245
  this.renderingContext = {
240
246
  ...Object.fromEntries(renderingItems.map((context) => [context.name, context.data])),
241
- ...externalContext,
247
+ ...renderingExternalContext,
242
248
  };
243
249
  this.input = this.inputInstances.map((input) => {
244
250
  return input.render(this.renderingContext).toJSON();
245
251
  });
246
252
  }
247
253
 
248
- saveContext(externalContext: ExternalContext) {
254
+ saveContext({ scopeGlobal, ...externalContext }: ExternalContext) {
255
+ if (scopeGlobal) {
256
+ this.renderContext(scopeGlobal);
257
+ }
258
+
249
259
  this.savePersistentContext();
250
260
  this.saveRenderingContext(externalContext);
251
261
  }