@twin.org/modules 0.0.3-next.3 → 0.0.3-next.31

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # TWIN Framework Modules
2
2
 
3
- Helper classes for loading and executing from modules.
3
+ This package is part of the framework workspace and provides helper classes for loading and executing from modules to support consistent development workflows across the ecosystem.
4
4
 
5
5
  ## Installation
6
6
 
@@ -70,7 +70,7 @@ export class ModuleHelper {
70
70
  }
71
71
  throw new GeneralError(ModuleHelper.CLASS_NAME, "notFunction", {
72
72
  module,
73
- entry: method
73
+ method
74
74
  });
75
75
  }
76
76
  const moduleEntry = await ModuleHelper.getModuleEntry(module, methodParts[0]);
@@ -79,7 +79,7 @@ export class ModuleHelper {
79
79
  }
80
80
  throw new GeneralError(ModuleHelper.CLASS_NAME, "notFunction", {
81
81
  module,
82
- entry: method
82
+ method
83
83
  });
84
84
  }
85
85
  /**
@@ -99,78 +99,106 @@ export class ModuleHelper {
99
99
  * @param module The module.
100
100
  * @param method The method to execute from the module.
101
101
  * @param args The arguments to pass to the method.
102
+ * @param contextIds The context IDs.
102
103
  * @returns The result of the method execution.
103
104
  * @throws GeneralError if executing the module entry failed.
104
105
  */
105
- static async execModuleMethodThread(module, method, args) {
106
+ static async execModuleMethodThread(module, method, args, contextIds) {
106
107
  return new Promise((resolve, reject) => {
107
- const worker = new Worker(`(async () => {
108
- try {
109
- const { workerData, parentPort } = await import('node:worker_threads');
108
+ const messageModule = ModuleHelper.execModuleMethodThreadMessage(module, (resultMethod, result, err) => {
109
+ if (err) {
110
+ reject(err);
111
+ }
112
+ else {
113
+ resolve(result);
114
+ }
115
+ });
116
+ messageModule.executeMethod(method, args, contextIds);
117
+ });
118
+ }
119
+ /**
120
+ * Load the module and provide a messaging interface.
121
+ * @param module The module.
122
+ * @param completed Callback called when the worker thread processes a completion.
123
+ * @param options Optional settings.
124
+ * @param options.threadName The name of the thread.
125
+ * @returns The messaging interface.
126
+ * @throws GeneralError if executing the module entry failed.
127
+ */
128
+ static execModuleMethodThreadMessage(module, completed, options) {
129
+ const worker = new Worker(`(async () => {
130
+ const { workerData, parentPort } = await import('node:worker_threads');
131
+ const { ContextIdStore } = await import('@twin.org/context');
132
+ const { BaseError } = await import('@twin.org/core');
133
+ const { module } = workerData;
110
134
 
111
- function rejectError(errorType, cause) {
112
- parentPort.postMessage({ errorType, cause });
113
- }
135
+ function rejectError(errorType, methodName, args, cause) {
136
+ parentPort.postMessage({ errorType, method: methodName, args, cause: BaseError.fromError(cause).toJsonObject(true) });
137
+ }
114
138
 
115
- async function executeMethod(method) {
116
- try {
139
+ async function executeMethod(method, methodName, args, contextIds) {
140
+ try {
141
+ await ContextIdStore.run(contextIds ?? {}, async () => {
117
142
  const result = await method(...(args ?? []));
118
143
 
119
- parentPort.postMessage({ result });
120
- } catch (err) {
121
- rejectError('resultError', err);
122
- }
144
+ parentPort.postMessage({ method: methodName, result });
145
+ });
146
+ } catch (err) {
147
+ rejectError('resultError', methodName, args, err);
123
148
  }
149
+ }
124
150
 
125
- const { module, method, args } = workerData;
151
+ const modules = {};
126
152
 
127
- const moduleInstance = await import(module);
128
- const methodParts = method.split('.');
129
- const moduleEntry = moduleInstance[methodParts[0]];
153
+ parentPort.on('message', async msg => {
154
+ const { method, args, contextIds } = msg;
130
155
 
131
- if (moduleEntry === undefined) {
132
- rejectError('entryNotFound');
133
- } else if (methodParts.length === 2) {
134
- const moduleMethod = moduleEntry[methodParts[1]];
135
- if (typeof moduleMethod === 'function') {
136
- await executeMethod(moduleMethod, args);
156
+ try {
157
+ const moduleInstance = modules[module] ?? (await import(module));
158
+ modules[module] = moduleInstance;
159
+
160
+ const methodParts = method.split('.');
161
+ const moduleEntry = moduleInstance[methodParts[0]];
162
+
163
+ if (moduleEntry === undefined) {
164
+ rejectError('entryNotFound', method, args);
165
+ } else if (methodParts.length === 2) {
166
+ const moduleMethod = moduleEntry[methodParts[1]];
167
+ if (typeof moduleMethod === 'function') {
168
+ await executeMethod(moduleMethod, method, args, contextIds);
169
+ } else {
170
+ rejectError('notFunction', method, args);
171
+ }
172
+ } else if (typeof moduleEntry === 'function') {
173
+ await executeMethod(moduleEntry, method, args, contextIds);
137
174
  } else {
138
- rejectError('notFunction');
175
+ rejectError('notFunction', method, args);
139
176
  }
140
- } else if (typeof moduleEntry === 'function') {
141
- await executeMethod(moduleEntry, args);
142
- } else {
143
- rejectError('notFunction');
177
+ } catch (errInner) {
178
+ rejectError('moduleNotFound', method, args, errInner);
144
179
  }
145
- } catch (err) {
146
- rejectError('moduleNotFound', err);
147
- }
148
- })();
149
- `, { eval: true, workerData: { module, method, args: args ?? [] } });
150
- worker.on("message", msg => {
151
- if (Is.stringValue(msg.errorType)) {
152
- reject(new GeneralError(ModuleHelper.CLASS_NAME, msg.errorType, { module, entry: method }, msg.cause));
153
- }
154
- else {
155
- resolve(msg.result);
156
- }
157
- });
158
- worker.on("error", err => {
159
- reject(new GeneralError(ModuleHelper.CLASS_NAME, "workerException", {
160
- module,
161
- entry: method
162
- }, err));
163
- });
164
- worker.on("exit", code => {
165
- if (code === 1) {
166
- reject(new GeneralError(ModuleHelper.CLASS_NAME, "workerFailed", {
167
- module,
168
- entry: method,
169
- exitCode: code
170
- }));
171
- }
172
- });
180
+ });
181
+ })();`, { eval: true, workerData: { module }, name: options?.threadName });
182
+ worker.on("message", msg => {
183
+ if (Is.stringValue(msg.errorType)) {
184
+ completed(msg.method, undefined, new GeneralError(ModuleHelper.CLASS_NAME, msg.errorType, { module, method: msg.method, args: msg.args }, msg.cause));
185
+ }
186
+ else {
187
+ completed(msg.method, msg.result);
188
+ }
189
+ });
190
+ worker.on("error", err => {
191
+ completed("error", undefined, new GeneralError(ModuleHelper.CLASS_NAME, "workerException", {
192
+ module
193
+ }, err));
194
+ });
195
+ worker.on("exit", code => {
196
+ completed("terminate", code);
173
197
  });
198
+ return {
199
+ executeMethod: (method, args, contextIds) => worker.postMessage({ method, args, contextIds }),
200
+ terminate: async () => worker.terminate()
201
+ };
174
202
  }
175
203
  /**
176
204
  * Check if a module is a local module.
@@ -1 +1 @@
1
- {"version":3,"file":"moduleHelper.js","sourceRoot":"","sources":["../../../src/helpers/moduleHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Worker } from \"node:worker_threads\";\nimport { BaseError, GeneralError, Is, SharedStore } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\n\n/**\n * Helper functions for modules.\n */\nexport class ModuleHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<ModuleHelper>();\n\n\t/**\n\t * Override the import function for modules.\n\t * @param overrideImport The override import function.\n\t */\n\tpublic static overrideImport(\n\t\toverrideImport: (moduleName: string) => Promise<{ module?: unknown; useDefault: boolean }>\n\t): void {\n\t\tSharedStore.set(\"overrideImport\", overrideImport);\n\t}\n\n\t/**\n\t * Get the module entry.\n\t * @param module The module.\n\t * @param entry The entry to get from the module.\n\t * @returns The entry from the module.\n\t * @throws GeneralError if getting the module entry failed.\n\t */\n\tpublic static async getModuleEntry<T>(module: string, entry: string): Promise<T> {\n\t\tlet moduleInstance;\n\n\t\ttry {\n\t\t\tlet useDefault = true;\n\n\t\t\tconst overrideImport =\n\t\t\t\tSharedStore.get<(moduleName: string) => Promise<{ module?: unknown; useDefault: boolean }>>(\n\t\t\t\t\t\"overrideImport\"\n\t\t\t\t);\n\n\t\t\tif (Is.function(overrideImport)) {\n\t\t\t\tconst overrideResult = await overrideImport(module);\n\n\t\t\t\tmoduleInstance = overrideResult.module;\n\t\t\t\tuseDefault = overrideResult.useDefault;\n\t\t\t}\n\n\t\t\tif (useDefault) {\n\t\t\t\tmoduleInstance = await import(module);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tModuleHelper.CLASS_NAME,\n\t\t\t\t\"moduleNotFound\",\n\t\t\t\t{\n\t\t\t\t\tmodule,\n\t\t\t\t\tentry\n\t\t\t\t},\n\t\t\t\tBaseError.fromError(err)\n\t\t\t);\n\t\t}\n\n\t\tconst moduleEntry = moduleInstance?.[entry];\n\n\t\tif (Is.empty(moduleEntry)) {\n\t\t\tthrow new GeneralError(ModuleHelper.CLASS_NAME, \"entryNotFound\", {\n\t\t\t\tmodule,\n\t\t\t\tentry\n\t\t\t});\n\t\t}\n\n\t\treturn moduleEntry as T;\n\t}\n\n\t/**\n\t * Get the method from a module.\n\t * @param module The module.\n\t * @param method The method to execute from the module, use dot notation to get a static class method.\n\t * @returns The result of the method execution.\n\t * @throws GeneralError if executing the module entry failed.\n\t */\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tpublic static async getModuleMethod<T extends (...args: any[]) => any = (...args: any[]) => any>(\n\t\tmodule: string,\n\t\tmethod: string\n\t): Promise<T> {\n\t\tconst methodParts = method.split(\".\");\n\n\t\tif (methodParts.length === 2) {\n\t\t\tconst moduleEntry = await ModuleHelper.getModuleEntry<{\n\t\t\t\t[id: string]: T;\n\t\t\t}>(module, methodParts[0]);\n\n\t\t\tif (Is.function<T>(moduleEntry[methodParts[1]])) {\n\t\t\t\treturn moduleEntry[methodParts[1]];\n\t\t\t}\n\t\t\tthrow new GeneralError(ModuleHelper.CLASS_NAME, \"notFunction\", {\n\t\t\t\tmodule,\n\t\t\t\tentry: method\n\t\t\t});\n\t\t}\n\n\t\tconst moduleEntry = await ModuleHelper.getModuleEntry<T>(module, methodParts[0]);\n\n\t\tif (Is.function<T>(moduleEntry)) {\n\t\t\treturn moduleEntry;\n\t\t}\n\n\t\tthrow new GeneralError(ModuleHelper.CLASS_NAME, \"notFunction\", {\n\t\t\tmodule,\n\t\t\tentry: method\n\t\t});\n\t}\n\n\t/**\n\t * Execute the method in the module.\n\t * @param module The module.\n\t * @param method The method to execute from the module.\n\t * @param args The arguments to pass to the method.\n\t * @returns The result of the method execution.\n\t * @throws GeneralError if executing the module entry failed.\n\t */\n\tpublic static async execModuleMethod<T>(\n\t\tmodule: string,\n\t\tmethod: string,\n\t\targs?: unknown[]\n\t): Promise<T> {\n\t\tconst moduleMethod = await ModuleHelper.getModuleMethod<(...args: unknown[]) => T>(\n\t\t\tmodule,\n\t\t\tmethod\n\t\t);\n\n\t\treturn moduleMethod(...(args ?? []));\n\t}\n\n\t/**\n\t * Execute the method in the module in a thread.\n\t * @param module The module.\n\t * @param method The method to execute from the module.\n\t * @param args The arguments to pass to the method.\n\t * @returns The result of the method execution.\n\t * @throws GeneralError if executing the module entry failed.\n\t */\n\tpublic static async execModuleMethodThread<T>(\n\t\tmodule: string,\n\t\tmethod: string,\n\t\targs?: unknown[]\n\t): Promise<T> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst worker = new Worker(\n\t\t\t\t`(async () => {\n\ttry {\n\t\tconst { workerData, parentPort } = await import('node:worker_threads');\n\n\t\tfunction rejectError(errorType, cause) {\n\t\t\tparentPort.postMessage({ errorType, cause });\n\t\t}\n\n\t\tasync function executeMethod(method) {\n\t\t\ttry {\n\t\t\t\tconst result = await method(...(args ?? []));\n\n\t\t\t\tparentPort.postMessage({ result });\n\t\t\t} catch (err) {\n\t\t\t\trejectError('resultError', err);\n\t\t\t}\n\t\t}\n\n\t\tconst { module, method, args } = workerData;\n\n\t\tconst moduleInstance = await import(module);\n\t\tconst methodParts = method.split('.');\n\t\tconst moduleEntry = moduleInstance[methodParts[0]];\n\n\t\tif (moduleEntry === undefined) {\n\t\t\trejectError('entryNotFound');\n\t\t} else if (methodParts.length === 2) {\n\t\t\tconst moduleMethod = moduleEntry[methodParts[1]];\n\t\t\tif (typeof moduleMethod === 'function') {\n\t\t\t\tawait executeMethod(moduleMethod, args);\n\t\t\t} else {\n\t\t\t\trejectError('notFunction');\n\t\t\t}\n\t\t} else if (typeof moduleEntry === 'function') {\n\t\t\tawait executeMethod(moduleEntry, args);\n\t\t} else {\n\t\t\trejectError('notFunction');\n\t\t}\n\t} catch (err) {\n\t\trejectError('moduleNotFound', err);\n\t}\n})();\n\t\t\t`,\n\t\t\t\t{ eval: true, workerData: { module, method, args: args ?? [] } }\n\t\t\t);\n\n\t\t\tworker.on(\"message\", msg => {\n\t\t\t\tif (Is.stringValue(msg.errorType)) {\n\t\t\t\t\treject(\n\t\t\t\t\t\tnew GeneralError(\n\t\t\t\t\t\t\tModuleHelper.CLASS_NAME,\n\t\t\t\t\t\t\tmsg.errorType,\n\t\t\t\t\t\t\t{ module, entry: method },\n\t\t\t\t\t\t\tmsg.cause\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tresolve(msg.result);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tworker.on(\"error\", err => {\n\t\t\t\treject(\n\t\t\t\t\tnew GeneralError(\n\t\t\t\t\t\tModuleHelper.CLASS_NAME,\n\t\t\t\t\t\t\"workerException\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmodule,\n\t\t\t\t\t\t\tentry: method\n\t\t\t\t\t\t},\n\t\t\t\t\t\terr\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t});\n\n\t\t\tworker.on(\"exit\", code => {\n\t\t\t\tif (code === 1) {\n\t\t\t\t\treject(\n\t\t\t\t\t\tnew GeneralError(ModuleHelper.CLASS_NAME, \"workerFailed\", {\n\t\t\t\t\t\t\tmodule,\n\t\t\t\t\t\t\tentry: method,\n\t\t\t\t\t\t\texitCode: code\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Check if a module is a local module.\n\t * @param name The name of the module.\n\t * @returns True if the module is local, false otherwise.\n\t */\n\tpublic static isLocalModule(name: string): boolean {\n\t\treturn name.startsWith(\".\") || name.startsWith(\"/\");\n\t}\n\n\t/**\n\t * Check if a module is a relative module.\n\t * @param name The name of the module.\n\t * @returns True if the module is relative, false otherwise.\n\t */\n\tpublic static isRelativeModule(name: string): boolean {\n\t\treturn name.startsWith(\".\");\n\t}\n}\n"]}
1
+ {"version":3,"file":"moduleHelper.js","sourceRoot":"","sources":["../../../src/helpers/moduleHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Worker } from \"node:worker_threads\";\nimport type { IContextIds } from \"@twin.org/context\";\nimport { BaseError, GeneralError, Is, SharedStore } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IModuleWorker } from \"../models/IModuleWorker.js\";\n\n/**\n * Helper functions for modules.\n */\nexport class ModuleHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<ModuleHelper>();\n\n\t/**\n\t * Override the import function for modules.\n\t * @param overrideImport The override import function.\n\t */\n\tpublic static overrideImport(\n\t\toverrideImport: (moduleName: string) => Promise<{ module?: unknown; useDefault: boolean }>\n\t): void {\n\t\tSharedStore.set(\"overrideImport\", overrideImport);\n\t}\n\n\t/**\n\t * Get the module entry.\n\t * @param module The module.\n\t * @param entry The entry to get from the module.\n\t * @returns The entry from the module.\n\t * @throws GeneralError if getting the module entry failed.\n\t */\n\tpublic static async getModuleEntry<T>(module: string, entry: string): Promise<T> {\n\t\tlet moduleInstance;\n\n\t\ttry {\n\t\t\tlet useDefault = true;\n\n\t\t\tconst overrideImport =\n\t\t\t\tSharedStore.get<(moduleName: string) => Promise<{ module?: unknown; useDefault: boolean }>>(\n\t\t\t\t\t\"overrideImport\"\n\t\t\t\t);\n\n\t\t\tif (Is.function(overrideImport)) {\n\t\t\t\tconst overrideResult = await overrideImport(module);\n\n\t\t\t\tmoduleInstance = overrideResult.module;\n\t\t\t\tuseDefault = overrideResult.useDefault;\n\t\t\t}\n\n\t\t\tif (useDefault) {\n\t\t\t\tmoduleInstance = await import(module);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tModuleHelper.CLASS_NAME,\n\t\t\t\t\"moduleNotFound\",\n\t\t\t\t{\n\t\t\t\t\tmodule,\n\t\t\t\t\tentry\n\t\t\t\t},\n\t\t\t\tBaseError.fromError(err)\n\t\t\t);\n\t\t}\n\n\t\tconst moduleEntry = moduleInstance?.[entry];\n\n\t\tif (Is.empty(moduleEntry)) {\n\t\t\tthrow new GeneralError(ModuleHelper.CLASS_NAME, \"entryNotFound\", {\n\t\t\t\tmodule,\n\t\t\t\tentry\n\t\t\t});\n\t\t}\n\n\t\treturn moduleEntry as T;\n\t}\n\n\t/**\n\t * Get the method from a module.\n\t * @param module The module.\n\t * @param method The method to execute from the module, use dot notation to get a static class method.\n\t * @returns The result of the method execution.\n\t * @throws GeneralError if executing the module entry failed.\n\t */\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tpublic static async getModuleMethod<T extends (...args: any[]) => any = (...args: any[]) => any>(\n\t\tmodule: string,\n\t\tmethod: string\n\t): Promise<T> {\n\t\tconst methodParts = method.split(\".\");\n\n\t\tif (methodParts.length === 2) {\n\t\t\tconst moduleEntry = await ModuleHelper.getModuleEntry<{\n\t\t\t\t[id: string]: T;\n\t\t\t}>(module, methodParts[0]);\n\n\t\t\tif (Is.function<T>(moduleEntry[methodParts[1]])) {\n\t\t\t\treturn moduleEntry[methodParts[1]];\n\t\t\t}\n\t\t\tthrow new GeneralError(ModuleHelper.CLASS_NAME, \"notFunction\", {\n\t\t\t\tmodule,\n\t\t\t\tmethod\n\t\t\t});\n\t\t}\n\n\t\tconst moduleEntry = await ModuleHelper.getModuleEntry<T>(module, methodParts[0]);\n\n\t\tif (Is.function<T>(moduleEntry)) {\n\t\t\treturn moduleEntry;\n\t\t}\n\n\t\tthrow new GeneralError(ModuleHelper.CLASS_NAME, \"notFunction\", {\n\t\t\tmodule,\n\t\t\tmethod\n\t\t});\n\t}\n\n\t/**\n\t * Execute the method in the module.\n\t * @param module The module.\n\t * @param method The method to execute from the module.\n\t * @param args The arguments to pass to the method.\n\t * @returns The result of the method execution.\n\t * @throws GeneralError if executing the module entry failed.\n\t */\n\tpublic static async execModuleMethod<T>(\n\t\tmodule: string,\n\t\tmethod: string,\n\t\targs?: unknown[]\n\t): Promise<T> {\n\t\tconst moduleMethod = await ModuleHelper.getModuleMethod<(...args: unknown[]) => T>(\n\t\t\tmodule,\n\t\t\tmethod\n\t\t);\n\n\t\treturn moduleMethod(...(args ?? []));\n\t}\n\n\t/**\n\t * Execute the method in the module in a thread.\n\t * @param module The module.\n\t * @param method The method to execute from the module.\n\t * @param args The arguments to pass to the method.\n\t * @param contextIds The context IDs.\n\t * @returns The result of the method execution.\n\t * @throws GeneralError if executing the module entry failed.\n\t */\n\tpublic static async execModuleMethodThread<T>(\n\t\tmodule: string,\n\t\tmethod: string,\n\t\targs?: unknown[],\n\t\tcontextIds?: IContextIds\n\t): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst messageModule = ModuleHelper.execModuleMethodThreadMessage(\n\t\t\t\tmodule,\n\t\t\t\t(resultMethod, result, err) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresolve(result as T);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tmessageModule.executeMethod(method, args, contextIds);\n\t\t});\n\t}\n\n\t/**\n\t * Load the module and provide a messaging interface.\n\t * @param module The module.\n\t * @param completed Callback called when the worker thread processes a completion.\n\t * @param options Optional settings.\n\t * @param options.threadName The name of the thread.\n\t * @returns The messaging interface.\n\t * @throws GeneralError if executing the module entry failed.\n\t */\n\tpublic static execModuleMethodThreadMessage(\n\t\tmodule: string,\n\t\tcompleted: (operation: string, result?: unknown, err?: Error) => void,\n\t\toptions?: {\n\t\t\tthreadName?: string;\n\t\t}\n\t): IModuleWorker {\n\t\tconst worker = new Worker(\n\t\t\t`(async () => {\n\tconst { workerData, parentPort } = await import('node:worker_threads');\n\tconst { ContextIdStore } = await import('@twin.org/context');\n\tconst { BaseError } = await import('@twin.org/core');\n\tconst { module } = workerData;\n\n\tfunction rejectError(errorType, methodName, args, cause) {\n\t\tparentPort.postMessage({ errorType, method: methodName, args, cause: BaseError.fromError(cause).toJsonObject(true) });\n\t}\n\n\tasync function executeMethod(method, methodName, args, contextIds) {\n\t\ttry {\n\t\t\tawait ContextIdStore.run(contextIds ?? {}, async () => {\n\t\t\t\tconst result = await method(...(args ?? []));\n\n\t\t\t\tparentPort.postMessage({ method: methodName, result });\n\t\t\t});\n\t\t} catch (err) {\n\t\t\trejectError('resultError', methodName, args, err);\n\t\t}\n\t}\n\n\tconst modules = {};\n\n\tparentPort.on('message', async msg => {\n\t\tconst { method, args, contextIds } = msg;\n\n\t\ttry {\n\t\t\tconst moduleInstance = modules[module] ?? (await import(module));\n\t\t\tmodules[module] = moduleInstance;\n\n\t\t\tconst methodParts = method.split('.');\n\t\t\tconst moduleEntry = moduleInstance[methodParts[0]];\n\n\t\t\tif (moduleEntry === undefined) {\n\t\t\t\trejectError('entryNotFound', method, args);\n\t\t\t} else if (methodParts.length === 2) {\n\t\t\t\tconst moduleMethod = moduleEntry[methodParts[1]];\n\t\t\t\tif (typeof moduleMethod === 'function') {\n\t\t\t\t\tawait executeMethod(moduleMethod, method, args, contextIds);\n\t\t\t\t} else {\n\t\t\t\t\trejectError('notFunction', method, args);\n\t\t\t\t}\n\t\t\t} else if (typeof moduleEntry === 'function') {\n\t\t\t\tawait executeMethod(moduleEntry, method, args, contextIds);\n\t\t\t} else {\n\t\t\t\trejectError('notFunction', method, args);\n\t\t\t}\n\t\t} catch (errInner) {\n\t\t\t\trejectError('moduleNotFound', method, args, errInner);\n\t\t}\n\t});\n})();`,\n\t\t\t{ eval: true, workerData: { module }, name: options?.threadName }\n\t\t);\n\n\t\tworker.on(\"message\", msg => {\n\t\t\tif (Is.stringValue(msg.errorType)) {\n\t\t\t\tcompleted(\n\t\t\t\t\tmsg.method,\n\t\t\t\t\tundefined,\n\t\t\t\t\tnew GeneralError(\n\t\t\t\t\t\tModuleHelper.CLASS_NAME,\n\t\t\t\t\t\tmsg.errorType,\n\t\t\t\t\t\t{ module, method: msg.method, args: msg.args },\n\t\t\t\t\t\tmsg.cause\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tcompleted(msg.method, msg.result);\n\t\t\t}\n\t\t});\n\n\t\tworker.on(\"error\", err => {\n\t\t\tcompleted(\n\t\t\t\t\"error\",\n\t\t\t\tundefined,\n\t\t\t\tnew GeneralError(\n\t\t\t\t\tModuleHelper.CLASS_NAME,\n\t\t\t\t\t\"workerException\",\n\t\t\t\t\t{\n\t\t\t\t\t\tmodule\n\t\t\t\t\t},\n\t\t\t\t\terr\n\t\t\t\t)\n\t\t\t);\n\t\t});\n\n\t\tworker.on(\"exit\", code => {\n\t\t\tcompleted(\"terminate\", code);\n\t\t});\n\n\t\treturn {\n\t\t\texecuteMethod: (method: string, args?: unknown, contextIds?: IContextIds) =>\n\t\t\t\tworker.postMessage({ method, args, contextIds }),\n\t\t\tterminate: async () => worker.terminate()\n\t\t};\n\t}\n\n\t/**\n\t * Check if a module is a local module.\n\t * @param name The name of the module.\n\t * @returns True if the module is local, false otherwise.\n\t */\n\tpublic static isLocalModule(name: string): boolean {\n\t\treturn name.startsWith(\".\") || name.startsWith(\"/\");\n\t}\n\n\t/**\n\t * Check if a module is a relative module.\n\t * @param name The name of the module.\n\t * @returns True if the module is relative, false otherwise.\n\t */\n\tpublic static isRelativeModule(name: string): boolean {\n\t\treturn name.startsWith(\".\");\n\t}\n}\n"]}
package/dist/es/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // Copyright 2024 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  export * from "./helpers/moduleHelper.js";
4
+ export * from "./models/IModuleWorker.js";
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,2BAA2B,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./helpers/moduleHelper.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./helpers/moduleHelper.js\";\nexport * from \"./models/IModuleWorker.js\";\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IModuleWorker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IModuleWorker.js","sourceRoot":"","sources":["../../../src/models/IModuleWorker.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IContextIds } from \"@twin.org/context\";\n\n/**\n * Worker definition for modules.\n */\nexport interface IModuleWorker {\n\t/**\n\t * Execute a method in the module.\n\t * @param method The method to execute.\n\t * @param args The arguments for the method.\n\t * @param contextIds The context IDs.\n\t * @returns The result of the method.\n\t */\n\texecuteMethod(method: string, args?: unknown, contextIds?: IContextIds): void;\n\n\t/**\n\t * Terminate the worker.\n\t * @returns A promise that resolves when the worker is terminated including the exit code.\n\t */\n\tterminate(): Promise<number>;\n}\n"]}
@@ -1,3 +1,5 @@
1
+ import type { IContextIds } from "@twin.org/context";
2
+ import type { IModuleWorker } from "../models/IModuleWorker.js";
1
3
  /**
2
4
  * Helper functions for modules.
3
5
  */
@@ -44,10 +46,23 @@ export declare class ModuleHelper {
44
46
  * @param module The module.
45
47
  * @param method The method to execute from the module.
46
48
  * @param args The arguments to pass to the method.
49
+ * @param contextIds The context IDs.
47
50
  * @returns The result of the method execution.
48
51
  * @throws GeneralError if executing the module entry failed.
49
52
  */
50
- static execModuleMethodThread<T>(module: string, method: string, args?: unknown[]): Promise<T>;
53
+ static execModuleMethodThread<T>(module: string, method: string, args?: unknown[], contextIds?: IContextIds): Promise<T>;
54
+ /**
55
+ * Load the module and provide a messaging interface.
56
+ * @param module The module.
57
+ * @param completed Callback called when the worker thread processes a completion.
58
+ * @param options Optional settings.
59
+ * @param options.threadName The name of the thread.
60
+ * @returns The messaging interface.
61
+ * @throws GeneralError if executing the module entry failed.
62
+ */
63
+ static execModuleMethodThreadMessage(module: string, completed: (operation: string, result?: unknown, err?: Error) => void, options?: {
64
+ threadName?: string;
65
+ }): IModuleWorker;
51
66
  /**
52
67
  * Check if a module is a local module.
53
68
  * @param name The name of the module.
@@ -1 +1,2 @@
1
1
  export * from "./helpers/moduleHelper.js";
2
+ export * from "./models/IModuleWorker.js";
@@ -0,0 +1,19 @@
1
+ import type { IContextIds } from "@twin.org/context";
2
+ /**
3
+ * Worker definition for modules.
4
+ */
5
+ export interface IModuleWorker {
6
+ /**
7
+ * Execute a method in the module.
8
+ * @param method The method to execute.
9
+ * @param args The arguments for the method.
10
+ * @param contextIds The context IDs.
11
+ * @returns The result of the method.
12
+ */
13
+ executeMethod(method: string, args?: unknown, contextIds?: IContextIds): void;
14
+ /**
15
+ * Terminate the worker.
16
+ * @returns A promise that resolves when the worker is terminated including the exit code.
17
+ */
18
+ terminate(): Promise<number>;
19
+ }
package/docs/changelog.md CHANGED
@@ -1,4 +1,587 @@
1
- # @twin.org/modules - Changelog
1
+ # Changelog
2
+
3
+ ## [0.0.3-next.31](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.30...modules-v0.0.3-next.31) (2026-04-14)
4
+
5
+
6
+ ### Miscellaneous Chores
7
+
8
+ * **modules:** Synchronize repo versions
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/core bumped from 0.0.3-next.30 to 0.0.3-next.31
16
+ * @twin.org/context bumped from 0.0.3-next.30 to 0.0.3-next.31
17
+ * @twin.org/nameof bumped from 0.0.3-next.30 to 0.0.3-next.31
18
+ * devDependencies
19
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.30 to 0.0.3-next.31
20
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.30 to 0.0.3-next.31
21
+ * @twin.org/validate-locales bumped from 0.0.3-next.30 to 0.0.3-next.31
22
+
23
+ ## [0.0.3-next.30](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.29...modules-v0.0.3-next.30) (2026-04-14)
24
+
25
+
26
+ ### Miscellaneous Chores
27
+
28
+ * **modules:** Synchronize repo versions
29
+
30
+
31
+ ### Dependencies
32
+
33
+ * The following workspace dependencies were updated
34
+ * dependencies
35
+ * @twin.org/core bumped from 0.0.3-next.29 to 0.0.3-next.30
36
+ * @twin.org/context bumped from 0.0.3-next.29 to 0.0.3-next.30
37
+ * @twin.org/nameof bumped from 0.0.3-next.29 to 0.0.3-next.30
38
+ * devDependencies
39
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.29 to 0.0.3-next.30
40
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.29 to 0.0.3-next.30
41
+ * @twin.org/validate-locales bumped from 0.0.3-next.29 to 0.0.3-next.30
42
+
43
+ ## [0.0.3-next.29](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.28...modules-v0.0.3-next.29) (2026-04-14)
44
+
45
+
46
+ ### Miscellaneous Chores
47
+
48
+ * **modules:** Synchronize repo versions
49
+
50
+
51
+ ### Dependencies
52
+
53
+ * The following workspace dependencies were updated
54
+ * dependencies
55
+ * @twin.org/core bumped from 0.0.3-next.28 to 0.0.3-next.29
56
+ * @twin.org/context bumped from 0.0.3-next.28 to 0.0.3-next.29
57
+ * @twin.org/nameof bumped from 0.0.3-next.28 to 0.0.3-next.29
58
+ * devDependencies
59
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.28 to 0.0.3-next.29
60
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.28 to 0.0.3-next.29
61
+ * @twin.org/validate-locales bumped from 0.0.3-next.28 to 0.0.3-next.29
62
+
63
+ ## [0.0.3-next.28](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.27...modules-v0.0.3-next.28) (2026-03-27)
64
+
65
+
66
+ ### Miscellaneous Chores
67
+
68
+ * **modules:** Synchronize repo versions
69
+
70
+
71
+ ### Dependencies
72
+
73
+ * The following workspace dependencies were updated
74
+ * dependencies
75
+ * @twin.org/core bumped from 0.0.3-next.27 to 0.0.3-next.28
76
+ * @twin.org/context bumped from 0.0.3-next.27 to 0.0.3-next.28
77
+ * @twin.org/nameof bumped from 0.0.3-next.27 to 0.0.3-next.28
78
+ * devDependencies
79
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.27 to 0.0.3-next.28
80
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.27 to 0.0.3-next.28
81
+ * @twin.org/validate-locales bumped from 0.0.3-next.27 to 0.0.3-next.28
82
+
83
+ ## [0.0.3-next.27](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.26...modules-v0.0.3-next.27) (2026-03-27)
84
+
85
+
86
+ ### Miscellaneous Chores
87
+
88
+ * **modules:** Synchronize repo versions
89
+
90
+
91
+ ### Dependencies
92
+
93
+ * The following workspace dependencies were updated
94
+ * dependencies
95
+ * @twin.org/core bumped from 0.0.3-next.26 to 0.0.3-next.27
96
+ * @twin.org/context bumped from 0.0.3-next.26 to 0.0.3-next.27
97
+ * @twin.org/nameof bumped from 0.0.3-next.26 to 0.0.3-next.27
98
+ * devDependencies
99
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.26 to 0.0.3-next.27
100
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.26 to 0.0.3-next.27
101
+ * @twin.org/validate-locales bumped from 0.0.3-next.26 to 0.0.3-next.27
102
+
103
+ ## [0.0.3-next.26](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.25...modules-v0.0.3-next.26) (2026-03-24)
104
+
105
+
106
+ ### Miscellaneous Chores
107
+
108
+ * **modules:** Synchronize repo versions
109
+
110
+
111
+ ### Dependencies
112
+
113
+ * The following workspace dependencies were updated
114
+ * dependencies
115
+ * @twin.org/core bumped from 0.0.3-next.25 to 0.0.3-next.26
116
+ * @twin.org/context bumped from 0.0.3-next.25 to 0.0.3-next.26
117
+ * @twin.org/nameof bumped from 0.0.3-next.25 to 0.0.3-next.26
118
+ * devDependencies
119
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.25 to 0.0.3-next.26
120
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.25 to 0.0.3-next.26
121
+ * @twin.org/validate-locales bumped from 0.0.3-next.25 to 0.0.3-next.26
122
+
123
+ ## [0.0.3-next.25](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.24...modules-v0.0.3-next.25) (2026-03-23)
124
+
125
+
126
+ ### Miscellaneous Chores
127
+
128
+ * **modules:** Synchronize repo versions
129
+
130
+
131
+ ### Dependencies
132
+
133
+ * The following workspace dependencies were updated
134
+ * dependencies
135
+ * @twin.org/core bumped from 0.0.3-next.24 to 0.0.3-next.25
136
+ * @twin.org/context bumped from 0.0.3-next.24 to 0.0.3-next.25
137
+ * @twin.org/nameof bumped from 0.0.3-next.24 to 0.0.3-next.25
138
+ * devDependencies
139
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.24 to 0.0.3-next.25
140
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.24 to 0.0.3-next.25
141
+ * @twin.org/validate-locales bumped from 0.0.3-next.24 to 0.0.3-next.25
142
+
143
+ ## [0.0.3-next.24](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.23...modules-v0.0.3-next.24) (2026-03-19)
144
+
145
+
146
+ ### Bug Fixes
147
+
148
+ * ensure __decorate is defined for decorators ([103a563](https://github.com/twinfoundation/framework/commit/103a563ce01ebdef6240d2e590e7b026e8692684))
149
+
150
+
151
+ ### Dependencies
152
+
153
+ * The following workspace dependencies were updated
154
+ * dependencies
155
+ * @twin.org/core bumped from 0.0.3-next.23 to 0.0.3-next.24
156
+ * @twin.org/context bumped from 0.0.3-next.23 to 0.0.3-next.24
157
+ * @twin.org/nameof bumped from 0.0.3-next.23 to 0.0.3-next.24
158
+ * devDependencies
159
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.23 to 0.0.3-next.24
160
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.23 to 0.0.3-next.24
161
+ * @twin.org/validate-locales bumped from 0.0.3-next.23 to 0.0.3-next.24
162
+
163
+ ## [0.0.3-next.23](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.22...modules-v0.0.3-next.23) (2026-03-17)
164
+
165
+
166
+ ### Miscellaneous Chores
167
+
168
+ * **modules:** Synchronize repo versions
169
+
170
+
171
+ ### Dependencies
172
+
173
+ * The following workspace dependencies were updated
174
+ * dependencies
175
+ * @twin.org/core bumped from 0.0.3-next.22 to 0.0.3-next.23
176
+ * @twin.org/context bumped from 0.0.3-next.22 to 0.0.3-next.23
177
+ * @twin.org/nameof bumped from 0.0.3-next.22 to 0.0.3-next.23
178
+ * devDependencies
179
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.22 to 0.0.3-next.23
180
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.22 to 0.0.3-next.23
181
+ * @twin.org/validate-locales bumped from 0.0.3-next.22 to 0.0.3-next.23
182
+
183
+ ## [0.0.3-next.22](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.21...modules-v0.0.3-next.22) (2026-02-26)
184
+
185
+
186
+ ### Miscellaneous Chores
187
+
188
+ * **modules:** Synchronize repo versions
189
+
190
+
191
+ ### Dependencies
192
+
193
+ * The following workspace dependencies were updated
194
+ * dependencies
195
+ * @twin.org/core bumped from 0.0.3-next.21 to 0.0.3-next.22
196
+ * @twin.org/context bumped from 0.0.3-next.21 to 0.0.3-next.22
197
+ * @twin.org/nameof bumped from 0.0.3-next.21 to 0.0.3-next.22
198
+ * devDependencies
199
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.21 to 0.0.3-next.22
200
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.21 to 0.0.3-next.22
201
+ * @twin.org/validate-locales bumped from 0.0.3-next.21 to 0.0.3-next.22
202
+
203
+ ## [0.0.3-next.21](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.20...modules-v0.0.3-next.21) (2026-02-26)
204
+
205
+
206
+ ### Features
207
+
208
+ * add context id features ([#206](https://github.com/twinfoundation/framework/issues/206)) ([ef0d4ee](https://github.com/twinfoundation/framework/commit/ef0d4ee11a4f5fc6cc6f52a4958ce905c04ee13b))
209
+ * add guards arrayEndsWith and arrayStartsWith ([95d875e](https://github.com/twinfoundation/framework/commit/95d875ec8ccb4713c145fdde941d4cfedcec2ed3))
210
+ * eslint migration to flat config ([74427d7](https://github.com/twinfoundation/framework/commit/74427d78d342167f7850e49ab87269326355befe))
211
+ * improve async pattern and error handling ([aaa1f68](https://github.com/twinfoundation/framework/commit/aaa1f6879d60adf04b78b0c1bbbec50f2873f020))
212
+ * improve Is.function and ModuleHelper.getModuleMethod signatures ([ecf968b](https://github.com/twinfoundation/framework/commit/ecf968b02934b3676be4bf7cd2d1e7f8e7af6ce2))
213
+ * locales validation ([#197](https://github.com/twinfoundation/framework/issues/197)) ([55fdadb](https://github.com/twinfoundation/framework/commit/55fdadb13595ce0047f787bd1d4135d429a99f12))
214
+ * module helper can now handle long running threads with messaging ([4ecbb9a](https://github.com/twinfoundation/framework/commit/4ecbb9a526927d462d4fb3f95ba2a44889202753))
215
+ * nodeIdentity optional in IComponent methods ([c78dc17](https://github.com/twinfoundation/framework/commit/c78dc17f4357d3e1ae40e415f468d3eae13e81f4))
216
+ * provide module helper override ([e998a64](https://github.com/twinfoundation/framework/commit/e998a64842cfd18693a14444be33b084fef2bb90))
217
+ * relocate core packages from tools ([bcab8f3](https://github.com/twinfoundation/framework/commit/bcab8f3160442ea4fcaf442947462504f3d6a17d))
218
+ * retain detail in Errors from background threads ([52bdeef](https://github.com/twinfoundation/framework/commit/52bdeef7a75fd462ed118073b9dc7612304e038d))
219
+ * update dependencies ([f3bd015](https://github.com/twinfoundation/framework/commit/f3bd015efd169196b7e0335f5cab876ba6ca1d75))
220
+ * use cause instead of inner for errors ([1f4acc4](https://github.com/twinfoundation/framework/commit/1f4acc4d7a6b71a134d9547da9bf40de1e1e49da))
221
+ * use new shared store mechanism ([#131](https://github.com/twinfoundation/framework/issues/131)) ([934385b](https://github.com/twinfoundation/framework/commit/934385b2fbaf9f5c00a505ebf9d093bd5a425f55))
222
+
223
+
224
+ ### Dependencies
225
+
226
+ * The following workspace dependencies were updated
227
+ * dependencies
228
+ * @twin.org/core bumped from 0.0.3-next.20 to 0.0.3-next.21
229
+ * @twin.org/context bumped from 0.0.3-next.20 to 0.0.3-next.21
230
+ * @twin.org/nameof bumped from 0.0.3-next.20 to 0.0.3-next.21
231
+ * devDependencies
232
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.20 to 0.0.3-next.21
233
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.20 to 0.0.3-next.21
234
+ * @twin.org/validate-locales bumped from 0.0.3-next.20 to 0.0.3-next.21
235
+
236
+ ## [0.0.3-next.20](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.19...modules-v0.0.3-next.20) (2026-02-26)
237
+
238
+
239
+ ### Miscellaneous Chores
240
+
241
+ * **modules:** Synchronize repo versions
242
+
243
+
244
+ ### Dependencies
245
+
246
+ * The following workspace dependencies were updated
247
+ * dependencies
248
+ * @twin.org/core bumped from 0.0.3-next.19 to 0.0.3-next.20
249
+ * @twin.org/context bumped from 0.0.3-next.19 to 0.0.3-next.20
250
+ * @twin.org/nameof bumped from 0.0.3-next.19 to 0.0.3-next.20
251
+ * devDependencies
252
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.19 to 0.0.3-next.20
253
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.19 to 0.0.3-next.20
254
+ * @twin.org/validate-locales bumped from 0.0.3-next.19 to 0.0.3-next.20
255
+
256
+ ## [0.0.3-next.19](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.18...modules-v0.0.3-next.19) (2026-02-26)
257
+
258
+
259
+ ### Miscellaneous Chores
260
+
261
+ * **modules:** Synchronize repo versions
262
+
263
+
264
+ ### Dependencies
265
+
266
+ * The following workspace dependencies were updated
267
+ * dependencies
268
+ * @twin.org/core bumped from 0.0.3-next.18 to 0.0.3-next.19
269
+ * @twin.org/context bumped from 0.0.3-next.18 to 0.0.3-next.19
270
+ * @twin.org/nameof bumped from 0.0.3-next.18 to 0.0.3-next.19
271
+ * devDependencies
272
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.18 to 0.0.3-next.19
273
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.18 to 0.0.3-next.19
274
+ * @twin.org/validate-locales bumped from 0.0.3-next.18 to 0.0.3-next.19
275
+
276
+ ## [0.0.3-next.18](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.17...modules-v0.0.3-next.18) (2026-02-23)
277
+
278
+
279
+ ### Miscellaneous Chores
280
+
281
+ * **modules:** Synchronize repo versions
282
+
283
+
284
+ ### Dependencies
285
+
286
+ * The following workspace dependencies were updated
287
+ * dependencies
288
+ * @twin.org/core bumped from 0.0.3-next.17 to 0.0.3-next.18
289
+ * @twin.org/context bumped from 0.0.3-next.17 to 0.0.3-next.18
290
+ * @twin.org/nameof bumped from 0.0.3-next.17 to 0.0.3-next.18
291
+ * devDependencies
292
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.17 to 0.0.3-next.18
293
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.17 to 0.0.3-next.18
294
+ * @twin.org/validate-locales bumped from 0.0.3-next.17 to 0.0.3-next.18
295
+
296
+ ## [0.0.3-next.17](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.16...modules-v0.0.3-next.17) (2026-02-09)
297
+
298
+
299
+ ### Miscellaneous Chores
300
+
301
+ * **modules:** Synchronize repo versions
302
+
303
+
304
+ ### Dependencies
305
+
306
+ * The following workspace dependencies were updated
307
+ * dependencies
308
+ * @twin.org/core bumped from 0.0.3-next.16 to 0.0.3-next.17
309
+ * @twin.org/context bumped from 0.0.3-next.16 to 0.0.3-next.17
310
+ * @twin.org/nameof bumped from 0.0.3-next.16 to 0.0.3-next.17
311
+ * devDependencies
312
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.16 to 0.0.3-next.17
313
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.16 to 0.0.3-next.17
314
+ * @twin.org/validate-locales bumped from 0.0.3-next.16 to 0.0.3-next.17
315
+
316
+ ## [0.0.3-next.16](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.15...modules-v0.0.3-next.16) (2026-02-06)
317
+
318
+
319
+ ### Miscellaneous Chores
320
+
321
+ * **modules:** Synchronize repo versions
322
+
323
+
324
+ ### Dependencies
325
+
326
+ * The following workspace dependencies were updated
327
+ * dependencies
328
+ * @twin.org/core bumped from 0.0.3-next.15 to 0.0.3-next.16
329
+ * @twin.org/context bumped from 0.0.3-next.15 to 0.0.3-next.16
330
+ * @twin.org/nameof bumped from 0.0.3-next.15 to 0.0.3-next.16
331
+ * devDependencies
332
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.15 to 0.0.3-next.16
333
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.15 to 0.0.3-next.16
334
+ * @twin.org/validate-locales bumped from 0.0.3-next.15 to 0.0.3-next.16
335
+
336
+ ## [0.0.3-next.15](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.14...modules-v0.0.3-next.15) (2026-01-29)
337
+
338
+
339
+ ### Miscellaneous Chores
340
+
341
+ * **modules:** Synchronize repo versions
342
+
343
+
344
+ ### Dependencies
345
+
346
+ * The following workspace dependencies were updated
347
+ * dependencies
348
+ * @twin.org/core bumped from 0.0.3-next.14 to 0.0.3-next.15
349
+ * @twin.org/context bumped from 0.0.3-next.14 to 0.0.3-next.15
350
+ * @twin.org/nameof bumped from 0.0.3-next.14 to 0.0.3-next.15
351
+ * devDependencies
352
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.14 to 0.0.3-next.15
353
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.14 to 0.0.3-next.15
354
+ * @twin.org/validate-locales bumped from 0.0.3-next.14 to 0.0.3-next.15
355
+
356
+ ## [0.0.3-next.14](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.13...modules-v0.0.3-next.14) (2026-01-22)
357
+
358
+
359
+ ### Miscellaneous Chores
360
+
361
+ * **modules:** Synchronize repo versions
362
+
363
+
364
+ ### Dependencies
365
+
366
+ * The following workspace dependencies were updated
367
+ * dependencies
368
+ * @twin.org/core bumped from 0.0.3-next.13 to 0.0.3-next.14
369
+ * @twin.org/context bumped from 0.0.3-next.13 to 0.0.3-next.14
370
+ * @twin.org/nameof bumped from 0.0.3-next.13 to 0.0.3-next.14
371
+ * devDependencies
372
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.13 to 0.0.3-next.14
373
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.13 to 0.0.3-next.14
374
+ * @twin.org/validate-locales bumped from 0.0.3-next.13 to 0.0.3-next.14
375
+
376
+ ## [0.0.3-next.13](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.12...modules-v0.0.3-next.13) (2026-01-08)
377
+
378
+
379
+ ### Miscellaneous Chores
380
+
381
+ * **modules:** Synchronize repo versions
382
+
383
+
384
+ ### Dependencies
385
+
386
+ * The following workspace dependencies were updated
387
+ * dependencies
388
+ * @twin.org/core bumped from 0.0.3-next.12 to 0.0.3-next.13
389
+ * @twin.org/context bumped from 0.0.3-next.12 to 0.0.3-next.13
390
+ * @twin.org/nameof bumped from 0.0.3-next.12 to 0.0.3-next.13
391
+ * devDependencies
392
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.12 to 0.0.3-next.13
393
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.12 to 0.0.3-next.13
394
+ * @twin.org/validate-locales bumped from 0.0.3-next.12 to 0.0.3-next.13
395
+
396
+ ## [0.0.3-next.12](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.11...modules-v0.0.3-next.12) (2026-01-08)
397
+
398
+
399
+ ### Miscellaneous Chores
400
+
401
+ * **modules:** Synchronize repo versions
402
+
403
+
404
+ ### Dependencies
405
+
406
+ * The following workspace dependencies were updated
407
+ * dependencies
408
+ * @twin.org/core bumped from 0.0.3-next.11 to 0.0.3-next.12
409
+ * @twin.org/context bumped from 0.0.3-next.11 to 0.0.3-next.12
410
+ * @twin.org/nameof bumped from 0.0.3-next.11 to 0.0.3-next.12
411
+ * devDependencies
412
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.11 to 0.0.3-next.12
413
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.11 to 0.0.3-next.12
414
+ * @twin.org/validate-locales bumped from 0.0.3-next.11 to 0.0.3-next.12
415
+
416
+ ## [0.0.3-next.11](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.10...modules-v0.0.3-next.11) (2026-01-07)
417
+
418
+
419
+ ### Miscellaneous Chores
420
+
421
+ * **modules:** Synchronize repo versions
422
+
423
+
424
+ ### Dependencies
425
+
426
+ * The following workspace dependencies were updated
427
+ * dependencies
428
+ * @twin.org/core bumped from 0.0.3-next.10 to 0.0.3-next.11
429
+ * @twin.org/context bumped from 0.0.3-next.10 to 0.0.3-next.11
430
+ * @twin.org/nameof bumped from 0.0.3-next.10 to 0.0.3-next.11
431
+ * devDependencies
432
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.10 to 0.0.3-next.11
433
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.10 to 0.0.3-next.11
434
+ * @twin.org/validate-locales bumped from 0.0.3-next.10 to 0.0.3-next.11
435
+
436
+ ## [0.0.3-next.10](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.9...modules-v0.0.3-next.10) (2026-01-07)
437
+
438
+
439
+ ### Features
440
+
441
+ * retain detail in Errors from background threads ([52bdeef](https://github.com/twinfoundation/framework/commit/52bdeef7a75fd462ed118073b9dc7612304e038d))
442
+
443
+
444
+ ### Dependencies
445
+
446
+ * The following workspace dependencies were updated
447
+ * dependencies
448
+ * @twin.org/core bumped from 0.0.3-next.9 to 0.0.3-next.10
449
+ * @twin.org/context bumped from 0.0.3-next.9 to 0.0.3-next.10
450
+ * @twin.org/nameof bumped from 0.0.3-next.9 to 0.0.3-next.10
451
+ * devDependencies
452
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.9 to 0.0.3-next.10
453
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.9 to 0.0.3-next.10
454
+ * @twin.org/validate-locales bumped from 0.0.3-next.9 to 0.0.3-next.10
455
+
456
+ ## [0.0.3-next.9](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.8...modules-v0.0.3-next.9) (2026-01-05)
457
+
458
+
459
+ ### Miscellaneous Chores
460
+
461
+ * **modules:** Synchronize repo versions
462
+
463
+
464
+ ### Dependencies
465
+
466
+ * The following workspace dependencies were updated
467
+ * dependencies
468
+ * @twin.org/core bumped from 0.0.3-next.8 to 0.0.3-next.9
469
+ * @twin.org/context bumped from 0.0.3-next.8 to 0.0.3-next.9
470
+ * @twin.org/nameof bumped from 0.0.3-next.8 to 0.0.3-next.9
471
+ * devDependencies
472
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.8 to 0.0.3-next.9
473
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.8 to 0.0.3-next.9
474
+ * @twin.org/validate-locales bumped from 0.0.3-next.8 to 0.0.3-next.9
475
+
476
+ ## [0.0.3-next.8](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.7...modules-v0.0.3-next.8) (2025-11-26)
477
+
478
+
479
+ ### Miscellaneous Chores
480
+
481
+ * **modules:** Synchronize repo versions
482
+
483
+
484
+ ### Dependencies
485
+
486
+ * The following workspace dependencies were updated
487
+ * dependencies
488
+ * @twin.org/core bumped from 0.0.3-next.7 to 0.0.3-next.8
489
+ * @twin.org/context bumped from 0.0.3-next.7 to 0.0.3-next.8
490
+ * @twin.org/nameof bumped from 0.0.3-next.7 to 0.0.3-next.8
491
+ * devDependencies
492
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.7 to 0.0.3-next.8
493
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.7 to 0.0.3-next.8
494
+ * @twin.org/validate-locales bumped from 0.0.3-next.7 to 0.0.3-next.8
495
+
496
+ ## [0.0.3-next.7](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.6...modules-v0.0.3-next.7) (2025-11-25)
497
+
498
+
499
+ ### Features
500
+
501
+ * add context id features ([#206](https://github.com/twinfoundation/framework/issues/206)) ([ef0d4ee](https://github.com/twinfoundation/framework/commit/ef0d4ee11a4f5fc6cc6f52a4958ce905c04ee13b))
502
+ * add guards arrayEndsWith and arrayStartsWith ([95d875e](https://github.com/twinfoundation/framework/commit/95d875ec8ccb4713c145fdde941d4cfedcec2ed3))
503
+ * eslint migration to flat config ([74427d7](https://github.com/twinfoundation/framework/commit/74427d78d342167f7850e49ab87269326355befe))
504
+ * improve async pattern and error handling ([aaa1f68](https://github.com/twinfoundation/framework/commit/aaa1f6879d60adf04b78b0c1bbbec50f2873f020))
505
+ * improve Is.function and ModuleHelper.getModuleMethod signatures ([ecf968b](https://github.com/twinfoundation/framework/commit/ecf968b02934b3676be4bf7cd2d1e7f8e7af6ce2))
506
+ * locales validation ([#197](https://github.com/twinfoundation/framework/issues/197)) ([55fdadb](https://github.com/twinfoundation/framework/commit/55fdadb13595ce0047f787bd1d4135d429a99f12))
507
+ * module helper can now handle long running threads with messaging ([4ecbb9a](https://github.com/twinfoundation/framework/commit/4ecbb9a526927d462d4fb3f95ba2a44889202753))
508
+ * nodeIdentity optional in IComponent methods ([c78dc17](https://github.com/twinfoundation/framework/commit/c78dc17f4357d3e1ae40e415f468d3eae13e81f4))
509
+ * provide module helper override ([e998a64](https://github.com/twinfoundation/framework/commit/e998a64842cfd18693a14444be33b084fef2bb90))
510
+ * relocate core packages from tools ([bcab8f3](https://github.com/twinfoundation/framework/commit/bcab8f3160442ea4fcaf442947462504f3d6a17d))
511
+ * update dependencies ([f3bd015](https://github.com/twinfoundation/framework/commit/f3bd015efd169196b7e0335f5cab876ba6ca1d75))
512
+ * use cause instead of inner for errors ([1f4acc4](https://github.com/twinfoundation/framework/commit/1f4acc4d7a6b71a134d9547da9bf40de1e1e49da))
513
+ * use new shared store mechanism ([#131](https://github.com/twinfoundation/framework/issues/131)) ([934385b](https://github.com/twinfoundation/framework/commit/934385b2fbaf9f5c00a505ebf9d093bd5a425f55))
514
+
515
+
516
+ ### Dependencies
517
+
518
+ * The following workspace dependencies were updated
519
+ * dependencies
520
+ * @twin.org/core bumped from 0.0.3-next.6 to 0.0.3-next.7
521
+ * @twin.org/context bumped from 0.0.3-next.6 to 0.0.3-next.7
522
+ * @twin.org/nameof bumped from 0.0.3-next.6 to 0.0.3-next.7
523
+ * devDependencies
524
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.6 to 0.0.3-next.7
525
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.6 to 0.0.3-next.7
526
+ * @twin.org/validate-locales bumped from 0.0.3-next.6 to 0.0.3-next.7
527
+
528
+ ## [0.0.3-next.6](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.5...modules-v0.0.3-next.6) (2025-11-25)
529
+
530
+
531
+ ### Features
532
+
533
+ * module helper can now handle long running threads with messaging ([4ecbb9a](https://github.com/twinfoundation/framework/commit/4ecbb9a526927d462d4fb3f95ba2a44889202753))
534
+
535
+
536
+ ### Dependencies
537
+
538
+ * The following workspace dependencies were updated
539
+ * dependencies
540
+ * @twin.org/core bumped from 0.0.3-next.5 to 0.0.3-next.6
541
+ * @twin.org/context bumped from 0.0.3-next.5 to 0.0.3-next.6
542
+ * @twin.org/nameof bumped from 0.0.3-next.5 to 0.0.3-next.6
543
+ * devDependencies
544
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.5 to 0.0.3-next.6
545
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.5 to 0.0.3-next.6
546
+ * @twin.org/validate-locales bumped from 0.0.3-next.5 to 0.0.3-next.6
547
+
548
+ ## [0.0.3-next.5](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.4...modules-v0.0.3-next.5) (2025-11-20)
549
+
550
+
551
+ ### Miscellaneous Chores
552
+
553
+ * **modules:** Synchronize repo versions
554
+
555
+
556
+ ### Dependencies
557
+
558
+ * The following workspace dependencies were updated
559
+ * dependencies
560
+ * @twin.org/core bumped from 0.0.3-next.4 to 0.0.3-next.5
561
+ * @twin.org/nameof bumped from 0.0.3-next.4 to 0.0.3-next.5
562
+ * devDependencies
563
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.4 to 0.0.3-next.5
564
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.4 to 0.0.3-next.5
565
+ * @twin.org/validate-locales bumped from 0.0.3-next.4 to 0.0.3-next.5
566
+
567
+ ## [0.0.3-next.4](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.3...modules-v0.0.3-next.4) (2025-11-13)
568
+
569
+
570
+ ### Miscellaneous Chores
571
+
572
+ * **modules:** Synchronize repo versions
573
+
574
+
575
+ ### Dependencies
576
+
577
+ * The following workspace dependencies were updated
578
+ * dependencies
579
+ * @twin.org/core bumped from 0.0.3-next.3 to 0.0.3-next.4
580
+ * @twin.org/nameof bumped from 0.0.3-next.3 to 0.0.3-next.4
581
+ * devDependencies
582
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.3 to 0.0.3-next.4
583
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.3 to 0.0.3-next.4
584
+ * @twin.org/validate-locales bumped from 0.0.3-next.3 to 0.0.3-next.4
2
585
 
3
586
  ## [0.0.3-next.3](https://github.com/twinfoundation/framework/compare/modules-v0.0.3-next.2...modules-v0.0.3-next.3) (2025-11-12)
4
587
 
package/docs/examples.md CHANGED
@@ -1 +1,110 @@
1
- # @twin.org/modules - Examples
1
+ # Modules Examples
2
+
3
+ Use these snippets for dynamic module loading patterns at runtime extension points.
4
+
5
+ ## ModuleHelper
6
+
7
+ ```typescript
8
+ import { ModuleHelper } from '@twin.org/modules';
9
+
10
+ console.log(ModuleHelper.isRelativeModule('./workers/sendEmail.js')); // true
11
+ console.log(ModuleHelper.isLocalModule('./workers/sendEmail.js')); // true
12
+ console.log(ModuleHelper.isLocalModule('@twin.org/core')); // false
13
+ ```
14
+
15
+ ```typescript
16
+ import { ModuleHelper } from '@twin.org/modules';
17
+
18
+ const convertToFahrenheit = await ModuleHelper.getModuleMethod<(celsius: number) => number>(
19
+ './workers/temperature.js',
20
+ 'convertToFahrenheit'
21
+ );
22
+
23
+ console.log(convertToFahrenheit(18)); // 64.4
24
+ ```
25
+
26
+ ```typescript
27
+ import { ModuleHelper } from '@twin.org/modules';
28
+
29
+ const sum = await ModuleHelper.getModuleEntry<(left: number, right: number) => number>(
30
+ './workers/math.js',
31
+ 'sum'
32
+ );
33
+
34
+ console.log(sum(7, 5)); // 12
35
+ ```
36
+
37
+ ```typescript
38
+ import { ModuleHelper } from '@twin.org/modules';
39
+
40
+ const result = await ModuleHelper.execModuleMethod<number>(
41
+ './workers/temperature.js',
42
+ 'convertToFahrenheit',
43
+ [18]
44
+ );
45
+
46
+ console.log(result); // 64.4
47
+ ```
48
+
49
+ ```typescript
50
+ import { ModuleHelper } from '@twin.org/modules';
51
+
52
+ ModuleHelper.overrideImport(async moduleName => {
53
+ if (moduleName === './workers/math.js') {
54
+ return {
55
+ module: {
56
+ sum: (left: number, right: number) => left + right
57
+ },
58
+ useDefault: false
59
+ };
60
+ }
61
+
62
+ return {
63
+ useDefault: true
64
+ };
65
+ });
66
+
67
+ const sum = await ModuleHelper.execModuleMethod<number>('./workers/math.js', 'sum', [3, 4]);
68
+
69
+ console.log(sum); // 7
70
+ ```
71
+
72
+ ```typescript
73
+ import { ModuleHelper } from '@twin.org/modules';
74
+
75
+ const threaded = await ModuleHelper.execModuleMethodThread<number>(
76
+ './workers/temperature.js',
77
+ 'convertToFahrenheit',
78
+ [18],
79
+ {
80
+ traceId: 'trc-01',
81
+ spanId: 'spn-02'
82
+ }
83
+ );
84
+
85
+ console.log(threaded); // 64.4
86
+ ```
87
+
88
+ ```typescript
89
+ import { ModuleHelper } from '@twin.org/modules';
90
+
91
+ const worker = ModuleHelper.execModuleMethodThreadMessage(
92
+ './workers/temperature.js',
93
+ (operation, result, err) => {
94
+ if (err) {
95
+ console.log(err.message); // resultError
96
+ return;
97
+ }
98
+
99
+ if (operation === 'convertToFahrenheit') {
100
+ console.log(result); // 64.4
101
+ }
102
+ },
103
+ {
104
+ threadName: 'temperature-worker'
105
+ }
106
+ );
107
+
108
+ worker.executeMethod('convertToFahrenheit', [18]);
109
+ await worker.terminate();
110
+ ```
@@ -14,7 +14,7 @@ Helper functions for modules.
14
14
 
15
15
  ## Properties
16
16
 
17
- ### CLASS\_NAME
17
+ ### CLASS\_NAME {#class_name}
18
18
 
19
19
  > `readonly` `static` **CLASS\_NAME**: `string`
20
20
 
@@ -22,7 +22,7 @@ Runtime name for the class.
22
22
 
23
23
  ## Methods
24
24
 
25
- ### overrideImport()
25
+ ### overrideImport() {#overrideimport}
26
26
 
27
27
  > `static` **overrideImport**(`overrideImport`): `void`
28
28
 
@@ -42,7 +42,7 @@ The override import function.
42
42
 
43
43
  ***
44
44
 
45
- ### getModuleEntry()
45
+ ### getModuleEntry() {#getmoduleentry}
46
46
 
47
47
  > `static` **getModuleEntry**\<`T`\>(`module`, `entry`): `Promise`\<`T`\>
48
48
 
@@ -80,7 +80,7 @@ GeneralError if getting the module entry failed.
80
80
 
81
81
  ***
82
82
 
83
- ### getModuleMethod()
83
+ ### getModuleMethod() {#getmodulemethod}
84
84
 
85
85
  > `static` **getModuleMethod**\<`T`\>(`module`, `method`): `Promise`\<`T`\>
86
86
 
@@ -118,7 +118,7 @@ GeneralError if executing the module entry failed.
118
118
 
119
119
  ***
120
120
 
121
- ### execModuleMethod()
121
+ ### execModuleMethod() {#execmodulemethod}
122
122
 
123
123
  > `static` **execModuleMethod**\<`T`\>(`module`, `method`, `args?`): `Promise`\<`T`\>
124
124
 
@@ -162,9 +162,9 @@ GeneralError if executing the module entry failed.
162
162
 
163
163
  ***
164
164
 
165
- ### execModuleMethodThread()
165
+ ### execModuleMethodThread() {#execmodulemethodthread}
166
166
 
167
- > `static` **execModuleMethodThread**\<`T`\>(`module`, `method`, `args?`): `Promise`\<`T`\>
167
+ > `static` **execModuleMethodThread**\<`T`\>(`module`, `method`, `args?`, `contextIds?`): `Promise`\<`T`\>
168
168
 
169
169
  Execute the method in the module in a thread.
170
170
 
@@ -194,6 +194,12 @@ The method to execute from the module.
194
194
 
195
195
  The arguments to pass to the method.
196
196
 
197
+ ##### contextIds?
198
+
199
+ `IContextIds`
200
+
201
+ The context IDs.
202
+
197
203
  #### Returns
198
204
 
199
205
  `Promise`\<`T`\>
@@ -206,7 +212,49 @@ GeneralError if executing the module entry failed.
206
212
 
207
213
  ***
208
214
 
209
- ### isLocalModule()
215
+ ### execModuleMethodThreadMessage() {#execmodulemethodthreadmessage}
216
+
217
+ > `static` **execModuleMethodThreadMessage**(`module`, `completed`, `options?`): [`IModuleWorker`](../interfaces/IModuleWorker.md)
218
+
219
+ Load the module and provide a messaging interface.
220
+
221
+ #### Parameters
222
+
223
+ ##### module
224
+
225
+ `string`
226
+
227
+ The module.
228
+
229
+ ##### completed
230
+
231
+ (`operation`, `result?`, `err?`) => `void`
232
+
233
+ Callback called when the worker thread processes a completion.
234
+
235
+ ##### options?
236
+
237
+ Optional settings.
238
+
239
+ ###### threadName?
240
+
241
+ `string`
242
+
243
+ The name of the thread.
244
+
245
+ #### Returns
246
+
247
+ [`IModuleWorker`](../interfaces/IModuleWorker.md)
248
+
249
+ The messaging interface.
250
+
251
+ #### Throws
252
+
253
+ GeneralError if executing the module entry failed.
254
+
255
+ ***
256
+
257
+ ### isLocalModule() {#islocalmodule}
210
258
 
211
259
  > `static` **isLocalModule**(`name`): `boolean`
212
260
 
@@ -228,7 +276,7 @@ True if the module is local, false otherwise.
228
276
 
229
277
  ***
230
278
 
231
- ### isRelativeModule()
279
+ ### isRelativeModule() {#isrelativemodule}
232
280
 
233
281
  > `static` **isRelativeModule**(`name`): `boolean`
234
282
 
@@ -3,3 +3,7 @@
3
3
  ## Classes
4
4
 
5
5
  - [ModuleHelper](classes/ModuleHelper.md)
6
+
7
+ ## Interfaces
8
+
9
+ - [IModuleWorker](interfaces/IModuleWorker.md)
@@ -0,0 +1,51 @@
1
+ # Interface: IModuleWorker
2
+
3
+ Worker definition for modules.
4
+
5
+ ## Methods
6
+
7
+ ### executeMethod() {#executemethod}
8
+
9
+ > **executeMethod**(`method`, `args?`, `contextIds?`): `void`
10
+
11
+ Execute a method in the module.
12
+
13
+ #### Parameters
14
+
15
+ ##### method
16
+
17
+ `string`
18
+
19
+ The method to execute.
20
+
21
+ ##### args?
22
+
23
+ `unknown`
24
+
25
+ The arguments for the method.
26
+
27
+ ##### contextIds?
28
+
29
+ `IContextIds`
30
+
31
+ The context IDs.
32
+
33
+ #### Returns
34
+
35
+ `void`
36
+
37
+ The result of the method.
38
+
39
+ ***
40
+
41
+ ### terminate() {#terminate}
42
+
43
+ > **terminate**(): `Promise`\<`number`\>
44
+
45
+ Terminate the worker.
46
+
47
+ #### Returns
48
+
49
+ `Promise`\<`number`\>
50
+
51
+ A promise that resolves when the worker is terminated including the exit code.
package/locales/en.json CHANGED
@@ -3,9 +3,8 @@
3
3
  "moduleHelper": {
4
4
  "moduleNotFound": "Failed to load module \"{module}\"",
5
5
  "entryNotFound": "Failed to load entry \"{entry}\" from module \"{module}\"",
6
- "notFunction": "The entry \"{entry}\" in module \"{module}\" is not a function",
7
- "workerFailed": "The thread worker failed for \"{entry}\" in module \"{module}\" with exit code \"{exitCode}\"",
8
- "workerException": "The thread worker failed with an exception \"{entry}\" in module \"{module}\""
6
+ "notFunction": "The entry \"{method}\" in module \"{module}\" is not a function",
7
+ "workerException": "The thread worker failed with an exception in module \"{module}\""
9
8
  }
10
9
  }
11
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/modules",
3
- "version": "0.0.3-next.3",
3
+ "version": "0.0.3-next.31",
4
4
  "description": "Helper classes for loading and executing from modules",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,8 +14,9 @@
14
14
  "node": ">=20.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@twin.org/core": "0.0.3-next.3",
18
- "@twin.org/nameof": "0.0.3-next.3"
17
+ "@twin.org/context": "0.0.3-next.31",
18
+ "@twin.org/core": "0.0.3-next.31",
19
+ "@twin.org/nameof": "0.0.3-next.31"
19
20
  },
20
21
  "main": "./dist/es/index.js",
21
22
  "types": "./dist/types/index.d.ts",